Saturday, February 27, 2021 Also published on Medium A guide to using FFmpeg to create all the animated content you want. Whether it’s for a website, a presentation, or sharing a fun clip with a…
Saturday, February 27, 2021
Also published on Medium
A guide to using FFmpeg to create all the animated content you want.
Whether it’s for a website, a presentation, or sharing a fun clip with a friend on chat, you might want to convert a video to an animated GIF or animated WebP. Unfortunately, the visual tools for doing this vary by your operating system. Additionally, most conversion tools don’t support the WebP format, even in 2021. WebP is based on VP8, a relatively recent video codec standard compared to the GIF image format.
So, this guide is for those who are willing to learn a bit of terminal in order to convert any video to the animated format of their choosing. The best part: this will work on all major operating systems and gives you all the control of the output you could want!
Let’s get started!
To use this guide, you will need the following:
FFmpeg is a free and open-source software project consisting of a large suite of libraries and programs for handling video, audio, and other multimedia files and streams.
For our purposes, we will use it to convert between formats, such as videos to GIFs or animated WebP. It has many uses, so I recommend checking it out for all your video processing needs!
Since all of these commands require FFmpeg, we need to make sure it’s available.
Open your terminal, and run this:
If FFmpeg is available, you will note output similar to this:
FFmpeg version 4.3.1 Copyright © 2000–2020 the FFmpeg developers
...
Version 4 or higher of FFmpeg is recommended for this guide.
If you get an output that says something similar to command not found: ffmpeg -version
, then check the Prerequisites section above and make sure you have FFmpeg installed on your system.
Base command
ffmpeg -i $INPUT_FILENAME \
-vf "fps=$OUTPUT_FPS,scale=$OUTPUT_WIDTH:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop $NUMBER_OF_LOOPS $OUTPUT_FILENAME
# Change these placeholders:
# * $INPUT_FILENAME - path to the input video.
# * $OUTPUT_FPS - ouput frames per second. Start with `10`.
# * $OUTPUT_WIDTH - output width in pixels. Aspect ratio is maintained.
# * $NUMBER_OF_LOOPS - use `0` to loop forever, or a specific number of loops.
# * $OUTPUT_FILENAME - the name of the output animated GIF.
Example usage of this command
Here is an example of this command with the input options filled out:
ffmpeg -i "sample_recording.mp4" \
-vf "fps=10,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop 0 sample_recording.gif
Base command
This is the base command with various options for converting part of a video to an animated GIF:
ffmpeg -ss $INPUT_START_TIME -t $LENGTH -i $INPUT_FILENAME \
-vf "fps=$OUTPUT_FPS,scale=$OUTPUT_WIDTH:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop $NUMBER_OF_LOOPS $OUTPUT_FILENAME
# Change these placeholders:
# * $INPUT_START_TIME - number of seconds in the input video to start from.
# * $LENGTH - number of seconds to convert from the input video.
# * $INPUT_FILENAME - path to the input video.
# * $OUTPUT_FPS - ouput frames per second. Start with `10`.
# * $OUTPUT_WIDTH - output width in pixels. Aspect ratio is maintained.
# * $NUMBER_OF_LOOPS - use `0` to loop forever, or a specific number of loops.
# * $OUTPUT_FILENAME - the name of the output animated GIF.
Example usage of this command
Here is an example of this command with the input options filled out:
ffmpeg -ss 32.5 -t 7 -i "sample_recording.mp4" \
-vf "fps=10,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop 0 sample_recording.gif
Base command
This is the base command with various options for converting an entire video to an animated WebP. You can use options like FPS, output width, and quality to determine the file size and quality of your output:
ffmpeg -i $INPUT_FILENAME \
-vf "fps=$OUTPUT_FPS,scale=$OUTPUT_WIDTH:-1:flags=lanczos" \
-vcodec libwebp -lossless 0 -compression_level 6 \
-q:v $OUTPUT_QUALITY -loop $NUMER_OF_LOOPS \
-preset picture -an -vsync 0 $OUTPUT_FILENAME
# Change these placeholders:
# * $INPUT_FILENAME - path to the input video.
# * $OUTPUT_FPS - ouput frames per second. Start with `10`.
# * $OUTPUT_WIDTH - output width in pixels. Aspect ratio is maintained.
# * $OUTPUT_QUALITY - quality of the WebP output. Start with `50`.
# * $NUMBER_OF_LOOPS - use `0` to loop forever, or a specific number of loops.
# * $OUTPUT_FILENAME - the name of the output animated WebP.
Example usage of this command
Here is an example of this command with the input options filled out:
ffmpeg -i "sample_recording.mp4" \
-vf "fps=10,scale=720:-1:flags=lanczos" \
-vcodec libwebp -lossless 0 -compression_level 6 \
-q:v 50 -loop 0 \
-preset picture -an -vsync 0 sample_recording.webp
Base command
This is the base command with various options for converting part of a video to an animated WebP:
ffmpeg -ss $INPUT_START_TIME -t $LENGTH -i $INPUT_FILENAME \
-vf "fps=$OUTPUT_FPS,scale=$OUTPUT_WIDTH:-1:flags=lanczos" \
-vcodec libwebp -lossless 0 -compression_level 6 \
-q:v $OUTPUT_QUALITY -loop $NUMER_OF_LOOPS \
-preset picture -an -vsync 0 $OUTPUT_FILENAME
# Change these placeholders:
# * $INPUT_START_TIME - number of seconds in the input video to start from.
# * $LENGTH - number of seconds to convert from the input video.
# * $INPUT_FILENAME - path to the input video.
# * $OUTPUT_FPS - ouput frames per second. Start with `10`.
# * $OUTPUT_WIDTH - output width in pixels. Aspect ratio is maintained.
# * $OUTPUT_QUALITY - quality of the WebP output. Start with `50`.
# * $NUMBER_OF_LOOPS - use `0` to loop forever, or a specific number of loops.
# * $OUTPUT_FILENAME - the name of the output animated WebP.
Example usage of this command
Here is an example of this command with the input options filled out:
ffmpeg -ss 32.5 -t 7 -i "sample_recording.mp4" \
-vf "fps=10,scale=720:-1:flags=lanczos" \
-vcodec libwebp -lossless 0 -compression_level 6 \
-q:v 50 -loop 0 \
-preset picture -an -vsync 0 sample_recording.webp
This depends on the quality, size, and support you want for your output. Modern browsers have support for animated WebP and the quality tends to be higher, but the processing power required is also higher.
This guide serves as a brief introduction to using FFmpeg to create an animated GIF or animated WebP from a video, but there is so much more you can do with the tool. There are also many options that FFmpeg supports for these formats that are not covered.
You can also get the code for all the commands and examples in one place by visiting the GitHub Gist here.
Perhaps it's time to start a collection of ffmpeg commands for every situation, similar to bro pages [1]. I feel like I have to look at my old projects every time I need to use it. It took me a lot of trial and error to write an all-purpose ffmpeg command.
This is how I convert videos to 720p web-playable videos (if it's not already web-playable):
https://github.com/nicbou/homeserver/blob/22c0a160f9df5f4c34...
This is how I create hover previews like on modern streaming sites:
https://github.com/nicbou/timeline/blob/9d9340930ed0213dffdd...
There's always commandlinefu.com - https://www.commandlinefu.com/commands/matching/ffmpeg/ZmZtc...
The hover previews is an awesome example. Thanks for sharing!
I like collections of commands. However, the challenges that seem unsolved are (1) keeping the example in sync with the CLI options, and (2) making it easy to dig into parts of examples. The former is a classic documentation problem, of course.
Thanks for sharing!
I use a collection of commands in a dotfiles [1] repo I share around a few machines.
This [2] command, compresses any video into a small web-playable file with relatively high quality so you can quickly share HD footage.
ffcompress INPUT.mp4
# creates INPUT.compressed.mp4
[1] https://github.com/benwinding/dotfiles[2] https://github.com/benwinding/dotfiles/blob/master/bin/ffcom...
Something like FFmprovisr:
https://amiaopensource.github.io/ffmprovisr/
https://github.com/amiaopensource/ffmprovisr
(CC Licensed, they accept pull requests.)
---
The ffmpeg subreddit also has some helpful contributors:
this is a wonderful idea. we launched a repo here to do exactly this; we'll update the repo over time as we collect commands.
https://github.com/HotpotDesign/FFMpeg-Online
could we list and credit your commands?
Sure, they're open source for a reason!
The videoprocessing one is more mature. It's been converting various torrents movies to playable mp4s for a few years.
The 10x1s preview looks magical once you see it in production. It's also a great introduction to ffmpeg filter syntax, which really isn't that complex.
thanks! i just wanted to confirm your permission before posting on the repo. :)
on a related note, do you have a command for converting HTML5 animations into webp or mp4 videos?
I just noticed you have one for "Create single-image video with audio".
This seemingly easy task is actually pretty hard to get right with FFMPEG.
Just leave a few notes here if anyone want to use that.
Video part
Chroma sampling
Without any `pix_fmt` argument, FFMPEG defaults to 4:4:4 instead of typical 4:2:0. Which has very limited support (for example, Firefox doesn't support it.)
To fix: add `pix_fmt yuv420p`, or chain a `format=yuv420p` vf at the end to your other vf(s).
Color space
FFMPEG by default uses BT.601 when converting your RGB images into YUV. This is an issue if your image / output video is in HD resolution. Almost all video players (including browsers) would assume BT.709 for anything >=720P. This causes color to shift in playback(255,0,0 would become 255,24,0).
To fix: add `-vf zscale=matrix=709`.
Note: there are some other video filters can do the same. The most famous being good ol' `scale` (based on libswscale). However, it has a notorious bug that would cause the color to shift on its own (becomes yellow) if the input is in BGR (all the `.bmp`s). See: https://trac.ffmpeg.org/ticket/979 So stick with better `zscale`.
Framerate etc.
You can set framerate with `-r` to a small number for both input (reading the same image X times per second) and output (since it's a still image, you can get away with very low framerate. `-tune stillimage` should also be used for (default) x264 encoder.
In summary:
ffmpeg -loop 1 -r 1 -i image.png -i audio.wav -r 1 -shortest -vf zscale=matrix=709,format=yuv420p -c:v libx264 -tune stillimage output_video.mp4
Audio part - length mismatch bug
Even if we ignore all the image/video troubles above, `ffmpeg -loop 1 -i image.png -i sound.mp3 -shortest video.mp4` still doesn't work well. "-shortest" argument has a long-standing bug (https://trac.ffmpeg.org/ticket/5456) that is the output video would be longer than your input audio (by quite a few seconds, worse if using -r 1). There are some workarounds (listed in the ticket) but they don't eliminate the issue entirely.
Your best bet (if the length match is crucial) is to just convert the output video again and use -t to cut to the proper length.
I have one that is pretty usefull to me, which I use to convert MTS files from my Sony camera to mp4.
# batch convert MTS files into mp4 files for f in .MTS; do avconv -i "$f" -deinterlace -vcodec libx264 -pass 1 "${f%.MTS}.mp4";done
there are many such syntax generator websites for simple video conversion like tasks. see:
https://www.mrfdev.com/ffmpeg-command-generator
https://github.com/tldr-pages/tldr is a large collection
I switched to ffmpegging my gifs a year ago and haven't looked back. It's a bit annoying to begin with but now so much easier than using some 3rd rate program or having to upload everything to EZGif. It's worth the pain!
Author here. This is the same journey I went through and I 100% agree! It can be intimidating at first, but FFmpeg is a a worthwhile utility to begin learning.
I've also added some shell functions[0] to my dotfiles to make it a bit easier to use when switching between several different machines.
[0] https://gist.github.com/devadvance/03d3c8f57b3e0254fb989e946...
You've made a really handy cheat sheet, thanks! Bookmarked it for future use!
And yeah, it's great, and not just for gifs. Just quickly re-encoding something in a different format / container is so much easier than faffing around opening a proper program.
Took a while to not get annoyed with it though and figure out annoying watch-outs like -pix_fmt yuv420p, which I now finally have memorised.
If you start from .png files I struggled to get ffmpeg to produce a high quality gif, and had much more luck with imagemagick.
I found that gifski makes great quality animated gifs. ffmpeg is a mess to use for that. You still need ffmpeg to generate individual frames from a video, but that's a relatively easy step in comparison to that cumbersome set up of dithering for gif generation.