Update 2009-07-28: In the final project another solution was pointed out to me, which ended up being better in most cases, see the commands here: http://code.google.com/p/wiidiaplayer/source/browse/trunk/wiidiaserver/rtmp/flvstreamprovider/convertvideoffmpeg.sh

In my previous blog I explained the format I want the flash video in (and the reasons why), however, getting my video in that format did pose some problems, most of which I hope I have tackled by now.

The actual command for converting the video itself is not so complicated:

 /usr/bin/mencoder "$1"
        -of lavf -lavfopts format=flv
        -af resample=44100:0:1 -af channels=2 -oac mp3lame -lameopts cbr:br=128 -mc 0
        -ovc lavc -lavcopts vcodec=flv:vbitrate=2500:autoaspect:vratetol=1000:keyint=1 -ofps $FPS
        -o "$2"

On the first line, it shows the command and inputfile, the second line defines handles the output container format; The third line handles all audio (44.1kHz (the next two numbers define the conversion type), use two channels, convert with lame to a constant bitrate of 128kbps. The final option disallows any shifting between video and audio frames.

The forth line defines the video options (codec flv, bitrate (max) 2500kbps (max 1000kbps tolerance), per 1 frame 1 keyframe (ie: use only keyframes), and the required framerate), the final line sets the output filename.

Now, before we continue, it’s necessary to note that I have installed 3 different versions of mplayer in the last week, and each version needed a slightly different command. Currently I’m using MPlayer SVN-r24130, as provided by Gentoo ebuild media-video/mplayer-1.0.20070824, if you have a different version, you might need to adjust some parameters. I’m using some unversioned version of ffmpeg, which is provided by media-video/ffmpeg-0.4.9_p20070330; however my experiences with ffmpeg is that it is much more stable and compatible between versions, so any recent version should probably do.

The problem with the command above is that it only works on certain video files, and obviously we would like to convert any video file that mplayer can play. It seems that there are several bugs in mplayer/mencoder that disallow this (try the command above on your average avi file and mencoder will crash with a segfault), so we have to take a work-around. We will convert the video in two steps: first convert the source to an intermediary format, next convert this format with the command above to the final result. To avoid quality loss and extra CPU load, the intermediary format is the raw, uncompressed video format. This format takes about 4.5MB per second, so we don’t want to store it on disk, but pipe it directly from one mencoder into the next. To accomplish this we need to redirect any other output from mencoder to somewhere else (so that only the audio/video data reaches the next mencoder, not the status information). Furthermore, we need to keep the first mencoder from wanting to seek in the file it writes; When writing avi-files, mencoder updates the avi header every couple of hundred megabytes, by seeking to the beginning of the file and writing the header – this obviously cannot be done with a pipe. The -noodml tag fixes this problem, but another problem persists: the second mencoder (behind the pipe) expects the maximum avi size to be 4GB (which is reached in 15 minutes), so after 15 minutes encoding stops. The only solution I’ve found to this is not using the avi container, but the asf container (the mpeg container and other containers have other problems, only asf seems to work).

So the encoding part of the file looks like this:

/usr/bin/mencoder "$1"
                -of lavf -lavfopts format=asf
                -oac pcm -af resample=44100:0:1
                -ovc raw -vf scale=400:224 -ofps $FPS
                -o /dev/fd/3 3>&1 >/var/log/mencoder/1 2>&1 |
        /usr/bin/mencoder /dev/stdin
                -of lavf -lavfopts format=flv
                -af resample=44100:0:1 -af channels=2 -oac mp3lame -lameopts cbr:br=128 -mc 0
                -ovc lavc -lavcopts vcodec=flv:vbitrate=2500:autoaspect:vratetol=1000:keyint=1 -ofps $FPS
                -o "$2" > /var/log/mencoder/2 2>&1

The first mencoder sets the correct resoltion, framerate and audio options, and outputs it to a filedescriptor called /dev/fd/3. The little filedescriptor magic afterwards redirects this filedescriptor 3 to stdout, while directing stdout en stderr to a logfile. Both the input and the output are files. For the wiidiaplayer all sources are (for now) files, and I render to a temporary directory as well, to improve caching and enable seeking in the video.

To this we add some extra padding that lowers the priority of the encoders (to avoid the computer locking up when coding), and some code that makes sure all child processes end when the parent process is killed, and that’s the full conversion script:

#!/bin/bash

function getmencoderchildids {
    ps --ppid $$ | awk '$4=="mencoder" {print $1}'
}

function stop_encoding {
 echo "now stopping"
 PIDS="$(getmencoderchildids)"
 for mypid in $PIDS; do
 	kill $mypid > /dev/null 2&>1;
 done
 sleep 2;
 PIDS="$(getmencoderchildids)"
 for mypid in $PIDS; do
 	kill -9 $mypid > /dev/null 2&>1;
 done
}

trap stop_encoding TERM;
trap stop_encoding EXIT;

FPS=18

nice -n 2 /usr/bin/mencoder "$1"
	        -of lavf -lavfopts format=asf
                -oac pcm -af resample=44100:0:1
                -ovc raw -vf scale=400:224 -ofps $FPS
                -o /dev/fd/3 3>&1 >/var/log/mencoder/1 2>&1 |
        nice -n 1 /usr/bin/mencoder /dev/stdin
        		-of lavf -lavfopts format=flv
        		-af resample=44100:0:1 -af channels=2 -oac mp3lame -lameopts cbr:br=128 -mc 0
        		-ovc lavc -lavcopts vcodec=flv:vbitrate=2500:autoaspect:vratetol=1000:keyint=1 -ofps $FPS
        		-o "$2" > /var/log/mencoder/2 2>&1 &
while [ -n "$(getmencoderchildids)" ]; do
 echo "$(getmencoderchildids)"
 sleep 1;
done
Advertisements