Target OS: Linux (Intel iGPU / AMD GPU supported)

What Is VAAPI

VAAPI (Video Acceleration API) is the standard way on Linux to have the GPU encode and decode video. It works with Intel integrated GPUs and AMD GPUs. On NVIDIA GPUs, VAAPI can only decode. Because the GPU’s own encoder does the work, it is much faster than encoding on the CPU.

GPU VAAPI Support
Intel iGPU H.264 (Sandy Bridge and later; Haswell and older need the old i965 driver); H.265 on newer generations; AV1 encode limited to Intel Arc / some newer Xe
AMD GPU H.264 / H.265 (depends on the VCE/VCN generation and the driver); AV1 on RDNA3 and later
NVIDIA (nouveau, etc.) Decode only (use NVENC for encoding)

Prerequisites

Installing the Required Packages

Ubuntu / Debian:

※ This command requires a VAAPI environment
sudo apt install vainfo libva-dev intel-media-va-driver-non-free  # Intel (Broadwell and later)
sudo apt install vainfo libva-dev i965-va-driver-shaders          # Intel (Haswell and older)
sudo apt install vainfo libva-dev mesa-va-drivers                 # AMD

The plain intel-media-va-driver package cannot encode H.264 on Broadwell. Both Intel packages are in the non-free repository on Debian and in multiverse on Ubuntu.

Arch Linux:

※ This command requires a VAAPI environment
sudo pacman -S intel-media-driver libva   # Intel
sudo pacman -S mesa libva                 # AMD

Verifying the VAAPI Device

※ This command requires a VAAPI environment
vainfo

If the output lists entry points (the jobs the GPU can do) such as VAEntrypointEncSlice, you are ready.

Basic h264_vaapi Command

※ This command requires a VAAPI (Linux) environment
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v h264_vaapi -qp 23 \
  -c:a aac -b:a 128k output_vaapi.mp4

What the options do:

  • -vaapi_device /dev/dri/renderD128: the VAAPI device to use. Some systems have more than one, such as /dev/dri/renderD129.
  • -vf 'format=nv12,hwupload': sends the frames to GPU memory. The encoder needs this.
  • -qp 23: the quality (1–51, lower means better quality; 0 means not set).

hevc_vaapi (H.265)

※ This command requires a VAAPI (Linux) environment
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v hevc_vaapi -qp 28 \
  -c:a aac -b:a 128k output_hevc_vaapi.mp4

av1_vaapi (AV1) — Intel Arc / Core Ultra and Later

※ This command requires a VAAPI (Linux / Intel Arc) environment
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v av1_vaapi -rc_mode CQP -global_quality 30 \
  -c:a aac -b:a 128k output_av1_vaapi.mp4

av1_vaapi has no -qp option, unlike h264_vaapi and hevc_vaapi. If you pass it anyway, FFmpeg only warns (Codec AVOption qp ... has not been used for any stream) and encodes at the default quality. For constant quality, use -rc_mode CQP -global_quality 30. For a set bitrate, use something like -b:v 4M. In av1_vaapi, -global_quality is the AV1 quantizer (0–255, 25 when not set). That is a different scale from -qp (1–51), but here too lower means better quality. AV1 encoding works on Intel Arc (Alchemist), Core Ultra (Meteor Lake) and later, and AMD RDNA3 and later.

Full-GPU Pipeline: VAAPI Decode + Encode

If the GPU (VAAPI) decodes as well, CPU usage stays close to zero:

※ This command requires a VAAPI (Linux) environment
ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 \
  -hwaccel_output_format vaapi \
  -i input.mp4 \
  -c:v h264_vaapi -qp 23 \
  -c:a copy output_fullgpu.mp4

The decoded frames stay on the GPU and are never copied to the CPU.

Bitrate-Targeted Encoding

-rc_mode CBR keeps the bitrate constant, and -b:v 4M sets it.

※ This command requires a VAAPI (Linux) environment
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v h264_vaapi -rc_mode CBR -b:v 4M \
  -c:a aac -b:a 128k output_cbr.mp4

Common Errors and Fixes

error while loading shared libraries: libva.so.2

libva is not installed. On Ubuntu, install it with sudo apt install libva2.

Device creation failed (/dev/dri/renderD128 not found)

Check that the device exists with ls /dev/dri/. Also check that the GPU’s kernel module is loaded (i915 for Intel, amdgpu for AMD).

Error without format=nv12,hwupload

VAAPI encoders only accept frames that are in GPU memory, so you need hwupload.

