From: Rémi Bernon rbernon@codeweavers.com
Unreal Engine has a race condition in its media player where it starts the resolution of a media source, waiting for its completion in an async task, and expects to set the media player source from its result.
At the same time, the media player uses timer to tick every 200ms or so, and if the first tick happens before the media source resolution end, the media player ends up in a corrupted state and playback may fail or get stuck.
On Windows, the media source resolution takes very little time, and usually less than 1ms. On Wine, because of the current design of the media soruce, it can often take more than 200ms depending on the setup, and very rarely less than 50ms. --- dlls/mf/tests/mf.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 18488649523..5afaaf86a3e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7053,6 +7053,89 @@ static void test_mpeg4_media_sink(void) IMFMediaType_Release(audio_type); }
+static void load_resource_stream(const WCHAR *name, IMFByteStream **stream) +{ + HRSRC resource = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); + void *resource_data; + DWORD resource_len; + HRESULT hr; + + ok(resource != 0, "FindResourceW %s failed, error %lu\n", debugstr_w(name), GetLastError()); + resource_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + resource_len = SizeofResource(GetModuleHandleW(NULL), resource); + + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_Write(*stream, resource_data, resource_len, &resource_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_SetCurrentPosition(*stream, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + +static void subtest_media_source_streams(const WCHAR *resource) +{ + IMFMediaSource *media_source; + IMFSourceResolver *resolver; + MF_OBJECT_TYPE object_type; + DWORD i, min_time = -1; + IMFByteStream *stream; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = MFCreateSourceResolver(&resolver); + ok(hr == S_OK, "got hr %#lx\n", hr); + + + load_resource_stream(resource, &stream); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, resource, MF_RESOLUTION_MEDIASOURCE, + NULL, &object_type, (IUnknown **)&media_source); + ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "got hr %#lx\n", hr); + IMFByteStream_Release(stream); + if (hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE) + { + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto skip_tests; + } + + /* the testbot Windows VMs might be very slow, take the minimum time over a couple of resolutions */ + for (i = 0; i < 10 && min_time > 0; ++i) + { + DWORD time = 0; + + hr = IMFMediaSource_Shutdown(media_source); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSource_Release(media_source); + + load_resource_stream(resource, &stream); + time -= GetTickCount(); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, resource, MF_RESOLUTION_MEDIASOURCE, + NULL, &object_type, (IUnknown **)&media_source); + time += GetTickCount(); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(object_type == MF_OBJECT_MEDIASOURCE, "got type %#x\n", object_type); + IMFByteStream_Release(stream); + + min_time = min(time, min_time); + } + todo_wine ok(min_time <= 2, "source resolution took %lums\n", min_time); + + hr = IMFMediaSource_Shutdown(media_source); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFMediaSource_Release(media_source); + +skip_tests: + IMFSourceResolver_Release(resolver); + + hr = MFShutdown(); + ok(hr == S_OK, "got hr %#lx\n", hr); +} + +static void test_media_source_streams(void) +{ + subtest_media_source_streams(L"test.mp4"); +} + START_TEST(mf) { init_functions(); @@ -7087,4 +7170,5 @@ START_TEST(mf) test_MFGetTopoNodeCurrentType(); test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); + test_media_source_streams(); }