improved h.264 derivatives!

We have thoroughly tested a newer and simpler way to create h.264 derivatives!

Changes you’ll notice:

  • More pixels!  previously 320 x 240    goes to 640 x 480 pixels
  • Slightly higher video bitrate — from about 512kb/s   to   about  700kb/s bitrate
  • Switching from mp4creator container maker to ffmpeg container + qt-faststart
  • Less back-end commands to make high-quality derivative

Nice things about this derivative (similar to prior derivative):

  • Plays in adobe flash plugin
  • Plays on all versions of iphone and ipad
  • Starts quickly, nearly instant seeking even to unbuffered areas of the video

Here’s a sample of how we do it with just 3 simple commands.  (We do/you should adjust “-r” argument appropriately to your video’s frames-per-second.  We also adjust the “640” in the “-vf scale” argument to be appropriate for the video’s *actual* aspect ratio, etc.  So for example, the 640 might become 852 for 16:9 widescreen video.  Although for our .mp4 specific derivative and playback ability on iPhone (1st gen and thus all versions), we would actually downrez that to 640×360).

ffmpeg -deinterlace -y -i 'camels.avi' -vcodec libx264 -fpre libx264-IA.ffpreset -vf scale=640:480 -r 20 -threads 2 -map_meta_data -1:0 -pass 1 -an tmp.mp4

ffmpeg -deinterlace -y -i 'camels.avi' -vcodec libx264 -fpre libx264-IA.ffpreset -vf scale=640:480 -r 20 -threads 2 -map_meta_data -1:0 -pass 2 -acodec aac -strict experimental -ab 128k -ac 2 -ar 44100 -metadata title='Camels at a Zoo -' -metadata year='2004' -metadata comment=license:'' tmp.mp4

qt-faststart tmp.mp4 'camels.mp4'

our preset file:


For the adventurous out there, you can create this same setup by building ffmpeg on mac, linux, or windows.  Linux is easy, but personally, I’m a mac gal.  So here’s some ffmpeg build tips on the mac.

Happy viewing!