10-bit material comes out as 8-bit

nv12 is an 8-bit format, so format=nv12,hwupload turns 10-bit material into 8-bit before it reaches the GPU. To keep 10 bits, use -vf 'format=p010le,hwupload' and a 10-bit profile such as -profile:v main10. Your GPU and driver must support 10-bit encoding.

VAAPI vs. NVENC

Aspect VAAPI (Intel/AMD) NVENC (NVIDIA)
Target GPU Intel iGPU / AMD GPU NVIDIA GPU
OS Primarily Linux Windows / Linux
Setup complexity Somewhat complex (device specification) Relatively simple
Speed Fast Fast

Speed and Quality Ballparks

Speed and quality depend heavily on the GPU generation and the driver (on Intel, the old i965 driver or the newer iHD/media-driver).

  • Encode speed: Even an integrated GPU is often faster than libx264 -preset medium on the CPU. Discrete GPUs such as Intel Arc or AMD RDNA can be faster still.
  • Quality: To reach the same VMAF score (a measure of picture quality), VAAPI usually needs a higher bitrate than libx264. The gap depends on the generation and the content. Intel’s newer media-driver (iHD) has tuning similar to QSV’s and gives steadier quality than the old i965.
  • AV1: On Intel Arc and AMD RDNA3, AV1 can give the same quality as H.264 in a clearly smaller file, which helps most for streaming.

Common Pitfalls

Permission error on /dev/dri/renderD128

  • Symptom: FFmpeg can’t create the device and stops with No VA display found for device /dev/dri/renderD128. With -v verbose, it also shows Failed to open /dev/dri/renderD128 as DRM device node.
  • Cause: Your user is not in the render (or video) group. This happens most often inside containers.
  • Fix: Add your user with sudo usermod -aG render $USER, then log out and back in. In Docker, pass the device with --device /dev/dri and give --group-add the host’s render group ID (GID).

Impossible to convert between the formats when format=nv12,hwupload is missing

  • Symptom: -c:v h264_vaapi without the filter stops with a format conversion error.
  • Cause: VAAPI encoders only take frames in GPU memory (called surfaces). Frames in CPU memory can’t be passed to them directly.
  • Fix: Always add -vf 'format=nv12,hwupload'. When the GPU also decodes (-hwaccel vaapi -hwaccel_output_format vaapi), the frames are already on the GPU and you don’t need hwupload.

Picking the wrong device on multi-GPU systems

  • Symptom: FFmpeg uses the discrete GPU when you wanted the integrated one, or the other way round.
  • Cause: Which GPU is /dev/dri/renderD128 and which is renderD129 differs from system to system.
  • Fix: Run ls -l /dev/dri/by-path/ to see which PCI address belongs to which device. Then pass the renderDxxx of the GPU you want to -vaapi_device.

10-bit input becomes 8-bit

  • Symptom: You encode HDR or 10-bit material with format=nv12,hwupload. There is no error, but the output is 8-bit.
  • Cause: nv12 is an 8-bit format, so the material is converted from 10-bit to 8-bit at that step.
  • Fix: Use -vf 'format=p010le,hwupload' instead, and set a 10-bit output profile such as -profile:v main10.

FAQ

How is VAAPI different from QSV (Quick Sync)?

Both use the Intel GPU’s hardware encoder. VAAPI is the standard Linux interface, while QSV (h264_qsv) goes through Intel’s own SDK. On Linux, VAAPI is the portable choice. QSV makes sense when you want Intel-only features or also need to run on Windows.

Should I control quality with -qp or -rc_mode?

Use -qp for constant quality (lower means better quality; start around 23). When you need a steady bitrate for streaming or a size limit, use rate control such as -rc_mode CBR -b:v 4M. For a mix of both, use -rc_mode VBR -b:v with -maxrate.

Can AMD GPUs use VAAPI too?

Yes. Install mesa-va-drivers (Mesa Gallium). With the kernel’s amdgpu module enabled, h264_vaapi and hevc_vaapi work. AV1 encoding needs RDNA3 or later.

Quality looks worse than my CPU encode

Even at the same quality setting, VAAPI compresses a little less well than libx264, so at the same file size it can look softer. Lower -qp by 1–2 or raise the bitrate by 10–20%, and the difference becomes hard to see.

How do I use VAAPI inside a Docker container?

Pass the device with docker run --device /dev/dri ... and add the container user to the host’s render group ID (GID). If vainfo works inside the container, you’re ready.