From: "Sergio Gómez Del Real" Subject: [PATCH v4 3/3] mf: Partially implement the topology loader. Message-Id: <20200403153553.6341-3-sdelreal@codeweavers.com> Date: Fri, 3 Apr 2020 10:35:53 -0500 In-Reply-To: <20200403153553.6341-1-sdelreal@codeweavers.com> References: <20200403153553.6341-1-sdelreal@codeweavers.com> Signed-off-by: Sergio Gómez Del Real --- dlls/mf/tests/mf.c | 95 +++++++----- dlls/mf/topology.c | 378 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 421 insertions(+), 52 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 1a762c6874..42db1bc4e2 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1380,7 +1380,7 @@ static void test_topology_loader(void) { IMFSampleGrabberSinkCallback test_grabber_callback = { &test_grabber_callback_vtbl }; IMFMediaSource test_media_source = { &test_media_source_vtbl }; - IMFTopology *topology, *topology2, *full_topology; + IMFTopology *topology, *topology2, *full_topology = NULL; IMFTopologyNode *src_node, *sink_node; IMFMediaType *media_type, *current_media_type; IMFPresentationDescriptor *pd; @@ -1418,7 +1418,6 @@ static void test_topology_loader(void) /* Empty topology */ hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); -todo_wine ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr); hr = MFCreateSourceResolver(&resolver); @@ -1463,7 +1462,6 @@ todo_wine /* Source node only. */ hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); -todo_wine ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr); /* Add grabber sink. */ @@ -1489,7 +1487,6 @@ todo_wine ok(hr == S_OK, "Failed to add sink node, hr %#x.\n", hr); hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); -todo_wine ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#x.\n", hr); hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); @@ -1513,38 +1510,49 @@ todo_wine ok(count == 0, "Unexpected count %u.\n", count); hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); +todo_wine ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); ok(full_topology != topology, "Unexpected instance.\n"); - - IMFTopology_GetNodeCount(full_topology, &node_count); + if (full_topology) + hr = IMFTopology_GetNodeCount(full_topology, &node_count); +todo_wine ok(node_count == 2, "Topology node count is %#x.\n", node_count); hr = IMFTopology_GetCount(topology, &count); ok(hr == S_OK, "Failed to get attribute count, hr %#x.\n", hr); ok(count == 0, "Unexpected count %u.\n", count); - hr = IMFTopology_GetCount(full_topology, &count); + hr = E_FAIL; + if (full_topology) + hr = IMFTopology_GetCount(full_topology, &count); +todo_wine { ok(hr == S_OK, "Failed to get attribute count, hr %#x.\n", hr); -todo_wine ok(count == 1, "Unexpected count %u.\n", count); +} - hr = IMFTopology_GetItemByIndex(full_topology, 0, &guid, NULL); + if (full_topology) + hr = IMFTopology_GetItemByIndex(full_topology, 0, &guid, NULL); todo_wine { ok(hr == S_OK, "Failed to get attribute key, hr %#x.\n", hr); ok(IsEqualGUID(&guid, &MF_TOPOLOGY_RESOLUTION_STATUS), "Unexpected key %s.\n", wine_dbgstr_guid(&guid)); } value = 0xdeadbeef; - hr = IMFTopology_GetUINT32(full_topology, &MF_TOPOLOGY_RESOLUTION_STATUS, &value); + if (full_topology) + hr = IMFTopology_GetUINT32(full_topology, &MF_TOPOLOGY_RESOLUTION_STATUS, &value); todo_wine { ok(hr == S_OK, "Failed to get attribute, hr %#x.\n", hr); ok(value == MF_TOPOLOGY_RESOLUTION_SUCCEEDED, "Unexpected value %#x.\n", value); } hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); +todo_wine ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); ok(full_topology != topology2, "Unexpected instance.\n"); - IMFTopology_Release(topology2); - IMFTopology_Release(full_topology); + if (full_topology) + { + IMFTopology_Release(topology2); + IMFTopology_Release(full_topology); + } IMFByteStream_Release(stream); IMFStreamDescriptor_Release(sd); @@ -1554,21 +1562,32 @@ todo_wine { hr = IMFPresentationDescriptor_DeselectStream(pd, 0); ok(hr == S_OK, "Failed to deselect stream, hr %#x.\n", hr); hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); +todo_wine ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); IMFTopologyNode_Release(src_node); - hr = IMFTopology_GetNode(full_topology, 0, &src_node); + if (full_topology) + hr = IMFTopology_GetNode(full_topology, 0, &src_node); +todo_wine ok(hr == S_OK, "Failed to get full topology source node, hr %#x.\n", hr); - IMFPresentationDescriptor_Release(pd); - hr = IMFTopologyNode_GetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, (void **)&pd); - ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); - IMFStreamDescriptor_Release(sd); - IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); - ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr); - ok(!selected, "Stream should not be selected\n."); + if (hr == S_OK) + { + IMFPresentationDescriptor_Release(pd); +todo_wine + hr = IMFTopologyNode_GetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, (void **)&pd); + ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); + IMFStreamDescriptor_Release(sd); + IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); +todo_wine + ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr); + ok(!selected, "Stream should not be selected\n."); + } - IMFPresentationDescriptor_Release(pd); - IMFTopologyNode_Release(src_node); - IMFTopology_Release(full_topology); + if (hr == S_OK) + { + IMFPresentationDescriptor_Release(pd); + IMFTopologyNode_Release(src_node); + IMFTopology_Release(full_topology); + } IMFTopology_Release(topology); IMFMediaSource_Release(source); IMFSourceResolver_Release(resolver); @@ -1636,7 +1655,6 @@ todo_wine { ok(hr == MF_E_ATTRIBUTENOTFOUND, "Attribute should not be set\n."); /* if no current media type set, loader uses first index exclusively */ hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); -todo_wine /* when major types differ, error is MF_E_TOPO_CODEC_NOT_FOUND */ ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#x.\n", hr); @@ -1661,14 +1679,18 @@ todo_wine ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr); hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); +todo_wine ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); - hr = IMFTopology_GetNodeCount(full_topology, &node_count); + if (hr == S_OK) + hr = IMFTopology_GetNodeCount(full_topology, &node_count); +todo_wine { ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr); -todo_wine ok(node_count == 3, "Unexpected node count.\n"); +} - IMFTopology_Release(full_topology); + if (hr == S_OK) + IMFTopology_Release(full_topology); /* now test with MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES set on topology */ hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 1); @@ -1723,7 +1745,7 @@ todo_wine { ok(hr == S_OK, "Failed to get stream descriptor, hr %#x.\n", hr); hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &mth); ok(hr == S_OK, "Failed to get media type handler, hr %#x.\n", hr); - hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 1, &media_type); + hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth, 2, &media_type); ok(hr == S_OK, "Failed getting media type, hr %#x.\n", hr); hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, media_type); ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr); @@ -1793,10 +1815,10 @@ todo_wine { /* unconnected nodes in partial topology are discarded */ hr = IMFTopology_GetNodeCount(full_topology, &node_count); ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr); -todo_wine - ok(node_count == 3, "Unexpected node count %d.\n", node_count); + ok(node_count == 2, "Unexpected node count %d.\n", node_count); - IMFTopology_Release(full_topology); + if (hr == S_OK) + IMFTopology_Release(full_topology); /* connect nodes for second branch */ hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); @@ -1804,7 +1826,6 @@ todo_wine /* all branches must have valid media types */ hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); -todo_wine ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#x.\n", hr); /* set valid media type for second branch */ @@ -1815,14 +1836,18 @@ todo_wine ok(hr == S_OK, "Failed setting current media type, hr %#x.\n", hr); hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); +todo_wine ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); - hr = IMFTopology_GetNodeCount(full_topology, &node_count); + if (hr == S_OK) + hr = IMFTopology_GetNodeCount(full_topology, &node_count); +todo_wine { ok(hr == S_OK, "Failed to get node count, hr %#x.\n", hr); -todo_wine ok(node_count == 6, "Unexpected node count %d.\n", node_count); +} - IMFTopology_Release(full_topology); + if (hr == S_OK) + IMFTopology_Release(full_topology); IMFMediaType_Release(media_type); IMFTopologyNode_Release(src_node); IMFTopologyNode_Release(sink_node); diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c index 0b4d734442..aeaa9cb84a 100644 --- a/dlls/mf/topology.c +++ b/dlls/mf/topology.c @@ -1913,47 +1913,391 @@ static ULONG WINAPI topology_loader_Release(IMFTopoLoader *iface) return refcount; } +static void topology_loader_add_branch(struct topology *topology, IMFTopologyNode *first, IMFTopologyNode *last) +{ + IMFTopology *full_topo = &topology->IMFTopology_iface; + IMFTopologyNode *in, *out; + DWORD index; + + in = first; + IMFTopology_AddNode(full_topo, in); + while (SUCCEEDED(IMFTopologyNode_GetOutput(in, 0, &out, &index))) + { + IMFTopology_AddNode(full_topo, out); + IMFTopologyNode_Release(out); + in = out; + } +} + +static HRESULT topology_loader_resolve_branch_connect_nodes(IMFTopologyNode *src, IMFMediaType *mediatype, IMFTopologyNode *sink, MF_CONNECT_METHOD method) +{ + HRESULT hr; + IMFMediaTypeHandler *mth; + IMFStreamSink *streamsink; + IMFActivate **activates_decs; + IMFActivate **activates_convs; + UINT32 num_activates_decs = 0; + UINT32 num_activates_convs = 0; + + IMFTopologyNode_GetObject(sink, (IUnknown **)&streamsink); + IMFStreamSink_GetMediaTypeHandler(streamsink, &mth); + if (method == MF_CONNECT_DIRECT) + { + if (FAILED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(mth, mediatype, NULL))) + goto out; + IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype); + IMFTopologyNode_ConnectOutput(src, 0, sink, 0); + goto out; + } + else + { + IMFTopologyNode *node_dec, *node_conv; + GUID major_type, subtype, mft_category; + MFT_REGISTER_TYPE_INFO mft_typeinfo; + UINT32 flags = MFT_ENUM_FLAG_ALL; + int i, j; + + IMFMediaType_GetGUID(mediatype, &MF_MT_MAJOR_TYPE, &major_type); + if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + mft_category = MFT_CATEGORY_AUDIO_DECODER; + else if (IsEqualGUID(&major_type, &MFMediaType_Video)) + mft_category = MFT_CATEGORY_VIDEO_DECODER; + else + { + hr = MF_E_TOPO_CODEC_NOT_FOUND; + goto out; + } + + IMFMediaType_GetGUID(mediatype, &MF_MT_SUBTYPE, &subtype); + mft_typeinfo.guidMajorType = major_type; + mft_typeinfo.guidSubtype = subtype; + MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_decs, &num_activates_decs); + + /* for getting converters later on */ + if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + mft_category = MFT_CATEGORY_AUDIO_EFFECT; + else if (IsEqualGUID(&major_type, &MFMediaType_Video)) + mft_category = MFT_CATEGORY_VIDEO_EFFECT; + + /* + * Iterate over number of decoders. + * Try to set input type on decoder with source's output media type. + * If succeeds, iterate over decoder's output media types. + * Try to set input type on sink with decoder's output media type. + * If fails, iterate over number of converters. + * Try to set input type on converter with decoder's output media type. + * If succeeds, iterate over converters output media types. + * Try to set input type on sink with converter's output media type. + */ + for (i = 0; i < num_activates_decs; i++) + { + IMFTransform *decoder; + + IMFActivate_ActivateObject(activates_decs[i], &IID_IMFTransform, (void **)&decoder); + if (SUCCEEDED(hr = IMFTransform_SetInputType(decoder, 0, mediatype, MFT_SET_TYPE_TEST_ONLY))) + { + IMFMediaType *decoder_mtype; + + int count = 0; + IMFTransform_SetInputType(decoder, 0, mediatype, 0); + while (SUCCEEDED(IMFTransform_GetOutputAvailableType(decoder, 0, count++, &decoder_mtype))) + { + IMFTransform *converter; + + /* succeeded with source -> decoder -> sink */ + if (SUCCEEDED(IMFMediaTypeHandler_IsMediaTypeSupported(mth, decoder_mtype, NULL))) + { + IMFMediaTypeHandler_SetCurrentMediaType(mth, decoder_mtype); + MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec); + IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder); + IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0); + IMFTopologyNode_ConnectOutput(node_dec, 0, sink, 0); + + IMFActivate_ShutdownObject(activates_decs[i]); + hr = S_OK; + goto out; + } + + IMFMediaType_GetGUID(decoder_mtype, &MF_MT_SUBTYPE, &subtype); + mft_typeinfo.guidSubtype = subtype; + MFTEnumEx(mft_category, flags, &mft_typeinfo, NULL, &activates_convs, &num_activates_convs); + for (j = 0; j < num_activates_convs; j++) + { + IMFMediaType *converter_mtype; + + IMFActivate_ActivateObject(activates_convs[j], &IID_IMFTransform, (void **)&converter); + if (SUCCEEDED(IMFTransform_SetInputType(converter, 0, decoder_mtype, MFT_SET_TYPE_TEST_ONLY))) + { + int count = 0; + IMFTransform_SetInputType(converter, 0, decoder_mtype, 0); + while (SUCCEEDED(IMFTransform_GetOutputAvailableType(converter, 0, count++, &converter_mtype))) + { + /* succeeded with source -> decoder -> converter -> sink */ + if (SUCCEEDED(IMFMediaTypeHandler_IsMediaTypeSupported(mth, converter_mtype, NULL))) + { + IMFMediaTypeHandler_SetCurrentMediaType(mth, converter_mtype); + MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_dec); + IMFTopologyNode_SetObject(node_dec, (IUnknown *)decoder); + MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_conv); + IMFTopologyNode_SetObject(node_conv, (IUnknown *)converter); + IMFTopologyNode_ConnectOutput(src, 0, node_dec, 0); + IMFTopologyNode_ConnectOutput(node_dec, 0, node_conv, 0); + IMFTopologyNode_ConnectOutput(node_conv, 0, sink, 0); + + IMFActivate_ShutdownObject(activates_convs[j]); + IMFActivate_ShutdownObject(activates_decs[i]); + hr = S_OK; + goto out; + } + } + } + IMFActivate_ShutdownObject(activates_convs[j]); + } + } + } + IMFActivate_ShutdownObject(activates_decs[i]); + } + } + hr = MF_E_TOPO_CODEC_NOT_FOUND; +out: + while (num_activates_convs--) + IMFActivate_Release(activates_convs[num_activates_convs]); + while (num_activates_decs--) + IMFActivate_Release(activates_decs[num_activates_decs]); + CoTaskMemFree(activates_decs); + CoTaskMemFree(activates_convs); + IMFMediaTypeHandler_Release(mth); + IMFStreamSink_Release(streamsink); + + return hr; +} + +static HRESULT topology_loader_resolve_branch(struct topology_node *src, struct topology_node *sink, struct topology *topology, struct topology *full_topology) +{ + IMFMediaTypeHandler *mth_src, *mth_sink; + IMFTopologyNode *clone_src, *clone_sink; + UINT32 method, enum_src_types, streamid; + IMFMediaType **src_mediatypes; + IMFStreamDescriptor *desc; + IMFAttributes *attrs_src; + IMFStreamSink *strm_sink; + IMFMediaType *mtype_src; + DWORD num_media_types; + HRESULT hr; + int i; + + attrs_src = src->attributes; + if (FAILED(hr = IMFAttributes_GetUnknown(attrs_src, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&desc))) + return hr; + strm_sink = (IMFStreamSink *)sink->object; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(desc, &mth_src))) + { + IMFStreamDescriptor_Release(desc); + return hr; + } + if (FAILED(hr = IMFStreamSink_GetMediaTypeHandler(strm_sink, &mth_sink))) + { + IMFStreamDescriptor_Release(desc); + IMFMediaTypeHandler_Release(mth_src); + return hr; + } + + enum_src_types = 0; + hr = IMFTopology_GetUINT32(&topology->IMFTopology_iface, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, &enum_src_types); + + if (FAILED(hr) || !enum_src_types) + { + num_media_types = 1; + if (FAILED(hr = IMFMediaTypeHandler_GetCurrentMediaType(mth_src, &mtype_src))) + if (FAILED(hr = IMFMediaTypeHandler_GetMediaTypeByIndex(mth_src, 0, &mtype_src))) + { + IMFMediaTypeHandler_Release(mth_src); + IMFMediaTypeHandler_Release(mth_sink); + IMFStreamDescriptor_Release(desc); + return hr; + } + } + else + IMFMediaTypeHandler_GetMediaTypeCount(mth_src, &num_media_types); + + src_mediatypes = heap_alloc(sizeof(IMFMediaType *) * num_media_types); + + if (enum_src_types) + for (i = 0; i < num_media_types; i++) + IMFMediaTypeHandler_GetMediaTypeByIndex(mth_src, i, &src_mediatypes[i]); + else + src_mediatypes[0] = mtype_src; + + + MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &clone_src); + MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &clone_sink); + IMFTopologyNode_CloneFrom(clone_src, &src->IMFTopologyNode_iface); + IMFTopologyNode_CloneFrom(clone_sink, &sink->IMFTopologyNode_iface); + + if (FAILED(IMFTopologyNode_GetUINT32(clone_sink, &MF_TOPONODE_STREAMID, &streamid))) + IMFTopologyNode_SetUINT32(clone_sink, &MF_TOPONODE_STREAMID, 0); + + if (enum_src_types) + { + hr = IMFAttributes_GetUINT32(attrs_src, &MF_TOPONODE_CONNECT_METHOD, &method); + if (hr == S_OK && method != MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES) + { + for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++) + for (i = 0; i < num_media_types; i++) + if (SUCCEEDED(hr = topology_loader_resolve_branch_connect_nodes(clone_src, src_mediatypes[i], clone_sink, method))) + { + topology_loader_add_branch(full_topology, clone_src, clone_sink); + goto out; + } + } + else + { + for (i = 0; i < num_media_types; i++) + for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++) + if (SUCCEEDED(hr = topology_loader_resolve_branch_connect_nodes(clone_src, src_mediatypes[i], clone_sink, method))) + { + topology_loader_add_branch(full_topology, clone_src, clone_sink); + goto out; + } + } + } + else + { + for (method = MF_CONNECT_DIRECT; method < MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES; method++) + if (SUCCEEDED(hr = topology_loader_resolve_branch_connect_nodes(clone_src, src_mediatypes[0], clone_sink, method))) + { + topology_loader_add_branch(full_topology, clone_src, clone_sink); + goto out; + } + } + + IMFTopologyNode_Release(clone_src); + IMFTopologyNode_Release(clone_sink); +out: + if (!enum_src_types) + IMFMediaType_Release(mtype_src); + IMFMediaTypeHandler_Release(mth_src); + IMFMediaTypeHandler_Release(mth_sink); + IMFStreamDescriptor_Release(desc); + heap_free(src_mediatypes); + return hr; +} + static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *input_topology, IMFTopology **output_topology, IMFTopology *current_topology) { struct topology *topology = unsafe_impl_from_IMFTopology(input_topology); + struct topology_node *(*node_pairs)[2]; + IMFTopology *full_topology; + int num_connections; IMFStreamSink *sink; HRESULT hr; - size_t i; + int i, idx; FIXME("%p, %p, %p, %p.\n", iface, input_topology, output_topology, current_topology); if (current_topology) FIXME("Current topology instance is ignored.\n"); + if (!topology || topology->nodes.count < 2) + return MF_E_TOPO_UNSUPPORTED; + + num_connections = 0; + for (i = 0; i < topology->nodes.count; i++) + { + struct topology_node *node = topology->nodes.nodes[i]; + + if (node->node_type == MF_TOPOLOGY_SOURCESTREAM_NODE) + { + if (node->outputs.count && node->outputs.streams->connection) + num_connections++; + } + } + + if (!num_connections) + return MF_E_TOPO_UNSUPPORTED; + + node_pairs = heap_alloc_zero(sizeof(struct topology_node *[2]) * num_connections); + + idx = 0; for (i = 0; i < topology->nodes.count; ++i) { struct topology_node *node = topology->nodes.nodes[i]; - switch (node->node_type) + if (node->node_type == MF_TOPOLOGY_SOURCESTREAM_NODE) { - case MF_TOPOLOGY_OUTPUT_NODE: - if (node->object) + if (node->outputs.count && node->outputs.streams->connection) + { + node_pairs[idx][0] = node; + if (node->outputs.streams->connection->node_type == MF_TOPOLOGY_TRANSFORM_NODE) { - /* Sinks must be bound beforehand. */ - if (FAILED(IUnknown_QueryInterface(node->object, &IID_IMFStreamSink, (void **)&sink))) - return MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED; - IMFStreamSink_Release(sink); + struct topology_node *sink = node->outputs.streams->connection; + + while (sink && sink->node_type != MF_TOPOLOGY_OUTPUT_NODE && sink->outputs.count) + sink = sink->outputs.streams->connection; + if (!sink || !sink->outputs.count) + { + FIXME("Check for MF_CONNECT_AS_OPTIONAL and MF_CONNECT_AS_OPTIONAL_BRANCH flags.\n"); + heap_free(node_pairs); + return MF_E_TOPO_UNSUPPORTED; + } + node_pairs[idx][1] = sink; } - break; - case MF_TOPOLOGY_SOURCESTREAM_NODE: - if (FAILED(hr = IMFAttributes_GetItem(node->attributes, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL))) - return hr; - break; - default: - ; + else if (node->outputs.streams->connection->node_type == MF_TOPOLOGY_OUTPUT_NODE) + node_pairs[idx][1] = node->outputs.streams->connection; + else { + FIXME("Tee nodes currently unhandled.\n"); + heap_free(node_pairs); + return MF_E_TOPO_UNSUPPORTED; + } + idx++; + } } } - if (FAILED(hr = MFCreateTopology(output_topology))) + /* all sinks must be activated */ + for (i = 0; i < num_connections; i++) + { + if (FAILED(IUnknown_QueryInterface(node_pairs[i][1]->object, &IID_IMFStreamSink, (void **)&sink))) + { + FIXME("Check for MF_CONNECT_AS_OPTIONAL and MF_CONNECT_AS_OPTIONAL_BRANCH flags.\n"); + heap_free(node_pairs); + return MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED; + } + IMFStreamSink_Release(sink); + } + + if (FAILED(hr = MFCreateTopology(&full_topology))) return hr; - return IMFTopology_CloneFrom(*output_topology, input_topology); + /* resolve each branch */ + for (i = 0; i < num_connections; i++) + { + struct topology_node *src = node_pairs[i][0]; + struct topology_node *sink = node_pairs[i][1]; + struct topology *full_topo; + + if (FAILED(hr = topology_loader_resolve_branch(src, sink, topology, full_topo = impl_from_IMFTopology(full_topology)))) + { + if (full_topo->nodes.count) + { + int count = 0; + while (count < full_topo->nodes.count) + { + IMFTopologyNode_Release(&full_topo->nodes.nodes[count]->IMFTopologyNode_iface); + count++; + } + } + heap_free(node_pairs); + IMFTopology_Release(full_topology); + return hr; + } + } + + *output_topology = full_topology; + heap_free(node_pairs); + return S_OK; } static const IMFTopoLoaderVtbl topologyloadervtbl = -- 2.17.1