agentleFS
Galbaz1Galbaz1/video-research-mcp/skills/ffmpeg-production/SKILL.md

ffmpeg-production

Skill23 starsChanged 39 days agoMITView on GitHub

FFmpeg video/audio processing — conversion, scaling, compression, trimming, concatenation, AI post-processing. Not for audio ducking/voice mixing (tts-production) or Remotion rendering.

  • Installs packages

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this order when these filters are needed.

    line 14
  2. Distinctive:

    Use two passes when targeting linear normalization.

    line 203

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

FFmpeg Production

FFmpeg recipes for video post-production. Covers the operations you reach for daily: format conversion, scaling, trimming, concatenation, compression, and AI video post-processing.

For platform-specific export presets (YouTube, TikTok, Instagram, Twitter), see references/platform-presets.md.

Post-Processing Filter Order

Use this order when these filters are needed. Compare against the original; unnecessary processing can remove detail or introduce artifacts.

[1] Temporal denoise  — remove inter-frame shimmer before any processing
[2] Scale             — scale before sharpening avoids halos
[3] Sharpen           — recover edge detail lost in generation/upscale
[4] Color grade (LUT) — normalize over-saturated AI palette
[5] Curves/EQ         — fine-tune contrast and shadow lift
[6] Film grain        — LAST before encode; grain before denoising destroys it
[7] Encode            — libx265 -tune grain or AV1 FGS

Denoising can remove added grain, and interpolation can distort it. Add grain after those operations.

Full chain as a single filtergraph:

ffmpeg -i input.mp4 \
  -vf "hqdn3d=2:2:3:3,\
       scale=1920:1080:flags=lanczos,\
       unsharp=5:5:0.8:5:5:0.4,\
       lut3d=grade.cube,\
       curves=r='0/0 0.5/0.52 1/1':b='0/0.03 1/0.97',\
       noise=alls=6:allf=t+u" \
  -c:v libx265 -crf 18 -preset slow -tune grain \
  -c:a copy output.mp4

Hald CLUT Color Grading

For iterative grading, export a Hald CLUT, edit it in Photoshop/GIMP, and re-import. LUT edits leave the source untouched; rendering the graded video still encodes it.

ffmpeg -i input.mp4 -i hald_clut_graded.png \
  -filter_complex "[0:v][1:v] haldclut" \
  -c:v libx265 -crf 18 output.mp4

Format Conversion

Codec Selection

TargetCodecWhen to use
Web/streaminglibx264Universal compatibility
Compact retained copylibx265Compare quality/size for the actual material; CRF encoding is lossy
Modern distributionlibsvtav1Compare compression, encode time, and target-player support
Browser embedlibvpx-vp9WebM container, good compression

Essential Flags

FlagPurpose
-movflags +faststartMoves moov atom to front for web streaming
-pix_fmt yuv420pEnsures player compatibility
-preset slow/medium/fastEncode speed vs compression tradeoff
-crf 18-28Quality control (lower = better, 23 is default)
-profile:v highH.264 feature set (high for quality, baseline for compat)

CRF Quality Guide

Use caseCRFPresetNotes
Archive/master18slowBest quality, large files
Production20-22mediumGood balance
Web/preview23-25fastSmaller files
Draft/quick28+veryfastFast encoding

GIF to MP4

ffmpeg -i input.gif -movflags faststart -pix_fmt yuv420p \
  -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" output.mp4

Compress Video

# Good quality, smaller file
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4

# Target file size (e.g., ~10MB for 60s = ~1.3Mbps)
ffmpeg -i input.mp4 -c:v libx264 -b:v 1300k -c:a aac -b:a 128k output.mp4

Video Scaling and Cropping

# Scale to 1080p (letterbox — maintain aspect ratio, add black bars)
ffmpeg -i input.mp4 \
  -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \
  output.mp4

# Scale to 1080p (crop to fill)
ffmpeg -i input.mp4 \
  -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080" \
  output.mp4

