ffmpeg Basics and Recipes
toolingRecently I wrote about ffmpeg, a Swiss-army knife command-line utility for media. While I find it incredibly handy NOW, it took time to build up a working knowledge of the tool to handle certain tasks with confidence. The downside of having a versatile tool is that it can do an awful lot of things!
So let's break down how it works and I'll share some of my most common recipes for converting media.
Installing
The easiest way to install (on a Mac) is with homebrew:
brew install ffmpeg
You can also download directly from their site, but that's a tad more involved. Homebrew drops it into your path, ready to go.
Basics
An ffmpeg
command will take:
- An input file,
- several sets of options,
- and an output file to spit everything out into.
A basic command will look something like:
ffmpeg -i input-file {lots of options here} output-file
Compression
One of the handiest things ffmpeg
can do is give you fine-grained control over video compression. Not only can you pick the method of compression, you can choose what codec to use.
There are several methods of compression, but two of the main ones you'll see are:
-b:v
Sets a fixed bitrate of the output, measured in kb (ex. 1200kb) or megabytes (ex. 2M). You'll typically see values between 1200-6000kb. The lower the number, the less 'bits' will be used.-crf
Sets a Constant Rate Factor (available for x264, x265 and libvpx). This is a smart value between 0 and 51 that attempts to vary the bitrate per frame for better-looking results (less artifacts). For x264, sane values are between 18 (better quality, larger file) and 28 (lower quality, larger file). The default is 23, so you can use this as a starting point.
Bitrates can get quite complicated but give you a lot of control. They're useful in situations for streaming videos or where you need to specify a minimum, maximum, or ensure a constant bitrate. Because I use ffmpeg
to operate on smaller videos I gravitate towards -crf
values.
Read more on how CRF works.
Convert to h264 mp4
If you need to share a video, you can't go wrong with an mp4. It's widely supported and everything except ancient devices should be able to play it. Here's a good default compression recipe:
ffmpeg -i input.mov -c:v libx264 -crf 28 -preset very slow output.mp4
Option descriptions:
-c:v
selects the compression format tolibx264
-crf 28
sets a decent constant rate factor.-preset
sets a series of options that will provide a certain encoding speed to compression ratio, slower speeds mean more compression.
One benefit of H264 compression is it's FAST and pretty efficient. I can usually get a .mov
file down to about 10% of its size with this recipe.
Read more about H264 compression.
Convert to webm
webm is now fully supported in modern browsers but is a little more finicky to encode, it's quite easy to set some options and wind up with large webm files. I find webm's to be slightly larger than well-compressed mp4s.
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 output.webm
-c:v
selects the compression format tolibvpx-vp9
which is the latest webm codec.-b:v
is set to 0 (required) to allow constant quality settings with:-crf 30
which follows the CRF numbers outlined above
They recommend that you use a two-pass method on webm for the best results, but it is QUITE slow, so grab a coffee while you wait:
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 1 -an -f null /dev/null && \
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 30 -pass 2 -c:a libopus output.webm
-pass {number}
lets it know that we're doing some multipass business here.-f
forces output, in this casenull
to prevent the first pass from outputting anything&&
is a normal bash/zsh command to run the second command if the first command is successful.
Read more on VP9 compression here.
Heads Up!
When you do a two-pass encoding, the first pass will LOOK like nothing is happening, just be patient, it will eventually start showing progress and spit out your file.
I'll also state that I haven't found much difference between the single pass and the double pass methods, but I also think I'm not operating at the size and length of video files to see the difference. If it's a smaller file, the single pass is probably good enough for your needs.
Common Recipes
Now we've talked about compression, what are some other things you can do? Here are a few of the tasks I find myself running.
Scale Video by Height
One of the easiest ways to shrink file size is to reduce the size of the video, especially if you've got a mondo-sized source file. To produce a 1080p video:
ffmpeg -i input.mp4 -vf scale=-1:1080 output.mp4
-vf
is an alias for-filter:v
(video filter)scale=-1:1080
resizes the video to ''whatever'' (the -1) by 1080 pixels high. If the video is 16:9, the width would then be 1920. If you ran-1:720
the output would be 1280x720 for a 16:9 video.
Note: Common resolutions for 16:9 video are: 1024Γ576, 1152Γ648, 1280Γ720, 1366Γ768, 1600Γ900, 1920Γ1080, 2560Γ1440 and 3840Γ2160. You can use these as some numbers to start with.
Trim Video by Timecode
Sometimes you only need a slice of a video, or you need to just shave off the first few seconds of something. Here's how you trim by timecode:
ffmpeg -i input.mp4 -ss 00:01:02.500 -t 00:01:03.250 -c copy output.mp4
-ss
indicates start time in seconds (with decimals for sub-second times)-to
is the end time inhh:mm:ss
format-c
copies existing formats
ffmpeg -i input.mp4 -ss 00:00:03 output.mp4
- Not providing a
-to
means it runs to the end of the file. This example will trim off the first 3 seconds of the file.
Remove Audio
ffmpeg -i example.mp4 -c copy -an example-nosound.mp4
-an
removes audio-c
copies existing formats
Boost Volume
Note that this can work on audio files, not just videos.
ffmpeg -i input.wav -af "volume=1.5" output.wav
-af
set audio filtersvolume=1.5
and boost the volume to 150% of the original. This can be proportional or in decibels:volume=-5dB
Create a Poster Image
Embedding an HTML video and don't have a poster image? Rather than trying to awkwardly screengrab something, you can use this to create an image at a specific timestamp.
ffmpeg -i example.mp4 -ss 00:00:00.000 -vframes 1 'thumb.png'
-ss
seeks to where weβre pulling a frame (in this case, the start of the video)-vframes
sets the number of frames to output*.png
(in output file extension) converts it to a PNG image
Advanced Recipes
Here are some of the more complicated recipes I use that string together some of the operations I've already covered.
Create Ambient Video MP4
Need to load up a looping ambient video, but you're given a 200+MB file?
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset veryslow -vf scale=-1:720 -movflags +faststart -an output.mp4
- Uses H264 compression
- Uses a Constant Rate of 28
- Uses the
veryslow
preset for better compression - Reduces to 1080x720 dimensions
-movflags +faststart
moves the metadata to the front of the video allowing immediate streaming (rather than waiting for the whole file to load)- Removes audio channels since it will be playing muted.
GIF to MP4
Want a GIF, but not with the filesize overhead?
ffmpeg -i animated.gif -movflags +faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" video.mp4
-movflags +faststart
to move metadata to the front for streaming-pix_fmt yuv420p
use the pixel format ofyuv420p
which is a chroma subsampling scheme.-vf
starts a filter. The formula in the string will upscale the resolution by 2x.
MP4 to GIF
Going the other way is a bit more involved because we need to take a full-color video and restrict it to 256 colors for a GIF. First, we'll need to generate a color palette from the video:
ffmpeg -i input.mp4 -y -ss 10 -vf fps=10,scale=320:-1:flags=lanczos,palettegen palette.png
- Generates a PNG color palette
-ss
seeks to timecode for what frame to sample the palette from
Then, using that palette, we'll convert the video into the best-fit gif.
ffmpeg -i input.mp4 -i palette.png -filter_complex "fps=10,scale=-1:480:flags=lanczos[x];[x][1:v]paletteuse" output.gif
- Notice the two inputs, we grab the video and the palette
-filter-complex
Sets up a filter that: sets the fps to 10, scales it to 480 pixels high, and uses the palette we specified earlier.
Max Compatibility
I hardly use this recipe anymore since MOST devices can handle mp4's just fine. But just in case you need to get something running on an older device:
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -profile:v baseline -level 3.0 -pix_fmt yuv420p -c:a aac -ac 2 -b:a 128k -movflags +faststart output.mp4
- H.264 video and AAC audio is the best combination for broad support. Ancient browsers that do not have H.264 decoders however will need a VP8/Vorbis video.
- The
-profile:v
baseline and-level 3.0
options are only needed for old mobile devices that cannot handle CPU-intensive features of H.264. - Uses a constant rate of 23.
-movflags +faststart
moves the metadata to the front of the video allowing immediate streaming
If you've come across other recipes, or you see a way how these could be improved, shoot me a note! I'm constantly improving and adding to this list.
Newsletter
Want to receive these thoughts and others in your inbox?
Discussion
Want to discuss this? Email Me