From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 29 ++++++------- dlls/mf/topology_loader.c | 90 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 4d96fcb5e87..6df7da89635 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2291,7 +2291,6 @@ enum loader_test_flags LOADER_EXPECT_MFT_INPUT_ENUMERATED = 0x8000, LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM = 0x10000, LOADER_EXPECT_MFT_OUTPUT_ENUMERATED_TODO = 0x20000, - LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO = 0x40000, }; static void test_topology_loader(void) @@ -2513,6 +2512,7 @@ static void test_topology_loader(void) unsigned int expected_output_index; unsigned int optional_mft_count; BOOL expect_optional_mft_rejected[2]; + BOOL expect_optional_mft_accepted_todo; unsigned int flags; GUID decoder_class; GUID converter_class; @@ -2903,7 +2903,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 +2911,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 +2920,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 +2929,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 +2938,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 +2947,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 +2956,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 */ @@ -2980,7 +2980,7 @@ static void test_topology_loader(void) .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280, &video_yuy2_1280}, .optional_mft_input_types = {&video_yuy2_1280}, - .optional_mft_count = 1, + .optional_mft_count = 1, .expect_optional_mft_accepted_todo = TRUE, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .flags = LOADER_ADD_TEST_MFT | LOADER_NO_CURRENT_OUTPUT | LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED | LOADER_EXPECT_MFT_OUTPUT_ENUMERATED_TODO, }, @@ -3431,7 +3431,7 @@ 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]) + todo_wine_if(test->expect_optional_mft_accepted_todo) 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]; @@ -3467,7 +3467,7 @@ 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) + todo_wine_if(test->expect_optional_mft_accepted_todo) ok(node_count == count, "Unexpected node count %u.\n", node_count); hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); @@ -3502,13 +3502,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) @@ -3672,7 +3670,7 @@ 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) + todo_wine_if(test->expect_optional_mft_accepted_todo) ok(node_count == count, "Unexpected node count %u.\n", node_count); ref = IMFTopology_Release(topology2); @@ -3695,8 +3693,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->flags & LOADER_EXPECT_MFT_OUTPUT_ENUMERATED_TODO) || (test_transform->output_enum_complete && (test->flags & LOADER_TODO))) ok(test_transform->output_enum_complete == !!(test->flags & (LOADER_EXPECT_MFT_OUTPUT_ENUMERATED | LOADER_EXPECT_MFT_OUTPUT_ENUMERATED_TODO)), diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 187789bad57..a44289c5f86 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -221,16 +221,40 @@ static HRESULT topology_branch_create_for_output(IMFTopologyNode *node, DWORD st { IMFTopologyNode *down_node; DWORD down_stream; + UINT32 method; HRESULT hr; if (FAILED(hr = IMFTopologyNode_GetOutput(node, stream, &down_node, &down_stream))) return hr; + while (SUCCEEDED(IMFTopologyNode_GetUINT32(down_node, &MF_TOPONODE_CONNECT_METHOD, &method)) + && (method & MF_CONNECT_AS_OPTIONAL)) + { + IMFTopologyNode *tmp = down_node; + hr = IMFTopologyNode_GetOutput(tmp, 0, &down_node, &down_stream); + IMFTopologyNode_Release(tmp); + if (FAILED(hr)) + return hr; + } hr = topology_branch_create(node, stream, down_node, down_stream, out); IMFTopologyNode_Release(down_node); return hr; } +/* create a branch for a given node input stream */ +static HRESULT topology_branch_create_for_input(IMFTopologyNode *node, DWORD stream, struct topology_branch **out) +{ + IMFTopologyNode *up_node; + DWORD up_stream; + HRESULT hr; + + if (FAILED(hr = IMFTopologyNode_GetInput(node, stream, &up_node, &up_stream))) + return hr; + hr = topology_branch_create(up_node, up_stream, node, stream, out); + IMFTopologyNode_Release(up_node); + return hr; +} + static HRESULT topology_branch_create_cloned(IMFTopology *topology, IMFTopologyNode *up_node, DWORD up_stream, IMFTopologyNode *down_node, DWORD down_stream, struct topology_branch **out) { @@ -251,6 +275,18 @@ static HRESULT topology_branch_create_cloned(IMFTopology *topology, IMFTopologyN return hr; } +static HRESULT topology_branch_create_optional(IMFTopology *topology, struct topology_branch *branch, + IMFTopologyNode *optional, DWORD stream, struct topology_branch **up, struct topology_branch **down) +{ + HRESULT hr; + + if (SUCCEEDED(hr = topology_branch_create_cloned(topology, branch->up.node, branch->up.stream, optional, stream, up)) + && FAILED(hr = topology_branch_create_cloned(topology, optional, 0, branch->down.node, branch->down.stream, down))) + topology_branch_destroy(*up); + + return hr; +} + static void media_type_try_copy_attr(IMFMediaType *dst, IMFMediaType *src, const GUID *attr, HRESULT *hr) { PROPVARIANT value; @@ -737,11 +773,58 @@ static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_metho return hr; } +static HRESULT topology_branch_connect_optional_chain(IMFTopology *topology, struct topology_branch *branch, + IMFTopologyNode *down_node, DWORD down_stream) +{ + struct topology_branch *current, *up_branch, *down_branch; + IMFTopologyNode *node; + DWORD stream; + HRESULT hr; + + TRACE("topology %p, branch %s, down_node %p, down_stream %lu\n", topology, debugstr_topology_branch(branch), + down_node, down_stream); + + if (FAILED(hr = IMFTopologyNode_GetOutput(branch->up.node, branch->up.stream, &node, &stream))) + return hr; + while (SUCCEEDED(hr) && node != branch->down.node) + { + IMFTopologyNode *tmp = node; + + /* 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 = topology_branch_create_for_input(down_node, down_stream, ¤t))) + break; + if (SUCCEEDED(hr = topology_branch_create_optional(topology, current, node, stream, &up_branch, &down_branch))) + { + if (FAILED(hr = topology_branch_connect(topology, CONNECT_DIRECT, up_branch, NULL)) + || FAILED(hr = topology_branch_connect(topology, CONNECT_DIRECT, down_branch, NULL))) + { + if (FAILED(hr = topology_branch_connect(topology, CONNECT_DIRECT, current, NULL))) + WARN("Failed to restore previous branch %s\n", debugstr_topology_branch(current)); + IMFTopology_RemoveNode(topology, down_branch->up.node); + } + topology_branch_destroy(down_branch); + topology_branch_destroy(up_branch); + } + topology_branch_destroy(current); + + hr = IMFTopologyNode_GetOutput(tmp, 0, &node, &stream); + IMFTopologyNode_Release(tmp); + if (FAILED(hr)) + return hr; + } + IMFTopologyNode_Release(node); + + TRACE("returning %#lx\n", hr); + return hr; +} + static HRESULT topology_loader_resolve_node(IMFTopology *topology, IMFTopologyNode *node) { enum connect_method method_mask = connect_method_from_mf(MF_CONNECT_ALLOW_DECODER); struct topology_branch *branch, *cloned; DWORD output_count; + UINT32 method; HRESULT hr; TRACE("topology %p, node %p\n", topology, node); @@ -749,12 +832,17 @@ static HRESULT topology_loader_resolve_node(IMFTopology *topology, IMFTopologyNo if (SUCCEEDED(hr = IMFTopologyNode_GetOutputCount(node, &output_count)) && !output_count) return topology_node_get_type(node) == MF_TOPOLOGY_OUTPUT_NODE ? S_OK : MF_E_TOPO_UNSUPPORTED; + if (SUCCEEDED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, &method)) + && (method & MF_CONNECT_AS_OPTIONAL_BRANCH)) + FIXME("MF_CONNECT_AS_OPTIONAL_BRANCH not implemented\n"); + for (UINT i = 0; SUCCEEDED(hr) && SUCCEEDED(topology_branch_create_for_output(node, i, &branch)); ++i) { if (SUCCEEDED(hr = topology_branch_create_cloned(topology, branch->up.node, branch->up.stream, branch->down.node, branch->down.stream, &cloned))) { - hr = topology_branch_connect(topology, method_mask, cloned, NULL); + if (SUCCEEDED(hr = topology_branch_connect(topology, method_mask, cloned, NULL))) + topology_branch_connect_optional_chain(topology, branch, cloned->down.node, cloned->down.stream); topology_branch_destroy(cloned); } if (SUCCEEDED(hr)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10791