ffmpeg Crush (use ffmpeg to reduce video file size)
(Originally uploaded as a Github Gist)
This is a function from one of my dotfiles.
It’s been very useful on QuickTime/iMovie exports of screencaps for bug reports, etc.
Currently it preserves aspect ratio, and sets some maximums for video and audio output settings: 720p, 24fps, 44.1 audio (hardly touched).
It’s still a work in progress, and if you have suggestions for improvements, they are welcome!
Example of a recent workflow:
- Take QuickTime screen capture: 198.2MB
- Edit a bit, and export from iMovie: 38.3MB (okay, not bad!)
ffmpeg_crush
it: 2.7MB! For a 720p video, ~2m45s long.
# Use ffmpeg to compress a given video to a 24fps mp4.
# It uses slow compression to get the smallest FILE SIZE.
# This has been incredibly helpful when I've exported screen
# casts from QuickTime or iMovie. There's usually no need
# for 1080p60fps for a bug report. 😅
# The maximum output size will be 1280w||720h, preserving aspect ratio.
# Optionally adding single-pixel black padding if the video size is
# an odd number of pixels (which makes h264 sad).
ffmpeg_crush() {
# If no arguments are passed, show Usage message.
if [[ $# -eq 0 ]]; then
echo "Usage: ffmpeg_crush input_video_file"
return 1
fi
# Check if input file exists
if [[ ! -f "$1" ]]; then
echo "Could not find the provided input file."
return 1
fi
# Extract input file name without extension
input_file_name_without_ext=${1:r}
# Run ffmpeg command!
# Explained:
# -i == input file
# -r == set 24fps. Might be redundant with the next, but 🤷♂️
# -vf fps 24 == video filter for 24fps. Might be redundant with previous. 🤷♂️ https://trac.ffmpeg.org/wiki/ChangingFrameRate
# ... scale == Some math+functions to scale the input video frames to fit within a 1280x720 size while preserving the aspect ratio. If the original video has a width greater than its height, it resizes the video to 1080 pixels wide while maintaining the aspect ratio. If the original video has a height greater than or equal to its width, it resizes the video to 720 pixels tall while maintaining the aspect ratio.
# -c:v == This specifies the video codec to be used for encoding the output. In this case, libx264 is chosen, which is a widely used H.264 video codec.
# -crf == This sets the Constant Rate Factor (CRF) for video quality. ChatGPT chose this. 😬
# -preset veryslow == This sets the encoding preset, which determines the trade-off between encoding speed and compression efficiency. The veryslow preset provides the highest compression efficiency but is the slowest in terms of encoding speed.
# {output_file_name} == setting the output filename to always be mp4.
ffmpeg -i "$1" \
-r 24 \
-vf "fps=24,scale='if(gt(iw,ih),1280,-1)':'if(gt(iw,ih),-1,720)',pad=ceil(iw/2)*2:ceil(ih/2)*2" \
-c:v libx264 \
-crf 23 \
-preset veryslow \
-af "aresample=async=1:min_comp=0.001:min_hard_comp=0.100000" \
-ar 44100 \
"${input_file_name_without_ext}-crushed.mp4"
}