From: Conor McCarthy <cmccarthy@codeweavers.com> Windows does not call MFTEnumEx to get a resampler. --- dlls/mf/tests/topology.c | 4 +- dlls/mf/topology_loader.c | 211 +++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index a1fcff28e33..39e8ecdb401 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2549,7 +2549,7 @@ static void test_topology_loader(void) /* PCM -> PCM, different enumerated bps, no current type, sink allow converter, no output types */ .input_types = {&audio_pcm_44100}, .output_types = {&audio_pcm_48000}, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = MF_CONNECT_DIRECT, .expected_result = MF_E_INVALIDMEDIATYPE, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_TODO, + .flags = LOADER_NO_CURRENT_OUTPUT, }, { /* PCM -> PCM, different enumerated bps, no current type, sink allow converter, no current output type */ @@ -2698,7 +2698,7 @@ static void test_topology_loader(void) .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, }, { diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index ce7ef64e228..fef4446a298 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -32,6 +32,8 @@ #include "wine/list.h" #include "mf_private.h" +#include "initguid.h" +#include "wmcodecdsp.h" WINE_DEFAULT_DEBUG_CHANNEL(mfplat); @@ -327,6 +329,7 @@ struct connection_context MF_TOPOLOGY_TYPE up_node_type; struct connection_context_type_enumerator up_types; struct connection_context_type_enumerator down_types; + BOOL is_video; }; static void connection_context_destroy(struct connection_context *context) @@ -339,6 +342,7 @@ static HRESULT connection_context_init(struct connection_context *context, IMFTo struct topology_branch *branch, MF_CONNECT_METHOD method, BOOL enumerate_source_types) { IMFMediaType *up_type; + GUID major; HRESULT hr; if (FAILED(hr = IMFTopologyNode_GetNodeType(branch->up.node, &context->up_node_type))) @@ -364,6 +368,10 @@ static HRESULT connection_context_init(struct connection_context *context, IMFTo hr = IMFMediaTypeHandler_GetCurrentMediaType(context->down_types.handler, &context->down_types.current); context->down_types.enumerate = (hr == MF_E_NOT_INITIALIZED || hr == MF_E_TRANSFORM_TYPE_NOT_SET); + if (FAILED(hr = IMFMediaType_GetMajorType(up_type, &major))) + goto failed; + context->is_video = IsEqualGUID(&major, &MFMediaType_Video); + return S_OK; failed: @@ -378,6 +386,39 @@ static void connection_context_init_down_type_enumeration(struct connection_cont IMFMediaTypeHandler_GetMediaTypeByIndex(context->down_types.handler, 0, &context->down_types.index0); } +static HRESULT clone_media_type(IMFMediaType *in_type, IMFMediaType **out_type) +{ + IMFMediaType *media_type; + HRESULT hr; + + if (FAILED(hr = MFCreateMediaType(&media_type))) + return hr; + + if (SUCCEEDED(hr = IMFMediaType_CopyAllItems(in_type, (IMFAttributes *)media_type))) + *out_type = media_type; + else + IMFMediaType_Release(media_type); + + return hr; +} + +static HRESULT clone_media_type_updated_from_upstream(IMFMediaType *in_type, IMFMediaType *up_type, + IMFMediaType **out_type) +{ + IMFMediaType *media_type; + HRESULT hr; + + if (FAILED(hr = clone_media_type(in_type, &media_type))) + return hr; + + if (SUCCEEDED(hr = update_media_type_from_upstream(media_type, up_type))) + *out_type = media_type; + else + IMFMediaType_Release(media_type); + + return hr; +} + static HRESULT topology_branch_connect(IMFTopology *topology, MF_CONNECT_METHOD method_mask, struct topology_branch *branch, BOOL enumerate_source_types); static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_METHOD method_mask, @@ -474,7 +515,12 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC } 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_TOPO_CODEC_NOT_FOUND; + } if (SUCCEEDED(hr)) hr = IMFTopology_AddNode(topology, node); if (SUCCEEDED(hr)) @@ -530,6 +576,162 @@ HRESULT topology_node_init_media_type(IMFTopologyNode *node, DWORD stream, BOOL return hr; } +static HRESULT create_transform_node(const GUID *class_id, IMFTopologyNode **node_out, + IMFTransform **transform_out) +{ + IMFTopologyNode *node = NULL; + IMFTransform *transform; + HRESULT hr; + + if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) + return hr; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node)) + || FAILED(hr = IMFTopologyNode_SetGUID(node, &MF_TOPONODE_TRANSFORM_OBJECTID, class_id)) + || FAILED(hr = IMFTopologyNode_SetObject(node, (IUnknown *)transform))) + goto failed; + + *node_out = node; + *transform_out = transform; + + return S_OK; + +failed: + if (node) + IMFTopologyNode_Release(node); + IMFTransform_Release(transform); + + return hr; +} + +static HRESULT topology_branch_connect_converter_with_types(IMFTopology *topology, struct topology_branch *branch, + IMFMediaType *up_type, IMFMediaType *down_type, struct connection_context *context) +{ + IMFMediaTypeHandler *down_handler = context->down_types.handler, *up_handler = context->up_types.handler; + IMFMediaType *output_type, *media_type; + IMFTransform *transform; + IMFTopologyNode *node; + HRESULT hr, type_hr; + UINT i; + + TRACE("branch %s, up_type %s, down_type %s.\n", debugstr_topology_branch(branch), debugstr_media_subtype(up_type), + debugstr_media_subtype(down_type)); + + /* Changing the resampler GUID in Windows regedit does not prevent a CResamplerMediaObject + * being used, implying that MFTEnumEx() is not called for resamplers. */ + if (FAILED(hr = create_transform_node(&CLSID_CResamplerMediaObject, &node, &transform))) + return hr; + + if (FAILED(hr = IMFTransform_SetInputType(transform, 0, up_type, 0)) + || FAILED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(up_handler, up_type, NULL))) + { + hr = MF_E_TOPO_CODEC_NOT_FOUND; + goto done; + } + + /* MF_E_INVALIDMEDIATYPE is returned if the transform cannot be connected down. */ + hr = MF_E_INVALIDMEDIATYPE; + + for (i = 0; SUCCEEDED(type_hr = IMFTransform_GetOutputAvailableType(transform, 0, i, &output_type)); ++i) + { + DWORD flags; + + /* IsMediaTypeSupported() is not called until the type passes a sanity check of some kind. + * The below condition at least matches the test results. */ + if (down_type && (FAILED(IMFMediaType_IsEqual(output_type, down_type, &flags)) + || !(flags & MF_MEDIATYPE_EQUAL_MAJOR_TYPES) || !(flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES))) + { + IMFMediaType_Release(output_type); + continue; + } + + if (down_type) + { + /* It is not clear exactly how the type should be compiled, but attributes from the down type should + * have priority, and then we should probably add any from upstream which have not been set. */ + if (FAILED(clone_media_type_updated_from_upstream(down_type, output_type, &media_type))) + continue; + update_media_type_from_upstream(media_type, up_type); + } + else + { + if (FAILED(clone_media_type_updated_from_upstream(output_type, up_type, &media_type))) + continue; + } + + IMFMediaType_Release(output_type); + + if (SUCCEEDED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(down_handler, media_type, NULL))) + { + if (context->up_types.enumerate) + hr = IMFMediaTypeHandler_SetCurrentMediaType(up_handler, up_type); + + if (SUCCEEDED(hr) && SUCCEEDED(hr = IMFTransform_SetOutputType(transform, 0, media_type, 0)) + && SUCCEEDED(hr = IMFTopologyNode_ConnectOutput(branch->up.node, branch->up.stream, node, 0)) + && SUCCEEDED(hr = IMFTopologyNode_ConnectOutput(node, 0, branch->down.node, branch->down.stream)) + && SUCCEEDED(hr = IMFTopology_AddNode(topology, node))) + { + TRACE("Connected converter %p, input %s, output %s.\n", transform, debugstr_media_subtype(up_type), + debugstr_media_subtype(media_type)); + } + } + IMFMediaType_Release(media_type); + + if (SUCCEEDED(hr)) + break; + } + + if (FAILED(type_hr) && type_hr != MF_E_NO_MORE_TYPES) + hr = type_hr; + +done: + if (node) + IMFTopologyNode_Release(node); + IMFTransform_Release(transform); + + return hr; +} + +static HRESULT topology_branch_connect_converter(IMFTopology *topology, + struct topology_branch *branch, struct connection_context *context) +{ + IMFMediaType *up_type, *down_type; + HRESULT hr; + GUID subtype; + + if (FAILED(hr = IMFMediaType_GetGUID(context->up_types.index0, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) + return MF_E_TOPO_CODEC_NOT_FOUND; + + connection_context_init_down_type_enumeration(context); + + type_enumerator_reset(&context->up_types); + type_enumerator_reset(&context->down_types); + while (SUCCEEDED(hr = type_enumerator_get_next_media_type(&context->up_types, &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(&context->down_types, &down_type))) + { + hr = topology_branch_connect_converter_with_types(topology, branch, up_type, down_type, context); + if (down_type) + IMFMediaType_Release(down_type); + if (SUCCEEDED(hr)) + { + IMFMediaType_Release(up_type); + return hr; + } + } + IMFMediaType_Release(up_type); + } + + return hr; +} + static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_METHOD method_mask, struct topology_branch *branch, IMFMediaType *up_type, struct connection_context *context) { @@ -562,8 +764,13 @@ static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_ME down_type = context->down_types.current ? context->down_types.current : context->down_types.index0; if (FAILED(hr) && (method & method_mask & MF_CONNECT_ALLOW_CONVERTER) == MF_CONNECT_ALLOW_CONVERTER) - hr = topology_branch_connect_indirect(topology, MF_CONNECT_ALLOW_CONVERTER, - branch, context->up_types.enumerate, up_type, down_type); + { + if (context->is_video) + hr = topology_branch_connect_indirect(topology, MF_CONNECT_ALLOW_CONVERTER, + branch, context->up_types.enumerate, up_type, down_type); + else + hr = topology_branch_connect_converter(topology, branch, context); + } if (FAILED(hr) && (method & method_mask & MF_CONNECT_ALLOW_DECODER) == MF_CONNECT_ALLOW_DECODER) hr = topology_branch_connect_indirect(topology, MF_CONNECT_ALLOW_DECODER, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10009