Linux Encoding

x264 encoding guide

To get started you need to install the latest svn version of mplayer, x264 and grab neroAacEnc from here. You also need to install gpac for muxing into the .mp4 container.

Using FFmpeg or mencoder to handle everything is convenient but they have problems. We can avoid these problems by breaking down the steps and using the best tool for that specific job.

Encoding the video

In order for x264 to encode the video stream we must first decode our source video stream with mplayer, convert it to a (lossless) yuv4mpeg stream, or y4m for short, and send it to x264 as input. x264 won't accept y4m as input through a conventional pipe '-' and y4m is uncompressed, so we'll use a fifo, or named pipe, to pipe the y4m through to x264 as input.

    Step 1a
      We create the fifo 'video.y4m'. Keep in mind that x264 expects the .y4m extension to identify the stream as yuv4mpeg. 'man pipe', 'man fifo' and 'man mkfifo' for more info.

      $ mkfifo video.y4m

      $ x264 --crf 20 --threads auto --output output.264 video.y4m 2>x264.log & mplayer star-wars.mkv -vo yuv4mpeg:file=video.y4m -nosound -benchmark

      The above command is a very basic example just to show how the process works. It's not intended to be used.

      In the first half of the command we run x264 with '--crf 20' to enable constant quality mode, '--threads auto' enables parallel encoding, '--output output.264' outputs the raw h264/avc bytestream to 'output.264' (this is the encoded video), 'video.y4m' is our fifo that mplayer is feeding y4m to and x264 is reading as input, '2>x264.log' sends the terminal output from x264 to x264.log (you can open another terminal and monitor x264's progress with 'tail -f x264.log') and '&' sends x264 to the background.

      In the second half of the command we specify mplayer's input file 'star-wars.mkv', convert our source video stream to y4m and direct the stream to our fifo '-vo yuv4mpeg:file=video.y4m', ignore the audio '-nosound' (we'll deal with audio encoding in Step 2) and perform faster than realtime '-benchmark'.

      For more info see 'x264 --fullhelp | less' and 'man mplayer'.
    Step 1b
      Here's another example that's more advanced. This is how I encode video for my iPhone.

      $ mkfifo video.y4m

      $ x264 --crf 20 --profile baseline --level 3 --no-cabac --partitions p8x8,b8x8,i4x4 --me umh --subme 7 --vbv-bufsize 10000 --vbv-maxrate 10000 --threads auto --no-fast-pskip --output output.264 video.y4m 2>x264.log & mplayer star-wars.mkv -nosound -benchmark -vo yuv4mpeg:file=video.y4m -sws 9 -vf dsize=480:320:0,scale=0:0,expand=480:320,dsize=1.5

      In the above command I set the lanczos scaler algorithm with '-sws 9' to be used when scaling the video down. It's slower than mplayer's default scaler but if quality is your main concern then I recommend using it. '-vf dsize=480:320:0,scale=0:0,expand=480:320,dsize=1.5' is the video filter chain that scales the video to 480x320 while maintaining the original aspect ratio and adds padding to the top and bottom or left and right and sets the aspect ratio to 1.5 (480/320).

    Step 1c
      Let's say you want to encode some anime for your phone, ps3 or whatever but you need to hardsub the subtitles because your device doesn't support loading softsubs and you haven't learned Japanese yet, slacker. In case you don't know, hardsubbing is encoding the subtitles into the video stream.

      $ mkfifo video.y4m

      $ x264 --crf 20 --profile baseline --level 3 --no-cabac --partitions p8x8,b8x8,i4x4 --me umh --subme 7 --vbv-bufsize 10000 --vbv-maxrate 10000 --threads auto --no-fast-pskip --output output.264 video.y4m 2>x264.log & mplayer anime.mkv -nosound -benchmark -vo yuv4mpeg:file=video.y4m -fontconfig -font 'Sans:style=Bold' -subfont-outline 2 -subfont-text-scale 2.1 -sub-bg-alpha 0 -subfont-blur 0 -sub-bg-color 0 -sws 9 -vf dsize=480:320:0,scale=0:0,expand=480:320,dsize=1.5

      You can adjust the size of the font by increasing/decreasing the value in '-subfont-text-scale 2.1'. If you want to use a different font, you can get a list by running 'fc-list | sort | less'.

      Note: If you want to hardsub advanced sub station styled subtitles, or ass for short, remove all the '-sub*' options along with -font 'Sans:style=Bold' and add '-ass'. Most of the time I think they look like ass, har har.
    x264 levels and profiles

      Most devices only support a specific level and one or more profiles. Levels define the max macroblocks per second, max frame size (macroblocks) and max video bit rate. Profiles define the h264 capabilities that can be used, such as b frames and CABAC. The H264 wikipedia page lists all the levels and profiles.

      For example, the iPhone supports level 3 with the Baseline profile. If you look a the levels chart on the H264 wikipedia page it says you can have a max video bit rate of 10 Mbit/s. '--level 3 --vbv-bufsize 10000 --vbv-maxrate 10000' sets the level to 3 and max video bitrate and bufsize to 10 Mbit/s (--vbv-maxrate and --vbv-bufsize takes kbit/s). Now look at the profile chart, Baseline doesn't support CABAC or b frames so I set '--no-cabac'. x264 doesn't enable b frames by default so no further settings are needed. Update: x264 now has presets and profile settings. 'x264 --fullhelp | less' for more info.

        iPhone, iPod 5.5G
          --level 3 --no-cabac --vbv-bufsize 10000 --vbv-maxrate 10000

        iPod
          --level 1.3 --no-cabac --vbv-bufsize 768 --vbv-maxrate 768

        PSP
          --level 2.1 --vbv-bufsize 4000 --vbv-maxrate 4000

        Zune
          --level 3 --no-cabac --vbv-bufsize 10000 --vbv-maxrate 10000

        AppleTV
          --level 3.1 --no-cabac --vbv-bufsize 14000 --vbv-maxrate 14000

        PS3-Xbox360
          --level 4.1 --vbv-bufsize 62500 --vbv-maxrate 62500

Encoding the audio

The audio will be handled much like the video. mplayer will be used to convert the audio to wav and send it to neroAacEnc via a fifo.
    Step 2

    $ mkfifo audio.wav

    $ neroAacEnc -ignorelength -lc -q 0.5 -if audio.wav -of audio.mp4 2>nero.log & mplayer -nocorrect-pts -vo null -vc null -ao pcm:file=audio.wav:fast start-wars.mkv

    In the first half of the command we set '-ignorelength' because neroAacEnc is reading from a fifo, '-lc' forces the use of the LC AAC profile (supported by most devices), '-q 0.5' enables target quality mode (vbr), '-if audio.wav' specifies the input, '-of audio.mp4' outputs the encoded aac audio to 'audio.mp4', '2>nero.log' sends the console output to nero.log (open another terminal and run 'tail -f nero.log' to monitor its progress) and '&' sends it to the background.

    In the second half of the command '-nocorrect-pts' gets around the "Too many buffered pts" bug in mplayer, 'vo null -vc null' ignores the video, '-ao pcm:file=audio.wav:fast' dumps the wav to the fifo and 'start-wars.mkv' is our input.

    See 'neroAacEnc -help 2>&1 | less' and 'man mplayer' for more info.

Muxing into the container

    Step 3

    Remember to use the fps from your source video.

    $ MP4Box -fps 25 -add output.264 -add audio.mp4 star-wars.mp4