filtergraph.md 3.4 KB

Filtergraphs — syntax, labels, chains, and the patterns that need them

Grammar in 60 seconds

-vf  "f1=a=1:b=2,f2"                 simple: one video chain, commas join filters
-af  "f1,f2"                          same for audio
-filter_complex "[0:v]f1[x];[x][1:v]f2[out]"   multiple inputs/outputs need labels
  • , chains filters; ; separates parallel chains.
  • [0:v] [1:a] = input file 0's video, file 1's audio. [label] = your wire.
  • Every labeled output must be consumed (or -mapped). Unconsumed = error.
  • One stream cannot feed two filters — split/asplit it first.
  • -vf/-af and -filter_complex are mutually exclusive per stream; filters and -c copy are mutually exclusive, full stop.

Escaping (three layers deep): the filter arg parser eats : and ,, the graph parser eats ; and [], then your shell takes a pass. Inside a filter argument, escape with \ (e.g. drawtext=text='1\:30'). Avoid the whole topic where possible: relative paths for files, single-quoted graphs (bash and PowerShell), no spaces in asset names.

split — the fan-out primitive

# blurred-background vertical (one decode, two consumers):
-filter_complex "[0:v]split[a][b];[a]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20[bg];[b]scale=1080:-2[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2"

Common graph patterns

# picture-in-picture, top-right, 1/4 size
-filter_complex "[1:v]scale=iw/4:-1[pip];[0:v][pip]overlay=W-w-24:24"

# overlay visible only between 5s and 12s
-filter_complex "[0:v][1:v]overlay=24:24:enable='between(t,5,12)'"

# crossfade two clips (1s, starting at 4s into clip A) — video and audio
-filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v];[0:a][1:a]acrossfade=d=1[a]"

# side-by-side A/B (heights must match; scale first if not)
-filter_complex "[0:v][1:v]hstack"
# 2x2 grid
-filter_complex "[0:v][1:v][2:v][3:v]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0"

Time manipulation

# constant speed: video PTS x factor, audio atempo (0.5-100; chain for <0.5)
-filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]"      # 2x
-filter_complex "[0:v]setpts=4*PTS[v];[0:a]atempo=0.5,atempo=0.5[a]"  # 0.25x

# speed RAMP (slow-mo a highlight 10-12s, normal speed around it): cut three
# ranges with trim/atrim, retime the middle, concat — see trim-concat.md's
# remove-middle pattern with setpts=2*PTS added to the middle chain.

# interpolated 60fps slow-mo (synthesizes frames; slow, occasionally wobbly
# around fast motion — check the output)
-vf "minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1,setpts=2*PTS" -an

Expressions

Filter args accept expressions: t (seconds), n (frame), w/h/iw/ih (sizes), main_w/overlay_w in overlay. Useful forms:

overlay=x='if(gte(t,3),24,-w)'         # slide in at t=3
drawtext=...:x=(w-text_w)/2:y=h-th-40  # centered lower third
select='not(mod(n,30))'                # every 30th frame
fade=t=in:st=0:d=1,fade=t=out:st=9:d=1 # fade in/out (10s clip)

Per-filter docs without leaving the terminal

ffmpeg -h filter=xfade        # all options + ranges for one filter
ffmpeg -filters | rg blur     # discover what this build has

Niche corners worth knowing exist: v360 (360°/VR re-projection), geq (per-pixel expressions), sendcmd (timed parameter changes), zmq (live parameter control).