-
Notifications
You must be signed in to change notification settings - Fork 1k
Hardware accelerated transcoding
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.
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.
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/driNote 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 GIDEncoding 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 thanscale; the regular filter can't operate on VAAPI surfaces. -
-vcodec hevc_vaapi— encode with the hardware HEVC encoder (h264_vaapifor H.264). - The
copystream+ffmpeg_i/copystream+ffmpeg_okeys attach the arguments to theFFmpegCopyStreampostprocessor's ffmpeg invocation,_ibefore the input and_obefore the output. Both matter here:-hwaccelis 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.
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.
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.