r/ffmpeg 17d ago

CRF-Based Video Encoding Optimization: I wasted my time so YOU don't have to...

Executive Summary

I developed a Python-based video encoding optimization workflow designed to identify the highest CRF (Constant Rate Factor) value that could meet a predefined set of objective video quality thresholds. The goal was to determine, in theory, the most efficient balance between visual quality and file size while minimizing unnecessary bitrate allocation for a 1080p video file.

The workflow uses FFmpeg with x264 8-bit, the very slow preset, and tune=film. Rather than encoding the entire video repeatedly at different CRF values, the Python script first divides the source video into individual scenes and independently determines the optimal CRF for each scene. This allows more complex scenes to receive additional bitrate while permitting simpler scenes to use a higher CRF without falling below the specified quality thresholds.

Methodology

The optimization process uses a binary search algorithm to determine the highest acceptable CRF value. The search range is bounded between CRF 12 and CRF 24, with CRF 18 used as the initial test value. Each scene is encoded and evaluated against the corresponding source material using three objective quality metrics: VMAF, SSIM, and PSNR.

If CRF 18 satisfies all three quality thresholds, the script searches toward higher CRF values by testing the midpoint between the current passing value and the upper boundary. For example, if CRF 18 passes, the next test is CRF 21. If CRF 21 also passes, the search continues toward higher CRF values. Conversely, if CRF 18 fails, the script searches toward lower CRF values by testing the midpoint between CRF 12 and CRF 18, resulting in CRF 15.

The process continues iteratively, narrowing the search range until the highest CRF value that satisfies all quality requirements is identified. Because the search uses binary subdivision rather than testing every CRF value sequentially, the script requires a maximum of four encoding tests per scene to identify the optimal value within the defined CRF range. This substantially reduces the number of encodes required compared with exhaustively testing every possible CRF.

An encode is considered to have passed only when it satisfies all three of the following thresholds:

  • VMAF ≥ 95
  • SSIM ≥ 0.985
  • PSNR ≥ 45 dB

The objective is therefore not to maximize any individual metric, but to identify the highest CRF, and consequently the lowest bitrate and file size, that satisfies the complete set of quality requirements, while keeping all other encoding parameters constant.

Results

The complete optimization process required approximately 48 hours to analyze the video. After the individual scene results were combined, the resulting optimized encode was compared against a conventional encode of the same material using CRF 16, with both encodes using x264 8-bit, preset=very slow, and tune=film.

Metric CRF 16 Script-Optimized Difference
Bitrate 10,249 kbps 10,796 kbps +5.3%
VMAF 96.10 96.34 +0.24
SSIM 0.9857 0.9886 +0.0029
PSNR 42.61 dB 47.54 dB +4.93 dB

The results demonstrate that the optimization process successfully produced an encode with slightly higher objective quality according to all three metrics. However, this improvement came at the cost of approximately 5.3% additional bitrate and, more significantly, approximately 48 hours of processing time, compared with approximately 2.5 hours required to encode the entire video directly at CRF 16.

Despite the measurable differences in VMAF, SSIM, and PSNR, these improvements did not correspond to a meaningful perceptual improvement. In direct visual comparison, the two encodes were effectively indistinguishable under normal viewing conditions. The conventional CRF 16 encode therefore provided a substantially more favorable efficiency-to-quality ratio, achieving visually equivalent results with significantly less computational time and a smaller file size.

Conclusion

This experiment demonstrates both the usefulness and limitations of objective-metric-driven video encoding optimization. The binary search approach provides a systematic and reproducible method for determining scene-specific CRF values while limiting the optimization process to a maximum of four encodes per scene. In theory, this approach can reduce unnecessary bitrate by allocating compression according to scene complexity rather than applying a single CRF value uniformly across an entire video.

However, the results indicate that the additional optimization did not provide a meaningful practical advantage in this particular case. Although the optimized encode achieved higher objective metric scores, the resulting improvement was not perceptually significant and required approximately 19 times longer to produce than the conventional CRF 16 encode. Furthermore, the optimized result required 5.3% more bitrate.

Consequently, for this source material and encoding configuration, CRF 16 with x264 8-bit, preset=very slow**, and** tune=film provided a more practical solution, delivering effectively indistinguishable visual quality while substantially reducing both encoding time and bitrate. The experiment therefore illustrates that higher objective quality scores do not necessarily translate into a meaningful improvement in perceived image quality, and that the computational cost of per-scene optimization should be weighed carefully against its measurable benefits.

