Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/tests/mf.c | 115 +++++++++++++++++++++- dlls/mf/topology.c | 235 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 308 insertions(+), 42 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 4974e043af..8b3b8b4373 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -39,7 +39,15 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
#include "wine/test.h"
-DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) +static void _expect_ref(IUnknown* obj, ULONG expected_refcount, int line) +{ + ULONG refcount; + IUnknown_AddRef(obj); + refcount = IUnknown_Release(obj); + ok_(__FILE__, line)(refcount == expected_refcount, "Unexpected refcount %d, expected %d.\n", refcount, + expected_refcount); +}
static HRESULT WINAPI test_unk_QueryInterface(IUnknown *iface, REFIID riid, void **obj) { @@ -79,9 +87,10 @@ static void test_topology(void) IMFTopologyNode *node, *node2, *node3; IMFMediaType *mediatype, *mediatype2; IMFTopology *topology, *topology2; + MF_TOPOLOGY_TYPE node_type; + UINT32 count, index; IUnknown *object; WORD node_count; - UINT32 count; DWORD size; HRESULT hr; TOPOID id; @@ -520,6 +529,108 @@ static void test_topology(void) }
IMFTopology_Release(topology); + + /* Connect nodes. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr); + + EXPECT_REF(node, 1); + EXPECT_REF(node2, 1); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr); + + EXPECT_REF(node, 2); + EXPECT_REF(node2, 2); + + IMFTopologyNode_Release(node); + + EXPECT_REF(node, 1); + EXPECT_REF(node2, 2); + + IMFTopologyNode_Release(node2); + + EXPECT_REF(node, 1); + EXPECT_REF(node2, 1); + + hr = IMFTopologyNode_GetNodeType(node2, &node_type); + ok(hr == S_OK, "Failed to get node type, hr %#x.\n", hr); + + IMFTopologyNode_Release(node); + + /* Connect within topology. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#x.\n", hr); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#x.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr); + + EXPECT_REF(node, 2); + EXPECT_REF(node2, 2); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr); + + EXPECT_REF(node, 3); + EXPECT_REF(node2, 3); + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#x.\n", hr); + +todo_wine { + EXPECT_REF(node, 1); + EXPECT_REF(node2, 1); +} + /* Removing connected node breaks connection. */ + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr); + + hr = IMFTopology_RemoveNode(topology, node); + ok(hr == S_OK, "Failed to remove a node, hr %#x.\n", hr); + +todo_wine { + EXPECT_REF(node, 1); + EXPECT_REF(node2, 2); +} + hr = IMFTopologyNode_GetOutput(node, 0, &node3, &index); +todo_wine + ok(hr == MF_E_NOT_FOUND, "Unexpected hr %#x.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#x.\n", hr); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#x.\n", hr); + + hr = IMFTopology_RemoveNode(topology, node2); + ok(hr == S_OK, "Failed to remove a node, hr %#x.\n", hr); + +todo_wine { + EXPECT_REF(node, 2); + EXPECT_REF(node2, 1); +} + IMFTopologyNode_Release(node); + IMFTopologyNode_Release(node2); + + IMFTopology_Release(topology); }
static HRESULT WINAPI test_getservice_QI(IMFGetService *iface, REFIID riid, void **obj) diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c index 298200c40d..eaae7c910c 100644 --- a/dlls/mf/topology.c +++ b/dlls/mf/topology.c @@ -57,6 +57,15 @@ struct topology struct node_stream { IMFMediaType *preferred_type; + struct topology_node *connection; + DWORD connection_stream; +}; + +struct node_streams +{ + struct node_stream *streams; + size_t size; + size_t count; };
struct topology_node @@ -67,18 +76,8 @@ struct topology_node MF_TOPOLOGY_TYPE node_type; TOPOID id; IUnknown *object; - struct - { - struct node_stream *streams; - size_t size; - size_t count; - } inputs; - struct - { - struct node_stream *streams; - size_t size; - size_t count; - } outputs; + struct node_streams inputs; + struct node_streams outputs; CRITICAL_SECTION cs; };
@@ -104,6 +103,15 @@ static struct topology_node *impl_from_IMFTopologyNode(IMFTopologyNode *iface) return CONTAINING_RECORD(iface, struct topology_node, IMFTopologyNode_iface); }
+static const IMFTopologyNodeVtbl topologynodevtbl; + +static struct topology_node *unsafe_impl_from_IMFTopologyNode(IMFTopologyNode *iface) +{ + if (!iface || iface->lpVtbl != &topologynodevtbl) + return NULL; + return impl_from_IMFTopologyNode(iface); +} + static struct topology_loader *impl_from_IMFTopoLoader(IMFTopoLoader *iface) { return CONTAINING_RECORD(iface, struct topology_loader, IMFTopoLoader_iface); @@ -114,6 +122,20 @@ static struct seq_source *impl_from_IMFSequencerSource(IMFSequencerSource *iface return CONTAINING_RECORD(iface, struct seq_source, IMFSequencerSource_iface); }
+static HRESULT topology_node_reserve_streams(struct node_streams *streams, DWORD index) +{ + if (!mf_array_reserve((void **)&streams->streams, &streams->size, index + 1, sizeof(*streams->streams))) + return E_OUTOFMEMORY; + + if (index >= streams->count) + { + memset(&streams->streams[streams->count], 0, (index - streams->count + 1) * sizeof(*streams->streams)); + streams->count = index + 1; + } + + return S_OK; +} + static HRESULT WINAPI topology_QueryInterface(IMFTopology *iface, REFIID riid, void **out) { struct topology *topology = impl_from_IMFTopology(iface); @@ -459,14 +481,17 @@ static HRESULT topology_get_node_by_id(const struct topology *topology, TOPOID i IMFTopologyNode *iter; unsigned int i = 0;
- while (IMFCollection_GetElement(topology->nodes, i, (IUnknown **)&iter) == S_OK) + while (IMFCollection_GetElement(topology->nodes, i++, (IUnknown **)&iter) == S_OK) { TOPOID node_id; HRESULT hr;
hr = IMFTopologyNode_GetTopoNodeID(iter, &node_id); if (FAILED(hr)) + { + IMFTopologyNode_Release(iter); return hr; + }
if (node_id == id) { @@ -474,7 +499,7 @@ static HRESULT topology_get_node_by_id(const struct topology *topology, TOPOID i return S_OK; }
- ++i; + IMFTopologyNode_Release(iter); }
return MF_E_NOT_FOUND; @@ -1218,35 +1243,177 @@ static HRESULT WINAPI topology_node_GetOutputCount(IMFTopologyNode *iface, DWORD return S_OK; }
+static HRESULT topology_node_disconnect_output(struct topology_node *node, DWORD output_index) +{ + struct topology_node *connection = NULL; + struct node_stream *stream; + DWORD connection_stream; + HRESULT hr = S_OK; + + EnterCriticalSection(&node->cs); + + if (output_index < node->outputs.count) + { + stream = &node->outputs.streams[output_index]; + + if (stream->connection) + { + connection = stream->connection; + connection_stream = stream->connection_stream; + stream->connection = NULL; + stream->connection_stream = 0; + } + else + hr = MF_E_NOT_FOUND; + } + else + hr = E_INVALIDARG; + + LeaveCriticalSection(&node->cs); + + if (connection) + { + EnterCriticalSection(&connection->cs); + + if (connection_stream < connection->inputs.count) + { + stream = &connection->inputs.streams[connection_stream]; + + if (stream->connection) + { + stream->connection = NULL; + stream->connection_stream = 0; + } + } + + LeaveCriticalSection(&connection->cs); + + IMFTopologyNode_Release(&connection->IMFTopologyNode_iface); + IMFTopologyNode_Release(&node->IMFTopologyNode_iface); + } + + return hr; +} + static HRESULT WINAPI topology_node_ConnectOutput(IMFTopologyNode *iface, DWORD output_index, - IMFTopologyNode *node, DWORD input_index) + IMFTopologyNode *peer, DWORD input_index) { - FIXME("(%p)->(%u, %p, %u)\n", iface, output_index, node, input_index); + struct topology_node *node = impl_from_IMFTopologyNode(iface); + struct topology_node *connection = unsafe_impl_from_IMFTopologyNode(peer); + struct node_stream *stream; + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %u, %p, %u.\n", iface, output_index, peer, input_index); + + if (!connection) + { + WARN("External node implementations are not supported.\n"); + return E_UNEXPECTED; + } + + if (node->node_type == MF_TOPOLOGY_OUTPUT_NODE || connection->node_type == MF_TOPOLOGY_SOURCESTREAM_NODE) + return E_FAIL; + + EnterCriticalSection(&node->cs); + EnterCriticalSection(&connection->cs); + + topology_node_disconnect_output(node, output_index); + if (input_index < connection->inputs.count) + { + stream = &connection->inputs.streams[input_index]; + if (stream->connection) + topology_node_disconnect_output(stream->connection, stream->connection_stream); + } + + hr = topology_node_reserve_streams(&node->outputs, output_index); + if (SUCCEEDED(hr)) + hr = topology_node_reserve_streams(&connection->inputs, input_index); + + if (SUCCEEDED(hr)) + { + node->outputs.streams[output_index].connection = connection; + IMFTopologyNode_AddRef(&node->outputs.streams[output_index].connection->IMFTopologyNode_iface); + node->outputs.streams[output_index].connection_stream = input_index; + connection->inputs.streams[input_index].connection = node; + IMFTopologyNode_AddRef(&connection->inputs.streams[input_index].connection->IMFTopologyNode_iface); + connection->inputs.streams[input_index].connection_stream = output_index; + } + + LeaveCriticalSection(&connection->cs); + LeaveCriticalSection(&node->cs); + + return hr; }
-static HRESULT WINAPI topology_node_DisconnectOutput(IMFTopologyNode *iface, DWORD index) +static HRESULT WINAPI topology_node_DisconnectOutput(IMFTopologyNode *iface, DWORD output_index) { - FIXME("(%p)->(%u)\n", iface, index); + struct topology_node *node = impl_from_IMFTopologyNode(iface);
- return E_NOTIMPL; + TRACE("%p, %u.\n", iface, output_index); + + return topology_node_disconnect_output(node, output_index); }
-static HRESULT WINAPI topology_node_GetInput(IMFTopologyNode *iface, DWORD input_index, IMFTopologyNode **node, +static HRESULT WINAPI topology_node_GetInput(IMFTopologyNode *iface, DWORD input_index, IMFTopologyNode **ret, DWORD *output_index) { - FIXME("(%p)->(%u, %p, %p)\n", iface, input_index, node, output_index); + struct topology_node *node = impl_from_IMFTopologyNode(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %u, %p, %p.\n", iface, input_index, ret, output_index); + + EnterCriticalSection(&node->cs); + + if (input_index < node->inputs.count) + { + const struct node_stream *stream = &node->inputs.streams[input_index]; + + if (stream->connection) + { + *ret = &stream->connection->IMFTopologyNode_iface; + IMFTopologyNode_AddRef(*ret); + *output_index = stream->connection_stream; + } + else + hr = MF_E_NOT_FOUND; + } + else + hr = E_INVALIDARG; + + LeaveCriticalSection(&node->cs); + + return hr; }
-static HRESULT WINAPI topology_node_GetOutput(IMFTopologyNode *iface, DWORD output_index, IMFTopologyNode **node, +static HRESULT WINAPI topology_node_GetOutput(IMFTopologyNode *iface, DWORD output_index, IMFTopologyNode **ret, DWORD *input_index) { - FIXME("(%p)->(%u, %p, %p)\n", iface, output_index, node, input_index); + struct topology_node *node = impl_from_IMFTopologyNode(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %u, %p, %p.\n", iface, output_index, ret, input_index); + + EnterCriticalSection(&node->cs); + + if (output_index < node->outputs.count) + { + const struct node_stream *stream = &node->outputs.streams[output_index]; + + if (stream->connection) + { + *ret = &stream->connection->IMFTopologyNode_iface; + IMFTopologyNode_AddRef(*ret); + *input_index = stream->connection_stream; + } + else + hr = MF_E_NOT_FOUND; + } + else + hr = E_INVALIDARG; + + LeaveCriticalSection(&node->cs); + + return hr; }
static HRESULT WINAPI topology_node_SetOutputPrefType(IMFTopologyNode *iface, DWORD index, IMFMediaType *mediatype) @@ -1260,23 +1427,14 @@ static HRESULT WINAPI topology_node_SetOutputPrefType(IMFTopologyNode *iface, DW
if (node->node_type != MF_TOPOLOGY_OUTPUT_NODE) { - if (mf_array_reserve((void **)&node->outputs.streams, &node->outputs.size, index + 1, - sizeof(*node->outputs.streams))) + if (SUCCEEDED(hr = topology_node_reserve_streams(&node->outputs, index))) { - if (index >= node->outputs.count) - { - memset(&node->outputs.streams[node->outputs.count], 0, - (index - node->outputs.count + 1) * sizeof(*node->outputs.streams)); - node->outputs.count = index + 1; - } if (node->outputs.streams[index].preferred_type) IMFMediaType_Release(node->outputs.streams[index].preferred_type); node->outputs.streams[index].preferred_type = mediatype; if (node->outputs.streams[index].preferred_type) IMFMediaType_AddRef(node->outputs.streams[index].preferred_type); } - else - hr = E_OUTOFMEMORY; } else hr = E_NOTIMPL; @@ -1322,8 +1480,7 @@ static HRESULT WINAPI topology_node_SetInputPrefType(IMFTopologyNode *iface, DWO
if (node->node_type != MF_TOPOLOGY_SOURCESTREAM_NODE && !(index > 0 && node->node_type == MF_TOPOLOGY_TEE_NODE)) { - if (mf_array_reserve((void **)&node->inputs.streams, &node->inputs.size, index + 1, - sizeof(*node->inputs.streams))) + if (SUCCEEDED(hr = topology_node_reserve_streams(&node->inputs, index))) { if (index >= node->inputs.count) { @@ -1337,8 +1494,6 @@ static HRESULT WINAPI topology_node_SetInputPrefType(IMFTopologyNode *iface, DWO if (node->inputs.streams[index].preferred_type) IMFMediaType_AddRef(node->inputs.streams[index].preferred_type); } - else - hr = E_OUTOFMEMORY; } else hr = node->node_type == MF_TOPOLOGY_TEE_NODE ? MF_E_INVALIDTYPE : E_NOTIMPL;