[HDR Metadata Passthrough] - Plex HTPC for Windows

True HDR Metadata passthrough with Plex HTPC on Windows


(For problems with Windows 11 24H2 and HDR switching scroll down to the end of this post)

Here I just want to write a small guide on how to achieve a true HDR experience with Plex HTPC, now that it´s released and the main Plex HTPC thread is closed.

Please read until the end and also keep in mind true metadata passthrough is in an experimental (but working very well…) state as of 05/2024!

You may ask why? Didn’t Plex just recently gained the option for enabling HDR switching on HDR files? Why bother with something else?

Well, there with HDR everything gets a bit hairy. First, we should disclose what HDR on Windows is doing and why it is not really what we want for our 4K HDR movies. Small spoiler it’s all about Metadata…

Enabling HDR on Windows under the Windows settings menu does three things.

  1. Switches your TV into a different color space => BT.2020
  2. Tells the TV it’s getting an HDR feed
  3. Sends some generic metadata to the TV.

Part 3 is where it´s getting fuzzy. The metadata we care about is:

  • Mastering display color primaries (BT.2020 or DCI-P3)
  • maxCLL (Maximum Content Light Level)
  • maxFall (Maximum Frame Average Light Level).
  • Minimum Luminance
  • Maximum Luminance

The usefulness of the latter two is not fully understood. I could also not find reliable information regarding these values. The information I could find would state that these values stem from the grading monitor used in the mastering process. There are also movies out there that have a max Luminance of 1000Nit but a maxCLL beyond 1000Nit…

As far as I could gather those values (measured with a hdfury arcana) for the generic Windows HDR, they are:

AMD GPU:
maxFALL: 799 cd/m2 maxCLL: 1499 cd/m2 Max Lum: 1499 cd/m2 Min Lum: 0.010 cd/m2 Primaries: unkown

Nvidia GPU:
maxFALL: 0 cd/m2 maxCLL: 0 cd/m2 Max Lum: 0 cd/m2 Min Lum: 0 cd/m2 Primaries: unkown
(so for Nvidia it outputs “default” BT.2020 and only tells the TV it should use the PQ gamma curve)

(cd/m2 = Nits)

For movies that are mastered in the same manner, it would be fine, but not every movie is mastered at the same levels, and probably not a single one at those generic values.

You can check color primaries, minLuminace, maxLuminace, maxCLL, and maxFall on your HDR files with MediaInfo.

One example =>

MediaInfo
Video
....
Mastering display color primaries        : Display P3
Mastering display luminance              : min: 0.0000 cd/m2, max: 4000 cd/m2
Maximum Content Light Level              : 1347 cd/m2
Maximum Frame-Average Light Level        : 546 cd/m2

Most UHD movies actually use the DCI-P3 colorspace inside the BT.2020 colorspace. Plex HTPC can let the TV know that the file is DCI-P3.


Now you may ask and how does Plex HTPC do it?

Well, Plex HTPC uses a Windows API to switch the HDR button under the Windows settings on for you when watching an HDR movie. As we learned you will only get the generic HDR. Your image will still look like an HDR file but not as rich as with full metadata passthrough. We still need this capability of Plex HTPC switching HDR on or off for the D3D11 route and Vulkan route.


How can we get true HDR passthrough?

Glad you asked! As you might know, Plex HTPC uses mpv as its playback engine. mpv is an extremely powerful video player that supports full HDR metadata passthrough via its new video output driver, called gpu-next.
If you like to read what is new compared to gpu which Plex HTPC uses, you may want to have a look here => GPU Next vs GPU · mpv-player/mpv Wiki · GitHub


The easy way to get full HDR metadata passthrough

  • Head over to settings in Plex HTPC
  • Go to the “Video” section and there select both “Enable HDR Switching” as well as “HDR Metadata Passthrough”. It then should look like the image below.

Now you are ready to experience HDR metadata passthrough!

The advanced way:

Continue reading!


Now comes the fun part.

The replacement of the mpv lib is no longer needed!

