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.
-- v6: 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 | 199 +++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 1cb60d168e1..92fd1f81b8f 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,202 @@ static void test_dxgi_device_manager(void) IMFDXGIDeviceManager_Release(manager2); }
+static IMFSample *create_dxgi_sample(ID3D11Device *device, UINT32 color, UINT bind_flags) +{ + 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 = bind_flags, + .CPUAccessFlags = 0, + .MiscFlags = 0, + }; + D3D11_SUBRESOURCE_DATA data = { + .pSysMem = &color, + .SysMemPitch = 4, + .SysMemSlicePitch = 4, + }; + IMFSample *sample; + ID3D11Texture2D *texture; + IMFMediaBuffer *buffer; + HRESULT hr; + + hr = ID3D11Device_CreateTexture2D(device, &desc, &data, &texture); + ok(hr == S_OK, "Failed to create d3d11 texture, hr %#lx.\n", hr); + + hr = pMFCreateDXGISurfaceBuffer(&IID_ID3D11Texture2D, (IUnknown *) texture, 0, FALSE, &buffer); + ok(hr == S_OK, "Failed to create dxgi surface buffer, hr %#lx.\n", hr); + + hr = IMFMediaBuffer_SetCurrentLength(buffer, 4); + ok(hr == S_OK, "Failed to set media buffer length, hr %#lx.\n", hr); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Failed create sample, hr %#lx.\n", hr); + + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Failed add buffer to sample, hr %#lx.\n", hr); + + hr = IMFSample_SetSampleTime(sample, 0); + ok(hr == S_OK, "Failed to set sample time, hr %#lx.\n", hr); + + hr = IMFSample_SetSampleDuration(sample, 10000000/60); + ok(hr == S_OK, "Failed to set sample duration, hr %#lx.\n", hr); + + IMFMediaBuffer_Release(buffer); + ID3D11Texture2D_Release(texture); + + return sample; +} + +static IMFSample *test_xvp_process_sample(IMFTransform *xvp, IMFSample *in_sample, IMFSample *out_sample) +{ + IMFMediaBuffer *out_buffer; + HRESULT hr; + MFT_OUTPUT_DATA_BUFFER out = { + .dwStreamID = 0, + .pSample = out_sample, + .dwStatus = 0, + .pEvents = NULL, + }; + MFT_OUTPUT_STREAM_INFO info; + DWORD status; + BYTE *out_data; + DWORD out_length; + + hr = IMFTransform_GetOutputStreamInfo(xvp, 0, &info); + ok(hr == S_OK, "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"); + + hr = IMFTransform_ProcessInput(xvp, 0, in_sample, 0); + ok(hr == S_OK, "Failed to process input, hr %#lx.\n", hr); + + hr = IMFTransform_ProcessOutput(xvp, 0, 1, &out, &status); + ok(hr == S_OK, "Failed to process output, hr %#lx.\n", hr); + + hr = IMFSample_ConvertToContiguousBuffer(out.pSample, &out_buffer); + ok(hr == S_OK, "Failed to convert sample to contiguous buffer, hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(out_buffer, &out_data, &out_length, NULL); + ok(hr == S_OK, "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(hr == S_OK, "Failed to unlock buffer, hr %#lx.\n", hr); + + IMFMediaBuffer_Release(out_buffer); + + if (out.pEvents) IMFCollection_Release(out.pEvents); + + return out.pSample; +} + +static void test_xvp_playback_mode(void) +{ + HRESULT hr; + UINT reset_token; + IMFDXGIDeviceManager *manager; + ID3D11Device *device; + IMFTransform *xvp; + IMFAttributes *xvp_attrs; + IMFSample *in_sample, *out_sample, *got_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(hr == S_OK, "Failed to create device manager, hr %#lx.\n", hr); + + hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *) device, reset_token); + ok(hr == S_OK, "Failed to reset device manager, hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_VideoProcessorMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **) &xvp); + ok(hr == S_OK, "Failed to create video processor MFT, hr %#lx.\n", hr); + + hr = IMFTransform_GetAttributes(xvp, &xvp_attrs); + ok(hr == S_OK, "Failed to get video processor attributes\n"); + + hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) manager); + if (hr == E_NOINTERFACE) + { + win_skip("D3D11 manager does not support video processing.\n"); + return; + } + ok(hr == S_OK, "Failed to set D3D manager, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "Failed create media type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Failed to set major type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_ARGB32); + ok(hr == S_OK, "Failed to set subtype, hr %#lx.\n", hr); + + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, ((UINT64) 1 << 32) | 1); + ok(hr == S_OK, "Failed to frame size, hr %#lx.\n", hr); + + hr = IMFTransform_SetInputType(xvp, 0, type, 0); + ok(hr == S_OK, "Failed to set input type, hr %#lx.\n", hr); + + hr = IMFTransform_SetOutputType(xvp, 0, type, 0); + ok(hr == S_OK, "Failed to set output type, hr %#lx.\n", hr); + + in_sample = create_dxgi_sample(device, 0xdeadbeef, 0); + + IMFSample_Release(test_xvp_process_sample(xvp, in_sample, NULL)); + + hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_NOTIFY_END_STREAMING, 0); + ok(hr == S_OK, "Failed to end streaming, hr %#lx.\n", hr); + + hr = IMFAttributes_SetUINT32(xvp_attrs, &MF_XVP_PLAYBACK_MODE, TRUE); + ok(hr == S_OK, "Failed to set MF_XVP_PLAYBACK_MODE, hr %#lx.\n", hr); + + out_sample = create_dxgi_sample(device, 0, D3D11_BIND_RENDER_TARGET); + got_out_sample = test_xvp_process_sample(xvp, in_sample, out_sample); + + todo_wine + ok(got_out_sample == out_sample, "Video processor should use caller-provided sample.\n"); + + if (got_out_sample != out_sample) IMFSample_Release(got_out_sample); + IMFSample_Release(in_sample); + IMFSample_Release(out_sample); + 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 +13907,7 @@ START_TEST(mfplat) test_MFInitMediaTypeFromAMMediaType(); test_MFCreatePathFromURL(); test_2dbuffer_copy(); + test_xvp_playback_mode();
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 92fd1f81b8f..7e018aec395 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6947,7 +6947,6 @@ static void test_xvp_playback_mode(void) out_sample = create_dxgi_sample(device, 0, D3D11_BIND_RENDER_TARGET); got_out_sample = test_xvp_process_sample(xvp, in_sample, out_sample);
- todo_wine ok(got_out_sample == out_sample, "Video processor should use caller-provided sample.\n");
if (got_out_sample != out_sample) IMFSample_Release(got_out_sample); 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:
- hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *) device, reset_token);
- ok(hr == S_OK, "Failed to reset device manager, hr %#lx.\n", hr);
- hr = CoCreateInstance(&CLSID_VideoProcessorMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **) &xvp);
- ok(hr == S_OK, "Failed to create video processor MFT, hr %#lx.\n", hr);
- hr = IMFTransform_GetAttributes(xvp, &xvp_attrs);
- ok(hr == S_OK, "Failed to get video processor attributes\n");
- hr = IMFTransform_ProcessMessage(xvp, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) manager);
- if (hr == E_NOINTERFACE)
- {
win_skip("D3D11 manager does not support video processing.\n");
return;
- }
Let's maybe jump closer to the end of the function to cleanup d3d parts and other things.
Nikolay Sivov (@nsivov) commented about dlls/winegstreamer/video_processor.c:
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)
I would rather use non-negated condition, like "if (provides_samples)", but it's not important.