From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/Makefile.in | 1 + dlls/mf/tests/mf.c | 5369 ++++++++++++------------------------- dlls/mf/tests/topology.c | 2688 +++++++++++++++++++ 3 files changed, 4462 insertions(+), 3596 deletions(-) create mode 100644 dlls/mf/tests/topology.c
diff --git a/dlls/mf/tests/Makefile.in b/dlls/mf/tests/Makefile.in index 3bb0c66de0a..598a002260b 100644 --- a/dlls/mf/tests/Makefile.in +++ b/dlls/mf/tests/Makefile.in @@ -4,4 +4,5 @@ IMPORTS = mf mfplat dmoguids mfuuid strmiids uuid wmcodecdspuuid ole32 user32 SOURCES = \ mf.c \ resource.rc \ + topology.c \ transform.c diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index b3cd355f49d..3f75ca943c8 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -419,12 +419,11 @@ static IMFMediaSource *create_test_source(IMFPresentationDescriptor *pd) return &source->IMFMediaSource_iface; }
-static HRESULT WINAPI test_unk_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +static HRESULT WINAPI test_getservice_QI(IMFGetService *iface, REFIID riid, void **obj) { - if (IsEqualIID(riid, &IID_IUnknown)) + if (IsEqualIID(riid, &IID_IMFGetService) || IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; - IUnknown_AddRef(iface); return S_OK; }
@@ -432,1207 +431,1343 @@ static HRESULT WINAPI test_unk_QueryInterface(IUnknown *iface, REFIID riid, void return E_NOINTERFACE; }
-static ULONG WINAPI test_unk_AddRef(IUnknown *iface) +static ULONG WINAPI test_getservice_AddRef(IMFGetService *iface) { return 2; }
-static ULONG WINAPI test_unk_Release(IUnknown *iface) +static ULONG WINAPI test_getservice_Release(IMFGetService *iface) { return 1; }
-static const IUnknownVtbl test_unk_vtbl = +static HRESULT WINAPI test_getservice_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) { - test_unk_QueryInterface, - test_unk_AddRef, - test_unk_Release, -}; + *obj = (void *)0xdeadbeef; + return 0x83eddead; +}
-static void test_topology(void) +static const IMFGetServiceVtbl testmfgetservicevtbl = { - IMFMediaType *mediatype, *mediatype2, *mediatype3; - IMFCollection *collection, *collection2; - IUnknown test_unk2 = { &test_unk_vtbl }; - IUnknown test_unk = { &test_unk_vtbl }; - IMFTopologyNode *node, *node2, *node3; - IMFTopology *topology, *topology2; - DWORD size, io_count, index; - MF_TOPOLOGY_TYPE node_type; - IUnknown *object; - WORD node_count; - UINT32 count; - HRESULT hr; - TOPOID id; - LONG ref; - - hr = MFCreateTopology(NULL); - ok(hr == E_POINTER, "got %#lx\n", hr); + test_getservice_QI, + test_getservice_AddRef, + test_getservice_Release, + test_getservice_GetService, +};
- hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); - hr = IMFTopology_GetTopologyID(topology, &id); - ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); - ok(id == 1, "Unexpected id.\n"); +static IMFGetService test_getservice = { &testmfgetservicevtbl };
- hr = MFCreateTopology(&topology2); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); - hr = IMFTopology_GetTopologyID(topology2, &id); - ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); - ok(id == 2, "Unexpected id.\n"); +static HRESULT WINAPI testservice_QI(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + return S_OK; + }
- ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); + *obj = NULL;
- hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); - hr = IMFTopology_GetTopologyID(topology, &id); - ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); - ok(id == 3, "Unexpected id.\n"); + if (IsEqualIID(riid, &IID_IMFGetService)) + return 0x82eddead;
- ref = IMFTopology_Release(topology2); - ok(ref == 0, "Release returned %ld\n", ref); + return E_NOINTERFACE; +}
- /* No attributes by default. */ - for (node_type = MF_TOPOLOGY_OUTPUT_NODE; node_type < MF_TOPOLOGY_TEE_NODE; ++node_type) +static HRESULT WINAPI testservice2_QI(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) { - hr = MFCreateTopologyNode(node_type, &node); - ok(hr == S_OK, "Failed to create a node for type %d, hr %#lx.\n", node_type, hr); - hr = IMFTopologyNode_GetCount(node, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - ok(!count, "Unexpected attribute count %u.\n", count); - ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); + *obj = iface; + return S_OK; }
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + if (IsEqualIID(riid, &IID_IMFGetService)) + { + *obj = &test_getservice; + return S_OK; + }
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + *obj = NULL; + return E_NOINTERFACE; +}
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static ULONG WINAPI testservice_AddRef(IUnknown *iface) +{ + return 2; +}
- hr = IMFTopologyNode_GetTopoNodeID(node, &id); - ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); - ok(((id >> 32) == GetCurrentProcessId()) && !!(id & 0xffff), "Unexpected node id %s.\n", wine_dbgstr_longlong(id)); +static ULONG WINAPI testservice_Release(IUnknown *iface) +{ + return 1; +}
- hr = IMFTopologyNode_SetTopoNodeID(node2, id); - ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); +static const IUnknownVtbl testservicevtbl = +{ + testservice_QI, + testservice_AddRef, + testservice_Release, +};
- hr = IMFTopology_GetNodeCount(topology, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); +static const IUnknownVtbl testservice2vtbl = +{ + testservice2_QI, + testservice_AddRef, + testservice_Release, +};
- hr = IMFTopology_AddNode(topology, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); +static IUnknown testservice = { &testservicevtbl }; +static IUnknown testservice2 = { &testservice2vtbl };
- node_count = 1; - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 0, "Unexpected node count %u.\n", node_count); +static void test_MFGetService(void) +{ + IUnknown *unk; + HRESULT hr;
- /* Same id, different nodes. */ - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + hr = MFGetService(NULL, NULL, NULL, NULL); + ok(hr == E_POINTER, "Unexpected return value %#lx.\n", hr);
- node_count = 0; - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count %u.\n", node_count); + unk = (void *)0xdeadbeef; + hr = MFGetService(NULL, NULL, NULL, (void **)&unk); + ok(hr == E_POINTER, "Unexpected return value %#lx.\n", hr); + ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n");
- hr = IMFTopology_AddNode(topology, node2); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); + hr = MFGetService(&testservice, NULL, NULL, NULL); + ok(hr == 0x82eddead, "Unexpected return value %#lx.\n", hr);
- hr = IMFTopology_GetNodeByID(topology, id, &node2); - ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); - ok(node2 == node, "Unexpected node.\n"); - IMFTopologyNode_Release(node2); + unk = (void *)0xdeadbeef; + hr = MFGetService(&testservice, NULL, NULL, (void **)&unk); + ok(hr == 0x82eddead, "Unexpected return value %#lx.\n", hr); + ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n");
- /* Change node id, add it again. */ - hr = IMFTopologyNode_SetTopoNodeID(node, ++id); - ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); + unk = NULL; + hr = MFGetService(&testservice2, NULL, NULL, (void **)&unk); + ok(hr == 0x83eddead, "Unexpected return value %#lx.\n", hr); + ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n"); +}
- hr = IMFTopology_GetNodeByID(topology, id, &node2); - ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); - ok(node2 == node, "Unexpected node.\n"); - IMFTopologyNode_Release(node2); +static void test_sequencer_source(void) +{ + IMFSequencerSource *seq_source; + HRESULT hr; + LONG ref;
- hr = IMFTopology_GetNodeByID(topology, id + 1, &node2); - ok(hr == MF_E_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- hr = IMFTopology_AddNode(topology, node); - ok(hr == E_INVALIDARG, "Failed to add a node, hr %#lx.\n", hr); + hr = MFCreateSequencerSource(NULL, &seq_source); + ok(hr == S_OK, "Failed to create sequencer source, hr %#lx.\n", hr);
- hr = IMFTopology_GetNode(topology, 0, &node2); - ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); - ok(node2 == node, "Unexpected node.\n"); - IMFTopologyNode_Release(node2); + check_interface(seq_source, &IID_IMFMediaSourceTopologyProvider, TRUE);
- hr = IMFTopology_GetNode(topology, 1, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ref = IMFSequencerSource_Release(seq_source); + ok(ref == 0, "Release returned %ld\n", ref);
- hr = IMFTopology_GetNode(topology, 1, &node2); - ok(hr == MF_E_INVALIDINDEX, "Failed to get a node, hr %#lx.\n", hr); + hr = MFShutdown(); + ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); +}
- hr = IMFTopology_GetNode(topology, -2, &node2); - ok(hr == MF_E_INVALIDINDEX, "Failed to get a node, hr %#lx.\n", hr); +struct test_handler +{ + IMFMediaTypeHandler IMFMediaTypeHandler_iface;
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, node2); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(node2); - ok(ref == 1, "Release returned %ld\n", ref); + ULONG set_current_count; + IMFMediaType *current_type; + IMFMediaType *invalid_type;
- node_count = 0; - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 2, "Unexpected node count %u.\n", node_count); - - /* Remove with detached node, existing id. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - hr = IMFTopologyNode_SetTopoNodeID(node2, id); - ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); - hr = IMFTopology_RemoveNode(topology, node2); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); + ULONG enum_count; + ULONG media_types_count; + IMFMediaType **media_types; +};
- hr = IMFTopology_RemoveNode(topology, node); - ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); +static struct test_handler *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct test_handler, IMFMediaTypeHandler_iface); +}
- node_count = 0; - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count %u.\n", node_count); +static HRESULT WINAPI test_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFMediaTypeHandler) + || IsEqualIID(riid, &IID_IUnknown)) + { + IMFMediaTypeHandler_AddRef((*obj = iface)); + return S_OK; + }
- hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + *obj = NULL; + return E_NOINTERFACE; +}
- node_count = 1; - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 0, "Unexpected node count %u.\n", node_count); +static ULONG WINAPI test_handler_AddRef(IMFMediaTypeHandler *iface) +{ + return 2; +}
- hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); +static ULONG WINAPI test_handler_Release(IMFMediaTypeHandler *iface) +{ + return 1; +}
- hr = IMFTopologyNode_SetTopoNodeID(node, 123); - ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); +static HRESULT WINAPI test_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, IMFMediaType *in_type, + IMFMediaType **out_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + BOOL result;
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); + if (out_type) + *out_type = NULL;
- /* Change id for attached node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + if (impl->invalid_type && IMFMediaType_Compare(impl->invalid_type, (IMFAttributes *)in_type, + MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) + return MF_E_INVALIDMEDIATYPE;
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + if (!impl->current_type) + return S_OK;
- hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + if (IMFMediaType_Compare(impl->current_type, (IMFAttributes *)in_type, + MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) + return S_OK;
- hr = IMFTopology_AddNode(topology, node2); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + return MF_E_INVALIDMEDIATYPE; +}
- hr = IMFTopologyNode_GetTopoNodeID(node, &id); - ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); +static HRESULT WINAPI test_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetTopoNodeID(node2, id); - ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); +static HRESULT WINAPI test_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, + IMFMediaType **type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface);
- hr = IMFTopology_GetNodeByID(topology, id, &node3); - ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); - ok(node3 == node, "Unexpected node.\n"); - IMFTopologyNode_Release(node3); + if (impl->media_types) + { + impl->enum_count++;
- /* Source/output collections. */ - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + if (index >= impl->media_types_count) + return MF_E_NO_MORE_TYPES;
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); + IMFMediaType_AddRef((*type = impl->media_types[index])); + return S_OK; + }
- hr = IMFTopology_GetSourceNodeCollection(topology, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopology_GetSourceNodeCollection(topology, &collection); - ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); - ok(!!collection, "Unexpected object pointer.\n"); +static HRESULT WINAPI test_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType *media_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface);
- hr = IMFTopology_GetSourceNodeCollection(topology, &collection2); - ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); - ok(!!collection2, "Unexpected object pointer.\n"); - ok(collection2 != collection, "Expected cloned collection.\n"); + if (impl->current_type) + IMFMediaType_Release(impl->current_type); + IMFMediaType_AddRef((impl->current_type = media_type)); + impl->set_current_count++;
- hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(!size, "Unexpected item count.\n"); + return S_OK; +}
- EXPECT_REF(collection, 1); - hr = IMFCollection_AddElement(collection, (IUnknown *)collection); - ok(hr == S_OK, "Failed to add element, hr %#lx.\n", hr); - EXPECT_REF(collection, 2); +static HRESULT WINAPI test_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **media_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr;
- hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(size == 1, "Unexpected item count.\n"); + if (!impl->current_type) + { + if (!impl->media_types) + return E_FAIL; + if (!impl->media_types_count) + return MF_E_TRANSFORM_TYPE_NOT_SET; + return MF_E_NOT_INITIALIZED; + }
- /* Empty collection to stop referencing itself */ - hr = IMFCollection_RemoveAllElements(collection); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + if (FAILED(hr = MFCreateMediaType(media_type))) + return hr;
- hr = IMFCollection_GetElementCount(collection2, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(!size, "Unexpected item count.\n"); + hr = IMFMediaType_CopyAllItems(impl->current_type, (IMFAttributes *)*media_type); + if (FAILED(hr)) + IMFMediaType_Release(*media_type);
- ref = IMFCollection_Release(collection2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFCollection_Release(collection); - ok(ref == 0, "Release returned %ld\n", ref); + return hr; +}
- /* Add some nodes. */ - hr = IMFTopology_GetSourceNodeCollection(topology, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopology_GetOutputNodeCollection(topology, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); +static const IMFMediaTypeHandlerVtbl test_handler_vtbl = +{ + test_handler_QueryInterface, + test_handler_AddRef, + test_handler_Release, + test_handler_IsMediaTypeSupported, + test_handler_GetMediaTypeCount, + test_handler_GetMediaTypeByIndex, + test_handler_SetCurrentMediaType, + test_handler_GetCurrentMediaType, + test_handler_GetMajorType, +};
- hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - IMFTopologyNode_Release(node); - - hr = IMFTopology_GetSourceNodeCollection(topology, &collection); - ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); - ok(!!collection, "Unexpected object pointer.\n"); - hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(size == 1, "Unexpected item count.\n"); - ref = IMFCollection_Release(collection); - ok(ref == 0, "Release returned %ld\n", ref); +static const struct test_handler test_handler = {.IMFMediaTypeHandler_iface.lpVtbl = &test_handler_vtbl};
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - IMFTopologyNode_Release(node); - - hr = IMFTopology_GetSourceNodeCollection(topology, &collection); - ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); - ok(!!collection, "Unexpected object pointer.\n"); - hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(size == 1, "Unexpected item count.\n"); - ref = IMFCollection_Release(collection); - ok(ref == 0, "Release returned %ld\n", ref); +struct test_media_sink +{ + IMFMediaSink IMFMediaSink_iface; + BOOL shutdown; +};
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - IMFTopologyNode_Release(node); - - hr = IMFTopology_GetSourceNodeCollection(topology, &collection); - ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); - ok(!!collection, "Unexpected object pointer.\n"); - hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(size == 1, "Unexpected item count.\n"); - ref = IMFCollection_Release(collection); - ok(ref == 0, "Release returned %ld\n", ref); +static struct test_media_sink *impl_from_IMFMediaSink(IMFMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_sink, IMFMediaSink_iface); +}
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFMediaSink) + || IsEqualIID(riid, &IID_IUnknown)) + { + IMFMediaSink_AddRef((*obj = iface)); + return S_OK; + }
- /* Associated object. */ - hr = IMFTopologyNode_SetObject(node, NULL); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + *obj = NULL; + return E_NOINTERFACE; +}
- hr = IMFTopologyNode_GetObject(node, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); +static ULONG WINAPI test_media_sink_AddRef(IMFMediaSink *iface) +{ + return 2; +}
- object = (void *)0xdeadbeef; - hr = IMFTopologyNode_GetObject(node, &object); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - ok(!object, "Unexpected object %p.\n", object); +static ULONG WINAPI test_media_sink_Release(IMFMediaSink *iface) +{ + return 1; +}
- hr = IMFTopologyNode_SetObject(node, &test_unk); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *characteristics) +{ + *characteristics = 0; + return S_OK; +}
- hr = IMFTopologyNode_GetObject(node, &object); - ok(hr == S_OK, "Failed to get object, hr %#lx.\n", hr); - ok(object == &test_unk, "Unexpected object %p.\n", object); - IUnknown_Release(object); +static HRESULT WINAPI test_media_sink_AddStreamSink(IMFMediaSink *iface, + DWORD stream_sink_id, IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetObject(node, &test_unk2); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetCount(node, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - ok(count == 0, "Unexpected attribute count %u.\n", count); +static HRESULT WINAPI test_media_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetGUID(node, &MF_TOPONODE_TRANSFORM_OBJECTID, &MF_TOPONODE_TRANSFORM_OBJECTID); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, IMFStreamSink **sink) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetObject(node, NULL); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id, IMFStreamSink **sink) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- object = (void *)0xdeadbeef; - hr = IMFTopologyNode_GetObject(node, &object); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - ok(!object, "Unexpected object %p.\n", object); +static HRESULT WINAPI test_media_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetCount(node, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - ok(count == 1, "Unexpected attribute count %u.\n", count); +static HRESULT WINAPI test_media_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- /* Preferred stream types. */ - hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); - ok(io_count == 0, "Unexpected count %lu.\n", io_count); +static HRESULT WINAPI test_media_sink_Shutdown(IMFMediaSink *iface) +{ + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + ok(!sink->shutdown, "Unexpected call.\n"); + sink->shutdown = TRUE; + return S_OK; +}
- hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); +static const IMFMediaSinkVtbl test_media_sink_vtbl = +{ + test_media_sink_QueryInterface, + test_media_sink_AddRef, + test_media_sink_Release, + test_media_sink_GetCharacteristics, + test_media_sink_AddStreamSink, + test_media_sink_RemoveStreamSink, + test_media_sink_GetStreamSinkCount, + test_media_sink_GetStreamSinkByIndex, + test_media_sink_GetStreamSinkById, + test_media_sink_SetPresentationClock, + test_media_sink_GetPresentationClock, + test_media_sink_Shutdown, +};
- hr = MFCreateMediaType(&mediatype); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); +static const struct test_media_sink test_media_sink = {.IMFMediaSink_iface.lpVtbl = &test_media_sink_vtbl};
- hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +struct test_stream_sink +{ + IMFStreamSink IMFStreamSink_iface; + IMFGetService IMFGetService_iface; + IMFMediaTypeHandler *handler; + IMFMediaSink *media_sink;
- hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); - ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); - ok(mediatype2 == mediatype, "Unexpected mediatype instance.\n"); - IMFMediaType_Release(mediatype2); + IMFAttributes *attributes; + IUnknown *device_manager; +};
- hr = IMFTopologyNode_SetInputPrefType(node, 0, NULL); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static struct test_stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct test_stream_sink, IMFStreamSink_iface); +}
- hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - ok(!mediatype2, "Unexpected mediatype instance.\n"); +static HRESULT WINAPI test_stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface);
- hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + if (IsEqualIID(riid, &IID_IMFStreamSink) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + IMFStreamSink_AddRef((*obj = iface)); + return S_OK; + }
- hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + if (IsEqualIID(riid, &IID_IMFAttributes) && impl->attributes) + { + IMFAttributes_AddRef((*obj = impl->attributes)); + return S_OK; + }
- hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); - ok(io_count == 2, "Unexpected count %lu.\n", io_count); + if (IsEqualIID(riid, &IID_IMFGetService)) + { + *obj = &impl->IMFGetService_iface; + IMFGetService_AddRef(&impl->IMFGetService_iface); + return S_OK; + }
- hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); - ok(io_count == 0, "Unexpected count %lu.\n", io_count); + *obj = NULL; + return E_NOINTERFACE; +}
- hr = IMFTopologyNode_SetOutputPrefType(node, 0, mediatype); - ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); +static ULONG WINAPI test_stream_sink_AddRef(IMFStreamSink *iface) +{ + return 2; +}
- ref = IMFTopologyNode_Release(node); - ok(ref == 1, "Release returned %ld\n", ref); +static ULONG WINAPI test_stream_sink_Release(IMFStreamSink *iface) +{ + return 1; +}
- /* Source node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); - ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetOutputPrefType(node, 2, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); - ok(hr == E_FAIL, "Failed to get preferred type, hr %#lx.\n", hr); - ok(!mediatype2, "Unexpected mediatype instance.\n"); +static HRESULT WINAPI test_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 3, "Unexpected count %lu.\n", io_count); +static HRESULT WINAPI test_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface);
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); + if (impl->media_sink) + { + IMFMediaSink_AddRef((*sink = impl->media_sink)); + return S_OK; + }
- /* Tee node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + todo_wine + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *id) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); - ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); - ok(mediatype2 == mediatype, "Unexpected mediatype instance.\n"); - IMFMediaType_Release(mediatype2); +static HRESULT WINAPI test_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface);
- hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + if (impl->handler) + { + IMFMediaTypeHandler_AddRef((*handler = impl->handler)); + return S_OK; + }
- hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 0, "Unexpected count %lu.\n", io_count); + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); - ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetInputPrefType(node, 3, mediatype); - ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_Flush(IMFStreamSink *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +}
- hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); +static const IMFStreamSinkVtbl test_stream_sink_vtbl = +{ + test_stream_sink_QueryInterface, + test_stream_sink_AddRef, + test_stream_sink_Release, + test_stream_sink_GetEvent, + test_stream_sink_BeginGetEvent, + test_stream_sink_EndGetEvent, + test_stream_sink_QueueEvent, + test_stream_sink_GetMediaSink, + test_stream_sink_GetIdentifier, + test_stream_sink_GetMediaTypeHandler, + test_stream_sink_ProcessSample, + test_stream_sink_PlaceMarker, + test_stream_sink_Flush, +};
- hr = MFCreateMediaType(&mediatype2); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); +static struct test_stream_sink *impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct test_stream_sink, IMFGetService_iface); +}
- /* Changing output type does not change input type. */ - hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype2); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct test_stream_sink *stream = impl_from_IMFGetService(iface); + return IMFStreamSink_QueryInterface(&stream->IMFStreamSink_iface, riid, obj); +}
- hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype3); - ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); - ok(mediatype3 == mediatype, "Unexpected mediatype instance.\n"); - IMFMediaType_Release(mediatype3); - - IMFMediaType_Release(mediatype2); - - hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 0, "Unexpected count %lu.\n", io_count); - - hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 5, "Unexpected count %lu.\n", io_count); +static ULONG WINAPI test_stream_sink_get_service_AddRef(IMFGetService *iface) +{ + struct test_stream_sink *stream = impl_from_IMFGetService(iface); + return IMFStreamSink_AddRef(&stream->IMFStreamSink_iface); +}
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); +static ULONG WINAPI test_stream_sink_get_service_Release(IMFGetService *iface) +{ + struct test_stream_sink *stream = impl_from_IMFGetService(iface); + return IMFStreamSink_Release(&stream->IMFStreamSink_iface); +}
- /* Transform node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); - ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); +static HRESULT WINAPI test_stream_sink_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) +{ + struct test_stream_sink *stream = impl_from_IMFGetService(iface);
- hr = IMFTopologyNode_SetInputPrefType(node, 3, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + if (IsEqualGUID(service, &MR_VIDEO_ACCELERATION_SERVICE) && stream->device_manager) + return IUnknown_QueryInterface(stream->device_manager, riid, obj);
- hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); - ok(io_count == 4, "Unexpected count %lu.\n", io_count); + return E_NOINTERFACE; +}
- hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); +static const IMFGetServiceVtbl test_stream_sink_get_service_vtbl = +{ + test_stream_sink_get_service_QueryInterface, + test_stream_sink_get_service_AddRef, + test_stream_sink_get_service_Release, + test_stream_sink_get_service_GetService, +};
- hr = IMFTopologyNode_GetInputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 4, "Unexpected count %lu.\n", io_count); +static const struct test_stream_sink test_stream_sink = +{ + .IMFStreamSink_iface.lpVtbl = &test_stream_sink_vtbl, + .IMFGetService_iface.lpVtbl = &test_stream_sink_get_service_vtbl, +};
- hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 5, "Unexpected count %lu.\n", io_count); +struct test_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount;
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); + HANDLE event; + IMFMediaEvent *media_event; + BOOL check_media_event; +};
- IMFMediaType_Release(mediatype); +static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); +}
- hr = IMFTopology_GetOutputNodeCollection(topology, &collection); - ok(hr == S_OK || broken(hr == E_FAIL) /* before Win8 */, "Failed to get output node collection, hr %#lx.\n", hr); - if (SUCCEEDED(hr)) +static HRESULT WINAPI testcallback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) { - ok(!!collection, "Unexpected object pointer.\n"); - hr = IMFCollection_GetElementCount(collection, &size); - ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); - ok(size == 1, "Unexpected item count.\n"); - ref = IMFCollection_Release(collection); - ok(ref == 0, "Release returned %ld\n", ref); + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; }
- ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); - - /* Connect nodes. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + *obj = NULL; + return E_NOINTERFACE; +}
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static ULONG WINAPI testcallback_AddRef(IMFAsyncCallback *iface) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + return InterlockedIncrement(&callback->refcount); +}
- EXPECT_REF(node, 1); - EXPECT_REF(node2, 1); +static ULONG WINAPI testcallback_Release(IMFAsyncCallback *iface) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount);
- hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + if (!refcount) + { + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event); + CloseHandle(callback->event); + free(callback); + }
- EXPECT_REF(node, 2); - EXPECT_REF(node2, 2); + return refcount; +}
- IMFTopologyNode_Release(node); +static HRESULT WINAPI testcallback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + ok(flags != NULL && queue != NULL, "Unexpected arguments.\n"); + return E_NOTIMPL; +}
- EXPECT_REF(node, 1); - EXPECT_REF(node2, 2); +static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_callback *callback = CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); + IUnknown *object; + HRESULT hr;
- IMFTopologyNode_Release(node2); + ok(result != NULL, "Unexpected result object.\n");
- EXPECT_REF(node, 1); - EXPECT_REF(node2, 1); + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event);
- hr = IMFTopologyNode_GetNodeType(node2, &node_type); - ok(hr == S_OK, "Failed to get node type, hr %#lx.\n", hr); + if (callback->check_media_event) + { + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
- IMFTopologyNode_Release(node); + hr = IMFAsyncResult_GetState(result, &object); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, + result, &callback->media_event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(object); + }
- /* Connect within topology. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + SetEvent(callback->event);
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + return S_OK; +}
- hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); +static const IMFAsyncCallbackVtbl testcallbackvtbl = +{ + testcallback_QueryInterface, + testcallback_AddRef, + testcallback_Release, + testcallback_GetParameters, + testcallback_Invoke, +};
- hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); +static IMFAsyncCallback *create_test_callback(BOOL check_media_event) +{ + struct test_callback *callback;
- hr = IMFTopology_AddNode(topology, node2); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + if (!(callback = calloc(1, sizeof(*callback)))) + return NULL;
- EXPECT_REF(node, 2); - EXPECT_REF(node2, 2); + callback->refcount = 1; + callback->check_media_event = check_media_event; + callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl; + callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError());
- hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + return &callback->IMFAsyncCallback_iface; +}
- EXPECT_REF(node, 3); - EXPECT_REF(node2, 3); +#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid;
- hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + do + { + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); + hr = IMFMediaEvent_GetType(impl->media_event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } while (type != expect_type);
- EXPECT_REF(node, 1); - EXPECT_REF(node2, 1); + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type);
- /* Removing connected node breaks connection. */ - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid));
- hr = IMFTopology_AddNode(topology, node2); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopology_RemoveNode(topology, node); - ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); + return status; +}
- EXPECT_REF(node, 1); - EXPECT_REF(node2, 2); +#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid;
- hr = IMFTopologyNode_GetOutput(node, 0, &node3, &index); - ok(hr == MF_E_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + do + { + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; + hr = IMFMediaEvent_GetType(impl->media_event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } while (type != expect_type);
- hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type);
- hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid));
- hr = IMFTopology_RemoveNode(topology, node2); - ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- EXPECT_REF(node, 2); - EXPECT_REF(node2, 1); + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- IMFTopologyNode_Release(node); - IMFTopologyNode_Release(node2); + return status; +}
- /* Cloning nodes of different types. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) +{ + IMFSourceResolver *resolver; + IMFAttributes *attributes; + const BYTE *resource_data; + MF_OBJECT_TYPE obj_type; + IMFMediaSource *source; + IMFByteStream *stream; + ULONG resource_len; + HRSRC resource; + HRESULT hr;
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + resource = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW %s failed, error %lu\n", debugstr_w(name), GetLastError()); + resource_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + resource_len = SizeofResource(GetModuleHandleW(NULL), resource);
- hr = IMFTopologyNode_CloneFrom(node, node2); - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_Write(stream, resource_data, resource_len, &resource_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_SetCurrentPosition(stream, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); + hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, mime); + ok(hr == S_OK, "Failed to set string value, hr %#lx.\n", hr); + IMFAttributes_Release(attributes);
- /* Cloning preferred types. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + hr = MFCreateSourceResolver(&resolver); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, + &obj_type, (IUnknown **)&source); + todo_wine_if(hr == MF_E_UNEXPECTED) /* Gitlab CI Debian runner */ + ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "Unexpected hr %#lx.\n", hr); + IMFSourceResolver_Release(resolver); + IMFByteStream_Release(stream);
- hr = MFCreateMediaType(&mediatype); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + if (FAILED(hr)) + return NULL;
- hr = IMFTopologyNode_SetOutputPrefType(node2, 0, mediatype); - ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type); + return source; +}
- /* Vista checks for additional attributes. */ - hr = IMFTopologyNode_CloneFrom(node, node2); - ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); +enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, +};
- hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); - ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); - ok(mediatype == mediatype2, "Unexpected media type.\n"); +#define MAX_OBJECT_STATE 1024
- IMFMediaType_Release(mediatype2); +struct object_state_record +{ + enum object_state states[MAX_OBJECT_STATE]; + unsigned int state_count; +}; +static struct object_state_record actual_object_state_record;
- ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); +#define add_object_state(a, b) _add_object_state(__LINE__, a, b) +static void _add_object_state(int line, struct object_state_record *record, enum object_state state) +{ + ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); + if (record->state_count < MAX_OBJECT_STATE) + record->states[record->state_count++] = state; +}
- IMFMediaType_Release(mediatype); +#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) +static void _compare_object_states(int line, const struct object_state_record *r1, + const struct object_state_record *r2) +{ + ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); + if (r1->state_count == r2->state_count) + ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); +}
- /* Existing preferred types are not cleared. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 1, "Unexpected output count.\n"); - - hr = IMFTopologyNode_CloneFrom(node, node2); - ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); - - hr = IMFTopologyNode_GetOutputCount(node, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 1, "Unexpected output count.\n"); - - hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); - ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); - ok(!!mediatype2, "Unexpected media type.\n"); - IMFMediaType_Release(mediatype2); - - hr = IMFTopologyNode_CloneFrom(node2, node); - ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); - - hr = IMFTopologyNode_GetOutputCount(node2, &io_count); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(io_count == 1, "Unexpected output count.\n"); - - ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); - - /* Add one node, connect to another that hasn't been added. */ - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count.\n"); - - hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); - - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count.\n"); - - /* Add same node to different topologies. */ - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); - - hr = MFCreateTopology(&topology2); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - EXPECT_REF(node, 2); - - hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count.\n"); - - hr = IMFTopology_GetNodeCount(topology2, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 0, "Unexpected node count.\n"); - - hr = IMFTopology_AddNode(topology2, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - EXPECT_REF(node, 3); +enum source_state +{ + SOURCE_STOPPED, + SOURCE_RUNNING, + SOURCE_PAUSED, +};
- hr = IMFTopology_GetNodeCount(topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count.\n"); +struct test_media_stream +{ + IMFMediaStream IMFMediaStream_iface; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *sd; + IMFMediaSource *source; + LONGLONG sample_duration; + LONGLONG sample_time; + BOOL is_new; + LONG refcount; +};
- hr = IMFTopology_GetNodeCount(topology2, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 1, "Unexpected node count.\n"); +static struct test_media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_stream, IMFMediaStream_iface); +}
- ref = IMFTopology_Release(topology2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); +static HRESULT WINAPI test_media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaStream) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + }
- ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); + IMFMediaStream_AddRef(iface); + return S_OK; +}
- /* Try cloning a topology without all outputs connected */ - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); +static ULONG WINAPI test_media_stream_AddRef(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return InterlockedIncrement(&stream->refcount); +}
- hr = MFCreateTopology(&topology2); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); +static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount);
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + if (!refcount) + { + IMFMediaEventQueue_Release(stream->event_queue); + free(stream); + }
- hr = IMFTopology_AddNode(topology, node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - EXPECT_REF(node, 2); + return refcount; +}
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +}
- hr = IMFTopology_AddNode(topology, node2); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); - EXPECT_REF(node, 2); +static HRESULT WINAPI test_media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +}
- hr = IMFTopologyNode_ConnectOutput(node, 1, node2, 0); - ok(hr == S_OK, "Failed to connect output, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +}
- hr = IMFTopology_CloneFrom(topology2, topology); - ok(hr == S_OK, "Failed to clone from topology, hr %#lx.\n", hr); +static HRESULT WINAPI test_media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +}
- hr = IMFTopology_GetNodeCount(topology2, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - ok(node_count == 2, "Unexpected node count %u.\n", node_count); +static HRESULT WINAPI test_media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface);
- hr = IMFTopology_GetNode(topology2, 0, &node3); - ok(hr == S_OK, "Failed to get node, hr %#lx.\n", hr); + *source = stream->source; + IMFMediaSource_AddRef(*source);
- hr = IMFTopologyNode_GetOutputCount(node3, &size); - ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); - ok(size == 2, "Unexpected output count %lu.\n", size); + return S_OK; +}
- IMFTopologyNode_Release(node3); +static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface);
- ref = IMFTopology_Release(topology2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); + *sd = stream->sd; + IMFStreamDescriptor_AddRef(*sd);
- ref = IMFTopologyNode_Release(node2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(node); - ok(ref == 0, "Release returned %ld\n", ref); + return S_OK; }
-static void test_topology_tee_node(void) +static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { - IMFTopologyNode *src_node, *tee_node; - IMFMediaType *mediatype, *mediatype2; - IMFTopology *topology; - DWORD count; + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + IMFMediaBuffer *buffer; + IMFSample *sample; HRESULT hr; - LONG ref; - - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); - - hr = MFCreateMediaType(&mediatype); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &tee_node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); - - hr = IMFTopologyNode_SetInputPrefType(tee_node, 0, mediatype); - ok(hr == S_OK, "Failed to set type, hr %#lx.\n", hr);
- /* Even though tee node has only one input and source has only one output, - it's possible to connect to higher inputs/outputs. */ + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (stream->sample_duration) + { + hr = IMFSample_SetSampleDuration(sample, stream->sample_duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* SRC(0) -> TEE(0) */ - hr = IMFTopologyNode_ConnectOutput(src_node, 0, tee_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFSample_SetSampleTime(sample, stream->sample_time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_GetInputCount(tee_node, &count); - ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); - ok(count == 1, "Unexpected count %lu.\n", count); + stream->sample_time += stream->sample_duration; + } + else + { + hr = IMFSample_SetSampleTime(sample, 123); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_GetInputPrefType(tee_node, 0, &mediatype2); - ok(hr == S_OK, "Failed to get type, hr %#lx.\n", hr); - ok(mediatype2 == mediatype, "Unexpected type.\n"); - IMFMediaType_Release(mediatype2); + hr = IMFSample_SetSampleDuration(sample, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + }
- /* SRC(0) -> TEE(1) */ - hr = IMFTopologyNode_ConnectOutput(src_node, 0, tee_node, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
- hr = IMFTopologyNode_GetInputCount(tee_node, &count); - ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); - ok(count == 2, "Unexpected count %lu.\n", count); + /* Reader expects buffers, empty samples are considered an error. */ + hr = MFCreateMemoryBuffer(8, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaBuffer_Release(buffer);
- hr = IMFTopologyNode_SetInputPrefType(tee_node, 1, mediatype); - ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample);
- /* SRC(1) -> TEE(1) */ - hr = IMFTopologyNode_ConnectOutput(src_node, 1, tee_node, 1); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + return S_OK; +}
- hr = IMFTopologyNode_GetOutputCount(src_node, &count); - ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); - ok(count == 2, "Unexpected count %lu.\n", count); +static const IMFMediaStreamVtbl test_media_stream_vtbl = +{ + test_media_stream_QueryInterface, + test_media_stream_AddRef, + test_media_stream_Release, + test_media_stream_GetEvent, + test_media_stream_BeginGetEvent, + test_media_stream_EndGetEvent, + test_media_stream_QueueEvent, + test_media_stream_GetMediaSource, + test_media_stream_GetStreamDescriptor, + test_media_stream_RequestSample, +};
- EXPECT_REF(src_node, 2); - EXPECT_REF(tee_node, 2); - hr = IMFTopologyNode_DisconnectOutput(src_node, 1); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +#define TEST_SOURCE_NUM_STREAMS 3
- ref = IMFTopologyNode_Release(src_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(tee_node); - ok(ref == 0, "Release returned %ld\n", ref); +struct test_seek_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFMediaEventQueue *event_queue; + IMFPresentationDescriptor *pd; + struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; + enum source_state state; + unsigned stream_count; + CRITICAL_SECTION cs; + BOOL seekable; + LONG refcount; +};
- ref = IMFMediaType_Release(mediatype); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); +static struct test_seek_source *impl_test_seek_source_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_seek_source, IMFMediaSource_iface); }
-static HRESULT WINAPI test_getservice_QI(IMFGetService *iface, REFIID riid, void **obj) +static HRESULT WINAPI test_seek_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) { - if (IsEqualIID(riid, &IID_IMFGetService) || IsEqualIID(riid, &IID_IUnknown)) + if (IsEqualIID(riid, &IID_IMFMediaSource) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) { - *obj = iface; - return S_OK; + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; }
- *obj = NULL; - return E_NOINTERFACE; + IMFMediaSource_AddRef(iface); + return S_OK; }
-static ULONG WINAPI test_getservice_AddRef(IMFGetService *iface) +static ULONG WINAPI test_seek_source_AddRef(IMFMediaSource *iface) { - return 2; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); }
-static ULONG WINAPI test_getservice_Release(IMFGetService *iface) +static ULONG WINAPI test_seek_source_Release(IMFMediaSource *iface) { - return 1; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(source->event_queue); + free(source); + } + + return refcount; }
-static HRESULT WINAPI test_getservice_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) +static HRESULT WINAPI test_seek_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) { - *obj = (void *)0xdeadbeef; - return 0x83eddead; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); }
-static const IMFGetServiceVtbl testmfgetservicevtbl = +static HRESULT WINAPI test_seek_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) { - test_getservice_QI, - test_getservice_AddRef, - test_getservice_Release, - test_getservice_GetService, -}; - -static IMFGetService test_getservice = { &testmfgetservicevtbl }; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +}
-static HRESULT WINAPI testservice_QI(IUnknown *iface, REFIID riid, void **obj) +static HRESULT WINAPI test_seek_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) { - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - return S_OK; - } + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +}
- *obj = NULL; +static HRESULT WINAPI test_seek_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +}
- if (IsEqualIID(riid, &IID_IMFGetService)) - return 0x82eddead; +static HRESULT WINAPI test_seek_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface);
- return E_NOINTERFACE; + if (source->seekable) + *flags = MFMEDIASOURCE_CAN_PAUSE | MFMEDIASOURCE_CAN_SEEK; + else + *flags = MFMEDIASOURCE_CAN_PAUSE; + return S_OK; }
-static HRESULT WINAPI testservice2_QI(IUnknown *iface, REFIID riid, void **obj) +static HRESULT WINAPI test_seek_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) { - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - return S_OK; - } + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; + IMFMediaType *media_type; + HRESULT hr = S_OK; + int i;
- if (IsEqualIID(riid, &IID_IMFGetService)) + EnterCriticalSection(&source->cs); + + if (source->pd) { - *obj = &test_getservice; - return S_OK; + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); } + else + { + for (i = 0; i < source->stream_count; ++i) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- *obj = NULL; - return E_NOINTERFACE; -} + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static ULONG WINAPI testservice_AddRef(IUnknown *iface) -{ - return 2; -} + hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static ULONG WINAPI testservice_Release(IUnknown *iface) -{ - return 1; + IMFMediaType_Release(media_type); + } + + hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SetUINT64(source->pd, &MF_PD_DURATION, 10 * 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < source->stream_count; ++i) + IMFStreamDescriptor_Release(sds[i]); + + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); + } + + LeaveCriticalSection(&source->cs); + + return hr; }
-static const IUnknownVtbl testservicevtbl = +static BOOL is_stream_selected(IMFPresentationDescriptor *pd, DWORD index) { - testservice_QI, - testservice_AddRef, - testservice_Release, -}; + IMFStreamDescriptor *sd; + BOOL selected = FALSE;
-static const IUnknownVtbl testservice2vtbl = -{ - testservice2_QI, - testservice_AddRef, - testservice_Release, -}; + if (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) + IMFStreamDescriptor_Release(sd);
-static IUnknown testservice = { &testservicevtbl }; -static IUnknown testservice2 = { &testservice2vtbl }; + return selected; +}
-static void test_MFGetService(void) +static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) { - IUnknown *unk; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + PROPVARIANT var; HRESULT hr; + int i;
- hr = MFGetService(NULL, NULL, NULL, NULL); - ok(hr == E_POINTER, "Unexpected return value %#lx.\n", hr); + add_object_state(&actual_object_state_record, SOURCE_START);
- unk = (void *)0xdeadbeef; - hr = MFGetService(NULL, NULL, NULL, (void **)&unk); - ok(hr == E_POINTER, "Unexpected return value %#lx.\n", hr); - ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n"); + ok(time_format && IsEqualGUID(time_format, &GUID_NULL), "Unexpected time format %s.\n", + wine_dbgstr_guid(time_format)); + ok(start_position && (start_position->vt == VT_I8 || start_position->vt == VT_EMPTY), + "Unexpected position type.\n");
- hr = MFGetService(&testservice, NULL, NULL, NULL); - ok(hr == 0x82eddead, "Unexpected return value %#lx.\n", hr); + /* This is what makes IMFMediaSession::Start() seeking fail, not the lacking of MFMEDIASOURCE_CAN_SEEK. + * Without this, IMFMediaSession::Start() seeking succeeds even with the missing MFMEDIASOURCE_CAN_SEEK. + * If this is check is not here, the first IMFMediaSession::Start() call to a non-zero position + * succeeds somehow on Windows 10, then all following seeks fails and no MESessionStarted events */ + if (!source->seekable && start_position && start_position->vt == VT_I8 && start_position->hVal.QuadPart) + return E_FAIL;
- unk = (void *)0xdeadbeef; - hr = MFGetService(&testservice, NULL, NULL, (void **)&unk); - ok(hr == 0x82eddead, "Unexpected return value %#lx.\n", hr); - ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n"); + EnterCriticalSection(&source->cs);
- unk = NULL; - hr = MFGetService(&testservice2, NULL, NULL, (void **)&unk); - ok(hr == 0x83eddead, "Unexpected return value %#lx.\n", hr); - ok(unk == (void *)0xdeadbeef, "Unexpected out object.\n"); -} + event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static void test_sequencer_source(void) -{ - IMFSequencerSource *seq_source; - HRESULT hr; - LONG ref; + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(pd, i)) + continue;
- hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); + var.vt = VT_UNKNOWN; + var.punkVal = (IUnknown *)&source->streams[i]->IMFMediaStream_iface; + event_type = source->streams[i]->is_new ? MENewStream : MEUpdatedStream; + source->streams[i]->is_new = FALSE; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateSequencerSource(NULL, &seq_source); - ok(hr == S_OK, "Failed to create sequencer source, hr %#lx.\n", hr); + event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + }
- check_interface(seq_source, &IID_IMFMediaSourceTopologyProvider, TRUE); + source->state = SOURCE_RUNNING;
- ref = IMFSequencerSource_Release(seq_source); - ok(ref == 0, "Release returned %ld\n", ref); + LeaveCriticalSection(&source->cs);
- hr = MFShutdown(); - ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); + return S_OK; }
-struct test_handler +static HRESULT WINAPI test_seek_source_Stop(IMFMediaSource *iface) { - IMFMediaTypeHandler IMFMediaTypeHandler_iface; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i;
- ULONG set_current_count; - IMFMediaType *current_type; - IMFMediaType *invalid_type; + add_object_state(&actual_object_state_record, SOURCE_STOP);
- ULONG enum_count; - ULONG media_types_count; - IMFMediaType **media_types; -}; + EnterCriticalSection(&source->cs);
-static struct test_handler *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) -{ - return CONTAINING_RECORD(iface, struct test_handler, IMFMediaTypeHandler_iface); -} + event_type = MESourceStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFMediaTypeHandler) - || IsEqualIID(riid, &IID_IUnknown)) + for (i = 0; i < source->stream_count; ++i) { - IMFMediaTypeHandler_AddRef((*obj = iface)); - return S_OK; + if (!is_stream_selected(source->pd, i)) + continue; + + event_type = MEStreamStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
- *obj = NULL; - return E_NOINTERFACE; -} + source->state = SOURCE_STOPPED;
-static ULONG WINAPI test_handler_AddRef(IMFMediaTypeHandler *iface) -{ - return 2; -} + LeaveCriticalSection(&source->cs);
-static ULONG WINAPI test_handler_Release(IMFMediaTypeHandler *iface) -{ - return 1; + return S_OK; }
-static HRESULT WINAPI test_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, IMFMediaType *in_type, - IMFMediaType **out_type) +static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) { - struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); - BOOL result; - - if (out_type) - *out_type = NULL; - - if (impl->invalid_type && IMFMediaType_Compare(impl->invalid_type, (IMFAttributes *)in_type, - MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) - return MF_E_INVALIDMEDIATYPE; - - if (!impl->current_type) - return S_OK; - - if (IMFMediaType_Compare(impl->current_type, (IMFAttributes *)in_type, - MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) - return S_OK; + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i;
- return MF_E_INVALIDMEDIATYPE; -} + add_object_state(&actual_object_state_record, SOURCE_PAUSE);
-static HRESULT WINAPI test_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + EnterCriticalSection(&source->cs);
-static HRESULT WINAPI test_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, - IMFMediaType **type) -{ - struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + event_type = MESourcePaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (impl->media_types) + for (i = 0; i < source->stream_count; ++i) { - impl->enum_count++; - - if (index >= impl->media_types_count) - return MF_E_NO_MORE_TYPES; + if (!is_stream_selected(source->pd, i)) + continue;
- IMFMediaType_AddRef((*type = impl->media_types[index])); - return S_OK; + event_type = MEStreamPaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
- ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI test_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType *media_type) -{ - struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); - - if (impl->current_type) - IMFMediaType_Release(impl->current_type); - IMFMediaType_AddRef((impl->current_type = media_type)); - impl->set_current_count++; + source->state = SOURCE_PAUSED; + LeaveCriticalSection(&source->cs);
return S_OK; }
-static HRESULT WINAPI test_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **media_type) +static HRESULT WINAPI test_seek_source_Shutdown(IMFMediaSource *iface) { - struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); HRESULT hr;
- if (!impl->current_type) - { - if (!impl->media_types) - return E_FAIL; - if (!impl->media_types_count) - return MF_E_TRANSFORM_TYPE_NOT_SET; - return MF_E_NOT_INITIALIZED; - } - - if (FAILED(hr = MFCreateMediaType(media_type))) - return hr; - - hr = IMFMediaType_CopyAllItems(impl->current_type, (IMFAttributes *)*media_type); - if (FAILED(hr)) - IMFMediaType_Release(*media_type); + add_object_state(&actual_object_state_record, SOURCE_SHUTDOWN);
- return hr; -} + hr = IMFMediaEventQueue_Shutdown(source->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + return S_OK; }
-static const IMFMediaTypeHandlerVtbl test_handler_vtbl = -{ - test_handler_QueryInterface, - test_handler_AddRef, - test_handler_Release, - test_handler_IsMediaTypeSupported, - test_handler_GetMediaTypeCount, - test_handler_GetMediaTypeByIndex, - test_handler_SetCurrentMediaType, - test_handler_GetCurrentMediaType, - test_handler_GetMajorType, -}; - -static const struct test_handler test_handler = {.IMFMediaTypeHandler_iface.lpVtbl = &test_handler_vtbl}; - -struct test_media_sink +static const IMFMediaSourceVtbl test_seek_source_vtbl = { - IMFMediaSink IMFMediaSink_iface; - BOOL shutdown; + test_seek_source_QueryInterface, + test_seek_source_AddRef, + test_seek_source_Release, + test_seek_source_GetEvent, + test_seek_source_BeginGetEvent, + test_seek_source_EndGetEvent, + test_seek_source_QueueEvent, + test_seek_source_GetCharacteristics, + test_seek_source_CreatePresentationDescriptor, + test_seek_source_Start, + test_seek_source_Stop, + test_seek_source_Pause, + test_seek_source_Shutdown, };
-static struct test_media_sink *impl_from_IMFMediaSink(IMFMediaSink *iface) -{ - return CONTAINING_RECORD(iface, struct test_media_sink, IMFMediaSink_iface); -} - -static HRESULT WINAPI test_media_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) +static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) { - if (IsEqualIID(riid, &IID_IMFMediaSink) - || IsEqualIID(riid, &IID_IUnknown)) + if (IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) { - IMFMediaSink_AddRef((*obj = iface)); + *obj = iface; + IMFClockStateSink_AddRef(iface); return S_OK; }
@@ -1640,2946 +1775,992 @@ static HRESULT WINAPI test_media_sink_QueryInterface(IMFMediaSink *iface, REFIID return E_NOINTERFACE; }
-static ULONG WINAPI test_media_sink_AddRef(IMFMediaSink *iface) +static ULONG WINAPI test_seek_clock_sink_AddRef(IMFClockStateSink *iface) { - return 2; + return 2; }
-static ULONG WINAPI test_media_sink_Release(IMFMediaSink *iface) +static ULONG WINAPI test_seek_clock_sink_Release(IMFClockStateSink *iface) { - return 1; + return 1; }
-static HRESULT WINAPI test_media_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *characteristics) +static HRESULT WINAPI test_seek_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) { - *characteristics = 0; - return S_OK; + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_START); + return S_OK; }
-static HRESULT WINAPI test_media_sink_AddStreamSink(IMFMediaSink *iface, - DWORD stream_sink_id, IMFMediaType *media_type, IMFStreamSink **stream_sink) +static HRESULT WINAPI test_seek_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_STOP); + return S_OK; }
-static HRESULT WINAPI test_media_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id) +static HRESULT WINAPI test_seek_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_PAUSE); + return S_OK; }
-static HRESULT WINAPI test_media_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) +static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_RESTART); + return S_OK; }
-static HRESULT WINAPI test_media_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, IMFStreamSink **sink) +static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); + return S_OK; }
-static HRESULT WINAPI test_media_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id, IMFStreamSink **sink) +static const IMFClockStateSinkVtbl test_seek_clock_sink_vtbl = { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + test_seek_clock_sink_QueryInterface, + test_seek_clock_sink_AddRef, + test_seek_clock_sink_Release, + test_seek_clock_sink_OnClockStart, + test_seek_clock_sink_OnClockStop, + test_seek_clock_sink_OnClockPause, + test_seek_clock_sink_OnClockRestart, + test_seek_clock_sink_OnClockSetRate, +};
-static HRESULT WINAPI test_media_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) +static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + struct test_media_stream *stream; + IMFPresentationDescriptor *pd; + BOOL selected; + HRESULT hr;
-static HRESULT WINAPI test_media_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + stream = calloc(1, sizeof(*stream)); + stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; + stream->refcount = 1; + hr = MFCreateEventQueue(&stream->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + stream->source = source; + IMFMediaSource_AddRef(stream->source); + stream->is_new = TRUE;
-static HRESULT WINAPI test_media_sink_Shutdown(IMFMediaSink *iface) -{ - struct test_media_sink *sink = impl_from_IMFMediaSink(iface); - ok(!sink->shutdown, "Unexpected call.\n"); - sink->shutdown = TRUE; - return S_OK; + IMFMediaSource_CreatePresentationDescriptor(source, &pd); + IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, stream_index, &selected, &stream->sd); + IMFPresentationDescriptor_Release(pd); + + return stream; }
-static const IMFMediaSinkVtbl test_media_sink_vtbl = +static IMFMediaSource *create_test_seek_source(BOOL seekable) { - test_media_sink_QueryInterface, - test_media_sink_AddRef, - test_media_sink_Release, - test_media_sink_GetCharacteristics, - test_media_sink_AddStreamSink, - test_media_sink_RemoveStreamSink, - test_media_sink_GetStreamSinkCount, - test_media_sink_GetStreamSinkByIndex, - test_media_sink_GetStreamSinkById, - test_media_sink_SetPresentationClock, - test_media_sink_GetPresentationClock, - test_media_sink_Shutdown, -}; + struct test_seek_source *source; + int i;
-static const struct test_media_sink test_media_sink = {.IMFMediaSink_iface.lpVtbl = &test_media_sink_vtbl}; + source = calloc(1, sizeof(*source)); + source->IMFMediaSource_iface.lpVtbl = &test_seek_source_vtbl; + source->refcount = 1; + source->stream_count = 1; + source->seekable = seekable; + MFCreateEventQueue(&source->event_queue); + InitializeCriticalSection(&source->cs); + for (i = 0; i < source->stream_count; ++i) + source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface);
-struct test_stream_sink -{ - IMFStreamSink IMFStreamSink_iface; - IMFMediaTypeHandler *handler; - IMFMediaSink *media_sink; -}; + return &source->IMFMediaSource_iface; +}
-static struct test_stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) -{ - return CONTAINING_RECORD(iface, struct test_stream_sink, IMFStreamSink_iface); -} - -static HRESULT WINAPI test_stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +static void test_media_session_events(void) { - if (IsEqualIID(riid, &IID_IMFStreamSink) - || IsEqualIID(riid, &IID_IMFMediaEventGenerator) - || IsEqualIID(riid, &IID_IUnknown)) + static const media_type_desc audio_float_44100 = { - IMFStreamSink_AddRef((*obj = iface)); - return S_OK; - } + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 44100), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), + }; + static const media_type_desc audio_pcm_48000 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 48000), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 2 * 8), + };
- *obj = NULL; - return E_NOINTERFACE; -} + struct test_stream_sink stream_sink = test_stream_sink; + struct test_media_sink media_sink = test_media_sink; + struct test_handler handler = test_handler; + struct test_source *source_impl; + IMFAsyncCallback *callback, *callback2; + IMFMediaType *input_type, *output_type; + IMFTopologyNode *src_node, *sink_node; + IMFPresentationDescriptor *pd; + IMFMediaSession *session; + IMFStreamDescriptor *sd; + IMFAsyncResult *result; + IMFMediaSource *source; + IMFTopology *topology; + IMFMediaEvent *event; + PROPVARIANT propvar; + HRESULT hr; + ULONG ref;
-static ULONG WINAPI test_stream_sink_AddRef(IMFStreamSink *iface) -{ - return 2; -} + stream_sink.handler = &handler.IMFMediaTypeHandler_iface; + stream_sink.media_sink = &media_sink.IMFMediaSink_iface;
-static ULONG WINAPI test_stream_sink_Release(IMFStreamSink *iface) -{ - return 1; -} + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
-static HRESULT WINAPI test_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + callback = create_test_callback(TRUE); + callback2 = create_test_callback(TRUE);
-static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr);
-static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, - IMFMediaEvent **event) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + hr = IMFMediaSession_GetEvent(session, MF_EVENT_FLAG_NO_WAIT, &event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, - REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + /* Async case. */ + hr = IMFMediaSession_BeginGetEvent(session, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) -{ - struct test_stream_sink *impl = impl_from_IMFStreamSink(iface); + hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)session); + ok(hr == S_OK, "Failed to Begin*, hr %#lx.\n", hr); + EXPECT_REF(callback, 2);
- if (impl->media_sink) - { - IMFMediaSink_AddRef((*sink = impl->media_sink)); - return S_OK; - } + /* Same callback, same state. */ + hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)session); + ok(hr == MF_S_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(callback, 2);
- todo_wine - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + /* Same callback, different state. */ + hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)callback); + ok(hr == MF_E_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(callback, 2);
-static HRESULT WINAPI test_stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *id) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + /* Different callback, same state. */ + hr = IMFMediaSession_BeginGetEvent(session, callback2, (IUnknown *)session); + ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(callback2, 1);
-static HRESULT WINAPI test_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) -{ - struct test_stream_sink *impl = impl_from_IMFStreamSink(iface); + /* Different callback, different state. */ + hr = IMFMediaSession_BeginGetEvent(session, callback2, (IUnknown *)callback); + ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(callback, 2);
- if (impl->handler) - { - IMFMediaTypeHandler_AddRef((*handler = impl->handler)); - return S_OK; - } + hr = MFCreateAsyncResult(NULL, callback, NULL, &result); + ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr);
- ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + hr = IMFMediaSession_EndGetEvent(session, result, &event); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + /* Shutdown behavior. */ + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + IMFMediaSession_Release(session);
-static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, - const PROPVARIANT *marker_value, const PROPVARIANT *context) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + /* Shutdown leaks callback */ + EXPECT_REF(callback, 2); + EXPECT_REF(callback2, 1);
-static HRESULT WINAPI test_stream_sink_Flush(IMFStreamSink *iface) -{ - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; -} + IMFAsyncCallback_Release(callback); + IMFAsyncCallback_Release(callback2);
-static const IMFStreamSinkVtbl test_stream_sink_vtbl = -{ - test_stream_sink_QueryInterface, - test_stream_sink_AddRef, - test_stream_sink_Release, - test_stream_sink_GetEvent, - test_stream_sink_BeginGetEvent, - test_stream_sink_EndGetEvent, - test_stream_sink_QueueEvent, - test_stream_sink_GetMediaSink, - test_stream_sink_GetIdentifier, - test_stream_sink_GetMediaTypeHandler, - test_stream_sink_ProcessSample, - test_stream_sink_PlaceMarker, - test_stream_sink_Flush, -};
-static const struct test_stream_sink test_stream_sink = {.IMFStreamSink_iface.lpVtbl = &test_stream_sink_vtbl}; + callback = create_test_callback(TRUE);
-struct test_callback -{ - IMFAsyncCallback IMFAsyncCallback_iface; - LONG refcount; + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- HANDLE event; - IMFMediaEvent *media_event; - BOOL check_media_event; -}; + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); -} + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); + todo_wine + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static HRESULT WINAPI testcallback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFAsyncCallback) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFAsyncCallback_AddRef(iface); - return S_OK; - } + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + todo_wine + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- *obj = NULL; - return E_NOINTERFACE; -} + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static ULONG WINAPI testcallback_AddRef(IMFAsyncCallback *iface) -{ - struct test_callback *callback = impl_from_IMFAsyncCallback(iface); - return InterlockedIncrement(&callback->refcount); -} + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static ULONG WINAPI testcallback_Release(IMFAsyncCallback *iface) -{ - struct test_callback *callback = impl_from_IMFAsyncCallback(iface); - ULONG refcount = InterlockedDecrement(&callback->refcount); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- if (!refcount) - { - if (callback->media_event) - IMFMediaEvent_Release(callback->media_event); - CloseHandle(callback->event); - free(callback); - } - - return refcount; -} - -static HRESULT WINAPI testcallback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - ok(flags != NULL && queue != NULL, "Unexpected arguments.\n"); - return E_NOTIMPL; -} + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) -{ - struct test_callback *callback = CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); - IUnknown *object; - HRESULT hr; + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(result != NULL, "Unexpected result object.\n"); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- if (callback->media_event) - IMFMediaEvent_Release(callback->media_event);
- if (callback->check_media_event) - { - hr = IMFAsyncResult_GetObject(result, &object); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFAsyncResult_GetState(result, &object); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, - result, &callback->media_event); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IUnknown_Release(object); - } + hr = MFCreateMediaType(&input_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(input_type, audio_float_44100, -1); + create_descriptors(1, &input_type, NULL, &pd, &sd);
- SetEvent(callback->event); + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(&stream_sink.IMFStreamSink_iface, -1, sink_node);
- return S_OK; -} + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, audio_pcm_48000, -1); + handler.media_types_count = 1; + handler.media_types = &output_type;
-static const IMFAsyncCallbackVtbl testcallbackvtbl = -{ - testcallback_QueryInterface, - testcallback_AddRef, - testcallback_Release, - testcallback_GetParameters, - testcallback_Invoke, -}; + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(NULL, -1, src_node, pd, sd);
-static IMFAsyncCallback *create_test_callback(BOOL check_media_event) -{ - struct test_callback *callback; + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (!(callback = calloc(1, sizeof(*callback)))) - return NULL; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == MF_E_TOPO_MISSING_SOURCE, "Unexpected hr %#lx.\n", hr);
- callback->refcount = 1; - callback->check_media_event = check_media_event; - callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl; - callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); - ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError()); + source = create_test_source(pd); + init_source_node(source, -1, src_node, pd, sd);
- return &callback->IMFAsyncCallback_iface; -} + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED, "Unexpected hr %#lx.\n", hr);
-#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) -{ - struct test_callback *impl = impl_from_IMFAsyncCallback(callback); - MediaEventType type; - HRESULT hr, status; - DWORD ret; - GUID guid; + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- do - { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ret = WaitForSingleObject(impl->event, timeout); - ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); - hr = IMFMediaEvent_GetType(impl->media_event, &type); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } while (type != expect_type); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!media_sink.shutdown, "media sink is shutdown.\n"); + media_sink.shutdown = FALSE;
- ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
- hr = IMFMediaEvent_GetValue(impl->media_event, value); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaEvent_GetStatus(impl->media_event, &status); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* SetTopology without a current output type */
- return status; -} + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) -{ - struct test_callback *impl = impl_from_IMFAsyncCallback(callback); - MediaEventType type; - HRESULT hr, status; - DWORD ret; - GUID guid; + IMFPresentationDescriptor_SelectStream(pd, 0);
- do - { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ret = WaitForSingleObject(impl->event, timeout); - if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; - hr = IMFMediaEvent_GetType(impl->media_event, &type); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } while (type != expect_type); + hr = IMFMediaSession_SetTopology(session, MFSESSION_SETTOPOLOGY_NORESOLUTION, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal == (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
- ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); + ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(!handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + handler.enum_count = handler.set_current_count = 0;
- hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- hr = IMFMediaEvent_GetValue(impl->media_event, value); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
- hr = IMFMediaEvent_GetStatus(impl->media_event, &status); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- return status; -} + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
-static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) -{ - IMFSourceResolver *resolver; - IMFAttributes *attributes; - const BYTE *resource_data; - MF_OBJECT_TYPE obj_type; - IMFMediaSource *source; - IMFByteStream *stream; - ULONG resource_len; - HRSRC resource; - HRESULT hr;
- resource = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); - ok(resource != 0, "FindResourceW %s failed, error %lu\n", debugstr_w(name), GetLastError()); - resource_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); - resource_len = SizeofResource(GetModuleHandleW(NULL), resource); + /* SetTopology without a current output type */
- hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &stream); + hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFByteStream_Write(stream, resource_data, resource_len, &resource_len); + + hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFByteStream_SetCurrentPosition(stream, 0); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
- hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes); + todo_wine + ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + handler.enum_count = handler.set_current_count = 0; + + hr = IMFMediaSession_ClearTopologies(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, mime); - ok(hr == S_OK, "Failed to set string value, hr %#lx.\n", hr); - IMFAttributes_Release(attributes); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- hr = MFCreateSourceResolver(&resolver); + hr = IMFMediaSession_Shutdown(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, - &obj_type, (IUnknown **)&source); - todo_wine_if(hr == MF_E_UNEXPECTED) /* Gitlab CI Debian runner */ - ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "Unexpected hr %#lx.\n", hr); - IMFSourceResolver_Release(resolver); - IMFByteStream_Release(stream); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
- if (FAILED(hr)) - return NULL; + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type); - return source; -} + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
-enum object_state -{ - SOURCE_START, - SOURCE_PAUSE, - SOURCE_STOP, - SOURCE_SHUTDOWN, - SINK_ON_CLOCK_START, - SINK_ON_CLOCK_PAUSE, - SINK_ON_CLOCK_STOP, - SINK_ON_CLOCK_RESTART, - SINK_ON_CLOCK_SETRATE, -};
-#define MAX_OBJECT_STATE 1024 + /* SetTopology without a current output type, refusing input type */
-struct object_state_record -{ - enum object_state states[MAX_OBJECT_STATE]; - unsigned int state_count; -}; -static struct object_state_record actual_object_state_record; + handler.invalid_type = input_type;
-#define add_object_state(a, b) _add_object_state(__LINE__, a, b) -static void _add_object_state(int line, struct object_state_record *record, enum object_state state) -{ - ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); - if (record->state_count < MAX_OBJECT_STATE) - record->states[record->state_count++] = state; -} + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) -static void _compare_object_states(int line, const struct object_state_record *r1, - const struct object_state_record *r2) -{ - ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); - if (r1->state_count == r2->state_count) - ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); -} + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
-enum source_state -{ - SOURCE_STOPPED, - SOURCE_RUNNING, - SOURCE_PAUSED, -}; + ok(handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + handler.enum_count = handler.set_current_count = 0;
-struct test_media_stream -{ - IMFMediaStream IMFMediaStream_iface; - IMFMediaEventQueue *event_queue; - IMFStreamDescriptor *sd; - IMFMediaSource *source; - LONGLONG sample_duration; - LONGLONG sample_time; - BOOL is_new; - LONG refcount; -}; + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static struct test_media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) -{ - return CONTAINING_RECORD(iface, struct test_media_stream, IMFMediaStream_iface); -} + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
-static HRESULT WINAPI test_media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) -{ - if (IsEqualIID(riid, &IID_IMFMediaStream) - || IsEqualIID(riid, &IID_IMFMediaEventGenerator) - || IsEqualIID(riid, &IID_IUnknown)) - { - *out = iface; - } - else - { - *out = NULL; - return E_NOINTERFACE; - } + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- IMFMediaStream_AddRef(iface); - return S_OK; -} + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
-static ULONG WINAPI test_media_stream_AddRef(IMFMediaStream *iface) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return InterlockedIncrement(&stream->refcount); -}
-static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - ULONG refcount = InterlockedDecrement(&stream->refcount); + /* SetTopology without a current output type, refusing input type, requiring a converter */
- if (!refcount) - { - IMFMediaEventQueue_Release(stream->event_queue); - free(stream); - } + handler.media_types_count = 0; + handler.invalid_type = input_type;
- return refcount; -} + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); -} + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
-static HRESULT WINAPI test_media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); -} + ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + handler.enum_count = handler.set_current_count = 0;
-static HRESULT WINAPI test_media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); -} + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
-static HRESULT WINAPI test_media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, - HRESULT hr, const PROPVARIANT *value) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); -} + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
-static HRESULT WINAPI test_media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- *source = stream->source; - IMFMediaSource_AddRef(*source); + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
- return S_OK; -}
-static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + /* SetTopology with a current output type */
- *sd = stream->sd; - IMFStreamDescriptor_AddRef(*sd); + handler.media_types_count = 1; + IMFMediaType_AddRef((handler.current_type = output_type));
- return S_OK; -} + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) -{ - struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - IMFMediaBuffer *buffer; - IMFSample *sample; - HRESULT hr; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
- hr = MFCreateSample(&sample); + ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + handler.enum_count = handler.set_current_count = 0; + + hr = IMFMediaSession_ClearTopologies(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (stream->sample_duration) - { - hr = IMFSample_SetSampleDuration(sample, stream->sample_duration); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
- hr = IMFSample_SetSampleTime(sample, stream->sample_time); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
- stream->sample_time += stream->sample_duration; - } - else - { - hr = IMFSample_SetSampleTime(sample, 123); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- hr = IMFSample_SetSampleDuration(sample, 1); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - }
- if (token) - IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + /* test IMFMediaSession_Start with source returning an error in BeginGetEvent */ + source_impl = impl_from_IMFMediaSource(source);
- /* Reader expects buffers, empty samples are considered an error. */ - hr = MFCreateMemoryBuffer(8, &buffer); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFSample_AddBuffer(sample, buffer); + hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFMediaBuffer_Release(buffer);
- hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, - (IUnknown *)sample); + hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFSample_Release(sample); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
- return S_OK; -} + source_impl->begin_get_event_res = 0x80001234;
-static const IMFMediaStreamVtbl test_media_stream_vtbl = -{ - test_media_stream_QueryInterface, - test_media_stream_AddRef, - test_media_stream_Release, - test_media_stream_GetEvent, - test_media_stream_BeginGetEvent, - test_media_stream_EndGetEvent, - test_media_stream_QueueEvent, - test_media_stream_GetMediaSource, - test_media_stream_GetStreamDescriptor, - test_media_stream_RequestSample, -}; - -#define TEST_SOURCE_NUM_STREAMS 3 - -struct test_seek_source -{ - IMFMediaSource IMFMediaSource_iface; - IMFMediaEventQueue *event_queue; - IMFPresentationDescriptor *pd; - struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; - enum source_state state; - unsigned stream_count; - CRITICAL_SECTION cs; - BOOL seekable; - LONG refcount; -}; - -static struct test_seek_source *impl_test_seek_source_from_IMFMediaSource(IMFMediaSource *iface) -{ - return CONTAINING_RECORD(iface, struct test_seek_source, IMFMediaSource_iface); -} - -static HRESULT WINAPI test_seek_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) -{ - if (IsEqualIID(riid, &IID_IMFMediaSource) - || IsEqualIID(riid, &IID_IMFMediaEventGenerator) - || IsEqualIID(riid, &IID_IUnknown)) - { - *out = iface; - } - else - { - *out = NULL; - return E_NOINTERFACE; - } - - IMFMediaSource_AddRef(iface); - return S_OK; -} - -static ULONG WINAPI test_seek_source_AddRef(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return InterlockedIncrement(&source->refcount); -} - -static ULONG WINAPI test_seek_source_Release(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - ULONG refcount = InterlockedDecrement(&source->refcount); - - if (!refcount) - { - IMFMediaEventQueue_Release(source->event_queue); - free(source); - } - - return refcount; -} - -static HRESULT WINAPI test_seek_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); -} - -static HRESULT WINAPI test_seek_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); -} - -static HRESULT WINAPI test_seek_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); -} - -static HRESULT WINAPI test_seek_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, - HRESULT hr, const PROPVARIANT *value) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); -} - -static HRESULT WINAPI test_seek_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - - if (source->seekable) - *flags = MFMEDIASOURCE_CAN_PAUSE | MFMEDIASOURCE_CAN_SEEK; - else - *flags = MFMEDIASOURCE_CAN_PAUSE; - return S_OK; -} - -static HRESULT WINAPI test_seek_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; - IMFMediaType *media_type; - HRESULT hr = S_OK; - int i; - - EnterCriticalSection(&source->cs); - - if (source->pd) - { - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - else - { - for (i = 0; i < source->stream_count; ++i) - { - hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFMediaType_Release(media_type); - } - - hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_SetUINT64(source->pd, &MF_PD_DURATION, 10 * 10000000); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - for (i = 0; i < source->stream_count; ++i) - IMFStreamDescriptor_Release(sds[i]); - - *pd = source->pd; - IMFPresentationDescriptor_AddRef(*pd); - } - - LeaveCriticalSection(&source->cs); - - return hr; -} - -static BOOL is_stream_selected(IMFPresentationDescriptor *pd, DWORD index) -{ - IMFStreamDescriptor *sd; - BOOL selected = FALSE; - - if (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) - IMFStreamDescriptor_Release(sd); - - return selected; -} - -static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, - const PROPVARIANT *start_position) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - PROPVARIANT var; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_START); - - ok(time_format && IsEqualGUID(time_format, &GUID_NULL), "Unexpected time format %s.\n", - wine_dbgstr_guid(time_format)); - ok(start_position && (start_position->vt == VT_I8 || start_position->vt == VT_EMPTY), - "Unexpected position type.\n"); - - /* This is what makes IMFMediaSession::Start() seeking fail, not the lacking of MFMEDIASOURCE_CAN_SEEK. - * Without this, IMFMediaSession::Start() seeking succeeds even with the missing MFMEDIASOURCE_CAN_SEEK. - * If this is check is not here, the first IMFMediaSession::Start() call to a non-zero position - * succeeds somehow on Windows 10, then all following seeks fails and no MESessionStarted events */ - if (!source->seekable && start_position && start_position->vt == VT_I8 && start_position->hVal.QuadPart) - return E_FAIL; - - EnterCriticalSection(&source->cs); - - event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(pd, i)) - continue; - - var.vt = VT_UNKNOWN; - var.punkVal = (IUnknown *)&source->streams[i]->IMFMediaStream_iface; - event_type = source->streams[i]->is_new ? MENewStream : MEUpdatedStream; - source->streams[i]->is_new = FALSE; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_RUNNING; - - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Stop(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_STOP); - - EnterCriticalSection(&source->cs); - - event_type = MESourceStopped; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(source->pd, i)) - continue; - - event_type = MEStreamStopped; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_STOPPED; - - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - MediaEventType event_type; - HRESULT hr; - int i; - - add_object_state(&actual_object_state_record, SOURCE_PAUSE); - - EnterCriticalSection(&source->cs); - - event_type = MESourcePaused; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < source->stream_count; ++i) - { - if (!is_stream_selected(source->pd, i)) - continue; - - event_type = MEStreamPaused; - hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - - source->state = SOURCE_PAUSED; - LeaveCriticalSection(&source->cs); - - return S_OK; -} - -static HRESULT WINAPI test_seek_source_Shutdown(IMFMediaSource *iface) -{ - struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); - HRESULT hr; - - add_object_state(&actual_object_state_record, SOURCE_SHUTDOWN); - - hr = IMFMediaEventQueue_Shutdown(source->event_queue); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - return S_OK; -} - -static const IMFMediaSourceVtbl test_seek_source_vtbl = -{ - test_seek_source_QueryInterface, - test_seek_source_AddRef, - test_seek_source_Release, - test_seek_source_GetEvent, - test_seek_source_BeginGetEvent, - test_seek_source_EndGetEvent, - test_seek_source_QueueEvent, - test_seek_source_GetCharacteristics, - test_seek_source_CreatePresentationDescriptor, - test_seek_source_Start, - test_seek_source_Stop, - test_seek_source_Pause, - test_seek_source_Shutdown, -}; - -static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFClockStateSink) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFClockStateSink_AddRef(iface); - return S_OK; - } - - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI test_seek_clock_sink_AddRef(IMFClockStateSink *iface) -{ - return 2; -} - -static ULONG WINAPI test_seek_clock_sink_Release(IMFClockStateSink *iface) -{ - return 1; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_START); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_STOP); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_PAUSE); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_RESTART); - return S_OK; -} - -static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) -{ - add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); - return S_OK; -} - -static const IMFClockStateSinkVtbl test_seek_clock_sink_vtbl = -{ - test_seek_clock_sink_QueryInterface, - test_seek_clock_sink_AddRef, - test_seek_clock_sink_Release, - test_seek_clock_sink_OnClockStart, - test_seek_clock_sink_OnClockStop, - test_seek_clock_sink_OnClockPause, - test_seek_clock_sink_OnClockRestart, - test_seek_clock_sink_OnClockSetRate, -}; - -static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) -{ - struct test_media_stream *stream; - IMFPresentationDescriptor *pd; - BOOL selected; - HRESULT hr; - - stream = calloc(1, sizeof(*stream)); - stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; - stream->refcount = 1; - hr = MFCreateEventQueue(&stream->event_queue); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - stream->source = source; - IMFMediaSource_AddRef(stream->source); - stream->is_new = TRUE; - - IMFMediaSource_CreatePresentationDescriptor(source, &pd); - IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, stream_index, &selected, &stream->sd); - IMFPresentationDescriptor_Release(pd); - - return stream; -} - -static IMFMediaSource *create_test_seek_source(BOOL seekable) -{ - struct test_seek_source *source; - int i; - - source = calloc(1, sizeof(*source)); - source->IMFMediaSource_iface.lpVtbl = &test_seek_source_vtbl; - source->refcount = 1; - source->stream_count = 1; - source->seekable = seekable; - MFCreateEventQueue(&source->event_queue); - InitializeCriticalSection(&source->cs); - for (i = 0; i < source->stream_count; ++i) - source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); - - return &source->IMFMediaSource_iface; -} - -static void test_media_session_events(void) -{ - static const media_type_desc audio_float_44100 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 44100), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), - }; - static const media_type_desc audio_pcm_48000 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 48000), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 2 * 8), - }; - - struct test_stream_sink stream_sink = test_stream_sink; - struct test_media_sink media_sink = test_media_sink; - struct test_handler handler = test_handler; - struct test_source *source_impl; - IMFAsyncCallback *callback, *callback2; - IMFMediaType *input_type, *output_type; - IMFTopologyNode *src_node, *sink_node; - IMFPresentationDescriptor *pd; - IMFMediaSession *session; - IMFStreamDescriptor *sd; - IMFAsyncResult *result; - IMFMediaSource *source; - IMFTopology *topology; - IMFMediaEvent *event; - PROPVARIANT propvar; - HRESULT hr; - ULONG ref; - - stream_sink.handler = &handler.IMFMediaTypeHandler_iface; - stream_sink.media_sink = &media_sink.IMFMediaSink_iface; - - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); - - callback = create_test_callback(TRUE); - callback2 = create_test_callback(TRUE); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); - - hr = IMFMediaSession_GetEvent(session, MF_EVENT_FLAG_NO_WAIT, &event); - ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); - - /* Async case. */ - hr = IMFMediaSession_BeginGetEvent(session, NULL, NULL); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)session); - ok(hr == S_OK, "Failed to Begin*, hr %#lx.\n", hr); - EXPECT_REF(callback, 2); - - /* Same callback, same state. */ - hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)session); - ok(hr == MF_S_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); - EXPECT_REF(callback, 2); - - /* Same callback, different state. */ - hr = IMFMediaSession_BeginGetEvent(session, callback, (IUnknown *)callback); - ok(hr == MF_E_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); - EXPECT_REF(callback, 2); - - /* Different callback, same state. */ - hr = IMFMediaSession_BeginGetEvent(session, callback2, (IUnknown *)session); - ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#lx.\n", hr); - EXPECT_REF(callback2, 1); - - /* Different callback, different state. */ - hr = IMFMediaSession_BeginGetEvent(session, callback2, (IUnknown *)callback); - ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#lx.\n", hr); - EXPECT_REF(callback, 2); - - hr = MFCreateAsyncResult(NULL, callback, NULL, &result); - ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); - - hr = IMFMediaSession_EndGetEvent(session, result, &event); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - - /* Shutdown behavior. */ - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); - IMFMediaSession_Release(session); - - /* Shutdown leaks callback */ - EXPECT_REF(callback, 2); - EXPECT_REF(callback2, 1); - - IMFAsyncCallback_Release(callback); - IMFAsyncCallback_Release(callback2); - - - callback = create_test_callback(TRUE); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Stop(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); - todo_wine - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Pause(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); - todo_wine - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Close(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateMediaType(&input_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(input_type, audio_float_44100, -1); - create_descriptors(1, &input_type, NULL, &pd, &sd); - - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_sink_node(&stream_sink.IMFStreamSink_iface, -1, sink_node); - - hr = MFCreateMediaType(&output_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_media_type(output_type, audio_pcm_48000, -1); - handler.media_types_count = 1; - handler.media_types = &output_type; - - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_source_node(NULL, -1, src_node, pd, sd); - - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == MF_E_TOPO_MISSING_SOURCE, "Unexpected hr %#lx.\n", hr); - - source = create_test_source(pd); - init_source_node(source, -1, src_node, pd, sd); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!media_sink.shutdown, "media sink is shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - - /* SetTopology without a current output type */ - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - IMFPresentationDescriptor_SelectStream(pd, 0); - - hr = IMFMediaSession_SetTopology(session, MFSESSION_SETTOPOLOGY_NORESOLUTION, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal == (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(!handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); - handler.enum_count = handler.set_current_count = 0; - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - - /* SetTopology without a current output type */ - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - todo_wine - ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); - handler.enum_count = handler.set_current_count = 0; - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - - /* SetTopology without a current output type, refusing input type */ - - handler.invalid_type = input_type; - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - ok(handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); - handler.enum_count = handler.set_current_count = 0; - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - - /* SetTopology without a current output type, refusing input type, requiring a converter */ - - handler.media_types_count = 0; - handler.invalid_type = input_type; - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); - handler.enum_count = handler.set_current_count = 0; - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - - /* SetTopology with a current output type */ - - handler.media_types_count = 1; - IMFMediaType_AddRef((handler.current_type = output_type)); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); - handler.enum_count = handler.set_current_count = 0; - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - - /* test IMFMediaSession_Start with source returning an error in BeginGetEvent */ - source_impl = impl_from_IMFMediaSource(source); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - source_impl->begin_get_event_res = 0x80001234; - - SET_EXPECT(test_source_BeginGetEvent); - SET_EXPECT(test_source_QueueEvent); - SET_EXPECT(test_source_Start); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == 0x80001234, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - CHECK_CALLED(test_source_BeginGetEvent); - CHECK_NOT_CALLED(test_source_Start); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - source_impl->begin_get_event_res = E_NOTIMPL; - - CLEAR_CALLED(test_source_BeginGetEvent); - CLEAR_CALLED(test_source_QueueEvent); - CLEAR_CALLED(test_source_Start); - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - - /* test IMFMediaSession_Start when test source BeginGetEvent returns S_OK */ - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_SetTopology(session, 0, topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - source_impl = impl_from_IMFMediaSource(source); - source_impl->begin_get_event_res = S_OK; - - SET_EXPECT(test_source_BeginGetEvent); - SET_EXPECT(test_source_QueueEvent); - SET_EXPECT(test_source_Start); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); - ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); - ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); - ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); - PropVariantClear(&propvar); - - CHECK_CALLED(test_source_BeginGetEvent); - CHECK_CALLED(test_source_Start); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(media_sink.shutdown, "media sink didn't shutdown.\n"); - media_sink.shutdown = FALSE; - - source_impl->begin_get_event_res = E_NOTIMPL; - - CLEAR_CALLED(test_source_BeginGetEvent); - CLEAR_CALLED(test_source_QueueEvent); - CLEAR_CALLED(test_source_Start); - - /* sometimes briefly leaking */ - IMFMediaSession_Release(session); - - IMFAsyncCallback_Release(callback); - - if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; - - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(src_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(sink_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); - - ref = IMFMediaSource_Release(source); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFPresentationDescriptor_Release(pd); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFStreamDescriptor_Release(sd); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFMediaType_Release(input_type); - ok(ref == 0, "Release returned %ld\n", ref); - - - hr = MFShutdown(); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); -} - -static void test_media_session(void) -{ - IMFRateSupport *rate_support; - IMFAttributes *attributes; - IMFMediaSession *session; - MFSHUTDOWN_STATUS status; - IMFTopology *topology; - IMFShutdown *shutdown; - PROPVARIANT propvar; - IMFGetService *gs; - IMFClock *clock; - HRESULT hr; - DWORD caps; - - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); - - check_interface(session, &IID_IMFGetService, TRUE); - check_interface(session, &IID_IMFRateSupport, TRUE); - check_interface(session, &IID_IMFRateControl, TRUE); - check_interface(session, &IID_IMFAttributes, FALSE); - check_interface(session, &IID_IMFTopologyNodeAttributeEditor, FALSE); - check_interface(session, &IID_IMFLocalMFTRegistration, FALSE); - check_service_interface(session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, TRUE); - check_service_interface(session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, TRUE); - check_service_interface(session, &MF_TOPONODE_ATTRIBUTE_EDITOR_SERVICE, &IID_IMFTopologyNodeAttributeEditor, TRUE); - check_service_interface(session, &MF_LOCAL_MFT_REGISTRATION_SERVICE, &IID_IMFLocalMFTRegistration, TRUE); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFShutdown_GetShutdownStatus(shutdown, &status); - ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); - - check_interface(session, &IID_IMFGetService, TRUE); - - hr = IMFMediaSession_QueryInterface(session, &IID_IMFGetService, (void **)&gs); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFGetService_GetService(gs, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - IMFGetService_Release(gs); - - hr = IMFShutdown_GetShutdownStatus(shutdown, &status); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(status == MFSHUTDOWN_COMPLETED, "Unexpected shutdown status %u.\n", status); - - IMFShutdown_Release(shutdown); - - hr = IMFMediaSession_ClearTopologies(session); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - - propvar.vt = VT_EMPTY; - hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Pause(session); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Stop(session); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Close(session); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == MF_E_SHUTDOWN || broken(hr == E_UNEXPECTED) /* Win7 */, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_GetSessionCapabilities(session, &caps); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_GetSessionCapabilities(session, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_GetFullTopology(session, MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSession_Shutdown(session); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - IMFMediaSession_Release(session); - - /* Custom topology loader, GUID is not registered. */ - hr = MFCreateAttributes(&attributes, 1); - ok(hr == S_OK, "Failed to create attributes, hr %#lx.\n", hr); - - hr = IMFAttributes_SetGUID(attributes, &MF_SESSION_TOPOLOADER, &MF_SESSION_TOPOLOADER); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); - - hr = MFCreateMediaSession(attributes, &session); - ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); - IMFMediaSession_Release(session); - - /* Disabled quality manager. */ - hr = IMFAttributes_SetGUID(attributes, &MF_SESSION_QUALITY_MANAGER, &GUID_NULL); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); - - hr = MFCreateMediaSession(attributes, &session); - ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); - IMFMediaSession_Release(session); - - IMFAttributes_Release(attributes); -} - -static void test_media_session_rate_control(void) -{ - IMFRateControl *rate_control, *clock_rate_control; - IMFPresentationClock *presentation_clock; - IMFPresentationTimeSource *time_source; - MFCLOCK_PROPERTIES clock_props; - IMFRateSupport *rate_support; - IMFMediaSession *session; - IMFClock *clock; - HRESULT hr; - float rate; - BOOL thin; - - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); - - hr = MFCreateMediaSession(NULL, &session); - ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); - - hr = MFGetService((IUnknown *)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support); - ok(hr == S_OK, "Failed to get rate support interface, hr %#lx.\n", hr); - - hr = MFGetService((IUnknown *)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void **)&rate_control); - ok(hr == S_OK, "Failed to get rate control interface, hr %#lx.\n", hr); - - hr = IMFRateControl_GetRate(rate_control, NULL, NULL); - ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); - - rate = 0.0f; - hr = IMFRateControl_GetRate(rate_control, NULL, &rate); - ok(hr == S_OK, "Failed to get playback rate, hr %#lx.\n", hr); - ok(rate == 1.0f, "Unexpected rate %f.\n", rate); - - hr = IMFRateControl_GetRate(rate_control, &thin, NULL); - ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); - - thin = TRUE; - rate = 0.0f; - hr = IMFRateControl_GetRate(rate_control, &thin, &rate); - ok(hr == S_OK, "Failed to get playback rate, hr %#lx.\n", hr); - ok(!thin, "Unexpected thinning.\n"); - ok(rate == 1.0f, "Unexpected rate %f.\n", rate); - - hr = IMFMediaSession_GetClock(session, &clock); - ok(hr == S_OK, "Failed to get clock, hr %#lx.\n", hr); - - hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); - ok(hr == S_OK, "Failed to get rate control, hr %#lx.\n", hr); - - hr = IMFClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&clock_rate_control); - ok(hr == S_OK, "Failed to get rate control, hr %#lx.\n", hr); - - rate = 0.0f; - hr = IMFRateControl_GetRate(clock_rate_control, NULL, &rate); - ok(hr == S_OK, "Failed to get clock rate, hr %#lx.\n", hr); - ok(rate == 1.0f, "Unexpected rate %f.\n", rate); - - hr = IMFRateControl_SetRate(clock_rate_control, FALSE, 1.5f); - ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#lx.\n", hr); - - hr = IMFRateControl_SetRate(rate_control, FALSE, 1.5f); - ok(hr == S_OK, "Failed to set rate, hr %#lx.\n", hr); - - hr = IMFClock_GetProperties(clock, &clock_props); - ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateSystemTimeSource(&time_source); - ok(hr == S_OK, "Failed to create time source, hr %#lx.\n", hr); - - hr = IMFPresentationClock_SetTimeSource(presentation_clock, time_source); - ok(hr == S_OK, "Failed to set time source, hr %#lx.\n", hr); - - hr = IMFRateControl_SetRate(rate_control, FALSE, 1.5f); - ok(hr == S_OK, "Failed to set rate, hr %#lx.\n", hr); - - rate = 0.0f; - hr = IMFRateControl_GetRate(clock_rate_control, NULL, &rate); - ok(hr == S_OK, "Failed to get clock rate, hr %#lx.\n", hr); - ok(rate == 1.0f, "Unexpected rate %f.\n", rate); - - IMFPresentationTimeSource_Release(time_source); - - IMFRateControl_Release(clock_rate_control); - IMFPresentationClock_Release(presentation_clock); - IMFClock_Release(clock); - - IMFRateControl_Release(rate_control); - IMFRateSupport_Release(rate_support); - - IMFMediaSession_Release(session); - - hr = MFShutdown(); - ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); -} - -struct test_grabber_callback -{ - IMFSampleGrabberSinkCallback IMFSampleGrabberSinkCallback_iface; - LONG refcount; - - IMFCollection *samples; - HANDLE ready_event; - HANDLE done_event; -}; - -static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) -{ - return CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); -} - -static HRESULT WINAPI test_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) || - IsEqualIID(riid, &IID_IMFClockStateSink) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFSampleGrabberSinkCallback_AddRef(iface); - return S_OK; - } - - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI test_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface) -{ - struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); - return InterlockedIncrement(&grabber->refcount); -} - -static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface) -{ - struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); - ULONG refcount = InterlockedDecrement(&grabber->refcount); - - if (!refcount) - { - IMFCollection_Release(grabber->samples); - if (grabber->ready_event) - CloseHandle(grabber->ready_event); - if (grabber->done_event) - CloseHandle(grabber->done_event); - free(grabber); - } - - return refcount; -} + SET_EXPECT(test_source_BeginGetEvent); + SET_EXPECT(test_source_QueueEvent); + SET_EXPECT(test_source_Start);
-static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) -{ - return S_OK; -} + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == 0x80001234, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
-static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) -{ - return S_OK; -} + CHECK_CALLED(test_source_BeginGetEvent); + CHECK_NOT_CALLED(test_source_Start);
-static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) -{ - return S_OK; -} + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
-static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) -{ - return S_OK; -} + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
-static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) -{ - return S_OK; -} + source_impl->begin_get_event_res = E_NOTIMPL;
-static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, - IMFPresentationClock *clock) -{ - return S_OK; -} + CLEAR_CALLED(test_source_BeginGetEvent); + CLEAR_CALLED(test_source_QueueEvent); + CLEAR_CALLED(test_source_Start);
-static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface, REFGUID major_type, - DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration, const BYTE *buffer, DWORD sample_size) -{ - struct test_grabber_callback *grabber = CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); - IMFSample *sample; - HRESULT hr; - DWORD res; + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- if (!grabber->ready_event) - return E_NOTIMPL;
- sample = create_sample(buffer, sample_size); - hr = IMFSample_SetSampleFlags(sample, sample_flags); + /* test IMFMediaSession_Start when test source BeginGetEvent returns S_OK */ + hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - /* FIXME: sample time is inconsistent across windows versions, ignore it */ - hr = IMFSample_SetSampleTime(sample, 0); + + hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFSample_SetSampleDuration(sample, sample_duration); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFCollection_AddElement(grabber->samples, (IUnknown *)sample); - IMFSample_Release(sample); - - SetEvent(grabber->ready_event); - res = WaitForSingleObject(grabber->done_event, 1000); - ok(!res, "WaitForSingleObject returned %#lx\n", res); - - return S_OK; -} + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
-static HRESULT WINAPI test_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface) -{ - return S_OK; -} + source_impl = impl_from_IMFMediaSource(source); + source_impl->begin_get_event_res = S_OK;
-static const IMFSampleGrabberSinkCallbackVtbl test_grabber_callback_vtbl = -{ - test_grabber_callback_QueryInterface, - test_grabber_callback_AddRef, - test_grabber_callback_Release, - test_grabber_callback_OnClockStart, - test_grabber_callback_OnClockStop, - test_grabber_callback_OnClockPause, - test_grabber_callback_OnClockRestart, - test_grabber_callback_OnClockSetRate, - test_grabber_callback_OnSetPresentationClock, - test_grabber_callback_OnProcessSample, - test_grabber_callback_OnShutdown, -}; + SET_EXPECT(test_source_BeginGetEvent); + SET_EXPECT(test_source_QueueEvent); + SET_EXPECT(test_source_Start);
-static IMFSampleGrabberSinkCallback *create_test_grabber_callback(void) -{ - struct test_grabber_callback *grabber; - HRESULT hr; + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar);
- if (!(grabber = calloc(1, sizeof(*grabber)))) - return NULL; + CHECK_CALLED(test_source_BeginGetEvent); + CHECK_CALLED(test_source_Start);
- grabber->IMFSampleGrabberSinkCallback_iface.lpVtbl = &test_grabber_callback_vtbl; - grabber->refcount = 1; - hr = MFCreateCollection(&grabber->samples); + hr = IMFMediaSession_ClearTopologies(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- return &grabber->IMFSampleGrabberSinkCallback_iface; -} - -enum loader_test_flags -{ - LOADER_TODO = 0x4, - LOADER_NEEDS_VIDEO_PROCESSOR = 0x8, - LOADER_SET_ENUMERATE_SOURCE_TYPES = 0x10, - LOADER_NO_CURRENT_OUTPUT = 0x20, - LOADER_SET_INVALID_INPUT = 0x40, - LOADER_SET_MEDIA_TYPES = 0x80, - LOADER_ADD_RESAMPLER_MFT = 0x100, -}; + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE;
-static void test_topology_loader(void) -{ - static const media_type_desc audio_float_44100 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 44100), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), - }; - static const media_type_desc audio_pcm_44100 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), - }; - static const media_type_desc audio_pcm_48000 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 48000), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), - }; - static const media_type_desc audio_pcm_48000_resampler = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 192000), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), - ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), - }; - static const media_type_desc audio_float_48000 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 8 * 48000), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), - }; - static const media_type_desc audio_mp3_44100 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_MP3), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 16000), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), - }; - static const media_type_desc audio_pcm_44100_incomplete = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), - }; - static const media_type_desc audio_float_44100_stereo = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), - ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), - ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), - ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * 4), - ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), - ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 4 * 44100), - ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), - ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), - }; - static const media_type_desc video_i420_1280 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), - ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), - }; - static const media_type_desc video_color_convert_1280_rgb32 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, DMOVideoFormat_RGB32), - ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), - }; - static const media_type_desc video_video_processor_1280_rgb32 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), - ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), - }; - static const media_type_desc video_video_processor_rgb32 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), - }; - static const media_type_desc video_h264_1280 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), - ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), - }; - static const media_type_desc video_nv12_1280 = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), - ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), - ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), - ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), - ATTR_UINT32(MF_MT_SAMPLE_SIZE, 1280 * 720 * 3 / 2), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), - ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 1280), - ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), - ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), - }; - static const media_type_desc video_dummy = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - }; + source_impl->begin_get_event_res = E_NOTIMPL;
- const struct loader_test - { - const media_type_desc *input_type; - const media_type_desc *output_type; - const media_type_desc *current_input; - const media_type_desc *decoded_type; - MF_CONNECT_METHOD source_method; - MF_CONNECT_METHOD sink_method; - HRESULT expected_result; - unsigned int flags; - GUID decoder_class; - GUID converter_class; - } - loader_tests[] = - { - { - /* PCM -> PCM, same enumerated type, no current type */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = S_OK, - }, - { - /* PCM -> PCM, same enumerated type, incomplete current type */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_44100_incomplete, - .expected_result = MF_E_INVALIDMEDIATYPE, - .flags = LOADER_TODO, - }, - { - /* PCM -> PCM, same enumerated bps, different current bps */ - .input_type = &audio_pcm_48000, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_44100, - .expected_result = MF_E_INVALIDMEDIATYPE, - }, - { - /* PCM -> PCM, same enumerated bps, different current bps, force enumerate */ - .input_type = &audio_pcm_48000, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_44100, - .expected_result = S_OK, - .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, - }, + CLEAR_CALLED(test_source_BeginGetEvent); + CLEAR_CALLED(test_source_QueueEvent); + CLEAR_CALLED(test_source_Start);
- { - /* PCM -> PCM, incomplete enumerated type, same current type */ - .input_type = &audio_pcm_44100_incomplete, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_44100, - .expected_result = S_OK, - }, - { - /* PCM -> PCM, incomplete enumerated type, same current type, force enumerate */ - .input_type = &audio_pcm_44100_incomplete, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_44100, - .expected_result = MF_E_NO_MORE_TYPES, - .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES | LOADER_TODO, - }, + /* sometimes briefly leaking */ + IMFMediaSession_Release(session);
- { - /* PCM -> PCM, different enumerated bps, no current type */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = MF_E_INVALIDMEDIATYPE, - }, - { - /* PCM -> PCM, different enumerated bps, same current bps */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_48000, - .expected_result = S_OK, - }, - { - /* PCM -> PCM, different enumerated bps, same current bps, force enumerate */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_pcm_48000, - .expected_result = MF_E_NO_MORE_TYPES, - .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, - }, - { - /* PCM -> PCM, different enumerated bps, no current type, sink allow converter */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = MF_CONNECT_DIRECT, - .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, - }, - { - /* PCM -> PCM, different enumerated bps, same current type, sink allow converter, force enumerate */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = -1, - .current_input = &audio_pcm_48000, - .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, - }, - { - /* PCM -> PCM, different enumerated bps, no current type, sink allow decoder */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = MF_CONNECT_DIRECT, - .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, - }, - { - /* PCM -> PCM, different enumerated bps, no current type, default methods */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, - }, - { - /* PCM -> PCM, different enumerated bps, no current type, source allow converter */ - .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = MF_CONNECT_ALLOW_CONVERTER, - .expected_result = MF_E_INVALIDMEDIATYPE, - }, + IMFAsyncCallback_Release(callback);
- { - /* Float -> PCM, refuse input type, add converter */ - .input_type = &audio_float_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = MF_E_NO_MORE_TYPES, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, - }, - { - /* Float -> PCM, refuse input type, add converter, allow resampler output type */ - .input_type = &audio_float_44100, .output_type = &audio_pcm_48000_resampler, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, - }, + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL;
- { - /* MP3 -> PCM */ - .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_mp3_44100, - .expected_result = MF_E_INVALIDMEDIATYPE, - }, - { - /* MP3 -> PCM, force enumerate */ - .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, - .current_input = &audio_mp3_44100, - .expected_result = MF_E_NO_MORE_TYPES, - .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, - }, - { - /* MP3 -> PCM */ - .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = -1, - .current_input = &audio_mp3_44100, - .expected_result = MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION, - .flags = LOADER_TODO, - }, - { - /* MP3 -> PCM */ - .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, - .current_input = &audio_mp3_44100, - .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, - .flags = LOADER_TODO, - }, - { - /* MP3 -> PCM, need both decoder and converter */ - .input_type = &audio_mp3_44100, .output_type = &audio_float_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_TODO, - }, + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(src_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(sink_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref);
- { - /* I420 -> RGB32, Color Convert media type */ - .input_type = &video_i420_1280, .output_type = &video_color_convert_1280_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NEEDS_VIDEO_PROCESSOR, - }, - { - /* I420 -> RGB32, Video Processor media type */ - .input_type = &video_i420_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, - }, - { - /* I420 -> RGB32, Video Processor media type without frame size */ - .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, - }, - { - /* H264 -> RGB32, Video Processor media type */ - .input_type = &video_h264_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, - .decoded_type = &video_nv12_1280, - .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, - }, - { - /* RGB32 -> Any Video, no current output type */ - .input_type = &video_i420_1280, .output_type = &video_dummy, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, - .flags = LOADER_NO_CURRENT_OUTPUT, - }, - { - /* RGB32 -> Any Video, no current output type, refuse input type */ - .input_type = &video_i420_1280, .output_type = &video_dummy, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT, - }, - { - /* RGB32 -> Any Video, no current output type, refuse input type */ - .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, - .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, - .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT | LOADER_SET_MEDIA_TYPES, - }, - }; + ref = IMFMediaSource_Release(source); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFPresentationDescriptor_Release(pd); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFStreamDescriptor_Release(sd); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFMediaType_Release(input_type); + ok(ref == 0, "Release returned %ld\n", ref);
- IMFTopologyNode *src_node, *sink_node, *src_node2, *sink_node2, *mft_node; - IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); - struct test_stream_sink stream_sink = test_stream_sink; - IMFMediaType *media_type, *input_type, *output_type; - IMFTopology *topology, *topology2, *full_topology; - struct test_handler handler = test_handler; - IMFPresentationDescriptor *pd; - unsigned int i, count, value; - IMFActivate *sink_activate; - MF_TOPOLOGY_TYPE node_type; - IMFStreamDescriptor *sd; - IMFTransform *transform; - IMFMediaSource *source; - IMFTopoLoader *loader; - IUnknown *node_object; - WORD node_count; - TOPOID node_id, oldtopoid, newtopoid; - DWORD index; - HRESULT hr; - BOOL ret; - LONG ref;
- stream_sink.handler = &handler.IMFMediaTypeHandler_iface; + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + +static void test_media_session(void) +{ + IMFRateSupport *rate_support; + IMFAttributes *attributes; + IMFMediaSession *session; + MFSHUTDOWN_STATUS status; + IMFTopology *topology; + IMFShutdown *shutdown; + PROPVARIANT propvar; + IMFGetService *gs; + IMFClock *clock; + HRESULT hr; + DWORD caps;
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- hr = MFCreateTopoLoader(NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr);
- hr = MFCreateTopoLoader(&loader); - ok(hr == S_OK, "Failed to create topology loader, hr %#lx.\n", hr); + check_interface(session, &IID_IMFGetService, TRUE); + check_interface(session, &IID_IMFRateSupport, TRUE); + check_interface(session, &IID_IMFRateControl, TRUE); + check_interface(session, &IID_IMFAttributes, FALSE); + check_interface(session, &IID_IMFTopologyNodeAttributeEditor, FALSE); + check_interface(session, &IID_IMFLocalMFTRegistration, FALSE); + check_service_interface(session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, TRUE); + check_service_interface(session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, TRUE); + check_service_interface(session, &MF_TOPONODE_ATTRIBUTE_EDITOR_SERVICE, &IID_IMFTopologyNodeAttributeEditor, TRUE); + check_service_interface(session, &MF_LOCAL_MFT_REGISTRATION_SERVICE, &IID_IMFLocalMFTRegistration, TRUE);
- hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* Empty topology */ - hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); - todo_wine_if(hr == S_OK) - ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) IMFTopology_Release(full_topology); + hr = IMFClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* Add source node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + hr = IMFShutdown_GetShutdownStatus(shutdown, &status); + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);
- /* When a decoder is involved, windows requires this attribute to be present */ - create_descriptors(1, &media_type, NULL, &pd, &sd); - IMFMediaType_Release(media_type); + check_interface(session, &IID_IMFGetService, TRUE);
- source = create_test_source(pd); + hr = IMFMediaSession_QueryInterface(session, &IID_IMFGetService, (void **)&gs); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, (IUnknown *)source); - ok(hr == S_OK, "Failed to set node source, hr %#lx.\n", hr); + hr = IMFGetService_GetService(gs, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd); - ok(hr == S_OK, "Failed to set node sd, hr %#lx.\n", hr); + IMFGetService_Release(gs);
- hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd); - ok(hr == S_OK, "Failed to set node pd, hr %#lx.\n", hr); + hr = IMFShutdown_GetShutdownStatus(shutdown, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(status == MFSHUTDOWN_COMPLETED, "Unexpected shutdown status %u.\n", status);
- hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + IMFShutdown_Release(shutdown);
- /* Source node only. */ - hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); - ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); - ok(hr == S_OK, "Failed to create output node, hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); - hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); - ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaSession_Pause(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateSampleGrabberSinkActivate(media_type, grabber_callback, &sink_activate); - ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + hr = IMFMediaSession_Stop(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- IMFMediaType_Release(media_type); + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == MF_E_SHUTDOWN || broken(hr == E_UNEXPECTED) /* Win7 */, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Failed to add sink node, hr %#lx.\n", hr); + hr = IMFMediaSession_GetSessionCapabilities(session, &caps); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); - todo_wine_if(hr == MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED) - ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_GetSessionCapabilities(session, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFMediaSession_GetFullTopology(session, MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- /* Sink was not resolved. */ - hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); - ok(hr == MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- hr = IMFTopologyNode_SetObject(sink_node, NULL); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + IMFMediaSession_Release(session);
- hr = IMFActivate_ShutdownObject(sink_activate); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFActivate_Release(sink_activate); - ok(ref == 0, "Release returned %ld\n", ref); + /* Custom topology loader, GUID is not registered. */ + hr = MFCreateAttributes(&attributes, 1); + ok(hr == S_OK, "Failed to create attributes, hr %#lx.\n", hr);
- hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFMediaSource_Release(source); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFPresentationDescriptor_Release(pd); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFStreamDescriptor_Release(sd); - ok(ref == 0, "Release returned %ld\n", ref); + hr = IMFAttributes_SetGUID(attributes, &MF_SESSION_TOPOLOADER, &MF_SESSION_TOPOLOADER); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr);
+ hr = MFCreateMediaSession(attributes, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); + IMFMediaSession_Release(session);
- hr = MFCreateMediaType(&input_type); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + /* Disabled quality manager. */ + hr = IMFAttributes_SetGUID(attributes, &MF_SESSION_QUALITY_MANAGER, &GUID_NULL); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr);
- hr = MFCreateMediaType(&output_type); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + hr = MFCreateMediaSession(attributes, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); + IMFMediaSession_Release(session);
- for (i = 0; i < ARRAY_SIZE(loader_tests); ++i) - { - const struct loader_test *test = &loader_tests[i]; + IMFAttributes_Release(attributes); +}
- winetest_push_context("%u", i); +static void test_media_session_rate_control(void) +{ + IMFRateControl *rate_control, *clock_rate_control; + IMFPresentationClock *presentation_clock; + IMFPresentationTimeSource *time_source; + MFCLOCK_PROPERTIES clock_props; + IMFRateSupport *rate_support; + IMFMediaSession *session; + IMFClock *clock; + HRESULT hr; + float rate; + BOOL thin;
- init_media_type(input_type, *test->input_type, -1); - init_media_type(output_type, *test->output_type, -1); + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- handler.set_current_count = 0; - if (test->flags & LOADER_NO_CURRENT_OUTPUT) - handler.current_type = NULL; - else - IMFMediaType_AddRef((handler.current_type = output_type)); + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr);
- if (test->flags & LOADER_SET_INVALID_INPUT) - handler.invalid_type = input_type; - else - handler.invalid_type = NULL; + hr = MFGetService((IUnknown *)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support); + ok(hr == S_OK, "Failed to get rate support interface, hr %#lx.\n", hr);
- handler.enum_count = 0; - if (test->flags & LOADER_SET_MEDIA_TYPES) - { - handler.media_types_count = 1; - handler.media_types = &output_type; - } - else - { - handler.media_types_count = 0; - handler.media_types = NULL; - } + hr = MFGetService((IUnknown *)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void **)&rate_control); + ok(hr == S_OK, "Failed to get rate control interface, hr %#lx.\n", hr);
- if (test->flags & LOADER_ADD_RESAMPLER_MFT) - { - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFRateControl_GetRate(rate_control, NULL, NULL); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr);
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mft_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + rate = 0.0f; + hr = IMFRateControl_GetRate(rate_control, NULL, &rate); + ok(hr == S_OK, "Failed to get playback rate, hr %#lx.\n", hr); + ok(rate == 1.0f, "Unexpected rate %f.\n", rate);
- hr = CoCreateInstance(&CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &CLSID_CResamplerMediaObject); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetObject(mft_node, (IUnknown *)transform); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFTransform_Release(transform); + hr = IMFRateControl_GetRate(rate_control, &thin, NULL); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr);
- hr = IMFTopology_AddNode(topology, mft_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); - ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); - IMFTopologyNode_Release(mft_node); - } - else - { - hr = IMFTopology_Clear(topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } + thin = TRUE; + rate = 0.0f; + hr = IMFRateControl_GetRate(rate_control, &thin, &rate); + ok(hr == S_OK, "Failed to get playback rate, hr %#lx.\n", hr); + ok(!thin, "Unexpected thinning.\n"); + ok(rate == 1.0f, "Unexpected rate %f.\n", rate);
- create_descriptors(1, &input_type, test->current_input, &pd, &sd); + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Failed to get clock, hr %#lx.\n", hr);
- source = create_test_source(pd); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Failed to get rate control, hr %#lx.\n", hr);
- init_source_node(source, test->source_method, src_node, pd, sd); - init_sink_node(&stream_sink.IMFStreamSink_iface, test->sink_method, sink_node); + hr = IMFClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&clock_rate_control); + ok(hr == S_OK, "Failed to get rate control, hr %#lx.\n", hr);
- hr = IMFTopology_GetCount(topology, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - ok(!count, "Unexpected count %u.\n", count); + rate = 0.0f; + hr = IMFRateControl_GetRate(clock_rate_control, NULL, &rate); + ok(hr == S_OK, "Failed to get clock rate, hr %#lx.\n", hr); + ok(rate == 1.0f, "Unexpected rate %f.\n", rate);
- if (test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES) - IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 1); - hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); - IMFTopology_DeleteItem(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES); + hr = IMFRateControl_SetRate(clock_rate_control, FALSE, 1.5f); + ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#lx.\n", hr);
- if (test->flags & LOADER_NEEDS_VIDEO_PROCESSOR && !has_video_processor) - ok(hr == MF_E_INVALIDMEDIATYPE || hr == MF_E_TOPO_CODEC_NOT_FOUND, - "Unexpected hr %#lx\n", hr); - else - { - todo_wine_if(test->flags & LOADER_TODO) - ok(hr == test->expected_result, "Unexpected hr %#lx\n", hr); - ok(full_topology != topology, "Unexpected instance.\n"); - } + hr = IMFRateControl_SetRate(rate_control, FALSE, 1.5f); + ok(hr == S_OK, "Failed to set rate, hr %#lx.\n", hr);
- if (test->expected_result != hr) - { - if (hr != S_OK) ref = 0; - else ref = IMFTopology_Release(full_topology); - ok(ref == 0, "Release returned %ld\n", ref); - } - else if (test->expected_result == S_OK) - { - IMFTopology_GetTopologyID(topology, &oldtopoid); - IMFTopology_GetTopologyID(full_topology, &newtopoid); - ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); - ok(topology != full_topology, "Expected a different object for the resolved topology.\n"); - - hr = IMFTopology_GetCount(full_topology, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - todo_wine - ok(count == (test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES ? 2 : 1), - "Unexpected count %u.\n", count); - - value = 0xdeadbeef; - hr = IMFTopology_GetUINT32(full_topology, &MF_TOPOLOGY_RESOLUTION_STATUS, &value); -todo_wine { - ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - ok(value == MF_TOPOLOGY_RESOLUTION_SUCCEEDED, "Unexpected value %#x.\n", value); -} - count = 2; - if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) - count++; - if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) - count++; - - hr = IMFTopology_GetNodeCount(full_topology, &node_count); - ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_CMP3DecMediaObject)) - ok(node_count == count, "Unexpected node count %u.\n", node_count); - - hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); - ok(hr == S_OK, "Failed to get source node id, hr %#lx.\n", hr); - - hr = IMFTopology_GetNodeByID(full_topology, node_id, &src_node2); - ok(hr == S_OK, "Failed to get source in resolved topology, hr %#lx.\n", hr); - - hr = IMFTopologyNode_GetTopoNodeID(sink_node, &node_id); - ok(hr == S_OK, "Failed to get sink node id, hr %#lx.\n", hr); - - hr = IMFTopology_GetNodeByID(full_topology, node_id, &sink_node2); - ok(hr == S_OK, "Failed to get sink in resolved topology, hr %#lx.\n", hr); - - if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) - { - GUID class_id; - - hr = IMFTopologyNode_GetOutput(src_node2, 0, &mft_node, &index); - ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); - ok(!index, "Unexpected stream index %lu.\n", index); - - hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); - ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); - ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); - - value = 0; - hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_DECODER, &value); - ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - ok(value == 1, "Unexpected value.\n"); - - class_id = GUID_NULL; - hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); - ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - ok(IsEqualGUID(&class_id, &test->decoder_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); - - hr = IMFTopologyNode_GetObject(mft_node, &node_object); - ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); - IMFTopologyNode_Release(mft_node); - - hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); - ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); - IUnknown_Release(node_object); - - hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); - ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); - ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Input type of first transform doesn't match source node type.\n"); - IMFMediaType_Release(media_type); - - hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); - ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - if (IsEqualGUID(&test->converter_class, &GUID_NULL)) - { - hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); - ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Output type of first transform doesn't match sink node type.\n"); - } - else if (test->decoded_type) - { - check_media_type(media_type, *test->decoded_type, -1); - } - IMFMediaType_Release(media_type); - - IMFTransform_Release(transform); - } - - if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) - { - GUID class_id; - - hr = IMFTopologyNode_GetInput(sink_node2, 0, &mft_node, &index); - ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); - ok(!index, "Unexpected stream index %lu.\n", index); - - hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); - ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); - ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); - - class_id = GUID_NULL; - hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); - ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - todo_wine_if(IsEqualGUID(&test->converter_class, &CLSID_CColorConvertDMO)) - ok(IsEqualGUID(&class_id, &test->converter_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); - - hr = IMFTopologyNode_GetObject(mft_node, &node_object); - ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); - IMFTopologyNode_Release(mft_node); - - hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); - ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); - IUnknown_Release(node_object); - - hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); - ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - if (IsEqualGUID(&test->decoder_class, &GUID_NULL)) - { - hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); - ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Input type of last transform doesn't match source node type.\n"); - } - else if (test->decoded_type) - { - check_media_type(media_type, *test->decoded_type, -1); - } - IMFMediaType_Release(media_type); - - hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); - ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); - hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); - ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); - ok(ret, "Output type of last transform doesn't match sink node type.\n"); - IMFMediaType_Release(media_type); - - IMFTransform_Release(transform); - } - - IMFTopologyNode_Release(src_node2); - IMFTopologyNode_Release(sink_node2); - - hr = IMFTopology_SetUINT32(full_topology, &IID_IMFTopology, 123); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); - ok(hr == S_OK, "Failed to resolve topology, hr %#lx.\n", hr); - ok(full_topology != topology2, "Unexpected instance.\n"); - IMFTopology_GetTopologyID(topology2, &oldtopoid); - IMFTopology_GetTopologyID(full_topology, &newtopoid); - ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); - hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_GetProperties(clock, &clock_props); + ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#lx.\n", hr);
- ref = IMFTopology_Release(topology2); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(full_topology); - ok(ref == 0, "Release returned %ld\n", ref); - } + hr = MFCreateSystemTimeSource(&time_source); + ok(hr == S_OK, "Failed to create time source, hr %#lx.\n", hr);
- hr = IMFTopology_GetCount(topology, &count); - ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); - ok(!count, "Unexpected count %u.\n", count); + hr = IMFPresentationClock_SetTimeSource(presentation_clock, time_source); + ok(hr == S_OK, "Failed to set time source, hr %#lx.\n", hr);
- if (test->flags & LOADER_SET_MEDIA_TYPES) - ok(handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - else - ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); - ok(!handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + hr = IMFRateControl_SetRate(rate_control, FALSE, 1.5f); + ok(hr == S_OK, "Failed to set rate, hr %#lx.\n", hr);
- if (handler.current_type) - IMFMediaType_Release(handler.current_type); - handler.current_type = NULL; + rate = 0.0f; + hr = IMFRateControl_GetRate(clock_rate_control, NULL, &rate); + ok(hr == S_OK, "Failed to get clock rate, hr %#lx.\n", hr); + ok(rate == 1.0f, "Unexpected rate %f.\n", rate);
- hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFMediaSource_Release(source); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFPresentationDescriptor_Release(pd); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFStreamDescriptor_Release(sd); - ok(ref == 0, "Release returned %ld\n", ref); + IMFPresentationTimeSource_Release(time_source);
- winetest_pop_context(); - } + IMFRateControl_Release(clock_rate_control); + IMFPresentationClock_Release(presentation_clock); + IMFClock_Release(clock);
- ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopoLoader_Release(loader); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(src_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(sink_node); - ok(ref == 0, "Release returned %ld\n", ref); + IMFRateControl_Release(rate_control); + IMFRateSupport_Release(rate_support);
- ref = IMFMediaType_Release(input_type); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFMediaType_Release(output_type); - ok(ref == 0, "Release returned %ld\n", ref); + IMFMediaSession_Release(session);
hr = MFShutdown(); ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); - - IMFSampleGrabberSinkCallback_Release(grabber_callback); }
-static void test_topology_loader_evr(void) +struct test_grabber_callback { - static const media_type_desc media_type_desc = - { - ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), - ATTR_RATIO(MF_MT_FRAME_SIZE, 640, 480), - ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), - {0}, - }; - IMFTopologyNode *node, *source_node, *evr_node; - IMFTopology *topology, *full_topology; - IMFPresentationDescriptor *pd; - IMFMediaTypeHandler *handler; - unsigned int i, count, value; - IMFStreamSink *stream_sink; - IMFMediaType *media_type; - IMFStreamDescriptor *sd; - IMFActivate *activate; - IMFTopoLoader *loader; - IMFMediaSink *sink; - WORD node_count; - UINT64 value64; - HWND window; - HRESULT hr; - LONG ref; - - hr = CoInitialize(NULL); - ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); + IMFSampleGrabberSinkCallback IMFSampleGrabberSinkCallback_iface; + LONG refcount;
- /* EVR sink node. */ - window = create_window(); + IMFCollection *samples; + HANDLE ready_event; + HANDLE done_event; +};
- hr = MFCreateVideoRendererActivate(window, &activate); - ok(hr == S_OK, "Failed to create activate object, hr %#lx.\n", hr); +static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) +{ + return CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); +}
- hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); - if (FAILED(hr)) +static HRESULT WINAPI test_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) || + IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) { - skip("Failed to create an EVR sink, skipping tests.\n"); - DestroyWindow(window); - IMFActivate_Release(activate); - CoUninitialize(); - return; + *obj = iface; + IMFSampleGrabberSinkCallback_AddRef(iface); + return S_OK; } - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateTopoLoader(&loader); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + *obj = NULL; + return E_NOINTERFACE; +}
- /* Source node. */ - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &source_node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static ULONG WINAPI test_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface) +{ + struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); + return InterlockedIncrement(&grabber->refcount); +}
- hr = MFCreateMediaType(&media_type); - ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); - init_media_type(media_type, media_type_desc, -1); +static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface) +{ + struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); + ULONG refcount = InterlockedDecrement(&grabber->refcount);
- create_descriptors(1, &media_type, &media_type_desc, &pd, &sd); - init_source_node(NULL, -1, source_node, pd, sd); - IMFPresentationDescriptor_Release(pd); - IMFStreamDescriptor_Release(sd); + if (!refcount) + { + IMFCollection_Release(grabber->samples); + if (grabber->ready_event) + CloseHandle(grabber->ready_event); + if (grabber->done_event) + CloseHandle(grabber->done_event); + free(grabber); + }
+ return refcount; +}
- hr = IMFMediaSink_GetStreamSinkById(sink, 0, &stream_sink); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) +{ + return S_OK; +}
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &evr_node); - ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); +static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +}
- hr = IMFTopologyNode_SetObject(evr_node, (IUnknown *)stream_sink); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +}
- hr = IMFStreamSink_GetMediaTypeHandler(stream_sink, &handler); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, media_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFMediaTypeHandler_Release(handler); +static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +}
- IMFStreamSink_Release(stream_sink); +static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) +{ + return S_OK; +}
- hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, + IMFPresentationClock *clock) +{ + return S_OK; +}
- hr = IMFTopology_AddNode(topology, source_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, evr_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(source_node, 0, evr_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface, REFGUID major_type, + DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration, const BYTE *buffer, DWORD sample_size) +{ + struct test_grabber_callback *grabber = CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); + IMFSample *sample; + HRESULT hr; + DWORD res;
- hr = IMFTopologyNode_SetUINT32(evr_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_DIRECT); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!grabber->ready_event) + return E_NOTIMPL;
- hr = IMFTopologyNode_GetCount(evr_node, &count); + sample = create_sample(buffer, sample_size); + hr = IMFSample_SetSampleFlags(sample, sample_flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(count == 1, "Unexpected attribute count %u.\n", count); - - hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + /* FIXME: sample time is inconsistent across windows versions, ignore it */ + hr = IMFSample_SetSampleTime(sample, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFTopology_GetNodeCount(full_topology, &node_count); + hr = IMFSample_SetSampleDuration(sample, sample_duration); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(node_count == 3, "Unexpected node count %u.\n", node_count); - - for (i = 0; i < node_count; ++i) - { - MF_TOPOLOGY_TYPE node_type; - - hr = IMFTopology_GetNode(full_topology, i, &node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFTopologyNode_GetNodeType(node, &node_type); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - switch (node_type) - { - case MF_TOPOLOGY_OUTPUT_NODE: - { - value = 1; - hr = IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_STREAMID, &value); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!value, "Unexpected stream id %u.\n", value); - break; - } - case MF_TOPOLOGY_TRANSFORM_NODE: - { - IMFAttributes *attrs; - IMFTransform *copier; - IUnknown *obj; - - hr = IMFTopologyNode_GetObject(node, (IUnknown **)&obj); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IUnknown_QueryInterface(obj, &IID_IMFTransform, (void **)&copier); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFTransform_GetAttributes(copier, &attrs); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFCollection_AddElement(grabber->samples, (IUnknown *)sample); + IMFSample_Release(sample);
- value = 0xdeadbeef; - hr = IMFAttributes_GetUINT32(attrs, &MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &value); - ok(value == 1, "Unexpected dynamic state support state %u.\n", value); + SetEvent(grabber->ready_event); + res = WaitForSingleObject(grabber->done_event, 1000); + ok(!res, "WaitForSingleObject returned %#lx\n", res);
- IMFAttributes_Release(attrs); - IMFTransform_Release(copier); - IUnknown_Release(obj); - break; - } - case MF_TOPOLOGY_SOURCESTREAM_NODE: - { - value64 = 1; - hr = IMFTopologyNode_GetUINT64(node, &MF_TOPONODE_MEDIASTART, &value64); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!value64, "Unexpected value.\n"); - break; - } - default: - ok(0, "Got unexpected node type %u.\n", node_type); - break; - } + return S_OK; +}
- IMFTopologyNode_Release(node); - } +static HRESULT WINAPI test_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface) +{ + return S_OK; +}
- ref = IMFTopology_Release(full_topology); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopoLoader_Release(loader); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(source_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(evr_node); - ok(ref == 0, "Release returned %ld\n", ref); +static const IMFSampleGrabberSinkCallbackVtbl test_grabber_callback_vtbl = +{ + test_grabber_callback_QueryInterface, + test_grabber_callback_AddRef, + test_grabber_callback_Release, + test_grabber_callback_OnClockStart, + test_grabber_callback_OnClockStop, + test_grabber_callback_OnClockPause, + test_grabber_callback_OnClockRestart, + test_grabber_callback_OnClockSetRate, + test_grabber_callback_OnSetPresentationClock, + test_grabber_callback_OnProcessSample, + test_grabber_callback_OnShutdown, +};
- hr = IMFActivate_ShutdownObject(activate); - ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); - ref = IMFActivate_Release(activate); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFMediaSink_Release(sink); - ok(ref == 0, "Release returned %ld\n", ref); +static IMFSampleGrabberSinkCallback *create_test_grabber_callback(void) +{ + struct test_grabber_callback *grabber; + HRESULT hr;
- ref = IMFMediaType_Release(media_type); - ok(ref == 0, "Release returned %ld\n", ref); + if (!(grabber = calloc(1, sizeof(*grabber)))) + return NULL;
- DestroyWindow(window); + grabber->IMFSampleGrabberSinkCallback_iface.lpVtbl = &test_grabber_callback_vtbl; + grabber->refcount = 1; + hr = MFCreateCollection(&grabber->samples); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- CoUninitialize(); + return &grabber->IMFSampleGrabberSinkCallback_iface; }
static HRESULT WINAPI testshutdown_QueryInterface(IMFShutdown *iface, REFIID riid, void **obj) @@ -8250,10 +6431,6 @@ START_TEST(mf) return; }
- test_topology(); - test_topology_tee_node(); - test_topology_loader(); - test_topology_loader_evr(); test_MFGetService(); test_sequencer_source(); test_media_session(); diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c new file mode 100644 index 00000000000..ef4dc86d21e --- /dev/null +++ b/dlls/mf/tests/topology.c @@ -0,0 +1,2688 @@ +/* + * 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 <string.h> +#include <float.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" + +#include "uuids.h" +#include "wmcodecdsp.h" + +#include "mferror.h" + +#include "mf_test.h" + +#include "wine/test.h" + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT(func) \ + do { \ + CHECK_EXPECT2(func); \ + expect_ ## func = FALSE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CHECK_NOT_CALLED(func) \ + do { \ + ok(!called_ ## func, "unexpected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + +extern GUID DMOVideoFormat_RGB32; + +#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 %ld, expected %ld.\n", refcount, + expected_refcount); +} + +static HWND create_window(void) +{ + RECT r = {0, 0, 640, 480}; + + AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); + + return CreateWindowA("static", "mf_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, r.right - r.left, r.bottom - r.top, NULL, NULL, NULL, NULL); +} + +static IMFSample *create_sample(const BYTE *data, ULONG size) +{ + IMFMediaBuffer *media_buffer; + IMFSample *sample; + DWORD length; + BYTE *buffer; + HRESULT hr; + ULONG ret; + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "MFCreateSample returned %#lx\n", hr); + hr = MFCreateMemoryBuffer(size, &media_buffer); + ok(hr == S_OK, "MFCreateMemoryBuffer returned %#lx\n", hr); + hr = IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &length); + ok(hr == S_OK, "Lock returned %#lx\n", hr); + ok(length == 0, "got length %lu\n", length); + if (!data) memset(buffer, 0xcd, size); + else memcpy(buffer, data, size); + hr = IMFMediaBuffer_Unlock(media_buffer); + ok(hr == S_OK, "Unlock returned %#lx\n", hr); + hr = IMFMediaBuffer_SetCurrentLength(media_buffer, data ? size : 0); + ok(hr == S_OK, "SetCurrentLength returned %#lx\n", hr); + hr = IMFSample_AddBuffer(sample, media_buffer); + ok(hr == S_OK, "AddBuffer returned %#lx\n", hr); + ret = IMFMediaBuffer_Release(media_buffer); + ok(ret == 1, "Release returned %lu\n", ret); + + return sample; +} + +static void create_descriptors(UINT enum_types_count, IMFMediaType **enum_types, const media_type_desc *current_desc, + IMFPresentationDescriptor **pd, IMFStreamDescriptor **sd) +{ + HRESULT hr; + + hr = MFCreateStreamDescriptor(0, enum_types_count, enum_types, sd); + ok(hr == S_OK, "Failed to create stream descriptor, hr %#lx.\n", hr); + + hr = MFCreatePresentationDescriptor(1, sd, pd); + ok(hr == S_OK, "Failed to create presentation descriptor, hr %#lx.\n", hr); + + if (current_desc) + { + IMFMediaTypeHandler *handler; + IMFMediaType *type; + + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(type, *current_desc, -1); + + hr = IMFStreamDescriptor_GetMediaTypeHandler(*sd, &handler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaTypeHandler_Release(handler); + IMFMediaType_Release(type); + } +} + +static void init_source_node(IMFMediaSource *source, MF_CONNECT_METHOD method, IMFTopologyNode *node, + IMFPresentationDescriptor *pd, IMFStreamDescriptor *sd) +{ + HRESULT hr; + + hr = IMFTopologyNode_DeleteAllItems(node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd); + ok(hr == S_OK, "Failed to set node pd, hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd); + ok(hr == S_OK, "Failed to set node sd, hr %#lx.\n", hr); + + if (method != -1) + { + hr = IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, method); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + } + + if (source) + { + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_SOURCE, (IUnknown *)source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } +} + +static void init_sink_node(IMFStreamSink *stream_sink, MF_CONNECT_METHOD method, IMFTopologyNode *node) +{ + HRESULT hr; + + hr = IMFTopologyNode_DeleteAllItems(node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetObject(node, (IUnknown *)stream_sink); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + if (method != -1) + { + hr = IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, method); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + } +} + +DEFINE_EXPECT(test_source_BeginGetEvent); +DEFINE_EXPECT(test_source_QueueEvent); +DEFINE_EXPECT(test_source_Start); + +struct test_source +{ + IMFMediaSource IMFMediaSource_iface; + LONG refcount; + HRESULT begin_get_event_res; + IMFPresentationDescriptor *pd; +}; + +static struct test_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI test_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaSource) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaSource_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_source_AddRef(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); +} + +static ULONG WINAPI test_source_Release(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFPresentationDescriptor_Release(source->pd); + free(source); + } + + return refcount; +} + +static HRESULT WINAPI test_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + CHECK_EXPECT(test_source_BeginGetEvent); + return source->begin_get_event_res; +} + +static HRESULT WINAPI test_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + CHECK_EXPECT(test_source_QueueEvent); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + *flags = 0; + return S_OK; +} + +static HRESULT WINAPI test_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return IMFPresentationDescriptor_Clone(source->pd, pd); +} + +static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) +{ + CHECK_EXPECT(test_source_Start); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Stop(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Pause(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Shutdown(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMFMediaSourceVtbl test_source_vtbl = +{ + test_source_QueryInterface, + test_source_AddRef, + test_source_Release, + test_source_GetEvent, + test_source_BeginGetEvent, + test_source_EndGetEvent, + test_source_QueueEvent, + test_source_GetCharacteristics, + test_source_CreatePresentationDescriptor, + test_source_Start, + test_source_Stop, + test_source_Pause, + test_source_Shutdown, +}; + +static IMFMediaSource *create_test_source(IMFPresentationDescriptor *pd) +{ + struct test_source *source; + + source = calloc(1, sizeof(*source)); + source->IMFMediaSource_iface.lpVtbl = &test_source_vtbl; + source->refcount = 1; + source->begin_get_event_res = E_NOTIMPL; + IMFPresentationDescriptor_AddRef((source->pd = pd)); + + return &source->IMFMediaSource_iface; +} + +static HRESULT WINAPI test_unk_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_unk_AddRef(IUnknown *iface) +{ + return 2; +} + +static ULONG WINAPI test_unk_Release(IUnknown *iface) +{ + return 1; +} + +static const IUnknownVtbl test_unk_vtbl = +{ + test_unk_QueryInterface, + test_unk_AddRef, + test_unk_Release, +}; + +static void test_topology(void) +{ + IMFMediaType *mediatype, *mediatype2, *mediatype3; + IMFCollection *collection, *collection2; + IUnknown test_unk2 = { &test_unk_vtbl }; + IUnknown test_unk = { &test_unk_vtbl }; + IMFTopologyNode *node, *node2, *node3; + IMFTopology *topology, *topology2; + DWORD size, io_count, index; + MF_TOPOLOGY_TYPE node_type; + IUnknown *object; + WORD node_count; + UINT32 count; + HRESULT hr; + TOPOID id; + LONG ref; + + hr = MFCreateTopology(NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + hr = IMFTopology_GetTopologyID(topology, &id); + ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); + ok(id == 1, "Unexpected id.\n"); + + hr = MFCreateTopology(&topology2); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + hr = IMFTopology_GetTopologyID(topology2, &id); + ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); + ok(id == 2, "Unexpected id.\n"); + + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + hr = IMFTopology_GetTopologyID(topology, &id); + ok(hr == S_OK, "Failed to get id, hr %#lx.\n", hr); + ok(id == 3, "Unexpected id.\n"); + + ref = IMFTopology_Release(topology2); + ok(ref == 0, "Release returned %ld\n", ref); + + /* No attributes by default. */ + for (node_type = MF_TOPOLOGY_OUTPUT_NODE; node_type < MF_TOPOLOGY_TEE_NODE; ++node_type) + { + hr = MFCreateTopologyNode(node_type, &node); + ok(hr == S_OK, "Failed to create a node for type %d, hr %#lx.\n", node_type, hr); + hr = IMFTopologyNode_GetCount(node, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + ok(!count, "Unexpected attribute count %u.\n", count); + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + } + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetTopoNodeID(node, &id); + ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); + ok(((id >> 32) == GetCurrentProcessId()) && !!(id & 0xffff), "Unexpected node id %s.\n", wine_dbgstr_longlong(id)); + + hr = IMFTopologyNode_SetTopoNodeID(node2, id); + ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeCount(topology, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + node_count = 1; + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 0, "Unexpected node count %u.\n", node_count); + + /* Same id, different nodes. */ + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + node_count = 0; + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count %u.\n", node_count); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFTopology_GetNodeByID(topology, id, &node2); + ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); + ok(node2 == node, "Unexpected node.\n"); + IMFTopologyNode_Release(node2); + + /* Change node id, add it again. */ + hr = IMFTopologyNode_SetTopoNodeID(node, ++id); + ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeByID(topology, id, &node2); + ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); + ok(node2 == node, "Unexpected node.\n"); + IMFTopologyNode_Release(node2); + + hr = IMFTopology_GetNodeByID(topology, id + 1, &node2); + ok(hr == MF_E_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == E_INVALIDARG, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopology_GetNode(topology, 0, &node2); + ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); + ok(node2 == node, "Unexpected node.\n"); + IMFTopologyNode_Release(node2); + + hr = IMFTopology_GetNode(topology, 1, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_GetNode(topology, 1, &node2); + ok(hr == MF_E_INVALIDINDEX, "Failed to get a node, hr %#lx.\n", hr); + + hr = IMFTopology_GetNode(topology, -2, &node2); + ok(hr == MF_E_INVALIDINDEX, "Failed to get a node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(node2); + ok(ref == 1, "Release returned %ld\n", ref); + + node_count = 0; + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 2, "Unexpected node count %u.\n", node_count); + + /* Remove with detached node, existing id. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + hr = IMFTopologyNode_SetTopoNodeID(node2, id); + ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); + hr = IMFTopology_RemoveNode(topology, node2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFTopology_RemoveNode(topology, node); + ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); + + node_count = 0; + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count %u.\n", node_count); + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + node_count = 1; + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 0, "Unexpected node count %u.\n", node_count); + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetTopoNodeID(node, 123); + ok(hr == S_OK, "Failed to set node id, hr %#lx.\n", hr); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Change id for attached node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetTopoNodeID(node, &id); + ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetTopoNodeID(node2, id); + ok(hr == S_OK, "Failed to get node id, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeByID(topology, id, &node3); + ok(hr == S_OK, "Failed to get a node, hr %#lx.\n", hr); + ok(node3 == node, "Unexpected node.\n"); + IMFTopologyNode_Release(node3); + + /* Source/output collections. */ + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFTopology_GetSourceNodeCollection(topology, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection); + ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); + ok(!!collection, "Unexpected object pointer.\n"); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection2); + ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); + ok(!!collection2, "Unexpected object pointer.\n"); + ok(collection2 != collection, "Expected cloned collection.\n"); + + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(!size, "Unexpected item count.\n"); + + EXPECT_REF(collection, 1); + hr = IMFCollection_AddElement(collection, (IUnknown *)collection); + ok(hr == S_OK, "Failed to add element, hr %#lx.\n", hr); + EXPECT_REF(collection, 2); + + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(size == 1, "Unexpected item count.\n"); + + /* Empty collection to stop referencing itself */ + hr = IMFCollection_RemoveAllElements(collection); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + + hr = IMFCollection_GetElementCount(collection2, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(!size, "Unexpected item count.\n"); + + ref = IMFCollection_Release(collection2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFCollection_Release(collection); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Add some nodes. */ + hr = IMFTopology_GetSourceNodeCollection(topology, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_GetOutputNodeCollection(topology, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + IMFTopologyNode_Release(node); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection); + ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); + ok(!!collection, "Unexpected object pointer.\n"); + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(size == 1, "Unexpected item count.\n"); + ref = IMFCollection_Release(collection); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + IMFTopologyNode_Release(node); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection); + ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); + ok(!!collection, "Unexpected object pointer.\n"); + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(size == 1, "Unexpected item count.\n"); + ref = IMFCollection_Release(collection); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + IMFTopologyNode_Release(node); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection); + ok(hr == S_OK, "Failed to get source node collection, hr %#lx.\n", hr); + ok(!!collection, "Unexpected object pointer.\n"); + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(size == 1, "Unexpected item count.\n"); + ref = IMFCollection_Release(collection); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + /* Associated object. */ + hr = IMFTopologyNode_SetObject(node, NULL); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetObject(node, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + object = (void *)0xdeadbeef; + hr = IMFTopologyNode_GetObject(node, &object); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok(!object, "Unexpected object %p.\n", object); + + hr = IMFTopologyNode_SetObject(node, &test_unk); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetObject(node, &object); + ok(hr == S_OK, "Failed to get object, hr %#lx.\n", hr); + ok(object == &test_unk, "Unexpected object %p.\n", object); + IUnknown_Release(object); + + hr = IMFTopologyNode_SetObject(node, &test_unk2); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetCount(node, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + ok(count == 0, "Unexpected attribute count %u.\n", count); + + hr = IMFTopologyNode_SetGUID(node, &MF_TOPONODE_TRANSFORM_OBJECTID, &MF_TOPONODE_TRANSFORM_OBJECTID); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetObject(node, NULL); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + object = (void *)0xdeadbeef; + hr = IMFTopologyNode_GetObject(node, &object); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok(!object, "Unexpected object %p.\n", object); + + hr = IMFTopologyNode_GetCount(node, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + ok(count == 1, "Unexpected attribute count %u.\n", count); + + /* Preferred stream types. */ + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); + ok(io_count == 0, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMediaType(&mediatype); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); + ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); + ok(mediatype2 == mediatype, "Unexpected mediatype instance.\n"); + IMFMediaType_Release(mediatype2); + + hr = IMFTopologyNode_SetInputPrefType(node, 0, NULL); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok(!mediatype2, "Unexpected mediatype instance.\n"); + + hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); + ok(io_count == 2, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); + ok(io_count == 0, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_SetOutputPrefType(node, 0, mediatype); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + ref = IMFTopologyNode_Release(node); + ok(ref == 1, "Release returned %ld\n", ref); + + /* Source node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetOutputPrefType(node, 2, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); + ok(hr == E_FAIL, "Failed to get preferred type, hr %#lx.\n", hr); + ok(!mediatype2, "Unexpected mediatype instance.\n"); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 3, "Unexpected count %lu.\n", io_count); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Tee node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 0, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype2); + ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); + ok(mediatype2 == mediatype, "Unexpected mediatype instance.\n"); + IMFMediaType_Release(mediatype2); + + hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 0, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_SetInputPrefType(node, 1, mediatype); + ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 3, mediatype); + ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMediaType(&mediatype2); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + /* Changing output type does not change input type. */ + hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype2); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputPrefType(node, 0, &mediatype3); + ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); + ok(mediatype3 == mediatype, "Unexpected mediatype instance.\n"); + IMFMediaType_Release(mediatype3); + + IMFMediaType_Release(mediatype2); + + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 0, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 5, "Unexpected count %lu.\n", io_count); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Transform node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); + ok(hr == S_OK, "Failed to create a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(node, 3, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get input count, hr %#lx.\n", hr); + ok(io_count == 4, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_SetOutputPrefType(node, 4, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 4, "Unexpected count %lu.\n", io_count); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 5, "Unexpected count %lu.\n", io_count); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + + IMFMediaType_Release(mediatype); + + hr = IMFTopology_GetOutputNodeCollection(topology, &collection); + ok(hr == S_OK || broken(hr == E_FAIL) /* before Win8 */, "Failed to get output node collection, hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + ok(!!collection, "Unexpected object pointer.\n"); + hr = IMFCollection_GetElementCount(collection, &size); + ok(hr == S_OK, "Failed to get item count, hr %#lx.\n", hr); + ok(size == 1, "Unexpected item count.\n"); + ref = IMFCollection_Release(collection); + ok(ref == 0, "Release returned %ld\n", ref); + } + + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Connect nodes. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\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 %#lx.\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 %#lx.\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 %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\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 %#lx.\n", hr); + + EXPECT_REF(node, 3); + EXPECT_REF(node2, 3); + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + 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 %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopology_RemoveNode(topology, node); + ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); + + EXPECT_REF(node, 1); + EXPECT_REF(node2, 2); + + hr = IMFTopologyNode_GetOutput(node, 0, &node3, &index); + ok(hr == MF_E_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopology_RemoveNode(topology, node2); + ok(hr == S_OK, "Failed to remove a node, hr %#lx.\n", hr); + + EXPECT_REF(node, 2); + EXPECT_REF(node2, 1); + + IMFTopologyNode_Release(node); + IMFTopologyNode_Release(node2); + + /* Cloning nodes of different types. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_CloneFrom(node, node2); + ok(hr == MF_E_INVALIDREQUEST, "Unexpected hr %#lx.\n", hr); + + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Cloning preferred types. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&mediatype); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetOutputPrefType(node2, 0, mediatype); + ok(hr == S_OK, "Failed to set preferred type, hr %#lx.\n", hr); + + /* Vista checks for additional attributes. */ + hr = IMFTopologyNode_CloneFrom(node, node2); + ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); + ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); + ok(mediatype == mediatype2, "Unexpected media type.\n"); + + IMFMediaType_Release(mediatype2); + + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + IMFMediaType_Release(mediatype); + + /* Existing preferred types are not cleared. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 1, "Unexpected output count.\n"); + + hr = IMFTopologyNode_CloneFrom(node, node2); + ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputCount(node, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 1, "Unexpected output count.\n"); + + hr = IMFTopologyNode_GetOutputPrefType(node, 0, &mediatype2); + ok(hr == S_OK, "Failed to get preferred type, hr %#lx.\n", hr); + ok(!!mediatype2, "Unexpected media type.\n"); + IMFMediaType_Release(mediatype2); + + hr = IMFTopologyNode_CloneFrom(node2, node); + ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Vista */, "Failed to clone a node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputCount(node2, &io_count); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(io_count == 1, "Unexpected output count.\n"); + + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Add one node, connect to another that hasn't been added. */ + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count.\n"); + + hr = IMFTopologyNode_ConnectOutput(node, 0, node2, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count.\n"); + + /* Add same node to different topologies. */ + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Failed to clear topology, hr %#lx.\n", hr); + + hr = MFCreateTopology(&topology2); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + EXPECT_REF(node, 2); + + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count.\n"); + + hr = IMFTopology_GetNodeCount(topology2, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 0, "Unexpected node count.\n"); + + hr = IMFTopology_AddNode(topology2, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + EXPECT_REF(node, 3); + + hr = IMFTopology_GetNodeCount(topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count.\n"); + + hr = IMFTopology_GetNodeCount(topology2, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 1, "Unexpected node count.\n"); + + ref = IMFTopology_Release(topology2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + + /* Try cloning a topology without all outputs connected */ + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + hr = MFCreateTopology(&topology2); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + EXPECT_REF(node, 2); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node2); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, node2); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + EXPECT_REF(node, 2); + + hr = IMFTopologyNode_ConnectOutput(node, 1, node2, 0); + ok(hr == S_OK, "Failed to connect output, hr %#lx.\n", hr); + + hr = IMFTopology_CloneFrom(topology2, topology); + ok(hr == S_OK, "Failed to clone from topology, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeCount(topology2, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + ok(node_count == 2, "Unexpected node count %u.\n", node_count); + + hr = IMFTopology_GetNode(topology2, 0, &node3); + ok(hr == S_OK, "Failed to get node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputCount(node3, &size); + ok(hr == S_OK, "Failed to get output count, hr %#lx.\n", hr); + ok(size == 2, "Unexpected output count %lu.\n", size); + + IMFTopologyNode_Release(node3); + + ref = IMFTopology_Release(topology2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + ref = IMFTopologyNode_Release(node2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(node); + ok(ref == 0, "Release returned %ld\n", ref); +} + +static void test_topology_tee_node(void) +{ + IMFTopologyNode *src_node, *tee_node; + IMFMediaType *mediatype, *mediatype2; + IMFTopology *topology; + DWORD count; + HRESULT hr; + LONG ref; + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&mediatype); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &tee_node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetInputPrefType(tee_node, 0, mediatype); + ok(hr == S_OK, "Failed to set type, hr %#lx.\n", hr); + + /* Even though tee node has only one input and source has only one output, + it's possible to connect to higher inputs/outputs. */ + + /* SRC(0) -> TEE(0) */ + hr = IMFTopologyNode_ConnectOutput(src_node, 0, tee_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(tee_node, &count); + ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); + ok(count == 1, "Unexpected count %lu.\n", count); + + hr = IMFTopologyNode_GetInputPrefType(tee_node, 0, &mediatype2); + ok(hr == S_OK, "Failed to get type, hr %#lx.\n", hr); + ok(mediatype2 == mediatype, "Unexpected type.\n"); + IMFMediaType_Release(mediatype2); + + /* SRC(0) -> TEE(1) */ + hr = IMFTopologyNode_ConnectOutput(src_node, 0, tee_node, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetInputCount(tee_node, &count); + ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); + ok(count == 2, "Unexpected count %lu.\n", count); + + hr = IMFTopologyNode_SetInputPrefType(tee_node, 1, mediatype); + ok(hr == MF_E_INVALIDTYPE, "Unexpected hr %#lx.\n", hr); + + /* SRC(1) -> TEE(1) */ + hr = IMFTopologyNode_ConnectOutput(src_node, 1, tee_node, 1); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetOutputCount(src_node, &count); + ok(hr == S_OK, "Failed to get count, hr %#lx.\n", hr); + ok(count == 2, "Unexpected count %lu.\n", count); + + EXPECT_REF(src_node, 2); + EXPECT_REF(tee_node, 2); + hr = IMFTopologyNode_DisconnectOutput(src_node, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ref = IMFTopologyNode_Release(src_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(tee_node); + ok(ref == 0, "Release returned %ld\n", ref); + + ref = IMFMediaType_Release(mediatype); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); +} + +struct test_handler +{ + IMFMediaTypeHandler IMFMediaTypeHandler_iface; + + ULONG set_current_count; + IMFMediaType *current_type; + IMFMediaType *invalid_type; + + ULONG enum_count; + ULONG media_types_count; + IMFMediaType **media_types; +}; + +static struct test_handler *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct test_handler, IMFMediaTypeHandler_iface); +} + +static HRESULT WINAPI test_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFMediaTypeHandler) + || IsEqualIID(riid, &IID_IUnknown)) + { + IMFMediaTypeHandler_AddRef((*obj = iface)); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_handler_AddRef(IMFMediaTypeHandler *iface) +{ + return 2; +} + +static ULONG WINAPI test_handler_Release(IMFMediaTypeHandler *iface) +{ + return 1; +} + +static HRESULT WINAPI test_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, IMFMediaType *in_type, + IMFMediaType **out_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + BOOL result; + + if (out_type) + *out_type = NULL; + + if (impl->invalid_type && IMFMediaType_Compare(impl->invalid_type, (IMFAttributes *)in_type, + MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) + return MF_E_INVALIDMEDIATYPE; + + if (!impl->current_type) + return S_OK; + + if (IMFMediaType_Compare(impl->current_type, (IMFAttributes *)in_type, + MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result) == S_OK && result) + return S_OK; + + return MF_E_INVALIDMEDIATYPE; +} + +static HRESULT WINAPI test_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, + IMFMediaType **type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + + if (impl->media_types) + { + impl->enum_count++; + + if (index >= impl->media_types_count) + return MF_E_NO_MORE_TYPES; + + IMFMediaType_AddRef((*type = impl->media_types[index])); + return S_OK; + } + + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType *media_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + + if (impl->current_type) + IMFMediaType_Release(impl->current_type); + IMFMediaType_AddRef((impl->current_type = media_type)); + impl->set_current_count++; + + return S_OK; +} + +static HRESULT WINAPI test_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **media_type) +{ + struct test_handler *impl = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr; + + if (!impl->current_type) + { + if (!impl->media_types) + return E_FAIL; + if (!impl->media_types_count) + return MF_E_TRANSFORM_TYPE_NOT_SET; + return MF_E_NOT_INITIALIZED; + } + + if (FAILED(hr = MFCreateMediaType(media_type))) + return hr; + + hr = IMFMediaType_CopyAllItems(impl->current_type, (IMFAttributes *)*media_type); + if (FAILED(hr)) + IMFMediaType_Release(*media_type); + + return hr; +} + +static HRESULT WINAPI test_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMFMediaTypeHandlerVtbl test_handler_vtbl = +{ + test_handler_QueryInterface, + test_handler_AddRef, + test_handler_Release, + test_handler_IsMediaTypeSupported, + test_handler_GetMediaTypeCount, + test_handler_GetMediaTypeByIndex, + test_handler_SetCurrentMediaType, + test_handler_GetCurrentMediaType, + test_handler_GetMajorType, +}; + +static const struct test_handler test_handler = {.IMFMediaTypeHandler_iface.lpVtbl = &test_handler_vtbl}; + +struct test_stream_sink +{ + IMFStreamSink IMFStreamSink_iface; + IMFMediaTypeHandler *handler; + IMFMediaSink *media_sink; + + IMFAttributes *attributes; +}; + +static struct test_stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct test_stream_sink, IMFStreamSink_iface); +} + +static HRESULT WINAPI test_stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface); + + if (IsEqualIID(riid, &IID_IMFStreamSink) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + IMFStreamSink_AddRef((*obj = iface)); + return S_OK; + } + + if (IsEqualIID(riid, &IID_IMFAttributes) && impl->attributes) + { + IMFAttributes_AddRef((*obj = impl->attributes)); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_stream_sink_AddRef(IMFStreamSink *iface) +{ + return 2; +} + +static ULONG WINAPI test_stream_sink_Release(IMFStreamSink *iface) +{ + return 1; +} + +static HRESULT WINAPI test_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface); + + if (impl->media_sink) + { + IMFMediaSink_AddRef((*sink = impl->media_sink)); + return S_OK; + } + + todo_wine + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *id) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + struct test_stream_sink *impl = impl_from_IMFStreamSink(iface); + + if (impl->handler) + { + IMFMediaTypeHandler_AddRef((*handler = impl->handler)); + return S_OK; + } + + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_sink_Flush(IMFStreamSink *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMFStreamSinkVtbl test_stream_sink_vtbl = +{ + test_stream_sink_QueryInterface, + test_stream_sink_AddRef, + test_stream_sink_Release, + test_stream_sink_GetEvent, + test_stream_sink_BeginGetEvent, + test_stream_sink_EndGetEvent, + test_stream_sink_QueueEvent, + test_stream_sink_GetMediaSink, + test_stream_sink_GetIdentifier, + test_stream_sink_GetMediaTypeHandler, + test_stream_sink_ProcessSample, + test_stream_sink_PlaceMarker, + test_stream_sink_Flush, +}; + +static const struct test_stream_sink test_stream_sink = {.IMFStreamSink_iface.lpVtbl = &test_stream_sink_vtbl}; + +enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, +}; + +struct test_grabber_callback +{ + IMFSampleGrabberSinkCallback IMFSampleGrabberSinkCallback_iface; + LONG refcount; + + IMFCollection *samples; + HANDLE ready_event; + HANDLE done_event; +}; + +static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) +{ + return CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); +} + +static HRESULT WINAPI test_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) || + IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFSampleGrabberSinkCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface) +{ + struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); + return InterlockedIncrement(&grabber->refcount); +} + +static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface) +{ + struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); + ULONG refcount = InterlockedDecrement(&grabber->refcount); + + if (!refcount) + { + IMFCollection_Release(grabber->samples); + if (grabber->ready_event) + CloseHandle(grabber->ready_event); + if (grabber->done_event) + CloseHandle(grabber->done_event); + free(grabber); + } + + return refcount; +} + +static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, + IMFPresentationClock *clock) +{ + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface, REFGUID major_type, + DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration, const BYTE *buffer, DWORD sample_size) +{ + struct test_grabber_callback *grabber = CONTAINING_RECORD(iface, struct test_grabber_callback, IMFSampleGrabberSinkCallback_iface); + IMFSample *sample; + HRESULT hr; + DWORD res; + + if (!grabber->ready_event) + return E_NOTIMPL; + + sample = create_sample(buffer, sample_size); + hr = IMFSample_SetSampleFlags(sample, sample_flags); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* FIXME: sample time is inconsistent across windows versions, ignore it */ + hr = IMFSample_SetSampleTime(sample, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_SetSampleDuration(sample, sample_duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFCollection_AddElement(grabber->samples, (IUnknown *)sample); + IMFSample_Release(sample); + + SetEvent(grabber->ready_event); + res = WaitForSingleObject(grabber->done_event, 1000); + ok(!res, "WaitForSingleObject returned %#lx\n", res); + + return S_OK; +} + +static HRESULT WINAPI test_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface) +{ + return S_OK; +} + +static const IMFSampleGrabberSinkCallbackVtbl test_grabber_callback_vtbl = +{ + test_grabber_callback_QueryInterface, + test_grabber_callback_AddRef, + test_grabber_callback_Release, + test_grabber_callback_OnClockStart, + test_grabber_callback_OnClockStop, + test_grabber_callback_OnClockPause, + test_grabber_callback_OnClockRestart, + test_grabber_callback_OnClockSetRate, + test_grabber_callback_OnSetPresentationClock, + test_grabber_callback_OnProcessSample, + test_grabber_callback_OnShutdown, +}; + +static IMFSampleGrabberSinkCallback *create_test_grabber_callback(void) +{ + struct test_grabber_callback *grabber; + HRESULT hr; + + if (!(grabber = calloc(1, sizeof(*grabber)))) + return NULL; + + grabber->IMFSampleGrabberSinkCallback_iface.lpVtbl = &test_grabber_callback_vtbl; + grabber->refcount = 1; + hr = MFCreateCollection(&grabber->samples); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return &grabber->IMFSampleGrabberSinkCallback_iface; +} + +enum loader_test_flags +{ + LOADER_TODO = 0x4, + LOADER_NEEDS_VIDEO_PROCESSOR = 0x8, + LOADER_SET_ENUMERATE_SOURCE_TYPES = 0x10, + LOADER_NO_CURRENT_OUTPUT = 0x20, + LOADER_SET_INVALID_INPUT = 0x40, + LOADER_SET_MEDIA_TYPES = 0x80, + LOADER_ADD_RESAMPLER_MFT = 0x100, +}; + +static void test_topology_loader(void) +{ + static const media_type_desc audio_float_44100 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 4 * 44100), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), + }; + static const media_type_desc audio_pcm_44100 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), + }; + static const media_type_desc audio_pcm_48000 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 48000), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), + }; + static const media_type_desc audio_pcm_48000_resampler = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 192000), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1), + }; + static const media_type_desc audio_float_48000 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 8 * 48000), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 8), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), + }; + static const media_type_desc audio_mp3_44100 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_MP3), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 16000), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), + }; + static const media_type_desc audio_pcm_44100_incomplete = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 1), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8), + }; + static const media_type_desc audio_float_44100_stereo = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_NUM_CHANNELS, 2), + ATTR_UINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 2 * 4), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 4 * 44100), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), + ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + }; + static const media_type_desc video_i420_1280 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_I420), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + }; + static const media_type_desc video_color_convert_1280_rgb32 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, DMOVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + }; + static const media_type_desc video_video_processor_1280_rgb32 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + }; + static const media_type_desc video_video_processor_rgb32 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + }; + static const media_type_desc video_h264_1280 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + }; + static const media_type_desc video_nv12_1280 = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), + ATTR_RATIO(MF_MT_FRAME_SIZE, 1280, 720), + ATTR_RATIO(MF_MT_PIXEL_ASPECT_RATIO, 1, 1), + ATTR_UINT32(MF_MT_SAMPLE_SIZE, 1280 * 720 * 3 / 2), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 1280), + ATTR_UINT32(MF_MT_FIXED_SIZE_SAMPLES, 1), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 7), + }; + static const media_type_desc video_dummy = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + }; + + const struct loader_test + { + const media_type_desc *input_type; + const media_type_desc *output_type; + const media_type_desc *current_input; + const media_type_desc *decoded_type; + MF_CONNECT_METHOD source_method; + MF_CONNECT_METHOD sink_method; + HRESULT expected_result; + unsigned int flags; + GUID decoder_class; + GUID converter_class; + } + loader_tests[] = + { + { + /* PCM -> PCM, same enumerated type, no current type */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .expected_result = S_OK, + }, + { + /* PCM -> PCM, same enumerated type, incomplete current type */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_44100_incomplete, + .expected_result = MF_E_INVALIDMEDIATYPE, + .flags = LOADER_TODO, + }, + { + /* PCM -> PCM, same enumerated bps, different current bps */ + .input_type = &audio_pcm_48000, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_44100, + .expected_result = MF_E_INVALIDMEDIATYPE, + }, + { + /* PCM -> PCM, same enumerated bps, different current bps, force enumerate */ + .input_type = &audio_pcm_48000, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_44100, + .expected_result = S_OK, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, + }, + + { + /* PCM -> PCM, incomplete enumerated type, same current type */ + .input_type = &audio_pcm_44100_incomplete, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_44100, + .expected_result = S_OK, + }, + { + /* PCM -> PCM, incomplete enumerated type, same current type, force enumerate */ + .input_type = &audio_pcm_44100_incomplete, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_44100, + .expected_result = MF_E_NO_MORE_TYPES, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES | LOADER_TODO, + }, + + { + /* PCM -> PCM, different enumerated bps, no current type */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .expected_result = MF_E_INVALIDMEDIATYPE, + }, + { + /* PCM -> PCM, different enumerated bps, same current bps */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_48000, + .expected_result = S_OK, + }, + { + /* PCM -> PCM, different enumerated bps, same current bps, force enumerate */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_pcm_48000, + .expected_result = MF_E_NO_MORE_TYPES, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, + }, + { + /* PCM -> PCM, different enumerated bps, no current type, sink allow converter */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = MF_CONNECT_DIRECT, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + }, + { + /* PCM -> PCM, different enumerated bps, same current type, sink allow converter, force enumerate */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = -1, + .current_input = &audio_pcm_48000, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, + }, + { + /* PCM -> PCM, different enumerated bps, no current type, sink allow decoder */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = MF_CONNECT_DIRECT, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + }, + { + /* PCM -> PCM, different enumerated bps, no current type, default methods */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + }, + { + /* PCM -> PCM, different enumerated bps, no current type, source allow converter */ + .input_type = &audio_pcm_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = MF_CONNECT_ALLOW_CONVERTER, + .expected_result = MF_E_INVALIDMEDIATYPE, + }, + + { + /* Float -> PCM, refuse input type, add converter */ + .input_type = &audio_float_44100, .output_type = &audio_pcm_48000, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .expected_result = MF_E_NO_MORE_TYPES, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, + }, + { + /* Float -> PCM, refuse input type, add converter, allow resampler output type */ + .input_type = &audio_float_44100, .output_type = &audio_pcm_48000_resampler, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CResamplerMediaObject, + .flags = LOADER_SET_INVALID_INPUT | LOADER_ADD_RESAMPLER_MFT, + }, + + { + /* MP3 -> PCM */ + .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_mp3_44100, + .expected_result = MF_E_INVALIDMEDIATYPE, + }, + { + /* MP3 -> PCM, force enumerate */ + .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_DIRECT, .source_method = -1, + .current_input = &audio_mp3_44100, + .expected_result = MF_E_NO_MORE_TYPES, + .flags = LOADER_SET_ENUMERATE_SOURCE_TYPES, + }, + { + /* MP3 -> PCM */ + .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_CONVERTER, .source_method = -1, + .current_input = &audio_mp3_44100, + .expected_result = MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION, + .flags = LOADER_TODO, + }, + { + /* MP3 -> PCM */ + .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, + .current_input = &audio_mp3_44100, + .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, + .flags = LOADER_TODO, + }, + { + /* MP3 -> PCM, need both decoder and converter */ + .input_type = &audio_mp3_44100, .output_type = &audio_float_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_TODO, + }, + + { + /* I420 -> RGB32, Color Convert media type */ + .input_type = &video_i420_1280, .output_type = &video_color_convert_1280_rgb32, .sink_method = -1, .source_method = -1, + .expected_result = MF_E_TOPO_CODEC_NOT_FOUND, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NEEDS_VIDEO_PROCESSOR, + }, + { + /* I420 -> RGB32, Video Processor media type */ + .input_type = &video_i420_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + }, + { + /* I420 -> RGB32, Video Processor media type without frame size */ + .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + }, + { + /* H264 -> RGB32, Video Processor media type */ + .input_type = &video_h264_1280, .output_type = &video_video_processor_1280_rgb32, .sink_method = -1, .source_method = -1, + .decoded_type = &video_nv12_1280, + .expected_result = S_OK, .decoder_class = CLSID_CMSH264DecoderMFT, .converter_class = CLSID_CColorConvertDMO, + }, + { + /* RGB32 -> Any Video, no current output type */ + .input_type = &video_i420_1280, .output_type = &video_dummy, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, + .flags = LOADER_NO_CURRENT_OUTPUT, + }, + { + /* RGB32 -> Any Video, no current output type, refuse input type */ + .input_type = &video_i420_1280, .output_type = &video_dummy, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT, + }, + { + /* RGB32 -> Any Video, no current output type, refuse input type */ + .input_type = &video_i420_1280, .output_type = &video_video_processor_rgb32, .sink_method = -1, .source_method = -1, + .expected_result = S_OK, .converter_class = CLSID_CColorConvertDMO, + .flags = LOADER_NO_CURRENT_OUTPUT | LOADER_SET_INVALID_INPUT | LOADER_SET_MEDIA_TYPES, + }, + }; + + IMFTopologyNode *src_node, *sink_node, *src_node2, *sink_node2, *mft_node; + IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); + struct test_stream_sink stream_sink = test_stream_sink; + IMFMediaType *media_type, *input_type, *output_type; + IMFTopology *topology, *topology2, *full_topology; + struct test_handler handler = test_handler; + IMFPresentationDescriptor *pd; + unsigned int i, count, value; + IMFActivate *sink_activate; + MF_TOPOLOGY_TYPE node_type; + IMFStreamDescriptor *sd; + IMFTransform *transform; + IMFMediaSource *source; + IMFTopoLoader *loader; + IUnknown *node_object; + WORD node_count; + TOPOID node_id, oldtopoid, newtopoid; + DWORD index; + HRESULT hr; + BOOL ret; + LONG ref; + + stream_sink.handler = &handler.IMFMediaTypeHandler_iface; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); + + hr = MFCreateTopoLoader(NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopoLoader(&loader); + ok(hr == S_OK, "Failed to create topology loader, hr %#lx.\n", hr); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Failed to create topology, hr %#lx.\n", hr); + + /* Empty topology */ + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + todo_wine_if(hr == S_OK) + ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) IMFTopology_Release(full_topology); + + /* Add source node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + + /* When a decoder is involved, windows requires this attribute to be present */ + create_descriptors(1, &media_type, NULL, &pd, &sd); + IMFMediaType_Release(media_type); + + source = create_test_source(pd); + + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, (IUnknown *)source); + ok(hr == S_OK, "Failed to set node source, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd); + ok(hr == S_OK, "Failed to set node sd, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd); + ok(hr == S_OK, "Failed to set node pd, hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Failed to add a node, hr %#lx.\n", hr); + + /* Source node only. */ + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Failed to create output node, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + + hr = MFCreateSampleGrabberSinkActivate(media_type, grabber_callback, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + IMFMediaType_Release(media_type); + + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Failed to add sink node, hr %#lx.\n", hr); + + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + todo_wine_if(hr == MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED) + ok(hr == MF_E_TOPO_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + + /* Sink was not resolved. */ + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + ok(hr == MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetObject(sink_node, NULL); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + + hr = IMFActivate_ShutdownObject(sink_activate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFActivate_Release(sink_activate); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFMediaSource_Release(source); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFPresentationDescriptor_Release(pd); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFStreamDescriptor_Release(sd); + ok(ref == 0, "Release returned %ld\n", ref); + + + hr = MFCreateMediaType(&input_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(loader_tests); ++i) + { + const struct loader_test *test = &loader_tests[i]; + + winetest_push_context("%u", i); + + init_media_type(input_type, *test->input_type, -1); + init_media_type(output_type, *test->output_type, -1); + + handler.set_current_count = 0; + if (test->flags & LOADER_NO_CURRENT_OUTPUT) + handler.current_type = NULL; + else + IMFMediaType_AddRef((handler.current_type = output_type)); + + if (test->flags & LOADER_SET_INVALID_INPUT) + handler.invalid_type = input_type; + else + handler.invalid_type = NULL; + + handler.enum_count = 0; + if (test->flags & LOADER_SET_MEDIA_TYPES) + { + handler.media_types_count = 1; + handler.media_types = &output_type; + } + else + { + handler.media_types_count = 0; + handler.media_types = NULL; + } + + if (test->flags & LOADER_ADD_RESAMPLER_MFT) + { + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &CLSID_CResamplerMediaObject); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetObject(mft_node, (IUnknown *)transform); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTransform_Release(transform); + + hr = IMFTopology_AddNode(topology, mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); + ok(hr == S_OK, "Failed to connect nodes, hr %#lx.\n", hr); + IMFTopologyNode_Release(mft_node); + } + else + { + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + create_descriptors(1, &input_type, test->current_input, &pd, &sd); + + source = create_test_source(pd); + + init_source_node(source, test->source_method, src_node, pd, sd); + init_sink_node(&stream_sink.IMFStreamSink_iface, test->sink_method, sink_node); + + hr = IMFTopology_GetCount(topology, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + ok(!count, "Unexpected count %u.\n", count); + + if (test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES) + IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, 1); + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + IMFTopology_DeleteItem(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES); + + if (test->flags & LOADER_NEEDS_VIDEO_PROCESSOR && !has_video_processor) + ok(hr == MF_E_INVALIDMEDIATYPE || hr == MF_E_TOPO_CODEC_NOT_FOUND, + "Unexpected hr %#lx\n", hr); + else + { + todo_wine_if(test->flags & LOADER_TODO) + ok(hr == test->expected_result, "Unexpected hr %#lx\n", hr); + ok(full_topology != topology, "Unexpected instance.\n"); + } + + if (test->expected_result != hr) + { + if (hr != S_OK) ref = 0; + else ref = IMFTopology_Release(full_topology); + ok(ref == 0, "Release returned %ld\n", ref); + } + else if (test->expected_result == S_OK) + { + IMFTopology_GetTopologyID(topology, &oldtopoid); + IMFTopology_GetTopologyID(full_topology, &newtopoid); + ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); + ok(topology != full_topology, "Expected a different object for the resolved topology.\n"); + + hr = IMFTopology_GetCount(full_topology, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + todo_wine + ok(count == (test->flags & LOADER_SET_ENUMERATE_SOURCE_TYPES ? 2 : 1), + "Unexpected count %u.\n", count); + + value = 0xdeadbeef; + hr = IMFTopology_GetUINT32(full_topology, &MF_TOPOLOGY_RESOLUTION_STATUS, &value); +todo_wine { + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + ok(value == MF_TOPOLOGY_RESOLUTION_SUCCEEDED, "Unexpected value %#x.\n", value); +} + count = 2; + if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) + count++; + if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) + count++; + + hr = IMFTopology_GetNodeCount(full_topology, &node_count); + ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); + todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_CMP3DecMediaObject)) + ok(node_count == count, "Unexpected node count %u.\n", node_count); + + hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); + ok(hr == S_OK, "Failed to get source node id, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeByID(full_topology, node_id, &src_node2); + ok(hr == S_OK, "Failed to get source in resolved topology, hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetTopoNodeID(sink_node, &node_id); + ok(hr == S_OK, "Failed to get sink node id, hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeByID(full_topology, node_id, &sink_node2); + ok(hr == S_OK, "Failed to get sink in resolved topology, hr %#lx.\n", hr); + + if (!IsEqualGUID(&test->decoder_class, &GUID_NULL)) + { + GUID class_id; + + hr = IMFTopologyNode_GetOutput(src_node2, 0, &mft_node, &index); + ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); + ok(!index, "Unexpected stream index %lu.\n", index); + + hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); + ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); + ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); + + value = 0; + hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_DECODER, &value); + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + ok(value == 1, "Unexpected value.\n"); + + class_id = GUID_NULL; + hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + ok(IsEqualGUID(&class_id, &test->decoder_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); + + hr = IMFTopologyNode_GetObject(mft_node, &node_object); + ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); + IMFTopologyNode_Release(mft_node); + + hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); + ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); + IUnknown_Release(node_object); + + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Input type of first transform doesn't match source node type.\n"); + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + if (IsEqualGUID(&test->converter_class, &GUID_NULL)) + { + hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Output type of first transform doesn't match sink node type.\n"); + } + else if (test->decoded_type) + { + check_media_type(media_type, *test->decoded_type, -1); + } + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + if (!IsEqualGUID(&test->converter_class, &GUID_NULL)) + { + GUID class_id; + + hr = IMFTopologyNode_GetInput(sink_node2, 0, &mft_node, &index); + ok(hr == S_OK, "Failed to get decoder in resolved topology, hr %#lx.\n", hr); + ok(!index, "Unexpected stream index %lu.\n", index); + + hr = IMFTopologyNode_GetNodeType(mft_node, &node_type); + ok(hr == S_OK, "Failed to get transform node type in resolved topology, hr %#lx.\n", hr); + ok(node_type == MF_TOPOLOGY_TRANSFORM_NODE, "Unexpected node type %u.\n", node_type); + + class_id = GUID_NULL; + hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine_if(IsEqualGUID(&test->converter_class, &CLSID_CColorConvertDMO)) + ok(IsEqualGUID(&class_id, &test->converter_class), "got MF_TOPONODE_TRANSFORM_OBJECTID %s.\n", debugstr_guid(&class_id)); + + hr = IMFTopologyNode_GetObject(mft_node, &node_object); + ok(hr == S_OK, "Failed to get object of transform node, hr %#lx.\n", hr); + IMFTopologyNode_Release(mft_node); + + hr = IUnknown_QueryInterface(node_object, &IID_IMFTransform, (void **)&transform); + ok(hr == S_OK, "Failed to get IMFTransform from transform node's object, hr %#lx.\n", hr); + IUnknown_Release(node_object); + + hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + if (IsEqualGUID(&test->decoder_class, &GUID_NULL)) + { + hr = IMFMediaType_Compare(input_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Input type of last transform doesn't match source node type.\n"); + } + else if (test->decoded_type) + { + check_media_type(media_type, *test->decoded_type, -1); + } + IMFMediaType_Release(media_type); + + hr = IMFTransform_GetOutputCurrentType(transform, 0, &media_type); + ok(hr == S_OK, "Failed to get transform input type, hr %#lx.\n", hr); + hr = IMFMediaType_Compare(output_type, (IMFAttributes *)media_type, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &ret); + ok(hr == S_OK, "Failed to compare media types, hr %#lx.\n", hr); + ok(ret, "Output type of last transform doesn't match sink node type.\n"); + IMFMediaType_Release(media_type); + + IMFTransform_Release(transform); + } + + IMFTopologyNode_Release(src_node2); + IMFTopologyNode_Release(sink_node2); + + hr = IMFTopology_SetUINT32(full_topology, &IID_IMFTopology, 123); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopoLoader_Load(loader, full_topology, &topology2, NULL); + ok(hr == S_OK, "Failed to resolve topology, hr %#lx.\n", hr); + ok(full_topology != topology2, "Unexpected instance.\n"); + IMFTopology_GetTopologyID(topology2, &oldtopoid); + IMFTopology_GetTopologyID(full_topology, &newtopoid); + ok(oldtopoid == newtopoid, "Expected the same topology id. %I64u == %I64u\n", oldtopoid, newtopoid); + hr = IMFTopology_GetUINT32(topology2, &IID_IMFTopology, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ref = IMFTopology_Release(topology2); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(full_topology); + ok(ref == 0, "Release returned %ld\n", ref); + } + + hr = IMFTopology_GetCount(topology, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#lx.\n", hr); + ok(!count, "Unexpected count %u.\n", count); + + if (test->flags & LOADER_SET_MEDIA_TYPES) + ok(handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + else + ok(!handler.enum_count, "got %lu GetMediaTypeByIndex\n", handler.enum_count); + ok(!handler.set_current_count, "got %lu SetCurrentMediaType\n", handler.set_current_count); + + if (handler.current_type) + IMFMediaType_Release(handler.current_type); + handler.current_type = NULL; + + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_SOURCE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_STREAM_DESCRIPTOR, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(src_node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFMediaSource_Release(source); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFPresentationDescriptor_Release(pd); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFStreamDescriptor_Release(sd); + ok(ref == 0, "Release returned %ld\n", ref); + + winetest_pop_context(); + } + + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopoLoader_Release(loader); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(src_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(sink_node); + ok(ref == 0, "Release returned %ld\n", ref); + + ref = IMFMediaType_Release(input_type); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFMediaType_Release(output_type); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFShutdown(); + ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); + + IMFSampleGrabberSinkCallback_Release(grabber_callback); +} + +static void test_topology_loader_evr(void) +{ + static const media_type_desc media_type_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 640, 480), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), + {0}, + }; + IMFTopologyNode *node, *source_node, *evr_node; + IMFTopology *topology, *full_topology; + IMFPresentationDescriptor *pd; + IMFMediaTypeHandler *handler; + unsigned int i, count, value; + IMFStreamSink *stream_sink; + IMFMediaType *media_type; + IMFStreamDescriptor *sd; + IMFActivate *activate; + IMFTopoLoader *loader; + IMFMediaSink *sink; + WORD node_count; + UINT64 value64; + HWND window; + HRESULT hr; + LONG ref; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); + + /* EVR sink node. */ + window = create_window(); + + hr = MFCreateVideoRendererActivate(window, &activate); + ok(hr == S_OK, "Failed to create activate object, hr %#lx.\n", hr); + + hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); + if (FAILED(hr)) + { + skip("Failed to create an EVR sink, skipping tests.\n"); + DestroyWindow(window); + IMFActivate_Release(activate); + CoUninitialize(); + return; + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopoLoader(&loader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Source node. */ + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &source_node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + init_media_type(media_type, media_type_desc, -1); + + create_descriptors(1, &media_type, &media_type_desc, &pd, &sd); + init_source_node(NULL, -1, source_node, pd, sd); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + + hr = IMFMediaSink_GetStreamSinkById(sink, 0, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &evr_node); + ok(hr == S_OK, "Failed to create topology node, hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetObject(evr_node, (IUnknown *)stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_GetMediaTypeHandler(stream_sink, &handler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaTypeHandler_Release(handler); + + IMFStreamSink_Release(stream_sink); + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_AddNode(topology, source_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, evr_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(source_node, 0, evr_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_SetUINT32(evr_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_DIRECT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetCount(evr_node, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected attribute count %u.\n", count); + + hr = IMFTopoLoader_Load(loader, topology, &full_topology, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopology_GetNodeCount(full_topology, &node_count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(node_count == 3, "Unexpected node count %u.\n", node_count); + + for (i = 0; i < node_count; ++i) + { + MF_TOPOLOGY_TYPE node_type; + + hr = IMFTopology_GetNode(full_topology, i, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTopologyNode_GetNodeType(node, &node_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + switch (node_type) + { + case MF_TOPOLOGY_OUTPUT_NODE: + { + value = 1; + hr = IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_STREAMID, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!value, "Unexpected stream id %u.\n", value); + break; + } + case MF_TOPOLOGY_TRANSFORM_NODE: + { + IMFAttributes *attrs; + IMFTransform *copier; + IUnknown *obj; + + hr = IMFTopologyNode_GetObject(node, (IUnknown **)&obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IUnknown_QueryInterface(obj, &IID_IMFTransform, (void **)&copier); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTransform_GetAttributes(copier, &attrs); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + value = 0xdeadbeef; + hr = IMFAttributes_GetUINT32(attrs, &MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &value); + ok(value == 1, "Unexpected dynamic state support state %u.\n", value); + + IMFAttributes_Release(attrs); + IMFTransform_Release(copier); + IUnknown_Release(obj); + break; + } + case MF_TOPOLOGY_SOURCESTREAM_NODE: + { + value64 = 1; + hr = IMFTopologyNode_GetUINT64(node, &MF_TOPONODE_MEDIASTART, &value64); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!value64, "Unexpected value.\n"); + break; + } + default: + ok(0, "Got unexpected node type %u.\n", node_type); + break; + } + + IMFTopologyNode_Release(node); + } + + ref = IMFTopology_Release(full_topology); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopoLoader_Release(loader); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(source_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(evr_node); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFActivate_ShutdownObject(activate); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + ref = IMFActivate_Release(activate); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFMediaSink_Release(sink); + ok(ref == 0, "Release returned %ld\n", ref); + + ref = IMFMediaType_Release(media_type); + ok(ref == 0, "Release returned %ld\n", ref); + + DestroyWindow(window); + + CoUninitialize(); +} + +START_TEST(topology) +{ + init_functions(); + + test_topology(); + test_topology_tee_node(); + test_topology_loader(); + test_topology_loader_evr(); +}