Complete guide to FFmpeg filter_complex syntax, multi-input/output pipelines, and composition patterns. PROACTIVELY activate for: (1) filter_complex syntax questions, (2) Multi-input video composition (PiP, grids, overlays), (3) Video transitions (xfade, fades, wipes), (4) Audio mixing (amix, amerge), (5) Stream splitting and routing, (6) Named stream labels, (7) Complex filtergraphs, (8) GPU + CPU hybrid filter chains, (9) ABR encoding ladders, (10) Text and watermark overlays. Provides: Syntax reference, composition patterns, audio mixing examples, transition effects, GPU-accelerated filtergraphs, debugging tips.
Generates complex FFmpeg filtergraphs for multi-input video composition, transitions, and audio mixing.
npx claudepluginhub josiahsiegel/claude-plugin-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
MANDATORY: Always Use Backslashes on Windows for File Paths
When using Edit or Write tools on Windows, you MUST use backslashes (\) in file paths, NOT forward slashes (/).
| Task | Command Pattern |
|---|---|
| Basic filter_complex | -filter_complex "[0:v][1:v]overlay" |
| Named labels | [input]filter[output] |
| Chain filters | filter1,filter2,filter3 |
| Separate chains | chain1;chain2 |
| Map output | -map "[label]" |
Use for complex filtergraph operations:
For basic filters: use ffmpeg-fundamentals-2025
For hardware acceleration: use ffmpeg-hardware-acceleration
Comprehensive guide to complex filtergraphs, multi-input compositions, and advanced filter chains.
-filter_complex "filterchain1;filterchain2;..."
Each filterchain consists of:
[input_label]filter1,filter2,... (comma-separated)[output_label][input_label1][input_label2]filter=param1=value1:param2=value2[output_label]
| Component | Description | Example |
|---|---|---|
[0:v] | Video stream from input 0 | First input video |
[0:a] | Audio stream from input 0 | First input audio |
[1:v] | Video stream from input 1 | Second input video |
[label] | Custom named label | User-defined |
, | Chain filters sequentially | scale,fps |
; | Separate independent chains | chain1;chain2 |
= | Parameter assignment | scale=1280:720 |
: | Parameter separator | w=1280:h=720 |
| Reference | Meaning |
|---|---|
[0] | First input (all streams) |
[0:v] | First input, video stream |
[0:a] | First input, audio stream |
[0:s] | First input, subtitle stream |
[0:v:0] | First input, first video stream |
[1:a:1] | Second input, second audio stream |
inout# Wrap filtergraph in double quotes
ffmpeg -i input.mp4 \
-filter_complex "[0:v]drawtext=text='Hello World':fontsize=24[out]" \
-map "[out]" output.mp4
# Using shell variables
text="Dynamic Text"
ffmpeg -i input.mp4 \
-filter_complex "[0:v]drawtext=text='$text':fontsize=24[out]" \
-map "[out]" output.mp4
# Escaping special characters (colons in timestamps)
ffmpeg -i input.mp4 \
-filter_complex "[0:v]drawtext=text='Time\: %{pts\:hms}':fontsize=24[out]" \
-map "[out]" output.mp4
# Basic PiP - small video in bottom-right corner
ffmpeg -i main.mp4 -i pip.mp4 \
-filter_complex "\
[1:v]scale=320:240[pip];\
[0:v][pip]overlay=W-w-10:H-h-10" \
-c:v libx264 -c:a copy output.mp4
# PiP with border
ffmpeg -i main.mp4 -i pip.mp4 \
-filter_complex "\
[1:v]scale=320:240,drawbox=x=0:y=0:w=iw:h=ih:c=white:t=3[pip];\
[0:v][pip]overlay=W-w-10:H-h-10" \
output.mp4
# Animated PiP (moving)
ffmpeg -i main.mp4 -i pip.mp4 \
-filter_complex "\
[1:v]scale=320:240[pip];\
[0:v][pip]overlay='W-w-10-sin(t)*20':'H-h-10-cos(t)*20'" \
output.mp4
# PiP with fade-in
ffmpeg -i main.mp4 -i pip.mp4 \
-filter_complex "\
[1:v]scale=320:240,fade=in:st=2:d=1[pip];\
[0:v][pip]overlay=W-w-10:H-h-10:enable='gte(t,2)'" \
output.mp4
Using hstack/vstack (fast, simple):
# 2x2 Grid
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 \
-filter_complex "\
[0:v]scale=640:360[v0];\
[1:v]scale=640:360[v1];\
[2:v]scale=640:360[v2];\
[3:v]scale=640:360[v3];\
[v0][v1]hstack=inputs=2[top];\
[v2][v3]hstack=inputs=2[bottom];\
[top][bottom]vstack=inputs=2[out]" \
-map "[out]" output.mp4
# 3x2 Grid
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -i 6.mp4 \
-filter_complex "\
[0:v]scale=426:240[v0];\
[1:v]scale=426:240[v1];\
[2:v]scale=426:240[v2];\
[3:v]scale=426:240[v3];\
[4:v]scale=426:240[v4];\
[5:v]scale=426:240[v5];\
[v0][v1][v2]hstack=inputs=3[top];\
[v3][v4][v5]hstack=inputs=3[bottom];\
[top][bottom]vstack=inputs=2[out]" \
-map "[out]" output.mp4
Using xstack (more flexible positioning):
# 2x2 Grid with xstack
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 \
-filter_complex "\
[0:v]scale=640:360[v0];\
[1:v]scale=640:360[v1];\
[2:v]scale=640:360[v2];\
[3:v]scale=640:360[v3];\
[v0][v1][v2][v3]xstack=inputs=4:layout=0_0|640_0|0_360|640_360[out]" \
-map "[out]" output.mp4
# L-shaped layout
ffmpeg -i main.mp4 -i side1.mp4 -i side2.mp4 \
-filter_complex "\
[0:v]scale=960:720[main];\
[1:v]scale=320:360[s1];\
[2:v]scale=320:360[s2];\
[main][s1][s2]xstack=inputs=3:layout=0_0|960_0|960_360[out]" \
-map "[out]" output.mp4
# Simple fade transition
ffmpeg -i first.mp4 -i second.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
# Dissolve transition
ffmpeg -i first.mp4 -i second.mp4 \
-filter_complex "\
[0:v][1:v]xfade=transition=dissolve:duration=2:offset=3[v]" \
-map "[v]" output.mp4
# Wipe transitions
ffmpeg -i first.mp4 -i second.mp4 \
-filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1:offset=4[v]" \
-map "[v]" output.mp4
# Multiple transitions (3+ clips)
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 \
-filter_complex "\
[0:v]settb=AVTB,fps=30[v0];\
[1:v]settb=AVTB,fps=30[v1];\
[2:v]settb=AVTB,fps=30[v2];\
[v0][v1]xfade=transition=fade:duration=1:offset=4[xf1];\
[xf1][v2]xfade=transition=dissolve:duration=1:offset=8[out]" \
-map "[out]" output.mp4
Available xfade transitions:
fade, fadeblack, fadewhitewipeleft, wiperight, wipeup, wipedownslideleft, slideright, slideup, slidedowncirclecrop, rectcrop, distanceradial, smoothleft, smoothrightdissolve, pixelize, diagtl, diagtr, diagbl, diagbrhlslice, hrslice, vuslice, vdslicehblur, fadegrays, squeezev, squeezeh# Mix two audio tracks
ffmpeg -i video.mp4 -i music.mp3 \
-filter_complex "\
[0:a][1:a]amix=inputs=2:duration=first:dropout_transition=2[aout]" \
-map 0:v -map "[aout]" output.mp4
# Mix with volume control
ffmpeg -i vocals.mp4 -i music.mp3 \
-filter_complex "\
[0:a]volume=1.0[v0];\
[1:a]volume=0.3[v1];\
[v0][v1]amix=inputs=2:duration=first[aout]" \
-map 0:v -map "[aout]" output.mp4
# Audio with delay (ducking)
ffmpeg -i main.mp4 -i effect.mp3 \
-filter_complex "\
[1:a]adelay=2000|2000[delayed];\
[0:a][delayed]amix=inputs=2:duration=first[aout]" \
-map 0:v -map "[aout]" output.mp4
# Merge channels (stereo from two mono)
ffmpeg -i left.wav -i right.wav \
-filter_complex "[0:a][1:a]amerge=inputs=2[aout]" \
-map "[aout]" stereo.wav
# Audio crossfade between tracks
ffmpeg -i track1.mp3 -i track2.mp3 \
-filter_complex "[0:a][1:a]acrossfade=d=3:c1=tri:c2=tri[out]" \
-map "[out]" mixed.mp3
# Static text overlay
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]drawtext=text='Watermark':\
fontsize=48:fontcolor=white@0.8:\
x=10:y=H-th-10[out]" \
-map "[out]" output.mp4
# Fade-in text
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]drawtext=text='Hello':\
fontsize=64:fontcolor=white:\
x=(w-text_w)/2:y=(h-text_h)/2:\
alpha='if(lt(t,1),t,1)'[out]" \
-map "[out]" output.mp4
# Scrolling credits
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]drawtext=textfile=credits.txt:\
fontsize=32:fontcolor=white:\
x=(w-text_w)/2:y=h-t*50[out]" \
-map "[out]" output.mp4
# Timed text (appear/disappear)
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]drawtext=text='Show at 2s':\
fontsize=48:fontcolor=yellow:\
x=100:y=100:\
enable='between(t,2,5)'[out]" \
-map "[out]" output.mp4
# Logo/image watermark
ffmpeg -i video.mp4 -i logo.png \
-filter_complex "\
[1:v]scale=150:-1[logo];\
[0:v][logo]overlay=W-w-20:20[out]" \
-map "[out]" -map 0:a output.mp4
# Split for multiple resolutions
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]split=3[v1][v2][v3];\
[v1]scale=1920:1080[hd];\
[v2]scale=1280:720[sd];\
[v3]scale=640:360[mobile]" \
-map "[hd]" hd.mp4 \
-map "[sd]" sd.mp4 \
-map "[mobile]" mobile.mp4
# Split video and audio separately
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]split=2[v1][v2];\
[0:a]asplit=2[a1][a2];\
[v1]scale=1280:720[vout1];\
[v2]scale=640:360[vout2]" \
-map "[vout1]" -map "[a1]" output1.mp4 \
-map "[vout2]" -map "[a2]" output2.mp4
# Concatenate with format normalization
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 \
-filter_complex "\
[0:v]scale=1920:1080,fps=30,setpts=PTS-STARTPTS[v0];\
[1:v]scale=1920:1080,fps=30,setpts=PTS-STARTPTS[v1];\
[2:v]scale=1920:1080,fps=30,setpts=PTS-STARTPTS[v2];\
[0:a]aformat=sample_rates=48000:channel_layouts=stereo[a0];\
[1:a]aformat=sample_rates=48000:channel_layouts=stereo[a1];\
[2:a]aformat=sample_rates=48000:channel_layouts=stereo[a2];\
[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" output.mp4
# GPU decode -> GPU filter -> GPU encode
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i main.mp4 \
-hwaccel cuda -hwaccel_output_format cuda -i overlay.mp4 \
-filter_complex "\
[0:v]scale_cuda=1920:1080[main];\
[1:v]scale_cuda=320:180[pip];\
[main][pip]overlay_cuda=W-w-10:H-h-10[out]" \
-map "[out]" -map 0:a \
-c:v h264_nvenc -preset p4 output.mp4
When you need CPU-only filters (drawtext, subtitles), use hwdownload/hwupload:
# GPU scale -> CPU text -> GPU encode
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 \
-filter_complex "\
[0:v]scale_cuda=1920:1080[scaled];\
[scaled]hwdownload,format=nv12[cpu];\
[cpu]drawtext=text='Watermark':fontsize=48:x=10:y=10[text];\
[text]hwupload_cuda[out]" \
-map "[out]" \
-c:v h264_nvenc output.mp4
# Generate multiple resolutions on GPU
ffmpeg -y -vsync 0 \
-hwaccel cuda \
-hwaccel_output_format cuda \
-i input.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3];\
[v1]scale_cuda=1920:1080[hd];\
[v2]scale_cuda=1280:720[sd];\
[v3]scale_cuda=640:360[mobile]" \
-map "[hd]" -c:v h264_nvenc -b:v 5M output_1080p.mp4 \
-map "[sd]" -c:v h264_nvenc -b:v 2M output_720p.mp4 \
-map "[mobile]" -c:v h264_nvenc -b:v 500k output_360p.mp4
# Video with lower third, logo, and timecode
ffmpeg -i main.mp4 -i logo.png -i lower_third.png \
-filter_complex "\
[1:v]scale=150:-1[logo];\
[2:v]scale=400:-1[third];\
[0:v][logo]overlay=W-w-20:20[step1];\
[step1][third]overlay=50:H-h-80:enable='between(t,5,55)'[step2];\
[step2]drawtext=text='%{pts\\:hms}':\
fontsize=32:fontcolor=white:\
x=W-tw-20:y=H-th-20[out]" \
-map "[out]" -map 0:a output.mp4
# Waveform overlay on video
ffmpeg -i video.mp4 -i audio.mp3 \
-filter_complex "\
[1:a]showwaves=s=1920x200:mode=cline:colors=white@0.8[wave];\
[0:v][wave]overlay=0:H-h-50[out]" \
-map "[out]" -map 1:a output.mp4
# Spectrum analyzer
ffmpeg -i video.mp4 \
-filter_complex "\
[0:a]showspectrum=s=1920x200:legend=0:color=rainbow[spec];\
[0:v][spec]overlay=0:H-h[out]" \
-map "[out]" -map 0:a output.mp4
# Different effects at different times
ffmpeg -i input.mp4 \
-filter_complex "\
[0:v]split=3[v1][v2][v3];\
[v1]trim=0:5,setpts=PTS-STARTPTS[part1];\
[v2]trim=5:10,setpts=PTS-STARTPTS,eq=brightness=0.2:saturation=2.0[part2];\
[v3]trim=10,setpts=PTS-STARTPTS[part3];\
[part1][part2][part3]concat=n=3:v=1:a=0[out]" \
-map "[out]" -map 0:a output.mp4
# Enable debug logging
ffmpeg -loglevel debug -i input.mp4 -filter_complex "..." output.mp4
# Show filter graph info
ffmpeg -filter_complex_script script.txt -lavfi "..." 2>&1 | grep -i filter
# Test filtergraph without encoding (null output)
ffmpeg -i input.mp4 -filter_complex "..." -f null -
# Check specific filter options
ffmpeg -h filter=overlay
ffmpeg -h filter=xfade
ffmpeg -h filter=amix
| Error | Cause | Solution |
|---|---|---|
| "Input pad not connected" | Missing input label | Add [0:v] or correct label |
| "Output pad not connected" | Unused filter output | Add -map or connect to next filter |
| "Stream specifier not found" | Invalid stream reference | Check input has that stream type |
| "Filter not found" | Missing filter in FFmpeg build | Check ffmpeg -filters |
| "Discarding frame" | Timestamp discontinuity | Add setpts=PTS-STARTPTS |
-threads 0 to auto-detect optimal thread count[scaled] not [v1] for clarity-t 5 to test first 5 seconds-mapThis guide covers filter_complex patterns for 2025. For hardware acceleration details, see ffmpeg-hardware-acceleration. For basic filter syntax, see ffmpeg-fundamentals-2025.
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.