Video Editor — Professional Post-Production
Companion skills: remotion · video-gen · faceless-youtube
When to Use
Trigger phrases:
- "edit this video" · "add captions" · "color grade" · "make a promo video"
- "merge clips" · "add music" · "watermark" · "optimize for instagram"
- "speed ramp" · "slow motion" · "lower third" · "add transitions"
Use cases:
- Post-production editing of AI-generated or recorded footage
- Adding captions/subtitles with brand styling
- Color grading and visual correction
- Audio mixing, ducking, and loudness normalization
- Creating promotional videos from raw clips
- Platform-specific export (YouTube, TikTok, Instagram, LinkedIn)
- Batch processing multiple videos
- Thumbnail extraction and creation
When NOT to use:
- When creating video from scratch with React (use remotion)
- When generating video from AI models (use video-gen)
- When the task requires real-time editing (use a NLE like DaVinci Resolve)
Overview
FFmpeg is the industry-standard tool for video post-production. This skill covers professional-grade filter graphs for promotional videos: color grading, audio design, kinetic text, transitions, motion effects, and multi-platform export.
Process
- Ingest — Analyze source footage (duration, resolution, codec, audio levels)
- Assembly — Trim, arrange, and concatenate clips
- Color grade — Apply LUTs, curves, contrast, and color harmony
- Audio design — Mix music, voiceover, SFX; apply ducking and normalization
- Add text — Captions, lower thirds, title cards, CTAs
- Add brand — Logo overlay, watermark, consistent color scheme
- Transitions — Crossfade, wipe, or zoom between scenes
- Motion effects — Speed ramps, slow-mo, Ken Burns, motion blur
- Export — Master file + platform-specific optimized versions
- Thumbnail — Extract best frame, add text overlay
Promo Video Structure
Professional promotional videos follow a proven narrative arc:
┌─────────────────────────────────────────────────────────────────┐
│ HOOK (0-3s) │ Grab attention immediately │
│ Bold text / motion │ "Stop scrolling" moment │
├───────────────────────┼─────────────────────────────────────────┤
│ PROBLEM (3-8s) │ State the pain point │
│ Text + B-roll │ "You're wasting hours on..." │
├───────────────────────┼─────────────────────────────────────────┤
│ SOLUTION (8-20s) │ Show your product/service │
│ Screen recording / │ "Here's how X fixes this" │
│ product demo │ │
├───────────────────────┼─────────────────────────────────────────┤
│ PROOF (20-30s) │ Social proof, metrics, testimonials │
│ Stats / quotes │ "10K+ users" / "4.9★ rating" │
├───────────────────────┼─────────────────────────────────────────┤
│ CTA (30-45s) │ Clear call to action │
│ Bold text + logo │ "Try free today" + URL │
└─────────────────────────────────────────────────────────────────┘
Timing Guide
| Section | Duration | Frame Count (30fps) |
|---|---|---|
| Hook | 2-3s | 60-90 |
| Problem | 3-5s | 90-150 |
| Solution | 10-15s | 300-450 |
| Proof | 5-8s | 150-240 |
| CTA | 3-5s | 90-150 |
| Total | 25-40s | 750-1200 |
Professional FFmpeg Filter Graphs
1. Color Grading
Apply LUT (Look-Up Table)
# Apply a .cube LUT file for cinematic color grading
ffmpeg -i input.mp4 -vf "lut3d=file='cinematic.cube'" -c:a copy graded.mp4
# Apply LUT with reduced intensity (50% blend)
ffmpeg -i input.mp4 -vf "
[0:v]split[orig][lut];
[lut]lut3d=file='cinematic.cube'[graded];
[orig][graded]blend=all_expr='A*(1-0.5)+B*0.5'
" -c:a copy graded-subtle.mp4
Manual Color Correction
# Lift shadows, boost highlights, add warmth
ffmpeg -i input.mp4 -vf "
curves=lighter=0/0 0.25/0.22 0.5/0.52 0.75/0.78 1/1:
green=0/0 0.5/0.48 1/1:
blue=0/0 0.5/0.47 1/1,
eq=brightness=0.03:contrast=1.1:saturation=1.15
" -c:a copy color-corrected.mp4
Cinematic Teal & Orange
ffmpeg -i input.mp4 -vf "
curves=
r='0/0.05 0.3/0.25 0.5/0.55 0.7/0.75 1/0.9':
g='0/0.03 0.3/0.2 0.5/0.48 0.7/0.7 1/0.85':
b='0/0.1 0.3/0.3 0.5/0.45 0.7/0.6 1/0.7',
eq=saturation=1.3:contrast=1.15,
unsharp=5:5:0.8
" -c:a copy teal-orange.mp4
High-Contrast Promo Look
ffmpeg -i input.mp4 -vf "
eq=contrast=1.3:brightness=0.02:saturation=1.2,
curves=master='0/0 0.15/0.05 0.5/0.55 0.85/0.95 1/1',
unsharp=5:5:1.0
" -c:a copy high-contrast-promo.mp4
2. Audio Design
Background Music + Voiceover Mixing
# Mix voiceover (primary) with background music (ducked)
ffmpeg -i voiceover.mp4 -i music.mp3 -filter_complex "
[0:a]volume=1.0[vo];
[1:a]volume=0.15[music];
[vo][music]amix=inputs=2:duration=shortest:dropout_transition=3
" -c:v copy -c:a aac -b:a 192k mixed.mp4
Audio Ducking (Auto-Lower Music During Speech)
# Sidechain compress: music ducks when voice is present
ffmpeg -i voiceover.mp4 -i music.mp3 -filter_complex "
[1:a][0:a]sidechaincompress=threshold=0.02:ratio=10:attack=200:release=1000[ducked];
[0:a]volume=1.0[vo];
[vo][ducked]amix=inputs=2:duration=shortest
" -c:v copy -c:a aac ducked-mix.mp4
Loudness Normalization (EBU R128)
# Normalize to broadcast standard (-14 LUFS for streaming)
ffmpeg -i input.mp4 -af "loudnorm=I=-14:TP=-1.5:LRA=11" -c:v copy normalized.mp4
# Two-pass for accurate normalization
ffmpeg -i input.mp4 -af "loudnorm=I=-14:TP=-1.5:LRA=11:print_format=json" -f null - 2>&1 | tail -12
# Then apply measured values in second pass:
ffmpeg -i input.mp4 -af "loudnorm=I=-14:TP=-1.5:LRA=11:measured_I=-20:measured_TP=-3:measured_LRA=8" -c:v copy normalized.mp4
Fade In/Out
# Audio fade in (0.5s) and fade out (1s) at end
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=0.5,afade=t=out:st=29:d=1" -c:v copy audio-faded.mp4
3. Text & Captions
Animated Title Card
# Title that fades in, holds, fades out (frames 0-90 = 3s at 30fps)
ffmpeg -i input.mp4 -vf "
drawtext=text='YOUR BRAND':
fontfile=fonts/Bold.ttf:
fontsize=120:
fontcolor=white:
borderw=3:
bordercolor=black@0.3:
x=(w-text_w)/2:
y=(h-text_h)/2:
enable='between(t,0,3)':
alpha='if(lt(t,0.5),t*2,if(gt(t,2.5),(3-t)*2,1))'
" -c:a copy title-card.mp4
Lower Third (Name + Title)
# Animated lower third with background bar
ffmpeg -i input.mp4 -vf "
drawbox=x=0:y=ih-160:w=iw:h=160:color=black@0.7:t=fill:
enable='between(t,2,8)',
drawtext=text='John Smith':
fontfile=fonts/Bold.ttf:fontsize=48:fontcolor=white:
x=60:y=ih-130:
enable='between(t,2,8)':
alpha='if(lt(t,2.5),(t-2)*2,if(gt(t,7.5),(8-t)*2,1))',
drawtext=text='CEO, Company':
fontfile=fonts/Regular.ttf:fontsize=32:fontcolor=white@0.8:
x=60:y=ih-80:
enable='between(t,2,8)':
alpha='if(lt(t,2.5),(t-2)*2,if(gt(t,7.5),(8-t)*2,1))'
" -c:a copy lower-third.mp4
Styled Subtitles (Brand Colors)
# Burn subtitles with custom styling
ffmpeg -i input.mp4 -vf "
subtitles=captions.srt:
force_style='
FontName=Inter,
FontSize=28,
PrimaryColour=&H00FFFFFF,
OutlineColour=&H00000000,
BackColour=&H80000000,
Bold=1,
Outline=2,
Shadow=1,
MarginV=60,
Alignment=2
'
" -c:a copy captioned.mp4
Word-by-Word Highlight Captions (ASS Format)
# Generate ASS file with word-by-word highlighting
cat > captions.ass << 'EOF'
[Script Info]
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Inter,48,&H00FFFFFF,&H0000FFFF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,3,1,2,40,40,60,1
Style: Highlight,Inter,48,&H0000FFFF,&H0000FFFF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,3,1,2,40,40,60,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:03.00,Default,,0,0,0,,{\k50}This {\k30}is {\k40}your {\k50}brand
EOF
ffmpeg -i input.mp4 -vf "ass=captions.ass" -c:a copy word-highlight.mp4
4. Transitions
Crossfade Between Clips
# 1-second crossfade between two clips
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex "
[0:v]fade=t=out:st=4:d=1[v0];
[1:v]fade=t=in:st=0:d=1[v1];
[v0][v1]concat=n=2:v=1:a=0[outv];
[0:a]afade=t=out:st=4:d=1[a0];
[1:a]afade=t=in:st=0:d=1[a1];
[a0][a1]concat=n=2:v=0:a=1[outa]
" -map "[outv]" -map "[outa]" crossfade.mp4
Wipe Transition
# Horizontal wipe (left to right) between clips
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex "
[0:v][1:v]xfade=transition=wipeleft:duration=1:offset=4
" -c:a copy wipe-transition.mp4
Available xfade Transitions
| Transition | Name | Effect |
|---|---|---|
fade |
fade |
Crossfade |
wipeleft |
wipeleft |
Horizontal wipe L→R |
wiperight |
wiperight |
Horizontal wipe R→L |
wipeup |
wipeup |
Vertical wipe bottom→top |
wipedown |
wipedown |
Vertical wipe top→bottom |
slideleft |
slideleft |
Slide L→R |
slideright |
slideright |
Slide R→L |
circleopen |
circleopen |
Circle open reveal |
circleclose |
circleclose |
Circle close |
dissolve |
dissolve |
Dissolve |
pixelize |
pixelize |
Pixelize transition |
diagtl |
diagtl |
Diagonal from top-left |
5. Motion Effects
Speed Ramp (Slow-Mo → Normal)
# Slow-mo section (2x slower from 5s to 8s), normal elsewhere
ffmpeg -i input.mp4 -filter_complex "
[0:v]setpts='if(between(T,5,8),PTS*2,PTS)'[v];
[0:a]atempo='if(between(T,5,8),0.5,1.0)'[a]
" -map "[v]" -map "[a]" speed-ramp.mp4
Ken Burns (Slow Zoom on Still Image)
# 10-second Ken Burns zoom-in on a product image
ffmpeg -loop 1 -i product.jpg -t 10 -vf "
zoompan=z='min(zoom+0.001,1.5)':
x='iw/2-(iw/zoom/2)':
y='ih/2-(ih/zoom/2)':
d=300:
s=1920x1080:fps=30
" -c:v libx264 -pix_fmt yuv420p ken-burns.mp4
Motion Blur (For Fast Movements)
ffmpeg -i input.mp4 -vf "
tmix=frames=4:weights='1 1 1 1'
" -c:a copy motion-blur.mp4
6. Brand Elements
Logo Overlay (Corner)
# Logo in bottom-right corner, 10% opacity watermarked
ffmpeg -i input.mp4 -i logo.png -filter_complex "
[1:v]scale=150:-1,format=rgba,colorchannelmixer=aa=0.3[logo];
[0:v][logo]overlay=W-w-30:H-h-30
" -c:a copy watermarked.mp4
Animated Logo Intro (Logo + Fade)
# Logo appears centered, fades in over 0.5s, holds 2s, fades out
ffmpeg -i input.mp4 -i logo.png -filter_complex "
[1:v]scale=300:-1,format=rgba[logo];
[0:v][logo]overlay=(W-w)/2:(H-h)/2:
enable='between(t,0,3)':
format=auto
" -c:a copy logo-intro.mp4
7. Multi-Clip Assembly
Full Promo Assembly Pipeline
# Step 1: Prepare individual clips (trim, color grade, normalize audio)
ffmpeg -i raw_hook.mp4 -t 3 -vf "eq=contrast=1.2:saturation=1.1" -af "loudnorm=I=-14" -c:v libx264 -crf 18 hook.mp4
ffmpeg -i raw_problem.mp4 -ss 2 -t 5 -vf "eq=contrast=1.2:saturation=1.1" -af "loudnorm=I=-14" -c:v libx264 -crf 18 problem.mp4
ffmpeg -i raw_solution.mp4 -ss 0 -t 12 -vf "eq=contrast=1.2:saturation=1.1" -af "loudnorm=I=-14" -c:v libx264 -crf 18 solution.mp4
ffmpeg -i raw_proof.mp4 -ss 5 -t 8 -vf "eq=contrast=1.2:saturation=1.1" -af "loudnorm=I=-14" -c:v libx264 -crf 18 proof.mp4
ffmpeg -i raw_cta.mp4 -t 4 -vf "eq=contrast=1.2:saturation=1.1" -af "loudnorm=I=-14" -c:v libx264 -crf 18 cta.mp4
# Step 2: Create concat list with crossfades
cat > clips.txt << EOF
file 'hook.mp4'
file 'problem.mp4'
file 'solution.mp4'
file 'proof.mp4'
file 'cta.mp4'
EOF
# Step 3: Concatenate with xfade transitions
ffmpeg -i hook.mp4 -i problem.mp4 -i solution.mp4 -i proof.mp4 -i cta.mp4 -filter_complex "
[0:v][1:v]xfade=transition=fade:duration=0.5:offset=2.5[v01];
[v01][2:v]xfade=transition=wipeleft:duration=0.5:offset=7[v012];
[v012][3:v]xfade=transition=fade:duration=0.5:offset=18.5[v0123];
[v0123][4:v]xfade=transition=fade:duration=0.5:offset=26[vout];
[0:a][1:a]acrossfade=d=0.5[a01];
[a01][2:a]acrossfade=d=0.5[a012];
[a012][3:a]acrossfade=d=0.5[a0123];
[a0123][4:a]acrossfade=d=0.5[aout]
" -map "[vout]" -map "[aout]" -c:v libx264 -crf 18 -c:a aac -b:a 192k promo-assembled.mp4
# Step 4: Add music bed with ducking
ffmpeg -i promo-assembled.mp4 -i bg-music.mp3 -filter_complex "
[0:a]volume=1.0[vo];
[1:a]volume=0.12,afade=t=out:st=30:d=3[music];
[music][vo]sidechaincompress=threshold=0.02:ratio=12:attack=150:release=800[ducked];
[vo][ducked]amix=inputs=2:duration=shortest:dropout_transition=2,
loudnorm=I=-14:TP=-1.5:LRA=11
" -c:v copy -c:a aac -b:a 192k promo-with-music.mp4
# Step 5: Add logo watermark
ffmpeg -i promo-with-music.mp4 -i logo.png -filter_complex "
[1:v]scale=120:-1,format=rgba,colorchannelmixer=aa=0.25[logo];
[0:v][logo]overlay=W-w-25:H-h-25
" -c:a copy promo-final-master.mp4
Batch Processing
Export for All Platforms
#!/bin/bash
# export-all.sh — Export master video for all platforms
MASTER="promo-final-master.mp4"
OUTDIR="exports"
mkdir -p "$OUTDIR"
# YouTube (16:9, 1080p)
ffmpeg -i "$MASTER" -vf "scale=1920:1080" -c:v libx264 -preset slow -crf 18 -c:a aac -b:a 192k "$OUTDIR/youtube.mp4"
# Instagram Reels (9:16, 1080x1920)
ffmpeg -i "$MASTER" -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:white" -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -t 90 "$OUTDIR/instagram-reels.mp4"
# TikTok (9:16, 1080x1920)
ffmpeg -i "$MASTER" -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -t 600 "$OUTDIR/tiktok.mp4"
# Instagram Feed (1:1, 1080x1080)
ffmpeg -i "$MASTER" -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:white" -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -t 60 "$OUTDIR/instagram-feed.mp4"
# LinkedIn (16:9, 1080p)
ffmpeg -i "$MASTER" -vf "scale=1920:1080" -c:v libx264 -preset slow -crf 20 -c:a aac -b:a 192k -t 600 "$OUTDIR/linkedin.mp4"
# X / Twitter (16:9, 720p)
ffmpeg -i "$MASTER" -vf "scale=1280:720" -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -t 140 "$OUTDIR/twitter.mp4"
# Thumbnail
ffmpeg -i "$MASTER" -ss 00:00:03 -vframes 1 -q:v 2 "$OUTDIR/thumbnail.jpg"
echo "✅ Exported to $OUTDIR/"
ls -lh "$OUTDIR/"
Platform Specifications
| Platform | Resolution | Aspect | Max Length | Codec | CRF | Audio |
|---|---|---|---|---|---|---|
| YouTube | 1920×1080 | 16:9 | Unlimited | H.264 | 18 | AAC 192k |
| Instagram Reels | 1080×1920 | 9:16 | 90s | H.264 | 22 | AAC 128k |
| Instagram Feed | 1080×1080 | 1:1 | 60s | H.264 | 22 | AAC 128k |
| TikTok | 1080×1920 | 9:16 | 10min | H.264 | 22 | AAC 128k |
| 1920×1080 | 16:9 | 10min | H.264 | 20 | AAC 192k | |
| X / Twitter | 1280×720 | 16:9 | 2:20 | H.264 | 22 | AAC 128k |
| 1280×720 | 16:9 | 240min | H.264 | 22 | AAC 128k |
Color Grading Presets
Cinematic (Teal & Orange)
-vf "curves=r='0/0.05 0.3/0.25 0.5/0.55 0.7/0.75 1/0.9':g='0/0.03 0.3/0.2 0.5/0.48 0.7/0.7 1/0.85':b='0/0.1 0.3/0.3 0.5/0.45 0.7/0.6 1/0.7',eq=saturation=1.3:contrast=1.15"
Clean Corporate (Bright, High Contrast)
-vf "eq=brightness=0.03:contrast=1.2:saturation=1.1,curves=master='0/0 0.15/0.08 0.5/0.55 0.85/0.95 1/1'"
Moody Dark (Low Key)
-vf "eq=brightness=-0.05:contrast=1.3:saturation=0.9,curves=master='0/0 0.2/0.1 0.5/0.4 0.8/0.75 1/0.9'"
Vibrant Social Media (Punchy Colors)
-vf "eq=brightness=0.02:contrast=1.25:saturation=1.4,unsharp=5:5:0.8"
Desaturated Premium (Muted, Elegant)
-vf "eq=saturation=0.7:contrast=1.15,curves=master='0/0.02 0.3/0.28 0.5/0.52 0.7/0.73 1/0.95'"
Anti-Rationalization Table
| Rationalization | Reality |
|---|---|
| "Just concat the clips" | Raw concatenation has jarring cuts. Use xfade transitions for professional flow. |
| "Skip color grading" | Ungraded footage looks amateur. Even subtle correction (contrast +5%, saturation +10%) transforms quality. |
| "Audio doesn't matter" | Audio is 50% of perceived quality. Normalize loudness, mix music properly, add SFX. |
| "Burn-in captions are fine" | Unstyled captions look auto-generated. Use brand colors, proper fonts, and word-by-word highlighting. |
| "One export for all platforms" | Each platform has different specs. A 16:9 video letterboxed on 9:16 looks terrible. Export per platform. |
| "Skip the thumbnail" | Thumbnail is the #1 click driver. Extract the best frame, add bold text overlay. |
| "CRF 23 is good enough" | For promotional content, use CRF 18. Quality is visible, especially on large screens. |
| "Audio ducking is optional" | Without ducking, music drowns voiceover. Professional promos always duck music during speech. |
Verification
- Audio normalized to -14 LUFS (streaming standard)
- Music ducked during voiceover/speech
- Color grading applied consistently across all clips
- Transitions between scenes are smooth (xfade, not hard cuts)
- Captions styled with brand colors and fonts
- Logo/watermark placed consistently
- CTA is bold, readable, and appears long enough to read
- Exported for each target platform with correct specs
- Thumbnail extracted and styled
- Final video watched from start to finish before delivery