By actual measurement, luma levels of 235 are forced to 255 and luma levels of 16 are forced to 0. Input file is flagged as "full range". Unfortunately there is no way around this behavior.
Please allow users to configure VLC to defeat this behavior. I had to revert to version 2.2.8 which works just fine, leaving 235 at 235 and 16 at 16. ffplay leaves 235 at 235 on the same file in both the player and waveform modes.
Full range: video level 16 is forced to 0 (should be 16); video level 235 is forced to 255 (should be 235).
Limited range: video level 16 is forced to 0 (should be 16); video level 235 forced to 255 (should be 235).
VLC 3.0.12 is not displaying video levels accurately. On-screen video levels checked with eyedropper/color picker program.
Video levels of test files have been checked using ffmpeg waveform. Test-signal files are correctly flagged as full/limited and checked with MediaInfo.
Version 2.2.8 displays video levels correctly on both full- and limited-range videos.
As I commented on this issue 21986, I think the problem is that matrix works in limited range applies to full range content, at least in my case. I don't have the latest 3rd party libraries to compile master branch on Windows, so I choose to verify my thought at this branch 4.0.0-dev(SHA1:1f2e534f). Full range content is transformed to limited range before color space matrix starts to work in HLSL shader
int range_adjust = sys->display.colorspace->b_full_range; if (!IsRGBShader(format)) range_adjust--; /* the YUV->RGB conversion already output full range */ if (src_full_range) range_adjust--;
And full range content looks right this time. I attach a picture, you can find these differences. Hope this could help :)
D3D11 and D3D9 still don't work correctly in 4.0 and the result looks exactly like nightly build version. OpenGL crashed in my build version, I think I didn't compile it correctly.
D3D9 version
Allow hardware yuv->rgb conversions in setting dialog. Its' output:
direct3d9 debug: Direct3d9 Device: AMD Radeon RX 580 2048SP 4098 28639 239direct3d9 warning: trying surface pixel format: NV12direct3d9 debug: selected surface pixel format is NV12direct3d9 debug: Direct3D9 scene created successfullydirect3d9 warning: Direct3D shaders initialization failed !direct3d9 debug: Direct3D9 device adapter successfully initializedmain debug: using vout display module "direct3d9"main debug: A filter to adapt decoder DX11 to display NV12 is neededmain debug: looking for video converter module matching "any": 24 candidatesmain debug: using video converter module "d3d11_filters"main debug: Filter 'd3d11_filters' (0000023732721b50) appended to chainmain debug: original format sz 592x400, of (0,0), vsz 586x386, 4cc DX11, sar 1:1, msk r0x0 g0x0 b0x0
I don't know how direct3d9 convert nv12 to display format, what type of color matrix and range type are currently using!
Disallow hardware yuv->rgb conversions
direct3d9 debug: Direct3D9 scene created successfullydirect3d9 warning: Direct3D shaders initialization failed !direct3d9 debug: Direct3D9 device adapter successfully initializedmain debug: using vout display module "direct3d9"main debug: A filter to adapt decoder DX11 to display RV32 is neededmain debug: looking for video converter module matching "any": 24 candidateschain debug: Trying to use chroma I420 as middle manmain debug: looking for video converter module matching "any": 24 candidatesmain debug: using video converter module "d3d11_filters"main debug: Filter 'd3d11_filters' (00000292c056a860) appended to chainmain debug: looking for video converter module matching "any": 24 candidatesswscale debug: 586x386 (592x400) chroma: I420 -> 586x386 (592x400) chroma: RV32 with scaling using Bicubic (good quality)main debug: using video converter module "swscale"main debug: Filter 'Swscale' (00000292c0568970) appended to chainmain debug: using video converter module "chain"main debug: Filter 'chain' (00000292c0569730) appended to chainmain debug: original format sz 592x400, of (0,0), vsz 586x386, 4cc DX11, sar 1:1, msk r0x0 g0x0 b0x0
It is complicated! ID3D11VideoContext1::VideoProcessorSetOutputColorSpace1, src_range and dst_range options of swscale module could help?
OpenGL version
I found that vlc is using matrix like matrix_bt601_tv2full and ignore range type information.
VLC version 3.0.17.4
Windows 10 64bit (up to date, DirectX End-User Runtimes (June 2010) is installed)
Nvidia GeForce GTX 750 Ti (up to date driver with default settings, all video settings are "Use the video player setting")
I have 3 possible workarounds for this issue in VLC "Tools > Preferences" menu :
Set "Video > Output" to "Direct3D 9 video output" (works with any value in "Input / Codec > Hardware accelerated decoding").
Set "Video > Output" to "DirectX (DirectDraw) video output" AND set "Input / Codec > Hardware accelerated decoding" to Disable or "DirectX Video Acceleration (DXVA 2.0)".
Set "Video > Output" to "OpenGL video output for Windows" or "OpenGL video output " AND set "Input / Codec > Hardware accelerated decoding" to "DirectX Video Acceleration (DXVA 2.0)".
All other combinations of these options result in incorrect contrast for a full range video (when the video output works of course).
BTW Full Range YUV sources are incorrect. The BT 601/709/2020 define conversion matrices for limited range to full RGB. So if you use values out of the limited range (0-15, 235-255) in YUV they will be blown out in RGB. The D3D11 shader is trying to reduce the range (before clipping) but that's just a hack and not a mathematically correct operation.
@robUx4 Correct me if I misunderstand what you mean. As far as I know, Full Range YUV sources are valid and correct in h.264 bitstream, video_full_range_flag syntax element is used to signal it. And I don't think the decoder can help us in this situation, it just decodes what bitstream says and doesn't care if the decoded value is in full/limited range.
I am using vlc 4.0(SHA-1: 1f2e534f). Notice that we already have a variable named src_full_range in CompilePixelShader in direct3d11.c, since we are using matrices for limited range to full RGB anyway, then an extra step can be added to convert full range to limited range before colorspace conversion. Or using full range yuv to full range rgb matrix, there is a great article about how to calculate these matrices.
Yes, I totally agree with you! I think VLC already can perfectly pass these information to d3d render. So I don't know why we are talking about decoders here? Or are you saying that decoders provide false info to later filters sometimes? That should be anther problem. Let's put it aside and assume that decoder does a good job, is it okay to use limited range to full RGB matrix even yuv is in full range? My knowledge is based on vlc 4.0 and d3d11, things could change if we are talking about different codes.
The official conversions I know are the ones defined in BT-601, BT-709 and BT-2020. They all define conversions from YUV limited range to RGB full range. They do not define how to convert full YUV to full RGB, nor limited YUV to limited RGB.
It seems the avisynth conversions fall on the same values for limited YUV to full RGB (yuv [16,235] <-> rgb [0,255]). I'll see if their math can be used for full YUV to full RGB.