It is only necessary if you need Dolby Vision profile 5 and 8.x. tone mapping and/or if you have problems with TrueHD passthrough.
If you are having issues with TrueHD passthrough, you should try this build (which also includes the Dolby Vision tone mapping bits) => Dolby TrueHD passthrough - modified mpv build

You can follow this HTPC Tips and Tricks - #3 by gbooker02 on how to change the mpv build or read further if you want to stay here. (they are the same steps)


Note about the newest libmpv-2.dll and HDR - breaking changes!

Starting with build 2025-07-30 17:57 ( Release 2025-07-30 17:57 · mitzsch/mpv-winbuild · GitHub ) the libmpv-2.dll now includes the newly introduced “–target-colorspace-hint-mode” option. This defaults to mpv doing the tonemapping and not the display (= no metadata passthrough). To restore the old behavior, add target-colorspace-hint-mode=source to your mpv.conf!


What do we need?

  1. A PC with a recent GPU and a recent driver.
  2. Downloaded or self-compiled version of the mpv-1.dll
  3. Show file extensions under Explorer settings
  4. notepad or any text editor

First, we want to gather a new mpv build. You can just download it here:

If you like to self-compile your mpv for Windows I can highly recommend this GitHub repo:

Or my fork of that repo for building mpv with modifications needed for Plex HTPC to work:


Starting with Plex HTPC Version 1.20.2 the mpv-1.dll is now called mpv-2.dll!*

The mpv-2.dll is now called libmpv-2.dll!!

Unzip the archive. Since 20.01.2023 the mpv lib you download may have a different name (libmpv-2.dll), just rename it to mpv-2.dll. Then go to the Plex HTPC install folder. (C:\Program Files\Plex\Plex HTPC) and rename the libmpv-2.dll you find there to something like libmpv-2.dll.orig. Now place the unzipped libmpv-2.dll file in the same directory.


In the Appdata dir (C:\Users\xxx\AppData\Local\Plex HTPC) create a new mpv.txt textfile. Now change the file extension from .txt to .conf. Then right-click on the mpv.conf and open with editor or notepad.

Inside the file, we need to add

The Vulkan Route

vo=gpu-next
gpu-api=vulkan
gpu-context=winvk
tls-ca-file=C:\Program Files\Plex\Plex HTPC\resources\cacert.pem

The D3D11 Route

vo=gpu-next
tls-ca-file=C:\Program Files\Plex\Plex HTPC\resources\cacert.pem

hit save and exit!

The line target-colorspace-hint=yes is no longer needed. Since Plex HTPC Version 1.28.1 this setting is set when the HDR metadata passthrough setting is on and an HDR file is being played.

Also, vo=gpu-next is not necessarily needed. Since Plex HTPC Version 1.28.1 this setting is set when the HDR metadata passthrough setting is on and an HDR file is being played.
Having the vo=gpu-next line in mpv.conf means that the vo is also used for SDR content. It works perfectly fine so I recommend also adding this line.


Plex HTPC Settings:

Vulkan route:
Turn on the HDR switching and HDR metadata passthrough under the Plex HTPC settings

D3D11 route:
Turn on the HDR switching and HDR metadata passthrough under the Plex HTPC settings


Now you can open Plex HTPC and play your HDR files with full Metadata passthrough.
This means just static HDR10, no HDR10+ or DV!


Difference between the Vulkan and D3D11 method:

The mpv D3D11 route relies on Plex HTPC to turn your TV into (generic) HDR mode, when this has happened, mpv takes over and passes through the actual metadata of the file you are watching. The result is the same as with the Vulkan setting. The downside on this one is it relies on Microsoft to keep the HDR capabilities on Windows as they currently are and not restrict them any further. (mpv was capable of switching your TV into HDR mode with D3D11 until MS decided to strip away this feature) With the Vulkan approach, mpv can switch the TV into HDR, regardless of what Windows is doing. This is highly dependent on working Vulkan drivers for your GPU.


