FFmpeg for Video Production
FFmpeg is the essential tool for video/audio processing. This skill covers common operations for Remotion video projects.
Quick Reference
GIF to MP4 (Remotion-compatible)
ffmpeg -i input.gif -movflags faststart -pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" output.mp4
Why these flags:
-movflags faststart - Moves metadata to start for web streaming
-pix_fmt yuv420p - Ensures compatibility with most players
scale=trunc(...) - Forces even dimensions (required by most codecs)
Resize Video
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" output.mp4
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080" output.mp4
ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4
Compress Video
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 96k output.mp4
ffmpeg -i input.mp4 -c:v libx264 -b:v 1300k -c:a aac -b:a 128k output.mp4
Extract Audio
ffmpeg -i input.mp4 -vn -acodec libmp3lame -q:a 2 output.mp3
ffmpeg -i input.mp4 -vn -acodec aac -b:a 192k output.m4a
ffmpeg -i input.mp4 -vn output.wav
Convert Audio Formats
ffmpeg -i input.m4a -codec:a libmp3lame -qscale:a 2 output.mp3
ffmpeg -i input.wav -codec:a libmp3lame -b:a 192k output.mp3
ffmpeg -i input.mp3 -filter:a "volume=1.5" output.mp3
Trim/Cut Video
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c:v libx264 -c:a aac output.mp4
ffmpeg -i input.mp4 -ss 00:00:30 -to 00:00:45 -c:v libx264 -c:a aac output.mp4
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c copy output.mp4
Note: Re-encoding is recommended for trimming. Stream copy (-c copy) can silently drop video if the seek point doesn't align with a keyframe.
Speed Up / Slow Down
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
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
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" -an output.mp4
Concatenate Videos
echo "file 'clip1.mp4'" > list.txt
echo "file 'clip2.mp4'" >> list.txt
echo "file 'clip3.mp4'" >> list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -c:a aac output.mp4
Add Fade In/Out
ffmpeg -i input.mp4 -vf "fade=t=in:st=0:d=1,fade=t=out:st=9:d=1" -c:a copy output.mp4
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=1,afade=t=out:st=9:d=1" -c:v copy output.mp4
Get Video Info
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
Remotion-Specific Patterns
Video Speed Adjustment for Remotion
When to use FFmpeg vs Remotion playbackRate:
| Scenario |
Use FFmpeg |
Use Remotion |
| Constant speed (1.5x, 2x) |
Either works |
β
Simpler |
| Extreme speeds (>4x or <0.25x) |
β
More reliable |
May have issues |
| Variable speed (accelerate over time) |
β
Pre-process |
Complex workaround needed |
| Need perfect audio sync |
β
Guaranteed |
Usually fine |
| Demo needs to fit voiceover timing |
β
Pre-calculate |
Runtime adjustment |
Remotion limitation: playbackRate must be constant. Dynamic interpolation like playbackRate={interpolate(frame, [0, 100], [1, 5])} won't work correctly because Remotion evaluates frames independently.
ffmpeg -i demo-raw.mp4 \
-filter_complex "[0:v]setpts=0.333*PTS[v];[0:a]atempo=3.0[a]" \
-map "[v]" -map "[a]" \
public/demos/demo-fast.mp4
ffmpeg -i action.mp4 \
-filter_complex "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]" \
-map "[v]" -map "[a]" \
public/demos/action-slow.mp4
ffmpeg -i demo.mp4 -filter:v "setpts=0.5*PTS" -an public/demos/demo-2x.mp4
ffmpeg -i long-demo.mp4 -filter:v "setpts=0.1*PTS" -an public/demos/timelapse.mp4
Calculate speed factor:
- To fit X seconds of video into Y seconds of scene:
speed = X / Y
- setpts multiplier =
1 / speed (e.g., 3x speed = setpts=0.333*PTS)
- atempo value =
speed (e.g., 3x speed = atempo=3.0)
Extreme speed (>2x audio): Chain atempo filters (each limited to 0.5-2.0 range):
-filter_complex "[0:a]atempo=2.0,atempo=2.0[a]"
-filter_complex "[0:a]atempo=2.0,atempo=2.0,atempo=2.0[a]"
Prepare Demo Recording for Remotion
ffmpeg -i raw-recording.mp4 \
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30" \
-c:v libx264 -crf 18 -preset slow \
-c:a aac -b:a 192k \
-movflags faststart \
public/demos/demo.mp4
Screen Recording to Remotion Asset
ffmpeg -i iphone-recording.mov \
-vf "scale=1920:-2,fps=30" \
-c:v libx264 -crf 20 \
-an \
public/demos/mobile-demo.mp4
Batch Convert GIFs
for f in assets/*.gif; do
ffmpeg -i "$f" -movflags faststart -pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
"public/demos/$(basename "$f" .gif).mp4"
done
Common Issues
"Height not divisible by 2"
Add scale filter: -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2"
Video won't play in browser
Use: -movflags faststart -pix_fmt yuv420p -c:v libx264
Audio out of sync after speed change
Use filter_complex with atempo: -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]"
File too large
Increase CRF (23β28) or reduce resolution
Quality Guidelines
| Use Case |
CRF |
Preset |
Notes |
| Archive/Master |
18 |
slow |
Best quality, large files |
| Production |
20-22 |
medium |
Good balance |
| Web/Preview |
23-25 |
fast |
Smaller files |
| Draft/Quick |
28+ |
veryfast |
Fast encoding |
Platform-Specific Output Optimization
After Remotion renders your video (typically to out/video.mp4), use FFmpeg to optimize for each distribution platform.
Workflow Integration
Remotion render (master) FFmpeg optimization Platform upload
β β β
out/video.mp4 βββββββββ out/video-youtube.mp4 ββββ YouTube
βββββββββ out/video-twitter.mp4 ββββ Twitter/X
βββββββββ out/video-linkedin.mp4 ββββ LinkedIn
βββββββββ out/video-web.mp4 ββββ Website embed
YouTube (Recommended Settings)
YouTube re-encodes everything, so upload high quality:
ffmpeg -i out/video.mp4 \