← All posts

FFmpeg Video Compression: Best Practices Guide

Master FFmpeg video compression with this deep technical guide covering CRF, presets, two-pass encoding, codec comparison, resolution scaling, and real-world scenarios.

FFHub·2026-05-11
FFmpeg Video Compression: Best Practices Guide

FFmpeg video compression comes down to balancing three knobs — CRF (quality), preset (speed), and codec (efficiency). This deep guide covers everything from CRF math to two-pass encoding, H.264 vs H.265 vs AV1 benchmarks, and real-world scenarios (web, social, archival, HLS). Every command in this guide is copy-paste ready.

Try it in your browser

Test compression settings in your browser before scripting them — drop a clip, pick CRF / preset, download the result.

Understanding CRF (Constant Rate Factor)

CRF is the most important setting for quality-based video compression. It tells the encoder to target a consistent visual quality level, letting the bitrate vary as needed. Lower CRF = higher quality = larger file. Higher CRF = lower quality = smaller file.

CRF Values for H.264 (libx264)

ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4

The CRF scale for H.264 ranges from 0 (lossless) to 51 (worst quality). Here's what different values produce:

CRFQuality LevelTypical Use CaseFile Size (relative to CRF 23)
0LosslessArchival, editing masters10-50x larger
14Near-losslessProfessional post-production4-6x larger
18Visually losslessHigh-quality delivery, streaming premium2-3x larger
20ExcellentHigh-quality web video1.5-2x larger
23Good (default)General purpose, web delivery1x (baseline)
26AcceptableSocial media, mobile delivery0.5-0.7x
28ModerateBandwidth-constrained delivery0.3-0.5x
32LowPreviews, thumbnails0.15-0.25x
40Very lowExtreme compression (barely watchable)0.05-0.1x
51WorstNever use thisTiny

The sweet spot for most use cases is CRF 18-28. Below 18, you're spending bits on quality differences most viewers can't perceive. Above 28, artifacts become clearly visible.

CRF Values for H.265 (libx265)

H.265 achieves the same visual quality at roughly 40-50% lower bitrate than H.264. However, the CRF scale shifts: CRF 28 in H.265 is approximately equivalent to CRF 23 in H.264.

ffmpeg -i input.mp4 -c:v libx265 -crf 28 output.mp4
CRF (H.265)Equivalent H.264 CRFQuality Level
18~14Near-lossless
22~18Visually lossless
24~20Excellent
28~23Good (default)
32~26Acceptable
36~30Low

How to Choose the Right CRF

A practical approach:

  1. Start with the default (23 for H.264, 28 for H.265)
  2. Watch the output — is the quality acceptable?
  3. Adjust by 2-4 points in either direction
  4. A/B test — encode the same complex scene at two CRF values and compare

The scenes that reveal compression artifacts most are: fast motion, fine textures (grass, fabric), gradients (sky, fog), and dark scenes with subtle detail. For a beginner-friendly introduction, see our guide to compressing video with FFmpeg.

Encoding Presets: Speed vs Quality

FFmpeg's preset parameter controls how much CPU time the encoder spends optimizing compression. Slower presets achieve better compression (smaller files at the same quality) at the cost of longer encoding times.

H.264 Preset Benchmark

All tests below use CRF 23 with a 1080p 60fps source video (2 minutes):

PresetEncoding TimeFile SizeQuality (VMAF)
ultrafast15s85 MB92.1
superfast22s62 MB93.8
veryfast30s48 MB94.5
faster40s42 MB95.0
fast52s38 MB95.3
medium70s35 MB95.5
slow120s33 MB95.7
slower210s31 MB95.8
veryslow450s30 MB95.9
placebo1800s29.5 MB95.9

Key observations:

  • medium is the default and the sweet spot for most workflows
  • Going from medium to slow saves ~6% file size but takes 70% longer
  • Going from medium to veryslow saves ~14% but takes 6.4x longer
  • placebo offers virtually no improvement over veryslow — never use it
  • ultrafast produces files 2.4x larger than medium — avoid for final delivery
# Good default for most use cases
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium output.mp4

# When encoding time matters more than file size
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset fast output.mp4

# When file size matters more than encoding time
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset slow output.mp4

H.265 Preset Behavior

H.265 presets follow the same naming convention but encoding is significantly slower at each tier:

PresetH.264 TimeH.265 TimeH.265 File Size
fast52s180s25 MB
medium70s300s22 MB
slow120s600s20 MB

H.265 encoding takes roughly 3-5x longer than H.264 at the same preset, but produces 30-40% smaller files at equivalent quality.

Two-Pass Encoding

Two-pass encoding gives you precise control over output bitrate, which is essential when you need to hit a specific file size target (e.g., "this video must be under 50MB").

