plugins/ffmpeg-core/skills/ffmpeg-filter-complex-patterns/SKILL.md
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.
npx skillsauth add JosiahSiegel/claude-plugin-marketplace ffmpeg-filter-complex-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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:[email protected]:\
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:[email protected][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.
development
This skill should be used when the user asks to train, debug, scale, or improve ML models. PROACTIVELY activate for: (1) PyTorch, TensorFlow/Keras, JAX, Flax, Hugging Face Trainer/Accelerate training loops, (2) distributed training, DDP/FSDP/DeepSpeed, TPU/GPU setup, (3) mixed precision AMP/bf16, gradient accumulation, checkpointing, seeding, (4) overfitting, imbalance, loss functions, regularization, LR schedules, warmup, (5) memory optimization, gradient checkpointing, offloading, quantization-aware training. Provides: reproducible training best practices across deep learning and classical ML.
development
This skill should be used when the user asks to productionize, track, version, govern, monitor, or automate ML systems. PROACTIVELY activate for: (1) MLflow, Weights & Biases, Neptune, Comet, ClearML experiment tracking, (2) model registry, model versioning, artifact lineage, reproducibility, (3) Kubeflow, SageMaker Pipelines, Vertex AI Pipelines, Azure ML pipelines, Databricks workflows, (4) CI/CD, continuous training/evaluation, A/B tests, canary/shadow deployments, (5) drift detection, model monitoring, data validation, responsible AI governance. Provides: end-to-end MLOps architecture and operational safeguards.
development
This skill should be used when the user asks to optimize, export, serve, compress, or accelerate ML inference. PROACTIVELY activate for: (1) latency, throughput, p95/p99, batching, concurrency, KV cache, memory, or cost issues, (2) quantization INT8/INT4, GPTQ, AWQ, bitsandbytes, pruning, sparsity, distillation, (3) ONNX export, ONNX Runtime, TensorRT, TorchScript, torch.compile, XLA, OpenVINO, Core ML, TFLite, (4) Triton, TorchServe, TF Serving, BentoML, Seldon, KServe configuration, (5) edge deployment, CPU/GPU/TPU/Inferentia serving. Provides: hardware-aware inference optimization and safe benchmarking.
testing
This skill should be used when the user asks to tune hyperparameters, run sweeps, optimize search spaces, or use AutoML. PROACTIVELY activate for: (1) Optuna, Ray Tune, FLAML, AutoGluon, Hyperopt, Nevergrad, KerasTuner, W&B sweeps, (2) grid search, random search, Bayesian optimization, TPE, Gaussian processes, evolutionary search, (3) ASHA, Hyperband, successive halving, multi-fidelity optimization, population-based training, (4) learning-rate finder, batch-size search, early stopping, pruning, (5) reproducible sweep design and experiment analysis. Provides: budget-aware hyperparameter search strategy.