MR !607 was trying to fix an issue with Life Is Strange Remastered, but although it fixed some race conditions with presentation end, the issue it was trying to fix is still there.
The game calls IMFMediaSession_Stop while the presentation is ending, expects that command to quickly execute, interrupting the presentation end and emitting a MESessionStopped event instead of the MESessionEnded.
Delaying the Stop command and emitting the MESessionEnded event breaks the game assumptions and it crashes.
-- v4: mf: Discard end of presentation on IMFMediaSession_Stop. mf/tests: Test IMFMediaSession_Stop command near presentation end. mf/tests: Test Start / Pause / Stop IMFMEdiaSession events. mf/tests: Split wait_media_event helper into wait_next_media_event.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/audiodata.wav | Bin 0 -> 41004 bytes dlls/mf/tests/mf.c | 46 +++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 dlls/mf/tests/audiodata.wav
diff --git a/dlls/mf/tests/audiodata.wav b/dlls/mf/tests/audiodata.wav new file mode 100644 index 0000000000000000000000000000000000000000..33e41b2b7a2c5fd34d7763efa9c93fafc960afe1 GIT binary patch literal 41004 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 NIdFgQ|2NLb{{V`?gXRDL
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 399f983983f..11dd143cb55 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1879,38 +1879,46 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
+#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f) +static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + HRESULT hr = S_OK, status; + DWORD ret; + + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); + hr = IMFMediaEvent_GetType(impl->media_event, type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetExtendedType(impl->media_event, guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return status; +} + #define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { - struct test_callback *impl = impl_from_IMFAsyncCallback(callback); MediaEventType type; - HRESULT hr, status; - DWORD ret; + HRESULT status; GUID guid;
do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ret = WaitForSingleObject(impl->event, timeout); - ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); - hr = IMFMediaEvent_GetType(impl->media_event, &type); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(value); + status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value); } while (type != expect_type);
- ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); - - hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid));
- hr = IMFMediaEvent_GetValue(impl->media_event, value); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEvent_GetStatus(impl->media_event, &status); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - return status; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 215 +++++++++++++++++++++++++++++++++++--- dlls/mf/tests/resource.rc | 17 +++ 2 files changed, 217 insertions(+), 15 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 11dd143cb55..9142673f2e0 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1879,17 +1879,20 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
-#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f) +#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f, FALSE) static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value) + DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value, BOOL timed_out) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); HRESULT hr = S_OK, status; DWORD ret;
- hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + if (!timed_out) + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); + if (ret == WAIT_TIMEOUT) + return WAIT_TIMEOUT; ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); hr = IMFMediaEvent_GetType(impl->media_event, type); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1903,9 +1906,9 @@ static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsy return status; }
-#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) +#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e, FALSE) static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value, BOOL timed_out) { MediaEventType type; HRESULT status; @@ -1914,7 +1917,10 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal do { PropVariantClear(value); - status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value); + status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value, timed_out); + if (status == WAIT_TIMEOUT) + return status; + timed_out = FALSE; } while (type != expect_type);
ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); @@ -1997,15 +2003,20 @@ static void test_media_session_events(void) IMFMediaType *input_type, *output_type; IMFTopologyNode *src_node, *sink_node; IMFPresentationDescriptor *pd; + MediaEventType event_type; IMFMediaSession *session; + BOOL selected, timed_out; IMFStreamDescriptor *sd; IMFAsyncResult *result; IMFMediaSource *source; + IMFMediaSink *renderer; + IMFStreamSink *stream; IMFTopology *topology; IMFMediaEvent *event; PROPVARIANT propvar; HRESULT hr; ULONG ref; + GUID guid;
stream_sink.handler = &handler.IMFMediaTypeHandler_iface; stream_sink.media_sink = &media_sink.IMFMediaSink_iface; @@ -2371,20 +2382,14 @@ static void test_media_session_events(void) /* sometimes briefly leaking */ IMFMediaSession_Release(session);
- IMFAsyncCallback_Release(callback); - if (handler.current_type) IMFMediaType_Release(handler.current_type); handler.current_type = NULL;
- hr = IMFTopology_Clear(topology); + hr = IMFTopologyNode_DeleteAllItems(src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_DeleteAllItems(sink_node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(src_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(sink_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref);
ref = IMFMediaSource_Release(source); ok(ref == 0, "Release returned %ld\n", ref); @@ -2396,6 +2401,186 @@ static void test_media_session_events(void) ok(ref == 0, "Release returned %ld\n", ref);
+ /* MFMediaSource playback */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MEEndOfPresentation, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionEnded, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + + /* test successive commands */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + /* FIXME: sometimes (rarely) triggers a Wine race condition */ + hr = wait_next_media_event(session, callback, 2000, &event_type, &guid, &propvar); + timed_out = (hr == WAIT_TIMEOUT); + todo_wine_if(timed_out) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!timed_out) + { + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + ok(event_type == MEEndOfPresentation, "got type %lu\n", event_type); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionEnded, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + } + + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_(__LINE__, session, callback, MESessionPaused, 1000, &propvar, timed_out); + todo_wine_if(timed_out) + ok(hr == MF_E_SESSION_PAUSEWHILESTOPPED, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(src_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(sink_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + /* sometimes briefly leaking */ + IMFAsyncCallback_Release(callback); + hr = MFShutdown(); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index 25768d21983..e9e13565bf8 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -20,9 +20,26 @@
#include "windef.h"
+/* Generated with: + * gst-launch-1.0 audiomixer name=mix ! \ + * audio/x-raw,format=F32LE,rate=22050,channels=2 ! \ + * filesink location=dlls/mf/tests/audiodata.bin \ + * audiotestsrc freq=400 volume=0.2 num-buffers=1 \ + * samplesperbuffer=22050 ! mix. \ + * audiotestsrc freq=600 volume=0.2 num-buffers=1 \ + * samplesperbuffer=22050 timestamp-offset=20000000 ! mix. + */ /* @makedep: audiodata.bin */ audiodata.bin RCDATA audiodata.bin
+/* Generated with: + * gst-launch-1.0 filesrc location=dlls/mf/tests/audiodata.bin num-buffers=10 ! \ + * audio/x-raw,format=F32LE,rate=22050,channels=2,layout=interleaved ! \ + * wavenc ! filesink location=dlls/mf/tests/audiodata.wav + */ +/* @makedep: audiodata.wav */ +audiodata.wav RCDATA audiodata.wav + /* @makedep: audioconvdata.bin */ audioconvdata.bin RCDATA audioconvdata.bin
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 86 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 9142673f2e0..76311029bcf 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2569,16 +2569,98 @@ static void test_media_session_events(void) IMFStreamDescriptor_Release(sd);
+ /* calling IMFMediaSession_Stop near MEEndOfPresentation preempts MESessionEnded */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MEEndOfPresentation, 2000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (event_type == MESessionTopologyStatus) /* sometimes */ + { + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + if (event_type == MESessionCapabilitiesChanged) /* > w7 */ + { + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + todo_wine + ok(event_type == MESessionStopped, "got type %lu\n", event_type); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + hr = IMFTopology_Clear(topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ref = IMFTopologyNode_Release(src_node); ok(ref == 0, "Release returned %ld\n", ref); ref = IMFTopologyNode_Release(sink_node); ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref);
/* sometimes briefly leaking */ + IMFTopology_Release(topology); IMFAsyncCallback_Release(callback);
hr = MFShutdown();
From: Rémi Bernon rbernon@codeweavers.com
MR !607 was trying to fix an issue with Life Is Strange Remastered, but although it fixed some race conditions with presentation end, the issue it was trying to fix is still there.
The game calls IMFMediaSession_Stop while the presentation is ending, expects that command to quickly execute, interrupting the presentation end and emitting a MESessionStopped event instead of the MESessionEnded.
Delaying the Stop command and emitting the MESessionEnded event breaks the game assumptions and it crashes. --- dlls/mf/session.c | 12 +++++++++++- dlls/mf/tests/mf.c | 1 - 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index b2371763150..bee10cad746 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2524,6 +2524,13 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
EnterCriticalSection(&session->cs);
+ if ((session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION) && op->command != SESSION_CMD_STOP) + { + WARN("session %p command is ending, waiting for it to complete.\n", session); + LeaveCriticalSection(&session->cs); + return S_OK; + } + if (session->presentation.flags & SESSION_FLAG_PENDING_COMMAND) { WARN("session %p command is in progress, waiting for it to complete.\n", session); @@ -2550,6 +2557,9 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, session_pause(session); break; case SESSION_CMD_STOP: + if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION) + session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED); + session_clear_end_of_presentation(session); session_stop(session); break; case SESSION_CMD_CLOSE: @@ -3521,7 +3531,7 @@ static void session_raise_end_of_presentation(struct media_session *session) { if (session_nodes_is_mask_set(session, MF_TOPOLOGY_MAX, SOURCE_FLAG_END_OF_PRESENTATION)) { - session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION | SESSION_FLAG_PENDING_COMMAND; + session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION; IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, NULL); } } diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 76311029bcf..65dc407eecc 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2624,7 +2624,6 @@ static void test_media_session_events(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); - todo_wine ok(event_type == MESessionStopped, "got type %lu\n", event_type); ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
v3: Fix the test failures.
Could you try if this patch fixes the crash as well? Calling stop again while in the middle of stopping looks more fragile to me. Existing logic should already cover calls that you added.
[patch.diff](/uploads/e1698d75ddf9ee6ec628fdac021820cd/patch.diff)
On Thu Dec 8 16:07:55 2022 +0000, Nikolay Sivov wrote:
Could you try if this patch fixes the crash as well? Calling stop again while in the middle of stopping looks more fragile to me. Existing logic should already cover calls that you added. [patch.diff](/uploads/e1698d75ddf9ee6ec628fdac021820cd/patch.diff)
No, it crashes with that patch, I think it is very sensitive to preemption.
In general, like I described, Start/Stop commands don't seem to be waiting for presentation end to complete and rather interrupt it in the middle while being able to restart the presentation end the next call to Start.
I haven't included tests for the Pause command because the current code doesn't seem to support restarting the end of presentation properly, and I think it would need more changes to, but it's not so different from the Stop test.
On Thu Dec 8 16:07:55 2022 +0000, Rémi Bernon wrote:
No, it crashes with that patch, I think it is very sensitive to preemption. In general, like I described, Start/Stop commands don't seem to be waiting for presentation end to complete and rather interrupt it in the middle while being able to restart the presentation end the next call to Start. I haven't included tests for the Pause command because the current code doesn't seem to support restarting the end of presentation properly, and I think it would need more changes to, but it's not so different from the Stop test.
So it crashes for some other reason than just a mismatched event type?
On Thu Dec 8 16:10:27 2022 +0000, Nikolay Sivov wrote:
So it crashes for some other reason than just a mismatched event type?
Yes. I have no idea if it is a matter of timing, race condition, or if something (the game itself maybe) blocks the end of presentation, but the game calls Stop at most twice and crashes if the right event doesn't arrive soon enough.
This merge request was closed by Rémi Bernon.