The Swiss Army Knife of Audio & Video Processing
The most common one-liners you will reach for daily.
ffmpeg -i input.mkv -c copy output.mp4
Remux without re-encoding. Near-instant.
ffmpeg -i input.mov -c:v libx264 \
-crf 23 -c:a aac output.mp4
Re-encode to H.264 + AAC for broad compatibility.
ffmpeg -i input.mp4 -c:v libvpx-vp9 \
-crf 30 -b:v 0 -c:a libopus output.webm
VP9 + Opus for web delivery.
ffmpeg -i input.mp4 -vf \
"fps=12,scale=480:-1:flags=lanczos,\
split[s0][s1];[s0]palettegen[p];\
[s1][p]paletteuse" output.gif
High-quality GIF with optimized palette.
ffmpeg -i input.mp4 -vf "scale=-2:720" \
-c:v libx264 -crf 23 output.mp4
Width auto-calculated, divisible by 2.
ffmpeg -i input.mp4 -vf \
"crop=min(iw\,ih):min(iw\,ih)" \
output.mp4
Crop to largest centered square.
ffmpeg -i input.mp4 -vn -c:a copy audio.aac
# Or convert to MP3:
ffmpeg -i input.mp4 -vn -q:a 2 audio.mp3
# From 1:30 to 2:45
ffmpeg -ss 00:01:30 -to 00:02:45 \
-i input.mp4 -c copy trimmed.mp4
Place -ss before -i for fast seek.
# One frame per second
ffmpeg -i input.mp4 -vf "fps=1" \
frames/frame_%04d.png
ffmpeg -ss 00:00:10 -i input.mp4 \
-frames:v 1 thumbnail.jpg
How FFmpeg reads, routes, and writes media streams.
Every FFmpeg invocation follows this flow:
input file --> demuxer --> decoder --> [filters] --> encoder --> muxer --> output file
(container) (codec) (transform) (codec) (container)
With -c copy, the decoder/encoder steps are skipped entirely (stream copy mode). This is fast but prevents any filtering.
# Two inputs, select streams explicitly
ffmpeg -i video.mp4 -i audio.m4a \
-map 0:v:0 -map 1:a:0 \
-c copy output.mp4
# Map notation breakdown:
# -map 0:v:0 = input 0, first video stream
# -map 1:a:0 = input 1, first audio stream
# -map 0 = all streams from input 0
# -map 0:s = all subtitle streams from input 0
-map, FFmpeg picks one stream per type (video, audio, subtitle) from the first input that has it, choosing the "best" by bitrate/resolution. Use -map when you need precise control.
| Option | Meaning | Example |
|---|---|---|
-c:v |
Video codec | -c:v libx264 |
-c:a |
Audio codec | -c:a aac |
-c:s |
Subtitle codec | -c:s mov_text |
-c copy |
Copy all streams (no re-encode) | -c copy |
-c:v copy -c:a aac |
Copy video, re-encode audio | Mix copy and encode |
-b:v |
Video bitrate | -b:v 4M |
-b:a |
Audio bitrate | -b:a 192k |
| Flag | Purpose |
|---|---|
-y | Overwrite output without asking |
-n | Never overwrite (fail if output exists) |
-hide_banner | Suppress build/config info |
-v quiet | Suppress all output except errors |
-stats | Show encoding progress only |
-t 30 | Limit output duration to 30 seconds |
-ss HH:MM:SS | Seek to position (before -i = fast seek) |
-to HH:MM:SS | Stop at position |
-f fmt | Force input/output format |
-threads 0 | Auto-detect thread count |
Codecs, quality targets, and encoding strategies for every use case.
CRF (Constant Rate Factor) targets a consistent visual quality. Lower CRF = higher quality, larger file. Bitrate mode targets a file size. Use CRF for archival and local playback; use bitrate for streaming with bandwidth constraints.
| Mode | Flag | Best For | Example |
|---|---|---|---|
| CRF (constant quality) | -crf N |
Local playback, archival | -crf 23 |
| ABR (average bitrate) | -b:v N |
Streaming, file size target | -b:v 4M |
| CBR (constant bitrate) | -b:v N -minrate N -maxrate N -bufsize 2N |
Live streaming | -b:v 4M -maxrate 4M -bufsize 8M |
| Constrained CRF | -crf N -maxrate M -bufsize 2M |
Quality with ceiling | -crf 23 -maxrate 5M -bufsize 10M |
The most widely supported codec. Works everywhere: browsers, phones, smart TVs, and media players.
# High quality archival
ffmpeg -i input.mov -c:v libx264 -crf 18 -preset slow \
-c:a aac -b:a 192k output.mp4
# Fast web export
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset medium \
-profile:v high -level 4.1 \
-movflags +faststart -c:a aac output.mp4
| Preset | Speed | Compression |
|---|---|---|
ultrafast | Fastest | Largest files |
superfast | ||
veryfast | ||
faster | ||
fast | ||
medium | Default | Good balance |
slow | ||
slower | ||
veryslow | Slowest | Smallest files |
~50% better compression than H.264 at the same quality. Slower to encode. Not universally supported in browsers (Safari yes, Chrome partial).
ffmpeg -i input.mov -c:v libx265 -crf 28 -preset medium \
-tag:v hvc1 -c:a aac output.mp4
# -tag:v hvc1 is required for Apple/QuickTime compatibility
# CRF 28 in x265 ≈ CRF 23 in x264 quality
Google's open codec. Broadly supported in browsers. Good for WebM delivery.
# Two-pass VP9 for best quality
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 2M \
-pass 1 -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 2M \
-pass 2 -c:a libopus -b:a 128k output.webm
# CRF mode (simpler)
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 \
-c:a libopus output.webm
Next-gen open codec. Best compression, but slowest encoding. Growing browser support.
# SVT-AV1 (fastest AV1 encoder)
ffmpeg -i input.mp4 -c:v libsvtav1 -crf 30 \
-preset 6 -svtav1-params tune=0 \
-c:a libopus output.mp4
# Presets: 0 (slowest/best) to 13 (fastest)
# CRF range: 0-63, default 35
| Codec | Encoder | Quality/Size | Speed | Browser Support |
|---|---|---|---|---|
| H.264 | libx264 |
Good | Fast | Universal |
| H.265 | libx265 |
Better | Slower | Partial |
| VP9 | libvpx-vp9 |
Better | Slow | Most (not Safari) |
| AV1 | libsvtav1 |
Best | Slowest | Growing |
Codecs, quality settings, and volume normalization.
| Codec | Encoder | Container | Typical Use | Quality Flag |
|---|---|---|---|---|
| AAC | aac |
MP4, M4A | Video, streaming | -b:a 192k |
| MP3 | libmp3lame |
MP3 | Music, podcasts | -q:a 2 (VBR ~190kbps) |
| Opus | libopus |
WebM, OGG | VoIP, web, streaming | -b:a 128k |
| FLAC | flac |
FLAC | Lossless archival | -compression_level 8 |
| Vorbis | libvorbis |
OGG, WebM | Legacy web audio | -q:a 6 |
# Convert to high-quality MP3
ffmpeg -i input.wav -c:a libmp3lame -q:a 0 output.mp3
# Convert to AAC with specific bitrate
ffmpeg -i input.wav -c:a aac -b:a 256k output.m4a
# Lossless FLAC from WAV
ffmpeg -i input.wav -c:a flac output.flac
# Change sample rate
ffmpeg -i input.wav -ar 44100 output.wav
# Mono to stereo / stereo to mono
ffmpeg -i input.wav -ac 1 mono.wav
ffmpeg -i input.wav -ac 2 stereo.wav
FFmpeg offers two normalization filters. loudnorm (EBU R128) is the industry standard for broadcast/streaming.
# EBU R128 loudness normalization (two-pass for best results)
# Pass 1: analyze
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json \
-f null /dev/null 2>> analysis.json
# Pass 2: apply (use measured values from pass 1)
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:\
measured_I=-23.5:measured_TP=-4.2:measured_LRA=8.1:\
measured_thresh=-34.0:linear=true \
-c:v copy output.mp4
# Simple single-pass normalization (good enough for most cases)
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11 \
-c:v copy output.mp4
# Simple volume adjustment
ffmpeg -i input.mp4 -af "volume=1.5" louder.mp4 # 150% volume
ffmpeg -i input.mp4 -af "volume=-3dB" quieter.mp4 # reduce by 3dB
ffmpeg -i input.mp4 -af "volume=6dB" louder.mp4 # boost by 6dB
-16 LUFS is the standard for streaming platforms (YouTube, Spotify). -24 LUFS is typical for broadcast TV. TP=-1.5 sets the true peak ceiling.
Transform video and audio with FFmpeg's powerful filter system.
For single-input, single-output chains. Filters are comma-separated.
# Chain multiple video filters
ffmpeg -i input.mp4 -vf "scale=1280:720,fps=30,eq=brightness=0.06" \
output.mp4
# Chain audio filters
ffmpeg -i input.mp4 -af "highpass=f=200,lowpass=f=3000,volume=2" \
output.mp4
| Filter | Usage | Purpose |
|---|---|---|
scale | scale=1280:720 | Resize video |
crop | crop=640:480:0:0 | Crop to w:h at x:y |
fps | fps=24 | Change framerate |
pad | pad=1920:1080:(ow-iw)/2:(oh-ih)/2 | Add borders (letterbox) |
transpose | transpose=1 | Rotate 90 CW |
hflip / vflip | hflip | Mirror horizontally/vertically |
eq | eq=brightness=0.06:saturation=1.3 | Brightness/contrast/saturation |
deinterlace | yadif | Deinterlace video |
drawtext | drawtext=text='Hello':fontsize=48:x=10:y=10 | Burn text into video |
overlay | overlay=W-w-10:10 | Overlay image/video |
setpts | setpts=0.5*PTS | Speed up (0.5x = 2x speed) |
| Filter | Usage | Purpose |
|---|---|---|
volume | volume=1.5 | Adjust volume |
loudnorm | loudnorm=I=-16 | EBU R128 normalization |
highpass | highpass=f=200 | Remove low frequencies |
lowpass | lowpass=f=3000 | Remove high frequencies |
afade | afade=t=in:d=2 | Fade in/out |
atempo | atempo=2.0 | Speed up audio (0.5-100) |
aresample | aresample=44100 | Resample audio |
silenceremove | silenceremove=1:0:-50dB | Strip silence |
For multiple inputs/outputs, splitting streams, or combining streams. Uses labeled pads in square brackets.
# Picture-in-picture
ffmpeg -i main.mp4 -i overlay.mp4 -filter_complex \
"[1:v]scale=320:240[pip]; \
[0:v][pip]overlay=W-w-10:10" \
-c:a copy output.mp4
# Side-by-side comparison
ffmpeg -i left.mp4 -i right.mp4 -filter_complex \
"[0:v]scale=640:480[l]; \
[1:v]scale=640:480[r]; \
[l][r]hstack=inputs=2" \
output.mp4
# Crossfade between two clips
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex \
"[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v]; \
[0:a][1:a]acrossfade=d=1[a]" \
-map "[v]" -map "[a]" output.mp4
# Add watermark
ffmpeg -i input.mp4 -i logo.png -filter_complex \
"[1:v]scale=100:-1,format=rgba,colorchannelmixer=aa=0.5[wm]; \
[0:v][wm]overlay=W-w-20:20" \
-c:a copy output.mp4
[input_label]filter=params[output_label] -- Semicolons separate filter chains. Commas chain filters within a single stream. Labels in [] connect chains together.
# 2x speed (video + audio)
ffmpeg -i input.mp4 -vf "setpts=0.5*PTS" \
-af "atempo=2.0" fast.mp4
# 0.5x slow motion
ffmpeg -i input.mp4 -vf "setpts=2.0*PTS" \
-af "atempo=0.5" slow.mp4
# 4x speed (atempo max is 100, but chain for clarity)
ffmpeg -i input.mp4 -vf "setpts=0.25*PTS" \
-af "atempo=2.0,atempo=2.0" fast4x.mp4
HLS, DASH, and RTMP for live and on-demand delivery.
Apple's adaptive streaming protocol. Produces .m3u8 playlist + .ts segments. Universally supported.
# Basic HLS output
ffmpeg -i input.mp4 -c:v libx264 -crf 23 \
-c:a aac -b:a 128k \
-hls_time 6 -hls_list_size 0 \
-hls_segment_filename "segment_%03d.ts" \
playlist.m3u8
# Multi-bitrate HLS (adaptive)
ffmpeg -i input.mp4 \
-map 0:v -map 0:v -map 0:v -map 0:a \
-c:v libx264 -c:a aac \
-b:v:0 5M -s:v:0 1920x1080 \
-b:v:1 2M -s:v:1 1280x720 \
-b:v:2 800k -s:v:2 640x360 \
-b:a 128k \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" \
-master_pl_name master.m3u8 \
-hls_time 6 -hls_list_size 0 \
-hls_segment_filename "v%v/seg_%03d.ts" \
v%v/index.m3u8
| Option | Purpose |
|---|---|
-hls_time 6 | Target segment duration (seconds) |
-hls_list_size 0 | Keep all segments in playlist (VOD) |
-hls_list_size 5 | Rolling window of 5 segments (live) |
-hls_flags delete_segments | Delete old segments |
-hls_playlist_type vod | Mark as VOD (adds EXT-X-ENDLIST) |
MPEG-DASH is the open standard alternative to HLS. Produces .mpd manifest + .m4s segments.
ffmpeg -i input.mp4 -c:v libx264 -crf 23 \
-c:a aac -b:a 128k \
-f dash -seg_duration 4 \
-init_seg_name "init_$RepresentationID$.m4s" \
-media_seg_name "seg_$RepresentationID$_$Number$.m4s" \
manifest.mpd
Push live video to platforms like YouTube, Twitch, or a custom server.
# Stream screen to RTMP
ffmpeg -f x11grab -s 1920x1080 -i :0.0 \
-f pulse -i default \
-c:v libx264 -preset veryfast -b:v 3M \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/YOUR_STREAM_KEY
# Re-stream a file as live
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -b:v 2500k \
-maxrate 2500k -bufsize 5000k \
-c:a aac -b:a 128k \
-f flv rtmp://server/live/stream
-re reads input at native framerate (real-time). Without it, FFmpeg processes as fast as possible, which overwhelms live servers.
Offload encoding and decoding to GPU for 5-20x speedups.
Requires an NVIDIA GPU with Turing or newer (GTX 16xx+, RTX series).
# H.264 with NVENC
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
-i input.mp4 \
-c:v h264_nvenc -preset p4 -cq 23 \
-c:a copy output.mp4
# H.265 with NVENC
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
-i input.mp4 \
-c:v hevc_nvenc -preset p5 -cq 28 \
-tag:v hvc1 -c:a copy output.mp4
# AV1 with NVENC (RTX 40 series)
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
-i input.mp4 \
-c:v av1_nvenc -cq 30 \
-c:a copy output.mp4
| NVENC Preset | Speed | Quality |
|---|---|---|
p1 | Fastest | Lowest |
p4 | Balanced | Good |
p7 | Slowest | Best |
Available on Intel CPUs with integrated graphics (6th gen+).
# H.264 with QSV
ffmpeg -hwaccel qsv -hwaccel_output_format qsv \
-i input.mp4 \
-c:v h264_qsv -global_quality 23 \
-c:a copy output.mp4
# H.265 with QSV
ffmpeg -hwaccel qsv -hwaccel_output_format qsv \
-i input.mp4 \
-c:v hevc_qsv -global_quality 28 \
-c:a copy output.mp4
macOS only. Uses the Apple silicon media engine or Intel Quick Sync.
# H.264 with VideoToolbox
ffmpeg -i input.mp4 \
-c:v h264_videotoolbox -q:v 65 \
-c:a copy output.mp4
# H.265 with VideoToolbox
ffmpeg -i input.mp4 \
-c:v hevc_videotoolbox -q:v 65 \
-tag:v hvc1 -c:a copy output.mp4
Video Acceleration API for Linux. Works with Intel and AMD GPUs.
ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi \
-hwaccel_device /dev/dri/renderD128 \
-i input.mp4 \
-c:v h264_vaapi -qp 23 \
-c:a copy output.mp4
# List all available encoders
ffmpeg -encoders 2>/dev/null | grep -E "nvenc|qsv|vaapi|videotoolbox"
# List available hardware acceleration methods
ffmpeg -hwaccels
Patterns for processing many files efficiently.
# Convert all MKV files to MP4
for f in *.mkv; do
ffmpeg -i "$f" -c:v libx264 -crf 23 -c:a aac \
"${f%.mkv}.mp4"
done
# Batch resize to 720p
mkdir -p resized
for f in *.mp4; do
ffmpeg -i "$f" -vf "scale=-2:720" \
-c:v libx264 -crf 23 "resized/$f"
done
# Extract audio from all videos
for f in *.mp4; do
ffmpeg -i "$f" -vn -c:a libmp3lame -q:a 2 \
"${f%.mp4}.mp3"
done
# Using GNU parallel (limit to 4 concurrent jobs)
ls *.mkv | parallel -j4 \
'ffmpeg -i {} -c:v libx264 -crf 23 -c:a aac {.}.mp4'
# Using xargs
find . -name "*.avi" -print0 | \
xargs -0 -P4 -I{} ffmpeg -i {} -c:v libx264 -crf 23 {}.mp4
# Method 1: concat demuxer (same codec, recommended)
# Create a file list:
cat > filelist.txt <<EOF
file 'clip1.mp4'
file 'clip2.mp4'
file 'clip3.mp4'
EOF
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4
# Method 2: concat filter (different codecs/resolutions)
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex \
"[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" output.mp4
# Generate filelist from directory
for f in clip_*.mp4; do echo "file '$f'"; done > filelist.txt
# Write progress to a file (for GUIs / scripts)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 \
-progress progress.log -y output.mp4
# Parse progress in a script
tail -f progress.log | grep --line-buffered "out_time="
# Get total duration for percentage calculation
duration=$(ffprobe -v quiet -show_entries \
format=duration -of csv=p=0 input.mp4)
#!/bin/bash
# Encode with logging and error handling
encode() {
local input="$1"
local output="${input%.*}_encoded.mp4"
echo "Encoding: $input"
if ffmpeg -hide_banner -i "$input" \
-c:v libx264 -crf 23 -preset medium \
-c:a aac -b:a 128k \
-movflags +faststart \
-y "$output" 2>> encode.log; then
echo " Done: $output"
else
echo " FAILED: $input" | tee -a failures.log
fi
}
export -f encode
find . -name "*.mov" -print0 | \
xargs -0 -P$(nproc) -I{} bash -c 'encode "{}"'
Inspect and analyze media files without touching a single frame.
# Quick summary
ffprobe -hide_banner input.mp4
# Show all streams
ffprobe -show_streams input.mp4
# Show format/container info
ffprobe -show_format input.mp4
# Show everything
ffprobe -show_format -show_streams input.mp4
# Get duration in seconds
ffprobe -v quiet -show_entries format=duration \
-of csv=p=0 input.mp4
# Get resolution
ffprobe -v quiet -select_streams v:0 \
-show_entries stream=width,height \
-of csv=p=0 input.mp4
# Get video codec
ffprobe -v quiet -select_streams v:0 \
-show_entries stream=codec_name \
-of csv=p=0 input.mp4
# Get framerate
ffprobe -v quiet -select_streams v:0 \
-show_entries stream=r_frame_rate \
-of csv=p=0 input.mp4
# Get bitrate
ffprobe -v quiet -show_entries format=bit_rate \
-of csv=p=0 input.mp4
# Get audio sample rate and channels
ffprobe -v quiet -select_streams a:0 \
-show_entries stream=sample_rate,channels \
-of csv=p=0 input.mp4
# Full probe as JSON (great for scripting)
ffprobe -v quiet -print_format json \
-show_format -show_streams input.mp4
# Parse with jq
ffprobe -v quiet -print_format json \
-show_streams input.mp4 | \
jq '.streams[] | select(.codec_type=="video") |
{codec: .codec_name, width, height, duration}'
# Show keyframe (I-frame) timestamps
ffprobe -select_streams v -show_frames \
-show_entries frame=pict_type,pts_time \
-of csv input.mp4 | grep ",I,"
# Count total frames
ffprobe -v quiet -count_frames -select_streams v:0 \
-show_entries stream=nb_read_frames \
-of csv=p=0 input.mp4
# Show packet sizes (for bitrate analysis)
ffprobe -select_streams v -show_packets \
-show_entries packet=pts_time,size \
-of csv input.mp4
#!/bin/bash
# List all videos with their resolution, codec, and duration
printf "%-40s %-10s %-12s %s\n" "File" "Codec" "Resolution" "Duration"
printf "%s\n" "$(printf '=%.0s' {1..80})"
for f in *.mp4 *.mkv *.mov 2>/dev/null; do
[ -f "$f" ] || continue
codec=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=codec_name -of csv=p=0 "$f")
res=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=width,height -of csv=p=0 "$f")
dur=$(ffprobe -v quiet -show_entries format=duration \
-of csv=p=0 "$f" | cut -d. -f1)
mins=$((dur / 60))
secs=$((dur % 60))
printf "%-40s %-10s %-12s %d:%02d\n" "$f" "$codec" "$res" "$mins" "$secs"
done
csv for simple scripts, json for complex parsing, flat for key-value pairs, ini for config-style output. Add p=0 to csv to suppress section headers.