FFmpeg CRF Explained: Quality vs File Size Guide

Every video encoder faces the same tradeoff: quality vs. file size. CRF (Constant Rate Factor) is how FFmpeg lets you control that tradeoff with a single number.
Most guides say "just use CRF 23." That's fine for a quick transcode, but CRF 23 means completely different things depending on your codec, your content, and where you're delivering. This guide covers what CRF actually does, how the scale works across codecs, and how to pick the right value.
What Is CRF in FFmpeg?
CRF is a rate control mode that targets consistent perceptual quality across an entire video, letting the bitrate float as needed. Simple scenes (static shots, solid colors) get fewer bits. Complex scenes (fast motion, fine detail) get more bits. The result is a file where quality looks consistent to your eyes, even though the bitrate varies frame to frame.
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
Lower CRF always means higher quality and larger files. CRF 0 is mathematically lossless (and produces enormous files). The default for libx264 is 23.
CRF Scale by Codec
CRF values aren't universal. Each codec has its own scale, and the same number produces different quality levels.
| Codec | Encoder | CRF Range | Default | Visually Lossless | Good Balance | Small File |
|---|---|---|---|---|---|---|
| H.264 | libx264 | 0-51 | 23 | 17-18 | 20-23 | 28-30 |
| H.265/HEVC | libx265 | 0-51 | 28 | 20-22 | 24-28 | 32-35 |
| VP9 | libvpx-vp9 | 0-63 | 31 | 15-20 | 25-31 | 35-40 |
| AV1 | libaom-av1 | 0-63 | 32 | 20-25 | 28-32 | 38-45 |
CRF 23 in H.264 is not the same quality as CRF 23 in H.265. H.265 is a more efficient encoder, so its default is CRF 28 to produce roughly comparable quality.
A useful rule of thumb for H.264: every +6 CRF roughly halves the file size. Going from CRF 23 to 17 will roughly double your file. Going from 23 to 29 will cut it in half.
CRF Examples for Each Codec
H.264 (most common):
ffmpeg -i input.mp4 -c:v libx264 -crf 20 -preset medium -c:a aac -b:a 192k output.mp4
H.265/HEVC (better compression, slower):
ffmpeg -i input.mp4 -c:v libx265 -crf 24 -preset medium -c:a aac -b:a 192k output_hevc.mp4
VP9 (WebM format, used by YouTube internally):
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus output.webm
VP9 requires -b:v 0 alongside CRF. Without it, the encoder applies a default bitrate cap that overrides your CRF setting. This trips up a lot of people.
AV1 (best compression, slowest encoding):
ffmpeg -i input.mp4 -c:v libaom-av1 -crf 30 -cpu-used 4 -c:a libopus output.mkv
How Preset Interacts with CRF
CRF controls quality. Preset controls how hard the encoder works to hit that quality at the smallest file size. They're independent settings, but they interact.
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset slow output.mp4
Presets for libx264, from fastest to smallest output: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow.
At the same CRF value, a slower preset produces a smaller file with identical visual quality. The tradeoff is encoding time. medium (the default) or slow gives the best balance for most workflows.
CRF vs CBR vs VBR
| Mode | How It Works | Best For |
|---|---|---|
| CRF | Consistent quality, variable bitrate | Downloads, on-demand streaming |
| CBR | Fixed bitrate throughout | Live streaming, hard bandwidth limits |
| VBR | Average bitrate with a ceiling | Streaming platforms, predictable file sizes |
CRF is the best default for most developers. Consistent quality without guessing at bitrate numbers.
CBR is for live streaming or hard bandwidth constraints. Quality varies, but bitrate stays predictable:
ffmpeg -i input.mp4 -c:v libx264 -b:v 4000k -maxrate 4000k -bufsize 8000k output.mp4
VBR (two-pass) gives you a target file size with smart bit allocation. Takes twice as long:
ffmpeg -i input.mp4 -c:v libx264 -b:v 4000k -pass 1 -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 4000k -pass 2 output.mp4
Recommended CRF by Use Case
- Archiving source footage: CRF 17-18 (H.264) or CRF 20-22 (H.265). Near-lossless, file size doesn't matter.
- YouTube/social media: CRF 18-20 (H.264). YouTube re-encodes everything, so give it the best source you can.
- Web delivery: CRF 23-25 (H.264) or CRF 28-30 (H.265). Good quality, reasonable file sizes.
- Mobile-first: CRF 26-28 (H.264). Smaller screens forgive compression artifacts.
- Thumbnails and previews: CRF 30-35. Small and disposable. Save the bytes.
Common CRF Pitfalls
**Forgetting -b:v 0 with VP9.** VP9 ignores your CRF and uses a default bitrate cap instead. Always add -b:v 0 when using CRF with libvpx-vp9.
Comparing CRF across codecs. CRF 23 in H.264 produces different quality than CRF 23 in H.265. Use the table above when switching codecs.
Using CRF 0 for "best quality." CRF 0 is lossless and creates enormous files. CRF 17-18 is visually indistinguishable from lossless for H.264, at a fraction of the size.
Using CRF with the FFmpeg Micro API
If you're processing video programmatically, FFmpeg Micro's API handles CRF through a simple preset mode and an advanced mode for raw FFmpeg options.
Preset mode maps quality names to CRF values (high = CRF 18, medium = CRF 23, low = CRF 28):
curl -X POST "https://www.ffmpeg-micro.com/v1/transcodes" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": [{"url": "https://storage.example.com/video.mp4"}],
"outputFormat": "mp4",
"preset": {"quality": "high", "resolution": "1080p"}
}'
Advanced mode lets you set any CRF value directly:
curl -X POST "https://www.ffmpeg-micro.com/v1/transcodes" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": [{"url": "https://storage.example.com/video.mp4"}],
"outputFormat": "mp4",
"options": [
{"option": "-c:v", "argument": "libx264"},
{"option": "-crf", "argument": "20"},
{"option": "-preset", "argument": "slow"}
]
}'
Full flexibility without managing servers or installing FFmpeg. Get an API key from the FFmpeg Micro docs and start processing video in minutes.
Frequently Asked Questions
What CRF should I use for H.264?
CRF 23 is the default and works for most cases. For archiving or YouTube uploads, drop to CRF 18-20. For web delivery or mobile, use CRF 26-28. Visually lossless starts around CRF 17-18.
Does CRF 18 in H.265 equal CRF 18 in H.264?
No. H.265 is more efficient, so CRF 18 in H.265 produces higher quality than CRF 18 in H.264. The rough H.265 equivalent of H.264's CRF 23 is around CRF 28.
Can I use CRF with hardware encoding (NVENC, QSV)?
Hardware encoders use a different parameter called CQ (Constant Quality) instead of CRF. The flag is -cq or -qp, not -crf. Similar concept, different implementation.
Why is VP9 ignoring my CRF setting?
Add -b:v 0. VP9 in FFmpeg applies a default bitrate cap even when CRF is set. Removing the cap with -b:v 0 lets CRF control quality exclusively.
What's the difference between CRF and QP?
CRF adjusts quality per-frame based on scene complexity. QP (Quantization Parameter) applies the same quantization to every frame. CRF produces more consistent perceived quality across the video.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

How to Compress Video with FFmpeg Without Losing Quality
Learn how to compress video with FFmpeg using CRF, two-pass encoding, and preset tuning. Reduce file size by 50-80% without visible quality loss.

How to Compress Video with FFmpeg API (No Server Required)
Learn how to compress video using a cloud FFmpeg API with no server setup. Includes working code examples in cURL, Python, and Node.js.

How to Transcode Video with an API (Skip the FFmpeg Server Setup)
Learn how to add video transcoding to your app with a simple API instead of managing FFmpeg servers, job queues, and scaling infrastructure.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free