Ahosting Logo
Knowledge Base

Why Converted Video Looks Washed Out or Too Dark

Two reasons a conversion changes how the picture looksA lost colour tag· the file describes its colour space in metadata· if that is not carried across, the player guesses· and a wrong guess shifts every colourA range mismatch· video uses a limited range, computers use a full one· treating one as the other crushes blacks or washes out highlightsSo it is not an encoder faultThe pixels are usually fine. What changed is the description of what those numbers mean, whichis why it looks wrong everywhere at once.

A video is converted and the result looks wrong: washed out, too dark, or oddly saturated. The encoder is almost never the reason.

Find out what the source is

Before changing any setting:

ffprobe -v error -select_streams v:0 \
 -show_entries stream=color_space,color_transfer,color_primaries,color_range \
 -of default=noprint_wrappers=1 input.mp4

Those four values describe how the numbers in the file should be interpreted. They are metadata: the pixel data is the same either way, and the description is what tells a player how to display it.

Case one: the tag was lost

If the source has values and the output does not, the picture is unchanged and the description of it has gone. The player guesses, usually assuming the most common standard, and if the source was something else the colours shift.

The fix is to carry the tags across:

ffmpeg -i in.mp4 -c:v libx264 -crf 20 \
 -colorspace bt709 -color_primaries bt709 -color_trc bt709 -color_range tv \
 -c:a copy out.mp4

Use the values ffprobe reported for the source, not these, unless they match. Guessing here is what caused the problem.

This is the common case for footage from phones and cameras, which often tag their files in ways a naive conversion drops.

Case two: the source is HDR

If color_transfer says smpte2084 or arib-std-b67, the source is high dynamic range.

The numbers in an HDR file mean something different from the same numbers in an ordinary file. Re-encoding without conversion keeps the numbers and loses the meaning, and the result is the characteristic flat, grey, desaturated picture people describe as "washed out".

The conversion has to be explicit:

ffmpeg -i hdr.mp4 -vf "zscale=t=linear:npl=100,format=gbrpf32le,\
zscale=p=bt709,tonemap=tonemap=hable:desat=0,\
zscale=t=bt709:m=bt709:r=tv,format=yuv420p" \
 -c:v libx264 -crf 20 -c:a copy sdr.mp4

That chain converts to a linear representation, maps the brightness range down, and converts back to ordinary video. It requires the zscale filter, which not every build includes:

ffmpeg -hide_banner -filters | grep zscale

Different tone-mapping methods produce different results, and hable is a reasonable default. It is worth encoding a short section and looking rather than processing an hour and then judging.

Full range and limited range

The third cause, less common and easy to recognise.

Video normally uses a limited range where black is 16 rather than 0. Some sources use the full range. Misreading one as the other gives you crushed blacks or grey blacks: the picture looks either contrasty and clipped, or hazy.

Setting -color_range tv or pc to match the source resolves it, and the source's value is in the ffprobe output above.

Do not fix it with brightness

The tempting response is to add a contrast or saturation filter until it looks right.

That treats a description problem as a picture problem. The result looks acceptable on your screen and wrong elsewhere, because the underlying tag is still missing or wrong and other players interpret it differently again.

Fix the tagging or do the proper conversion. Adjust the picture only when you actually want it to look different.

Check on more than one screen

Colour problems are the class of fault most affected by what you are looking at. A phone, a laptop and a television will each render a mis-tagged file differently.

Before concluding a conversion is correct, view it somewhere other than the machine that produced it, and for anything you serve to the public, an ordinary browser is the relevant test. Serving video efficiently covers the delivery side, and using ffprobe goes over confirming the output carries the tags you intended.

Read the colour properties rather than guessing

Every colour problem has a specific cause recorded in the file, and one command shows all of it.

ffprobe -v error -select_streams v:0 \
  -show_entries stream=color_range,color_space,color_transfer,color_primaries,pix_fmt \
  -of default=nw=1 input.mp4

Compare the source and the output side by side. Fields that are present on one and empty on the other identify exactly what was lost, which turns a vague complaint about the picture into a specific missing tag.

An empty field is not neutral. Players assume a default when nothing is stated, and that assumption is frequently different from what the file actually contains, which is the whole mechanism behind the washed out result.

Carry the tags through rather than reapplying them

The reliable fix is to state the properties explicitly on the output, so nothing depends on what a player assumes.

ffmpeg -i in.mp4 -c:v libx264 -crf 20 \
  -color_range tv -colorspace bt709 -color_trc bt709 -color_primaries bt709 \
  -pix_fmt yuv420p -c:a copy out.mp4

Setting them costs nothing and removes the ambiguity permanently. Do it in the pipeline rather than as a correction afterwards, since a second pass to fix colour is another generation of loss.

Check the result with the same inspection command. If the fields now match the source, the problem is solved at the file level and any remaining difference is the screen you are looking at rather than the encode.

Test with a still frame before encoding the file

Colour problems are visible in one frame, and extracting one takes a second rather than the length of an encode.

ffmpeg -ss 30 -i input.mp4 -frames:v 1 source.png
ffmpeg -ss 30 -i output.mp4 -frames:v 1 result.png
ffprobe -v error -show_entries stream=pix_fmt,color_range -of csv=p=0 source.png result.png

Compare the two images side by side rather than comparing your memory of the source against the result. Differences that seem obvious while watching are frequently smaller than they appear, and differences that matter are frequently missed.

Iterate on the still. Once one frame comes out correct, the same parameters produce a correct file, and you have spent seconds rather than an hour per attempt.

Check it somewhere other than your own screen

A display with an unusual profile makes correct output look wrong and wrong output look fine.

View the result on a second device, ideally a phone and a different computer. Consistent appearance across three screens means the file is right; a difference on one means the screen rather than the encode.

Where the material is being delivered to a platform that processes it further, upload a short test and look at what comes back. Some platforms apply their own conversion, and a file that is correct before upload can be altered by that step in ways nothing local predicts.