HDR formats and tone mapping
HDR video is ordinary video with three things changed: a transfer function that can describe much brighter highlights (PQ or HLG), wide-gamut BT.2020 primaries, and at least 10 bits per sample. HDR10, HDR10+ and Dolby Vision add metadata on top of that to help a display that is dimmer than the mastering monitor. None of them is a codec — the same HDR10 signal can be carried in HEVC, AV1, VP9 or AVC.
The formats
Section titled “The formats”| Format | Transfer | Metadata | On a display without support | Where you meet it |
|---|---|---|---|---|
| HDR10 | PQ (smpte2084) | Static: mastering display (SMPTE ST 2086) and MaxCLL/MaxFALL | SDR displays need a tone-mapped version | UHD Blu-ray, streaming, games |
| HDR10+ | PQ | HDR10 static metadata plus per-scene SMPTE ST 2094-40 dynamic metadata | Plays as HDR10 | Some UHD Blu-rays and streaming services |
| Dolby Vision | PQ (profiles 5, 7, 8.1, 10.1) or HLG (8.4) | Per-frame RPU, plus HDR10 static metadata in compatible profiles | Depends on profile, see below | UHD Blu-ray, streaming, phone cameras |
| HLG | HLG (arib-std-b67) | None required | Designed to look acceptable on SDR displays too | Broadcast and live TV, phone cameras |
Newer systems — HDR10+ Advanced (a backward-compatible extension of HDR10+), Dolby Vision 2 (a display-side engine announced in September 2025) and the ETSI SL-HDR family (an SDR or HDR base signal plus reconstruction metadata) — have no support in the open-source encoders and tools covered on this wiki.
Identify what a file contains
Section titled “Identify what a file contains”ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt,color_transfer,color_primaries,color_space -of default=nw=1 input.mkvffprobe -v error -select_streams v:0 -read_intervals "%+#1" -show_frames input.mkv| ffprobe shows | It is |
|---|---|
color_transfer=smpte2084, Mastering display metadata, Content light level metadata | HDR10 |
The above plus HDR Dynamic Metadata SMPTE2094-40 (HDR10+) | HDR10+ |
DOVI configuration record on the stream; Dolby Vision Metadata on frames | Dolby Vision; the record’s dv_profile and dv_bl_signal_compatibility_id give the profile (8 and 1 → 8.1) |
color_transfer=arib-std-b67 | HLG |
dv_profile=5, with color tags unknown or ipt-c2 | Dolby Vision profile 5: no standard base layer |
A file can be several at once: an HDR10+ and Dolby Vision 8.1 stream is still valid HDR10 underneath. dovi_tool info and hdr10plus_tool --verify extract give a fuller picture of the dynamic metadata.
HDR10 static metadata
Section titled “HDR10 static metadata”Two SEI messages (or AV1 metadata OBUs), normally repeated at every keyframe:
- Mastering display color volume (SMPTE ST 2086): the primaries, white point and minimum/maximum luminance of the monitor the content was graded on — for example P3-D65 at 1000 cd/m² peak.
- Content light level (CTA-861.3): MaxCLL, the brightest pixel anywhere in the program, and MaxFALL, the highest frame-average light level, both in cd/m².
The display uses these to decide how to compress highlights it cannot reproduce. They describe the grade, so copy them from the source; never make them up. The exact x265, SVT-AV1 and FFmpeg syntax is in preserving HDR10 metadata. x265 has --hdr10-opt (block-level QP adjustment for PQ content), which it recommends using together with adaptive quantization.
HDR10+
Section titled “HDR10+”HDR10+ adds SMPTE ST 2094-40 metadata — per-scene brightness statistics and a tone-mapping curve — to an HDR10 stream. In HEVC it is an ITU-T T.35 SEI message, normally on every frame; AV1 carries the same payload in a metadata OBU. Because the base is HDR10, a display without HDR10+ support just ignores it.
Encoders don’t create HDR10+ metadata from the picture, so it has to be carried over from the source: FFmpeg’s libaom-av1 passes it through, libx265 and libsvtav1 drop it, and for HEVC you extract it with hdr10plus_tool and give it to x265. The commands are in the color and HDR guide.
Dolby Vision
Section titled “Dolby Vision”Dolby Vision adds an RPU (reference processing unit) to every frame: per-shot brightness (L1), trims for target displays (L2, L8), active-area offsets (L5) and static values (L6), among others. The profile defines the base layer and therefore what a non-Dolby Vision player sees.
| Profile | Codec and layers | Base layer | Notes |
|---|---|---|---|
| 5 | HEVC, single layer | IPTPQc2 (proprietary) | Streaming. Without Dolby Vision processing the picture has wrong, typically green and purple, colors. |
| 7 | HEVC, base + enhancement layer | HDR10 | UHD Blu-ray. The enhancement layer is either MEL (metadata only) or FEL (extra picture data). |
| 8.1 | HEVC, single layer | HDR10 | The usual target for re-encodes; falls back to HDR10. |
| 8.2 | HEVC, single layer | SDR BT.709 | |
| 8.4 | HEVC, single layer | HLG | Phone cameras. |
| 10 | AV1, single layer | 10.1 HDR10, 10.4 HLG, 10.0 IPTPQc2 | What FFmpeg writes when re-encoding Dolby Vision to AV1. |
A re-encode keeps the source’s RPU and has to match it frame for frame: extract it with dovi_tool (converting profile 7 to 8.1 on the way), then let x265 write it or inject it into the new stream; FFmpeg can also pass single-layer Dolby Vision through with -dolbyvision 1. Converting profile 7 FEL to 8.1 discards the enhancement layer’s picture data; with MEL nothing visible is lost. The steps are in the color and HDR guide and on the dovi_tool page.
HLG (arib-std-b67, transfer code 18) is relative rather than absolute: its signal describes scene light, and the display scales it to its own peak brightness. It needs no metadata, which is why broadcasters use it for live production. Encode it as 10-bit with bt2020 primaries, bt2020nc matrix and arib-std-b67 transfer, and do not add HDR10 mastering metadata unless the source has it.
Tone-map HDR to SDR
Section titled “Tone-map HDR to SDR”Converting HDR to SDR is a real conversion — linearize, compress the highlights, map BT.2020 colors into BT.709, re-encode with the SDR transfer. Retagging PQ as bt709 only produces a gray, washed-out picture.
With a Vulkan-capable GPU, libplacebo does this in one filter and also applies Dolby Vision RPUs, so it handles profile 5 sources correctly:
ffmpeg -i hdr.mkv -vf "libplacebo=colorspace=bt709:color_primaries=bt709:color_trc=bt709:range=tv:format=yuv420p,sidedata=mode=delete:type=MASTERING_DISPLAY_METADATA,sidedata=mode=delete:type=CONTENT_LIGHT_LEVEL" -c:v libx264 -crf 18 sdr.mkvOn the CPU, zscale plus tonemap is the classic chain. The tonemap filter needs linear-light floating-point input, which the first two steps provide:
ffmpeg -i hdr.mkv -vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p,sidedata=mode=delete:type=MASTERING_DISPLAY_METADATA,sidedata=mode=delete:type=CONTENT_LIGHT_LEVEL" -c:v libx264 -crf 18 sdr.mkvnpl=100 maps 100 cd/m² in the source to SDR white; raise it to darken the result. This chain ignores Dolby Vision metadata, so a profile 5 source comes out with wrong colors.
Automatic tone mapping is a compromise. A dark scene and a bright scene need different curves, and a fixed curve that suits one will clip or dull the other, so watch the result on an SDR display. When an official SDR version exists, it is usually better than any automatic conversion.