Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v5: Correct connect method handling. In v4, the MF_CONNECT_ALLOW_DECODER path is only triggered if the conidtion for sink_method & MF_CONNECT_ALLOW_CONVERTER is not satisfied. This is never true, effectively making MF_CONNECT_ALLOW_DECODER a noop. Instead of using else here, just check for FAILED(hr). --- dlls/mf/topology.c | 273 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c index a09f6ef0ef4..e3a4dfd5141 100644 --- a/dlls/mf/topology.c +++ b/dlls/mf/topology.c @@ -1992,15 +1992,278 @@ static HRESULT topology_loader_clone_node(struct topoloader_context *context, IM return hr; }
+struct transform_output_type +{ + IMFMediaType *type; + IMFTransform *transform; +}; + +struct connect_context +{ + struct topoloader_context *context; + IMFTopologyNode *upstream_node; + IMFTopologyNode *sink; + IMFMediaTypeHandler *sink_handler; + const GUID *converter_category; +}; + +typedef HRESULT (*p_connect_func)(struct transform_output_type *output_type, struct connect_context *context); + +static void topology_loader_release_transforms(IMFActivate **activates, unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; ++i) + IMFActivate_Release(activates[i]); + CoTaskMemFree(activates); +} + +static HRESULT topology_loader_enumerate_output_types(const GUID *category, IMFMediaType *input_type, + p_connect_func connect_func, struct connect_context *context) +{ + MFT_REGISTER_TYPE_INFO mft_typeinfo; + IMFActivate **activates; + unsigned int i, count; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetMajorType(input_type, &mft_typeinfo.guidMajorType))) + return hr; + + if (FAILED(hr = IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &mft_typeinfo.guidSubtype))) + return hr; + + if (FAILED(hr = MFTEnumEx(*category, MFT_ENUM_FLAG_ALL, &mft_typeinfo, NULL, &activates, &count))) + return hr; + + hr = E_FAIL; + + for (i = 0; i < count; ++i) + { + IMFTransform *transform; + + if (FAILED(IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void **)&transform))) + { + WARN("Failed to create a transform.\n"); + continue; + } + + if (SUCCEEDED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0))) + { + struct transform_output_type output_type; + unsigned int output_count = 0; + + output_type.transform = transform; + while (SUCCEEDED(IMFTransform_GetOutputAvailableType(transform, 0, output_count++, &output_type.type))) + { + hr = connect_func(&output_type, context); + IMFMediaType_Release(output_type.type); + if (SUCCEEDED(hr)) + { + topology_loader_release_transforms(activates, count); + return hr; + } + } + } + + IMFActivate_ShutdownObject(activates[i]); + } + + topology_loader_release_transforms(activates, count); + + return hr; +} + +static HRESULT connect_to_sink(struct transform_output_type *output_type, struct connect_context *context) +{ + IMFTopologyNode *node; + HRESULT hr; + + if (FAILED(IMFMediaTypeHandler_IsMediaTypeSupported(context->sink_handler, output_type->type, NULL))) + return MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node))) + return hr; + + IMFTopologyNode_SetObject(node, (IUnknown *)output_type->transform); + IMFTopology_AddNode(context->context->output_topology, node); + IMFTopologyNode_ConnectOutput(context->upstream_node, 0, node, 0); + IMFTopologyNode_ConnectOutput(node, 0, context->sink, 0); + + IMFTopologyNode_Release(node); + + hr = IMFMediaTypeHandler_SetCurrentMediaType(context->sink_handler, output_type->type); + if (SUCCEEDED(hr)) + hr = IMFTransform_SetOutputType(output_type->transform, 0, output_type->type, 0); + + return S_OK; +} + +static HRESULT connect_to_converter(struct transform_output_type *output_type, struct connect_context *context) +{ + struct connect_context sink_ctx; + IMFTopologyNode *node; + HRESULT hr; + + if (SUCCEEDED(connect_to_sink(output_type, context))) + return S_OK; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node))) + return hr; + + IMFTopologyNode_SetObject(node, (IUnknown *)output_type->transform); + + sink_ctx = *context; + sink_ctx.upstream_node = node; + + if (SUCCEEDED(hr = topology_loader_enumerate_output_types(context->converter_category, output_type->type, + connect_to_sink, &sink_ctx))) + { + hr = IMFTopology_AddNode(context->context->output_topology, node); + } + IMFTopologyNode_Release(node); + + if (SUCCEEDED(hr)) + { + IMFTopology_AddNode(context->context->output_topology, node); + IMFTopologyNode_ConnectOutput(context->upstream_node, 0, node, 0); + + hr = IMFTransform_SetOutputType(output_type->transform, 0, output_type->type, 0); + } + + return hr; +} + typedef HRESULT (*p_topology_loader_connect_func)(struct topoloader_context *context, IMFTopologyNode *upstream_node, unsigned int output_index, IMFTopologyNode *downstream_node, unsigned int input_index);
-static HRESULT topology_loader_connect_source_node(struct topoloader_context *context, IMFTopologyNode *upstream_node, - unsigned int output_index, IMFTopologyNode *downstream_node, unsigned int input_index) +static HRESULT topology_loader_get_node_type_handler(IMFTopologyNode *node, IMFMediaTypeHandler **handler) { - FIXME("Unimplemented.\n"); + MF_TOPOLOGY_TYPE node_type; + IMFStreamSink *stream_sink; + IMFStreamDescriptor *sd; + IUnknown *object; + HRESULT hr;
- return E_NOTIMPL; + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type))) + return hr; + + switch (node_type) + { + case MF_TOPOLOGY_OUTPUT_NODE: + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, (IUnknown **)&object))) + { + if (SUCCEEDED(hr = IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&stream_sink))) + { + hr = IMFStreamSink_GetMediaTypeHandler(stream_sink, handler); + IMFStreamSink_Release(stream_sink); + } + IUnknown_Release(object); + } + break; + case MF_TOPOLOGY_SOURCESTREAM_NODE: + if (SUCCEEDED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, + &IID_IMFStreamDescriptor, (void **)&sd))) + { + hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, handler); + IMFStreamDescriptor_Release(sd); + } + break; + default: + WARN("Unexpected node type %u.\n", node_type); + return MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT topology_loader_get_mft_categories(IMFMediaTypeHandler *handler, GUID *decode_cat, GUID *convert_cat) +{ + GUID major; + HRESULT hr; + + if (FAILED(hr = IMFMediaTypeHandler_GetMajorType(handler, &major))) + return hr; + + if (IsEqualGUID(&major, &MFMediaType_Audio)) + { + *decode_cat = MFT_CATEGORY_AUDIO_DECODER; + *convert_cat = MFT_CATEGORY_AUDIO_EFFECT; + } + else if (IsEqualGUID(&major, &MFMediaType_Video)) + { + *decode_cat = MFT_CATEGORY_VIDEO_DECODER; + *convert_cat = MFT_CATEGORY_VIDEO_EFFECT; + } + else + { + WARN("Unexpected major type %s.\n", debugstr_guid(&major)); + return MF_E_INVALIDTYPE; + } + + return S_OK; +} + +static HRESULT topology_loader_connect_source_to_sink(struct topoloader_context *context, IMFTopologyNode *source, + unsigned int output_index, IMFTopologyNode *sink, unsigned int input_index) +{ + IMFMediaTypeHandler *source_handler = NULL, *sink_handler = NULL; + struct connect_context convert_ctx, sink_ctx; + MF_CONNECT_METHOD source_method, sink_method; + GUID decode_cat, convert_cat; + IMFMediaType *media_type; + HRESULT hr; + + TRACE("attempting to connect %p:%u to %p:%u\n", source, output_index, sink, input_index); + + if (FAILED(hr = topology_loader_get_node_type_handler(source, &source_handler))) + goto done; + + if (FAILED(hr = topology_loader_get_node_type_handler(sink, &sink_handler))) + goto done; + + if (FAILED(IMFTopologyNode_GetUINT32(source, &MF_TOPONODE_CONNECT_METHOD, &source_method))) + source_method = MF_CONNECT_DIRECT; + if (FAILED(IMFTopologyNode_GetUINT32(sink, &MF_TOPONODE_CONNECT_METHOD, &sink_method))) + sink_method = MF_CONNECT_ALLOW_DECODER; + + if (FAILED(hr = topology_loader_get_mft_categories(source_handler, &decode_cat, &convert_cat))) + goto done; + + sink_ctx.context = context; + sink_ctx.upstream_node = source; + sink_ctx.sink = sink; + sink_ctx.sink_handler = sink_handler; + + convert_ctx = sink_ctx; + convert_ctx.converter_category = &convert_cat; + + if (SUCCEEDED(hr = IMFMediaTypeHandler_GetCurrentMediaType(source_handler, &media_type))) + { + /* Direct connection. */ + if (SUCCEEDED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(sink_handler, media_type, NULL)) + && SUCCEEDED(hr = IMFMediaTypeHandler_SetCurrentMediaType(sink_handler, media_type))) + { + hr = IMFTopologyNode_ConnectOutput(source, output_index, sink, input_index); + } + if (FAILED(hr) && sink_method & MF_CONNECT_ALLOW_CONVERTER) + { + hr = topology_loader_enumerate_output_types(&convert_cat, media_type, connect_to_sink, &sink_ctx); + } + if (FAILED(hr) && sink_method & MF_CONNECT_ALLOW_DECODER) + { + hr = topology_loader_enumerate_output_types(&decode_cat, media_type, connect_to_converter, &convert_ctx); + } + + IMFMediaType_Release(media_type); + } + +done: + if (source_handler) + IMFMediaTypeHandler_Release(source_handler); + if (sink_handler) + IMFMediaTypeHandler_Release(sink_handler); + + return hr; }
static HRESULT topology_loader_resolve_branch(struct topoloader_context *context, IMFTopologyNode *upstream_node, @@ -2009,7 +2272,7 @@ static HRESULT topology_loader_resolve_branch(struct topoloader_context *context static const p_topology_loader_connect_func connectors[MF_TOPOLOGY_TEE_NODE+1][MF_TOPOLOGY_TEE_NODE+1] = { /* OUTPUT */ { NULL }, - /* SOURCESTREAM */ { topology_loader_connect_source_node, NULL, NULL, NULL }, + /* SOURCESTREAM */ { topology_loader_connect_source_to_sink, NULL, NULL, NULL }, /* TRANSFORM */ { NULL }, /* TEE */ { NULL }, };