How to convert HDR video to SDR with ffmpeg

By | September 15, 2023

HDR vs SDR

HDR (High Dynamic Range) is the newer and more advanced version of display technology that shows more colorful and brighter pictures on displays that support it. It is much superior to its predecessor SDR.

Videos in HDR format use 10-bits for storing color information and use different color transfer functions compared to SDR videos. For example SDR videos use the BT.709 color space whereas HDR uses BT.2020 along with SMPTE2084 color transfer function.

If you are working with an hdr video, there could be a number of reasons to convert it to SDR. If you are working on a system that does not support HDR, then the colors might look washed out (pale/faded). The only way to work around this to convert the video to SDR which will make it look similar (but inferior) to the HDR version without loosing too much color information.

In this post we shall take a look at how to convert an hdr video to sdr video using ffmpeg from the command line.

Check with ffprobe

The original video details as shown by ffprobe:

ffprobe .\costa_rica_4k_60fps_hdr_uhd.webm -hide_banner
Input #0, matroska,webm, from '.\costa_rica_4k_60fps_hdr_uhd.webm':
  Metadata:
    ENCODER         : Lavf59.27.100
  Duration: 00:05:13.80, start: -0.007000, bitrate: 28952 kb/s
  Stream #0:0(eng): Video: vp9 (Profile 2), yuv420p10le(tv, bt2020nc/bt2020/smpte2084), 3840x2160, SAR 1:1 DAR 16:9, 59.94 fps, 59.94 tbr, 1k tbn (default)
    Metadata:
      DURATION        : 00:05:13.780000000
    Side data:
      Content Light Level Metadata, MaxCLL=1100, MaxFALL=180
      Mastering Display Metadata, has_primaries:1 has_luminance:1 r(0.6780,0.3220) g(0.2450,0.7030) b(0.1380 0.0520) wp(0.3127, 0.3290) min_luminance=0.000000, max_luminance=1000.000000
  Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)
    Metadata:
      DURATION        : 00:05:13.801000000
>

Note the stream details indicating HDR:
yuv420p10le(tv, bt2020nc/bt2020/smpte2084)

It indicates 10-bit color in bt2020 colorspace using smpte2084 color transfer function which is commonly used in HDR videos. If you see these details in any video then it is very likely saved in HDR format (and consequently needs an hdr capable display and media program to play properly).

The same details can be checked with VLC Player as well.

VLC Player Codec Details - Costa Rica HDR Video

VLC Player Codec Details - Costa Rica HDR Video

Converting to SDR

First try converting a small duration of the clip with the ss and t options to verify if the output quality is acceptable or not.

ffmpeg -hide_banner -y -i .\costa_rica_4k_60fps_hdr_uhd.webm -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 -ss 5 -t 10 .\costa_rica_4k_60fps_sdr_uhd-10s.libx264.mp4

The above command will try to preserve the color and keep them as similar to the original, though it can never be like hdr quality. The filter process is a chain of multiple processing stages over here:

  • 1. zscale=t=linear:npl=100
  • 2. format=gbrpf32le
  • 3. zscale=p=bt709
  • 4. tonemap=tonemap=hable:desat=0
  • 5. zscale=t=bt709:m=bt709:r=tv
  • 6. format=yuv420p

Check the documentation of zscale filter and tonemap filter and format filter

Fast Decoding Format: To optimise the video for fast decoding, we can use the -tune option and set it to fastdecode. Though this is not necessary.

ffmpeg -hide_banner -y -i .\costa_rica_4k_60fps_hdr_uhd.webm -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 -tune fastdecode -ss 5 -t 10 .\costa_rica_4k_60fps_sdr_uhd.mp4

Other options: Can also use options like crf and preset for quality control and size optimisation

ffmpeg -i video-input.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 libx265 -crf 22 -preset medium -tune fastdecode video-output.mp4

Other Commands

If you try to convert like this:

ffmpeg -hide_banner -y -i .\costa_rica_4k_60fps_hdr_uhd.webm -c:v libx264 -pix_fmt "yuv420p" -ss 5 -t 10 .\costa-10s.libx264.mp4

It will still retain the HDR format.

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

One Comment

How to convert HDR video to SDR with ffmpeg
  1. Will C

    Hi! This is great! Can you help put together a guide for using QSV and tonemapping hdr to sdr? I’m really struggling!

    Here’s what I have right now that works:
    ffmpeg -init_hw_device qsv=hw -filter_hw_device hw -i input.mkv -vf zscale=transfer=linear,tonemap=clip,zscale=transfer=bt709,format=yuv420p,scale=1280:-2 -vcodec h264_qsv -map 0:v -map 0:a:0 -map 0:a:1 -c:a copy -map 0:s:0 -c:s copy outputfile720p.mkv

    What this is doing:
    1)UHD HDR hevc source
    2)Tonemapping
    3)Scaling to 720p
    4) Encoding to h264
    5) Copying the first two audio tracks and the first subtitle track using the map function. I have to map video as well or else it fails.

    This correctly tonemaps, and I can tell via intel_gpu_top that some gpu is being used, but the filter is not going through qsv.

    Bonus question: I can’t get global_quality to work, how does that work with -vcodec h264_qsv

Leave a Reply

Your email address will not be published. Required fields are marked *