Adventures in Video Transcoding
A few days ago I was growing somewhat despondent while trying to find a way to convert a large quicktime MOV file that had been encoded in 720p HD into several other formats. After fumbling around for a while and thinking I was going to have to use the commercial (and excellent) Adobe Media Encoder, I found an unexpected gem that wound up exceeding my needs and allowing me to do something truly cool.
Before I unveil the final solution, allow me to tell you a bit of what I’ve discovered along this little journey and a few of the specifics about what I needed to accomplish.
From my HD movie file I needed to create an MP4 file to load into a web-based flash player as well as downloads for iPhone & iPod Touch, other video iPods, and standalone downloads. I had a few goals in mind - Of course I wanted it to look as good as possible at our chosen resolutions without really noticable compression artifacts. I also really wanted it to be command-line based so that I could script its use. I like the command line. It’s comfortable for me, and it’s flexible. I can always build a gui wrapper, a separate command line wapper, or even a web front end for command line interfaces. And to top it off, I want it to run on my Mac.
As I experimented and thought about what I wanted, I finally decided that with whatever solution I came up with, I wanted to be able to make sure my colleague across the hall could run it as well. Afterall I didn’t want to be the only one able to encode videos.
I first flirted with the idea of building a service on the web server that would encode our videos for us and plug them in where we needed them. The problem with this is that I didn’t want to bog down the web server’s CPU with encoding tasks. I could move those tasks to a sepaate or dedicated server, but then the amount of hardware you have to begin throwing at the problem seemed overmuch for our current needs and budget.
So I decided I wanted to build something that would run locally on my Mac to encode, and we’d update the websites with the appropriate files the same way we update the rest of the websites contents (Using Subversion and Capistrano).
Now it was time to begin experimenting with encoders. Just for fun, and because it came with Adobe CS4, I tried the encode using Adobe Media Encoder. This tool is primarily concerned with encoding content destined for flash, as it will only encode in MP4 and FLV formats. This is only a minor limitation though, as I probably wouldn’t be straying too far from MP4’s anyway. I was pretty impressed with the Adobe offering. The GUI was intuitive, its encoder seemed to be multi-threaded, and it’s results were astounding. There was no apparent loss of color depth, no compression artifacts that jumped out at you, and the file size was notably smallish.
The quality bar being thusly set, I began looking to see if I could replicate those results with command-line, preferably open source tools. I started with ffmpeg. I’ve had great luck encoding videos into flv’s (flash video) with ffmpeg, so I expected this to be fairly open and shut. I was unfortunately disappointed. The first problem was that almost regardless of what encode settings I used, I was getting some nasty compression artifacts. The color was also getting flatter. When you have a truly well-shot piece of video with good lighting, well-done depth of field and great color balance, getting flat colors out of your encoded video is really unacceptable. Trying to move forward and thinking that I was simply too unfamiliar with some of the more arcane incantations used to get awesome results out of ffmpeg, I opened up FFmpegX, a GUI wrapper for ffmpeg and other command-line tools.
Using FFMpegX, I was able to get pretty good results. Almost as good as the Adobe Media Encoder. The resultant video file had some faintly noticable compression artifacts in the more contrasty pieces of the source video, and it took about 5 times longer to encode, but it wasn’t bad overall. Then I noticed that I had selected the H.264 x264 (mencoder) option instead of the H.264 ffmpeg option. I switched it to ffmpeg and got the same nasty compression artifacts. It wasn’t just me apparently, it was the core library being used by ffmpeg to compress to H.264 that just wasn’t very good. This brought me to two conclusions: First, FFmpegX is in fact very poorly named in that it uses far more than ffmpeg to accomplish it’s goals. To be precise, before everything was said and done, I’d seen it use about 6 different utilities to accomplish its goals. Second, I really needed to be looking at mencoder.
Before I move on to my adventures with mencoder, I’ll note that mencoder and ffmpeg share one “flaw” from the point of view of a fairly novice user: documentation. Trying to discern what the best options to use are like trying to learn how to program an Apollo-era comand module computer. Sure there are instructions, and to those who’ve already attained a certain amount of applicable knowledge the instructions will make sense. But for those of us who’ve never been introduced to the minutae of video encoding, these tools’ documentation reads more like heiroglyphics than English.
I was a little disappointed with mencoder. First, the latest OSX binary distribution was something like 3 years old. I wanted the latest and greatest stable build. So I tried to build it myself. This is normally a fairly straightforward process. The only caveat was that I needed to build what’s known as a static binary, where all the necessary libraries and executable code are bundled up into a single file instead of putting a small executable in one place and the libraries in other various places in the computer’s filesystem. This was necessary because I wanted to bundle everything up for the dude across the hall to use as well. The problem is that mencoder static builds wont build on OSX. I could get it to build the non-static binary just fine, but this just wouldn’t do. So I used the older static binary and started playing with it.
I cobbled together a few command lines based on what I’d learned from the documentation and from a few forum posts. I could get an encoded file, but none were really awesome. This didn’t really make sense as I’d seen good results from mencoder via FFmpegX. So I ran FFmpegX again and tool a look at the commands it was running. I was stunned at the sheer amount of work it was doing. It was doing 2-pass encoding, which I was expecting. I wasn’t expecting it to be doing audio encoding as a completely separate set of steps.
So it would run pass 1 (mencoder), save the pass 1 log, run pass 2 (mencoder again), extract the audio from the source video as a wav (movtowav), normalize that wav (normalize), encode the wav as an AAC mp4 file (ffmpeg), then mux everything together (mp4box) and delete all the temporary files it had created in the process. On the plus side I saw it using some mencoder options I hadn’t fully understood, so things were good there. Using this information I was able to cobble together a script that did the encoding I wanted and could tweak target bitrates and frame sizes at will.
The MP4’s generated this way would not, however, run on an iPhone. Capturing the commands FFmpegX would use to create an iPhone compatable MP4 showed me that there was an additional step needed, adding in some metadata. Part of this process uses movtool, written by the same guy that wrote movtoy4m and movtowav (Johan Lindström), but it used a restricted licence that was distributable only by the guy that publishes FFmpegX. In the immortal words of Eeyore, “Oh Bother”. While I had no intention of distributing to anyone outside the company, this “Do not redistribute” thing bugs me. What would I do if somehow this software got popular and we did decide we wanted to distribute our encoding system? No, I’d rather have something I knew I could distribute from the get-go. So while mencoder itself seemed to be a great tool, using 6 different tools and at least one with a questionable license made me think this was becoming more trouble than it was worth. Adobe Media Encoder was looking better and better, and it was sitll the fastest encoder I’d used.
Still determined, however, I went googling to see if I could discover mencoder options that would work for me. This is when I stumbled upon an obscure little post in some forum deep in the underbelly of the internet that mentioned HandBrake had a command line interface.
HandBrake? Isn’t that a DVD ripping tool, and a rather good one at that? What could it’s command line do? I went to HandBrake’s website and was delighted to find a command line build of its software, and that the command line on this is actually much more awesome than its GUI. The handbrake GUI is pretty well taylored to be only useful for ripping DVDs, but the command line can do that and much more! After reading its refreshingly easy-to-read and succint documentation, I was able to accomplish all the encoding I needed with a single command per transcoded file, instead of the 6 or 7 commands mencoder and friends would have taken.
To make the deal sweeter, the MP4’s HandBrake was generating would also work on iPhones and iPods without separate meta-data steps. And the icing on this particular cake is that HandBrake is multithreaded and capable of utilizing all of my processing power. My Mac at work has Dual Quad-Core Xeons, so it’s stupid not to take advantage of them. All this means that Handbrake is capable of encoding just as fast as Adobe Media Encoder, and with the same quality and compression ratios, all in an un-license-restricted, open source package. The cherry on top of the icing of this sweet cake is that the HandBrake binary is monolithic and static. I can do everything I need without complex installation of multiple files! HandBrake happens to use the same (although more recent version than what I had available to me) x264 encoding libray that mencoder uses. This means that some of the arcane options I learned from mencoder can also be applied with HandBrake.
Since this little project started, I wound up building up a fairly cool utility I’ve dubbed Cyphon That my colleagues and I will use to encode our videos. In a separate post I’ll share that utility with you, and show you how to stitch together various tools into a slick little utility that does exactly what you need it to.
Update @ 10:00AM 2009-05-13 – I’ve since discovered that mencoder and ffmpeg are both capable of multi-threading as well, you just have to enable that functionality at the command line. mencoder is a bit more elegant in that it can autodetect the number of threads to use, whereas with ffmpeg you have to tell it how many to use.
Endikos