FFmpeg HDR to SDR Conversion Using Tonemap Filter

This article provides a quick overview and step-by-step guide on how to convert High Dynamic Range (HDR) video to Standard Dynamic Range (SDR) using FFmpeg. You will learn how to use the tonemap filter in combination with color space conversion filters to prevent washed-out colors and achieve a high-quality SDR output.

To convert HDR video (typically BT.2020 color space with PQ or HLG transfer characteristics) to SDR (BT.709 color space), you must first convert the video to a linear color space, apply the tone-mapping filter, and then convert the color space to BT.709.

Because FFmpeg's native tonemap filter requires linear light input, the most reliable way to achieve this is by using the zscale filter (which requires FFmpeg to be compiled with the --enable-libzimg flag).

The Conversion Command

Use the following FFmpeg command to perform the HDR to SDR conversion:

ffmpeg -i input_hdr.mkv -vf "zscale=transfer=linear,tonemap=tonemap=hable:desat=2,zscale=transfer=bt709:primaries=bt709:matrix=bt709,format=yuv420p" -c:v libx264 -crf 20 -c:a copy output_sdr.mp4

Breakdown of the Video Filter Chain (-vf)

  • zscale=transfer=linear: This step converts the input HDR transfer characteristics (such as SMPTE ST 2084 / PQ) into linear light. Tone mapping must be performed in linear light to calculate brightness adjustments accurately.
  • tonemap=tonemap=hable:desat=2: This applies the actual tone-mapping algorithm.
    • tonemap: Specifies the algorithm. Popular options include hable (highly recommended for preserving contrast), reinhard, mobius, and linear.
    • desat: Controls desaturation to prevent bright colors from looking oversaturated. A value of 2 is a standard starting point.
  • zscale=transfer=bt709:primaries=bt709:matrix=bt709: Once tone-mapped, this converts the linear light back to standard BT.709 transfer characteristics, color primaries, and color matrix required for SDR playback.
  • format=yuv420p: Converts the pixel format from 10-bit (common in HDR) to 8-bit YUV 4:2:0, ensuring maximum compatibility with older SDR displays and players.

Alternative Tone-Mapping Algorithms

You can adjust the tonemap parameter inside the filter chain depending on your source material:

  • Hable (tonemap=hable): Excellent for retaining details in dark areas and highlights. It is the default choice for most cinematic content.
  • Reinhard (tonemap=reinhard): A classic tone-mapping algorithm that yields a softer contrast but handles extremely bright peaks well.
  • Mobius (tonemap=mobius): Best for preserving highly saturated colors in transition areas.