FFmpeg yuv420p vs yuv444p, 420 is what plays

Compare FFmpeg pixel formats on one clip, check why a video fails in QuickTime or Safari, and fix it with one flag. yuv420p also wrote the smallest file.

Share

If an H.264 video plays in one player and fails in QuickTime or Safari, check its pixel format first. A file encoded as yuv444p carries the High 4:4:4 Predictive profile, which many players and hardware decoders do not support. Re-encode with -pix_fmt yuv420p and it plays, and on the sweep below yuv420p also wrote the smallest file of the four formats.

Check the file

Terminal window
ffprobe -v error -select_streams v:0 \
-show_entries stream=pix_fmt,profile -of default=nw=1 in.mp4

A playable delivery file prints pix_fmt=yuv420p with a profile of High, Main or Baseline. If you see yuv444p with High 4:4:4 Predictive, or yuv420p10le with High 10, that is your problem.

How files end up in 4:4:4

The common path is RGB input. Encode a sequence of PNGs, a screenshot or a screen capture, and FFmpeg picks the libx264 pixel format closest to the input, which is yuv444p. The encode succeeds, and the problem only shows up when someone tries to play the file.

The fix is one flag on the output, or a filter at the end of the chain:

Terminal window
# On the output
ffmpeg -framerate 30 -i frame_%04d.png -c:v libx264 -crf 23 -pix_fmt yuv420p out.mp4
# Or as the last filter in an existing chain
ffmpeg -i in.mp4 -vf "scale=1280:-2,format=yuv420p" -c:v libx264 -crf 23 out.mp4

Either one does nothing when the frames are already 4:2:0, so it is safe to add to every delivery command.

What the higher formats cost on this clip

Each variant forced one pixel format with -vf format=... on the same 5-second clip. The source and the noise rules are on how the benchmarks are set up.

Pixel formats
What does chroma subsampling cost in bytes and compatibility?
Held constant: libx264 CRF 23 preset medium, same source, audio stripped
Bar chart. Pixel formats. Encode time and output size for each variant, each metric scaled to its own maximum.
VariantEncodeSizeCost
yuv420p899 ms fastest433.0 KB$0.0024
yuv422p1152 ms475.0 KB$0.0026
yuv444p1487 ms456.7 KB$0.0031
yuv420p10le1257 ms467.9 KB$0.0029
Measured 2026-08-20 on the Rendobar API. Encode time is the FFmpeg step alone, separated from download and upload. 4 of 4 runs succeeded. Sizes are exact and repeatable. Timings are a single sample and vary up to 2x run to run, so treat small differences as noise. Why.

Against yuv420p, yuv444p wrote 5.5% more bytes, yuv420p10le 8.1% more and yuv422p 9.7% more.

Read the 4:4:4 row with the source in mind. The clip is already yuv420p, so its colour was stored at quarter resolution before this sweep touched it. Converting it to 4:4:4 interpolates the missing chroma. It cannot restore it. So that row measures what it costs to store upsampled colour, not what 4:4:4 is worth on a source that has full-resolution colour, such as a lossless screen recording. That comparison would need a native 4:4:4 source and was not run.

The encode times ran from 899 ms for yuv420p to 1,487 ms for yuv444p. Each is a single sample and every gap is under 2x, so I would not claim from them that one format encodes faster than another.

4:2:2 wrote more than 4:4:4

yuv422p stores less chroma than yuv444p and still wrote the larger file. I do not have a verified explanation. x264 codes the two formats through different profiles and the rate control is not guaranteed to land in the same place for each, but that is a guess, not a measurement. The practical point stands without the explanation: output size does not follow from how much chroma a format stores, so measure it.

Why 4:2:0 is the delivery format

H.264 at 8-bit 4:2:0 is the combination players, browsers and phone hardware decoders are built around. 4:4:4 and 10-bit exist for capture, grading and intermediate files, where the extra colour precision survives to the next step. On the last mile they mostly buy playback failures.

Each format was one API call on the same source, so checking what a pixel format does to your own footage is four calls, and the probe above tells you which one a file already has.

I would put -pix_fmt yuv420p on every H.264 delivery encode, whatever the input. It costs nothing when the input is already 4:2:0, and it removes a playback bug that is hard to diagnose from the player’s error message.

Frequently asked questions

Is -pix_fmt yuv420p the same as format=yuv420p?

They insert the same conversion. -pix_fmt yuv420p applies to the output after every filter has run, and format=yuv420p inside -vf places it at a specific point in the chain. For a delivery encode, either works as long as it comes last.

Sources

Tags #ffmpeg#x264#pixel-format#encoding#benchmarks
All posts
Share
  1. How we benchmark FFmpeg Engineering blog
  2. Custom fonts in a video API fail silently Engineering blog
  3. Opus vs AAC vs MP3, requested vs delivered bitrate Engineering blog