Difference between the Vulkan and D3D11 method regarding the used GPU:

  • The D3D11 method works on AMD, Nvidia, and Intel (ARC) GPU-based systems.
  • The Vulkan method works on Nvidia GPU-based systems - AMD unclear, Intel ARC not tested.

Note for AMD Systems: It only works, when HDR Switching in Plex HTPC is turned on.
(If Windows HDR is not activated upon playback Vulkan would error out with VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT - (tested with mpv cli))

  • The Vulkan method however, seems to work only with an Nvidia GPU as of August 2022.
    (On the AMD side, I tested it with an RX 580 and it spits out Vulkan errors.)

Performance issues with HDR Metadata passthrough enabled:

Nvidia GPUs may show a very high number of dropped frames during 4K HDR playback with the HDR metadata passthrough setting enabled (with D3D11).
Changing the power mode to max in the Nvidia control panel has shown a significant reduction in dropped frames. If you don´t want your GPU to run at the full clock all the time you can also create a profile for Plex HTPC. When running Plex HTPC only then the GPU runs at full clock speed. This is also done through the Nvidia control panel. (Manage 3D settings → Program settings → Add → select Plex HTPC → Change power mode to max → save)


Note on HDR10+ and Dolby Vision:

Newer versions of libplacebo/mpv are capable of using the provided dynamic metadata of those HDR formats. This can lead to additional tone-mapping introduced to image processing, even when having HDR passthrough (=in my definition no additional signal processing) enabled. This happens only when the dynamic metadata has values above the static ones - mpv will then tone map those dynamic bits to the static given metadata. Whether you want this or not, depends highly on your viewing preferences. This “feature” can cause frame drops in some cases when the shader creation for the tone mapping algorithm can not be done fast enough.

For HDR10 purists (as this is the only format Windows and mpv support natively) like me, we don’t want this additional processing.
For pure HDR10 metadata processing, add: libplacebo-opts=tone_map_metadata=hdr10 to your mpv.conf. This option tells mpv to only use the HDR10 layer and ignore any other dynamic metadata.

This does not affect your Dolby Vision Profile 5 files. Those are still processed and tone-mapped. (Only with upstream mpv.dll!)


Testing HDR Metadata passthrough:

Just for reference how someone can determine if metadata passthrough is really working without expensive gear like an HDfury Arcana/Vertex. Use the Dolby Core Universe HDR clip (which can be found online) and you can clearly see if it’s working when he says “… what you thought is black isn´t… this is black” (around the 1´30 mark). You should first see a very grey almost black screen, which should switch to a true black when he says “This is black”. When HDR metadata passthrough is not working the very grey is just grey. (but also switches to true black.)


What does not work currently? (Vulkan route for mpv cli only!!)

Starting with Version 1.28.1 Plex HTPC includes a workaround fixing this!

No software is bug-free and unfortunately, mpv has one… Switching the TV into HDR works but not back to SDR after the file ends with the parameters needed for Plex HTPC to work properly (–idle=yes). Opening an SDR file or quitting Plex HTPC will switch it back to SDR. Starting with Version 1.28.1 Plex HTPC includes a workaround fixing this! It does so by playing an “invisible” SDR file and therefore resets the mpv logic and reverts the TV back to SDR.

I have already opened a bug report on Github addressing this one.


GPUs used for testing:

  • Nvidia RTX 3050 8Gb
  • Nvidia RTX 4060 8Gb
  • AMD RX580 8Gb
  • Intel ARC A770 16Gb


Sidenote:

No HDR switching with Windows 11 24H2

With Windows 11 24H2 there is a new feature introduced. It’s called “Automatic color management” or short “ACM” - in SDR mode, it pretends HDR is enabled. This causes apps like Plex HTPC to get confused, as they think they are already in HDR mode. Turning ACM off fixes the issue.
Go to System settings => System => Display => Color profile and turn off ACM


Sidenote:
Stuttering on Nvidia GPUs:

Newer Nvidia drivers (All after 512.96) cause 23/24p playback to visibly stutter but without dropping frames.
Add: swapchain-depth=1 to the mpv.conf and the problem should be fixed.


