How to Use tonemap_vaapi in FFmpeg

This article provides a practical guide on using FFmpeg's tonemap_vaapi filter to convert High Dynamic Range (HDR) video to Standard Dynamic Range (SDR) using VA-API hardware acceleration. You will learn the required hardware setup, essential filter options, and complete command-line examples for both full-hardware pipelines and hybrid workflows.


Prerequisites

To use tonemap_vaapi, you must meet the following requirements:

  1. A compatible Intel or AMD GPU that supports VA-API tone-mapping (typically Intel Gen9 Skylake or newer, or modern AMD GPUs).
  2. FFmpeg compiled with --enable-vaapi and relevant driver support (intel-media-driver / iHD or radeonsi).
  3. Access to the direct rendering device node, typically /dev/dri/renderD128.

Understanding the Filter Parameters

The tonemap_vaapi filter maps wide color gamuts and high dynamic ranges down to standard color profiles directly inside GPU memory. Key parameters include:

  • format: The output pixel format. For standard SDR H.264/H.265 encoding, use nv12.
  • p / primaries: Target color primaries. Common setting: bt709.
  • t / transfer: Target transfer characteristics. Common setting: bt709.
  • m / matrix: Target color space matrix. Common setting: bt709.

Complete Hardware Pipeline (Decode, Tonemap, Encode)

The most efficient way to use tonemap_vaapi is to keep all frame data in GPU memory from decoding through encoding. This eliminates the overhead of copying frames between system RAM and video RAM.

ffmpeg -init_hw_device vaapi=va:/dev/dri/renderD128 \
  -hwaccel vaapi -hwaccel_output_format vaapi \
  -i input_hdr.mkv \
  -vf "tonemap_vaapi=format=nv12:p=bt709:t=bt709:m=bt709" \
  -c:v h264_vaapi -b:v 8M -c:a copy \
  output_sdr.mp4

Breakdown of the command:

  • -init_hw_device vaapi=va:/dev/dri/renderD128: Initializes the VA-API device context.
  • -hwaccel vaapi -hwaccel_output_format vaapi: Decodes the HDR stream directly into hardware surfaces.
  • -vf "tonemap_vaapi=format=nv12:p=bt709:t=bt709:m=bt709": Maps the color space from HDR10 (BT.2020 / SMPTE 2084) to SDR (BT.709) and sets the pixel format to NV12.
  • -c:v h264_vaapi: Encodes the tonemapped video using the VA-API H.264 encoder.

Hybrid Workflow (Software Decode, VA-API Tonemap)

If your GPU does not support hardware decoding of the specific HDR profile (such as 10-bit HEVC), you can decode on the CPU and upload frames to the GPU for tone-mapping and encoding.

ffmpeg -init_hw_device vaapi=va:/dev/dri/renderD128 \
  -i input_hdr.mkv \
  -vf "format=p010,hwupload,tonemap_vaapi=format=nv12:p=bt709:t=bt709:m=bt709" \
  -c:v h264_vaapi -b:v 8M -c:a copy \
  output_sdr.mp4

Breakdown of additions:

  • format=p010: Ensures software-decoded frames are in a 10-bit format compatible with VA-API upload.
  • hwupload: Moves frames from host memory to the VA-API hardware surfaces before processing with tonemap_vaapi.

Exporting to Software Encoders (e.g., libx264)

If you require software encoding (such as libx264 for higher compression efficiency), download the tonemapped frames back to system memory using hwdownload:

ffmpeg -init_hw_device vaapi=va:/dev/dri/renderD128 \
  -hwaccel vaapi -hwaccel_output_format vaapi \
  -i input_hdr.mkv \
  -vf "tonemap_vaapi=format=nv12:p=bt709:t=bt709:m=bt709,hwdownload,format=nv12" \
  -c:v libx264 -crf 20 -preset medium -c:a copy \
  output_sdr.mp4

Note: The trailing format=nv12 filter after hwdownload removes the hardware surface wrapper so the software encoder can process the raw frames.