Hardware Transcoding (HW) not working with libva/VA-API using d3d12 driver (WSL2)

Server Version#: 1.41.3.9314-a0bfb8370
Player Version#: 4.141.0

Hey there!

I’m not sure this is supported, but I thought it should work since Plex does support VA-API. :weary:

GPU acceleration in WSL has been around for a while now and using it in a Docker container is fairly easy. Although I’ve managed to successfully perform HW transcoding using ffmpeg, Plex itself fails to load the drivers.

Here is the error output on Ubuntu 22.04:

[Req#149/Transcode] Codecs: testing h264 (decoder) with hwdevice vaapi
[Req#149/Transcode] Codecs: hardware transcoding: testing API vaapi for device '' ()
[Req#149/Transcode] [FFMPEG] - libva: dlopen of /usr/lib/x86_64-linux-gnu/dri/d3d12_drv_video.so failed: Error relocating /usr/lib/x86_64-linux-gnu/dri/d3d12_drv_video.so: fcntl64: symbol not found
[Req#149/Transcode] [FFMPEG] - Failed to initialise VAAPI connection: -1 (unknown libva error).
[Req#149/Transcode] Codecs: hardware transcoding: opening hw device failed - probably not supported by this system, error: I/O error
[Req#149/Transcode] Could not create hardware context for h264

With Ubuntu 24.04 I get the same kind of error, but it’s a different symbol… probably because of the different libs versions:

[FFMPEG] - libva: dlopen of /usr/lib/x86_64-linux-gnu/dri/d3d12_drv_video.so failed: Error relocating /usr/lib/x86_64-linux-gnu/dri/d3d12_drv_video.so: __isoc23_strtoul: symbol not found

This is the output for vainfo --display drm on Ubuntu 24.04:

libva info: VA-API version 1.20.0
libva info: User environment variable requested driver 'd3d12'
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/d3d12_drv_video.so
libva info: Found init function __vaDriverInit_1_20
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.20 (libva 2.12.0)
vainfo: Driver version: Mesa Gallium driver 24.0.9-0ubuntu0.3 for D3D12 (Intel(R) Iris(R) Xe Graphics)
vainfo: Supported profile and entrypoints
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSlice
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSlice
      VAProfileHEVCMain               : VAEntrypointVLD
      VAProfileHEVCMain               : VAEntrypointEncSlice
      VAProfileHEVCMain10             : VAEntrypointVLD
      VAProfileHEVCMain10             : VAEntrypointEncSlice
      VAProfileVP9Profile0            : VAEntrypointVLD
      VAProfileVP9Profile2            : VAEntrypointVLD
      VAProfileAV1Profile0            : VAEntrypointVLD
      VAProfileNone                   : VAEntrypointVideoProc

I did my homework and I’m guessing this happens because of this change. And others have faced similar problems before… see this reply and this Dockerfile, which downloads the drivers in Alpine to work around the issue.

This is what I’m going to try next, but I don’t think people should go through all this trouble to make it work. I don’t have much expertise with this kind of issue, but I was hoping it could be fixed in Plex.

Here’s a minimal Docker setup if you’d like to reproduce it:

Just add any video inside the samples folder and then include it in a library once Plex is running. I used this video and on the web player I set resolution to 480p to trigger transcoding.

Not sure it matters, but this is my wsl --version output:

WSL version: 2.3.26.0
Kernel version: 5.15.167.4-1
WSLg version: 1.0.65
MSRDC version: 1.2.5620
Direct3D version: 1.611.1-81528511
DXCore version: 10.0.26100.1-240331-1435.ge-release
Windows version: 10.0.26100.2894

I’ve shared how I got here in this repo. While it primarily documents my personal home server setup, it includes valuable context that might help others troubleshooting similar issues.

@ChuckPa any chance you could help me with this? :smiling_face_with_tear:

I’m curious as to why you’re trying to use WSL with all those additional translation layers instead of the native package.

Your logs are showing that MUSL doesn’t know where to look for the D3D support libraries it needs to back-link into the windows runtimes.

As for what’s happening with the unresolved symbols;

  1. MUSL must be told where to look for runtime libraries (the toolchain)
  2. To keep configuration sane, I write the MUSL config each time PMS is started but before PMS actually launches the binary

Here is the code which adds (or skips) the Nvidia GPU libraries for PMS to use on QNAP


      # Construct ld-musl-x86_64.path
      mkdir -p ${QPKG_DIR}/etc
      {
       echo /lib
       echo /usr/lib
       echo /usr/local/lib
       echo /usr/local/cuda/lib64
      } > ${QPKG_DIR}/etc/ld-musl-x86_64.path

      # If GPU is supported (Has Drivers & Is Available)
      if [ $SupportedGPU -gt 0 ]; then

        # determine and write paths  (QTS & QuTS)
        GPU_DRV_DIR="$(getcfg -f $Config NVIDIA_GPU_DRV Install_Path)"
        [ "$GPU_DRV_DIR" != "" ] && echo $GPU_DRV_DIR/usr/lib >> ${QPKG_DIR}/etc/ld-musl-x86_64.path

        # Older versions
        if [ -d "/opt/NVIDIA_GPU_DRV" ]; then
          {
            echo /opt/NVIDIA_GPU_DRV/usr/syslib
            echo /opt/NVIDIA_GPU_DRV/usr/lib
            echo /opt/NVIDIA_GPU_DRV/usr/applib
          } >> ${QPKG_DIR}/etc/ld-musl-x86_64.path
        fi
      fi
    fi   # if x86_64

    # Make certain ${QPKG_DIR}/Library/tmp exists (those cases when the user is resetting but maintaining existing install on the side)
    if [ ! -d "${QPKG_DIR}/Library/tmp" ]; then

      # Create and set permissions
      mkdir -p "${QPKG_DIR}/Library/tmp"
      chown -R 0:0 "${QPKG_DIR}/Library/tmp"
    fi

While you can make this change for yours, I don’t see what benefit, if any, there will be.

May I ask what your end goal is?

In fact, I’m currently running Plex directly on Windows as a workaround, but I prefer to have all my services running in Docker containers. All the compose/Dockerfiles can be easily synced from/to Git and it makes it easier for me to manage versions and configs.

The base OS for my home server used to be Ubuntu and mapping /dev/dri was all that I needed, but now I’m trying to making the switch to Windows and have been struggling with this issue.

I thought support libraries were successfully found because first I had to set this in my environment to get ffmpeg to work:

LIBVA_DRIVER_NAME=d3d12
LD_LIBRARY_PATH=/usr/lib/wsl/lib

But that alone wasn’t enough for Plex:

[Req#87/Transcode] Codecs: testing h264 (decoder) with hwdevice vaapi
[Req#87/Transcode] Codecs: hardware transcoding: testing API vaapi for device '' ()
[Req#87/Transcode] [FFMPEG] - Failed to initialise VAAPI connection: -1 (unknown libva error).
[Req#87/Transcode] Codecs: hardware transcoding: opening hw device failed - probably not supported by this system, error: I/O error
[Req#87/Transcode] Could not create hardware context for h264

For Plex to find the drivers I also had to set LIBVA_DRIVERS_PATH=/usr/lib/x86_64-linux-gnu/dri. I discovered some of its child processes like Plex Plug-in [com.plexapp.system] had it set to /config/Library/Application Support/Plex Media Server/Cache/va-dri-linux-x86_64, which was just an empty folder.

So it doesn’t seem like an issue to find the support libraries but to actually load them? I truly appreciate the code you shared though! Will take a closer look soon and get back to you!

@victorsebrito

What you’re showing me is all based on using glibc which, in most cases, is the correct answer.

Plex doesn’t use glibc. It uses MUSL for multiple reasons:

  1. Smaller binaries
  2. Faster executables
  3. Increased portability
  4. Decreased dependency issues.
  5. Improved and consistent baseline (NAS glibc implementations are bad)

You’ll find that there are times when a host OS-native app is required.

Think of the structure of:

Host OS → Linux VM → Linux Container → Docker container
(There are different abstractions here. Look carefully)

Docker is on the bottom of the functionality list and least flexible.
If you want to look at something more flexible, just as light, easier to use, that Plex does run in — Look at Linux Containers ( example: Incus or LXD )

FWIW: I don’t use VMs or Docker (hate them both). I use LXC because it’s still a container (like Docker) with a full OS capability in it (like a VM) but not only a few hundred MB for the entire OS (no bloat)

PMS runs in these Linux Containers without issue. It takes about 5 minutes to spin up a new container and have PMS running – with GPU :wink:

I appreciate you taking your time to reply! It’s been a while, but I finally got some free time to work on this again.

I have this weird setup because I’m trying to share the GPU between the Windows host and the Docker containers inside WSL. :smiling_face_with_tear:

Anyways… I managed to build the drivers from source on Alpine and load them successfully into Plex, but stumbled upon what appears to be an issue with WSL itself. We’ll see how that goes.

I’m confident that Plex will work just fine once I get the drivers to work on Alpine.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.