
FFmpeg and Automated Video Flows (Beginner Recipes)
Copy-paste recipes for converting, trimming, captioning, and batching with FFmpeg.
Copy‑paste first, then tweak. Everything here works on macOS (Terminal) and Windows (PowerShell).
What FFmpeg is & when to use it
FFmpeg is the Swiss‑Army knife of media. Paste a short command, get a precise, repeatable result: convert, trim, join, resize, caption, analyze. It’s perfect for creators who want consistent outputs without clicky menus.
Use it when you need:
- Batchable jobs — normalize 20 voiceovers, make vertical crops for Shorts, burn captions, add a logo.
- House presets — lock resolution/bitrate/format for every export.
- Headless/overnight runs — queue renders while you sleep.
- Interchange — when an editor refuses a format, FFmpeg usually saves the day.
If a command overwrites a file, you’ll see -y
(yes to overwrite). Prefer safety? Remove -y
and FFmpeg will ask first.
Installing & verifying
macOS (Homebrew)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install ffmpeg
ffmpeg -version
ffprobe -version
Windows (Chocolatey | run PowerShell as Administrator)
Set-ExecutionPolicy Bypass -Scope Process -Force; `
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install ffmpeg -y
ffmpeg -version
ffprobe -version
If ffmpeg
isn’t found after installing, close and reopen your terminal so PATH updates. You’ll use ffprobe
(ships with FFmpeg) to inspect files before editing.
Inspect before you edit (ffprobe)
Prevent avoidable re‑renders and wrong aspect ratios—peek first.
Quick look (human‑friendly)
ffprobe -hide_banner -i "input.mp4"
Targeted facts (easy to paste into notes)
# Resolution and frame rate
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate -of default=noprint_wrappers=1 "input.mp4"
# Audio layout and sample rate
ffprobe -v error -select_streams a:0 -show_entries stream=channel_layout,sample_rate -of default=noprint_wrappers=1 "input.mp4"
Why it matters:
- Resolution/FPS → choose the right resize or padding recipe.
- Audio channels → don’t downmix or lose a channel by accident.
- Duration/codecs → some concat methods require matching codecs.
The five starter recipes creators need
Assume H.264 + AAC outputs—friendly across platforms. Add -y
to overwrite and -movflags +faststart
for faster web playback.
1) Normalize audio loudness (dialog‑friendly)
Levels audio to a consistent perceived loudness so videos don’t jump in volume.
Simple one‑pass (great default)
ffmpeg -i input.wav -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:a aac -b:a 192k output.m4a
Works for embedded audio too:
ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:v copy -c:a aac -b:a 160k out.mp4
Tip: For music‑heavy mixes, try
I=-14
LUFS. Save your “house” target in a notes file.
2) Resize & crop for platforms (16:9 → 9:16 Shorts, 1:1 square)
Horizontal → vertical (9:16, 1080×1920)
ffmpeg -i input.mp4 -vf "scale=-2:1920,crop=1080:1920" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart vertical_1080x1920.mp4
Square (1:1, 1080×1080)
ffmpeg -i input.mp4 -vf "scale=-2:1080,crop=1080:1080" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart square_1080.mp4
Letterbox instead of crop (keep full frame)
ffmpeg -i input.mp4 -vf "scale=1080:-2:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart vertical_letterbox.mp4
Add
-r 30
or-r 60
if you need a specific frame rate.
3) Burn subtitles from .srt
(always‑visible captions)
Basic (default styling)
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart captioned.mp4
Readable styling (font size & outline)
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='Fontsize=26,OutlineColour=&H80000000&,BorderStyle=3,Outline=2,Shadow=0'" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart captioned_readable.mp4
If Windows quoting fights you, use double quotes and escape inner quotes.
4) Stitch intro + main + outro (concat demuxer)
Fast path (no re‑encode; codecs must match)
# Create a list file
printf "file 'intro.mp4'
file 'main.mp4'
file 'outro.mp4'
" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
Always‑works path (re‑encode to a house preset)
ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart final_preset.mp4
5) Add a watermark/logo (safe corners & margins)
Bottom‑right (30px margin)
ffmpeg -i input.mp4 -i logo.png -filter_complex "[1:v]scale=200:-1[wm];[0:v][wm]overlay=main_w-overlay_w-30:main_h-overlay_h-30" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart watermarked.mp4
Top‑left
ffmpeg -i input.mp4 -i logo.png -filter_complex "[1:v]scale=200:-1[wm];[0:v][wm]overlay=30:30" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart watermarked_tl.mp4
Output presets (YouTube, Shorts, Podcast audio)
Adjust -crf
(quality) and -preset
(speed): lower CRF = higher quality; slower preset = smaller files, longer encodes.
YouTube 1080p (16:9)
ffmpeg -i input.mov -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart yt_1080p.mp4
YouTube 4K (16:9)
ffmpeg -i input.mov -vf "scale=3840:2160:force_original_aspect_ratio=decrease,pad=3840:2160:(ow-iw)/2:(oh-ih)/2:black" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -c:a aac -b:a 192k -movflags +faststart yt_4k.mp4
Shorts / Reels (9:16 @ 30 fps)
ffmpeg -i input.mp4 -vf "scale=-2:1920,crop=1080:1920" -r 30 -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart short_1080x1920_30fps.mp4
Podcast / VO audio
# AAC .m4a with gentle loudness normalization
ffmpeg -i input.wav -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:a aac -b:a 192k podcast.m4a
# MP3 alternative
ffmpeg -i input.wav -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:a libmp3lame -b:a 192k podcast.mp3
Use
yuv420p
for the widest compatibility and always add-movflags +faststart
for web playback.
Speeding up or keeping it simple
Pick one path to start.
CPU (libx264/libx265) — simplest & consistent
ffmpeg -i input.mp4 -c:v libx264 -crf 20 -preset medium -c:a aac -b:a 160k out.mp4
Mac GPU (VideoToolbox) — fast on Apple silicon
ffmpeg -i input.mp4 -c:v h264_videotoolbox -b:v 6M -maxrate 8M -bufsize 12M -c:a aac -b:a 160k -movflags +faststart out_vtb.mp4
For 4K, try ~20–30 Mb/s; for 1080p, ~6–10 Mb/s.
NVIDIA GPU (NVENC) — Windows/Linux with NVIDIA
ffmpeg -i input.mp4 -c:v h264_nvenc -preset p5 -cq 23 -b:v 0 -c:a aac -b:a 160k -movflags +faststart out_nvenc.mp4
-cq
controls quality (lower = better). Keep-b:v 0
for CQ mode.
Rule of thumb: If you care most about size/quality per file, stick to CPU/libx264. If speed matters most, try VideoToolbox (Mac) or NVENC (NVIDIA).
Organizing repeatable “render recipes”
Keep a small recipes.json
and a tiny script to apply them—everyone on the team renders the same way.
{
"youtube_1080p": {
"vf": "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black",
"vcodec": "libx264", "crf": 20, "preset": "medium",
"acodec": "aac", "ab": "160k", "ext": "mp4"
},
"shorts_9x16_1080x1920": {
"vf": "scale=-2:1920,crop=1080:1920",
"vcodec": "libx264", "crf": 20, "preset": "medium",
"acodec": "aac", "ab": "128k", "ext": "mp4"
},
"podcast_audio": {
"af": "loudnorm=I=-16:TP=-1.5:LRA=11",
"acodec": "aac", "ab": "192k", "ext": "m4a"
}
}
Folder/naming for final renders
/renders
/youtube_1080p/
/shorts_9x16_1080x1920/
/podcast_audio/
Name files like: Project_Scene01_v03_youtube_1080p.mp4
— the preset is baked into the filename.

“Drop folder → auto render” (beginner batch)
Start with a manual batch before using watchers.
macOS/Linux (bash)
for f in to_render/*.mp4; do
ffmpeg -y -i "$f" -vf "scale=-2:1920,crop=1080:1920" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart "renders/shorts_9x16_1080x1920/$(basename "${f%.*}")_short.mp4"
done
Windows (PowerShell)
Get-ChildItem . o_render\*.mp4 | ForEach-Object {
$name = [System.IO.Path]::GetFileNameWithoutExtension($_.FullName)
ffmpeg -y -i $_.FullName `
-vf "scale=-2:1920,crop=1080:1920" `
-c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p `
-c:a aac -b:a 128k -movflags +faststart `
"renders\shorts_9x16_1080x1920\$name`_short.mp4"
}
Watchers can re‑trigger mid‑copy and chew CPU. Add them later (PowerShell FileSystemWatcher,
fswatch
/entr
on macOS) once presets are stable.
Troubleshooting (greatest hits)
“Unknown encoder ‘libx264’”
You’re using a minimal build. Reinstall via Homebrew/Chocolatey.
Workarounds: on Mac use -c:v h264_videotoolbox
; on NVIDIA use -c:v h264_nvenc
.
“Unknown encoder ‘libfdk_aac’”
Use built‑in AAC: -c:a aac -b:a 160k
(FDK isn’t in many builds).
Subtitles filter errors (fonts)
Install common fonts or force style:
-vf "subtitles=subs.srt:force_style='Fontsize=26,Outline=2,BorderStyle=3'"
Windows quoting can be tricky—switch to double quotes and escape inner quotes.
Concat fails / “Codec not found”
Stream‑copy concat requires matching streams. If in doubt, re‑encode:
-c:v libx264 -c:a aac
with your preset.
Audio out of sync
For VFR → CFR: add -vf fps=30
(or your target).
Stubborn? -af aresample=async=1:first_pts=0
.
“Invalid argument”
Most often a mis‑typed filter graph or quotes. Start minimal and add filters one by one.
Green video / odd colors
Ensure -pix_fmt yuv420p
for widest compatibility.
FAQ & resources
CRF or bitrate?
CRF for CPU encoders (quality‑first). Bitrate/CQ for GPU or strict delivery specs.
Best preset?
Start with -preset medium
. Faster → -preset fast
. Smaller files (slower) → -preset slow
.
Good CRF?
18–22 for 1080p H.264. Lower CRF = higher quality/larger files.
Bookmarks
- FFmpeg filters reference (look up
scale
,crop
,pad
,subtitles
,overlay
). - FFprobe examples (quickly pull resolution/FPS).
- Community recipe lists (“FFmpeg wiki” + the filter you need).