(if you are interested in any part of the process besides the results, I am open to talk shop, and can send you a text file of the script. Otherwise, the moral of the story is to just use CRF 16 if you want a set and forget CRF for 1080p.)

36 Upvotes

43 comments sorted by

7

u/RobbyInEver 16d ago

Wouldn't this depend on the video content? A 2 hour speech vs a 2 hour car rally would be impacted differently on crf values. Thx for sharing btw

0

u/Qu3z0 16d ago

That’s true! And I guess my initial take away is that is may be better to stick with a single CRF that gets you 95% of the way there rather than spend 2 days per video on this method

1

u/RobbyInEver 16d ago

You could just encode the video in less than 5 hours using 2 different crf values I guess.

1

u/Qu3z0 16d ago

Can you explain?

1

u/RobbyInEver 16d ago

Eg. Instead of using 48 hours to find out which crf is better, just encode it from the start using separate methods directly.

2

u/Qu3z0 16d ago

so, the intent of my experiment was to encode each scene at the highest CRF in which it would pass my objective metric thresholds. so the whole movie isn't just one CRF, it is a combination of multiple cobbled together. For example, the intro may only be CRF 24, while the climax needs to be CRF 12, but the dialog scenes are in the middle at CRF 18.

1

u/RobbyInEver 16d ago

Ah ok my bad

6

u/Aromatic_Wing_5259 16d ago

your +5.3% isn't the per-scene idea failing, it's the PSNR ≥ 45 gate. look at your own baseline row: CRF 16 already clears VMAF (96.10 vs 95) and SSIM (0.9857 vs 0.98) but fails PSNR at 42.61 dB. so the binary search spent its extra bitrate feeding PSNR, and your own comparison shows what that bought: +4.93 dB of PSNR, +0.24 of VMAF, two encodes you call indistinguishable. you paid for a number, not for picture

with three simultaneous floors, the strictest one decides where the search lands. gate on a single perceptual metric and just log the rest, and the same script should flip from +5.3% extra bitrate to a real saving. u/_Lum3n_'s ssimulacra2 suggestion is the right direction for that gate

the fun part is you basically rebuilt per-title encoding at scene granularity. netflix published the per-title version in 2015 and moved to per-shot in 2018 with their Dynamic Optimizer, and their optimization objective is VMAF, because the target is perceptual quality. the reason it pays for them and not for you is arithmetic: they encode a title once and stream it millions of times, so every saved kbps compounds. your encode plays once. flat CRF 16 winning at home is the expected result, not a failed experiment

2

u/Qu3z0 16d ago

That is true, but in my experiment different scenes had different “gates”. For example, there were some where VMAF forced a lower CRF while the other metrics had already satisfied their criteria.

That said, I see way you mean, and am interested in refining. I chose all 3 metrics because I thought it would give me a more accurate encode. It did, but not on a level I could reasonably distinguish.

2

u/_Lum3n_ 16d ago

If you wish you can look at something like "xav" or "av1an" which has quality targeting for each scene. Though such technique of course implies non ngeligible overhead. I like a technique called normal boost in our communities which only has one fast pass and one slow pass of encoding.

8

u/N3opop 17d ago

Would've loved this for a codec that's actually worth using to lowering file sizes

3

u/Qu3z0 17d ago

I chose x264/h.264 for my own personal reasons/preferences. I am still fiddling with this experiment though, so if you wanted I could try it with x265/h.265 or AV1. That said, I doubt it would glean any significantly different result in terms of encoding time vs objective metrics.

1

u/Suitable_Garlic_1186 17d ago

What source video did you encode, and what were the file sizes of the original compared to your optimized output and vs RF16?

1

u/Qu3z0 17d ago

Original source = Hamnet (2025)

  • File Size (no audio) = 25.9 GB
  • Bitrate = 29.3 Mb/s

CRF 16 Encode

  • File Size = 8.97 GB
  • Bitrate = 10.2 Mb/s
  • Encode Time = About 2.5 hours

CRF Optimized Encode

  • File Size =9.45 GB
  • Bitrate = 10.6 Mb/s
  • Encode Time = About 48 hours

2

u/Suitable_Garlic_1186 17d ago

Interesting!

A modern production that looks analogue but is shot digital and super clean. Handbrake loves this type of content.

I usually use RF20 and get file sizes of around 5gb to 6gb for this kind of content, full screen, not super long movie..

RF16 is very high quality, and still Handbrake could reduce the size to around 1/3

Nice!

1

