FFmpeg (2025): transcode, stream & clean metadata
A practical playbook for FFmpeg: web-ready presets, hardware acceleration (NVENC/VAAPI/VideoToolbox), filters, subtitles, concat, HLS/DASH streaming, ffprobe verification, and privacy-safe metadata handling.
Compatibility
H.264 + AAC
Efficiency
HEVC / AV1
Privacy risk
Embedded tags
Install FFmpeg & verify
Homebrew
With codecs
brew install ffmpeg --with-fdk-aac || brew install ffmpeg ffmpeg -version ffprobe -version
Basic transcode (web presets)
H.264 + AAC (mp4)
Fast-start for web
ffmpeg -i input.mov -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart -map_metadata 0 -map_chapters 0 output.mp4
Use -crf 18–23 (lower = better). -movflags +faststart helps progressive playback.
HEVC / AV1
Higher efficiency
# HEVC (CPU) ffmpeg -i in.mp4 -c:v libx265 -crf 24 -preset slow -c:a aac -b:a 128k out_hevc.mp4 # AV1 (CPU) ffmpeg -i in.mp4 -c:v libaom-av1 -crf 28 -b:v 0 -cpu-used 4 -row-mt 1 -c:a libopus -b:a 128k out_av1.mkv
Hardware acceleration (NVENC / VAAPI / VideoToolbox)
NVIDIA NVENC
Windows/Linux
ffmpeg -y -hwaccel cuda -i in.mp4 -c:v h264_nvenc -preset p5 -rc vbr -cq 23 -b:v 5M -c:a aac -b:a 160k -movflags +faststart out_nvenc.mp4
Intel VAAPI
Linux
ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi -i in.mp4 -vf 'format=nv12,hwupload' -c:v h264_vaapi -b:v 5M -c:a aac -b:a 128k out_vaapi.mp4
Apple VideoToolbox
macOS
ffmpeg -i in.mov -c:v h264_videotoolbox -b:v 5M -c:a aac -b:a 160k -movflags +faststart out_vtb.mp4
Filters: scale, crop, fps, subtitles
Scale & FPS
Sharpen pipelines
# 1080p → 720p at 30 fps ffmpeg -i in.mp4 -vf "scale=-2:720,fps=30" -c:v libx264 -crf 21 -preset medium -c:a aac out_720p.mp4
Use -2 to preserve mod-2 alignment.
Subtitles
Burn-in or soft
# Burn-in SRT (hard subs) ffmpeg -i in.mp4 -vf "subtitles='subs.srt'" -c:v libx264 -crf 20 -c:a copy out_hardsub.mp4 # Soft subs (MKV) ffmpeg -i in.mp4 -i subs.srt -c:v copy -c:a copy -c:s srt out_soft.mkv
Audio: AAC / Opus, loudness & mapping
AAC / Opus
Web & streaming
# AAC stereo ffmpeg -i in.wav -c:a aac -b:a 160k out.m4a # Opus in WebM ffmpeg -i in.wav -c:a libopus -b:a 96k out.webm
Loudness
EBU R128
# Analyze ffmpeg -hide_banner -i in.wav -af loudnorm=print_format=json -f null - # Apply (replace with measured params for 2nd pass) ffmpeg -i in.wav -af "loudnorm=I=-16:TP=-1.5:LRA=11" out_norm.wav
Concatenate & trim
Concat demuxer
Same codec/params
# files.txt file 'a.mp4' file 'b.mp4' # Merge without re-encode (when params match) ffmpeg -f concat -safe 0 -i files.txt -c copy merged.mp4
Trim
Accurate cuts
# Re-encode for frame-accurate trim ffmpeg -ss 00:00:10 -to 00:00:25 -i in.mp4 -c:v libx264 -crf 20 -c:a aac cut.mp4
HLS/DASH streaming
HLS (VOD)
.m3u8 + .ts/.mp4
ffmpeg -i in.mp4 -c:v libx264 -crf 21 -c:a aac -b:a 128k -start_number 0 -hls_time 6 -hls_playlist_type vod -hls_segment_type mpegts -master_pl_name master.m3u8 -f hls out_%v.m3u8
DASH (VOD)
.mpd
ffmpeg -i in.mp4 -map 0:v -map 0:a -c:v libx264 -c:a aac -b:v 4M -bf 1 -keyint_min 60 -g 60 -sc_threshold 0 -use_timeline 1 -use_template 1 -seg_duration 6 -adaptation_sets "id=0,streams=v id=1,streams=a" -f dash manifest.mpd
Metadata: probe, set, remove
Probe
Streams & tags
ffprobe -hide_banner -v error -show_format -show_streams "video.mp4" ffprobe -v error -print_format json -show_entries format:stream "video.mp4"
Set / Remove
Container-level
# Set title ffmpeg -i in.mp4 -c copy -metadata title="Demo" out_title.mp4 # Remove all container metadata ffmpeg -i in.mp4 -c copy -map_metadata -1 out_clean.mp4
Thumbnails & privacy tip
Generate thumbnails
Preview frames
# Grab a frame at 5s ffmpeg -ss 5 -i in.mp4 -frames:v 1 -q:v 2 thumb.jpg # Grid contact sheet (every 5s, 3 columns) ffmpeg -i in.mp4 -vf "fps=1/5,scale=640:-2,tile=3x" -q:v 2 sheet.jpg
Sanitize images
Before sharing
Remove GPS, camera serials, and author fields from JPG/PNG thumbnails.
# CLI fallback exiftool -overwrite_original -GPS*= -Artist= -SerialNumber= *.jpg
Verify outputs with ffprobe
# Summarize container & streams ffprobe -hide_banner -v error -select_streams v:0 -show_entries stream=codec_name,avg_frame_rate,width,height -of default=noprint_wrappers=1 "out.mp4" # Check for stray metadata ffprobe -v error -show_entries format_tags:stream_tags -of json "out.mp4"
FAQ
Ship clean, web-ready media — protect privacy
Use FFmpeg for quality and size. For images you publish alongside your videos, scrub hidden EXIF/GPS first.