# Scale to width, auto height (even dimensions)
ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4

Audio Extraction and Conversion

# Extract to MP3
ffmpeg -i input.mp4 -vn -acodec libmp3lame -q:a 2 output.mp3

# Extract to AAC
ffmpeg -i input.mp4 -vn -acodec aac -b:a 192k output.m4a

# Extract to WAV (uncompressed)
ffmpeg -i input.mp4 -vn output.wav

# M4A to MP3
ffmpeg -i input.m4a -codec:a libmp3lame -qscale:a 2 output.mp3

# Adjust volume
ffmpeg -i input.mp3 -filter:a "volume=1.5" output.mp3

Trimming and Cutting

# Cut from timestamp for duration (recommended — re-encode for accuracy)
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c:v libx264 -c:a aac output.mp4

# Cut between timestamps
ffmpeg -i input.mp4 -ss 00:00:30 -to 00:00:45 -c:v libx264 -c:a aac output.mp4

# Stream copy (faster but may lose frames at non-keyframe cut points)
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c copy output.mp4

Re-encoding is recommended for trimming. Stream copy (-c copy) can silently drop video if the seek point misses a keyframe.

Concatenation

# Create file list
printf "file '%s'\n" clip1.mp4 clip2.mp4 clip3.mp4 > list.txt

# Concatenate (same codec/resolution — stream copy)
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4

# Concatenate (mixed sources — re-encode)
ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -c:a aac output.mp4

Speed Change

# 2x speed (video + audio)
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

# 0.5x slow motion
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]" \
  -map "[v]" -map "[a]" output.mp4

# Video only (drop audio)
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" -an output.mp4

Speed math: To fit X seconds into Y seconds: speed = X / Y, setpts = 1/speed * PTS, atempo = speed.

Extreme speeds (>2x audio): atempo accepts 0.5–100, but values above 2 skip samples. Chain factors at or below 2 to avoid that behavior (filter contract):

# 4x audio: atempo=2.0,atempo=2.0
# 8x audio: atempo=2.0,atempo=2.0,atempo=2.0

Fade and Transitions

# Video fade in/out (1s each, 10s video)
ffmpeg -i input.mp4 \
  -vf "fade=t=in:st=0:d=1,fade=t=out:st=9:d=1" -c:a copy output.mp4

# Audio fade in/out
ffmpeg -i input.mp4 \
  -af "afade=t=in:st=0:d=1,afade=t=out:st=9:d=1" -c:v copy output.mp4

Two-Pass Loudnorm (EBU R128)

Use two passes when targeting linear normalization. Supply all measured values; the filter falls back to dynamic mode if the target LRA or true-peak constraints prevent linear gain. Inspect its reported mode (loudnorm contract).

# Pass 1: measure
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-16:LRA=11:TP=-1.5:print_format=json" \
  -f null - 2>loudnorm_stats.json

# Pass 2: apply (substitute measured values from stats)
ffmpeg -i input.mp4 \
  -af "loudnorm=I=-16:LRA=11:TP=-1.5:measured_I=-18.2:measured_LRA=9.1:measured_TP=-3.2:measured_thresh=-28.5:linear=true" \
  output_normalized.mp4

Target levels: YouTube -14 LUFS TP -1 dBFS | Streaming -14 LUFS TP -2 dBFS | Broadcast EBU R128 -23 LUFS TP -1 dBFS.

Probe and Validate

# Get duration
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4

# Full info as JSON
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4

# Inspect the reported video codec; verify decode/playback separately
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 output.mp4

Common Errors

ErrorFix
"height not divisible by 2"Add -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2"
Video won't play in browserUse -movflags faststart -pix_fmt yuv420p -c:v libx264
Audio out of sync after speed changeUse filter_complex with matched setpts + atempo
Output 0 bytesCheck full ffmpeg output — silent failures hide in stderr
"encoder not found"Install FFmpeg with full codecs (brew install ffmpeg)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.