First some background: A while ago I wrote a simple Python script that processed a sequence of images in some specific way. I used the PIL to open the images, then do the transformation and write a sequence of images again. It quickly became apparent that there was significant overhead involved in dealing with larger sequences (>10000 files), especially for some OS/filesystem combinations. The slowest of these was probably the OSX Finder trying to access such a directory over SMB, switching to AFP helped a lot there.
To get rid of the need to use (at least one of) these image sequences at all, I decided to redo the reading part of the script to use ffmpeg to read the frames from a quicktime file. As it stands there are some attempts to wrap ffmpeg in Python (see PyMedia, PyFFmpeg), but they seemed to be outdated or weren't compiling on my 64bit machine. So, I went ahead to try a ctypes-based approach on my own, which, as it turns out, is really easy. I don't want to bore you with the details of what I tried, here just comes the summary of what worked well:
First some prerequisites that you might not have:
sudo apt-get install gccxml libavcodec-dev \
libavformat-dev libswscale-dev libavfilter-dev \
libavdevice-dev libpostproc-dev
Next is the tool that makes everything incredibly simple: ctypeslib.
svn co http://svn.python.org/projects/ctypes/trunk/ctypeslib
Note that the trunk version of ctypeslib only works well with older versions
of gccxml (like 0.7), if you have a newer version, get the following branch:
svn co http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9
After that a simple
sudo ./setup.py install
should give you a working h2xml.py and xml2py.py. With these you can
generate ctypes wrappers:
h2xml.py -c -I/usr/include/ffmpeg libavcodec/avcodec.h \
libavformat/avformat.h libswscale/swscale.h \
libavdevice/avdevice.h libavfilter/avfilter.h \
libpostproc/postprocess.h -o av.xml
xml2py.py av.xml -o av.py -l libavcodec.so -l libavformat.so \
-l libswscale.so -l libavdevice.so -l libavfilter.so \
-l libpostproc.so
If that worked you can use ffmpeg in Python like this:
from av import *
av_register_all()
pFormatCtx = POINTER(AVFormatContext)()
if av_open_input_file(pFormatCtx, filename, None, 0, None):
handle_error("could not open file")
... (insert your usual ffmpeg code, examples here and here) ...
Up to now I only found two things that I needed that weren't converted
correctly by ctypeslib. The first were some defines, which could
probably be done right but I didn't need many so I just added them by
hand. The second was this inline function
static inline void av_free_packet(AVPacket *pkt)
{
if (pkt && pkt->destruct) {
pkt->destruct(pkt);
}
}
which I replaced by this definition (to be called with an AVPacket
object instead of a pointer to one):
def av_free_packet(pkt):
if pkt and pkt.destruct:
pkt.destruct(byref(pkt))
With a little bit of scripting to convert the ffmpeg decoded frame to
a PIL image the script can now read anything ffmpeg can read. All
thats left to do is encode the output with ffmpeg, once that is done I
might post this as a module that encapsules the conversion from
ffmpeg to PIL image sequence and back.
Last update on: Thursday, 9th October, 2008