25 thoughts on “improved h.264 derivatives!

  1. Mike

    Very good news! I’m curious as to whether the 512kb/s mp4s derived for old videos will stick around, or whether existing links to those files will break.

  2. traceypooh Post author

    long term we haven’t figured that out — we typically do make a remake pass over all videos about every 2 years — so we could consider a pass in 6-12 months across the videos.

    i think we’ll *want* to update the existing h.264 derivatives to the new ones, which would mean removing the old ones. we have a working scheme right now that will auto-404-catch-and-redirect an old style name/format derivative to the new named file. so as long as permalinks get used, eg:

    then at least it will *feel* like the old derivative is still around.

    PS: future plans already in the work is to replace .ogv/”Ogg Video” files with .webm/”WebM” and do a similar auto-redirect. (Even though it’s going from VP3 video to VP8 video).

  3. Dave Rice

    Hi Tracey,
    Great news! Thanks for posting your scripts.
    Since you’re using libavfilter, you may get better results with mixed interlaced/progressive content using yadif (i.e. using -vf “yadif,scale=640:480” instead of -deinterlace, see Also using scale=640:480 is going to stretch or squeeze non-4/3 video. Using “-1” will scale the video to the other number, like scale=640:-1 will scale to the width of 640 but scale the height to preserve the aspect ratio.

  4. traceypooh Post author

    hi dave!
    OK these are great things for me to try out.
    I forgot to mention above that we have code that already inspects both the “Pixel Aspect Ratio” (PAR) and “Display Aspect Ratio” (DAR) as well as the width and height in terms of pixels (be the pixels square or “rectangular”/anamorphic) and then always tailor the scaling argument accordingly, to output always square pixels and the width that keeps the proper aspect ratio, eg: “-vf scale={{640 for 4×3; 852 for 16×9; etc.}}:480”

    I will certainly check out the “-1” option and the yadif, though — thanks for those great tips!

  5. Kurt Nordstrom

    Hey, thanks for posting this…it’s been extremely helpful to us as we figure out our own workflow for creating derivative videos from our master files.

    A question for you…have you encountered any issues working with video that’s /not/ interlaced? Do you have any method of testing it prior to assigning the -deinterlace switch in ffmpeg?

    1. traceypooh Post author

      we run it mostly always now, even if the input video is progressive.
      sometimes we see “deinterlaced failed” warnings (seem cosmetic — haven’t seen it messup and output garbage), and very *very* rarely the ffmpeg run will fail. at *that* point, recently I added in an update to retry the same command without the “-deinterlace” option. I am not yet convinced that is fixing things, because sometimes I think we just have servers or code that in these rare times blow a bit or have a 2-thread issue or something else. So the upshot is, I recommend always using that param, but if your ffmpeg run fails, you can try a second rerun w/o the param. Chances are, that will be very very rarely ever needed! Best of luck, and feel free to post back later how things go.

      1. Kurt Nordstrom

        Been a while, but things are going fairly well for our adventures in MP4 production. ffmpeg is a…tricky little beast…especially when you’re using the latest version with only a semi-documented set of features.

        One question unrelated to the actual video creation…what sort of setup are you guys using for your backend to host the videos? Do you use Apache with the h264 extension? Or something else to handle the pseudostreaming thing?

        1. traceypooh Post author

          Hi Kurt,
          So we use nginx right now, but we’ve also used lighttpd before that, and apache before that.

          All of them we’ve used

          for it. For nginx we compile in a module from them (I did a really minor change to it to *not* use the module if no “start” and no “end” param are being requested from the mp4 and just have nginx serve it normally, but that’s prolly not critical — happy to show you the patch, though!)

          This allows the server to chop up bits of video for when the client/browser isn’t able to use normal HTTP/1.1 “byte range request” to jump around (even ahead to “unbuffered” parts in the video).

          *Another* great thing about mod_h264_streaming is kind of subtle — for very long vids. With mp4 files, the moov header of the keyframes and video info can get quite big — we clock it at around 1MB for every 30 minutes of video. and all that has to be downloaded/parsed by a video player *first* before the video will start playing a single frame. So for a 2-hour program, that gets very big! So, for example, for our latest project:

          we use the mod_h264_streaming *always* so that it (and this is the C compiled module compiled into nginx)
          opens the .mp4 on the server, finds the 3o-seconds to play, fakes/remakes a new mp4 header and moov atom, and writes that new header and video info to the client request. That way, it starts *super* fast, since the video header is very very small now (even for a 4 hour news program).

          hope that helps!
          best regards and let me know how it goes…

          1. Brandon

            Hi Tracey,

            Kurt and I are both working on getting the MP4s streaming. We are using the Apache version of the H264 Streaming Module. We have tested our own videos with it, and even downloaded one of the iArchives 30 min videos to test as well. The issue we are having is that the audio starts playing as soon as you click play, or jump ahead to a new time before the video has downloaded, but the video doesn’t start playing or catch up until the entire video finishes downloading. From what I understood in your last post, you don’t have those issues, and you don’t use anything on top of that module to help streaming. Do you have any idea what might cause the entire video to have to download before the video syncs up with the audio? We have used qt-faststart and I’ve verified with a hex editor that the metadata is at the front of the file.

          2. Tony

            Do you have the mod_h264 patch you mentioned? I am looking to do something similar — C compiled mod_h264 compiled into nginx…

  6. traceypooh Post author

    weird, do you have a link that i could take a look at
    and/or can you mention the IA video you tested, too (just so i can verify it’s one of our standard derivatives)?

    does “clicking” directly on the .mp4 in question (served directly from apache) in something like safari or chrome start playing immediately + correctly?

    1. traceypooh Post author

      well, in safari and chrome (on mac) at least for me, it starts playing after a short delay (likely reading the moov atom semi-large header at the front of the 30 min video (we see about 1MB per 30 minutes of .mp4 video here)), and then it plays in sync relatively soon after that.

      firefox *does* seem to be downloading the entire file first, before anything starts (but then it starts and plays in A/V sync fine).

      i’m *guessing* the moov atom in the front is maybe not in the front or has some kind of minor issue??

      actually, i’m not sure what firefox status is now w/ .mp4 — maybe it’s playing for me via some kind of quicktime passthru on my mac?

      can you try that link in chrome?

  7. Brandon

    I’m actually on Ubuntu, and Chrome and Firefox start the video playing right away for me. But when we use any sort of player in Firefox, the entire video has to download before the video syncs up with the audio. If we use Chrome and jwplayer, it seems to work fine though…

    I guess it was foolish of me to think the players would work the same across all platforms. I’m still not sure why it works for some and not for others though. I guess we will trying tinkering with the metadata, but when I look at the video file with a hex editor, the string “moov” appears at the beginning of the file.

  8. Brandon


    Other browsers weren’t working because of user error. My version of flash wasn’t working properly for Firefox on Ubuntu, apparently, so I re-installed that and it works fine.

    Also we thought it wasn’t working in IE either, but it was because when we were trying to emulate IE 8 and IE 7, we were doing it using the IE 64 bit version, which automatically tries to use HTML5. When we ran the 32 bit verion of IE and emulated the older versions (IE 7 and IE 8), it worked perfectly.


  9. traceypooh Post author

    Some minor updates to the example (for the 3-commands) which work with current built head of ffmpeg tree (as of Nov2, 2011):

    ffmpeg -deinterlace -y -i ‘camels.avi’ -vcodec libx264 -fpre libx264-IA.ffpreset -vf scale=640:480 -r 20 -threads 2 -map_metadata -1,g:0,g -pass 1 -an tmp.mp4;

    ffmpeg -deinterlace -y -i ‘camels.avi’ -vcodec libx264 -fpre libx264-IA.ffpreset -vf scale=640:480 -r 20 -threads 2 -map_metadata -1,g:0,g -pass 2 -map 0:0 -map 0:1 -acodec aac -strict experimental -ab 128k -ac 2 -ar 44100 -metadata title=’Camels at a Zoo –‘ -metadata year=’2004′ -metadata comment=license:’’ tmp.mp4;

    qt-faststart tmp.mp4 ‘camels.mp4’

  10. mocha

    I hate to communicate via a blog comment system, but do you have the latest method you use for encoding to Theora using current ffmpeg builds? The last method posted on this blog doesn’t work anymore. Thanks.

  11. traceypooh Post author


    this is clipped from our bash script install (so you may need to minor tweak it).
    the gist is we are using the nginx v0.8.54-4 package from ubuntu as the base to compile in the mp4 support. so once you have something like that and/or likely the current head of nginx source setup….

    # add in ability to jump directly into the middle of a h.264 mp4 video..

    UBU_NAME=`fgrep CODENAME /etc/lsb-release |cut -f2 -d=`; # eg: “lucid”
    if [ “$UBU_NAME” == “lucid” ]; then

    if [ “$SHOPSVN” == “1” ]; then
    rm -rf nginx_http_h264_streaming;
    svn export $SHOP/nginx nginx_http_h264_streaming;
    svn export –force $SHOP/mp4split nginx_http_h264_streaming;
    wget -N $SHOP;
    zcat $(basename $SHOP) | tar xf -;
    rm -rf nginx_http_h264_streaming;
    mv $(basename $SHOP .tar.gz) nginx_http_h264_streaming;

    # tracey aug2009
    # we have some .mp4 user uploaded videos that dont work w/ mod_h264_streaming.
    # so if there are no “start=” and no “end” args, we’ll return video instead AS IS.
    rsync -av –delete nginx_http_h264_streaming/ /tmp/ngx-h264-orig/;
    # NOTE: 1st replacement (for =natty)
    # NOTE: 3rd replacement (for >=natty) apparent bug since removed after nginx v0.7
    perl -i \
    -pe ‘s/(double end.*)/$1 if (start<=0.0 && endstartendzero_in_uri/0/;’\
    nginx_http_h264_streaming/src/ngx_http_h264_streaming_module.c \
    # verify a change was made!!
    diff -r nginx_http_h264_streaming /tmp/ngx-h264-orig | egrep .;
    diff -r nginx_http_h264_streaming /tmp/ngx-h264-orig | fgrep DECLINED;

    # then compile your nginx source w/ this extra flag


    hope that helps/works! 😎

    1. Tony

      Thanks Tracey. I’m looking at the ngx_http_streaming_module.c file now and I am trying to figure out what your script patches. I guess I have to translate this command, which doesn’t want to run on my system:

      perl -i \
      -pe ‘s/(double end.*)/$1 if (start<=0.0 && endstartendzero_in_uri/0/;’\
      nginx_http_h264_streaming/src/ngx_http_h264_streaming_module.c \

  12. traceypooh Post author


    OK, how about trying this as a patch.

    diff -U3 -r nginx_http_h264_streaming/src/ngx_http_streaming_module.c /tmp/ngx-h264-orig/src/ngx_http_streaming_module.c
    --- nginx_http_h264_streaming/src/ngx_http_streaming_module.c 2012-07-22 22:07:36.488683952 +0000
    +++ /tmp/ngx-h264-orig/src/ngx_http_streaming_module.c 2010-01-09 20:32:24.000000000 +0000
    @@ -155,7 +155,7 @@

    /* TODO: Win32 */
    - if (0)
    + if (r->zero_in_uri)
    return NGX_DECLINED;
    @@ -174,7 +174,7 @@
    return NGX_DECLINED;

    - if (options->startend<=0.0) return NGX_DECLINED; last = ngx_http_map_uri_to_path(r, &path, &root, 0);
    + last = ngx_http_map_uri_to_path(r, &path, &root, 0);
    if (last == NULL)

    1. Tony

      Thanks Tracey, I finally got a chance to try it and that code helped a lot. It gave me an error about “startend” not being a variable or something, so I split up the condition into two parts:

      if ((options->startend<=0)) return NGX_DECLINED;

      After that, it compiled fine and seems to be working.

      I also just found a different nginx mp4 streaming module — curious if there are any differences?

      1. traceypooh Post author

        hi Tony,
        this fell deep in my inbox queue…. 8-p

        yes, as far as I know those are different, yet similar mp4 streaming modules. we accidentally rebuilt our nginx for a short period with that mp4 module instead of our “mod_h264_streaming” one and had issues immediately.

        so I think the “mod_h264_streaming” is the way to go for now, at least.

Comments are closed.