-- v3: mf/test: Check the topologies id's in topo loader.
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 b5472569595..97e987d0f93 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; @@ -752,57 +816,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; @@ -820,9 +833,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;
@@ -1825,11 +1835,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);
@@ -1897,9 +1907,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. */ @@ -1907,12 +1933,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)) { @@ -1944,14 +1976,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) @@ -2306,6 +2346,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); @@ -2352,7 +2393,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;
@@ -2373,7 +2414,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) { @@ -4307,15 +4348,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 c05482778df..754d6c23a08 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2006,9 +2006,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; @@ -2016,6 +2017,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); @@ -2038,6 +2042,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; }
@@ -3452,6 +3462,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; @@ -8251,6 +8500,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();
From: Santino Mazza smazza@codeweavers.com
--- dlls/mf/tests/mf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 754d6c23a08..82bc97658e8 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4200,7 +4200,7 @@ static void test_topology_loader(void) IMFTopoLoader *loader; IUnknown *node_object; WORD node_count; - TOPOID node_id; + TOPOID node_id, oldtopoid, newtopoid; DWORD index; HRESULT hr; BOOL ret; @@ -4426,6 +4426,10 @@ static void test_topology_loader(void) } else if (test->expected_result == S_OK) { + IMFTopology_GetTopologyID(topology, &oldtopoid); + IMFTopology_GetTopologyID(full_topology, &newtopoid); + todo_wine ok(oldtopoid == newtopoid, "Expected the same topology id. %llu == %llu\n", oldtopoid, newtopoid); + hr = IMFTopology_GetCount(full_topology, &count); ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); todo_wine @@ -4573,6 +4577,9 @@ todo_wine { hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); ok(hr == S_OK, "Failed to resolve topology, hr %#lx.\n", hr); ok(full_topology != topology2, "Unexpected instance.\n"); + IMFTopology_GetTopologyID(topology2, &oldtopoid); + IMFTopology_GetTopologyID(full_topology, &newtopoid); + todo_wine ok(oldtopoid == newtopoid, "Expected the same topology id. %llu == %llu\n", oldtopoid, newtopoid); hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
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=142935
Your paranoid android.
=== w11pro64_amd (64 bit report) ===
mf: mf.c:3586: Test failed: Unexpected hr 0xc00d36fa. mf.c:3588: Test failed: got punkVal 000000000D073080 mf.c:3592: Test failed: Unexpected hr 0x102.
On Thu Feb 8 13:23:22 2024 +0000, Nikolay Sivov wrote:
There is still a bug which is already in the tests, where the topology
id of the resolved topology will not match the original topology (when not using the no resolve flag), this is because of a bug with the topo loader where it creates a new topology object with a different ID, in Windows apparently it creates a new topology object but it contains the same ID as the not resolved topology. Is that Load() functionality to preserve IDs? We should probably test that with Load() as well. However, since one case use a custom loader, maybe this is implemented in both places.
I wrote the test for IMFTopoLoader::Load, as you can see the topology ID of the full topology doesn't change and the topology object address does change.
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
BOOL thin; float rate; } presentation;
- struct list topologies_queue; /* Topologies queued for playing. */ struct list topologies; struct list commands; enum session_state state;
Why do we need two lists?
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
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;
Is this something we can get rid of anyway, or is it related to your change?
Could you describe what the issue with release topologies is exactly?
On Thu Feb 8 19:22:06 2024 +0000, Nikolay Sivov wrote:
Could you describe what the issue with release topologies is exactly?
In Windows, when you set a new topology for the media session, the old or ended topologies are not shutdown, they just get removed from the queue, but a reference to it is kept. Only when you shutdown the media session all the topologies that have been used by the media session, including the ones that are not in the queue anymore, get shutdown and released.
In Wine this doesn't happen, when a topology is removed from the queue the media session shutdowns the topology, and then they are not able to be reused.
On Thu Feb 8 19:12:40 2024 +0000, Nikolay Sivov wrote:
Why do we need two lists?
We need to save references to the used topologies, I was thinking in maybe using one list and queue the next topology based on the state of the topology but I thought this approach was simpler.
On Thu Feb 8 19:12:41 2024 +0000, Nikolay Sivov wrote:
Is this something we can get rid of anyway, or is it related to your change?
If I'm not wrong, this was used to prevent the original topology from being cleared when session_clear_presentation is called, now this is done only when IMFMediaSession::Shutdown is called so it's not possible to get into that state anymore.
Could you please open a separate MR to preserve topology id for resolved topology? To fix that I think we should sacrifice a bit of overhead, and maybe use CloneFrom(), followed by Clear(). I don't think there is another way to preserve this id.
I'll take a look at refcounting problem you found.
On Mon Mar 11 15:40:55 2024 +0000, Nikolay Sivov wrote:
Could you please open a separate MR to preserve topology id for resolved topology? To fix that I think we should sacrifice a bit of overhead, and maybe use CloneFrom(), followed by Clear(). I don't think there is another way to preserve this id. I'll take a look at refcounting problem you found.
I created MR !5281 , I didn't make use of CloneFrom() though. I created a private function so we can create a topology with a set topoid.