[PATCH v7 0/5] MR10645: mf/topology_loader: Insert optional nodes after each branch is connected.
New tests show each optional node is connected after the nodes on either side are connected. This is an issue in the game Psycho Sleuth (at least in the demo) because it uses a transform which must have its input types enumerated before it will work correctly. This occurs when a decoder is inserted before the transform, but an optional node lies immediately upstream of the transform, and if optional nodes are not handled correctly, the decoder is initially connected to the optional instead of being inserted later. A couple of other issues revealed while making new tests are fixed here too. -- v7: mf/topology_loader: Remove the fixme from topology_loader_Load(). mf/topology_loader: Try to insert optional nodes after resolving the surrounding branch. mf/topology_loader: Resolve topologies by walking down from each source node. mf/topology_loader: Introduce a helper to get the connection semantic flags. mf/tests: Test optional topology nodes. https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 324 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 309 insertions(+), 15 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 2270d7d6b0e..6c86d23fbe1 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -247,6 +247,8 @@ static void init_sink_node(IMFStreamSink *stream_sink, MF_CONNECT_METHOD method, } } +static LONG sequence_count; + struct test_transform { IMFTransform IMFTransform_iface; @@ -258,6 +260,8 @@ struct test_transform UINT input_count; IMFMediaType **input_types; IMFMediaType *input_type; + LONG set_input_sequence_id; + LONG set_output_sequence_id; UINT output_count; IMFMediaType **output_types; @@ -267,6 +271,7 @@ struct test_transform BOOL input_type_set; BOOL output_type_set; BOOL set_input_nulls_output; + BOOL validate_output_type; IDirect3DDeviceManager9 *expect_d3d9_device_manager; BOOL got_d3d9_device_manager; @@ -437,6 +442,9 @@ static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, if (type) { + if (!transform->set_input_sequence_id) + transform->set_input_sequence_id = InterlockedIncrement(&sequence_count); + for (i = 0; i < transform->input_count; ++i) { if (IMFMediaType_Compare(transform->input_types[i], (IMFAttributes *)type, @@ -473,10 +481,25 @@ static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id struct test_transform *transform = test_transform_from_IMFTransform(iface); if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; + if (type && transform->validate_output_type) + { + BOOL result; + UINT i; + for (i = 0; i < transform->output_count; ++i) + { + if (IMFMediaType_Compare(transform->output_types[i], (IMFAttributes *)type, + MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) + break; + } + if (i == transform->output_count) + return MF_E_INVALIDMEDIATYPE; + } if (transform->output_type) IMFMediaType_Release(transform->output_type); if ((transform->output_type = type)) { + if (!transform->set_output_sequence_id) + transform->set_output_sequence_id = InterlockedIncrement(&sequence_count); transform->output_type_set = TRUE; IMFMediaType_AddRef(transform->output_type); } @@ -1739,7 +1762,7 @@ struct test_handler ULONG media_types_count; IMFMediaType **media_types; BOOL enum_complete; - BOOL is_supported_called; + LONG is_supported_sequence_id; }; static struct test_handler *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) @@ -1777,7 +1800,8 @@ static HRESULT WINAPI test_handler_IsMediaTypeSupported(IMFMediaTypeHandler *ifa BOOL result; ULONG i; - impl->is_supported_called = TRUE; + if (!impl->is_supported_sequence_id) + impl->is_supported_sequence_id = InterlockedIncrement(&sequence_count); if (out_type) *out_type = NULL; @@ -2265,6 +2289,9 @@ enum loader_test_flags LOADER_TEST_MFT_EXPECT_CONVERTER = 0x2000, LOADER_EXPECT_MFT_OUTPUT_ENUMERATED = 0x4000, 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) @@ -2473,6 +2500,8 @@ static void test_topology_loader(void) const media_type_desc *output_types[2]; const media_type_desc *mft_input_types[3]; const media_type_desc *mft_output_types[5]; + const media_type_desc *optional_mft_input_types[2]; /* one per node */ + const media_type_desc *optional_mft_output_type; const media_type_desc *current_input; const media_type_desc *mft_current_output; const media_type_desc *decoded_type; @@ -2482,6 +2511,8 @@ static void test_topology_loader(void) HRESULT expected_result; unsigned int mft_current_input_1based_index; unsigned int expected_output_index; + unsigned int optional_mft_count; + BOOL expect_optional_mft_rejected[2]; unsigned int flags; GUID decoder_class; GUID converter_class; @@ -2865,10 +2896,138 @@ static void test_topology_loader(void) .expected_result = S_OK, .flags = LOADER_ADD_TEST_MFT, }, + + { + /* H264 -> NV12, add optional test MFT and test MFT */ + .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}, + .optional_mft_count = 1, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + }, + { + /* H264 -> NV12, add two optional test MFTs and test MFT */ + .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}, + .optional_mft_count = 2, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + }, + { + /* H264 -> NV12, add unconnectable optional test MFT and test MFT */ + .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}, + .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, + }, + { + /* H264 -> NV12, add unconnectable optional test MFT, connectable optional test MFT and test MFT */ + .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}, + .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, + }, + { + /* #60 H264 -> NV12, add connectable optional test MFT, unconnectable optional test MFT and test MFT */ + .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}, + .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, + }, + { + /* H264 -> NV12, add two unconnectable optional test MFTs and test MFT */ + .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}, + .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, + }, + { + /* H264 -> NV12, add optional test MFT and test MFT, require test MFT input change for optional */ + .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = -1, .source_method = -1, + .mft_input_types = {&video_nv12_1280, &video_yuy2_1280}, .mft_output_types = {&video_nv12_1280}, + .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, + }, + { + /* H264 -> NV12, add test MFT and optional test MFT */ + .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}, + .optional_mft_count = 1, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, + .flags = LOADER_ADD_TEST_MFT | LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED, + }, + { + /* H264 -> NV12, add test MFT and optional test MFT, require sink input change for optional */ + .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280, &video_yuy2_1280}, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .mft_input_types = {&video_nv12_1280}, .mft_output_types = {&video_nv12_1280}, + .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_NO_CURRENT_OUTPUT | LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED, + }, + { + /* H264 -> NV12, add test MFT and optional test MFT, require test MFT output change for optional */ + .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, + .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, + }, + { + /* H264 -> NV12, add test MFT and unconnectable optional test MFT, converter is not added for optional connection */ + .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}, + .optional_mft_output_type = &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_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM | LOADER_EXPECT_MFT_INPUT_ENUMERATED, + }, + + { + /* 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 */ + .input_types = {&audio_pcm_44100}, .output_types = {&audio_pcm_44100}, .sink_method = -1, .source_method = MF_CONNECT_AS_OPTIONAL, + .expected_result = S_OK, + }, + { + /* 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, + }, + { + /* #70 H264 -> NV12, optional source is ignored */ + .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = -1, .source_method = MF_CONNECT_AS_OPTIONAL, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, + }, + { + /* H264 -> NV12, add test MFT, optional source is ignored */ + .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = -1, .source_method = MF_CONNECT_AS_OPTIONAL, + .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_EXPECT_MFT_INPUT_ENUMERATED, + }, }; IMFTopologyNode *src_node, *sink_node, *src_node2, *sink_node2, *mft_node; IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); + TOPOID node_id, oldtopoid, newtopoid, optional_mft_node_id[2]; IMFMediaType *media_type, *input_types[2], *output_types[2]; struct test_stream_sink stream_sink = test_stream_sink; IMFTopology *topology, *topology2, *full_topology; @@ -2883,7 +3042,6 @@ static void test_topology_loader(void) IMFTopoLoader *loader; IUnknown *node_object; WORD node_count; - TOPOID node_id, oldtopoid, newtopoid; DWORD index; HRESULT hr; BOOL ret; @@ -3009,8 +3167,10 @@ static void test_topology_loader(void) for (i = 0; i < ARRAY_SIZE(loader_tests); ++i) { + struct test_transform *test_transform = NULL, *optional_transforms[2] = {0}; const struct loader_test *test = &loader_tests[i]; - struct test_transform *test_transform = NULL; + IMFTopologyNode *optional_mft_nodes[2] = {0}; + IMFMediaType *optional_mft_types[2] = {0}; IMFMediaType *mft_output_types[4] = {0}; IMFMediaType *mft_input_types[2] = {0}; @@ -3088,6 +3248,40 @@ static void test_topology_loader(void) test_transform_create(input_count, mft_input_types, output_count, mft_output_types, FALSE, &transform); test_transform = test_transform_from_IMFTransform(transform); IMFTransform_AddRef(transform); + + if (test->optional_mft_count) + { + const media_type_desc *optional_desc; + IMFTransform *optional_mft; + + for (j = 0; j < test->optional_mft_count; j++) + { + IMFMediaType **optional_input_types = &mft_input_types[0], **optional_output_types = &mft_input_types[0]; + + optional_desc = test->optional_mft_input_types[j] ? test->optional_mft_input_types[j] : test->optional_mft_output_type; + if (optional_desc) + { + hr = MFCreateMediaType(&optional_mft_types[j]); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + init_media_type(optional_mft_types[j], *optional_desc, -1); + if (test->optional_mft_input_types[j]) + optional_input_types = &optional_mft_types[j]; + else + optional_output_types = &optional_mft_types[j]; + } + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &optional_mft_nodes[j]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT32(optional_mft_nodes[j], &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER | MF_CONNECT_AS_OPTIONAL); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + + test_transform_create(1, optional_input_types, 1, optional_output_types, FALSE, &optional_mft); + hr = IMFTopologyNode_SetObject(optional_mft_nodes[j], (IUnknown *)optional_mft); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + optional_transforms[j] = test_transform_from_IMFTransform(optional_mft); + optional_transforms[j]->validate_output_type = !!test->optional_mft_output_type; + } + } } else { @@ -3126,14 +3320,56 @@ static void test_topology_loader(void) { test_transform->input_type_set = FALSE; test_transform->output_type_set = FALSE; + test_transform->set_input_sequence_id = 0; + test_transform->set_output_sequence_id = 0; } hr = IMFTopology_AddNode(topology, mft_node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + if (test->optional_mft_count) + { + for (j = 0; j < test->optional_mft_count; j++) + { + hr = IMFTopology_AddNode(topology, optional_mft_nodes[j]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + if (test->flags & LOADER_ADD_OPTIONAL_TEST_MFT_DOWNSTREAM) + { + hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, optional_mft_nodes[0], 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(optional_mft_nodes[test->optional_mft_count - 1], 0, sink_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + } + else + { + hr = IMFTopologyNode_ConnectOutput(src_node, 0, optional_mft_nodes[0], 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(optional_mft_nodes[test->optional_mft_count - 1], 0, mft_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + } + if (optional_mft_nodes[1]) + { + hr = IMFTopologyNode_ConnectOutput(optional_mft_nodes[0], 0, optional_mft_nodes[1], 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + } + for (j = 0; j < test->optional_mft_count; j++) + { + hr = IMFTopologyNode_GetTopoNodeID(optional_mft_nodes[j], &optional_mft_node_id[j]); + ok(hr == S_OK, "Failed to get source node id, hr %#lx.\n", hr); + IMFTopologyNode_Release(optional_mft_nodes[j]); + } + } + else + { + hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + } IMFTopologyNode_Release(mft_node); } else @@ -3159,7 +3395,7 @@ static void test_topology_loader(void) ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); ok(!count, "Unexpected count %u.\n", count); - handler.is_supported_called = FALSE; + handler.is_supported_sequence_id = 0; if (test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES) IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 1); @@ -3187,11 +3423,30 @@ static void test_topology_loader(void) } else if (test->expected_result == S_OK) { + BOOL expect_optional_rejected = FALSE; + IMFTopology_GetTopologyID(topology, &oldtopoid); IMFTopology_GetTopologyID(full_topology, &newtopoid); ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); ok(topology != full_topology, "Expected a different object for the resolved topology.\n"); + 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]; + + if (hr == S_OK) + { + UINT32 method; + hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_CONNECT_METHOD, &method); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(method == (MF_CONNECT_ALLOW_DECODER | MF_CONNECT_AS_OPTIONAL), "Unexpected method %#x\n", method); + IMFTopologyNode_Release(mft_node); + } + } + expected_count = 1 + !!(test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES) + !!(test->flags & LOADER_SET_XVP_FOR_PLAYBACK); hr = IMFTopology_GetCount(full_topology, &count); @@ -3203,9 +3458,10 @@ static void test_topology_loader(void) hr = IMFTopology_GetUINT32(full_topology, &MF_TOPOLOGY_RESOLUTION_STATUS, &value); todo_wine { ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - ok(value == MF_TOPOLOGY_RESOLUTION_SUCCEEDED, "Unexpected value %#x.\n", value); + ok(value == (expect_optional_rejected ? MF_OPTIONAL_NODE_REJECTED_MEDIA_TYPE : MF_TOPOLOGY_RESOLUTION_SUCCEEDED), + "Unexpected value %#x.\n", value); } - count = 2 + !!test_transform; + count = 2 + !!test_transform + test->optional_mft_count - test->expect_optional_mft_rejected[0] - test->expect_optional_mft_rejected[1]; if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) count++; if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) @@ -3213,6 +3469,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) ok(node_count == count, "Unexpected node count %u.\n", node_count); hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); @@ -3238,7 +3495,37 @@ todo_wine { if (hr == S_OK) IMFMediaType_Release(media_type); - ok(handler.is_supported_called, "Sink input support not checked.\n"); + ok(!!handler.is_supported_sequence_id, "Sink input sequence id not set.\n"); + + if (optional_transforms[0]) + { + ok(!!optional_transforms[0]->set_input_sequence_id, "Optional transform input sequence id not set.\n"); + ok(!!test_transform->set_input_sequence_id, "Test transform input sequence id not set.\n"); + 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) + { + /* 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_transforms[0]->set_output_sequence_id < test_transform->set_output_sequence_id, + "Optional transform output was not configured before the non-optional one.\n"); + } + } + for (j = 0; j < test->optional_mft_count; j++) + IMFTransform_Release(&optional_transforms[j]->IMFTransform_iface); + } if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) { @@ -3386,6 +3673,10 @@ todo_wine { ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); 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); ok(ref == 0, "Release returned %ld\n", ref); @@ -3407,10 +3698,11 @@ todo_wine { if (test_transform) { - ok(test_transform->input_enum_complete == !!(test->flags & LOADER_EXPECT_MFT_INPUT_ENUMERATED), + 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)), "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), + 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)), "got transform output_enum_complete %u\n", test_transform->output_enum_complete); ok(test_transform->input_type_set == (test->expected_result != MF_E_TOPO_CODEC_NOT_FOUND), "Got transform input_type_set %u.\n", test_transform->input_type_set); @@ -3437,6 +3729,8 @@ todo_wine { ok(ref == 0, "Release returned %ld\n", ref); for (j = 0; j < ARRAY_SIZE(mft_input_types) && mft_input_types[j]; ++j) IMFMediaType_Release(mft_input_types[j]); + for (j = 0; j < ARRAY_SIZE(optional_mft_types) && optional_mft_types[j]; ++j) + IMFMediaType_Release(optional_mft_types[j]); for (j = 0; j < ARRAY_SIZE(mft_output_types) && mft_output_types[j]; ++j) IMFMediaType_Release(mft_output_types[j]); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/topology_loader.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 08557d354a8..f289b36eebe 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -244,6 +244,17 @@ static HRESULT topology_branch_clone_nodes(struct topoloader_context *context, s return hr; } +static UINT32 topology_node_get_connect_method_semantics(IMFTopologyNode *node) +{ + UINT32 method; + + /* default to false for all semantic flags */ + if (FAILED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, &method))) + return 0; + + return method; +} + static HRESULT topology_node_list_branches(IMFTopologyNode *node, struct list *branches) { struct topology_branch *branch; @@ -732,9 +743,7 @@ static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_metho down_method = MF_CONNECT_ALLOW_DECODER; down_method = connect_method_from_mf(down_method) & method_mask; - if (topology_node_get_type(branch->up.node) != MF_TOPOLOGY_SOURCESTREAM_NODE - || FAILED(IMFTopologyNode_GetUINT32(branch->up.node, &MF_TOPONODE_CONNECT_METHOD, &up_method))) - up_method = MF_CONNECT_DIRECT; + up_method = topology_node_get_connect_method_semantics(branch->up.node); if (up_method & MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES) hr = topology_branch_foreach_up_types(topology, down_method, branch, upstream); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 1 - dlls/mf/topology_loader.c | 144 ++++++++++++++++++++++++-------------- 2 files changed, 92 insertions(+), 53 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 6c86d23fbe1..0a268054a46 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -3518,7 +3518,6 @@ 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_transforms[0]->set_output_sequence_id < test_transform->set_output_sequence_id, "Optional transform output was not configured before the non-optional one.\n"); } diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index f289b36eebe..bf1c208a0aa 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -105,6 +105,7 @@ struct topoloader_context { IMFTopology *input_topology; IMFTopology *output_topology; + UINT resolved_count; /* includes accepted and rejected optional nodes */ }; static HRESULT topology_loader_clone_node(struct topoloader_context *context, IMFTopologyNode *node, IMFTopologyNode **clone) @@ -146,8 +147,6 @@ struct topology_branch DWORD stream; IMFMediaTypeHandler *handler; } up, down; - - struct list entry; }; static const char *debugstr_topology_type(MF_TOPOLOGY_TYPE type) @@ -255,28 +254,6 @@ static UINT32 topology_node_get_connect_method_semantics(IMFTopologyNode *node) return method; } -static HRESULT topology_node_list_branches(IMFTopologyNode *node, struct list *branches) -{ - struct topology_branch *branch; - DWORD i, count, down_stream; - IMFTopologyNode *down_node; - HRESULT hr; - - hr = IMFTopologyNode_GetOutputCount(node, &count); - for (i = 0; SUCCEEDED(hr) && i < count; ++i) - { - if (FAILED(IMFTopologyNode_GetOutput(node, i, &down_node, &down_stream))) - continue; - - if (SUCCEEDED(hr = topology_branch_create(node, i, down_node, down_stream, &branch))) - list_add_tail(branches, &branch->entry); - - IMFTopologyNode_Release(down_node); - } - - return hr; -} - static void media_type_try_copy_attr(IMFMediaType *dst, IMFMediaType *src, const GUID *attr, HRESULT *hr) { PROPVARIANT value; @@ -761,24 +738,65 @@ static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_metho return hr; } -static HRESULT topology_loader_resolve_branches(struct topoloader_context *context, struct list *branches) +static HRESULT topology_node_resolve_foreach_output(IMFTopologyNode *node, struct topoloader_context *context); + +static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *context, IMFTopologyNode *node, DWORD output_stream) { enum connect_method method_mask = connect_method_from_mf(MF_CONNECT_ALLOW_DECODER); - struct topology_branch *branch, *next; + struct topology_branch *branch = NULL; + IMFTopologyNode *down_node = NULL; + DWORD down_stream; HRESULT hr = S_OK; - LIST_FOR_EACH_ENTRY_SAFE(branch, next, branches, struct topology_branch, entry) + 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, output_stream, &down_node, &down_stream))) + return hr; + + context->resolved_count++; + + if (FAILED(hr = topology_branch_create(node, output_stream, down_node, down_stream, &branch))) + goto done; + + if (FAILED(hr = topology_branch_clone_nodes(context, branch))) { - list_remove(&branch->entry); + WARN("Failed to clone nodes for branch %s\n", debugstr_topology_branch(branch)); + goto done; + } - 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 (FAILED(hr = topology_branch_connect(context->output_topology, method_mask, branch, NULL))) + goto done; + + topology_branch_destroy(branch); + branch = NULL; + + if (SUCCEEDED(hr)) + hr = topology_node_resolve_foreach_output(down_node, context); +done: + if (branch) topology_branch_destroy(branch); - if (FAILED(hr)) - break; + IMFTopologyNode_Release(down_node); + + return hr; +} + +static HRESULT topology_node_resolve_foreach_output(IMFTopologyNode *node, struct topoloader_context *context) +{ + DWORD i, count; + HRESULT hr; + + if (FAILED(hr = IMFTopologyNode_GetOutputCount(node, &count))) + return hr; + + for (i = 0; i < count; ++i) + { + if (FAILED(hr = topology_loader_resolve_subgraph(context, node, i))) + { + WARN("Failed to resolve subgraph under source node %p, stream %lu.\n", node, i); + return hr; + } } return hr; @@ -1010,14 +1028,14 @@ static void topology_loader_resolve_complete(struct topoloader_context *context) static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *input_topology, IMFTopology **ret_topology, IMFTopology *current_topology) { - struct list branches = LIST_INIT(branches); struct topoloader_context context = { 0 }; - struct topology_branch *branch, *next; + DWORD i = 0, source_count = 0; IMFTopology *output_topology; + IMFCollection *collection; IMFTopologyNode *node; - unsigned short i = 0; IMFStreamSink *sink; IUnknown *object; + WORD node_count; TOPOID topoid; HRESULT hr = E_FAIL; @@ -1059,35 +1077,55 @@ static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *in return hr; } + if (FAILED(hr = IMFTopology_GetSourceNodeCollection(input_topology, &collection))) + return hr; + if (FAILED(hr = IMFCollection_GetElementCount(collection, &source_count))) + goto done; + + if (!source_count) + { + hr = MF_E_TOPO_UNSUPPORTED; + goto done; + } + IMFTopology_GetTopologyID(input_topology, &topoid); if (FAILED(hr = create_topology(topoid, &output_topology))) - return hr; + goto done; IMFTopology_CopyAllItems(input_topology, (IMFAttributes *)output_topology); context.input_topology = input_topology; context.output_topology = output_topology; - for (i = 0; SUCCEEDED(IMFTopology_GetNode(input_topology, i, &node)); i++) + for (i = 0; SUCCEEDED(hr) && i < source_count; i++) { - hr = topology_node_list_branches(node, &branches); - IMFTopologyNode_Release(node); - if (FAILED(hr)) + IUnknown *element; + + if (FAILED(hr = IMFCollection_GetElement(collection, i, &element))) break; - } - if (SUCCEEDED(hr) && list_empty(&branches)) - hr = MF_E_TOPO_UNSUPPORTED; - while (SUCCEEDED(hr) && !list_empty(&branches)) - hr = topology_loader_resolve_branches(&context, &branches); + hr = IUnknown_QueryInterface(element, &IID_IMFTopologyNode, (void **)&node); + IUnknown_Release(element); - LIST_FOR_EACH_ENTRY_SAFE(branch, next, &branches, struct topology_branch, entry) - { - WARN("Failed to resolve branch %s\n", debugstr_topology_branch(branch)); - list_remove(&branch->entry); - topology_branch_destroy(branch); + if (SUCCEEDED(hr)) + { + if (topology_node_get_connect_method_semantics(node) & MF_CONNECT_AS_OPTIONAL_BRANCH) + FIXME("Optional branches are not supported.\n"); + + hr = topology_node_resolve_foreach_output(node, &context); + IMFTopologyNode_Release(node); + } } + IMFTopology_GetNodeCount(input_topology, &node_count); + + if (SUCCEEDED(hr) && !context.resolved_count) + hr = MF_E_TOPO_UNSUPPORTED; + + context.resolved_count += source_count; + if (SUCCEEDED(hr) && context.resolved_count != node_count) + WARN("Failed to resolve %u nodes.\n", node_count - context.resolved_count); + if (FAILED(hr)) IMFTopology_Release(output_topology); else @@ -1096,6 +1134,8 @@ static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *in *ret_topology = output_topology; } +done: + IMFCollection_Release(collection); return hr; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 31 +++--- dlls/mf/topology_loader.c | 192 +++++++++++++++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 23 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 0a268054a46..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, }, @@ -2998,7 +2998,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 */ @@ -3009,7 +3008,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, }, { /* #70 H264 -> NV12, optional source is ignored */ @@ -3433,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]; @@ -3469,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); @@ -3504,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) @@ -3674,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); @@ -3697,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 bf1c208a0aa..e45dc72a5c7 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); @@ -738,24 +757,181 @@ static HRESULT topology_branch_connect(IMFTopology *topology, enum connect_metho return hr; } +static HRESULT topology_node_find_down_node(IMFTopologyNode *node, DWORD output_stream, IMFTopologyNode **first_opt_node, + DWORD *opt_input_stream, IMFTopologyNode **down_node, DWORD *down_stream, struct topoloader_context *context) +{ + IMFTopologyNode *optional = NULL; + DWORD opt_stream = 0; + HRESULT hr; + + *first_opt_node = NULL; + *opt_input_stream = 0; + *down_node = NULL; + *down_stream = 0; + + IMFTopologyNode_AddRef(node); + + for (;;) + { + IMFTopologyNode *next_node; + DWORD input_stream; + + if (FAILED(hr = IMFTopologyNode_GetOutput(node, output_stream, &next_node, &input_stream))) + break; + + context->resolved_count++; + + IMFTopologyNode_Release(node); + node = next_node; + + if (!(topology_node_get_connect_method_semantics(node) & MF_CONNECT_AS_OPTIONAL)) + { + *first_opt_node = optional; + *opt_input_stream = opt_stream; + *down_node = node; + *down_stream = input_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 = input_stream; + } + + output_stream = 0; + } + + if (optional) + IMFTopologyNode_Release(optional); + IMFTopologyNode_Release(node); + return hr; +} + +static HRESULT topology_branch_connect_optional_with_type(struct topology_branch *branch, + IMFMediaType *type, struct topoloader_context *context, BOOL *accepted) +{ + IMFTopologyNode *optional_node = branch->up.node; + IMFTransform *transform = NULL; + HRESULT hr, connect_hr = S_OK; + + 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_object(optional_node, &IID_IMFTransform, (void **)&transform))) + goto done; + + /* The rules for the upstream node output type are a bit odd. Types can be enumerated + * from an MFT already present in the topology, but not for an inserted decoder. + * TODO: reconfigure the upstream output type if possible and allowed. */ + 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); + return hr; +} + +static HRESULT topology_branch_connect_optional_chain(struct topology_branch *branch, + IMFTopologyNode *optional, DWORD opt_input_stream, struct topoloader_context *context) +{ + IMFTopologyNode *up_node, *optional_node = NULL; + IMFMediaTypeHandler *handler; + IMFMediaType *type = NULL; + BOOL accepted = FALSE; + DWORD up_stream; + HRESULT 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"); + return hr; + } + + 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)) + goto done; + + if (FAILED(hr = topology_loader_clone_node(context, optional, &optional_node))) + goto done; + + if (SUCCEEDED(hr = topology_branch_set_source_node(branch, optional_node, 0))) + hr = topology_branch_connect_optional_with_type(branch, type, context, &accepted); + + if (accepted) + hr = IMFTopologyNode_ConnectOutput(up_node, up_stream, optional_node, opt_input_stream); + else if (FAILED(IMFTopology_RemoveNode(context->output_topology, optional_node))) + ERR("Failed to remove node.\n"); + + IMFTopologyNode_Release(optional_node); + optional_node = NULL; + + if (SUCCEEDED(hr)) + { + DWORD count; + if (SUCCEEDED(IMFTopologyNode_GetOutputCount(optional, &count)) && count > 1) + FIXME("Ignoring %lu extra outputs.\n", count); + hr = IMFTopologyNode_GetOutput(optional, 0, &optional, &opt_input_stream); + } + + if (SUCCEEDED(hr)) + { + if (topology_node_get_connect_method_semantics(optional) & MF_CONNECT_AS_OPTIONAL) + hr = topology_branch_connect_optional_chain(branch, optional, opt_input_stream, context); + IMFTopologyNode_Release(optional); + } + +done: + if (type) + IMFMediaType_Release(type); + if (optional_node) + IMFTopologyNode_Release(optional_node); + IMFTopologyNode_Release(up_node); + return hr; +} + static HRESULT topology_node_resolve_foreach_output(IMFTopologyNode *node, struct topoloader_context *context); static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *context, IMFTopologyNode *node, DWORD output_stream) { enum connect_method method_mask = connect_method_from_mf(MF_CONNECT_ALLOW_DECODER); + IMFTopologyNode *down_node = NULL, *first_opt_node = NULL; struct topology_branch *branch = NULL; - IMFTopologyNode *down_node = NULL; - DWORD down_stream; + DWORD down_stream, opt_input_stream; HRESULT hr = S_OK; 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, output_stream, &down_node, &down_stream))) + if (FAILED(hr = topology_node_find_down_node(node, output_stream, &first_opt_node, &opt_input_stream, + &down_node, &down_stream, context))) return hr; - context->resolved_count++; - if (FAILED(hr = topology_branch_create(node, output_stream, down_node, down_stream, &branch))) goto done; @@ -768,6 +944,10 @@ 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 (first_opt_node && FAILED(hr = topology_branch_connect_optional_chain(branch, + first_opt_node, opt_input_stream, context))) + goto done; + topology_branch_destroy(branch); branch = NULL; @@ -777,6 +957,8 @@ static HRESULT topology_loader_resolve_subgraph(struct topoloader_context *conte done: if (branch) topology_branch_destroy(branch); + if (first_opt_node) + IMFTopologyNode_Release(first_opt_node); IMFTopologyNode_Release(down_node); return hr; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/topology_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index e45dc72a5c7..510f345b636 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -1221,7 +1221,7 @@ static HRESULT WINAPI topology_loader_Load(IMFTopoLoader *iface, IMFTopology *in TOPOID topoid; HRESULT hr = E_FAIL; - FIXME("iface %p, input_topology %p, ret_topology %p, current_topology %p stub!\n", + TRACE("iface %p, input_topology %p, ret_topology %p, current_topology %p.\n", iface, input_topology, ret_topology, current_topology); if (current_topology) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
Rebase and major revision to address the issues. I haven't implemented changing the upstream type to connect an optional, or the addition of a converter. I added a test for upstream type change. I tried adding one for `MFT -> converter -> optional` but it didn't work on native unless I enabled XVP, even though the converter supports NV12 -> YUY2, and then the resolved topology reload failed. I also tried `optional -> converter -> sink` which worked for YUY2 -> NV12 with the sink restricted to direct connection only. I didn't include either of those since it seems native results may change. It's broken for the first, and not strictly valid for the second. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10645#note_138147
I've made various modifications in https://gitlab.winehq.org/wine/wine/-/merge_requests/10791, which I believe makes the diff and the code more readable, re-using as much as possible of the existing connection helpers for optional nodes. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10645#note_138419
This merge request was closed by Conor McCarthy. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10645
participants (3)
-
Conor McCarthy -
Conor McCarthy (@cmccarthy) -
Rémi Bernon (@rbernon)