Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to run Pygame as a cronjob?

Tags:

python

pygame

I hope this question isn't inherently nonsensical.

I'm developing a game. I've separated out the underlying game engine (in Python) from the graphics component. I have a script that fuzzes some parameters, simulates part of a game using the game engine, and then visualizes it using Pygame.

I'd like to automate the following process:

  1. Run simulation as cronjob
  2. Visualize it using Pygame (headlessly)
  3. Save visualization as a short (~10 sec) video file
  4. Programmatically upload video to Youtube

Ideally, I'd like to do this a few times a day so non-technical members of my team could view the video and give feedback on visual aspects of the game.

I'd like to use Pygame because I've already got the code ready to go. But I suspect I should probably use something like PIL to create a series of image files and go from there.

Is this possible with Pygame? Should I just use PIL? Any other ideas for accomplishing something like this?

like image 558
klenwell Avatar asked Jan 22 '13 02:01

klenwell


2 Answers

Assuming you are running on linux, and your graphics engine works in X, you can headlessly run any application you want using Xvfb (X virtual framebuffer). You can run a video encoder of the virtual (headless) framebuffer session. There are a couple of utilities to make this task easier:

  • https://github.com/leonid-shevtsov/headless
  • https://github.com/lightsofapollo/x-recorder

You'd want to make a top level script which would:

  • Start up Xvfb
  • Start up a video encoder (ie, ffmpeg)
  • Run your game in the Xvfb. You shouldn't need to modify your game in any way, assuming it can run with no user input, just set the DISPLAY environment variable to the correct value.
  • End the video encoding
  • Upload the video to youtube

Xvfb and ffmpeg are definitely the way to go for recording the game headlessly, this ensures you can record your game as is with no modifications. It should be doable but not necessarily easy. The above scripts should hopefully get you started.

like image 65
Anton I. Sipos Avatar answered Sep 30 '22 16:09

Anton I. Sipos


Anton's answer inspired me to dig deeper into this question. Happily, I discovered it is possible to run Pygame headlessly, enabling me to accomplish what I wanted to do a bit more simply than Anton's method.

The basic workflow goes as follows:

  1. Setup pygame to run headlessly
  2. Run my game, saving a screen image for each frame using Pygame
  3. Create video from image files using ffmpeg
  4. Upload video to Youtube using youtube-upload

Sample code (simplified version of my own code so this has not been strictly tested):

# imports
import os
import subprocess
import pygame
import mygame

# setup pygame to run headlessly
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.display.set_mode((1,1))

# can't use display surface to capture images for some reason, so I set up
# my own screen using a pygame rect
width, height = 400, 400
black = (0,0,0)
flags = pygame.SRCALPHA
depth = 32
screen = pygame.Surface((width, height), flags, depth)
pygame.draw.rect(screen, black, (0, 0, width, height), 0)

# my game object: screen becomes attribute of game object: game.screen
game = mygame.MyGame(screen)

# need this file format for saving images and encoding video with ffmpeg
image_file_f = 'frame_%03d.png'

# run game, saving images of each screen
game.init()
while game.is_running:
    game.update()   # updates screen
    image_path = image_file_f % (game.frame_num)
    pygame.image.save(game.screen, image_path)

# create video of images using ffmpeg
output_path = '/tmp/mygame_clip_for_youtube.mp4'

ffmpeg_command = (
    'ffmpeg',
    '-r', str(game.fps),
    '-sameq',
    '-y',
    '-i', image_file_f,
    output_path
)

subprocess.check_call(ffmpeg_command)
print "video file created:", output_path

# upload video to Youtube using youtube-upload
gmail_address='[email protected]'
gmail_password='test123'

upload_command = (
    'youtube-upload',
    '--unlisted',
    '--email=%s' % (gmail_address),
    '--password=%s' % (gmail_password),
    '--title="Sample Game Clip"',
    '--description="See https://stackoverflow.com/q/14450581/1093087"',
    '--category=Games',
    output_path
)

proc = subprocess.Popen(
    upload_command,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)
out, err = proc.communicate()

print "youtube link: %s" % (out)

You'd probably want to delete all the image files once your video was created.

I did have a little trouble capturing screenshots headlessly, which I worked around as described here: In Pygame, how can I save a screen image in headless mode?

I was able to schedule my script to run as a cronjob without issues.

like image 29
klenwell Avatar answered Sep 30 '22 15:09

klenwell