Add h264 support to ffmpeg on Ubuntu

So, I’ve got a handful of Ubuntu machines.  I also have a bigger handful of DVDs.  I’d like to conver the DVDs to easier-to-store videos which can be accessed by MythTV, XBMC, my mobile devices, and whatever else easily.  The best broadly-supported format to do that in is h264-encoded mp4 files.  And DVD::Rip does a nice job of letting me use all 20 or so CPUs I have laying around, rather than limiting me to just one workstation.

Unfortunately, DVD::Rip uses transcode, which uses ffmpeg to do the encoding.  And Ubuntu’s ffmpeg, for whatever reason, lacks h264 support.  There’s a guid to rebuilding it which has you pull down the latest source for all the utilities from CVS, and make new packages which don’t work right and are a pain to maintain.  I, on the other hand, want to just take the Ubuntu package and add one compile-time option, so it’ll still work like the vendor-provided package.  After all, all I ned to do is build the exact same thing with the “–enable-libx264” option.  Here’s how.

First, get the compilation tools:

sauer@midnight:~$ sudo apt-get install build-essential fakeroot dpkg-dev

Next, make a build directory and pull down the source for ffmpeg and its dependencies – note that it actually gets libav rather than ffmpeg on Oneiric(it’s still ffmpeg by itself on Lucid), and note that I didn’t use sudo for this one.  That’s so the extracted files are owned by me, and so I can build without beign root (compiling as root is generally something to avoid).

sauer@midnight:~$ mkdir build
sauer@midnight:~$ cd build
sauer@midnight:~/build$ apt-get source ffmpeg
Reading package lists... Done
Building dependency tree
Reading state information... Done
Picking 'libav' as source package instead of 'ffmpeg'

And grab the compilation dependencies

sauer@midnight:~/build$ sudo apt-get build-dep ffmpeg

You’ll also need to get the build package for x264, which is what we need to add (you need to have enabled the multiverse repository for that):

sauer@midnight:~/build$ sudo apt-get install libx264-dev

At this point, extract the package so you can build it.  The directory would normally be named for your package, but ffmpeg is actually part of libav – so here, we’re extracting libav.  I used a wildcard in the filename so you can copy and paste, but if you’ve extracted multiple versions in this directory, you’ll have problems. :) The last command is to change to the extracted-and-patched directory.

sauer@midnight:~/build$ ls
 libav-0.7.3 libav_0.7.3-0ubuntu0.11.10.1.dsc
 libav_0.7.3-0ubuntu0.11.10.1.debian.tar.gz libav_0.7.3.orig.tar.gz
 sauer@midnight:~/build$ dpkg-source -x libav*.dsc
 gpgv: Signature made Wed 04 Jan 2012 09:58:14 AM CST using RSA key ID A744BE93
 gpgv: Can't check signature: public key not found
 dpkg-source: warning: failed to verify signature on ./libav_0.7.3-0ubuntu0.11.10.1.dsc
 dpkg-source: info: extracting libav in libav-0.7.3
 dpkg-source: info: unpacking libav_0.7.3.orig.tar.gz
 dpkg-source: info: unpacking libav_0.7.3-0ubuntu0.11.10.1.debian.tar.gz
 dpkg-source: info: applying 01-Tweak-doxygen-config.patch
 dpkg-source: info: applying 02-make-MAP_ANONYMOUS_AVAILABLE.patch
 sauer@midnight:~/build$ cd libav-*

This, BTW, is the other reason to use the vendor source package.  Any patches and stuff are all integrated in already.  You could just rebuild now for your architecture using dpkg-buildpackage -rfakeroot -b.  But we need to pass in an extra option.  This is still super easy:

sauer@midnight:~/build/libav-0.7.3$ DEB_BUILD_OPTIONS="--enable-libx264" \
  fakeroot debian/rules binary

And now you wait while the magic compile gnomes take care of the hard parts. Don’t worry about all the warnings about undocumented parameters; those don’t hurt the actual binaries. :)  You might notice that the ffmpeg script dumps out “enabled muxers” at one point, and that h264 is now included.  Yay!

Actually, though, we could tweak ffmpeg a little more so that it’s tuned for our CPU.  The most effective way to do that if you have a version of GCC which is newer than 4.2 (which is probably the case, but you should check first; Oneiric uses 4.6.1 and Lucid is 4.4.3) is to pass in the “-march=native” C flag.  That’s still very easy.  Just do this instead of the command above.

