Intel Quick Sync (VAAPI) on a Linux VPS: ffmpeg commands that work
Intel Quick Sync is the video engine inside Intel GPUs. On Linux, ffmpeg drives
it through VAAPI (h264_vaapi, hevc_vaapi, scale_vaapi…). This page is a
cheat sheet of commands we run on an Intel UHD 770; the encode, transcode and
AV1 commands are the ones used in our
shared-GPU load test, where they
ran for 20 minutes without a single failure.
On our VMs, ffmpeg and the VAAPI drivers are preinstalled and the GPU is the
render node /dev/dri/renderD128. On another machine, adjust the device path.
What the UHD 770 can do in hardware
| Codec | Decode | Encode |
|---|---|---|
| H.264 | yes | yes (h264_vaapi) |
| HEVC 8-bit and 10-bit | yes | yes (hevc_vaapi, -profile:v main10 for 10-bit) |
| VP9 | yes | yes |
| AV1 | yes | no: decode only |
1. Check the device
ls -l /dev/dri/ # renderD128 should be listed
vainfo --display drm --device /dev/dri/renderD128
In the vainfo output, each line pairs a profile with an entrypoint:
VAEntrypointVLD: hardware decode;VAEntrypointEncSlice: classic hardware encode;VAEntrypointEncSliceLP: low-power encode (-low_power 1in ffmpeg).
For AV1 (VAProfileAV1Profile0) you will only see VAEntrypointVLD: decode, no
encode.
If vainfo fails with a permission error as a normal user, add yourself to the
render group and log in again:
sudo usermod -aG render "$USER"
2. Test without a video file
ffmpeg can generate a test pattern, handy to check that encoding works before touching real files:
ffmpeg -vaapi_device /dev/dri/renderD128 \
-f lavfi -i testsrc2=size=1920x1080:rate=30 -t 60 \
-vf format=nv12,hwupload -c:v h264_vaapi -b:v 8M -f null -
-f null - discards the output: you only measure the encoder. Without
-nostats, ffmpeg prints speed=…x while it runs; above 1x means faster than
real time.
3. H.264 hardware encode
The input is decoded on the CPU, frames are uploaded to the GPU (hwupload),
then encoded by Quick Sync:
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' \
-c:v h264_vaapi -b:v 8M -c:a copy output.mp4
Low-power mode, a separate encoder block on the GPU:
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' \
-c:v h264_vaapi -low_power 1 -b:v 8M -c:a copy output.mp4
Both modes work on our VMs. If one of them errors out on another machine, check
that vainfo lists the matching entrypoint (EncSlice or EncSliceLP).
4. HEVC (H.265) hardware encode
Same pattern, different encoder:
# classic
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' -c:v hevc_vaapi -b:v 6M -c:a copy output.mkv
# low power
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' -c:v hevc_vaapi -low_power 1 -b:v 6M -c:a copy output.mkv
5. 10-bit HEVC (Main10)
10-bit uses the p010 pixel format and the main10 profile:
ffmpeg -vaapi_device /dev/dri/renderD128 -i input-10bit.mkv \
-vf 'format=p010,hwupload' \
-c:v hevc_vaapi -profile:v main10 -b:v 20M -c:a copy output-main10.mkv
The command we used to create a 4K Main10 test file (30 s, 40 Mbit/s):
ffmpeg -vaapi_device /dev/dri/renderD128 \
-f lavfi -i testsrc2=size=3840x2160:rate=30 -t 30 \
-vf format=p010,hwupload -c:v hevc_vaapi -profile:v main10 -b:v 40M -y src.mkv
6. Full GPU transcode (what Plex and Jellyfin do)
Decode, resize and encode all on the GPU: frames never go back to system memory, so the CPU stays almost idle.
ffmpeg -vaapi_device /dev/dri/renderD128 \
-hwaccel vaapi -hwaccel_output_format vaapi -i movie-4k.mkv \
-vf 'scale_vaapi=w=1920:h=1080:format=nv12' \
-c:v h264_vaapi -b:v 8M -c:a copy movie-1080p.mkv
-hwaccel vaapi -hwaccel_output_format vaapi: decode on the GPU and keep the frames there;scale_vaapi=…:format=nv12: resize on the GPU and convert 10-bit to 8-bit, because H.264 output is 8-bit;- add
-low_power 1afterh264_vaapifor the low-power encoder.
In our load test, this exact pipeline (4K HEVC 10-bit → 1080p H.264) ran at about 1.3× real time per server while the shared GPU was saturated. On an idle GPU with the low-power encoder, our Jellyfin & Plex guide measured 12× real time for a 4K HDR → 1080p transcode.
7. AV1: hardware decode, HEVC or H.264 output
The UHD 770 decodes AV1 but has no AV1 encoder: av1_vaapi will not work on
this GPU. Decode AV1 on the GPU and re-encode to HEVC or H.264:
ffmpeg -vaapi_device /dev/dri/renderD128 \
-hwaccel vaapi -hwaccel_output_format vaapi -i input-av1.mkv \
-vf scale_vaapi=w=1920:h=1080 -c:v hevc_vaapi -b:v 6M -c:a copy output-hevc.mkv
If you need AV1 output, encode it on the CPU (for example libsvtav1), which is
how we created our AV1 test file:
ffmpeg -f lavfi -i testsrc2=size=3840x2160:rate=30 -t 10 \
-c:v libsvtav1 -preset 12 -y src.mkv
To measure decode speed alone, send the frames to the null muxer:
ffmpeg -vaapi_device /dev/dri/renderD128 \
-hwaccel vaapi -hwaccel_output_format vaapi -i input-av1.mkv -f null -
8. Inside Docker
Pass the render node and its group to the container:
docker run --rm -it \
--device /dev/dri/renderD128 \
--group-add "$(stat -c %g /dev/dri/renderD128)" \
-v "$PWD":/work -w /work \
your-ffmpeg-image ffmpeg -vaapi_device /dev/dri/renderD128 ...
The image needs its own VAAPI driver (Intel media driver) and an ffmpeg built with VAAPI.
9. Check that the GPU is really working
nvtopworks inside our VMs: GPU load for the VM and per process.intel_gpu_topdoes not work inside a VM’s virtual GPU: it relies on i915 PMU counters that are not exposed to the VM. It only works on the physical host.gpu-check(preinstalled on our VMs) tests VAAPI, Vulkan, OpenCL and OpenVINO in one command.- After a heavy job, look for GPU errors in the kernel log:
journalctl -k -b | grep -iE "gpu hang|context reset|reset.*engine|guc.*fail"
Empty output means no hang was recorded since boot.
Common mistakes
| Symptom | Likely cause |
|---|---|
vainfo or ffmpeg: permission denied on /dev/dri/renderD128 | user not in the render group (or container started without --group-add) |
Impossible to convert between the formats | frames are in system memory but the encoder expects GPU frames: add hwupload (with -vaapi_device) |
| High CPU during a “GPU” transcode | -hwaccel_output_format vaapi missing, or a CPU filter (scale, not scale_vaapi) in the chain |
| AV1 encode fails | expected: the UHD 770 has no AV1 encoder |
Related
- Shared Intel UHD 770 under full load: a 20-minute GPU stress test
- Hardware-accelerated video transcoding without a €500/month GPU
- UHD 770 vs HD Graphics 530 benchmarks
Try it
A Linux VM with an Intel UHD 770 inside, hosted in Germany: ffmpeg, VAAPI,
OpenCL and nvtop preinstalled, root SSH, dedicated IPv6.
Plans from €14.99 excl. VAT/month, flat price.