Video transcoding with FFmpeg

Using the FFmpeg command line to convert MP4, OGV, and WebM video.

This quick guide goes through some of the details on how we convert some video using FFmpeg. Video conversion is really important for those who are hosting their own files and not relying on a third party site such as YouTube or Vimeo because you are looking for the most compatible video standards that offer the best quality at the lowest bandwidth so that no only your server is happy but the users that visit your site. FFmpeg is awesome in respects that since it’s open-source it’s a binary that can be compiled on a remote server that you own and begin converting videos on the spot if necessary and if you have the CPU power to do it. You could even run FFmpeg on a Raspberry Pi if you wanted to wait a few years for the results! The real drawback is FFMpeg is pure command line with no GUI which may scare some people away. We have other blog articles talking about GUI video conversion later on for those who are interest.

Making videos via command line / FFmpeg.

All of the examples given below are strictly ones which worked out well for me and was discovered through going through the FFmpegs documentation on their website.

Prepping FFMpeg onto your linux box.

sudo apt-get install ffmpeg libav-tools

This is the command that we passed to get it working on our raspberry Pi. It should also work with many linux environments that use the apt command such as debian. Windows users can simply go to the FFMpeg site and download the stock binaries. We will be avoiding any self-compiling here out of simplicity. Since we do not own a mac we are unable to provide instructions as to what to do on on OSX.

Test video download.

Download softwareFor our test video we shall be using something from our own site which is the Ukidig-HD.mp4 file which was recorded in dual-pass 25-35mb stream giving the maximum quality. This file is rather large at 290+ megs in size. You are welcome to download and follow along with this guide if you wish.

Making an MP4 file for web streaming.

ffmpeg -i Ukidig-HD.mp4 -c:v libx264 -profile:v high -preset slow -b:v 1500k -maxrate 2500k -bufsize 5000k -threads 0 -codec:a aac -b:a 128k Ukidig.mp4

Single-pass MP4 encoding example.

The flag “-i” specifies the input file which in this exampled I used my high quality MP4 files of Ukidig. The “-c:v libx264” specifies that we want to make a .h264 compliant video file. “-profile:v high” and “-preset slow” ensure to get the best quality image which means it may take some time depending on the processor core that you are using. “-b:v 1500k” sets our variable bit-rate to 1500kbs or 1.5M for general streaming on the web. “-maxrate 2500k” acts as a throttle is case there is a lot of action on the screen. “-bufsize 5000k” is based off of maxrate 2x and typically is used for streaming media such as gaming. But in WordPress this is helpful too as it gives us a minimum two second buffer in the event of latency or lag. ” -threads 0″ is to use all of the processor cores in a system, you can set this however you want. “-codec:a aac” is our audio codec. Many people will demand libfdk_aac but due to licensing problems the only way you can get that is by compiling ffmpeg yourself. The default AAC codec is really good and works well with the h264 codec. “-b:a 128k” The bit-rate of the audio. For voice and narration you can set it down to 64k or 96k to save to space. But since this has music we keep it higher at 128k. Phew! That was a lot of crap going on! But that is unfortunately the nature of MP4 and compressing it down to something smaller for web broadcast!

2-pass encoding of MP4.

2-padd encoding tends to give better quality renders of your video but may also increase the size of your file by approximately 10-20 percent. Here’s how that is done.

In windows:
ffmpeg -i Ukidig-HD.mp4 -c:v libx264 -profile:v high -preset slow -b:v 1500k -maxrate 2500k -bufsize 5000k -threads 0 -pass 1 -an -y -f mp4 NUL
ffmpeg -i Ukidig-HD.mp4 -c:v libx264 -profile:v high -preset slow -b:v 1500k -maxrate 2500k -bufsize 5000k -threads 0 -pass 2 -codec:a aac -b:a 128k -f mp4 Ukidig.mp4
del ffmpeg2pass-0.log

The -y flag is to force overwrite of “NUL” so it doesn’t ask us if we’re sure about this. You don’t have to delete the log file that it generates as it overwrites every-time you perform a -pass 1. But we just like our script to clean up a little once done.

