I have a couple of one-offs that I use when working with the videos that I’ve taken. I either need to do one thing or another, but I keep coming back to a couple basic actions.
This is my dump of my FFmpeg shorts.
Animated Gifs
Usually I’m creating “many” animated gifs, and I wasn’t exactly sure of dimensions, size, or framerate so a loop was always relevant. This typically gets embedded in a bash script, where $1 is the parameter passed into the script. Adjust the HEIGHT or FPS variables as needed, but these values always gave me good results.
for HEIGHT in "450" "300" "150"; do
for FPS in "15" "10" "5"; do
ffmpeg -i "$1" -filter_complex "[0:v] fps=$FPS,scale=$HEIGHT:-1:flags=lanczos,split [a][b];[a] palettegen [p];[b][p] paletteuse" "$1"_"$HEIGHT"_"$FPS".gif
done
done
Infinite Loop
Good, short videos, especially for website backgrounds, can get looped easily and cleanly with the right set of commands. It’s really a 2-step process: (1) reverse the video, (2) concatenate the videos. $1 being the source filename.
# make reverse
ffmpeg -i "$1" -vf reverse rev_"$1"
# ffmpeg
ffmpeg -i "$1" -i rev_"$1" \
-filter_complex "[0:v:0][1:v:0]concat=n=2:v=1[outv]" \
-map "[outv]" vidlooper.mp4
There are two ways to do the concatenation, with the first using the “concat” filter and a list of files as the input. The second way is the recommended way to concatenate files of separate codecs which has better results than using the filter alone.
Streaming Video
This one got a bit complicated the more I learned of it. The final result is so I can use video.js to stream large videos, like a vimeo or youtube channel.
The input is $1, and two additional parameters I’ve adapted: $VIDEOFOLDER is the name of the folder to place all files, and $FILENAME9 is the first 9 digits of the file hash of the video to make the end result filenames unique across a large swatch of videos I’ve had to encode.
ffmpeg \
-i "$1" \
-filter_complex '[0:v]split=1[v1]; [v1]copy[v1out];' \
-map [v1out] \
-c:v:0 libx264 \
-x264-params nal-hrd=cbr:force-cfr=1 \
-b:v:0 5M \
-maxrate:v:0 5M \
-minrate:v:0 5M \
-bufsize:v:0 10M \
-preset slow \
-g 48 \
-sc_threshold 0 \
-keyint_min 48 \
-map a:0 \
-c:a:0 aac \
-b:a:0 128k \
-ac 2 \
-f hls \
-hls_time 15 \
-hls_playlist_type vod \
-hls_flags independent_segments \
-hls_segment_type mpegts \
-hls_segment_filename "$VIDEOFOLDER/$FILENAME9-data%04d.ts" \
-master_pl_name "$FILENAME9-master.m3u8" \
-var_stream_map v:0,a:0 \
"$VIDEOFOLDER"/"$FILENAME9".m3u8
Removing Audio Tracks
Sometimes I come across videos where there are multiple embedded tracks, such as two separate audio tracks or multiple subtitles. To some this is gold, but to me I just need to clear out some cruft that I’ll never use. The following example uses Map to remove “input audio index 0” (1st audio stream) using negative mapping and keeps the remainder of mappings (video, subtitles, etc) with “select all streams”
ffmpeg -i input.mp4 -map 0 -map -0:a:0 -c copy output.mp4
Cropping Video
I have some small projects that require subsections of video to be cropped out. Using a Video Filter makes this real easy.
ffmpeg -i input.mp4 -vf "crop=600:600:100:0" output.mp4
Crop takes two or four parameters. With 2 it’s centered on the input video, width and height. With 4, it’s the Top-Left coordinate followed by width and height. It’s quite versatile for the purposes I need.
2021 Honda Odyssey Entertainment System
I spent quite some time figuring out how to convert my collection of videos to playable formats so my Honda Odyssey didn’t crash when playing them. In fact, I documented this many times before until I had the final breakthrough thanks to a multi-track audio that would cause the car player to reboot the entire system when it queued it up.
Trial-and-error is still one of the best way to fix things: Re-optimizing Video: 2021 Honda Odyssey
Video Dimensions
Programmatically I can pull dimensions of a video with ffprobe, and even determine if the multimedia file is a video or not due to the lack of data returned. The only data returned is comma-separated.
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 "$1"