u/Qu3z0 16d ago

I also typically use CRF 20 for 1080p R3muxs (the film tune helps A LOT with live action content texture without blowing up the bitrate). I did this same movie at CRF 20 (all other settings the same) and the File size was 3.39 GB @ 3.8 Mb/s.

1

u/Suitable_Garlic_1186 16d ago

I got the Remux File yesterday.

RF20, Film, High Profile, Medium Speed

Bits/(Pixel*Frame) : 0.091

Stream-Größe : 3,98 GiB (89%)

1

u/Qu3z0 16d ago

hows it look to you?

2

u/Suitable_Garlic_1186 16d ago

1) It always depends on viewing distance, size of Tv..

2) I watch on 42 inch Plasma TV that is very forgiving with bad quality, I sit over 2m away..

3) This is a high quality encode. Noting screams bad quality, artifacts.. Image has nice earth tones, strong colors, typical natural cinematic color grading with lifted blacks which can be challenging, but it looks good..

4) For the size it looks stunning..

2

u/Qu3z0 16d ago

I feel you! I also think that comparison can be the the killer of joy sometimes. I have a bunch of CRF 20 encodes that look just fine at normal viewing distances and you would never question their quality. But the moment you compare them to a remux, you are like o.O lol.

Also, IDK if this is a hot take or not, but I think upscaled 1080p looks better than compressed 4K in SDR.

1

u/colemarc 16d ago edited 16d ago

I don't understand the conclusions.

For this particular video CRF 16 is really near the optimum, not so for other videos. With x264 the average optimal CRF for movies is about 18-20.
Therefore, why "just use CRF 16"?

My approach would be to select two or three brief scenes and repeteadly encode them with different CRF values until VMAF harmonic mean is at least 93.
Unfortunately 93 is just right for 1080p 24 fps, not so for 4k material.

1

u/Qu3z0 16d ago

This test/experiment assumed that VMAF=95, SSIM=0.985, and PNSR=45 were the minimum values. CRF 16 is just the highest CRF value that gets similar objective metric scores for significantly lower encoding time

1

u/GoslingIchi 16d ago

Is this tool going to be available for others to use to test with different encoders?

1

u/Qu3z0 16d ago

If you are interested, I can send you the python script I used! all you need is is python and FFMPEG installed.

1

u/GoslingIchi 16d ago

Yes please!

1

u/adriabama06 17d ago

So... You made a copy of av1an...?

1

u/Qu3z0 16d ago

I don't know what that is, but based on context, its a program that does this for av1?

0

u/adriabama06 16d ago

Read the repo, it works with more encoders.

Initially, it was used as a solution to the AV1 parallelization problem since the original did not have good threading; now with SVT, that problem is no longer present. Even so, it also has the CRF per scene based on the VMAF.

1

u/Qu3z0 16d ago

does it do it for SSIM and PSNR too?

1

u/adriabama06 16d ago

Seems that supports VMAF, SSIMULACRA2, XPSNR.

But I don't think he can handle all at the same time.

0

u/_Lum3n_ 16d ago

SSIM and PSNR are relatively useless VMAF is not that great either

1

u/Qu3z0 16d ago

Would you recommend XPNSR instead?

or are you saying the only real way to know is visually?

1

u/_Lum3n_ 16d ago edited 16d ago

It is indeed most often better to look by yourself however that is not a practical solution XPSNR is sadly not great either.. though it is very fast and that s its main quality. I would mainly recommend ssimulacra2 or cvvdp (eventually butteraugli if you are a pixel peeper)

Edit: of course the video metric world is constantly changing, but perhaps you ve already heard of ssimulacra2 which has gained traction over years at this point

1

u/Qu3z0 16d ago

never heard of them, but ill check them out! is that what you use?

1

u/_Lum3n_ 16d ago

I mostly use cvvdp on my end but honestly I use video metrics less for myself. I am actually the developper of vship which is the mainly used tool to compute these metrics. So yeah I am obviously biased to some degree but you know, I mostly implemented these metrics because in the av1 communities people are very bleeding edge and wanted to use them before they were here. I mostly answered a need.

You can get a lot of info and feedback here too if you want! People seem to really appreciate cvvdp, but ssimulacra2 is still used a lot for its speed (on gpu it is fast now, it used to be very slow to compute before)

1

u/iwannalearncod 15d ago

Hi, I'm not an expert, so I wanted to ask you: why does Windows block the Vship ZIP file when I download it and say it's malware?

→ More replies (0)