In linux:
ffmpeg -i Ukidig-HD.mp4 -c:v libx264 -profile:v high -preset slow -b:v 1500k -maxrate 2500k -bufsize 5000k -threads 0 -pass 1 -an -f mp4 /dev/null && \
ffmpeg -i Ukidig-HD.mp4 -c:v libx264 -profile:v high -preset slow -b:v 1500k -maxrate 2500k -bufsize 5000k -threads 0 -pass 2 -codec:a aac -b:a 128k -f mp4 Ukidig.mp4

Yes, you are passing the command twice. The first pass generates a ffmpeg2pass-0.log file that FFmpeg then uses during its second pass to determine how to best optimize the mp4 file.

S, does it really matter? I mean, an encoder is an encoder right?

FFmpeg vs Adobe Premiere comparison. Initially we thought that way. Until we decided to re-encode one of our old videos. The XBCD V2 tutorial to be exact. Upon stopping both videos with VLC on 00:15 and taking a snap shot of my web-page at 1,500kbs or 1.5M. The constrast was startling. Even with doing a single pass render versus a 2-pass render in premiere it was still clearer. To add insult to injury. the Premiere file was 75.4Megs in size. the FFmpeg was 67.9Megs in size. This changes the way I encode things for my website and will be going through all of my master files to re-encode them for better-faster quality.

Making Webm files with FFMpeg.

ffmpeg -i Ukidig-HD.mp4 -c:v libvpx-vp9 -b:v 1000k -c:a libvorbis -b:a 96k -threads 0 Ukidig.webm

The flag “-i” specifies the input file which in this exampled I used my high quality MP4 files of Ukidig. The “-c:v libvpx-vp9” represents the video codec we will be using. Understand that you will need a very new version of FFMpeg to use the VP9 Library. Older FFmpeg that are in Debian distros will only have VP8 which is “-c:v libvpx.” We then specify a variable bit-rate of 1000k or 1M with “-b:v 1000K” which works very well when doing desktop based tutorials. For gaming or high action at 1080p you may want to increase this value to 2000k or more. Finally, the audio is compressed in vorbis which is traditional for WebM files with “-c:a libvorbis.” Bit-rate for the audio is set for 96k “-b:a 96k” as vorbis/ogg audio.

2-pass WebM encoding.

These commands are similar to MP4 but 2-pass encoding.

In Windows:
ffmpeg -i Ukidig-HD.mp4 -c:v libvpx-vp9 -b:v 1000k -c:a libvorbis -b:a 96k -pass 1 -threads 0 -y -f webm NUL
ffmpeg -i Ukidig-HD.mp4 -c:v libvpx-vp9 -b:v 1000k -c:a libvorbis -b:a 96k -pass 2 -threads 0 Ukidig.webm
del ffmpeg2pass-0.log
In linux:
ffmpeg -i Ukidig-HD.mp4 -c:v libvpx-vp9 -b:v 1000k -c:a libvorbis -b:a 96k -pass 1 -threads 0 -f webm /dev/null && \
ffmpeg -i Ukidig-HD.mp4 -c:v libvpx-vp9 -b:v 1000k -c:a libvorbis -b:a 96k -pass 2 -threads 0 Ukidig.webm

2-pass encoding is important with WebM as only using single pass gives inconsistent sizes and quality issues when using a variable bit-rate. By using a 2-pass system we can ensure that the video will have consistent quality throughout the entire encoding process. For more information check out the VP8 and VP9 documentation. There’s also better documentation on the WebM project Wiki on how to use FFMpeg and WebM.

Making OGV files with FFMpeg.

ffmpeg -i Ukidig-HD.mp4 -c:v libtheora -b:v 4000k -c:a libvorbis -b:a 96k -threads 0 Ukidig.ogv

This is not the usual way you’re supposed to pass the command to make an OGV file! We’re specifying bit-rate with “-b:v 4000k” for 4000kbs transfer giving us a predictable file size for our blog. Ogg-theora by design uses scale levels of compression versus bit-rate. If you are not careful and specify a bit-rate too low such as avconv’s default 200k. You will receive an unwatchable ogv file with frame skipping especially at 1080p. Audio is also limited to a 96k stream “-b:a 96k” because in our humble opinion ogg audio tends to handle a lot nicer then mp3. A more civilized way of making an ogv file is as follows:

ffmpeg -i Ukidig-HD.mp4 -codec:v libtheora -qscale:v 5 -codec:a libvorbis -qscale:a 3 Ukidig.ogv