Feel free to correct me, and of course, thanks to Plex for bringing up such a worthy successor of Plex Media Player!


Version/Edit 22 as of 11.08.2025

6 Likes

FYI, I outlined the instructions for substituting your own MPV library in this post: HTPC Tips and Tricks - #3 by gbooker02. In there I point the tls-ca-file to the cert in the Plex HTPC install which saves you a step.

Also, note: (At least the last time I tested it) gpu-next with winvk uses software decoding and not hardware decoding.

Oh, I see, well I missed that. Thanks for pointing this out!

hwdec with winvk and vo=gpu-next does indeed work with recent builds, but only nvdec and dxva2-copy flavors are supported.

##FIXED with Plex HTPC Version 1.26.1

[Vulkan and D3D11 route]:
Another thing I noticed while using Plex HTPC with target-colorspace-hint=yes is a grey square in the middle of the screen that seems it´s not affected by the HDR image processing. This happens with both the Vulkan and the D3D11 method but the square with D3D11 seems to be less noticeable.

This happens when you first watched an SDR file with refresh rate/resolution switching turned on and then watch an HDR file. With the SDR file, you should see the cursor for a second after the mode change, with the HDR file opened after SDR one there is no visible cursor. The cursor is normally invisible but with the target-colorspace-hint=yes it is shown as a grey square in the middle of the screen. It is also not noticeable in all scenarios and it will disappear if you move the mouse. (the cursor will appear in the same spot)

Here is an example of how it looks. The movie is Spider-Man: Into the Spider-Verse


Fix:

Download the small tool AutoHideMouseCursor
https://www.softwareok.com/?Download=AutoHideMouseCursor

Ideally, you would launch it before using Plex HTPC. When opened set the time to the lowest value (2s) and also under options the “Hide Mouse” to “New strategy + Aggressive”. Now the grey square will not appear again.

I know this is an unsupported case but maybe Plex could take a look at implementing the “disable mouse function” again like it is found in PlexMediaPlayer? (I know we had a discussion on this function already on the Plex HTPC main thread)

Given the nature of this thread being a bit of a howto, you may want to edit the first post to always have the most up-to-date info.

Arg, I forgot that the copy-back hwdec was added. Likely because I don’t consider it a great solution because it wastes a lot of memory bandwidth between the CPU/GPU. Is it copy-back or no-copy when using nvdec?

All this did is bypass the code that explicitly showed the mouse when it was moved. Perhaps a refresh rate change triggers a mouse-move event in Windows? The actual method for hiding the mouse is the same as in PMP. Maybe the better solution would be to ignore mouse move events shortly after a display change?

Updated!

For nvdec luckily it is no-copy.

image

Also when enabling this setting?

Plex Media Player has the same behavior regarding mouse hiding as Plex HTPC until I checked this setting. After that, the mouse did not appear even after a mode change in PMP.

Maybe this would help. Would it be possible to bring this to Plex HTPC?

Thanks. I asked for this because now I can link to it from the Tip/Tricks thread.

Yes. When the mouse is moved, PMP will re-show the mouse unless that setting is set. See https://github.com/plexinc/plex-media-player/blob/ae73e074b1d5a94a3975fc93c883840ab786ff0c/src/ui/EventFilter.cpp#L93-L101 which prevents https://github.com/plexinc/plex-media-player/blob/ae73e074b1d5a94a3975fc93c883840ab786ff0c/src/ui/EventFilter.cpp#L154-L157 from being called when the setting is set. But the actual technique used to hide the mouse is the same in HTPC and PMP. So I have doubts that bringing in that setting will help the box issue you are seeing unless what matters is just whether the mouse was displayed at all.

Ah, I see, thanks!

Well, I don’t know either if this would fix it. Somehow with the AutoHide tool, it works, unfortunately, I don’t know how the tool works. (Is it removing the mouse just from being displayed or is it really disabling it a deeper system level?) It’s kinda getting complex here.

Just for testing, it would probably take too much effort to bring that setting in?

