ffmpeg — audio / video swiss army knife¶
FFmpeg is the universal audio/video toolchain that almost every higher-level tool (vhs, OBS, video editors, ASR pipelines) calls under the hood. Bundled here under installMediaTools=true together with ImageMagick, ExifTool, and libvips.
- Install:
- macOS — Homebrew (
brew install ffmpeg), managed bydot_ansible/roles/media_tools/tasks/main.ymlwheninstallMediaTools=true. - Linux — apt (
sudo apt install ffmpeg), same role / same flag. Skipped automatically whennoRoot=true(the apt block carriestags: [sudo]). - Verify:
ffmpeg -version | head -1andffprobe -version | head -1. - Status in this repo: opt-in via
installMediaTools=true. Three zsh helpers (compress-video,extract-audio,to-wav16k) ship atdot_config/zsh/tools/29_media.zsh— see docs/shells/aliases.md → Media / AV. Also satisfies the runtime dep thatvhsneeds to record.
Common conversions¶
# Container conversion (.mov / .mkv / .webm → .mp4)
ffmpeg -i input.mov output.mp4
# Re-encode with x264 + AAC
ffmpeg -i input.mov -c:v libx264 -c:a aac output.mp4
-c copy skips re-encoding entirely when the codec is already compatible — orders of magnitude faster but the source streams must be valid for the target container.
Compression — CRF cheatsheet¶
# Smaller MP4 with x264 (preset slow trades CPU for size).
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset slow -c:a aac -b:a 128k output.mp4
-crf |
Use for |
|---|---|
| 18 | Visually lossless (archive masters) |
| 23 | Default — good quality, reasonable size |
| 28 | Decent quality, ~½ the size of CRF 23 (the compress-video zsh helper uses this) |
| 32 | Aggressive — visible artifacts but tiny files |
-preset knobs (ultrafast → placebo) trade encode time for compression efficiency at the same CRF.
Audio¶
# Strip video, keep audio without re-encoding (fastest)
ffmpeg -i input.mp4 -vn -c:a copy output.m4a
# Re-encode to a clean format
ffmpeg -i input.wav output.mp3
ffmpeg -i input.flac output.m4a
# Whisper / faster-whisper / wav2vec want 16 kHz mono WAV
ffmpeg -i input.m4a -ar 16000 -ac 1 output_16k.wav
The last two patterns are wrapped by extract-audio and to-wav16k.
Trim / cut¶
# Fast cut (stream-copy) — keyframe-aligned, may overshoot by a fraction of a sec
ffmpeg -ss 00:00:30 -i input.mp4 -t 10 -c copy clip.mp4
# Frame-accurate cut (re-encodes)
ffmpeg -ss 00:00:30 -i input.mp4 -t 10 clip.mp4
Place -ss before -i for fast input-side seek; place it after -i for accurate output-side seek (slower).
Concat¶
# files.txt:
# file 'part1.mp4'
# file 'part2.mp4'
ffmpeg -f concat -safe 0 -i files.txt -c copy output.mp4
All inputs must share codec + resolution + framerate. If they don't, re-encode first or use -filter_complex concat=n=N:v=1:a=1.
Frames ↔ video¶
# Pull every Nth second as a PNG
ffmpeg -i input.mp4 -vf fps=1 frame_%04d.png
# One frame at a specific timestamp
ffmpeg -ss 00:01:23 -i input.mp4 -frames:v 1 screenshot.png
# Image sequence → video (e.g. matplotlib export, ML training viz)
ffmpeg -framerate 30 -i frame_%04d.png -c:v libx264 -pix_fmt yuv420p output.mp4
-pix_fmt yuv420p is the safest pixel format for browsers / Slack / iOS Quick Look — without it, x264 may pick a 4:4:4 variant that some players refuse.
GIF (and why MP4 is usually better)¶
# Quick GIF
ffmpeg -i input.mp4 -vf "fps=12,scale=640:-1" output.gif
# Higher-quality GIF (palettegen → paletteuse, two-pass)
ffmpeg -i input.mp4 -vf "fps=12,scale=640:-1,palettegen" /tmp/palette.png
ffmpeg -i input.mp4 -i /tmp/palette.png -lavfi "fps=12,scale=640:-1 [v]; [v][1:v] paletteuse" output.gif
GIFs are 5–20× larger than equivalent MP4/WebM at similar visual quality. Prefer MP4 unless the destination requires GIF (some chat platforms still do).
Inspect¶
ffprobe input.mp4
ffprobe -v error -show_format -show_streams input.mp4 # structured
ffprobe -v error -show_streams -of json input.mp4 | jq # JSON pipeline
ffprobe ships in the same package — no separate install.
Subtitles¶
# Soft (mux) — embeds .srt as a track, players can toggle it
ffmpeg -i input.mp4 -i subtitle.srt -c copy output.mkv
# Hard (burn-in) — bakes text into pixels, no toggling
ffmpeg -i input.mp4 -vf subtitles=subtitle.srt output.mp4
Soft subs are smaller and editable; hard subs survive platforms that strip subtitle tracks.
See also¶
- VHS — terminal recorder; depends on ffmpeg at runtime to write GIF / MP4
- ImageMagick — sibling tool for stills (single-image transforms)
- ExifTool — strip metadata from the videos you produce here
- libvips — when batch image work outgrows ImageMagick
- docs/shells/aliases.md → Media / AV — the three shipped helper functions
- Upstream cheatsheet: https://trac.ffmpeg.org/wiki