# FFmpeg encoding settings compared

Canonical: https://rendobar.com/blog/ffmpeg-encoding-settings/
Author: Abdelrahman Essawy
Published: 2026-08-20
Updated: 2026-08-20

---

## Key takeaways

- Keyframe interval is the largest single lever after codec choice. All-keyframes cost 6.1x the default's bytes and a 1-second GOP cost 35% more.
- -tune zerolatency is the most expensive one-word mistake available, at 121% more bytes, and it is in a great many streaming configs that do not need it.
- CRF is worth about 20% of the file per 3 points, and it costs nothing in encode time, which makes it the cheapest size decision in the list.
- The scaler flag is free: five algorithms landed within 17 ms of each other while output size varied 66%.
- Every figure here came from a job run against a live API on one fixed source, 106 encodes in total. Sizes are exact and repeatable; timings are not.

There are perhaps a dozen FFmpeg settings that meaningfully change what comes out
of an encode, and most guides describe all of them without measuring any. This
one measures all of them, on the same file, so the numbers compare.

Short version, ranked by how much each setting moves the output:

1. **Codec** — up to 66% fewer bytes at matched quality
2. **Keyframe interval** — up to 6.1x, and 35% for the common HLS setting
3. **`-tune`** — up to 121% for one word
4. **CRF** — about 20% per 3 points
5. **Preset** — 6.6x across the full range, non-monotonic in the middle
6. **Pixel format** — 5% and a lot of compatibility
7. **Scaler flag** — 66% between best and worst, and free in time

Everything below came from a real job. 106 encodes on one 5-second 1280x720
source, run through a live API, with the exact commands and costs recorded.

## The settings that cost the most

### Keyframe interval is the biggest lever after codec

A keyframe is a complete picture; every other frame stores only what changed. So
the interval decides how often the encoder throws away its compression and starts
again, and it compounds.

Encoding every frame as a keyframe produced **2,661 KB against the default's 433
KB**. The 1-second GOP that HLS guides recommend costs **35% more bytes** than
x264's default, and a 2-second GOP costs 17%.

Encode time barely moves across the range, so this is a pure bandwidth decision.
For streaming, 2 seconds is usually right. For a download, the default is free
money. Full detail in
[FFmpeg keyframe interval compared](/blog/ffmpeg-keyframe-interval-cost/).

### -tune is the most expensive single word

`-tune zerolatency` produced **121% more bytes**. It disables lookahead,
B-frames and frame threading so the encoder never holds a frame back, and every
one of those exists to improve compression. `-tune fastdecode` costs 68% for the
same reason.

Neither flag is misnamed. The trap is that "tune for low latency" reads like a
scheduling hint and behaves like a doubled bitrate. If your pipeline already
buffers seconds of HLS segments, you have accepted the latency and are paying for
nothing. Detail in
[FFmpeg tune presets compared](/blog/ffmpeg-tune-presets-measured/).

### CRF is the cheapest decision available

About **20% of the remaining bytes per 3 points**, across a range from CRF 18 to
32 that spans 3.6x in file size. And encode time does not track CRF at all, so
raising it is close to a pure saving.

If you are on the default 23, moving to 26 removes **22% of your bytes** for a
difference most viewers will not see on typical content. Detail in
[FFmpeg CRF explained and measured](/blog/ffmpeg-crf-measured/).

## The settings people get wrong

### Preset ordering is not what you think

The ends behave as advertised: `ultrafast` produced **6.6x** the bytes of
`veryslow`. The middle does not. `veryfast` produced a **smaller** file than
`faster`, `fast`, `medium` and `slow`.

That is not a bug. CRF targets constant quality, so a slower preset is free to
spend the bytes it saves on detail it previously discarded. **Size at fixed CRF
is emergent, not a dial.** The same inversion appears in
[SVT-AV1 presets compared](/blog/svt-av1-presets-measured/), where preset 12 beat
preset 10 on both size and speed.

The practical rule: pick a preset by measuring your own content, and do not
assume slower means smaller.
[FFmpeg x264 presets compared](/blog/x264-presets-measured/) has the full curve.

### The pixel format that breaks Safari

`yuv420p` was **both the smallest and the fastest**. `yuv444p` cost 65% more
encode time for 5% more bytes and is rejected by a large amount of hardware.

If a video plays in Chrome and not in Safari or QuickTime, this is almost always
why: a filter somewhere in the chain output a higher-chroma or RGB format and
nothing converted back. Ending a delivery chain with `format=yuv420p` costs
nothing when the format is already right. Detail in
[FFmpeg yuv420p vs yuv444p](/blog/yuv420p-chroma-subsampling-cost/).

One caution from that sweep: `yuv422p` produced **more** bytes than `yuv444p`,
which is not what subsampling arithmetic predicts. Do not reason about output
size from chroma volume.

### The scaler flag is free, so use a good one

Five algorithms landed within **17 ms** of each other while output size varied
**66%**. Scaling is a per-pixel filter pass and encoding is a search problem, so
the encoder dominates completely.

