This MR adds two tests for additional test coverage in preparation of an update to MF Media Engine to use a dedicated Video Sink instead of the Sample Grabber
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfmediaengine/tests/mfmediaengine.c | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 2fefc89c220..471556cce09 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -2666,6 +2666,49 @@ done: IMFByteStream_Release(stream); }
+static void test_audio_only(void) +{ + struct media_engine_notify *notify; + IMFMediaEngine *media_engine; + IMFAttributes *attributes; + LONGLONG timestamp; + ULONG refcount; + HRESULT hr; + + notify = create_callback(); + + hr = MFCreateAttributes(&attributes, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)¬ify->IMFMediaEngineNotify_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_AUDIOONLY, attributes, &media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_OnVideoStreamTick(media_engine, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_OnVideoStreamTick(media_engine, ×tamp); + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngine_Shutdown(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFAttributes_Release(attributes); + + /* IMFMediaEngineEx_Shutdown can release media_engine in parallel. A small sleep allows this test to pass more + * often than not. But given its a matter of timing, this test is marked flaky */ + Sleep(10); + refcount = IMFMediaEngine_Release(media_engine); + flaky + ok(!refcount, "Unexpected refcount %lu.\n", refcount); + + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); +} + START_TEST(mfmediaengine) { HRESULT hr; @@ -2702,6 +2745,7 @@ START_TEST(mfmediaengine) test_GetSeekable(); test_media_extension(); test_SetCurrentTime(); + test_audio_only();
IMFMediaEngineClassFactory_Release(factory);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfmediaengine/tests/mfmediaengine.c | 121 +++++++++++++++++++++++ 1 file changed, 121 insertions(+)
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 471556cce09..8c3e4e3405e 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1376,6 +1376,126 @@ done: IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); }
+static void test_OnVideoStreamTick(void) +{ + struct test_transfer_notify *notify; + ID3D11Texture2D *texture = NULL; + IMFMediaEngineEx *media_engine = NULL; + IMFDXGIDeviceManager *manager; + D3D11_TEXTURE2D_DESC desc; + IMFByteStream *stream; + ID3D11Device *device; + RECT dst_rect; + UINT token; + HRESULT hr; + DWORD res; + BSTR url; + LONGLONG pts; + + stream = load_resource(L"i420-64x64.avi", L"video/avi"); + + notify = create_transfer_notify(); + + if (!(device = create_d3d11_device())) + { + skip("Failed to create a D3D11 device, skipping tests.\n"); + goto done; + } + + hr = pMFCreateDXGIDeviceManager(&token, &manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFDXGIDeviceManager_ResetDevice(manager, (IUnknown *)device, token); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + create_media_engine(¬ify->IMFMediaEngineNotify_iface, manager, DXGI_FORMAT_B8G8R8X8_UNORM, + &IID_IMFMediaEngineEx, (void **)&media_engine); + + IMFDXGIDeviceManager_Release(manager); + + if (!(notify->media_engine = media_engine)) + goto done; + + memset(&desc, 0, sizeof(desc)); + desc.Width = 64; + desc.Height = 64; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + desc.SampleDesc.Count = 1; + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + url = SysAllocString(L"i420-64x64.avi"); + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, stream, url); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(url); + + res = WaitForSingleObject(notify->frame_ready_event, 5000); + ok(!res, "Unexpected res %#lx.\n", res); + + if (FAILED(notify->error)) + { + win_skip("Media engine reported error %#lx, skipping tests.\n", notify->error); + goto done; + } + + res = 0; + hr = IMFMediaEngineEx_GetNumberOfStreams(media_engine, &res); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(res == 2, "Unexpected stream count %lu.\n", res); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SetRect(&dst_rect, 0, 0, desc.Width, desc.Height); + hr = IMFMediaEngineEx_TransferVideoFrame(notify->media_engine, (IUnknown *)texture, NULL, &dst_rect, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + todo_wine + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + + /* sleep until second frame is ready */ + while (IMFMediaEngineEx_GetCurrentTime(notify->media_engine) < 1./30.) + Sleep(1); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_TransferVideoFrame(notify->media_engine, (IUnknown *)texture, NULL, &dst_rect, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + todo_wine + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + + /* sleep until third frame is ready */ + while (IMFMediaEngineEx_GetCurrentTime(notify->media_engine) < 2./30.) + Sleep(1); + + hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + +done: + if (media_engine) + { + IMFMediaEngineEx_Shutdown(media_engine); + IMFMediaEngineEx_Release(media_engine); + } + + if (texture) + ID3D11Texture2D_Release(texture); + if (device) + ID3D11Device_Release(device); + + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + + IMFByteStream_Release(stream); +} + struct test_transform { IMFTransform IMFTransform_iface; @@ -2740,6 +2860,7 @@ START_TEST(mfmediaengine) test_SetSourceFromByteStream(); test_audio_configuration(); test_TransferVideoFrame(); + test_OnVideoStreamTick(); test_effect(); test_GetDuration(); test_GetSeekable();
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=145117
Your paranoid android.
=== w8 (32 bit report) ===
mfmediaengine: mfmediaengine.c:1451: Test failed: Unexpected hr 0x1.
=== w8adm (32 bit report) ===
mfmediaengine: mfmediaengine.c:1451: Test failed: Unexpected hr 0x1.
=== w864 (32 bit report) ===
mfmediaengine: mfmediaengine.c:1451: Test failed: Unexpected hr 0x1.
=== w864 (64 bit report) ===
mfmediaengine: mfmediaengine.c:1451: Test failed: Unexpected hr 0x1.
=== w1064_adm (64 bit report) ===
mfmediaengine: mfmediaengine.c:1480: Test failed: Unexpected hr 0x1.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/tests/mfmediaengine.c:
- hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaEngineEx_TransferVideoFrame(notify->media_engine, (IUnknown *)texture, NULL, &dst_rect, NULL);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts);
- todo_wine
- ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr);
- /* sleep until third frame is ready */
- while (IMFMediaEngineEx_GetCurrentTime(notify->media_engine) < 2./30.)
Sleep(1);
- hr = IMFMediaEngineEx_OnVideoStreamTick(notify->media_engine, &pts);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
This doesn't look like it could be made reliable, even if it works in practice. It's not clear if e.g. next frame might be ready already after TransferVideoFrame() and before OnVideoStreamTick().
The most important part as @besentv found was that no requests are made before OnVideoStreamTick() (or was it Transfer*?) is called for the first time. So when video stream is present, playback does not start automatically, then on first api call the clock-driven audio stream start to play, while video stream requests are still controlled by user calls.
On Tue Apr 23 09:08:57 2024 +0000, Nikolay Sivov wrote:
This doesn't look like it could be made reliable, even if it works in practice. It's not clear if e.g. next frame might be ready already after TransferVideoFrame() and before OnVideoStreamTick(). The most important part as @besentv found was that no requests are made before OnVideoStreamTick() (or was it Transfer*?) is called for the first time. So when video stream is present, playback does not start automatically, then on first api call the clock-driven audio stream start to play, while video stream requests are still controlled by user calls.
Yes, one can confirm this using dummy MFTs and looking at the processing quota. After the first call to OnVideoStreamTick, everything becomes really unreliable.
On Tue Apr 23 11:20:35 2024 +0000, Bernhard Kölbl wrote:
Yes, one can confirm this using dummy MFTs and looking at the processing quota. After the first call to OnVideoStreamTick, everything becomes really unreliable.
@nsivov That's a good point. Note that `IMFMediaEngineEx_OnVideoStreamTick` only returns `S_OK` though if `IMFMediaEngineEx_GetCurrentTime` is greater than the frames PTS. So the frame might be delivered to Media Engine, but it will still return `S_FALSE` until it's time to be presented.
So one way to guarantee this test will always pass is to check if `hr == S_FALSE || IMFMediaEngineEx_GetCurrentTime > PTS`; but then we might not be testing the right thing (i.e. I want to test hr == S_FALSE, but I might just be testing that our test is super slow). One way to mitigate this could be to use a video with a low FPS. As an extreme example, if I use 1 FPS, then it would need to take a whole second to complete `IMFMediaEngineEx_TransferVideoFrame` for hr != S_FALSE.