Fixes: 15b3eba742301147571f6b168255d52f728d14d6 Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/qcap/capturegraph.c | 245 ++++++++++----- dlls/qcap/tests/Makefile.in | 1 + dlls/qcap/tests/capturegraph.c | 538 +++++++++++++++++++++++++++++++++ include/axextend.idl | 6 + 4 files changed, 706 insertions(+), 84 deletions(-) create mode 100644 dlls/qcap/tests/capturegraph.c
diff --git a/dlls/qcap/capturegraph.c b/dlls/qcap/capturegraph.c index 8c85f59bb4c..33b0ecfaa11 100644 --- a/dlls/qcap/capturegraph.c +++ b/dlls/qcap/capturegraph.c @@ -222,25 +222,170 @@ fnCaptureGraphBuilder2_SetOutputFileName(ICaptureGraphBuilder2 * iface, return E_NOTIMPL; }
-static HRESULT WINAPI -fnCaptureGraphBuilder2_FindInterface(ICaptureGraphBuilder2 * iface, - const GUID *pCategory, - const GUID *pType, - IBaseFilter *pf, - REFIID riid, - void **ppint) +static BOOL pin_has_majortype(IPin *pin, const GUID *majortype) { - CaptureGraphImpl *This = impl_from_ICaptureGraphBuilder2(iface); + IEnumMediaTypes *enummt; + AM_MEDIA_TYPE *mt;
- FIXME("(%p/%p)->(%s, %s, %p, %s, %p) - workaround stub!\n", This, iface, - debugstr_guid(pCategory), debugstr_guid(pType), - pf, debugstr_guid(riid), ppint); + if (FAILED(IPin_EnumMediaTypes(pin, &enummt))) + return FALSE; + + while (IEnumMediaTypes_Next(enummt, 1, &mt, NULL) == S_OK) + { + if (IsEqualGUID(&mt->majortype, majortype)) + { + DeleteMediaType(mt); + IEnumMediaTypes_Release(enummt); + return TRUE; + } + DeleteMediaType(mt); + } + IEnumMediaTypes_Release(enummt); + return FALSE; +} + +static BOOL pin_matches(IPin *pin, PIN_DIRECTION dir, const GUID *category, + const GUID *majortype, BOOL unconnected) +{ + PIN_DIRECTION candidate_dir; + HRESULT hr; + IPin *peer; + + if (FAILED(hr = IPin_QueryDirection(pin, &candidate_dir))) + ERR("Failed to query direction, hr %#x.\n", hr); + + if (dir != candidate_dir) + return FALSE; + + if (unconnected && IPin_ConnectedTo(pin, &peer) == S_OK && peer) + { + IPin_Release(peer); + return FALSE; + } + + if (category) + { + IKsPropertySet *set; + GUID property; + DWORD size; + + if (FAILED(IPin_QueryInterface(pin, &IID_IKsPropertySet, (void **)&set))) + return FALSE; + + hr = IKsPropertySet_Get(set, &ROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, + NULL, 0, &property, sizeof(property), &size); + IKsPropertySet_Release(set); + if (FAILED(hr) || !IsEqualGUID(&property, category)) + return FALSE; + } + + if (majortype && !pin_has_majortype(pin, majortype)) + return FALSE; + + return TRUE; +} + +static HRESULT find_interface_recurse(PIN_DIRECTION dir, const GUID *category, + const GUID *majortype, IBaseFilter *filter, REFIID iid, void **out) +{ + BOOL found_category = FALSE; + IEnumPins *enumpins; + IPin *pin, *peer; + PIN_INFO info; + HRESULT hr; + + TRACE("Looking for %s pins, category %s, majortype %s from filter %p.\n", + dir == PINDIR_INPUT ? "sink" : "source", debugstr_guid(category), + debugstr_guid(majortype), filter); + + if (FAILED(hr = IBaseFilter_EnumPins(filter, &enumpins))) + { + ERR("Failed to enumerate pins, hr %#x.\n", hr); + return hr; + } + + while (IEnumPins_Next(enumpins, 1, &pin, NULL) == S_OK) + { + if (!pin_matches(pin, dir, category, majortype, FALSE)) + { + IPin_Release(pin); + continue; + } + + if (category) + found_category = TRUE; + + if (IPin_QueryInterface(pin, iid, out) == S_OK) + { + IPin_Release(pin); + IEnumPins_Release(enumpins); + return S_OK; + } + + hr = IPin_ConnectedTo(pin, &peer); + IPin_Release(pin); + if (hr == S_OK) + { + if (IPin_QueryInterface(peer, iid, out) == S_OK) + { + IPin_Release(peer); + IEnumPins_Release(enumpins); + return S_OK; + }
- return IBaseFilter_QueryInterface(pf, riid, ppint); - /* Looks for the specified interface on the filter, upstream and - * downstream from the filter, and, optionally, only on the output - * pin of the given category. - */ + IPin_QueryPinInfo(peer, &info); + IPin_Release(peer); + + if (IBaseFilter_QueryInterface(info.pFilter, iid, out) == S_OK) + { + IBaseFilter_Release(info.pFilter); + IEnumPins_Release(enumpins); + return S_OK; + } + + hr = find_interface_recurse(dir, NULL, NULL, info.pFilter, iid, out); + IBaseFilter_Release(info.pFilter); + if (hr == S_OK) + { + IEnumPins_Release(enumpins); + return S_OK; + } + } + } + IEnumPins_Release(enumpins); + + if (category && !found_category) + return E_NOINTERFACE; + + return E_FAIL; +} + +static HRESULT WINAPI fnCaptureGraphBuilder2_FindInterface(ICaptureGraphBuilder2 *iface, + const GUID *category, const GUID *majortype, IBaseFilter *filter, REFIID iid, void **out) +{ + CaptureGraphImpl *graph = impl_from_ICaptureGraphBuilder2(iface); + HRESULT hr; + + TRACE("graph %p, category %s, majortype %s, filter %p, iid %s, out %p.\n", + graph, debugstr_guid(category), debugstr_guid(majortype), filter, debugstr_guid(iid), out); + + if (category && IsEqualGUID(category, &LOOK_DOWNSTREAM_ONLY)) + return find_interface_recurse(PINDIR_OUTPUT, NULL, NULL, filter, iid, out); + + if (category && IsEqualGUID(category, &LOOK_UPSTREAM_ONLY)) + return find_interface_recurse(PINDIR_INPUT, NULL, NULL, filter, iid, out); + + if (IBaseFilter_QueryInterface(filter, iid, out) == S_OK) + return S_OK; + + if (!category) + majortype = NULL; + + hr = find_interface_recurse(PINDIR_OUTPUT, category, majortype, filter, iid, out); + if (hr == S_OK || hr == E_NOINTERFACE) + return hr; + + return find_interface_recurse(PINDIR_INPUT, NULL, NULL, filter, iid, out); }
static HRESULT match_smart_tee_pin(CaptureGraphImpl *This, @@ -574,74 +719,6 @@ fnCaptureGraphBuilder2_CopyCaptureFile(ICaptureGraphBuilder2 * iface, return E_NOTIMPL; }
-static BOOL pin_matches(IPin *pin, PIN_DIRECTION direction, const GUID *cat, const GUID *type, BOOL unconnected) -{ - IPin *partner; - PIN_DIRECTION pindir; - HRESULT hr; - - if (FAILED(hr = IPin_QueryDirection(pin, &pindir))) - ERR("Failed to query direction, hr %#x.\n", hr); - - if (unconnected && IPin_ConnectedTo(pin, &partner) == S_OK && partner!=NULL) - { - IPin_Release(partner); - TRACE("No match, %p already connected to %p\n", pin, partner); - return FALSE; - } - - if (pindir != direction) - return FALSE; - - if (cat) - { - IKsPropertySet *props; - GUID category; - DWORD fetched; - - hr = IPin_QueryInterface(pin, &IID_IKsPropertySet, (void**)&props); - if (FAILED(hr)) - return FALSE; - - hr = IKsPropertySet_Get(props, &ROPSETID_Pin, 0, NULL, - 0, &category, sizeof(category), &fetched); - IKsPropertySet_Release(props); - if (FAILED(hr) || !IsEqualIID(&category, cat)) - return FALSE; - } - - if (type) - { - IEnumMediaTypes *types; - AM_MEDIA_TYPE *media_type; - ULONG fetched; - - hr = IPin_EnumMediaTypes(pin, &types); - if (FAILED(hr)) - return FALSE; - - IEnumMediaTypes_Reset(types); - while (1) { - if (IEnumMediaTypes_Next(types, 1, &media_type, &fetched) != S_OK || fetched != 1) - { - IEnumMediaTypes_Release(types); - return FALSE; - } - - if (IsEqualIID(&media_type->majortype, type)) - { - DeleteMediaType(media_type); - break; - } - DeleteMediaType(media_type); - } - IEnumMediaTypes_Release(types); - } - - TRACE("Pin matched\n"); - return TRUE; -} - static HRESULT WINAPI fnCaptureGraphBuilder2_FindPin(ICaptureGraphBuilder2 * iface, IUnknown *pSource, diff --git a/dlls/qcap/tests/Makefile.in b/dlls/qcap/tests/Makefile.in index 4c480d524de..43ee5bcd1c5 100644 --- a/dlls/qcap/tests/Makefile.in +++ b/dlls/qcap/tests/Makefile.in @@ -5,6 +5,7 @@ C_SRCS = \ audiorecord.c \ avico.c \ avimux.c \ + capturegraph.c \ qcap.c \ smartteefilter.c \ videocapture.c diff --git a/dlls/qcap/tests/capturegraph.c b/dlls/qcap/tests/capturegraph.c new file mode 100644 index 00000000000..49c4e8fe755 --- /dev/null +++ b/dlls/qcap/tests/capturegraph.c @@ -0,0 +1,538 @@ +/* + * Capture graph builder unit tests + * + * Copyright 2019 Zebediah Figura + * Copyright 2020 Zebediah Figura for CodeWeavers + * + * 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 + */ + +#define COBJMACROS +#include "dshow.h" +#include "wine/strmbase.h" +#include "wine/test.h" + +static ICaptureGraphBuilder2 *create_capture_graph(void) +{ + ICaptureGraphBuilder2 *ret; + HRESULT hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, + CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, (void **)&ret); + ok(hr == S_OK, "Failed to create capture graph builder, hr %#x.\n", hr); + return ret; +} + +static const GUID testiid = {0x11111111}, testtype = {0x22222222}; + +struct testfilter +{ + struct strmbase_filter filter; + struct strmbase_source source, source2; + struct strmbase_sink sink, sink2; + BOOL filter_has_iface, source_has_iface, source2_has_iface, sink_has_iface, sink2_has_iface; + IKsPropertySet IKsPropertySet_iface; +}; + +static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, filter); +} + +static HRESULT testfilter_query_interface(struct strmbase_filter *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + + ok(!IsEqualGUID(iid, &IID_IKsPropertySet), "Unexpected query for IKsPropertySet.\n"); + + if (filter->filter_has_iface && IsEqualGUID(iid, &testiid)) + *out = &iface->IBaseFilter_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static struct strmbase_pin *testfilter_get_pin(struct strmbase_filter *iface, unsigned int index) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + + if (!index) + return &filter->source.pin; + else if (index == 1) + return &filter->sink.pin; + else if (index == 2) + return &filter->source2.pin; + else if (index == 3) + return &filter->sink2.pin; + return NULL; +} + +static void testfilter_destroy(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + strmbase_source_cleanup(&filter->source); + strmbase_sink_cleanup(&filter->sink); + strmbase_filter_cleanup(&filter->filter); +} + +static const struct strmbase_filter_ops testfilter_ops = +{ + .filter_query_interface = testfilter_query_interface, + .filter_get_pin = testfilter_get_pin, + .filter_destroy = testfilter_destroy, +}; + +static struct testfilter *impl_from_IKsPropertySet(IKsPropertySet *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, IKsPropertySet_iface); +} + +static HRESULT WINAPI property_set_QueryInterface(IKsPropertySet *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_IKsPropertySet(iface); + return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out); +} + +static ULONG WINAPI property_set_AddRef(IKsPropertySet *iface) +{ + struct testfilter *filter = impl_from_IKsPropertySet(iface); + return IPin_AddRef(&filter->source.pin.IPin_iface); +} + +static ULONG WINAPI property_set_Release(IKsPropertySet *iface) +{ + struct testfilter *filter = impl_from_IKsPropertySet(iface); + return IPin_Release(&filter->source.pin.IPin_iface); +} + +static HRESULT WINAPI property_set_Set(IKsPropertySet *iface, REFGUID set, DWORD id, + void *instance_data, DWORD instance_size, void *property_data, DWORD property_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI property_set_Get(IKsPropertySet *iface, REFGUID set, DWORD id, + void *instance_data, DWORD instance_size, void *property_data, DWORD property_size, DWORD *ret_size) +{ + if (winetest_debug > 1) trace("Get()\n"); + + ok(IsEqualGUID(set, &ROPSETID_Pin), "Got set %s.\n", debugstr_guid(set)); + ok(id == AMPROPERTY_PIN_CATEGORY, "Got id %#x.\n", id); + ok(!instance_data, "Got instance data %p.\n", instance_data); + ok(!instance_size, "Got instance size %u.\n", instance_size); + ok(property_size == sizeof(GUID), "Got property size %u.\n", property_size); + ok(!!ret_size, "Expected non-NULL return size.\n"); + memcpy(property_data, &PIN_CATEGORY_CAPTURE, sizeof(GUID)); + return S_OK; +} + +static HRESULT WINAPI property_set_QuerySupported(IKsPropertySet *iface, REFGUID set, DWORD id, DWORD *flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IKsPropertySetVtbl property_set_vtbl = +{ + property_set_QueryInterface, + property_set_AddRef, + property_set_Release, + property_set_Set, + property_set_Get, + property_set_QuerySupported, +}; + +static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + + if (iface == &filter->source.pin && filter->source_has_iface && IsEqualGUID(iid, &testiid)) + *out = &iface->IPin_iface; + else if (iface == &filter->source2.pin && filter->source2_has_iface && IsEqualGUID(iid, &testiid)) + *out = &iface->IPin_iface; + else if (iface == &filter->source.pin && filter->IKsPropertySet_iface.lpVtbl + && IsEqualGUID(iid, &IID_IKsPropertySet)) + *out = &filter->IKsPropertySet_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static HRESULT testsource_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + return S_OK; +} + +static HRESULT testsource_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt) +{ + if (!index) + { + memset(mt, 0, sizeof(*mt)); + mt->majortype = testtype; + return S_OK; + } + return VFW_S_NO_MORE_ITEMS; +} + +static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, + IMemInputPin *input, IMemAllocator **allocator) +{ + return S_OK; +} + +static const struct strmbase_source_ops testsource_ops = +{ + .base.pin_query_interface = testsource_query_interface, + .base.pin_query_accept = testsource_query_accept, + .base.pin_get_media_type = testsource_get_media_type, + .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideAllocator = testsource_DecideAllocator, +}; + +static HRESULT testsink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + + ok(!IsEqualGUID(iid, &IID_IKsPropertySet), "Unexpected query for IKsPropertySet.\n"); + + if (IsEqualGUID(iid, &IID_IMemInputPin)) + *out = &filter->sink.IMemInputPin_iface; + else if (iface == &filter->sink.pin && filter->sink_has_iface && IsEqualGUID(iid, &testiid)) + *out = &iface->IPin_iface; + else if (iface == &filter->sink2.pin && filter->sink2_has_iface && IsEqualGUID(iid, &testiid)) + *out = &iface->IPin_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static HRESULT testsink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + return S_OK; +} + +static const struct strmbase_sink_ops testsink_ops = +{ + .base.pin_query_interface = testsink_query_interface, + .base.pin_query_accept = testsink_query_accept, + .base.pin_get_media_type = strmbase_pin_get_media_type, +}; + +static void reset_interfaces(struct testfilter *filter) +{ + filter->filter_has_iface = filter->sink_has_iface = filter->sink2_has_iface = TRUE; + filter->source_has_iface = filter->source2_has_iface = TRUE; +} + +static void testfilter_init(struct testfilter *filter) +{ + static const GUID clsid = {0xabacab}; + memset(filter, 0, sizeof(*filter)); + strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); + strmbase_source_init(&filter->source, &filter->filter, L"source", &testsource_ops); + strmbase_source_init(&filter->source2, &filter->filter, L"source2", &testsource_ops); + strmbase_sink_init(&filter->sink, &filter->filter, L"sink", &testsink_ops, NULL); + strmbase_sink_init(&filter->sink2, &filter->filter, L"sink2", &testsink_ops, NULL); + reset_interfaces(filter); +} + +static void test_find_interface(void) +{ + static const AM_MEDIA_TYPE mt1 = + { + .majortype = {0x111}, + .subtype = {0x222}, + .formattype = {0x333}, + }; + static const AM_MEDIA_TYPE mt2 = + { + .majortype = {0x444}, + .subtype = {0x555}, + .formattype = {0x666}, + }; + static const GUID bogus_majortype = {0x777}; + + ICaptureGraphBuilder2 *capture_graph = create_capture_graph(); + struct testfilter filter1, filter2, filter3; + IGraphBuilder *graph; + unsigned int i; + IUnknown *unk; + HRESULT hr; + ULONG ref; + + struct + { + BOOL *expose; + const void *iface; + } + tests_from_filter2[] = + { + {&filter2.filter_has_iface, &filter2.filter.IBaseFilter_iface}, + {&filter2.source_has_iface, &filter2.source.pin.IPin_iface}, + {&filter3.sink_has_iface, &filter3.sink.pin.IPin_iface}, + {&filter3.filter_has_iface, &filter3.filter.IBaseFilter_iface}, + {&filter3.source_has_iface, &filter3.source.pin.IPin_iface}, + {&filter3.source2_has_iface, &filter3.source2.pin.IPin_iface}, + {&filter2.source2_has_iface, &filter2.source2.pin.IPin_iface}, + {&filter2.sink_has_iface, &filter2.sink.pin.IPin_iface}, + {&filter1.source_has_iface, &filter1.source.pin.IPin_iface}, + {&filter1.filter_has_iface, &filter1.filter.IBaseFilter_iface}, + {&filter1.sink_has_iface, &filter1.sink.pin.IPin_iface}, + {&filter1.sink2_has_iface, &filter1.sink2.pin.IPin_iface}, + {&filter2.sink2_has_iface, &filter2.sink2.pin.IPin_iface}, + }, tests_from_filter1[] = + { + {&filter1.filter_has_iface, &filter1.filter.IBaseFilter_iface}, + {&filter1.source_has_iface, &filter1.source.pin.IPin_iface}, + {&filter2.sink_has_iface, &filter2.sink.pin.IPin_iface}, + {&filter2.filter_has_iface, &filter2.filter.IBaseFilter_iface}, + {&filter2.source_has_iface, &filter2.source.pin.IPin_iface}, + {&filter3.sink_has_iface, &filter3.sink.pin.IPin_iface}, + {&filter3.filter_has_iface, &filter3.filter.IBaseFilter_iface}, + {&filter3.source_has_iface, &filter3.source.pin.IPin_iface}, + {&filter3.source2_has_iface, &filter3.source2.pin.IPin_iface}, + {&filter2.source2_has_iface, &filter2.source2.pin.IPin_iface}, + {&filter1.source2_has_iface, &filter1.source2.pin.IPin_iface}, + {&filter1.sink_has_iface, &filter1.sink.pin.IPin_iface}, + {&filter1.sink2_has_iface, &filter1.sink2.pin.IPin_iface}, + }, look_upstream_tests[] = + { + {&filter2.sink_has_iface, &filter2.sink.pin.IPin_iface}, + {&filter1.source_has_iface, &filter1.source.pin.IPin_iface}, + {&filter1.filter_has_iface, &filter1.filter.IBaseFilter_iface}, + {&filter1.sink_has_iface, &filter1.sink.pin.IPin_iface}, + {&filter1.sink2_has_iface, &filter1.sink2.pin.IPin_iface}, + {&filter2.sink2_has_iface, &filter2.sink2.pin.IPin_iface}, + }, look_downstream_tests[] = + { + {&filter2.source_has_iface, &filter2.source.pin.IPin_iface}, + {&filter3.sink_has_iface, &filter3.sink.pin.IPin_iface}, + {&filter3.filter_has_iface, &filter3.filter.IBaseFilter_iface}, + {&filter3.source_has_iface, &filter3.source.pin.IPin_iface}, + {&filter3.source2_has_iface, &filter3.source2.pin.IPin_iface}, + {&filter2.source2_has_iface, &filter2.source2.pin.IPin_iface}, + }, category_tests[] = + { + {&filter3.filter_has_iface, &filter3.filter.IBaseFilter_iface}, + {&filter3.source_has_iface, &filter3.source.pin.IPin_iface}, + {&filter3.source2_has_iface, &filter3.source2.pin.IPin_iface}, + {&filter2.sink_has_iface, &filter2.sink.pin.IPin_iface}, + {&filter1.source_has_iface, &filter1.source.pin.IPin_iface}, + {&filter1.filter_has_iface, &filter1.filter.IBaseFilter_iface}, + {&filter1.sink_has_iface, &filter1.sink.pin.IPin_iface}, + {&filter1.sink2_has_iface, &filter1.sink2.pin.IPin_iface}, + {&filter2.sink2_has_iface, &filter2.sink2.pin.IPin_iface}, + }; + + CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)&graph); + hr = ICaptureGraphBuilder2_SetFiltergraph(capture_graph, graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testfilter_init(&filter1); + IGraphBuilder_AddFilter(graph, &filter1.filter.IBaseFilter_iface, L"filter1"); + testfilter_init(&filter2); + IGraphBuilder_AddFilter(graph, &filter2.filter.IBaseFilter_iface, L"filter2"); + testfilter_init(&filter3); + IGraphBuilder_AddFilter(graph, &filter3.filter.IBaseFilter_iface, L"filter3"); + + hr = IGraphBuilder_ConnectDirect(graph, &filter1.source.pin.IPin_iface, &filter2.sink.pin.IPin_iface, &mt1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_ConnectDirect(graph, &filter2.source.pin.IPin_iface, &filter3.sink.pin.IPin_iface, &mt2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + /* Test search order without any restrictions applied. */ + + for (i = 0; i < ARRAY_SIZE(tests_from_filter2); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, NULL, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == tests_from_filter2[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *tests_from_filter2[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, NULL, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + reset_interfaces(&filter1); + reset_interfaces(&filter2); + reset_interfaces(&filter3); + + for (i = 0; i < ARRAY_SIZE(tests_from_filter1); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, NULL, &bogus_majortype, + &filter1.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == tests_from_filter1[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *tests_from_filter1[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, NULL, &bogus_majortype, + &filter1.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + /* Test with upstream/downstream flags. */ + + reset_interfaces(&filter1); + reset_interfaces(&filter2); + reset_interfaces(&filter3); + + for (i = 0; i < ARRAY_SIZE(look_upstream_tests); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &LOOK_UPSTREAM_ONLY, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == look_upstream_tests[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *look_upstream_tests[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &LOOK_UPSTREAM_ONLY, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + reset_interfaces(&filter1); + reset_interfaces(&filter2); + reset_interfaces(&filter3); + + for (i = 0; i < ARRAY_SIZE(look_downstream_tests); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &LOOK_DOWNSTREAM_ONLY, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == look_downstream_tests[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *look_downstream_tests[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &LOOK_DOWNSTREAM_ONLY, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + /* Test with a category flag. */ + + reset_interfaces(&filter1); + reset_interfaces(&filter2); + reset_interfaces(&filter3); + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Got hr %#x\n", hr); + ok(unk == (IUnknown *)&filter2.filter.IBaseFilter_iface, "Got wrong interface %p.\n", unk); + IUnknown_Release(unk); + filter2.filter_has_iface = FALSE; + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + filter2.IKsPropertySet_iface.lpVtbl = &property_set_vtbl; + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk == (IUnknown *)&filter2.source.pin.IPin_iface, "Got wrong interface %p.\n", unk); + IUnknown_Release(unk); + filter2.source_has_iface = FALSE; + + /* Native returns the filter3 sink next, but suffers from a bug wherein it + * releases a reference to the wrong pin. */ + filter3.sink_has_iface = FALSE; + + for (i = 0; i < ARRAY_SIZE(category_tests); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == category_tests[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *category_tests[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + /* Test with a media type. */ + + reset_interfaces(&filter1); + reset_interfaces(&filter2); + reset_interfaces(&filter3); + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Got hr %#x\n", hr); + ok(unk == (IUnknown *)&filter2.filter.IBaseFilter_iface, "Got wrong interface %p.\n", unk); + IUnknown_Release(unk); + filter2.filter_has_iface = FALSE; + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, &bogus_majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, &mt2.majortype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, &testtype, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk == (IUnknown *)&filter2.source.pin.IPin_iface, "Got wrong interface %p.\n", unk); + IUnknown_Release(unk); + filter2.source_has_iface = FALSE; + + filter3.sink_has_iface = FALSE; + + for (i = 0; i < ARRAY_SIZE(category_tests); ++i) + { + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == S_OK, "Test %u: got hr %#x.\n", i, hr); + ok(unk == category_tests[i].iface, "Test %u: got wrong interface %p.\n", i, unk); + IUnknown_Release(unk); + *category_tests[i].expose = FALSE; + } + + hr = ICaptureGraphBuilder2_FindInterface(capture_graph, &PIN_CATEGORY_CAPTURE, NULL, + &filter2.filter.IBaseFilter_iface, &testiid, (void **)&unk); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + ref = ICaptureGraphBuilder2_Release(capture_graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&filter1.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&filter2.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&filter3.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + +START_TEST(capturegraph) +{ + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + test_find_interface(); + + CoUninitialize(); +} diff --git a/include/axextend.idl b/include/axextend.idl index fd231bca3c4..2156b13315f 100644 --- a/include/axextend.idl +++ b/include/axextend.idl @@ -1153,6 +1153,12 @@ cpp_quote("#define _IKsPropertySet_") cpp_quote("#define KSPROPERTY_SUPPORT_GET 1") cpp_quote("#define KSPROPERTY_SUPPORT_SET 2")
+typedef enum AMPROPERTY_PIN +{ + AMPROPERTY_PIN_CATEGORY, + AMPROPERTY_PIN_MEDIUM, +} AMPROPERTY_PIN; + [ object, uuid(31efac30-515c-11d0-a9aa-00aa0061be93),