How to Use FFmpeg tonemap_opencl for HDR to SDR

This guide explains how to leverage the tonemap_opencl filter in FFmpeg to perform fast, hardware-accelerated HDR-to-SDR tone mapping using OpenCL. Tone mapping converts High Dynamic Range (HDR) content with wide color gamuts (like BT.2020) down to Standard Dynamic Range (SDR) with Rec.709 color profiles. Using OpenCL offloads the computationally heavy color transformation to your GPU, resulting in significantly faster encoding times compared to software-based filters like zscale.

Prerequisites

Before using the filter, ensure your environment meets the following requirements:

  1. FFmpeg Compilation: FFmpeg must be compiled with --enable-opencl. You can check this by running ffmpeg -version and looking for --enable-opencl in the configuration output.
  2. GPU Drivers: Proper OpenCL runtime drivers must be installed for your hardware (Intel Neo/Compute Runtime, NVIDIA CUDA/OpenCL, or AMD ROCm/AMDGPU-PRO).
  3. OpenCL Device: Identify your available OpenCL devices with clinfo or by using FFmpeg's device detection.

The OpenCL Processing Pipeline

The tonemap_opencl filter operates in GPU memory. When reading an input stream decoded on the CPU, you must transfer frames to the GPU, process them, and download them back unless you pass them directly to an OpenCL-compatible hardware encoder.

The filtergraph sequence follows this general structure:

  1. Initialize the OpenCL hardware device (-init_hw_device).
  2. Format the CPU frames into an OpenCL-compatible pixel format (typically 10-bit p010le for HDR10).
  3. Upload frames to the GPU (hwupload).
  4. Apply the tonemap_opencl filter to convert luminance and color primaries.
  5. Download frames back to CPU memory (hwdownload) and reformat them (usually to 8-bit nv12 or yuv420p for standard SDR encoding).

Basic Command Example

The following command converts an HDR10 video to SDR using the Mobius tone mapping algorithm and encodes it using libx264:

ffmpeg -init_hw_device opencl=gpu:0.0 -filter_hw_device gpu \
-i input_hdr.mkv \
-filter_complex \
"[0:v]format=p010,hwupload,tonemap_opencl=tonemap=mobius:format=nv12:desat=0.5,hwdownload,format=nv12[v]" \
-map "[v]" -map 0:a? \
-c:v libx264 -crf 20 -preset medium \
-c:a copy output_sdr.mp4

Explanation of Key Flags

  • -init_hw_device opencl=gpu:0.0 -filter_hw_device gpu: Initializes the OpenCL context using the first platform and first device (0.0), assigning it the label gpu and binding it to the filter graph.
  • format=p010,hwupload: Ensures the decoded input is in 10-bit format before transferring it to the GPU via hwupload.
  • tonemap_opencl: The core filter.
    • tonemap=mobius: The tone-mapping curve. Alternatives include reinhard, hable, and clip. mobius typically preserves highlights while maintaining contrast.
    • format=nv12: Specifies the output pixel format in OpenCL memory. Setting this to nv12 reduces the frame depth to 8-bit, appropriate for SDR.
    • desat=0.5: Adjusts desaturation for bright pixels to prevent blown-out colors.
  • hwdownload,format=nv12: Pulls the processed frames from GPU memory back to host RAM and normalizes the format for standard CPU encoders.

Common tonemap_opencl Parameters

You can fine-tune the output by adjusting the following options inside the tonemap_opencl filter:

  • tonemap: Specifies the tone-mapping algorithm:
    • mobius: Smooth roll-off of highlights; best balance between details and contrast.
    • hable: Filmic tone mapping; prevents oversaturated highlights at the expense of dark detail.
    • reinhard: Simple non-linear curve; bright scenes can appear washed out if not tuned.
    • clip: Hard clips out-of-range luminance; fastest, but loses highlight details.
  • param: Modifies algorithm-specific behavior (e.g., transition threshold for mobius). Default is typically tuned for general use.
  • desat: Controls desaturation strength for out-of-gamut luminance (range 0.0 to 1.0). Higher values prevent color clipping in high-brightness areas.
  • peak: Overrides signal peak luminance in nits. If the source metadata is absent or inaccurate, you can define it explicitly (e.g., peak=1000).

Advanced: Integrating with Hardware Encoders

If you use a hardware encoder like VAAPI or NVENC, you can reduce host memory roundtrips by deriving the hardware device context directly, avoiding hwdownload where supported by your driver stack. For general compatibility across diverse GPU vendors, the standard pipeline using hwupload and hwdownload paired with standard encoders provides the most consistent tone-mapping results.