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.
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 185 +++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 1cb60d168e1..44d42c18946 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,187 @@ 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; + 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_ProcessMessage(xvp, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) manager); + ok(SUCCEEDED(hr), "failed to set D3D manager, hr %#lx.\n", hr); + + hr = IMFTransform_GetAttributes(xvp, &xvp_attrs); + ok(SUCCEEDED(hr), "failed to get video processor attributes\n"); + + 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_PROVIDES_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 +13892,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 44d42c18946..ce3bec58578 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6917,7 +6917,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);