This fixes a misbehaving proton game that sets MF_XVP_PLAYBACK_MODE.
MF_XVP_PLAYBACK_MODE isn't mentioned in microsoft docs, but it's explained and used in the MIT-licensed [Windows-classic-samples](https://github.com/microsoft/Windows-classic-samples/blob/2b94df5730177ec27e...)
Not sure if mfplat is the right place to add the tests for this since the code is in winegstreamer, but it's very similar to the existing mfplat tests and the test does not interface with winegstreamer directly.
It should be noted that on windows, ProcessOutput errors out if we provide a pSample with MF_XVP_PLAYBACK_MODE unset, wine simply ignores it.
-- v3: winegstreamer: Allow caller to allocate samples in MF_XVP_PLAYBACK_MODE. mfplat/tests: Add test for MF_XVP_PLAYBACK_MODE.
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 195 +++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 1cb60d168e1..fca144c829a 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -122,6 +122,8 @@ DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_wvp2,MAKEFOURCC('w','v','p','2')); DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_X264,MAKEFOURCC('X','2','6','4')); DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_x264,MAKEFOURCC('x','2','6','4'));
+DEFINE_GUID(MF_XVP_PLAYBACK_MODE, 0x3c5d293f, 0xad67, 0x4e29, 0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9); + static BOOL is_win8_plus;
#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) @@ -6762,6 +6764,197 @@ static void test_dxgi_device_manager(void) IMFDXGIDeviceManager_Release(manager2); }
+static void test_dxgi_xvp_mft(BOOL playback_mode) +{ + HRESULT hr; + UINT reset_token; + MFT_OUTPUT_STREAM_INFO info; + DWORD status; + MFT_OUTPUT_DATA_BUFFER out = { 0 }; + BYTE *out_data; + DWORD out_length; + BOOL d3d_aware; + IMFMediaBuffer *out_buffer; + IMFDXGIDeviceManager *manager; + ID3D11Device *device; + IMFTransform *xvp; + IMFAttributes *xvp_attrs; + IMFSample *in_sample, *out_sample; + IMFMediaType *type; + + if (!pMFCreateDXGIDeviceManager) + { + win_skip("MFCreateDXGIDeviceManager not found.\n"); + return; + } + + if (!pMFCreateDXGISurfaceBuffer) + { + win_skip("MFCreateDXGISurfaceBuffer() is not available.\n"); + return; + } + + hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, + NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL); + if (FAILED(hr)) + { + skip("Failed to create D3D11 device object.\n"); + return; + } + + hr = pMFCreateDXGIDeviceManager(&reset_token, &manager); + ok(SUCCEEDED(hr), "failed to create device manager, hr %#lx.\n", hr); + + hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *) device, reset_token); + ok(SUCCEEDED(hr), "failed to reset device manager, hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_VideoProcessorMFT, NULL, 1, &IID_IMFTransform, (void **) &xvp); + ok(SUCCEEDED(hr), "failed to create video processor MFT, hr %#lx.\n", hr); + + hr = IMFTransform_GetAttributes(xvp, &xvp_attrs); + ok(SUCCEEDED(hr), "failed to get video processor attributes\n"); + + if (FAILED(IMFAttributes_GetUINT32(xvp_attrs, &MF_SA_D3D11_AWARE, (UINT32 *) &d3d_aware))) + d3d_aware = FALSE; + + if (!d3d_aware) + { + skip("video processor MFT is not D3D11 aware\n"); + return; + } + + hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) manager); + ok(SUCCEEDED(hr), "failed to set D3D manager, hr %#lx.\n", hr); + + if (playback_mode) + { + hr = IMFAttributes_SetUINT32(xvp_attrs, &MF_XVP_PLAYBACK_MODE, 1); + ok(SUCCEEDED(hr), "failed to set MF_XVP_PLAYBACK_MODE, hr %#lx.\n", hr); + } + + hr = MFCreateMediaType(&type); + ok(SUCCEEDED(hr), "failed create media type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(SUCCEEDED(hr), "failed to set major type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_ARGB32); + ok(SUCCEEDED(hr), "failed to set subtype, hr %#lx.\n", hr); + + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, ((UINT64) 1 << 32) | 1); + ok(SUCCEEDED(hr), "failed to frame size, hr %#lx.\n", hr); + + hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_NOTIFY_END_STREAMING, 0); + ok(SUCCEEDED(hr), "failed to end streaming, hr %#lx.\n", hr); + + hr = IMFTransform_SetInputType(xvp, 0, type, 0); + ok(SUCCEEDED(hr), "failed to set input type, hr %#lx.\n", hr); + + hr = IMFTransform_SetOutputType(xvp, 0, type, 0); + ok(SUCCEEDED(hr), "failed to set output type, hr %#lx.\n", hr); + + hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); + ok(SUCCEEDED(hr), "failed to begin streaming, hr %#lx.\n", hr); + + hr = IMFTransform_GetOutputStreamInfo(xvp, 0, &info); + ok(SUCCEEDED(hr), "failed to get output stream info, hr %#lx.\n", hr); + + ok(!!(info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES), "MFT_OUTPUT_STREAM_PROVIDES_SAMPLES expected\n"); + ok(!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES), "MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES unexpected\n"); + ok(info.cbSize == 4, "output size should be 4\n"); + + for (int i = 0; i < 2; i++) { + D3D11_TEXTURE2D_DESC desc = { + .Width = 1, + .Height = 1, + .MipLevels = 1, + .ArraySize = 1, + .Format = DXGI_FORMAT_B8G8R8A8_UNORM, + .SampleDesc = { + .Count = 1, + .Quality = 0, + }, + .Usage = D3D11_USAGE_DEFAULT, + .BindFlags = i == 0 ? 0 : D3D11_BIND_RENDER_TARGET, + .CPUAccessFlags = 0, + .MiscFlags = 0, + }; + UINT32 color = i == 0 ? 0xdeadbeef : 0; + D3D11_SUBRESOURCE_DATA data = { + .pSysMem = &color, + .SysMemPitch = 4, + .SysMemSlicePitch = 4, + }; + IMFSample **sample = i == 0 ? &in_sample : &out_sample; + ID3D11Texture2D *texture; + IMFMediaBuffer *buffer; + + hr = ID3D11Device_CreateTexture2D(device, &desc, &data, &texture); + ok(SUCCEEDED(hr), "failed to create d3d11 texture, hr %#lx.\n", hr); + + hr = pMFCreateDXGISurfaceBuffer(&IID_ID3D11Texture2D, (IUnknown *) texture, 0, FALSE, &buffer); + ok(SUCCEEDED(hr), "failed to create dxgi surface buffer, hr %#lx.\n", hr); + + hr = IMFMediaBuffer_SetCurrentLength(buffer, 4); + ok(SUCCEEDED(hr), "failed to set media buffer length, hr %#lx.\n", hr); + + hr = MFCreateSample(sample); + ok(SUCCEEDED(hr), "failed create sample, hr %#lx.\n", hr); + + hr = IMFSample_AddBuffer(*sample, buffer); + ok(SUCCEEDED(hr), "failed add buffer to sample, hr %#lx.\n", hr); + + hr = IMFSample_SetSampleTime(*sample, 0); + ok(SUCCEEDED(hr), "failed to set sample time %#lx.\n", hr); + + hr = IMFSample_SetSampleDuration(*sample, 10000000/60); + ok(SUCCEEDED(hr), "failed to set sample duration %#lx.\n", hr); + + IMFMediaBuffer_Release(buffer); + ID3D11Texture2D_Release(texture); + } + + hr = IMFTransform_ProcessInput(xvp, 0, in_sample, 0); + ok(SUCCEEDED(hr), "failed to process input, hr %#lx.\n", hr); + + if (playback_mode) + { + out.pSample = out_sample; + } + + hr = IMFTransform_ProcessOutput(xvp, 0, 1, &out, &status); + ok(SUCCEEDED(hr), "failed to process output, hr %#lx.\n", hr); + + if (playback_mode) + { + todo_wine + ok(out.pSample == out_sample, "video processor should use caller-provided sample\n"); + } + + hr = IMFSample_ConvertToContiguousBuffer(out.pSample, &out_buffer); + ok(SUCCEEDED(hr), "failed to convert sample to contiguous buffer, hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(out_buffer, &out_data, &out_length, NULL); + ok(SUCCEEDED(hr), "failed to lock buffer, hr %#lx.\n", hr); + + ok(out_length == 4, "output sample length should be 4\n"); + ok(*(UINT32 *) out_data == 0xdeadbeef, "output sample should be populated with input data\n"); + + hr = IMFMediaBuffer_Unlock(out_buffer); + ok(SUCCEEDED(hr), "failed to unlock buffer, hr %#lx.\n", hr); + + if (out.pEvents) IMFCollection_Release(out.pEvents); + if (out.pSample != out_sample) IMFSample_Release(out_sample); + IMFSample_Release(out.pSample); + IMFSample_Release(in_sample); + IMFMediaBuffer_Release(out_buffer); + IMFMediaType_Release(type); + IMFTransform_Release(xvp); + IMFAttributes_Release(xvp_attrs); + IMFDXGIDeviceManager_Release(manager); + ID3D11Device_Release(device); +} + static void test_MFCreateTransformActivate(void) { IMFActivate *activate; @@ -13709,6 +13902,8 @@ START_TEST(mfplat) test_MFInitMediaTypeFromAMMediaType(); test_MFCreatePathFromURL(); test_2dbuffer_copy(); + test_dxgi_xvp_mft(TRUE); + test_dxgi_xvp_mft(FALSE);
CoUninitialize(); }
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 1 - dlls/winegstreamer/video_processor.c | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index fca144c829a..57f90ca5072 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6927,7 +6927,6 @@ static void test_dxgi_xvp_mft(BOOL playback_mode)
if (playback_mode) { - todo_wine ok(out.pSample == out_sample, "video processor should use caller-provided sample\n"); }
diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 3cb2c1a5bc6..347d2ca1d36 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -30,6 +30,8 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
extern GUID MFVideoFormat_ABGR32;
+static const GUID MF_XVP_PLAYBACK_MODE = { 0x3c5d293f, 0xad67, 0x4e29, { 0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9 } }; + static const GUID *const input_types[] = { &MFVideoFormat_IYUV, @@ -696,6 +698,7 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f MFT_OUTPUT_STREAM_INFO info; IMFSample *output_sample; HRESULT hr; + BOOL use_caller_samples;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
@@ -709,7 +712,12 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) return hr;
- if (impl->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + if (FAILED(IMFAttributes_GetUINT32(impl->attributes, &MF_XVP_PLAYBACK_MODE, (UINT32 *) &use_caller_samples))) + use_caller_samples = FALSE; + + use_caller_samples = use_caller_samples || !(impl->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES); + + if (!use_caller_samples) { if (FAILED(hr = video_processor_init_allocator(impl))) return hr; @@ -727,7 +735,7 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f goto done; wg_sample_queue_flush(impl->wg_sample_queue, false);
- if (impl->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + if (!use_caller_samples) { samples->pSample = output_sample; IMFSample_AddRef(output_sample);
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
- if (!pMFCreateDXGISurfaceBuffer)
- {
win_skip("MFCreateDXGISurfaceBuffer() is not available.\n");
return;
- }
- hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
- if (FAILED(hr))
- {
skip("Failed to create D3D11 device object.\n");
return;
- }
- hr = pMFCreateDXGIDeviceManager(&reset_token, &manager);
- ok(SUCCEEDED(hr), "failed to create device manager, hr %#lx.\n", hr);
Please compare to exact value, S_OK in this case.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
- hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
- if (FAILED(hr))
- {
skip("Failed to create D3D11 device object.\n");
return;
- }
- hr = pMFCreateDXGIDeviceManager(&reset_token, &manager);
- ok(SUCCEEDED(hr), "failed to create device manager, hr %#lx.\n", hr);
- hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *) device, reset_token);
- ok(SUCCEEDED(hr), "failed to reset device manager, hr %#lx.\n", hr);
- hr = CoCreateInstance(&CLSID_VideoProcessorMFT, NULL, 1, &IID_IMFTransform, (void **) &xvp);
Why literal "1" ?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
- ok(SUCCEEDED(hr), "failed to reset device manager, hr %#lx.\n", hr);
- hr = CoCreateInstance(&CLSID_VideoProcessorMFT, NULL, 1, &IID_IMFTransform, (void **) &xvp);
- ok(SUCCEEDED(hr), "failed to create video processor MFT, hr %#lx.\n", hr);
- hr = IMFTransform_GetAttributes(xvp, &xvp_attrs);
- ok(SUCCEEDED(hr), "failed to get video processor attributes\n");
- if (FAILED(IMFAttributes_GetUINT32(xvp_attrs, &MF_SA_D3D11_AWARE, (UINT32 *) &d3d_aware)))
d3d_aware = FALSE;
- if (!d3d_aware)
- {
skip("video processor MFT is not D3D11 aware\n");
return;
- }
When is it not d3d-aware?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
- ok(SUCCEEDED(hr), "failed to set input type, hr %#lx.\n", hr);
- hr = IMFTransform_SetOutputType(xvp, 0, type, 0);
- ok(SUCCEEDED(hr), "failed to set output type, hr %#lx.\n", hr);
- hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
- ok(SUCCEEDED(hr), "failed to begin streaming, hr %#lx.\n", hr);
- hr = IMFTransform_GetOutputStreamInfo(xvp, 0, &info);
- ok(SUCCEEDED(hr), "failed to get output stream info, hr %#lx.\n", hr);
- ok(!!(info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES), "MFT_OUTPUT_STREAM_PROVIDES_SAMPLES expected\n");
- ok(!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES), "MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES unexpected\n");
- ok(info.cbSize == 4, "output size should be 4\n");
- for (int i = 0; i < 2; i++) {
I would add a helper instead of such loop, that has to check iteration counter to use differing paths.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
return;
- }
- hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
- if (FAILED(hr))
- {
skip("Failed to create D3D11 device object.\n");
return;
- }
- hr = pMFCreateDXGIDeviceManager(&reset_token, &manager);
- ok(SUCCEEDED(hr), "failed to create device manager, hr %#lx.\n", hr);
- hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *) device, reset_token);
- ok(SUCCEEDED(hr), "failed to reset device manager, hr %#lx.\n", hr);
Traces are normally formatted as a regular sentence in this file, with a capital first letter.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/tests/mfplat.c:
test_MFInitMediaTypeFromAMMediaType(); test_MFCreatePathFromURL(); test_2dbuffer_copy();
- test_dxgi_xvp_mft(TRUE);
- test_dxgi_xvp_mft(FALSE);
I would rather use a single function with necessary helpers to reduce duplication. Function name is also too generic.