How It Works

  • Pass 1: FFmpeg analyzes the video and creates a log file with complexity data
  • Pass 2: FFmpeg uses the log to distribute bits optimally — more bits for complex scenes, fewer for simple ones

Basic Two-Pass Command

# Pass 1 — analyze (output is discarded)
ffmpeg -i input.mp4 -c:v libx264 -b:v 4M -pass 1 -f null /dev/null

# Pass 2 — encode using analysis data
ffmpeg -i input.mp4 -c:v libx264 -b:v 4M -pass 2 output.mp4

Calculating Target Bitrate

To fit a video into a target file size:

Video bitrate = (Target file size in bits - Audio size in bits) / Duration in seconds

Example: 10-minute video, 100MB target, 128kbps audio:

Audio size = 128,000 bps × 600s = 76,800,000 bits = 9.6 MB
Video bitrate = (100 MB - 9.6 MB) × 8,000,000 / 600 = 1,205,333 bps ≈ 1.2 Mbps
ffmpeg -i input.mp4 -c:v libx264 -b:v 1200k -pass 1 -c:a aac -b:a 128k -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1200k -pass 2 -c:a aac -b:a 128k output.mp4

CRF vs Two-Pass: When to Use Which

ApproachBest ForControl
CRFConsistent quality, unknown file sizeQuality-focused
Two-passTarget file size or bitrateSize-focused
CRF + maxrateQuality with bitrate ceilingStreaming

For streaming with a bitrate ceiling:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -maxrate 4M -bufsize 8M output.mp4

This targets CRF 23 quality but caps the bitrate at 4 Mbps — useful for streaming where bandwidth is limited.

Codec Comparison: H.264 vs H.265 vs VP9 vs AV1

Choosing the right codec is a tradeoff between compression efficiency, encoding speed, and playback compatibility.

Compression Efficiency

At equivalent visual quality (VMAF 95), relative file sizes:

CodecRelative File SizeCompression vs H.264
H.264 (libx264)100%Baseline
H.265 (libx265)55-65%35-45% smaller
VP9 (libvpx-vp9)55-65%35-45% smaller
AV1 (libaom-av1)40-50%50-60% smaller

Encoding Speed

Relative encoding time for a 1080p 60fps source at equivalent quality:

CodecEncoding Time (relative)Notes
H.2641xFast, well-optimized
H.2653-5xSignificantly slower
VP95-10xVery slow (single-threaded by default)
AV1 (libaom)20-50xExtremely slow
AV1 (SVT-AV1)3-8xMuch faster AV1 implementation

Playback Compatibility (2026)

CodecBrowsersMobileSmart TVsHardware Decode
H.26499%+99%+99%+Universal
H.265Safari, Edge (partial)iOS, Android 5+Most modernCommon
VP9Chrome, Firefox, EdgeAndroid 5+LimitedGrowing
AV1Chrome, Firefox, Edge, Safari 17+Flagship phonesLimitedEmerging

Practical Commands

# H.264 — maximum compatibility
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4

# H.265 — 40% smaller, good mobile support
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4

# VP9 — royalty-free, good for web
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -row-mt 1 -c:a libopus -b:a 128k output.webm

# AV1 (SVT-AV1) — best compression, modern devices
ffmpeg -i input.mp4 -c:v libsvtav1 -crf 30 -preset 6 -c:a libopus -b:a 128k output.mp4

Which Codec to Choose

ScenarioRecommended CodecWhy
Maximum compatibilityH.264Plays everywhere
Bandwidth savings, Apple ecosystemH.265Good compression, iOS/macOS native
Web-first, royalty-freeVP9Chrome/Firefox support, no licensing fees
Future-proof, best compressionAV1 (SVT-AV1)50%+ savings, growing support
Mixed audienceH.264 with H.265/AV1 fallbackServe best codec per device

For more details on switching between container formats, see our video format conversion guide.

Resolution Scaling Best Practices

Downscaling video properly involves more than just changing the resolution.

Common Target Resolutions

NameResolutionTypical Bitrate (H.264)Typical Bitrate (H.265)
4K UHD3840x216015-30 Mbps8-15 Mbps
1440p2560x14408-15 Mbps4-8 Mbps
1080p Full HD1920x10804-8 Mbps2-4 Mbps
720p HD1280x7202-4 Mbps1-2 Mbps
480p SD854x4801-2 Mbps0.5-1 Mbps
360p640x3600.5-1 Mbps0.3-0.5 Mbps

Scaling with Aspect Ratio Preservation

Always use -1 for one dimension to maintain the aspect ratio, and use -2 to ensure the value is divisible by 2 (required for most codecs):

