From: Santino Mazza smazza@codeweavers.com
--- dlls/mf/session.c | 193 ++++++++++++++++++++-------------- dlls/mf/tests/mf.c | 254 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 368 insertions(+), 79 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 865399488ae..04ee4a3b5cd 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -80,11 +80,11 @@ struct session_op struct list entry; };
-struct queued_topology +struct topology_entry { struct list entry; + TOPOID topoid; IMFTopology *topology; - MF_TOPOSTATUS status; };
enum session_state @@ -256,6 +256,7 @@ struct media_session BOOL thin; float rate; } presentation; + struct list topologies_queue; /* Topologies queued for playing. */ struct list topologies; struct list commands; enum session_state state; @@ -491,9 +492,9 @@ static HRESULT session_submit_simple_command(struct media_session *session, enum
static void session_clear_queued_topologies(struct media_session *session) { - struct queued_topology *ptr, *next; + struct topology_entry *ptr, *next;
- LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &session->topologies, struct queued_topology, entry) + LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &session->topologies_queue, struct topology_entry, entry) { list_remove(&ptr->entry); IMFTopology_Release(ptr->topology); @@ -501,6 +502,69 @@ static void session_clear_queued_topologies(struct media_session *session) } }
+ +static void session_shutdown_topology(struct media_session *session, IMFTopology *topology) +{ + unsigned int shutdown, force_shutdown; + MF_TOPOLOGY_TYPE node_type; + IMFStreamSink *stream_sink; + IMFTopologyNode *node; + IMFActivate *activate; + IMFMediaSink *sink; + WORD idx = 0; + HRESULT hr; + + force_shutdown = session->state == SESSION_STATE_SHUT_DOWN; + + /* FIXME: should handle async MFTs, but these are not supported by the rest of the pipeline currently. */ + + while (SUCCEEDED(IMFTopology_GetNode(topology, idx++, &node))) + { + if (SUCCEEDED(IMFTopologyNode_GetNodeType(node, &node_type)) && + node_type == MF_TOPOLOGY_OUTPUT_NODE) + { + shutdown = 1; + IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &shutdown); + + if (force_shutdown || shutdown) + { + if (SUCCEEDED(IMFTopologyNode_GetUnknown(node, &_MF_TOPONODE_IMFActivate, &IID_IMFActivate, + (void **)&activate))) + { + if (FAILED(hr = IMFActivate_ShutdownObject(activate))) + WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); + IMFActivate_Release(activate); + } + else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) + { + if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) + { + IMFMediaSink_Shutdown(sink); + IMFMediaSink_Release(sink); + } + + IMFStreamSink_Release(stream_sink); + } + } + } + + IMFTopologyNode_Release(node); + } +} + +static void session_release_topologies(struct media_session *session) +{ + struct topology_entry *ptr, *next; + + LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &session->topologies, struct topology_entry, entry) + { + list_remove(&ptr->entry); + session_shutdown_topology(session, ptr->topology); + IMFTopology_Release(ptr->topology); + free(ptr); + } +} + static void session_set_topo_status(struct media_session *session, HRESULT status, MF_TOPOSTATUS topo_status) { @@ -510,7 +574,7 @@ static void session_set_topo_status(struct media_session *session, HRESULT statu if (topo_status == MF_TOPOSTATUS_INVALID) return;
- if (list_empty(&session->topologies)) + if (list_empty(&session->topologies_queue)) { FIXME("Unexpectedly empty topology queue.\n"); return; @@ -518,7 +582,7 @@ static void session_set_topo_status(struct media_session *session, HRESULT statu
if (topo_status > session->presentation.topo_status) { - struct queued_topology *topology = LIST_ENTRY(list_head(&session->topologies), struct queued_topology, entry); + struct topology_entry *topology = LIST_ENTRY(list_head(&session->topologies_queue), struct topology_entry, entry);
param.vt = VT_UNKNOWN; param.punkVal = (IUnknown *)topology->topology; @@ -793,57 +857,6 @@ static void release_topo_node(struct topo_node *node) free(node); }
-static void session_shutdown_current_topology(struct media_session *session) -{ - unsigned int shutdown, force_shutdown; - MF_TOPOLOGY_TYPE node_type; - IMFStreamSink *stream_sink; - IMFTopology *topology; - IMFTopologyNode *node; - IMFActivate *activate; - IMFMediaSink *sink; - WORD idx = 0; - HRESULT hr; - - topology = session->presentation.current_topology; - force_shutdown = session->state == SESSION_STATE_SHUT_DOWN; - - /* FIXME: should handle async MFTs, but these are not supported by the rest of the pipeline currently. */ - - while (SUCCEEDED(IMFTopology_GetNode(topology, idx++, &node))) - { - if (SUCCEEDED(IMFTopologyNode_GetNodeType(node, &node_type)) && - node_type == MF_TOPOLOGY_OUTPUT_NODE) - { - shutdown = 1; - IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &shutdown); - - if (force_shutdown || shutdown) - { - if (SUCCEEDED(IMFTopologyNode_GetUnknown(node, &_MF_TOPONODE_IMFActivate, &IID_IMFActivate, - (void **)&activate))) - { - if (FAILED(hr = IMFActivate_ShutdownObject(activate))) - WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); - IMFActivate_Release(activate); - } - else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) - { - if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) - { - IMFMediaSink_Shutdown(sink); - IMFMediaSink_Release(sink); - } - - IMFStreamSink_Release(stream_sink); - } - } - } - - IMFTopologyNode_Release(node); - } -} - static void session_clear_command_list(struct media_session *session) { struct session_op *op, *op2; @@ -861,9 +874,6 @@ static void session_clear_presentation(struct media_session *session) struct media_sink *sink, *sink2; struct topo_node *node, *node2;
- session_shutdown_current_topology(session); - - IMFTopology_Clear(session->presentation.current_topology); session->presentation.topo_status = MF_TOPOSTATUS_INVALID; session->presentation.flags = 0;
@@ -1900,11 +1910,11 @@ static HRESULT session_set_current_topology(struct media_session *session, IMFTo IMFMediaEvent *event; HRESULT hr;
- if (FAILED(hr = IMFTopology_CloneFrom(session->presentation.current_topology, topology))) - { - WARN("Failed to clone topology, hr %#lx.\n", hr); - return hr; - } + if (session->presentation.current_topology) + IMFTopology_Release(session->presentation.current_topology); + + session->presentation.current_topology = topology; + IMFTopology_AddRef(topology);
session_collect_nodes(session);
@@ -1972,9 +1982,25 @@ static HRESULT session_set_current_topology(struct media_session *session, IMFTo return S_OK; }
+/* Get a topology from the topologies list by it's id. */ +static struct topology_entry *get_topology_entry_by_id(struct media_session *session, TOPOID id) +{ + struct topology_entry *entry; + + LIST_FOR_EACH_ENTRY(entry, &session->topologies, struct topology_entry, entry) + { + if (entry->topoid == id) + return entry; + } + + return NULL; +} + static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology) { + TOPOID topo_id; IMFTopology *resolved_topology = NULL; + struct topology_entry *topology_entry = NULL; HRESULT hr = S_OK;
/* Resolve unless claimed to be full. */ @@ -1982,12 +2008,18 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF { if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION)) { - hr = session_bind_output_nodes(topology); + IMFTopology_GetTopologyID(topology, &topo_id); + if (!(topology_entry = get_topology_entry_by_id(session, topo_id))) + { + hr = session_bind_output_nodes(topology);
- if (SUCCEEDED(hr)) - hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); - if (SUCCEEDED(hr)) - hr = session_init_media_types(resolved_topology); + if (SUCCEEDED(hr)) + hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); + if (SUCCEEDED(hr)) + hr = session_init_media_types(resolved_topology); + } + else + resolved_topology = topology_entry->topology;
if (SUCCEEDED(hr)) { @@ -2019,14 +2051,22 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF /* With no current topology set it right away, otherwise queue. */ if (topology) { - struct queued_topology *queued_topology; + struct topology_entry *queued_topology; + + if (!topology_entry && (topology_entry = calloc(1, sizeof(*topology_entry)))) + { + topology_entry->topology = topology; + IMFTopology_AddRef(topology_entry->topology); + + list_add_tail(&session->topologies, &topology_entry->entry); + }
if ((queued_topology = calloc(1, sizeof(*queued_topology)))) { queued_topology->topology = topology; IMFTopology_AddRef(queued_topology->topology);
- list_add_tail(&session->topologies, &queued_topology->entry); + list_add_tail(&session->topologies_queue, &queued_topology->entry); }
if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID) @@ -2381,6 +2421,7 @@ static HRESULT WINAPI mfsession_Shutdown(IMFMediaSession *iface) session->clock = NULL; session_clear_presentation(session); session_clear_queued_topologies(session); + session_release_topologies(session); session_submit_simple_command(session, SESSION_CMD_SHUTDOWN); } LeaveCriticalSection(&session->cs); @@ -2427,7 +2468,7 @@ static HRESULT WINAPI mfsession_GetSessionCapabilities(IMFMediaSession *iface, D static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD flags, TOPOID id, IMFTopology **topology) { struct media_session *session = impl_from_IMFMediaSession(iface); - struct queued_topology *queued; + struct topology_entry *queued; TOPOID topo_id; HRESULT hr;
@@ -2448,7 +2489,7 @@ static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD fl } else { - LIST_FOR_EACH_ENTRY(queued, &session->topologies, struct queued_topology, entry) + LIST_FOR_EACH_ENTRY(queued, &session->topologies_queue, struct topology_entry, entry) { if (SUCCEEDED(IMFTopology_GetTopologyID(queued->topology, &topo_id)) && topo_id == id) { @@ -4587,15 +4628,13 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses object->sink_finalizer_callback.lpVtbl = &session_sink_finalizer_callback_vtbl; object->refcount = 1; list_init(&object->topologies); + list_init(&object->topologies_queue); list_init(&object->commands); list_init(&object->presentation.sources); list_init(&object->presentation.sinks); list_init(&object->presentation.nodes); InitializeCriticalSection(&object->cs);
- if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology))) - goto failed; - if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto failed;
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index a2f183e5cc4..60bdd3fd71d 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1169,9 +1169,10 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal return status; }
-#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) +#define get_media_event(a, b, c, d, e, f) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e, f) +#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e, NULL) static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value, IMFMediaEvent **media_event) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); MediaEventType type; @@ -1179,6 +1180,9 @@ static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *sessi DWORD ret; GUID guid;
+ if (media_event) + *media_event = NULL; + do { hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); @@ -1201,6 +1205,12 @@ static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *sessi hr = IMFMediaEvent_GetStatus(impl->media_event, &status); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ if (media_event) + { + IMFMediaEvent_AddRef(impl->media_event); + *media_event = impl->media_event; + } + return status; }
@@ -2615,6 +2625,245 @@ static void test_media_session_rate_control(void) ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); }
+#define check_topologyid(a, b) check_topologyid_(__LINE__, a, b) +static void check_topologyid_(unsigned int line, IMFMediaEvent *event, TOPOID expected_topoid) +{ + PROPVARIANT pv; + TOPOID topoid; + IMFTopology *topology; + + IMFMediaEvent_GetValue(event, &pv); + IUnknown_QueryInterface(pv.punkVal, &IID_IMFTopology, (void**)&topology); + IMFTopology_GetTopologyID(topology, &topoid); + ok_(__FILE__, line)(topoid == expected_topoid, "Expected topoid %I64u, got %I64u\n", expected_topoid, topoid); + IMFTopology_Release(topology); +} + +#define check_topostatus(a, b) check_topostatus_(__LINE__, a, b) +static void check_topostatus_(unsigned int line, IMFMediaEvent *event, UINT32 expected_status) +{ + UINT32 topostatus; + IMFMediaEvent_GetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, &topostatus); + ok_(__FILE__, line)(topostatus == expected_status, "Expected MF_TOPOSTATUS_READY got %d\n", topostatus); +} + +static IMFTopologyNode *get_sink_node(IMFTopology *topology, DWORD index) +{ + IMFCollection *output_nodes; + IMFTopologyNode *node; + IUnknown *nodeunk; + + IMFTopology_GetOutputNodeCollection(topology, &output_nodes); + IMFCollection_GetElement(output_nodes, index, &nodeunk); + IMFCollection_Release(output_nodes); + IUnknown_QueryInterface(nodeunk, &IID_IMFTopologyNode, (void**)&node); + IUnknown_Release(nodeunk); + + return node; +} + +static IMFTopology *create_topology_from_file(LPCWSTR file, LPCWSTR mime) +{ + IMFTopologyNode *src_node, *sink_node; + IMFPresentationDescriptor *pd; + IMFActivate *sar_activate; + IMFStreamDescriptor *sd; + IMFMediaSource *source; + IMFTopology *topology; + BOOL selected; + HRESULT hr; + + if(!(source = create_media_source(file, mime))) + return NULL; + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSource_CreatePresentationDescriptor(source, &pd); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateAudioRendererActivate(&sar_activate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFTopologyNode_SetObject(sink_node, (IUnknown *)sar_activate); + + IMFTopology_AddNode(topology, src_node); + IMFTopology_AddNode(topology, sink_node); + IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + + IMFTopologyNode_Release(src_node); + IMFTopologyNode_Release(sink_node); + IMFActivate_Release(sar_activate); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + return topology; +} + +static void test_media_session_topologies(void) +{ + IMFTopology *topology; + IUnknown *nodeunk; + IMFStreamSink *stream_sink; + IMFTopologyNode *node; + IMFAsyncCallback *callback; + IMFMediaSession *session; + IMFMediaEvent *event; + TOPOID topoid; + PROPVARIANT pv; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); + + if (!(topology = create_topology_from_file(L"mp3encdata.bin", L"audio/mpeg"))) + { + win_skip("MP3 not supported.\n"); + MFShutdown(); + return; + } + + IMFTopology_GetTopologyID(topology, &topoid); + + + callback = create_test_callback(TRUE); + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); + + node = get_sink_node(topology, 0); + IMFTopologyNode_GetObject(node, &nodeunk); + check_interface(nodeunk, &IID_IMFActivate, TRUE); + IUnknown_Release(nodeunk); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_UNKNOWN, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = get_media_event(session, callback, MESessionTopologyStatus, 1000, &pv, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine check_topologyid(event, topoid); + check_topostatus(event, MF_TOPOSTATUS_READY); + IMFMediaEvent_Release(event); + + node = get_sink_node(topology, 0); + IMFTopologyNode_GetObject(node, &nodeunk); + check_interface(nodeunk, &IID_IMFActivate, FALSE); + check_interface(nodeunk, &IID_IMFStreamSink, TRUE); + IUnknown_QueryInterface(nodeunk, &IID_IMFStreamSink, (void**)&stream_sink); + IUnknown_Release(nodeunk); + + pv.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = get_media_event(session, callback, MESessionTopologyStatus, 1000, &pv, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine check_topologyid(event, topoid); + check_topostatus(event, MF_TOPOSTATUS_ENDED); + IMFMediaEvent_Release(event); + + hr = wait_media_event(session, callback, MESessionEnded, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_UNKNOWN, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + pv.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = get_media_event(session, callback, MESessionTopologyStatus, 1000, &pv, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine check_topologyid(event, topoid); + check_topostatus(event, MF_TOPOSTATUS_ENDED); + IMFMediaEvent_Release(event); + + hr = wait_media_event(session, callback, MESessionEnded, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, MFSESSION_SETTOPOLOGY_IMMEDIATE, topology); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_UNKNOWN, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pv.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + hr = wait_media_event(session, callback, MESessionEnded, 1000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + + IMFMediaSession_Close(session); + hr = wait_media_event(session, callback, MESessionClosed, 2000, &pv); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pv.vt == VT_EMPTY, "got vt %u\n", pv.vt); + ok(pv.punkVal != (IUnknown *)topology, "got punkVal %p\n", pv.punkVal); + PropVariantClear(&pv); + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSession_Shutdown(session); + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == MF_E_STREAMSINK_REMOVED, "Unexpected hr %#lx.\n", hr); + IMFStreamSink_Release(stream_sink); + + IMFMediaSession_Release(session); + IMFAsyncCallback_Release(callback); + IMFTopology_Release(topology); + + MFShutdown(); +} + struct test_grabber_callback { IMFSampleGrabberSinkCallback IMFSampleGrabberSinkCallback_iface; @@ -6549,6 +6798,7 @@ START_TEST(mf) test_media_session(); test_media_session_events(); test_media_session_rate_control(); + test_media_session_topologies(); test_MFShutdownObject(); test_presentation_clock(); test_sample_grabber();