Capturing Animations From XScreenSaver

Intro

With the following bit of code and a few extra lines in your XScreenSaver module, you'll be able to capture sequences of images as jpg files. These files can then be turned into animations that'll play back in standard Media Player software like mplayer.

Requirements

I've been doing this with XScreenSaver v4.10 on Linux Mandrake 9.0 but any recent Linux distribution should be ok. The only major dependancy that i can see is libjpeg. My version appears to be 6.2, you'll need the 'devel' release that includes the headers as well as the libraries.

Downloading The Code

It's very minor (<4k) and can be found here. I've put mine in the xscreensaver/hacks/glx directory as that's where all the other code i was writing was.

Modifying The XScreenSaver Module

Most of the XScreenSaver modules have a main loop named draw_modulename(). Just add a call to grab_frame(display, window); to the end of this loop (usually after a call to glFlush(); and/or glXSwapBuffers(); and/or do_fps();). You may also need to add a function prototype to the top of the file (but below the #includes) to stop compiler warnings appearing. The function prototype should read: extern void grab_frame(Display *display, Window window);

That's about it. I often add a -grab flag to the command line arguments for the module so that i can control whether the module grabs frames everytime i run it. I'll leave adding this flag as an exercise for the reader (hint: look at gleidescope.c and search for "#ifdef GRAB")

Modifying The XScreenSaver Makefile

Now you need to modify the Makefile so that it nows about the grab_frame function and that it also needs to use libjpeg. Find the compile line for your module (search for modulename:) and add grab_frame.o to that line (this is the line defining the dependancies for you module's executable) and grab_frame.o -ljpeg to the line below it (this line is the actual command that compiles the executable).

before:
modulename: modulename.o $(HACK_OBJS)
    $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(HACK_LIBS)
after:
modulename: modulename.o $(HACK_OBJS) grab_frame.o
    $(CC_HACK) -o $@ $@.o $(HACK_OBJS) grab_frame.o -ljpeg

Compiling and Installing

Compiling is just a matter of typing make. Installing probably requires you to have root privilege and is simply a matter of make install. Beware that if you add grab_frame() to a module it will generate grabbed frames every time it runs so it may be an idea to NOT install the software after modification and just run it from the build directory using the command line.

If you get problems check that libjpeg is installed correctly and in a place where the compiler can get to it (header file jpeglib.h is usually in /usr/include or /usr/local/include, libjpeg.so is usually in /usr/lib or /usr/local/lib).

Running

XScreenSaver modules can be run from the command line. Change directory to somewhere where there is lots of space and where the grabbed images won't get mixed in with other files. Then just run the module:

/path/to/module/myModule -geometry 352x288 [other options]

(the -geometry option determines the size of the window the screensaver module runs in, 352x288 being PAL VCD resolution, change as appropriate. It may also be an idea to run the sequence at twice the final size and resize it down during encoding so that the final version is antialiased.)

This creates consecutively numbered files in the current directory starting at grab00000.jpg and ending when you kill the process or the disk runs out of space. I get around 5 frames a second during grabbing so don't be surprised if it takes a while. Each file is typically 30K in size at the above resolution.

NB. if any other windows obscure the window being captured then the overlap will be grabbed. Remember to turn your screensaver off whilst grabbing 8)

Converting The Grabbed Frames Into An Animation

I use mencoder to create the final animations. Other encoders are available, transcode for instance, but each has a long list of complicated options and, well, i'll stick with what i know. The -mf flag takes multiple input files and ties them all together. A lot of the following command (codec, bitrate for instance) is a matter of personal preference and target platform but this works for me (note the use of the \ to escape the * in the filename to stop the shell from expanding it):

mencoder -mf on -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=2000:keyint=100 grab\*.jpg -o out.avi

PAL video requires 25 frames per second, 1500 for a minute, 90,000 for an hour. The number of files can mount up really quickly so keep an eye on your disk space.

Playing The Finished Animation

The out.avi created by the above conversion can now just be played with:

mplayer out.avi

I have examples from my other screensavers here, here, here and here.
These are all deliberately small to save on download time but you get the idea...