Making proper MP4 files with ffmpeg

29 Jun 2017

I recently had reason to convert some video files to a format that would be friendly to iDevices, and so I spent some time figuring out a formula that worked nicely. Now you can use it, if you like, so you don’t have to do the same trawling through the internets.

Here’s the command, in bash format:

vid2m4v () {
    ffmpeg \
        -i $1 \
        -vcodec libx264 -crf 20 -preset slow \
        -acodec libfdk_aac -vbr 4 \
        -pix_fmt yuv420p \
        -movflags +faststart \
        -profile:v high -level 4.2 \

I’ll go through the options line by line. I’m not going to include the continuation character (\) since that gets tiresome. You know what to do with those already, I hope.

vid2m4v () {

This just defines a bash function, so you can call it nicely instead of having to remember all the options every time.


The name of the command, of course.

-i $1

This tells ffmpeg which file to use as input. The $1 means, use the first argument to the function. So you would call the bash function like vid2m4v my_cool_video_file.avi or whatever.

-vcodec libx264 -crf 20 -preset slow

OK, now we’re getting into the real stuff. This tells ffmpeg to use the libx264 encoder for the video channel (thus the vcodec bit), meaning we want H.264 video, which iDevices like.

The -crf 20 part indicates the Constant Rate Factor, which basically just means what quality level we want. The range varies for this parameter, depending on what codec you’re using, but 20 is a nice reliable value.

The -preset slow part tells ffmpeg how hard to work on squeezing our precious video down before writing it into the output file. There are a bunch of different presets, but the slow one is my favorite.

-acodec libfdk_aac -vbr 4

This line tells ffmpeg to use the FDK AAC encoder for our audio channels, and to use variable bit rate encoding, with a quality level of 4, which is pretty good without being obsessive about it. You may not have the FDK library compiled into your version of ffmpeg, since it has had some legal shenanigans swirling around it in the past, thanks to software patents and all the nastiness that they entail. If ffmpeg complains that it doesn’t know what the libfdk_aac encoder is, look at the ffmpeg help page listed below to find which AAC library to use instead.

-pix_fmt yuv420p

This line specifies which format of pixels ffmpeg should use when writing out the video. Apple devices only recognize this format, apparently, so it’s kind of important to specify it here.

-movflags +faststart

This line enables “web-optimized” MP4 output–in other words, it moves the metadata for the file to the beginning so the player knows the important stuff before the rest of the file has to be downloaded.

-profile:v high -level 4.2

This line specifies which kind of H.264 video we want, compatibility-wise. This particular specification is for fairly recent devices (iPhone 5s and later, for example.) You may need to adjust this if your video might be viewed by people with older hardware. See the ffmpeg help page listed below for details.


This bizarre-looking piece of line noise (just dated myself I guess) is a bit of bash syntax that chops off the file extension to keep things tidy, and then adds on the m4v extension that belongs with the kind of H.264/AAC data we’re generating. You can use that part before the .m4v in other bash scripts too–there’s a whole range of wonderful weirdness you can do with bash to extract various parts of a variable’s value. Google “bash parameter expansions” for all the gory details.

OK, that should work. At least it did for me. Hopefully it produces decent-quality video for you.

Here are the aforementioned ffmpeg help pages in regard to H.264 and AAC encoding:



Happy encoding!

Update 2018-05-03: For more detail than you ever wanted to know about ffmpeg settings for good H.264 encoding results, take a look at this article from Jason Robert Carey Patterson.