Beyond Basic Conversion: FFmpeg's Filter System
If you've spent any time with FFmpeg, you know it handles basic conversion effortlessly. But the real power lives inside its -vf and -af flags — the video and audio filter graphs that let you chain dozens of operations into a single command. Understanding how these work changes what you can accomplish without touching any GUI tool.
This guide assumes you're comfortable with basic FFmpeg commands. If you're starting from zero, check out how to use FFmpeg as a beginner first. Here we're going into the filter graph system, complex overlays, audio processing chains, and the kinds of multi-pass workflows that professionals rely on.
For quick one-off conversions and browser-based processing, ConvertIntoMP4's video tools handle most tasks without any command line. But when you need repeatable, scriptable filter pipelines, FFmpeg is unmatched.
How FFmpeg's Filter Graph Works
FFmpeg processes media through a graph of interconnected filters. Each filter takes one or more inputs, transforms them, and passes the result forward. The -vf flag is shorthand for a simple linear chain, while -filter_complex handles branching graphs with multiple inputs and outputs.
Simple Filter Chains (-vf)
The basic syntax strings filters together with commas:
ffmpeg -i input.mp4 -vf "scale=1280:720,unsharp=5:5:0.8" output.mp4
This scales to 720p then applies an unsharp mask for sharpening. The order matters — scale first, then sharpen the scaled result.
Complex Filter Graphs (-filter_complex)
When you have multiple inputs or need to split a stream, use -filter_complex:
ffmpeg -i video.mp4 -i logo.png -filter_complex "[1:v]scale=200:-1[logo];[0:v][logo]overlay=W-w-20:H-h-20" output.mp4
Breaking this down:
[1:v]selects the video stream from the second input (the logo)scale=200:-1scales it to 200px wide, preserving aspect ratio[logo]names this output stream[0:v][logo]overlay=W-w-20:H-h-20overlays the logo 20px from the bottom-right corner
You can accomplish watermarking visually through ConvertIntoMP4's watermark tool, but the filter_complex approach gives you pixel-perfect control for scripted workflows.
Essential Video Filters
Scale and Resize
FFmpeg's scale filter is more capable than it appears at first glance.
Standard resize:
-vf "scale=1920:1080"
Maintain aspect ratio (width only):
-vf "scale=1280:-1"
Force even dimensions (required by some codecs):
-vf "scale=1280:-2"
Using -2 instead of -1 ensures the calculated dimension is divisible by 2, which H.264 requires.
Scale to fit within bounds without stretching:
-vf "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease"
Scale with padding to exact dimensions:
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black"
This letterboxes the video into a 1920×1080 frame with black bars.
Crop
# Crop center 1280x720 from any resolution
-vf "crop=1280:720"
# Crop with offset (x, y from top-left)
-vf "crop=1280:720:200:100"
# Crop to 16:9 from center (resolution-aware)
-vf "crop=in_h*16/9:in_h"
For visual cropping, ConvertIntoMP4's crop video tool lets you drag a selection area without memorizing pixel math.
Color Correction
Brightness, contrast, saturation:
-vf "eq=brightness=0.05:contrast=1.2:saturation=1.3"
Values: brightness (-1 to 1), contrast (0 to 3), saturation (0 to 3).
Curves for precise color grading:
-vf "curves=red='0/0 0.5/0.6 1/1':blue='0/0 0.5/0.4 1/1'"
This lifts the reds and pulls down the blues — a warm color grade.
Levels adjustment:
-vf "levels=input_black_point=30:input_white_point=220"
Hue shift:
-vf "hue=h=30:s=1.2"
Denoising
Light noise reduction (fast):
-vf "hqdn3d=4:3:6:4.5"
Stronger denoising with nlmeans:
-vf "nlmeans=10:7:5:3:3"
NLmeans is significantly slower but preserves edges better. Good for footage with heavy grain or compression artifacts.
Sharpening
Unsharp mask:
-vf "unsharp=5:5:1.5:5:5:0"
Parameters: luma_msize_x:luma_msize_y:luma_amount:chroma_msize_x:chroma_msize_y:chroma_amount. Values above 1.5 for amount look harsh; 0.5-1.0 is more natural.
Sharpen with CAS (Contrast Adaptive Sharpening):
-vf "cas=strength=0.8"
Audio Filters
Volume Normalization
Simple volume boost:
-af "volume=1.5"
Loudnorm (broadcast standard -23 LUFS):
-af "loudnorm=I=-23:TP=-2:LRA=7"
This is a two-pass process for accurate results. The first pass analyzes, the second applies. For single-pass (less accurate):
-af "loudnorm=I=-16:TP=-1.5:LRA=11:linear=true:print_format=json"
Noise Reduction
High-pass filter (remove low-frequency rumble):
-af "highpass=f=200"
Low-pass filter (remove hiss above a frequency):
-af "lowpass=f=8000"
Combined for voice cleanup:
-af "highpass=f=80,lowpass=f=12000,afftdn=nf=-25"
afftdn is FFmpeg's spectral noise reduction filter.
De-essing (reduce sibilance):
-af "adeclick,highpass=f=80,aexciter=level_in=1:drive=8.5:harmonics=8.5:blend=0"
Audio Mixing and Channels
Mix stereo to mono:
-af "amerge=inputs=2,pan=mono|c0=0.5*c0+0.5*c1"
Delay one channel (for stereo widening):
-af "stereotools=delay=20"
Add echo:
-af "aecho=0.8:0.88:60:0.4"
Compress dynamic range:
-af "acompressor=threshold=0.089:ratio=9:attack=200:release=1000"
This is useful for podcast audio where some speakers are much louder than others.
Complex Filter Graph Examples
Overlay Logo in Corner
ffmpeg -i main_video.mp4 -i watermark.png \
-filter_complex "[1:v]scale=150:-1,format=rgba,colorchannelmixer=aa=0.7[logo];[0:v][logo]overlay=W-w-30:30" \
-codec:a copy output.mp4
This scales the watermark, makes it 70% transparent, then overlays it 30px from the top-right corner.
Picture-in-Picture
ffmpeg -i main.mp4 -i pip.mp4 \
-filter_complex "[1:v]scale=320:180[pip];[0:v][pip]overlay=W-w-20:H-h-20" \
-codec:a copy output.mp4
Add Subtitles Burned In
ffmpeg -i video.mp4 -vf "subtitles=subtitles.srt:force_style='FontSize=24,PrimaryColour=&H00FFFFFF'" output.mp4
Fade In/Out
ffmpeg -i input.mp4 -vf "fade=t=in:st=0:d=1,fade=t=out:st=29:d=1" \
-af "afade=t=in:st=0:d=1,afade=t=out:st=29:d=1" output.mp4
st is the start time in seconds, d is duration. Adjust based on your video's total duration.
Speed Change
Double speed (0.5 = half speed, 2.0 = double):
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" output.mp4
Note that atempo maxes out at 2.0. For faster speed changes, chain multiple atempo filters:
-af "atempo=2.0,atempo=2.0" # 4x speed audio
Split and Tile Multiple Videos
ffmpeg -i v1.mp4 -i v2.mp4 -i v3.mp4 -i v4.mp4 \
-filter_complex "[0:v][1:v]hstack[top];[2:v][3:v]hstack[bottom];[top][bottom]vstack" \
output.mp4
This creates a 2×2 grid from four input videos.
Two-Pass Encoding for Quality Control
For video destined for specific file sizes or bitrates, two-pass encoding produces significantly better quality than one-pass CRF encoding at the same file size.
Pass 1 (analysis only, writes to null):
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -pass 1 -an -f null /dev/null
Pass 2 (encode with gathered stats):
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -pass 2 output.mp4
Pro tip: CRF mode is generally better for achieving consistent visual quality, while two-pass target bitrate encoding is better when you need a specific file size. For web delivery where you want small files, ConvertIntoMP4's video compressor handles this automatically without needing two terminal windows.
Batch Processing with Filter Chains
Applying the same filters to multiple files is where scripting pays off:
#!/bin/bash
for file in *.mp4; do
ffmpeg -i "$file" \
-vf "scale=1920:-2,unsharp=5:5:0.8" \
-af "loudnorm=I=-16:TP=-1.5:LRA=11" \
-c:v libx264 -crf 22 -preset slow \
-c:a aac -b:a 192k \
"processed_${file}"
done
This script processes every MP4 in the current directory — scales to 1080p width, sharpens slightly, normalizes audio to -16 LUFS, and encodes with good quality settings.
For a visual batch processing approach, see how to batch convert files without command line tools.
Hardware Acceleration
Modern systems can offload encoding/decoding to GPU, dramatically reducing processing time:
NVIDIA (NVENC/NVDEC):
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset slow -cq 23 output.mp4
Intel Quick Sync:
ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv -global_quality 23 output.mp4
Apple Silicon (VideoToolbox):
ffmpeg -i input.mp4 -c:v h264_videotoolbox -q:v 65 output.mp4
Hardware encoders are generally faster but produce slightly larger files at the same quality level compared to software encoders like libx264 or libx265.
Filter Reference Table
| Filter | Purpose | Example |
|---|---|---|
scale | Resize video | scale=1280:720 |
crop | Cut to dimensions | crop=1280:720:0:0 |
overlay | Composite two inputs | overlay=10:10 |
unsharp | Sharpen/blur | unsharp=5:5:1.0 |
eq | Brightness/contrast | eq=brightness=0.1 |
curves | Color grading | curves=vintage |
fade | Fade in/out | fade=t=in:d=1 |
setpts | Speed control | setpts=0.5*PTS |
hstack | Side-by-side | [0:v][1:v]hstack |
vstack | Top-bottom | [0:v][1:v]vstack |
loudnorm | Audio normalization | loudnorm=I=-16 |
acompressor | Dynamic compression | acompressor=ratio=4 |
afftdn | Spectral denoising | afftdn=nf=-25 |
atempo | Audio speed | atempo=1.5 |
Common Mistakes and Fixes
Problem: Output is choppy or has dropped frames.
Fix: Add -vsync 1 or use -fps_mode passthrough for variable frame rate sources.
Problem: Audio goes out of sync after speed change.
Fix: Always pair setpts (video) with atempo (audio) when changing speed.
Problem: Overlay positioned incorrectly.
Fix: Use W (output width), H (output height), w (overlay width), h (overlay height) in overlay expressions. E.g., overlay=W-w-10:10 positions 10px from top-right.
Problem: Filter graph error "Filtergraph" with multiple outputs.
Fix: Each named output in filter_complex must be mapped explicitly with -map "[name]".
Problem: Color shift after scaling.
Fix: Add scale=...:flags=lanczos for better resampling, and ensure colorspace flags match the source.
Frequently Asked Questions
Can I preview a filter without encoding the full file?
Yes. Add -t 10 to process only the first 10 seconds, and use a fast preset:
ffmpeg -i input.mp4 -t 10 -vf "your_filter" -c:v libx264 -preset ultrafast preview.mp4
How do I know what filters are available?
Run ffmpeg -filters for a complete list. For details on a specific filter: ffmpeg -h filter=scale.
Does filter order matter?
Yes — filters process sequentially. Scale before sharpening sharpens the scaled result. Sharpening before scaling loses that work. Think of each step applying to the output of the previous one.
How do I apply a filter to only part of a video?
Use the enable expression. Example — apply sharpen only from 10s to 20s:
-vf "unsharp=5:5:1.0:enable='between(t,10,20)'"
Can I use GPU for filter processing too?
Some filters support hardware acceleration (like scale_cuda for NVIDIA). Most software filters still run on CPU even with hardware encoding. For general transcoding speed, the hardware encoding step is usually the bottleneck.
Wrapping Up
FFmpeg's filter system takes time to internalize, but the investment pays back repeatedly. Once you've built a filter chain that works for your workflow, it's reusable, scriptable, and infinitely tweakable. The examples above cover the most common real-world use cases — from color grading and noise reduction to overlays and speed changes.
When you want a visual interface instead of command-line work, tools like ConvertIntoMP4's video editor and compressor tools handle most common operations. But for automation, batch processing, and precise control, these filter techniques are essential.
The batch processing guide covers automating these commands across large file sets if you're working at scale.



