1
0
mirror of https://github.com/wine-mirror/wine synced 2024-07-08 20:06:18 +00:00
wine/dlls/mf/topology_loader.c

839 lines
29 KiB
C

/*
* Copyright 2017 Nikolay Sivov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include <stddef.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "mfidl.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "mf_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
struct topology_loader
{
IMFTopoLoader IMFTopoLoader_iface;
LONG refcount;
};
static inline struct topology_loader *impl_from_IMFTopoLoader(IMFTopoLoader *iface)
{
return CONTAINING_RECORD(iface, struct topology_loader, IMFTopoLoader_iface);
}
static HRESULT WINAPI topology_loader_QueryInterface(IMFTopoLoader *iface, REFIID riid, void **out)
{
TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
if (IsEqualIID(riid, &IID_IMFTopoLoader)
|| IsEqualIID(riid, &IID_IUnknown))
{
*out = iface;
IMFTopoLoader_AddRef(iface);
return S_OK;
}
WARN("Unsupported %s.\n", debugstr_guid(riid));
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI topology_loader_AddRef(IMFTopoLoader *iface)
{
struct topology_loader *loader = impl_from_IMFTopoLoader(iface);
ULONG refcount = InterlockedIncrement(&loader->refcount);
TRACE("iface %p, refcount %lu.\n", iface, refcount);
return refcount;
}
static ULONG WINAPI topology_loader_Release(IMFTopoLoader *iface)
{
struct topology_loader *loader = impl_from_IMFTopoLoader(iface);
ULONG refcount = InterlockedDecrement(&loader->refcount);
TRACE("iface %p, refcount %lu.\n", iface, refcount);
if (!refcount)
free(loader);
return refcount;
}
struct topoloader_context
{
IMFTopology *input_topology;
IMFTopology *output_topology;
};
static HRESULT topology_loader_clone_node(struct topoloader_context *context, IMFTopologyNode *node, IMFTopologyNode **clone)
{
MF_TOPOLOGY_TYPE node_type;
HRESULT hr;
TOPOID id;
if (FAILED(hr = IMFTopologyNode_GetTopoNodeID(node, &id)))
return hr;
if (SUCCEEDED(hr = IMFTopology_GetNodeByID(context->output_topology, id, clone)))
return hr;
IMFTopologyNode_GetNodeType(node, &node_type);
if (FAILED(hr = MFCreateTopologyNode(node_type, clone)))
return hr;
hr = IMFTopologyNode_CloneFrom(*clone, node);
if (SUCCEEDED(hr))
hr = IMFTopology_AddNode(context->output_topology, *clone);
if (FAILED(hr))
{
IMFTopologyNode_Release(*clone);
*clone = NULL;
}
return hr;
}
struct transform_output_type
{
IMFMediaType *type;
IMFTransform *transform;
IMFActivate *activate;
};
struct connect_context
{
struct topoloader_context *context;
IMFTopologyNode *upstream_node;
IMFTopologyNode *sink;
IMFMediaTypeHandler *sink_handler;
unsigned int output_index;
unsigned int input_index;
GUID converter_category;
GUID decoder_category;
};
struct topology_branch
{
struct
{
IMFTopologyNode *node;
DWORD stream;
} up, down;
struct list entry;
};
static const char *debugstr_topology_branch(struct topology_branch *branch)
{
return wine_dbg_sprintf("%p:%lu to %p:%lu", branch->up.node, branch->up.stream, branch->down.node, branch->down.stream);
}
static HRESULT topology_branch_create(IMFTopologyNode *source, DWORD source_stream,
IMFTopologyNode *sink, DWORD sink_stream, struct topology_branch **out)
{
struct topology_branch *branch;
if (!(branch = calloc(1, sizeof(*branch))))
return E_OUTOFMEMORY;
IMFTopologyNode_AddRef((branch->up.node = source));
branch->up.stream = source_stream;
IMFTopologyNode_AddRef((branch->down.node = sink));
branch->down.stream = sink_stream;
*out = branch;
return S_OK;
}
static void topology_branch_destroy(struct topology_branch *branch)
{
IMFTopologyNode_Release(branch->up.node);
IMFTopologyNode_Release(branch->down.node);
free(branch);
}
static HRESULT topology_branch_clone_nodes(struct topoloader_context *context, struct topology_branch *branch)
{
IMFTopologyNode *up, *down;
HRESULT hr;
if (FAILED(hr = topology_loader_clone_node(context, branch->up.node, &up)))
return hr;
if (FAILED(hr = topology_loader_clone_node(context, branch->down.node, &down)))
{
IMFTopologyNode_Release(up);
return hr;
}
IMFTopologyNode_Release(branch->up.node);
IMFTopologyNode_Release(branch->down.node);
branch->up.node = up;
branch->down.node = down;
return hr;
}
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;
PropVariantInit(&value);
if (SUCCEEDED(*hr) && FAILED(IMFMediaType_GetItem(dst, attr, NULL))
&& SUCCEEDED(IMFMediaType_GetItem(src, attr, &value)))
*hr = IMFMediaType_SetItem(dst, attr, &value);
PropVariantClear(&value);
}
/* update a media type with additional attributes reported by upstream element */
/* also present in mfreadwrite/reader.c pipeline */
static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMediaType *upstream_type)
{
HRESULT hr = S_OK;
/* propagate common video attributes */
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_SIZE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_RATE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_DEFAULT_STRIDE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_ROTATION, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FIXED_SIZE_SAMPLES, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_PIXEL_ASPECT_RATIO, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_CHROMA_SITING, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_INTERLACE_MODE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_TRANSFER_FUNCTION, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_PRIMARIES, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_YUV_MATRIX, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_LIGHTING, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_NOMINAL_RANGE, &hr);
/* propagate common audio attributes */
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_NUM_CHANNELS, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_CHANNEL_MASK, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_SAMPLES_PER_BLOCK, &hr);
media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &hr);
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,
struct topology_branch *branch, IMFMediaType *up_type);
static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNECT_METHOD method_mask,
struct topology_branch *branch, IMFMediaType *up_type, IMFMediaType *down_type)
{
BOOL decoder = (method_mask & MF_CONNECT_ALLOW_DECODER) == MF_CONNECT_ALLOW_DECODER;
MFT_REGISTER_TYPE_INFO input_info, output_info;
IMFTransform *transform;
IMFActivate **activates;
IMFTopologyNode *node;
unsigned int i, count;
GUID category, guid;
HRESULT hr;
TRACE("topology %p, method_mask %#x, branch %s, up_type %p, down_type %p.\n",
topology, method_mask, debugstr_topology_branch(branch), up_type, down_type);
if (FAILED(hr = IMFMediaType_GetMajorType(up_type, &input_info.guidMajorType)))
return hr;
if (FAILED(hr = IMFMediaType_GetGUID(up_type, &MF_MT_SUBTYPE, &input_info.guidSubtype)))
return hr;
if (!down_type)
output_info = input_info;
else
{
if (FAILED(hr = IMFMediaType_GetMajorType(down_type, &output_info.guidMajorType)))
return hr;
if (FAILED(hr = IMFMediaType_GetGUID(down_type, &MF_MT_SUBTYPE, &output_info.guidSubtype)))
return hr;
}
if (IsEqualGUID(&input_info.guidMajorType, &MFMediaType_Audio))
category = decoder ? MFT_CATEGORY_AUDIO_DECODER : MFT_CATEGORY_AUDIO_EFFECT;
else if (IsEqualGUID(&input_info.guidMajorType, &MFMediaType_Video))
category = decoder ? MFT_CATEGORY_VIDEO_DECODER : MFT_CATEGORY_VIDEO_PROCESSOR;
else
return MF_E_INVALIDMEDIATYPE;
if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node)))
return hr;
if (!decoder)
method_mask = MF_CONNECT_DIRECT;
else
{
IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_DECODER, 1);
method_mask = MF_CONNECT_ALLOW_CONVERTER;
}
if (FAILED(hr = MFTEnumEx(category, MFT_ENUM_FLAG_ALL, &input_info, decoder ? NULL : &output_info, &activates, &count)))
return hr;
for (i = 0, hr = MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION; i < count; ++i)
{
struct topology_branch down_branch = {.up.node = node, .down = branch->down};
struct topology_branch up_branch = {.up = branch->up, .down.node = node};
MF_CONNECT_METHOD method = method_mask;
IMFMediaType *media_type;
if (FAILED(IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void **)&transform)))
continue;
IMFTopologyNode_SetObject(node, (IUnknown *)transform);
IMFTopologyNode_DeleteItem(node, &MF_TOPONODE_TRANSFORM_OBJECTID);
if (SUCCEEDED(IMFActivate_GetGUID(activates[i], &MFT_TRANSFORM_CLSID_Attribute, &guid)))
IMFTopologyNode_SetGUID(node, &MF_TOPONODE_TRANSFORM_OBJECTID, &guid);
hr = topology_branch_connect_down(topology, MF_CONNECT_DIRECT, &up_branch, up_type);
if (down_type)
{
if (SUCCEEDED(update_media_type_from_upstream(down_type, up_type))
&& SUCCEEDED(IMFTransform_SetOutputType(transform, 0, down_type, 0)))
method = MF_CONNECT_DIRECT;
}
IMFTransform_Release(transform);
if (SUCCEEDED(hr) && method != MF_CONNECT_DIRECT
&& SUCCEEDED(IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type)))
{
if (SUCCEEDED(update_media_type_from_upstream(media_type, up_type)))
IMFTransform_SetOutputType(transform, 0, media_type, 0);
IMFMediaType_Release(media_type);
}
if (SUCCEEDED(hr))
hr = topology_branch_connect(topology, method, &down_branch, !down_type);
if (SUCCEEDED(hr))
hr = IMFTopology_AddNode(topology, node);
if (SUCCEEDED(hr))
break;
}
IMFTopologyNode_Release(node);
for (i = 0; i < count; ++i)
IMFActivate_Release(activates[i]);
CoTaskMemFree(activates);
if (!count)
return MF_E_TOPO_CODEC_NOT_FOUND;
return hr;
}
static HRESULT get_first_supported_media_type(IMFMediaTypeHandler *handler, IMFMediaType **type)
{
IMFMediaType *media_type;
HRESULT hr;
DWORD i;
hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, type);
if (hr != MF_E_NOT_INITIALIZED)
return hr;
for (i = 0; SUCCEEDED(hr = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, i, &media_type)); i++)
{
if (SUCCEEDED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(handler, media_type, NULL)))
{
*type = media_type;
return hr;
}
IMFMediaType_Release(media_type);
}
return hr;
}
HRESULT topology_node_init_media_type(IMFTopologyNode *node, DWORD stream, BOOL output, IMFMediaType **type)
{
IMFMediaTypeHandler *handler;
HRESULT hr;
if (SUCCEEDED(hr = topology_node_get_type_handler(node, stream, output, &handler)))
{
if (SUCCEEDED(hr = get_first_supported_media_type(handler, type)))
hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, *type);
IMFMediaTypeHandler_Release(handler);
}
return hr;
}
static HRESULT topology_branch_connect_down(IMFTopology *topology, MF_CONNECT_METHOD method_mask,
struct topology_branch *branch, IMFMediaType *up_type)
{
IMFMediaTypeHandler *down_handler;
IMFMediaType *down_type = NULL;
MF_TOPOLOGY_TYPE type;
UINT32 method;
DWORD flags;
HRESULT hr;
TRACE("topology %p, method_mask %#x, branch %s, up_type %p.\n",
topology, method_mask, debugstr_topology_branch(branch), up_type);
if (FAILED(IMFTopologyNode_GetUINT32(branch->down.node, &MF_TOPONODE_CONNECT_METHOD, &method)))
method = MF_CONNECT_ALLOW_DECODER;
if (FAILED(hr = topology_node_get_type_handler(branch->down.node, branch->down.stream, FALSE, &down_handler)))
return hr;
if (SUCCEEDED(hr = get_first_supported_media_type(down_handler, &down_type))
&& IMFMediaType_IsEqual(up_type, down_type, &flags) == S_OK)
{
TRACE("Connecting branch %s with current type %p.\n", debugstr_topology_branch(branch), up_type);
hr = IMFTopologyNode_ConnectOutput(branch->up.node, branch->up.stream, branch->down.node, branch->down.stream);
goto done;
}
if (SUCCEEDED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(down_handler, up_type, NULL)))
{
TRACE("Connected branch %s with upstream type %p.\n", debugstr_topology_branch(branch), up_type);
if (SUCCEEDED(IMFTopologyNode_GetNodeType(branch->down.node, &type)) && type == MF_TOPOLOGY_TRANSFORM_NODE
&& FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(down_handler, up_type)))
WARN("Failed to set transform node media type, hr %#lx\n", hr);
hr = IMFTopologyNode_ConnectOutput(branch->up.node, branch->up.stream, branch->down.node, branch->down.stream);
goto done;
}
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, up_type, down_type);
if (FAILED(hr) && (method & method_mask & MF_CONNECT_ALLOW_DECODER) == MF_CONNECT_ALLOW_DECODER)
hr = topology_branch_connect_indirect(topology, MF_CONNECT_ALLOW_DECODER,
branch, up_type, down_type);
done:
if (down_type)
IMFMediaType_Release(down_type);
IMFMediaTypeHandler_Release(down_handler);
return hr;
}
static HRESULT topology_branch_foreach_up_types(IMFTopology *topology, MF_CONNECT_METHOD method_mask,
struct topology_branch *branch)
{
IMFMediaTypeHandler *handler;
IMFMediaType *type;
DWORD index = 0;
HRESULT hr;
if (FAILED(hr = topology_node_get_type_handler(branch->up.node, branch->up.stream, TRUE, &handler)))
return hr;
while (SUCCEEDED(hr = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, index++, &type)))
{
hr = topology_branch_connect_down(topology, method_mask, branch, type);
if (SUCCEEDED(hr))
hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, type);
IMFMediaType_Release(type);
if (SUCCEEDED(hr))
break;
}
IMFMediaTypeHandler_Release(handler);
return hr;
}
static HRESULT topology_branch_connect(IMFTopology *topology, MF_CONNECT_METHOD method_mask,
struct topology_branch *branch, BOOL enumerate_source_types)
{
UINT32 method;
HRESULT hr;
TRACE("topology %p, method_mask %#x, branch %s.\n", topology, method_mask, debugstr_topology_branch(branch));
if (FAILED(IMFTopologyNode_GetUINT32(branch->up.node, &MF_TOPONODE_CONNECT_METHOD, &method)))
method = MF_CONNECT_DIRECT;
if (enumerate_source_types)
{
if (method & MF_CONNECT_RESOLVE_INDEPENDENT_OUTPUTTYPES)
hr = topology_branch_foreach_up_types(topology, method_mask & MF_CONNECT_ALLOW_DECODER, branch);
else
{
hr = topology_branch_foreach_up_types(topology, method_mask & MF_CONNECT_DIRECT, branch);
if (FAILED(hr))
hr = topology_branch_foreach_up_types(topology, method_mask & MF_CONNECT_ALLOW_CONVERTER, branch);
if (FAILED(hr))
hr = topology_branch_foreach_up_types(topology, method_mask & MF_CONNECT_ALLOW_DECODER, branch);
}
}
else
{
IMFMediaTypeHandler *up_handler;
IMFMediaType *up_type;
if (FAILED(hr = topology_node_get_type_handler(branch->up.node, branch->up.stream, TRUE, &up_handler)))
return hr;
if (SUCCEEDED(hr = IMFMediaTypeHandler_GetCurrentMediaType(up_handler, &up_type))
|| SUCCEEDED(hr = IMFMediaTypeHandler_GetMediaTypeByIndex(up_handler, 0, &up_type)))
{
hr = topology_branch_connect_down(topology, method_mask, branch, up_type);
IMFMediaType_Release(up_type);
}
IMFMediaTypeHandler_Release(up_handler);
}
TRACE("returning %#lx\n", hr);
return hr;
}
static HRESULT topology_loader_resolve_branches(struct topoloader_context *context, struct list *branches,
BOOL enumerate_source_types)
{
struct list new_branches = LIST_INIT(new_branches);
struct topology_branch *branch, *next;
MF_TOPOLOGY_TYPE node_type;
HRESULT hr = S_OK;
LIST_FOR_EACH_ENTRY_SAFE(branch, next, branches, struct topology_branch, entry)
{
list_remove(&branch->entry);
if (FAILED(hr = topology_node_list_branches(branch->down.node, &new_branches)))
WARN("Failed to list branches from branch %s\n", debugstr_topology_branch(branch));
else if (FAILED(hr = IMFTopologyNode_GetNodeType(branch->up.node, &node_type)))
WARN("Failed to get source node type for branch %s\n", debugstr_topology_branch(branch));
else 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, MF_CONNECT_ALLOW_DECODER, branch, enumerate_source_types);
if (hr == MF_E_INVALIDMEDIATYPE && !enumerate_source_types && node_type == MF_TOPOLOGY_TRANSFORM_NODE)
hr = topology_branch_connect(context->output_topology, MF_CONNECT_ALLOW_DECODER, branch, TRUE);
}
topology_branch_destroy(branch);
if (FAILED(hr))
break;
}
list_move_tail(branches, &new_branches);
return hr;
}
static BOOL topology_loader_is_node_d3d_aware(IMFTopologyNode *node)
{
IMFAttributes *attributes;
unsigned int d3d_aware = 0;
IMFTransform *transform;
if (FAILED(topology_node_get_object(node, &IID_IMFAttributes, (void **)&attributes)))
return FALSE;
IMFAttributes_GetUINT32(attributes, &MF_SA_D3D_AWARE, &d3d_aware);
IMFAttributes_Release(attributes);
if (!d3d_aware && SUCCEEDED(topology_node_get_object(node, &IID_IMFTransform, (void **)&transform)))
{
d3d_aware = mf_is_sample_copier_transform(transform);
IMFTransform_Release(transform);
}
return !!d3d_aware;
}
static HRESULT topology_loader_create_copier(IMFTopologyNode *upstream_node, DWORD upstream_output,
IMFTopologyNode *downstream_node, unsigned int downstream_input, IMFTransform **copier)
{
IMFMediaType *up_type = NULL;
IMFTransform *transform;
HRESULT hr;
if (FAILED(hr = MFCreateSampleCopierMFT(&transform)))
return hr;
if (FAILED(hr = MFGetTopoNodeCurrentType(upstream_node, upstream_output, TRUE, &up_type)))
WARN("Failed to get upstream media type hr %#lx.\n", hr);
if (SUCCEEDED(hr) && FAILED(hr = IMFTransform_SetInputType(transform, 0, up_type, 0)))
WARN("Input type wasn't accepted, hr %#lx.\n", hr);
/* We assume, that up_type is set to a value compatible with the down node by the branch resolver. */
if (SUCCEEDED(hr) && FAILED(hr = IMFTransform_SetOutputType(transform, 0, up_type, 0)))
WARN("Output type wasn't accepted, hr %#lx.\n", hr);
if (SUCCEEDED(hr))
{
*copier = transform;
IMFTransform_AddRef(*copier);
}
if (up_type)
IMFMediaType_Release(up_type);
IMFTransform_Release(transform);
return hr;
}
static HRESULT topology_loader_connect_copier(struct topoloader_context *context, IMFTopologyNode *upstream_node,
DWORD upstream_output, IMFTopologyNode *downstream_node, DWORD downstream_input, IMFTransform *copier)
{
IMFTopologyNode *copier_node;
HRESULT hr;
if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &copier_node)))
return hr;
IMFTopologyNode_SetObject(copier_node, (IUnknown *)copier);
IMFTopology_AddNode(context->output_topology, copier_node);
IMFTopologyNode_ConnectOutput(upstream_node, upstream_output, copier_node, 0);
IMFTopologyNode_ConnectOutput(copier_node, 0, downstream_node, downstream_input);
IMFTopologyNode_Release(copier_node);
return S_OK;
}
/* Right now this should be used for output nodes only. */
static HRESULT topology_loader_connect_d3d_aware_input(struct topoloader_context *context,
IMFTopologyNode *node)
{
IMFTopologyNode *upstream_node;
IMFTransform *copier = NULL;
IMFStreamSink *stream_sink;
DWORD upstream_output;
HRESULT hr;
if (FAILED(hr = topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink)))
return hr;
if (topology_loader_is_node_d3d_aware(node))
{
if (SUCCEEDED(IMFTopologyNode_GetInput(node, 0, &upstream_node, &upstream_output)))
{
if (!topology_loader_is_node_d3d_aware(upstream_node))
{
if (SUCCEEDED(hr = topology_loader_create_copier(upstream_node, upstream_output, node, 0, &copier)))
{
hr = topology_loader_connect_copier(context, upstream_node, upstream_output, node, 0, copier);
IMFTransform_Release(copier);
}
}
IMFTopologyNode_Release(upstream_node);
}
}
IMFStreamSink_Release(stream_sink);
return hr;
}
static void topology_loader_resolve_complete(struct topoloader_context *context)
{
MF_TOPOLOGY_TYPE node_type;
IMFTopologyNode *node;
WORD i, node_count;
HRESULT hr;
IMFTopology_GetNodeCount(context->output_topology, &node_count);
for (i = 0; i < node_count; ++i)
{
if (SUCCEEDED(IMFTopology_GetNode(context->output_topology, i, &node)))
{
IMFTopologyNode_GetNodeType(node, &node_type);
if (node_type == MF_TOPOLOGY_OUTPUT_NODE)
{
/* Set MF_TOPONODE_STREAMID for all outputs. */
if (FAILED(IMFTopologyNode_GetItem(node, &MF_TOPONODE_STREAMID, NULL)))
IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_STREAMID, 0);
if (FAILED(hr = topology_loader_connect_d3d_aware_input(context, node)))
WARN("Failed to connect D3D-aware input, hr %#lx.\n", hr);
}
else if (node_type == MF_TOPOLOGY_SOURCESTREAM_NODE)
{
/* Set MF_TOPONODE_MEDIASTART for all sources. */
if (FAILED(IMFTopologyNode_GetItem(node, &MF_TOPONODE_MEDIASTART, NULL)))
IMFTopologyNode_SetUINT64(node, &MF_TOPONODE_MEDIASTART, 0);
}
IMFTopologyNode_Release(node);
}
}
}
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;
UINT32 enumerate_source_types;
IMFTopology *output_topology;
MF_TOPOLOGY_TYPE node_type;
IMFTopologyNode *node;
unsigned short i = 0;
IMFStreamSink *sink;
IUnknown *object;
HRESULT hr = E_FAIL;
FIXME("iface %p, input_topology %p, ret_topology %p, current_topology %p stub!\n",
iface, input_topology, ret_topology, current_topology);
if (current_topology)
FIXME("Current topology instance is ignored.\n");
/* Basic sanity checks for input topology:
- source nodes must have stream descriptor set;
- sink nodes must be resolved to stream sink objects;
*/
while (SUCCEEDED(IMFTopology_GetNode(input_topology, i++, &node)))
{
IMFTopologyNode_GetNodeType(node, &node_type);
switch (node_type)
{
case MF_TOPOLOGY_OUTPUT_NODE:
if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object)))
{
/* Sinks must be bound beforehand. */
if (FAILED(IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&sink)))
hr = MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED;
else if (sink)
IMFStreamSink_Release(sink);
IUnknown_Release(object);
}
break;
case MF_TOPOLOGY_SOURCESTREAM_NODE:
hr = IMFTopologyNode_GetItem(node, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL);
break;
default:
;
}
IMFTopologyNode_Release(node);
if (FAILED(hr))
return hr;
}
if (FAILED(hr = MFCreateTopology(&output_topology)))
return hr;
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++)
{
hr = topology_node_list_branches(node, &branches);
IMFTopologyNode_Release(node);
if (FAILED(hr))
break;
}
if (SUCCEEDED(hr) && list_empty(&branches))
hr = MF_E_TOPO_UNSUPPORTED;
if (FAILED(IMFTopology_GetUINT32(input_topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES,
&enumerate_source_types)))
enumerate_source_types = 0;
while (SUCCEEDED(hr) && !list_empty(&branches))
hr = topology_loader_resolve_branches(&context, &branches, enumerate_source_types);
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 (FAILED(hr))
IMFTopology_Release(output_topology);
else
{
topology_loader_resolve_complete(&context);
*ret_topology = output_topology;
}
return hr;
}
static const IMFTopoLoaderVtbl topology_loader_vtbl =
{
topology_loader_QueryInterface,
topology_loader_AddRef,
topology_loader_Release,
topology_loader_Load,
};
/***********************************************************************
* MFCreateTopoLoader (mf.@)
*/
HRESULT WINAPI MFCreateTopoLoader(IMFTopoLoader **loader)
{
struct topology_loader *object;
TRACE("loader %p.\n", loader);
if (!loader)
return E_POINTER;
if (!(object = calloc(1, sizeof(*object))))
return E_OUTOFMEMORY;
object->IMFTopoLoader_iface.lpVtbl = &topology_loader_vtbl;
object->refcount = 1;
*loader = &object->IMFTopoLoader_iface;
return S_OK;
}