By using “-qscale:v 5” and “-qscale:a 3” of OGV ensures that the video will be compressed on a more exacting measure of quality over compression. However doing it this way may lead to some unpredictable file sizes especially when working with large resolution files like 1080p. Using a video qscale of 5 is even a little low for 1080p@60fps but it keeps the bandwidth in check on my site when showing something high action. Also, the percentage of users falling all the way back to OGV is incredibly low which is why we aren’t overly concerned about the quality. If you specify a qscale level. FFmpeg will then ignore variable bit-rate commands “-b:v” you pass to it in favor of scale settings.

Desktop tutorial settings.
ffmpeg -i Ukidig-HD.mp4 -r 30 -s 1280x720 -codec:v libtheora -qscale:v 5 -codec:a libvorbis -qscale:a 3 Ukidig.ogv

When encoding desktop videos such as the many tutorials on my site. We found it to be a lot better to simple reduce the resolution with “-s 1280×720” to bring it down to 720p. Also drop the frame rate down to 30fps with “-r 30” in order to half the framerate to keep the size low. Although a lot of the videos were shot at 1080p the desktop becomes a lot more readable and the size of the OGV becomes something that is easier to handle on this site versus fighting with a blocky 1080p OGV file.

2-pass OGV encoding?!?

Yes, it’s possible. But there wasn’t much of a perceivable difference when we tried it out.

For Windows:
ffmpeg -i Ukidig-HD.mp4 -c:v libtheora -b:v 2000k -c:a libvorbis -b:a 96k -pass 1 -threads 0 -y -f ogv NUL
ffmpeg -i Ukidig-HD.mp4 -c:v libtheora -b:v 2000k -c:a libvorbis -b:a 96k -pass 2 -threads 0 Ukidig.ogv
del ffmpeg2pass-0.log
For Linux:
ffmpeg -i Ukidig-HD.mp4 -c:v libtheora -b:v 2000k -c:a libvorbis -b:a 96k -pass 1 -threads 0 -y -f ogv /dev/null && \
ffmpeg -i Ukidig-HD.mp4 -c:v libtheora -b:v 2000k -c:a libvorbis -b:a 96k -pass 2 -threads 0 Ukidig.ogv

2-pass mode really does not like the -qscale command. 2-pass however compatible with -r 30 -s 1280×720 for those interesting in slowing frame-rate and resizing in order to save disk space. The only observed difference is it tends to smooth out the key-frames a little. But that’s about it. For more information check out the OGV documentation.

Update 2/27/2019

FFMpeg OGV Failure.It has come to our attention after updating FFMpeg that for some reason our OGV encoding is broken in the latest build. The error is as follows:

[libtheora @ 0000000002f08780] theora_encode_packetout failed [-1]
Video encoding failed
[libvorbis @ 0000000002f09b00] 39 frames left in the queue on closing
Conversion failed!

Download softwareWe ultimately had to go back to a previous build for this and we backed up that very build in case you wish to download this. Since the OGV format is not going to be going through any advancements in the near future what we did is renamed ffmpeg.exe as ffmpeg-old.exe and copied that into my new build folder. using ffmpeg-old.exe for just the OGV transcoding. We hope that they fix this bug issue. In the meantime you could at least download our version by clicking on the red icon in this paragraph. Here’s the version information of the ffmpeg that worked:

ffmpeg version N-85750-ga75ef15 Copyright (c) 2000-2017 the FFmpeg developers.
built with gcc 6.3.0 (GCC)
configuration: --enable-gpl --enable-version3 --enable-cuda --enable-cuvid --e
nable-d3d11va --enable-dxva2 --enable-libmfx --enable-nvenc --enable-avisynth --
enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv
--enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-li
bfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug -
-enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enabl
e-libopenh264 --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-li
bsnappy --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolam
e --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx
--enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable
-libxavs --enable-libxvid --enable-libzimg --enable-lzma --enable-zlib
libavutil 55. 61.100 / 55. 61.100
libavcodec 57. 93.100 / 57. 93.100
libavformat 57. 72.101 / 57. 72.101
libavdevice 57. 7.100 / 57. 7.100
libavfilter 6. 88.100 / 6. 88.100
libswscale 4. 7.101 / 4. 7.101
libswresample 2. 8.100 / 2. 8.100
libpostproc 54. 6.100 / 54. 6.100
Hyper fast Audio and Video encoder

We hope this helps you.


Leave a Comment to the Void