I went ahead and implemented the code for this so it’ll be in a future version. No setting; it’s always going to ignore “mouse movements” after a refresh rate/resolution/hdr change. Though it may not make the cutoff for the next version but should be in the following one.

1 Like

Reading this mpv issue, it sounds like with libplacebo version >= 4.204.0 used with --vo=gpu-next and --target-colorspace-hint=yes should now correctly set the HDR metadata based on source content with d3d11 context, though I haven’t tested this (and don’t know the impact on hardware decoding). Can anyone confirm?

It should work although it will not automatically switch HDR on for you. With Plex HTPC being able to turn it on, we should definitely try them in conjunction. I will try it on the weekend and report back, if it works fine I will also update the guide.

In a discussion I had with some MPV developers on IRC, they stated that Microsoft had imposed restrictions on the driver vendors on what they are able to do with D3D11. One of the notable examples is that the driver cannot switch HDR on/off. Apparently they used to do this and MS came down on them. Additionally this restriction is not in place with Vulkan which is why it can switch HDR on (and should also be able to switch it off when the bug is fixed).

The GHI you linked indicates that the metadata setting works in this case but I do wonder if MS might decide to clamp-down on it in the future.

I have now tried it with gpu-context=d3d11 (default), vo=gpu-next and target-colorspace-hint=yes. If you have HDR switching turned on in the Plex HTPC settings it works great and the metadata is also correctly passed through. Hardware decoding also works fine. After playback HDR is also turned off correctly, thanks to Plex HTPCs logic. The mentioned bug in the mpv HDR logic is probably still present but not noticeable, due to Plex switching HDR on and off and with every file, you open next the logic gets reset (to the new file values). I will update the first post.

Another thing I noticed while testing it with the default gpu-context is I got fewer dropped frames. With Vulkan sometimes I got 10+ dropped frames, with d3d11 I only had 1 or 2.
(This also happens with the cli player - so no Plex issue)


Hopefully, Microsoft will not restrict it any further!


  • Testing HDR Passthrough

Just for reference how someone can determine if metadata passthrough is really working without expensive gear like an HDfury vertex. Use the Dolby Core Universe HDR clip (which can be found online) and you can clearly see if it’s working when he says “… what you thought is black isn´t… this is black” (around the 1´30 mark). You should first see a very grey almost black screen, which should switch to true black when he says “this is black”. When HDR metadata passthrough is not working the very grey is just grey. (but also switches to true black.)

1 Like

Interesting that you get the metadata passthrough with D3D11. Maybe it was just the HDR toggle that MS didn’t want drivers doing in D3D11 but if it’s already in HDR they are fine with metadata passthrough? Do note that you may be hitting the D3D11 VRAM leak that I previously submitted a PR to fix. I’m not sure if it uses this code path in gpu-next but it might.

Speaking of which, I posed a thought in your issue on vulkan not switching HDR back to SDR and they now have another patch to try out (this time in libplacebo).

Interesting. With vo=gpu (not gpu-next) I was getting more frame drops in D3D11 than in Vulkan.

Yeah, I was hitting this one again while testing, so definitely also happening with vo=gpu-next. I hope your fix gets merged somewhere soon.

I will try that one and report back! Thanks for peeking in there!

Oh, that is interesting. Maybe it´s just the CPU on my side. I’m using an “older” Ryzen quadcore.

How is your GPU usage while using Vulkan? (Mine is substantially higher with Vulkan in terms of clock speed and VRAM usage)

Awesome! Now we just need a way to preserve the modified mpv-1 dll after a Plex HTPC install. @gbooker02 any chance you could add support to the installer for a “post install script” option to execute after the installer is done (maybe tricky cross platform I know…)

So I hadn’t compared until you suggested. For reference I’m using an RTX 2060 with the Make My GPU Hurt preset. I compared D3D11 vs Vulkan with vo=gpu-next. I had previously looked at D3D11 with vo=gpu and the numbers I see there are similar to D3D11 with vo=gpu-next.
Anyway, I’m seeing Vulkan using more memory than D3D11 for the same settings. It seems like it uses twice the memory of D3D11. The amount of memory used is proportional to the pixel count in the source file so I would suppose this is decode buffers.

