From: Conor McCarthy <cmccarthy@codeweavers.com> --- dlls/mf/tests/topology.c | 36 ++++----- dlls/mf/topology_loader.c | 162 +++++++++++++++++++++++++++++++++----- 2 files changed, 159 insertions(+), 39 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index f80bff61154..17ad76ad559 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2264,7 +2264,6 @@ enum loader_test_flags LOADER_TEST_MFT_EXPECT_CONVERTER = 0x2000, LOADER_EXPECT_MFT_OUTPUT_ENUMERATED = 0x4000, LOADER_EXPECT_MFT_INPUT_ENUMERATED = 0x8000, - LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO = 0x10000, LOADER_TODO_MFT_IN_TYPE = 0x20000, LOADER_TODO_MFT_OUT_TYPE = 0x40000, }; @@ -2624,21 +2623,21 @@ static void test_topology_loader(void) .input_types = {&audio_mp3_44100}, .output_types = {&audio_float_48000}, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, .decoded_type = &audio_float_44100_no_ch, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED, }, { /* MP3 -> {float, PCM}, need both decoder and converter, no current output type */ .input_types = {&audio_mp3_44100}, .output_types = {&audio_float_48000, &audio_pcm_48000}, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, .decoded_type = &audio_float_44100_stereo, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED, }, { /* MP3 -> {PCM, float}, need both decoder and converter, no current output type */ .input_types = {&audio_mp3_44100}, .output_types = {&audio_pcm_48000, &audio_float_48000}, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, .decoded_type = &audio_float_44100_stereo, .expected_output_index = 1, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED, }, { @@ -2652,7 +2651,7 @@ static void test_topology_loader(void) .input_types = {&audio_mp3_44100, &audio_pcm_44100}, .output_types = {&audio_pcm_44100}, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_SET_ENUMERATE_SOURCE_TYPES | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_SET_ENUMERATE_SOURCE_TYPES | LOADER_EXPECT_SINK_ENUMERATED, }, { @@ -2692,14 +2691,14 @@ static void test_topology_loader(void) .mft_input_type = &audio_pcm_44100, .mft_output_types = {&audio_pcm_48000}, .decoded_type = &audio_pcm_44100, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO | LOADER_TODO_MFT_OUT_TYPE, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED | LOADER_TODO_MFT_OUT_TYPE, }, { /* MP3 -> PCM, different enumerated bps, add incompatible test MFT, configure MFT */ .input_types = {&audio_mp3_44100}, .output_types = {&audio_pcm_48000}, .sink_method = MF_CONNECT_DIRECT, .source_method = MF_CONNECT_DIRECT, .mft_input_type = &audio_aac_44100, .mft_output_types = {&audio_pcm_48000}, .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, - .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { @@ -2762,7 +2761,7 @@ static void test_topology_loader(void) .input_types = {&video_h264_1280}, .output_types = {&video_color_convert_1280_rgb32, &video_video_processor_1280_rgb32}, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_output_index = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED_TODO | LOADER_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED, }, { /* H264 -> {DMO_RGB32, MF_RGB32} */ @@ -2777,28 +2776,28 @@ static void test_topology_loader(void) .decoded_type = &video_generic_yuv_1280, .expected_output_index = 1, .decoded_subtypes = {&MFVideoFormat_NV12, &MFVideoFormat_YUY2}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED, }, { /* H264 -> {RGB24, RGB32} */ .input_types = {&video_h264_1280}, .output_types = {&video_video_processor_1280_rgb24, &video_video_processor_1280_rgb32}, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_output_index = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED, }, { /* H264 -> {RGB24, RGB555} */ .input_types = {&video_h264_1280}, .output_types = {&video_video_processor_1280_rgb24, &video_video_processor_1280_rgb555}, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_output_index = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED, }, { /* H264 -> {RGB555, RGB24} */ .input_types = {&video_h264_1280}, .output_types = {&video_video_processor_1280_rgb555, &video_video_processor_1280_rgb24}, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_output_index = 1, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_TODO_OUT_TYPE | LOADER_EXPECT_SINK_ENUMERATED, }, { @@ -2806,7 +2805,7 @@ static void test_topology_loader(void) .input_types = {&video_h264_1280}, .output_types = {&video_color_convert_1280_rgb32}, .sink_method = -1, .source_method = -1, .decoded_type = &video_nv12_1280, .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_EXPECT_SINK_ENUMERATED, }, { @@ -2815,7 +2814,7 @@ static void test_topology_loader(void) .decoded_type = &video_generic_yuv_1280, .expected_output_index = 1, .decoded_subtypes = {&MFVideoFormat_NV12, &MFVideoFormat_YUY2}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_VideoProcessorMFT, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_SET_XVP_FOR_PLAYBACK | LOADER_EXPECT_SINK_ENUMERATED_TODO | LOADER_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_MEDIA_TYPES | LOADER_SET_XVP_FOR_PLAYBACK | LOADER_EXPECT_SINK_ENUMERATED, }, { /* H264 -> RGB32, resize, set XVP */ @@ -2830,21 +2829,21 @@ static void test_topology_loader(void) .input_types = {&video_h264_1280}, .output_types = {&video_color_convert_1280_rgb32}, .sink_method = -1, .source_method = -1, .mft_input_type = &video_color_convert_1280_rgb32, .mft_output_types = {&video_color_convert_1280_rgb32}, .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, .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 -> RGB32, Video Processor media type, add test MFT */ .input_types = {&video_h264_1280}, .output_types = {&video_video_processor_1280_rgb32}, .sink_method = -1, .source_method = -1, .mft_input_type = &video_video_processor_1280_rgb32, .mft_output_types = {&video_video_processor_1280_rgb32}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_ADD_TEST_MFT | LOADER_TODO_MFT_OUT_TYPE | LOADER_TEST_MFT_EXPECT_CONVERTER | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_TODO_MFT_OUT_TYPE | LOADER_TEST_MFT_EXPECT_CONVERTER | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, { /* H264 -> NV12, add test MFT */ .input_types = {&video_h264_1280}, .output_types = {&video_nv12_1280}, .sink_method = -1, .source_method = -1, .mft_input_type = &video_nv12_1280, .mft_output_types = {&video_nv12_1280}, .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, - .flags = LOADER_ADD_TEST_MFT | LOADER_TODO_MFT_OUT_TYPE | LOADER_EXPECT_MFT_INPUT_ENUMERATED_TODO, + .flags = LOADER_ADD_TEST_MFT | LOADER_TODO_MFT_OUT_TYPE | LOADER_EXPECT_MFT_INPUT_ENUMERATED, }, }; @@ -3376,8 +3375,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 a78ee5ad8f9..e343ba2083a 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -129,6 +129,8 @@ struct topology_branch { IMFTopologyNode *node; DWORD stream; + BOOL enumerate; + BOOL enum_initialised; } up, down; struct list entry; @@ -267,6 +269,87 @@ static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMedi return hr; } +struct type_enumerator +{ + IMFMediaTypeHandler *handler; + BOOL enumerate; + BOOL down; + BOOL have_current; + int i; +}; + +static void type_enumerator_init(struct type_enumerator *enumerator, IMFMediaTypeHandler *handler, BOOL enumerate, BOOL down) +{ + enumerator->handler = handler; + enumerator->enumerate = enumerate; + enumerator->down = down; + enumerator->have_current = FALSE; + enumerator->i = enumerate - 1; +} + +static void topology_branch_init_up_type_enumeration(struct topology_branch *branch, IMFMediaTypeHandler *up_handler) +{ + IMFMediaType *current; + HRESULT hr; + + if (branch->up.enum_initialised || branch->up.enumerate) + return; + + if (topology_node_get_type(branch->up.node) != MF_TOPOLOGY_SOURCESTREAM_NODE) + { + if (SUCCEEDED(hr = IMFMediaTypeHandler_GetCurrentMediaType(up_handler, ¤t))) + IMFMediaType_Release(current); + branch->up.enumerate = FAILED(hr); + } + branch->up.enum_initialised = TRUE; +} + +static void topology_branch_init_down_type_enumeration(struct topology_branch *branch, IMFMediaTypeHandler *down_handler) +{ + IMFMediaType *current; + HRESULT hr; + + if (branch->down.enum_initialised) + return; + + if (SUCCEEDED(hr = IMFMediaTypeHandler_GetCurrentMediaType(down_handler, ¤t))) + IMFMediaType_Release(current); + + branch->down.enumerate = hr == MF_E_NOT_INITIALIZED + /* One of the mf tests returns MF_E_TRANSFORM_TYPE_NOT_SET from a sink. */ + || (hr == MF_E_TRANSFORM_TYPE_NOT_SET && topology_node_get_type(branch->down.node) == MF_TOPOLOGY_TRANSFORM_NODE); + branch->down.enum_initialised = TRUE; +} + +static HRESULT type_enumerator_get_next_media_type(struct type_enumerator *enumerator, IMFMediaType **type) +{ + int i = enumerator->i++; + + if (i < 0) + { + if (SUCCEEDED(IMFMediaTypeHandler_GetCurrentMediaType(enumerator->handler, type))) + { + enumerator->have_current = TRUE; + return S_OK; + } + i = enumerator->i++; + } + + if (!i && enumerator->down && !enumerator->have_current && !enumerator->enumerate) + { + /* No current type and not enumerating. This occus if a sink returns E_FAIL when getting the current type. */ + *type = NULL; + return S_OK; + } + + /* If we are not enumerating and we have a current type, do not check index 0. */ + if ((!i && !enumerator->have_current) || enumerator->enumerate) + return IMFMediaTypeHandler_GetMediaTypeByIndex(enumerator->handler, i, type); + + /* return MF_E_INVALIDMEDIATYPE if not enumerating types */ + return MF_E_INVALIDMEDIATYPE; +} + static HRESULT clone_media_type(IMFMediaType *in_type, IMFMediaType **out_type) { IMFMediaType *media_type; @@ -381,12 +464,7 @@ static HRESULT topology_branch_connect_decoder(IMFTopology *topology, } if (SUCCEEDED(hr)) - { hr = topology_branch_connect(topology, method, &down_branch, !down_type); - /* Failure to connect a converter must result in MF_E_TOPO_CODEC_NOT_FOUND */ - if (hr == MF_E_INVALIDMEDIATYPE || hr == MF_E_NO_MORE_TYPES) - hr = MF_E_TOPO_CODEC_NOT_FOUND; - } if (SUCCEEDED(hr)) hr = IMFTopology_AddNode(topology, node); if (SUCCEEDED(hr)) @@ -471,9 +549,8 @@ failed: } static HRESULT topology_branch_connect_converter_with_types(IMFTopology *topology, struct topology_branch *branch, - IMFMediaType *up_type, IMFMediaType *down_type) + IMFMediaType *up_type, IMFMediaType *down_type, IMFMediaTypeHandler *up_handler, IMFMediaTypeHandler *down_handler) { - IMFMediaTypeHandler *up_handler = NULL, *down_handler = NULL; HRESULT hr, ret_hr = MF_E_TOPO_CODEC_NOT_FOUND; IMFMediaType *output_type, *media_type; IMFTransform *transform; @@ -515,11 +592,6 @@ static HRESULT topology_branch_connect_converter_with_types(IMFTopology *topolog return MF_E_INVALIDMEDIATYPE; } - if (FAILED(hr = topology_node_get_type_handler(branch->up.node, branch->up.stream, TRUE, &up_handler))) - goto done; - if (FAILED(hr = topology_node_get_type_handler(branch->down.node, branch->down.stream, FALSE, &down_handler))) - goto done; - if (FAILED(hr = IMFTransform_SetInputType(transform, 0, up_type, 0)) || FAILED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(up_handler, up_type, NULL))) { @@ -582,10 +654,6 @@ static HRESULT topology_branch_connect_converter_with_types(IMFTopology *topolog ret_hr = hr; done: - if (down_handler) - IMFMediaTypeHandler_Release(down_handler); - if (up_handler) - IMFMediaTypeHandler_Release(up_handler); if (node) IMFTopologyNode_Release(node); IMFTransform_Release(transform); @@ -593,6 +661,58 @@ done: return ret_hr; } +static HRESULT topology_branch_connect_converter(IMFTopology *topology, struct topology_branch *branch) +{ + struct type_enumerator up_enumerator, down_enumerator; + IMFMediaTypeHandler *up_handler, *down_handler = NULL; + HRESULT hr, ret_hr = MF_E_TOPO_CODEC_NOT_FOUND; + IMFMediaType *up_type, *down_type; + + if (FAILED(hr = topology_node_get_type_handler(branch->up.node, branch->up.stream, TRUE, &up_handler))) + return hr; + if (FAILED(hr = topology_node_get_type_handler(branch->down.node, branch->down.stream, FALSE, &down_handler))) + goto done; + + topology_branch_init_up_type_enumeration(branch, up_handler); + topology_branch_init_down_type_enumeration(branch, down_handler); + + type_enumerator_init(&up_enumerator, up_handler, branch->up.enumerate, FALSE); + type_enumerator_init(&down_enumerator, down_handler, branch->down.enumerate, TRUE); + + while (SUCCEEDED(hr = type_enumerator_get_next_media_type(&up_enumerator, &up_type))) + { + /* TODO: sink types are tried in reverse order, and float at any index is preferred for audio, + * probably because it has a greater bit depth and dynamic range. All indices are always + * fetched, which is consistent with sorting audio types and starting at the last index + * without fetching the type count. */ + while (SUCCEEDED(hr = type_enumerator_get_next_media_type(&down_enumerator, &down_type))) + { + ret_hr = topology_branch_connect_converter_with_types(topology, branch, up_type, down_type, up_handler, down_handler); + + if (down_type) + IMFMediaType_Release(down_type); + + if (SUCCEEDED(ret_hr)) + { + IMFMediaType_Release(up_type); + goto done; + } + } + + IMFMediaType_Release(up_type); + } + + /* Return unexpected errors such as allocation failure. */ + if (FAILED(hr) && hr != MF_E_INVALIDMEDIATYPE && hr != MF_E_NO_MORE_TYPES) + ret_hr = hr; + +done: + if (down_handler) + IMFMediaTypeHandler_Release(down_handler); + IMFMediaTypeHandler_Release(up_handler); + return ret_hr; +} + static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_METHOD method, struct topology_branch *branch, IMFMediaType *up_type) { @@ -629,7 +749,7 @@ static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_ME } if (FAILED(hr) && (method & MF_CONNECT_ALLOW_CONVERTER) == MF_CONNECT_ALLOW_CONVERTER) - hr = topology_branch_connect_converter_with_types(topology, branch, up_type, down_type); + hr = topology_branch_connect_converter(topology, branch); if (FAILED(hr) && (method & MF_CONNECT_ALLOW_DECODER) == MF_CONNECT_ALLOW_DECODER) hr = topology_branch_connect_decoder(topology, branch, up_type, down_type); @@ -679,6 +799,8 @@ static HRESULT topology_branch_connect(IMFTopology *topology, MF_CONNECT_METHOD down_method = MF_CONNECT_ALLOW_DECODER; down_method &= method_mask; + branch->up.enumerate = enumerate_source_types && topology_node_get_type(branch->up.node) == MF_TOPOLOGY_SOURCESTREAM_NODE; + if (enumerate_source_types) { UINT32 up_method; @@ -692,9 +814,9 @@ static HRESULT topology_branch_connect(IMFTopology *topology, MF_CONNECT_METHOD else { hr = topology_branch_foreach_up_types(topology, down_method & MF_CONNECT_DIRECT, branch); - if (FAILED(hr)) - hr = topology_branch_foreach_up_types(topology, down_method & MF_CONNECT_ALLOW_CONVERTER, branch); - if (FAILED(hr)) + if (FAILED(hr) && (down_method & MF_CONNECT_ALLOW_CONVERTER)) + hr = topology_branch_connect_converter(topology, branch); + if (FAILED(hr) && (down_method & MF_CONNECT_ALLOW_DECODER) == MF_CONNECT_ALLOW_DECODER) hr = topology_branch_foreach_up_types(topology, down_method & MF_CONNECT_ALLOW_DECODER, branch); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10009