sauer@midnight:~/build/libav-0.7.3$ CC="-march=native" \
  DEB_BUILD_OPTIONS="--enable-libx264" \
  fakeroot debian/rules binary

Be aware that this package is now tuned for your specific CPU; it’s not just a 32- or 64-bit package.  So if you have multiple CPU types, you’ll have to redo that for each one of them.  Not a huge deal, but still something to consider.

When it’s all done (it took a couple of hours on a single 2.4GHz Athlon), you’ll ideally get a bunch of output that looks like

dpkg-deb: building package `libavformat53' in `../libavformat53_0.7.3-0ubuntu0.11.10.1_i386.deb'.
dpkg-deb: warning: 'debian/libavfilter2/DEBIAN/control' contains user-defined field 'Original-Maintainer'
dpkg-deb: warning: ignoring 1 warning about the control file(s)

You get a bunch of packages created, because libav is more than just ffmpeg. :)  In fact, it’s all of these:

sauer@midnight:~/build/libav-0.7.3$ ls ../*.deb
../ffmpeg_0.7.3-0ubuntu0.11.10.1_i386.deb
../ffmpeg-dbg_0.7.3-0ubuntu0.11.10.1_i386.deb
../ffmpeg-doc_0.7.3-0ubuntu0.11.10.1_all.deb
../libavcodec53_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavcodec-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libav-dbg_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavdevice53_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavdevice-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libav-doc_0.7.3-0ubuntu0.11.10.1_all.deb
../libavfilter2_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavfilter-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavformat53_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavformat-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libav-source_0.7.3-0ubuntu0.11.10.1_all.deb
../libavutil51_0.7.3-0ubuntu0.11.10.1_i386.deb
../libavutil-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libpostproc52_0.7.3-0ubuntu0.11.10.1_i386.deb
../libpostproc-dev_0.7.3-0ubuntu0.11.10.1_i386.deb
../libswscale2_0.7.3-0ubuntu0.11.10.1_i386.deb
../libswscale-dev_0.7.3-0ubuntu0.11.10.1_i386.deb

You’ll want to replace any of those packages which you already have installed.  A relatively easy way to figure out which ones you have is to use a quick shell loop, such as

sauer@midnight:~/build/libav-0.7.3$ cd ..
sauer@midnight:~/build$ for P in *.deb; do dpkg -l ${P%%_*} | grep ^ii; done
ii ffmpeg 4:0.7.3-0ubuntu0.11.10.1 Multimedia player, server, encoder and transcoder
No packages found matching ffmpeg-dbg.
No packages found matching ffmpeg-doc.
No packages found matching libavcodec-dev.
No packages found matching libav-dbg.
ii libavdevice53 4:0.7.3-0ubuntu0.11.10.1 Libav device handling library
No packages found matching libavdevice-dev.
No packages found matching libav-doc.
ii libavfilter2 4:0.7.3-0ubuntu0.11.10.1 Libav video filtering library
No packages found matching libavfilter-dev.
ii libavformat53 4:0.7.3-0ubuntu0.11.10.1 Libav file format library
No packages found matching libavformat-dev.
No packages found matching libav-source.
No packages found matching libavutil-dev.
ii libpostproc52 4:0.7.3-0ubuntu0.11.10.1 Libav video postprocessing library
No packages found matching libpostproc-dev.
ii libswscale2 4:0.7.3-0ubuntu0.11.10.1 Libav video scaling library
No packages found matching libswscale-dev.

So, I have six packages I need to reinstall.  Here’s how I did it – your mileage may vary:

sauer@midnight:~/build$ sudo dpkg –install ./{ffmpeg,libavdevice53,libavfilter2,libavformat53,libpostproc52,libswscale2}_*.deb

Now I can go update a few other machines, and encode video using h264.  The problem? Well, the version now matches what’s current in the repository, so you are apt (heh) to overwrite your package next time there’s an update.  You’ll have to 1) notice that there’s an update available and 2) follow this procedure again to rebuild the new version.  Yeah, that’s a pain.  But, until the fine folks at Ubuntu are convinced to just bundle the encoder in the correct repo, them’s the breaks.

Also, this still doesn’t work with DVD::Rip, because there’s a command-line error in the way it calls ffmpeg with the h264 encoder. I haven’t tracked that down yet.  But this does get you a working ffmpeg, which, I suppose, is the point.