From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 25 ++---- dlls/mf/topology_loader.c | 185 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 188 insertions(+), 22 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 1afc5fc5f6e..a0ad1b5464a 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2290,7 +2290,6 @@ enum loader_test_flags LOADER_EXPECT_MFT_OUTPUT_ENUMERATED = 0x4000, LOADER_EXPECT_MFT_INPUT_ENUMERATED = 0x8000, LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM = 0x10000, - LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO = 0x20000, }; static void test_topology_loader(void) @@ -2903,7 +2902,7 @@ static void test_topology_loader(void) .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280}, .optional_mft_count = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add two optional test MFTs and test MFT */ @@ -2911,7 +2910,7 @@ static void test_topology_loader(void) .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280}, .optional_mft_count = 2, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add unconnectable optional test MFT and test MFT */ @@ -2920,7 +2919,7 @@ static void test_topology_loader(void) .optional_mft_input_types = {&video_yuy2_1280}, .optional_mft_count = 1, .expect_optional_mft_rejected = {TRUE}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add unconnectable optional test MFT, connectable optional test MFT and test MFT */ @@ -2929,7 +2928,7 @@ static void test_topology_loader(void) .optional_mft_input_types = {&video_yuy2_1280, &video_nv12_1280}, .optional_mft_count = 2, .expect_optional_mft_rejected = {TRUE, FALSE}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* #60 H264 -> NV12, add connectable optional test MFT, unconnectable optional test MFT and test MFT */ @@ -2938,7 +2937,7 @@ static void test_topology_loader(void) .optional_mft_input_types = {&video_nv12_1280, &video_yuy2_1280}, .optional_mft_count = 2, .expect_optional_mft_rejected = {FALSE, TRUE}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add two unconnectable optional test MFTs and test MFT */ @@ -2947,7 +2946,7 @@ static void test_topology_loader(void) .optional_mft_input_types = {&video_yuy2_1280, &video_yuy2_1280}, .optional_mft_count = 2, .expect_optional_mft_rejected = {TRUE, TRUE}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add optional test MFT and test MFT, require test MFT input change for optional */ @@ -2956,7 +2955,7 @@ static void test_topology_loader(void) .optional_mft_output_type = &video_yuy2_1280, .optional_mft_count = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add test MFT and optional test MFT */ @@ -2989,7 +2988,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 */ @@ -3000,7 +2998,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 */ @@ -3424,7 +3421,6 @@ static void test_topology_loader(void) for (j = 0; j < test->optional_mft_count; ++j) { hr = IMFTopology_GetNodeByID(full_topology, optional_mft_node_id[j], &mft_node); - todo_wine_if(test->expect_optional_mft_rejected[j]) ok(hr == (test->expect_optional_mft_rejected[j] ? MF_E_NOT_FOUND : S_OK), "Unexpected hr %#lx.\n", hr); expect_optional_rejected |= test->expect_optional_mft_rejected[j]; @@ -3460,7 +3456,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); @@ -3495,13 +3490,11 @@ 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_DOWNSTREAM) { - todo_wine ok(optional_transforms[0]->set_input_sequence_id > handler.is_supported_sequence_id, "Optional transform input was not configured after the sink input.\n"); } else { - todo_wine ok(optional_transforms[0]->set_input_sequence_id > test_transform->set_input_sequence_id, "Optional transform input was not configured after the non-optional one.\n"); if (optional_transforms[0]->set_output_sequence_id) @@ -3665,7 +3658,6 @@ todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFTopology_GetNodeCount(topology2, &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); ref = IMFTopology_Release(topology2); @@ -3688,8 +3680,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 6b4aa5c1f50..59dc4265656 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -201,6 +201,25 @@ static HRESULT topology_branch_create(IMFTopologyNode *source, DWORD source_stre return S_OK; } +static HRESULT topology_branch_set_source_node(struct topology_branch *branch, + IMFTopologyNode *source, DWORD source_stream) +{ + IMFMediaTypeHandler *source_handler; + HRESULT hr; + + if (SUCCEEDED(hr = topology_node_get_type_handler(source, source_stream, TRUE, &source_handler))) + { + IMFTopologyNode_Release(branch->up.node); + IMFMediaTypeHandler_Release(branch->up.handler); + + IMFTopologyNode_AddRef((branch->up.node = source)); + branch->up.stream = source_stream; + branch->up.handler = source_handler; + } + + return hr; +} + static void topology_branch_destroy(struct topology_branch *branch) { IMFTopologyNode_Release(branch->up.node); @@ -707,6 +726,51 @@ static HRESULT topology_branch_foreach_up_types(IMFTopology *topology, enum conn return hr; } +static HRESULT topology_branch_connect_optional(struct topoloader_context *context, + IMFTopologyNode *up_node, DWORD up_stream, struct topology_branch *branch, BOOL *accepted) +{ + IMFTopologyNode *optional_node = branch->up.node; + IMFTransform *transform = NULL; + HRESULT hr, connect_hr = S_OK; + IMFMediaTypeHandler *handler; + IMFMediaType *type = NULL; + + TRACE("topology %p, branch %s.\n", context->output_topology, debugstr_topology_branch(branch)); + + if (topology_node_get_type(optional_node) != MF_TOPOLOGY_TRANSFORM_NODE) + { + WARN("Optional node is not a transform.\n"); + return S_OK; + } + + 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, branch->up.stream, type, 0))) + goto done; + + connect_hr = topology_branch_foreach_up_types(context->output_topology, CONNECT_DIRECT, branch, type); + if (!(*accepted = SUCCEEDED(connect_hr))) + WARN("Rejected optional node %p, hr %#lx.\n", optional_node, connect_hr); + +done: + if (transform) + IMFTransform_Release(transform); + if (type) + IMFMediaType_Release(type); + return hr; +} + static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_method method_mask, struct topology_branch *branch, IMFMediaType *upstream) { @@ -738,23 +802,76 @@ static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_metho return hr; } +static HRESULT topology_node_find_down_node(IMFTopologyNode *node, DWORD stream, IMFTopologyNode **optional_node, + DWORD *optional_stream, IMFTopologyNode **down_node, DWORD *down_stream, struct topoloader_context *context) +{ + 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; + + context->resolved_count++; + + 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) + { + hr = MF_E_TOPO_UNSUPPORTED; + break; + } + + if (!optional) + { + IMFTopologyNode_AddRef(optional = node); + opt_stream = stream; + } + } + + if (optional) + IMFTopologyNode_Release(optional); + IMFTopologyNode_Release(node); + return hr; +} + static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *context, IMFTopologyNode *node, DWORD stream) { enum connect_method method_mask = connect_method_from_mf(MF_CONNECT_ALLOW_DECODER); + IMFTopologyNode *down_node = NULL, *optional_node = NULL; struct topology_branch *branch = NULL; - IMFTopologyNode *down_node = NULL; - DWORD down_stream; + DWORD down_stream, optional_stream; HRESULT hr = S_OK; DWORD count; if (topology_node_get_connect_method_semantics(node) & MF_CONNECT_AS_OPTIONAL_BRANCH) FIXME("Optional branches are not supported.\n"); - if (FAILED(hr = IMFTopologyNode_GetOutput(node, stream, &down_node, &down_stream))) + if (FAILED(hr = topology_node_find_down_node(node, stream, &optional_node, &optional_stream, + &down_node, &down_stream, context))) return hr; - context->resolved_count++; - if (FAILED(hr = topology_branch_create(node, stream, down_node, down_stream, &branch))) goto done; @@ -767,6 +884,62 @@ static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *conte if (FAILED(hr = topology_branch_connect(context->output_topology, method_mask, branch, NULL))) goto done; + if (optional_node) + { + IMFTopologyNode *up_node; + DWORD up_stream; + + /* 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; + } + + do + { + IMFTopologyNode *ret_node, *optional_clone; + BOOL accepted = FALSE; + DWORD ret_stream; + + if (FAILED(hr = topology_loader_clone_node(context, optional_node, &optional_clone))) + break; + + if (SUCCEEDED(hr = topology_branch_set_source_node(branch, optional_clone, optional_stream))) + hr = topology_branch_connect_optional(context, up_node, up_stream, branch, &accepted); + + if (accepted) + { + hr = IMFTopologyNode_ConnectOutput(up_node, up_stream, optional_clone, optional_stream); + IMFTopologyNode_Release(up_node); + IMFTopologyNode_AddRef(up_node = optional_clone); + up_stream = optional_stream; + } + else + { + if (FAILED(IMFTopology_RemoveNode(context->output_topology, optional_clone))) + ERR("Failed to remove node.\n"); + } + + IMFTopologyNode_Release(optional_clone); + + if (SUCCEEDED(hr) + && SUCCEEDED(hr = IMFTopologyNode_GetOutput(optional_node, optional_stream, &ret_node, &ret_stream))) + { + IMFTopologyNode_Release(optional_node); + optional_node = ret_node; + optional_stream = ret_stream; + + if (!(topology_node_get_connect_method_semantics(optional_node) & MF_CONNECT_AS_OPTIONAL)) + break; + } + } + while (SUCCEEDED(hr)); + + IMFTopologyNode_Release(up_node); + } + topology_branch_destroy(branch); branch = NULL; @@ -776,6 +949,8 @@ static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *conte done: if (branch) topology_branch_destroy(branch); + if (optional_node) + IMFTopologyNode_Release(optional_node); IMFTopologyNode_Release(down_node); return hr; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645