# Scale to 720p width, auto-calculate height (divisible by 2)
ffmpeg -i input.mp4 -vf "scale=1280:-2" -c:v libx264 -crf 23 output.mp4

# Scale to 1080 height, auto-calculate width
ffmpeg -i input.mp4 -vf "scale=-2:1080" -c:v libx264 -crf 23 output.mp4

Scaling Filter Quality

FFmpeg offers multiple scaling algorithms. The default (bilinear) is fine for most cases, but for best quality downscaling, use Lanczos:

# High-quality downscale with Lanczos
ffmpeg -i input.mp4 -vf "scale=1280:720:flags=lanczos" -c:v libx264 -crf 23 output.mp4
AlgorithmSpeedDownscale QualityBest For
fast_bilinearFastestLowReal-time processing
bilinearFastModerateDefault, general use
bicubicMediumGoodModerate quality needs
lanczosSlowerBestFinal delivery, quality-critical
splineSlowerExcellentAlternative to Lanczos

Don't Upscale

As a general rule, never upscale video. Encoding a 720p source as 1080p wastes bandwidth without adding real detail. If you must upscale, keep it minimal and use Lanczos:

# If you must upscale (avoid when possible)
ffmpeg -i input_720p.mp4 -vf "scale=1920:1080:flags=lanczos" -c:v libx264 -crf 20 output.mp4

Audio Compression

Audio is often overlooked in video compression workflows, but optimizing it can save significant bandwidth.

Recommended Audio Settings

Use CaseCodecBitrateCommand
General web videoAAC128 kbps-c:a aac -b:a 128k
High-quality videoAAC192 kbps-c:a aac -b:a 192k
Music-focused contentAAC256 kbps-c:a aac -b:a 256k
WebM/VP9 videoOpus128 kbps-c:a libopus -b:a 128k
Podcast/speechAAC64 kbps-c:a aac -b:a 64k
Podcast/speech (Opus)Opus48 kbps-c:a libopus -b:a 48k

Normalizing Audio Loudness

Inconsistent audio levels across videos is a common problem. FFmpeg's loudnorm filter normalizes to broadcast standards:

# Normalize to -16 LUFS (standard for streaming)
ffmpeg -i input.mp4 -c:v copy -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:a aac -b:a 128k output.mp4

Stripping Audio

If you don't need audio at all:

ffmpeg -i input.mp4 -an -c:v libx264 -crf 23 output.mp4

Copying Audio Without Re-encoding

If the audio is already in a suitable format:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a copy output.mp4

Real-World Compression Scenarios

Web Delivery (General Purpose)

The most common scenario — compressing video for embedding on websites:

ffmpeg -i input.mp4 \
  -c:v libx264 -crf 23 -preset medium \
  -vf "scale=1920:1080:flags=lanczos" \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  output.mp4

Key details:

  • -movflags +faststart moves metadata to the beginning of the file, enabling progressive playback (essential for web)
  • CRF 23 provides a good balance of quality and size
  • AAC 128k is sufficient for most web content

Social Media Optimization

Platforms like Twitter/X, Instagram, and TikTok have specific requirements:

# Optimized for social platforms (H.264, AAC, fast start)
ffmpeg -i input.mp4 \
  -c:v libx264 -crf 23 -preset medium \
  -profile:v high -level:v 4.0 \
  -pix_fmt yuv420p \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
  -c:a aac -b:a 128k -ar 44100 \
  -movflags +faststart \
  output.mp4
  • -profile:v high -level:v 4.0 ensures broad compatibility
  • -pix_fmt yuv420p guarantees playback on all devices
  • The scale + pad filter creates a 9:16 vertical video with letterboxing

Archival (Maximum Quality, Minimum Loss)

For long-term storage where file size is less important:

ffmpeg -i input.mp4 \
  -c:v libx264 -crf 16 -preset slow \
  -c:a flac \
  output.mkv
  • CRF 16 preserves virtually all visual detail
  • FLAC audio is lossless
  • MKV container supports more codecs than MP4

Streaming (Adaptive Bitrate with HLS)

Generating multiple quality levels for adaptive streaming:

ffmpeg -i input.mp4 \
  -map 0:v -map 0:a -map 0:v -map 0:a -map 0:v -map 0:a \
  -c:v libx264 -crf 22 \
  -c:a aac -b:a 128k \
  -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
  -b:v:0 5M -maxrate:v:0 5.5M -bufsize:v:0 10M -s:v:0 1920x1080 \
  -b:v:1 2.5M -maxrate:v:1 2.75M -bufsize:v:1 5M -s:v:1 1280x720 \
  -b:v:2 1M -maxrate:v:2 1.1M -bufsize:v:2 2M -s:v:2 854x480 \
  -f hls -hls_time 6 -hls_list_size 0 \
  -master_pl_name master.m3u8 \
  stream_%v/playlist.m3u8

