From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/tests/mf.c | 247 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index f2dc8bfd91e..515a98f9aa6 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4317,7 +4317,8 @@ static void test_sample_grabber_is_mediatype_supported(void) }
/* create a test topology with the specified source, sink, and option MFT. Return duration if required */ -static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *sink, IUnknown *mft, UINT64 *duration) +static IMFTopology *create_test_topology_unk_noshutdown(IMFMediaSource *source, IUnknown *sink, IUnknown *mft, + UINT64 *duration, UINT noshutdown_on_remove) { IMFTopologyNode *src_node, *sink_node, *mft_node; IMFPresentationDescriptor *pd; @@ -4369,6 +4370,11 @@ static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *s ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + if (noshutdown_on_remove != UINT_MAX) + { + hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, noshutdown_on_remove); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
@@ -4379,6 +4385,11 @@ static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *s return topology; }
+static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *sink, IUnknown *mft, UINT64 *duration) +{ + return create_test_topology_unk_noshutdown(source, (IUnknown*)sink, mft, duration, UINT_MAX); +} + static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) { return create_test_topology_unk(source, (IUnknown*)sink_activate, NULL, duration); @@ -8451,6 +8462,239 @@ static void test_media_session_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+static void test_media_session_sink_shutdown(void) +{ + media_type_desc video_rgb32_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + }; + struct test_grabber_callback *grabber_callback, *grabber_callbacks[3]; + IMFAsyncCallback *callback; + IMFStreamSink *stream_sink; + IMFActivate *sink_activate; + IMFMediaType *output_type; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + PROPVARIANT propvar; + HRESULT hr; + LONG ref; + UINT i; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + + callback = create_test_callback(TRUE); + + /* Sinks are always shut down on session shutdown, but never before. + * MF_TOPONODE_NOSHUTDOWN_ON_REMOVE is ignored. */ + + for (i = 0; i < 4; ++i) + { + winetest_push_context("Test %u", i); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (i & 2) + { + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + topology = create_test_topology_unk_noshutdown(source, (IUnknown *)sink_activate, NULL, NULL, i & 1); + IMFActivate_Release(sink_activate); + } + else + { + grabber_callback = create_activated_test_grabber_callback(output_type, &stream_sink); + topology = create_test_topology_unk_noshutdown(source, (IUnknown *)stream_sink, NULL, NULL, i & 1); + IMFStreamSink_Release(stream_sink); + } + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + 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, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStopped, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, MFSESSION_SETTOPOLOGY_CLEAR_CURRENT, NULL); + 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); + PropVariantClear(&propvar); + todo_wine_if(i == 1) + ok(!grabber_callback->shutdown, "Media sink was shut down.\n"); + + 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); + todo_wine_if(i == 1) + ok(!grabber_callback->shutdown, "Media sink was shut down.\n"); + + 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); + todo_wine_if(i == 1) + ok(!grabber_callback->shutdown, "Media sink was shut down.\n"); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(i != 1) + ok(grabber_callback->shutdown, "Media sink is not shut down.\n"); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + ref = IMFMediaSession_Release(session); + todo_wine_if(i != 1) + ok(!ref, "Unexpected refcount %ld.\n", ref); + ref = IMFMediaSource_Release(source); + ok(!ref, "Unexpected refcount %ld.\n", ref); + + winetest_pop_context(); + } + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + /* Sinks in queued topologies are not shut down until session shutdown. */ + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < 2; ++i) + { + grabber_callbacks[i] = create_activated_test_grabber_callback(output_type, &stream_sink); + topology = create_test_topology_unk_noshutdown(source, (IUnknown *)stream_sink, NULL, NULL, 0); + IMFStreamSink_Release(stream_sink); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + } + + grabber_callbacks[2] = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callbacks[2]->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + topology = create_test_topology_unk_noshutdown(source, (IUnknown *)sink_activate, NULL, NULL, 0); + IMFActivate_Release(sink_activate); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + 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, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + for (i = 0; i < ARRAY_SIZE(grabber_callbacks); ++i) + ok(!grabber_callbacks[i]->shutdown, "Media sink %u was shut down.\n", i); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < ARRAY_SIZE(grabber_callbacks); ++i) + { + todo_wine_if(i) + ok(grabber_callbacks[i]->shutdown, "Media sink %u is not shut down.\n", i); + IMFSampleGrabberSinkCallback_Release(&grabber_callbacks[i]->IMFSampleGrabberSinkCallback_iface); + } + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + ref = IMFMediaSession_Release(session); + ok(!ref, "Unexpected refcount %ld.\n", ref); + + ref = IMFMediaSource_Release(source); + ok(!ref, "Unexpected refcount %ld.\n", ref); + + /* Unexpected source shutdown does not shut down sinks. + * NOTE: Source re-use is broken in Wine, for more reasons than + * just errors from multiple event subscription. */ + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + goto done; + } + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + grabber_callback = create_activated_test_grabber_callback(output_type, &stream_sink); + topology = create_test_topology_unk(source, (IUnknown *)stream_sink, NULL, NULL); + IMFStreamSink_Release(stream_sink); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + 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, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!grabber_callback->shutdown, "Media sink was shut down.\n"); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(grabber_callback->shutdown, "Media sink is not shut down.\n"); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + +done: + IMFAsyncCallback_Release(callback); + IMFMediaType_Release(output_type); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -8490,4 +8734,5 @@ START_TEST(mf) test_media_session_source_shutdown(); test_media_session_thinning(); test_media_session_seek(); + test_media_session_sink_shutdown(); }