Skip to content

Hardware accelerated transcoding

Alex Shnitman edited this page Jul 31, 2026 · 1 revision

yt-dlp can re-encode downloads with ffmpeg (via postprocessor_args), and on a machine with an Intel iGPU that encoding can run on the GPU instead of the CPU. The MeTube image already ships ffmpeg, but not the VAAPI driver, and the container has no access to the GPU by default. This page covers the three things you need to add.

Contributed by @kamaeff in discussion #1045, tested on Debian 13 with an Intel N150.

1. Install the VAAPI driver in the image

The MeTube image is based on python:3.13-slim (Debian), so you can layer the Intel media stack on top of it with a small Dockerfile of your own:

FROM ghcr.io/alexta69/metube:latest

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        intel-media-va-driver \
        libvpl2 \
        libmfx-gen1.2 && \
    rm -rf /var/lib/apt/lists/*

Add vainfo to that list if you want to be able to check the driver from inside the container (see Verifying below).

The host needs the Intel media drivers installed too, and /dev/dri/card0 and /dev/dri/renderD128 must exist there.

2. Give the container access to /dev/dri

Pass the devices through, and run MeTube with the host's render group so it's allowed to open them. Find the GID first:

getent group render | cut -d: -f3

Then, in your compose file:

  metube:
    build: ./metube          # the Dockerfile from step 1
    environment:
      - "PGID=993"           # <- the render GID from above
    devices:
      - /dev/dri:/dev/dri

Note that PGID is also the group MeTube's downloaded files end up owned by, since the entrypoint chowns the download directories to PUID:PGID. If you'd rather keep your own group for the files, set user: in compose instead — that makes the entrypoint skip its gosu step and keep whatever groups Docker gave the container, so a supplementary group works:

    user: "1000:1000"
    group_add:
      - "993"                # the render GID

3. Tell ffmpeg to use the GPU

Encoding options go in postprocessor_args. The example below is a preset (via YTDL_OPTIONS_PRESETS_FILE) that decodes on the GPU, scales to 720p, and encodes to HEVC — all in hardware:

{
    "hevc-720p-vaapi": {
        "format": "bv*[height<=1440]+ba/b",
        "merge_output_format": "mp4",
        "postprocessors": [
            { "key": "FFmpegCopyStream" }
        ],
        "postprocessor_args": {
            "copystream+ffmpeg_i": [
                "-vaapi_device", "/dev/dri/renderD128",
                "-hwaccel", "vaapi",
                "-hwaccel_output_format", "vaapi"
            ],
            "copystream+ffmpeg_o": [
                "-vf", "scale_vaapi=w=-2:h=720,fps=24",
                "-vcodec", "hevc_vaapi",
                "-qp", "26",
                "-tag:v", "hvc1",

                "-acodec", "aac",
                "-ac", "1",
                "-b:a", "64k",

                "-f", "mp4",
                "-movflags", "+faststart"
            ]
        }
    }
}

What the pieces do:

  • -hwaccel vaapi + -hwaccel_output_format vaapi — decode on the GPU and keep the decoded frames in GPU memory, so no copying back and forth.
  • scale_vaapi — resize on the GPU. Use this rather than scale; the regular filter can't operate on VAAPI surfaces.
  • -vcodec hevc_vaapi — encode with the hardware HEVC encoder (h264_vaapi for H.264).
  • The copystream+ffmpeg_i / copystream+ffmpeg_o keys attach the arguments to the FFmpegCopyStream postprocessor's ffmpeg invocation, _i before the input and _o before the output. Both matter here: -hwaccel is an input option and the filter/encoder settings are output options.

The same setup can be defined globally via YTDL_OPTIONS instead of as a preset, but a preset lets you pick hardware transcoding per download in the UI.

Verifying

With vainfo installed in your image:

docker exec metube vainfo --display drm --device /dev/dri/renderD128

It should list the driver and a set of VAProfile... entries including VAEntrypointEncSlice for the codecs your GPU can encode. Also useful:

docker exec metube ffmpeg -hide_banner -encoders | grep vaapi

If ffmpeg fails with a permission error on /dev/dri/renderD128, the group in step 2 is wrong or wasn't applied — check with docker exec metube id.

Other GPUs

The device passthrough and group setup in step 2 are the same for AMD; swap the driver package in step 1 for mesa-va-drivers. NVIDIA doesn't use VAAPI — it needs the NVIDIA Container Toolkit and NVENC encoder names (hevc_nvenc, h264_nvenc) instead.

Clone this wiki locally