However, I’m seeing Vulkan use SUBSTANTIALLY lower 3D usage on the GPU. In fact, it’s so low I have to question whether task manager is actually capable of measuring it. If I play a 4K HDR 60fps file on an SDR display at 1080p (which means it must tone map and downscale using SSimDownscale.glsl, so worse than playing on a 4k HDR display), D3D11 consumes around 50-60% of the 3D on the GPU but Vulkan is only a few percent.
I’m not sure I believe this number though. I can see from my other tests that the external shaders are in fact being used (blatantly obvious on upscaling a 120x160 pixel file), so unless the Vulkan API is just vastly more efficient at this than D3D11, I suspect the measurements.

Related to my original statement, I had also done some tests with the standalone MPV. I was using the stats.lua script (built-in to standalone MPV) to overlay the frame timings (I don’t recall what kind of file I was testing with though). On the same hardware, playing content that was 23.976fps with the shaders used in the highest preset, the D3D11 gpu-context was averaging ~8ms per frame with a peak of ~40ms per frame. With Vulkan I was seeing the same average frame time but the peak stayed around 8ms. I long suspected that this could lead to some frame drops in D3D11 and I swear I would see the occasional drop in some files.

I’m not sure that’s really practical.

I have also checked my measurements again and can confirm your measurements - roughly double the VRAM usage when using Vulkan + pixel-dependent amount increases.

On my system, they are equal in this regard. I measured it with GPU-Z and Task manager and both tools showed the same percentage. I have not used any shaders, maybe they have a huge impact on that one.

Same on my system, although d3d11 seemed more “stable”, whereas Vulkan was mostly a bit lower but had higher fluctuation. (might explain the higher gpu clock in some scenes, where the frame timings were higher)

A simple “copy that thing over there” powershell script, which the user executes after an update, could probably do the job.


#updated the first post

As I stated I do suspect Task Manager’s measurements here. Vulkan is capable of handling shaders more efficiently than D3D11 but I doubt it’s that big a difference. Also, the scaling and other operations do translate into shaders so you are likely using some shaders in both cases.

I was seeing the opposite. With D3D11 the frame times vary much more than with Vulkan. The average frame times were similar across the two but the maximum frame times were 5x longer in D3D11 than in Vulkan.

Just wrote a dirt-simple PowerShell script.
I used a folder under c:\temp\mpv as a place where I store my mpv builds that I use for Plex.

Put this in a move-my-mpv-dll-somewhere.ps1 file and open Powershell ISE as administrator. (we need admin rights as we want to change stuff under Program Files)
Now try if it works by running every line on its own (small sheet + play button symbol “run selection (F8)”) with caution and see if all files changed accordingly.

#simple script, that
#changes PLEX mpv-1.dll
#with custom one
$date = GET-DATE -UFORMAT "%d-%m-%Y"
echo "hello! lets start!"
#first change orig mpv-1.dll to mpv-1.dll.date and remove old ones
Remove-Item -Path "c:\Program Files\Plex\Plex HTPC\mpv-1.dll.*"
Remove-Item -Path "c:\Program Files\Plex\Plex HTPC\mpv-2.dll"
Rename-Item -Path "c:\Program Files\Plex\Plex HTPC\mpv-1.dll" -NewName "mpv-1.dll.$date"
#now move new mpv.dll to Plex install folder
Copy-Item -Path "c:\temp\mpv\mpv-2.dll" -Destination "C:\Program Files\Plex\Plex HTPC"
#rename mpv-2.dll to mpv-1.dll
Rename-Item -Path "C:\Program Files\Plex\Plex HTPC\mpv-2.dll" -NewName "mpv-1.dll"
Timeout /T 2
echo "finished!"

When everything works the .ps1 can be run without powershell ise, next time.


Oh well, then I missed something. 5x times more is pretty hefty, definitely not seeing this on my setup but I will keep an eye on it. I guess every setup is different! :wink: