Signed-off-by: Sergio Gómez Del Real sdelreal@codeweavers.com --- dlls/mf/tests/mf.c | 2 - dlls/mf/topology.c | 540 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 538 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 33c33f9a7c..63dbdecd22 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1851,7 +1851,6 @@ todo_wine { ok(hr == S_OK, "Failed to resolve topology, hr %#x.\n", hr); 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); IMFTopology_Release(full_topology);
@@ -1868,7 +1867,6 @@ todo_wine
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);
/* now test without MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES on source */ diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c index f1b3c3c9e4..3ca781e12f 100644 --- a/dlls/mf/topology.c +++ b/dlls/mf/topology.c @@ -1949,6 +1949,514 @@ out: return hr == MF_E_NOT_FOUND ? S_OK : hr; }
+static inline HRESULT topology_node_get_mediatypehandler(IMFTopologyNode *node, IMFMediaTypeHandler **mth) +{ + MF_TOPOLOGY_TYPE type; + HRESULT hr; + + if (!node || !mth) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &type))) + goto out; + + if (type == MF_TOPOLOGY_SOURCESTREAM_NODE) + { + IMFStreamDescriptor *streamdesc; + + if (SUCCEEDED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, (void **)&streamdesc))) + { + hr = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, mth); + IMFStreamDescriptor_Release(streamdesc); + } + } + else if (type == MF_TOPOLOGY_OUTPUT_NODE) + { + IMFStreamSink *streamsink; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&streamsink))) + { + hr = IMFStreamSink_GetMediaTypeHandler(streamsink, mth); + IMFStreamSink_Release(streamsink); + } + } + +out: + return hr; +} + +static inline HRESULT topology_loader_test_set_output_mediatype(IMFTopologyNode *node, IMFMediaType *mediatype, int set) +{ + MF_TOPOLOGY_TYPE nodetype; + HRESULT hr; + + if (!node || !mediatype) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + IMFTopologyNode_GetNodeType(node, &nodetype); + + if (nodetype == MF_TOPOLOGY_SOURCESTREAM_NODE) + { + IMFMediaTypeHandler *mth; + + if (SUCCEEDED(hr = topology_node_get_mediatypehandler(node, &mth))) + { + if (set) + hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype); + else + hr = IMFMediaTypeHandler_IsMediaTypeSupported(mth, mediatype, NULL); + } + } + else if (nodetype == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform *transform; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform))) + hr = IMFTransform_SetOutputType(transform, 0, mediatype, set); + } + +out: + return hr; +} + +static inline HRESULT topology_loader_test_set_input_mediatype(IMFTopologyNode *node, IMFMediaType *mediatype, int set) +{ + MF_TOPOLOGY_TYPE nodetype; + HRESULT hr; + + if (!node || !mediatype) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &nodetype))) + goto out; + + if (nodetype == MF_TOPOLOGY_OUTPUT_NODE) + { + IMFMediaTypeHandler *mth; + + if (SUCCEEDED(hr = topology_node_get_mediatypehandler(node, &mth))) + { + if (set) + hr = IMFMediaTypeHandler_SetCurrentMediaType(mth, mediatype); + else + { + DWORD flags; + IMFMediaType *curr_type; + + IMFMediaTypeHandler_GetCurrentMediaType(mth, &curr_type); + hr = IMFMediaType_IsEqual(curr_type, mediatype, &flags); + if (!(flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA)) + hr = MF_E_INVALIDMEDIATYPE; + } + } + } + else if (nodetype == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform *transform; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform))) + hr = IMFTransform_SetInputType(transform, 0, mediatype, set); + } + +out: + return hr; +} + +static inline HRESULT topology_loader_get_current_input_mediatype(IMFTopologyNode *node, IMFMediaType **in) +{ + MF_TOPOLOGY_TYPE type; + HRESULT hr; + + if (!node || !in) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &type))) + goto out; + + if (type == MF_TOPOLOGY_OUTPUT_NODE) + { + IMFMediaTypeHandler *mth; + + if (SUCCEEDED(hr = topology_node_get_mediatypehandler(node, &mth))) + hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, in); + } + else if (type == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform *transform; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform))) + hr = IMFTransform_GetInputCurrentType(transform, 0, in); + } + +out: + return hr; +} + +static inline HRESULT topology_loader_get_current_output_mediatype(IMFTopologyNode *node, IMFMediaType **out) +{ + MF_TOPOLOGY_TYPE type; + HRESULT hr; + + if (!node || !out) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &type))) + goto out; + + if (type == MF_TOPOLOGY_SOURCESTREAM_NODE) + { + IMFMediaTypeHandler *mth; + + if (SUCCEEDED(hr = topology_node_get_mediatypehandler(node, &mth))) + hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, out); + } + else if (type == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform *transform; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform))) + hr = IMFTransform_GetOutputCurrentType(transform, 0, out); + } + +out: + return hr; +} + +static inline HRESULT topology_loader_get_next_output_mediatype(IMFTopologyNode *node, UINT32 index, IMFMediaType **output) +{ + MF_TOPOLOGY_TYPE type; + HRESULT hr; + + if (!node || !output) + { + hr = MF_E_INVALIDREQUEST; + goto out; + } + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &type))) + goto out; + + if (type == MF_TOPOLOGY_SOURCESTREAM_NODE) + { + IMFMediaTypeHandler *mth; + + if (!index) + { + if (SUCCEEDED(hr = topology_node_get_mediatypehandler(node, &mth))) + { + hr = IMFMediaTypeHandler_GetCurrentMediaType(mth, output); + IMFMediaTypeHandler_Release(mth); + } + } + else + hr = MF_E_INVALIDREQUEST; + } + else if (type == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform *transform; + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform))) + { + hr = IMFTransform_GetOutputAvailableType(transform, 0, index, output); + IMFTransform_Release(transform); + } + } + +out: + return hr; +} + +static inline HRESULT topology_loader_get_mft_category(IMFMediaType *mediatype, MF_CONNECT_METHOD method, GUID *category) +{ + GUID major_type; + + if (!mediatype || !category) + return MF_E_INVALIDREQUEST; + + if (FAILED(IMFMediaType_GetGUID(mediatype, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDREQUEST; + + if (method == MF_CONNECT_ALLOW_DECODER || method == MF_CONNECT_ALLOW_CONVERTER) + { + if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + *category = MFT_CATEGORY_AUDIO_DECODER; + else if (IsEqualGUID(&major_type, &MFMediaType_Video)) + *category = MFT_CATEGORY_VIDEO_DECODER; + else + return MF_E_TOPO_CODEC_NOT_FOUND; + } + else + return MF_E_TOPO_CODEC_NOT_FOUND; + + return S_OK; +} + +static inline HRESULT topology_loader_get_mft_reg_typeinfo(IMFMediaType *type, MFT_REGISTER_TYPE_INFO *typeinfo) +{ + GUID major_type, subtype; + + if (!type || !typeinfo) + return MF_E_INVALIDREQUEST; + + IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type); + IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype); + + typeinfo->guidMajorType = major_type; + typeinfo->guidSubtype = subtype; + + return S_OK; +} + +static HRESULT topology_loader_get_mfts(IMFMediaType *in, IMFMediaType *out, MF_CONNECT_METHOD method, IMFActivate ***activates, UINT32 *num_activates) +{ + MFT_REGISTER_TYPE_INFO *info_in = NULL, *info_out = NULL; + MFT_REGISTER_TYPE_INFO mftinfo_in, mftinfo_out; + GUID mft_category; + + if (in == NULL && out == NULL) + return MF_E_INVALIDREQUEST; + + if (in != NULL) + { + topology_loader_get_mft_reg_typeinfo(in, &mftinfo_in); + topology_loader_get_mft_category(in, method, &mft_category); + info_in = &mftinfo_in; + } + if (out != NULL) + { + topology_loader_get_mft_reg_typeinfo(out, &mftinfo_out); + topology_loader_get_mft_category(out, method, &mft_category); + info_out = &mftinfo_out; + } + + return MFTEnumEx(mft_category, MFT_ENUM_FLAG_ALL, info_in, info_out, activates, num_activates); +} + +/* + * Receives upstream and downstream nodes, and input mediatype for upstream. + * Build a list of compatible topologies. Try, in order, direct connection, mft only and mft + converter. + * Always operate on cloned nodes and topologies, since resolution might fail, and nodes and/or branches might be optional, + * so the need arises to recover previous states. + */ +static HRESULT topology_loader_build_topologies_from_input_mediatype(IMFTopologyNode *upstream, IMFMediaType *input, IMFTopologyNode *downstream, MF_CONNECT_METHOD method, struct list *out_topologies) +{ + IMFMediaType *out = NULL; + MF_TOPOLOGY_TYPE type; + HRESULT hr; + + IMFTopologyNode_GetNodeType(upstream, &type); + + if (type != MF_TOPOLOGY_SOURCESTREAM_NODE) + { + if (input == NULL) + { + HRESULT hr = topology_loader_get_current_input_mediatype(upstream, &out); + + if (FAILED(hr) && hr != E_NOTIMPL) + return MF_E_INVALIDMEDIATYPE; + } + else + { + if (FAILED(topology_loader_test_set_input_mediatype(upstream, input, 0))) + return MF_E_INVALIDMEDIATYPE; + + topology_loader_test_set_input_mediatype(upstream, input, 1); + } + } + + if (out) + IMFMediaType_Release(out); + + if (method == MF_CONNECT_DIRECT) + { + struct list_topologies *topology_entry; + IMFMediaType *up_output_mtype; + IMFTopology *topology; + + if (SUCCEEDED(topology_loader_get_current_output_mediatype(upstream, &up_output_mtype))) + { + if (SUCCEEDED(topology_loader_test_set_input_mediatype(downstream, up_output_mtype, 0))) + { + IMFTopologyNode *upstream_clone, *downstream_clone; + + IMFTopologyNode_GetNodeType(upstream, &type); + MFCreateTopologyNode(type, &upstream_clone); + IMFTopologyNode_CloneFrom(upstream_clone, upstream); + + IMFTopologyNode_GetNodeType(downstream, &type); + MFCreateTopologyNode(type, &downstream_clone); + IMFTopologyNode_CloneFrom(downstream_clone, downstream); + topology_loader_test_set_input_mediatype(downstream_clone, up_output_mtype, 1); + + IMFTopologyNode_ConnectOutput(upstream_clone, 0, downstream_clone, 0); + + MFCreateTopology(&topology); + topology_loader_add_branch(impl_from_IMFTopology(topology), upstream_clone); + + topology_entry = heap_alloc_zero(sizeof(struct list_topologies *)); + topology_entry->topology = impl_from_IMFTopology(topology); + list_add_tail(out_topologies, &topology_entry->entry); + + hr = S_OK; + } + IMFMediaType_Release(up_output_mtype); + } + else + { + int count = 0; + while (SUCCEEDED(topology_loader_get_next_output_mediatype(upstream, count++, &up_output_mtype))) + { + if (SUCCEEDED(topology_loader_test_set_input_mediatype(downstream, up_output_mtype, 0))) + { + IMFTopologyNode *upstream_clone, *downstream_clone; + + IMFTopologyNode_GetNodeType(upstream, &type); + MFCreateTopologyNode(type, &upstream_clone); + IMFTopologyNode_CloneFrom(upstream_clone, upstream); + + IMFTopologyNode_GetNodeType(downstream, &type); + MFCreateTopologyNode(type, &downstream_clone); + IMFTopologyNode_CloneFrom(downstream_clone, downstream); + topology_loader_test_set_input_mediatype(downstream_clone, up_output_mtype, 1); + + IMFTopologyNode_ConnectOutput(upstream_clone, 0, downstream_clone, 0); + + MFCreateTopology(&topology); + topology_loader_add_branch(impl_from_IMFTopology(topology), upstream_clone); + topology_entry = heap_alloc_zero(sizeof(struct list_topologies *)); + topology_entry->topology = impl_from_IMFTopology(topology); + list_add_tail(out_topologies, &topology_entry->entry); + + hr = S_OK; + } + IMFMediaType_Release(up_output_mtype); + } + } + } + + else + { + IMFTopologyNode *upstream_clone, *downstream_clone; + struct list_topologies *up_to_mft_topologies; + struct list list_up_to_mft_topologies; + IMFMediaType *out_upstream; + int count; + + IMFTopologyNode_GetNodeType(upstream, &type); + MFCreateTopologyNode(type, &upstream_clone); + IMFTopologyNode_CloneFrom(upstream_clone, upstream); + + IMFTopologyNode_GetNodeType(downstream, &type); + MFCreateTopologyNode(type, &downstream_clone); + IMFTopologyNode_CloneFrom(downstream_clone, downstream); + + list_init(&list_up_to_mft_topologies); + + count = 0; + while (SUCCEEDED(topology_loader_get_next_output_mediatype(upstream_clone, count++, &out_upstream))) + { + IMFActivate **activate_mfts; + IMFTopologyNode *node_mft; + UINT32 num_activate_mfts; + IMFTransform *mft; + int i; + + /* set current media type so we don't iterate over all available media types in recursive call */ + topology_loader_test_set_output_mediatype(upstream_clone, out_upstream, 1); + + MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node_mft); + + topology_loader_get_mfts(out_upstream, NULL, method, &activate_mfts, &num_activate_mfts); + for (i = 0; i < num_activate_mfts; i++) + { + if (FAILED(hr = IMFActivate_ActivateObject(activate_mfts[i], &IID_IMFTransform, (void **)&mft))) + continue; + IMFTopologyNode_SetObject(node_mft, (IUnknown *)mft); + hr = topology_loader_build_topologies_from_input_mediatype(upstream_clone, NULL, node_mft, MF_CONNECT_DIRECT, &list_up_to_mft_topologies); + } + IMFTopologyNode_Release(node_mft); + IMFMediaType_Release(out_upstream); + } + + /* first try direct connections: up -> mft -> down */ + LIST_FOR_EACH_ENTRY(up_to_mft_topologies, &list_up_to_mft_topologies, struct list_topologies, entry) + { + IMFTopologyNode *current_up_to_mft_topology_upnode, *current_up_to_mft_topology_downnode; + struct list_topologies *mft_to_down_topologies, *safety_mft_to_down; + struct topology *current_up_to_mft_topology; + struct list list_mft_to_down_topologies; + + current_up_to_mft_topology = up_to_mft_topologies->topology; + current_up_to_mft_topology_upnode = ¤t_up_to_mft_topology->nodes.nodes[0]->IMFTopologyNode_iface; + current_up_to_mft_topology_downnode = ¤t_up_to_mft_topology->nodes.nodes[1]->IMFTopologyNode_iface; + + list_init(&list_mft_to_down_topologies); + hr = topology_loader_build_topologies_from_input_mediatype(current_up_to_mft_topology_downnode, NULL, downstream_clone, MF_CONNECT_DIRECT, &list_mft_to_down_topologies); + + LIST_FOR_EACH_ENTRY_SAFE(mft_to_down_topologies, safety_mft_to_down, &list_mft_to_down_topologies, struct list_topologies, entry) + { + IMFTopologyNode *current_mft_to_down_topology_upnode, *current_mft_to_down_topology_upnode_clone; + IMFTopologyNode *current_mft_to_down_topology_downnode, *current_mft_to_down_topology_downnode_clone; + IMFTopologyNode *current_up_to_mft_topology_upnode_clone; + struct topology *current_mft_to_down_topology; + struct list_topologies *out_entry; + IMFTopology *topology; + + current_mft_to_down_topology = mft_to_down_topologies->topology; + current_mft_to_down_topology_upnode = ¤t_mft_to_down_topology->nodes.nodes[0]->IMFTopologyNode_iface; + current_mft_to_down_topology_downnode = ¤t_mft_to_down_topology->nodes.nodes[1]->IMFTopologyNode_iface; + + IMFTopologyNode_GetNodeType(current_up_to_mft_topology_upnode, &type); + MFCreateTopologyNode(type, ¤t_up_to_mft_topology_upnode_clone); + IMFTopologyNode_CloneFrom(current_up_to_mft_topology_upnode_clone, current_up_to_mft_topology_upnode); + + IMFTopologyNode_GetNodeType(current_mft_to_down_topology_upnode, &type); + MFCreateTopologyNode(type, ¤t_mft_to_down_topology_upnode_clone); + IMFTopologyNode_CloneFrom(current_mft_to_down_topology_upnode_clone, current_mft_to_down_topology_upnode); + + IMFTopologyNode_GetNodeType(current_mft_to_down_topology_downnode, &type); + MFCreateTopologyNode(type, ¤t_mft_to_down_topology_downnode_clone); + IMFTopologyNode_CloneFrom(current_mft_to_down_topology_downnode_clone, current_mft_to_down_topology_downnode); + IMFTopologyNode_ConnectOutput(current_up_to_mft_topology_upnode_clone, 0, current_mft_to_down_topology_upnode_clone, 0); + IMFTopologyNode_ConnectOutput(current_mft_to_down_topology_upnode_clone, 0, current_mft_to_down_topology_downnode_clone, 0); + + MFCreateTopology(&topology); + topology_loader_add_branch(impl_from_IMFTopology(topology), current_up_to_mft_topology_upnode_clone); + out_entry = heap_alloc_zero(sizeof(struct list_topologies *)); + out_entry->topology = impl_from_IMFTopology(topology); + list_add_tail(out_topologies, &out_entry->entry); + + list_remove(&mft_to_down_topologies->entry); + heap_free(mft_to_down_topologies); + IMFTopology_Release(¤t_mft_to_down_topology->IMFTopology_iface); + } + } + + LIST_FOR_EACH_ENTRY(up_to_mft_topologies, &list_up_to_mft_topologies, struct list_topologies, entry) + { + IMFTopology_Release(&up_to_mft_topologies->topology->IMFTopology_iface); + heap_free(up_to_mft_topologies); + } + } + + return hr; +} + /* iterate through the branch that starts at source node with id srcid, and try to resolve it */ static HRESULT topology_loader_resolve_branch_connect_nodes(struct topology *topology, TOPOID srcid, MF_CONNECT_METHOD method) { @@ -1991,7 +2499,7 @@ static HRESULT topology_loader_resolve_branch_connect_nodes(struct topology *top /* 'method' argument passed to function only applies to source media types */ if (method == MF_CONNECT_DIRECT) { - hr = S_OK; + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_DIRECT, &list_topologies[i]); if (list_empty(&list_topologies[i])) { IMFTopologyNode_Release(up_clone); @@ -2004,7 +2512,11 @@ static HRESULT topology_loader_resolve_branch_connect_nodes(struct topology *top } else { - hr = S_OK; + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_DIRECT, &list_topologies[0]); + if (list_empty(&list_topologies[0])) + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_ALLOW_DECODER, &list_topologies[0]); + if (list_empty(&list_topologies[0])) + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_ALLOW_CONVERTER, &list_topologies[0]); if (list_empty(&list_topologies[i])) { IMFTopologyNode_Release(up_clone); @@ -2036,6 +2548,12 @@ static HRESULT topology_loader_resolve_branch_connect_nodes(struct topology *top prev_topology = prev_topologies->topology; IMFTopologyNode_CloneFrom(up_clone, &prev_topology->nodes.nodes[prev_topology->nodes.count-1]->IMFTopologyNode_iface);
+ hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_DIRECT, &list_topologies[i]); + if (list_empty(&list_topologies[i])) + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_ALLOW_DECODER, &list_topologies[i]); + if (list_empty(&list_topologies[i])) + hr = topology_loader_build_topologies_from_input_mediatype(up_clone, NULL, down_clone, MF_CONNECT_ALLOW_CONVERTER, &list_topologies[i]); + if (list_empty(&list_topologies[i])) { IMFTopologyNode_Release(up_clone); @@ -2072,6 +2590,24 @@ static HRESULT topology_loader_resolve_branch_connect_nodes(struct topology *top }
out: + if (SUCCEEDED(hr)) + { + int set = 0; + struct list_topologies *curr_topologies; + LIST_FOR_EACH_ENTRY(curr_topologies, &list_topologies[num_lists-1], struct list_topologies, entry) + { + struct topology *curr_topology = curr_topologies->topology; + + if (!set) + { + IMFTopology_CloneFrom(&topology->IMFTopology_iface, &curr_topology->IMFTopology_iface); + set = 1; + } + IMFTopology_Release(&curr_topology->IMFTopology_iface); + break; + } + } + heap_free(list_topologies); return hr; }