Nearest-neighbor is the expensive one, at 49% more bytes than bicubic, because
its hard stair-stepped edges are high-frequency detail the codec then pays to
describe. That generalises: **any filter that adds artificial sharpness costs you
encoder bytes.**

And smaller is not better here. Bilinear won on bytes because it is blurriest.
The useful cluster is bicubic, spline and lanczos, within 3% of each other.
Detail in
[FFmpeg lanczos vs bicubic scaling](/blog/ffmpeg-scaling-flags-measured/).

## Rate control: quality, bitrate, or both

Single-pass ABR at 700k delivered **483 KB where the arithmetic predicts 437.5
KB**, overshooting its own target by 10.4%. It aims without knowing what is
coming, so it corrects as it goes.

**CRF with a VBV cap produced the smallest file of the four.** That combination
is the one most people never reach for and usually the right one: quality drives
the encode and the cap only intervenes on the hardest passages.

```bash
-crf 23 -maxrate 900k -bufsize 1400k
```

Note that `-maxrate` without `-bufsize` does almost nothing, because the default
window is large enough that the ceiling rarely binds. That pairing is one of the
most commonly half-configured things in FFmpeg. Detail in
[FFmpeg CRF vs bitrate](/blog/crf-vs-bitrate-measured/).

## Threads, and why your benchmarks disagree

Eight threads was **2.99x faster than one, not 8x**. Encoding has genuinely
serial parts, so the curve flattens and **four threads captures most of the
benefit**.

The more useful finding is about measurement. With `-threads` pinned, repeated
runs landed within **1.4% to 8.9%**. On `auto`, the identical command ranged
**918 to 1,699 ms**, an 85% spread, because x264 sizes itself to whatever the
machine has free.

That variance was large enough to invalidate a claim we had already published,
which is documented in
[FFmpeg benchmark variance](/blog/measurement-noise-ffmpeg-benchmarks/). If your
FFmpeg timings wander between runs, pin the threads before concluding anything.

## What to actually set

For **web delivery**, the combination that sits near the knee of every curve
measured here:

```bash
ffmpeg -i input.mp4 \
  -c:v libx264 -crf 23 -preset medium -g 60 \
  -vf "scale=1280:-2:flags=lanczos,format=yuv420p" \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  output.mp4
```

Every element is there for a measured reason. CRF 23 is the quality anchor, `-g
60` is a 2-second GOP at 30fps costing 17% rather than 35%, `lanczos` is free,
`format=yuv420p` prevents the Safari failure, and `+faststart` is what lets a
browser begin playing before the file finishes downloading.

For **archival**, drop `-g 60`, raise the preset, and consider a modern codec:
[AV1 vs H.264 VMAF compared](/blog/av1-vmaf-matched-size/) measured AV1 delivering
20 more VMAF points at the same file size.

For **live**, `-tune zerolatency` is finally correct, and the 121% is the price
of the thing you are actually buying.

## How these numbers were produced

Every figure came from a job executed against a live API on one fixed source: a
5-second 1280x720 H.264 clip, audio stripped so the video numbers are clean. The
sweeps behind this page total **106 encodes**.

Two properties of that data are worth knowing before you use it:

**Sizes are exact and repeatable.** The same command produced byte-identical
output on all ten runs we checked, 443,351 bytes every time. Any size comparison
here would reproduce.

**Timings are a single sample unless stated.** The threads and SVT-AV1 sweeps
were repeated with pinned threads and report medians; everything else is one run.
Treat a timing difference under about 2x as noise.

That asymmetry is why this page leads with size on every setting and only makes
a speed claim where the gap is large or the sweep was repeated.

## Where this stops

One clip, one resolution, one content type. Grain and motion change encoder
behaviour more than almost anything else, and a static screencast or a
high-motion sports clip will move several of these numbers.

What should travel is the ordering and the mechanisms: keyframes are expensive,
`zerolatency` disables compression features by design, CRF trades size against
quality and nothing else, and any filter that adds artificial detail costs bytes
downstream.

## What this costs to run

Every sweep on this page was a real job billed at a real price. Across the last
425 FFmpeg jobs on this account the median was **$0.0025** and every one came in
under a cent, which is what makes a page like this affordable to produce rather
than a marketing exercise.

[FFmpeg API pricing compared](/blog/ffmpeg-api-pricing-compared/) works that full
distribution through six services and shows where per-GB, per-command and
per-compute billing invert against each other. Before any of it, though, comes
the cheaper step: [validating the input with ffprobe](/blog/ffprobe-validate-uploads/)
costs a flat $0.0010 and stops you paying for encodes that were always going to
fail.

Two more measurements sit under this page. [How long video transcoding takes](/blog/video-transcoding-time/)
shows that the encode is the fastest step in the pipeline, at a median of 861 ms
against a 3,626 ms wait to start. [How to compress video to a target size](/blog/compress-video-target-size/)
covers what it takes to hit a size and a quality target at once, measured across
667 probe encodes.
