From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 17 +--- dlls/mf/topology_loader.c | 162 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 158 insertions(+), 21 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index d56f3ed6325..057daa5dda8 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2292,7 +2292,6 @@ enum loader_test_flags LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM = 0x10000, LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM = 0x20000, LOADER_EXPECT_OPTIONAL_REJECTED = 0x40000, - LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO = 0x80000, }; static void test_topology_loader(void) @@ -2902,7 +2901,7 @@ static void test_topology_loader(void) .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = -1, .source_method = -1, .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add unconnectable optional test MFT and test MFT */ @@ -2910,7 +2909,7 @@ static void test_topology_loader(void) .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280}, .optional_mft_input_type = &video_yuy2_1280, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_OPTIONAL_REJECTED | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_OPTIONAL_REJECTED | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add optional test MFT and test MFT, require test MFT input change for optional */ @@ -2918,7 +2917,7 @@ static void test_topology_loader(void) .mft_input_types = {&video_nv12_1280, &video_yuy2_1280}, .mft_output_types = {&video_nv12_1280}, .optional_mft_output_type = &video_yuy2_1280, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add test MFT and optional test MFT */ @@ -2948,7 +2947,6 @@ static void test_topology_loader(void) /* PCM -> PCM, optional sink */ .input_types = {&audio_pcm_44100}, .output_types = {&audio_pcm_44100}, .sink_method = MF_CONNECT_AS_OPTIONAL, .source_method = -1, .expected_result = MF_E_TOPO_UNSUPPORTED, - .flags = LOADER_TODO, }, { /* PCM -> PCM, optional source is ignored */ @@ -2959,7 +2957,6 @@ static void test_topology_loader(void) /* H264 -> NV12, optional sink */ .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = MF_CONNECT_AS_OPTIONAL | MF_CONNECT_ALLOW_DECODER, .source_method = -1, .expected_result = MF_E_TOPO_UNSUPPORTED, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_TODO, }, { /* H264 -> NV12, optional source is ignored */ @@ -3368,7 +3365,6 @@ static void test_topology_loader(void) if (optional_mft_node) { hr = IMFTopology_GetNodeByID(full_topology, optional_mft_node_id, &mft_node); - todo_wine_if(expect_optional_rejected) ok(hr == (expect_optional_rejected ? MF_E_NOT_FOUND : S_OK), "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { @@ -3402,7 +3398,6 @@ todo_wine { hr = IMFTopology_GetNodeCount(full_topology, &node_count); ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - todo_wine_if(expect_optional_rejected) ok(node_count == count, "Unexpected node count %u.\n", node_count); hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); @@ -3437,7 +3432,6 @@ todo_wine { ok(!!test_transform->set_output_sequence_id, "Test transform output sequence id not set.\n"); if (test->flags & LOADER_ADD_OPTIONAL_TEST_MFT_UPSTREAM) { - todo_wine ok(optional_transform->set_input_sequence_id > test_transform->set_input_sequence_id, "Optional transform input was not configured after the non-optional one.\n"); if (optional_transform->set_output_sequence_id) @@ -3445,14 +3439,12 @@ todo_wine { /* Non-optional transform is connected to its downstream node after the optional * node is inserted upstream, i.e. after each branch is connected, its optional * nodes are inserted before the next branch is connected. */ - todo_wine ok(optional_transform->set_output_sequence_id < test_transform->set_output_sequence_id, "Optional transform output was not configured before the non-optional one.\n"); } } else { - todo_wine ok(optional_transform->set_input_sequence_id > handler.is_supported_sequence_id, "Optional transform input was not configured after the sink input.\n"); } @@ -3626,8 +3618,7 @@ todo_wine { if (test_transform) { - todo_wine_if(test->flags & LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO) - ok(test_transform->input_enum_complete == !!(test->flags & (LOADER_EXPECT_MFT_INPUT_ENUMERATED | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO)), + ok(test_transform->input_enum_complete == !!(test->flags & LOADER_EXPECT_MFT_INPUT_ENUMERATED), "got transform input_enum_complete %u\n", test_transform->input_enum_complete); todo_wine_if(test_transform->output_enum_complete && (test->flags & LOADER_TODO)) ok(test_transform->output_enum_complete == !!(test->flags & LOADER_EXPECT_MFT_OUTPUT_ENUMERATED), diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 4ec8100241a..89d52cd7c3b 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -145,7 +145,7 @@ struct topology_branch IMFTopologyNode *node; DWORD stream; IMFMediaTypeHandler *handler; - } up, down; + } up, down, optional; struct list entry; }; @@ -170,7 +170,8 @@ static const char *debugstr_topology_branch(struct topology_branch *branch) } static HRESULT topology_branch_create(IMFTopologyNode *source, DWORD source_stream, - IMFTopologyNode *sink, DWORD sink_stream, struct topology_branch **out) + IMFTopologyNode *optional, DWORD optional_stream, IMFTopologyNode *sink, DWORD sink_stream, + struct topology_branch **out) { IMFMediaTypeHandler *source_handler, *sink_handler; struct topology_branch *branch; @@ -197,6 +198,11 @@ static HRESULT topology_branch_create(IMFTopologyNode *source, DWORD source_stre IMFTopologyNode_AddRef((branch->down.node = sink)); branch->down.stream = sink_stream; branch->down.handler = sink_handler; + if (optional) + { + IMFTopologyNode_AddRef((branch->optional.node = optional)); + branch->optional.stream = optional_stream; + } *out = branch; return S_OK; @@ -208,6 +214,8 @@ static void topology_branch_destroy(struct topology_branch *branch) IMFMediaTypeHandler_Release(branch->up.handler); IMFTopologyNode_Release(branch->down.node); IMFMediaTypeHandler_Release(branch->down.handler); + if (branch->optional.node) + IMFTopologyNode_Release(branch->optional.node); free(branch); } @@ -216,9 +224,9 @@ static HRESULT topology_branch_create_indirect(struct topology_branch *branch, { HRESULT hr; - if (FAILED(hr = topology_branch_create(branch->up.node, branch->up.stream, node, 0, up))) + if (FAILED(hr = topology_branch_create(branch->up.node, branch->up.stream, NULL, 0, node, 0, up))) return hr; - if (FAILED(hr = topology_branch_create(node, 0, branch->down.node, branch->down.stream, down))) + if (FAILED(hr = topology_branch_create(node, 0, NULL, 0, branch->down.node, branch->down.stream, down))) topology_branch_destroy(*up); return hr; @@ -259,22 +267,86 @@ static UINT32 topology_node_get_connect_method_semantics(IMFTopologyNode *node) return method; } +static HRESULT topology_node_find_down_node(IMFTopologyNode *node, DWORD stream, IMFTopologyNode **optional_node, + DWORD *optional_stream, IMFTopologyNode **down_node, DWORD *down_stream) +{ + IMFTopologyNode *optional = NULL; + DWORD opt_stream = 0; + HRESULT hr; + + *optional_node = NULL; + *optional_stream = 0; + *down_node = NULL; + *down_stream = 0; + + IMFTopologyNode_AddRef(node); + + for (;;) + { + IMFTopologyNode *next_node; + + if (FAILED(hr = IMFTopologyNode_GetOutput(node, stream, &next_node, &stream))) + break; + + IMFTopologyNode_Release(node); + node = next_node; + + if (!(topology_node_get_connect_method_semantics(node) & MF_CONNECT_AS_OPTIONAL)) + { + *optional_node = optional; + *optional_stream = opt_stream; + *down_node = node; + *down_stream = stream; + return S_OK; + } + + if (topology_node_get_type(node) == MF_TOPOLOGY_OUTPUT_NODE) + break; + + if (!optional) + { + IMFTopologyNode_AddRef(optional = node); + opt_stream = stream; + } + else + { + FIXME("Multiple optional nodes per branch are not supported.\n"); + } + } + + if (optional) + IMFTopologyNode_Release(optional); + IMFTopologyNode_Release(node); + return hr; +} + static HRESULT topology_node_list_branches(IMFTopologyNode *node, struct list *branches) { + IMFTopologyNode *down_node = NULL, *optional_node = NULL; + DWORD i, count, down_stream, optional_stream = 0; struct topology_branch *branch; - DWORD i, count, down_stream; - IMFTopologyNode *down_node; + UINT32 method; HRESULT hr; + method = topology_node_get_connect_method_semantics(node); + + if (method & MF_CONNECT_AS_OPTIONAL_BRANCH) + FIXME("Optional branches are not supported.\n"); + + if (method & MF_CONNECT_AS_OPTIONAL) + return S_OK; + hr = IMFTopologyNode_GetOutputCount(node, &count); for (i = 0; SUCCEEDED(hr) && i < count; ++i) { - if (FAILED(IMFTopologyNode_GetOutput(node, i, &down_node, &down_stream))) + if (FAILED(topology_node_find_down_node(node, i, &optional_node, &optional_stream, &down_node, &down_stream)) || !down_node) continue; - if (SUCCEEDED(hr = topology_branch_create(node, i, down_node, down_stream, &branch))) + if (SUCCEEDED(hr = topology_branch_create(node, i, optional_node, optional_stream, down_node, down_stream, &branch))) list_add_tail(branches, &branch->entry); + if (optional_node) + IMFTopologyNode_Release(optional_node); IMFTopologyNode_Release(down_node); } @@ -734,6 +806,76 @@ static HRESULT topology_branch_foreach_up_types(IMFTopology *topology, enum conn return hr; } +static HRESULT topology_branch_connect_optional(struct topoloader_context *context, struct topology_branch *branch) +{ + DWORD up_stream, optional_stream = branch->optional.stream; + IMFTopologyNode *optional_node, *up_node = NULL; + struct topology_branch *optional = NULL; + IMFTransform *transform = NULL; + HRESULT hr, connect_hr = S_OK; + IMFMediaTypeHandler *handler; + IMFMediaType *type = NULL; + + if (!branch->optional.node) + return S_OK; + + TRACE("topology %p, branch %s.\n", context->output_topology, debugstr_topology_branch(branch)); + + if (topology_node_get_type(branch->optional.node) != MF_TOPOLOGY_TRANSFORM_NODE) + { + WARN("Optional node is not a transform.\n"); + return S_OK; + } + + if (FAILED(hr = topology_loader_clone_node(context, branch->optional.node, &optional_node))) + return hr; + + /* The loader may have inserted one or more transforms upstream. Only try inserting the optional node + * downstream of them. Support for upstream insertion is untested in native, but seems unlikely to work. */ + if (FAILED(hr = IMFTopologyNode_GetInput(branch->down.node, branch->down.stream, &up_node, &up_stream))) + { + ERR("Failed to get input.\n"); + goto done; + } + + if (FAILED(hr = topology_node_get_type_handler(up_node, up_stream, TRUE, &handler))) + goto done; + + if (FAILED(hr = media_type_handler_get_current(handler, &type))) + hr = media_type_handler_get_type(handler, 0, &type); + + IMFMediaTypeHandler_Release(handler); + + if (FAILED(hr) || FAILED(hr = topology_node_get_object(optional_node, &IID_IMFTransform, (void **)&transform))) + goto done; + + /* Optional node connections are restricted to the current or first upstream type. + * The up node is not reconfigured to allow connection even if possible. */ + if (FAILED(connect_hr = IMFTransform_SetInputType(transform, optional_stream, type, 0))) + goto done; + + if (FAILED(hr = topology_branch_create(branch->optional.node, branch->optional.stream, NULL, 0, branch->down.node, branch->down.stream, &optional))) + goto done; + connect_hr = topology_branch_foreach_up_types(context->output_topology, CONNECT_DIRECT, optional, type); + topology_branch_destroy(optional); + +done: + if (FAILED(connect_hr)) + WARN("Failed to insert optional node %p, hr %#lx.\n", branch->optional.node, connect_hr); + + if ((FAILED(hr) || FAILED(connect_hr)) && FAILED(IMFTopology_RemoveNode(context->output_topology, optional_node))) + ERR("Failed to remove node.\n"); + + if (transform) + IMFTransform_Release(transform); + if (type) + IMFMediaType_Release(type); + if (up_node) + IMFTopologyNode_Release(up_node); + IMFTopologyNode_Release(optional_node); + return hr; +} + static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_method method_mask, struct topology_branch *branch, IMFMediaType *upstream) { @@ -778,7 +920,11 @@ static HRESULT topology_loader_resolve_branches(struct topoloader_context *conte if (FAILED(hr = topology_branch_clone_nodes(context, branch))) WARN("Failed to clone nodes for branch %s\n", debugstr_topology_branch(branch)); else + { hr = topology_branch_connect(context->output_topology, method_mask, branch, NULL); + if (SUCCEEDED(hr)) + hr = topology_branch_connect_optional(context, branch); + } topology_branch_destroy(branch); if (FAILED(hr)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645