Batch Compression Script

For processing an entire directory of videos:

#!/bin/bash
# 批量压缩目录下所有视频
for f in *.mp4; do
  ffmpeg -i "$f" \
    -c:v libx264 -crf 23 -preset medium \
    -c:a aac -b:a 128k \
    -movflags +faststart \
    "compressed_${f}"
done

Cloud Compression with FFHub

All the commands in this guide work directly with FFHub.io — just send them via API:

# Compress a video in the cloud (no local FFmpeg needed)
curl -X POST https://api.ffhub.io/v1/tasks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "ffmpeg -i https://example.com/input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart output.mp4"
  }'

This offloads the CPU-intensive encoding work to the cloud, keeping your local machine or server free. Particularly useful for batch video transcoding or when running FFmpeg on your application server would impact performance.

Quick Reference Cheat Sheet

One-Liner Commands

# Good default compression
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -b:a 128k -movflags +faststart output.mp4

# Aggressive compression (smaller file)
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 96k output.mp4

# High-quality compression (larger file)
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow -c:a aac -b:a 192k output.mp4

# Maximum compression with H.265
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4

# Future-proof with AV1
ffmpeg -i input.mp4 -c:v libsvtav1 -crf 30 -preset 6 -c:a libopus -b:a 128k output.mp4

Decision Tree

  1. Need maximum compatibility? Use H.264
  2. Need smaller files? Use H.265 (Apple/mobile) or VP9 (web)
  3. Need smallest files and have time? Use AV1
  4. Need specific file size? Use two-pass encoding
  5. Need consistent quality? Use CRF
  6. Processing many files? Use fast or veryfast preset
  7. Processing one important file? Use slow preset

Conclusion

Effective video compression comes down to understanding and balancing three factors: quality (CRF), speed (preset), and file size (codec choice). Start with the defaults — CRF 23, medium preset, H.264 — and adjust based on your specific requirements. Test with your actual content, because compression behavior varies dramatically between talking-head videos and action sequences.

The commands in this guide are standard FFmpeg — they'll work on your local machine, on any server, or through a cloud service like FFHub. Master these fundamentals and you'll handle any video compression challenge that comes your way.

Try it in your browser

Validate a CRF / preset / codec combo on a real clip before wiring it into your pipeline. Same FFmpeg flags, no local install.

FAQ

What CRF should I use for streaming video?

For HLS / DASH adaptive streaming, CRF 21–23 with -maxrate and -bufsize caps is the standard. CRF keeps quality consistent across complexity changes, while the bitrate ceiling protects against viewer bandwidth spikes. Use CRF 23 for the highest variant, CRF 24–25 for mid quality, CRF 26–28 for the lowest variant.

Two-pass vs CRF: when should I use each?

Use CRF when you care about consistent quality (any web or archival workflow). Use two-pass when you must hit a specific file size (e.g., Discord 25MB cap, broadcast bitrate spec, ad spec). Two-pass takes ~2x longer than CRF but gives precise size control. For streaming with a bitrate ceiling but quality-first behavior, combine CRF with -maxrate and -bufsize.

Why does H.265 take so much longer than H.264?

H.265 evaluates far more block sizes, prediction modes, and motion vectors per frame to achieve its 30–40% compression advantage. The HEVC specification is intentionally more complex than H.264 in exchange for that efficiency. Real-world: H.265 with libx265 -preset medium is roughly 3–5x slower than H.264 with libx264 -preset medium for the same quality output.

Is AV1 ready for production use in 2026?

Yes for delivery, with caveats. Hardware AV1 decoders are now common on flagship phones, current-gen consoles, and Apple Silicon, but older devices still need an H.264 fallback. For encoding, SVT-AV1 (libsvtav1) is the practical choice — 3–8x slower than H.264 at equivalent quality, versus 20–50x slower for libaom-av1. Netflix, YouTube, and Vimeo already serve AV1 to capable clients.

How do I batch compress videos with consistent quality across different sources?

Use CRF mode, not bitrate mode — CRF adapts to each source's complexity. A simple shell loop with -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart produces uniform perceived quality across mixed inputs. If you also need a hard file-size ceiling, layer on -maxrate and -bufsize (e.g., -maxrate 4M -bufsize 8M).

Does -preset slow really make files smaller?

Yes, but with diminishing returns. From mediumslow you save ~6% file size at the same quality but encoding takes ~70% longer. mediumveryslow saves ~14% for 6.4x the encode time. placebo is virtually identical to veryslowdon't use placebo. The practical sweet spots are medium (default), slow (overnight batches, final delivery), and fast (real-time or proxy workflows).

Related Articles

FFmpeg Video Compression: Best Practices Guide | FFHub