[PATCH 2/5] qcap/capturegraph: Implement ICaptureGraphBuilder2::FindInterface().

Zebediah Figura z.figura12 at gmail.com
Thu Mar 5 20:04:34 CST 2020


Fixes: 15b3eba742301147571f6b168255d52f728d14d6
Signed-off-by: Zebediah Figura <zfigura at 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, &AMPROPSETID_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, &AMPROPSETID_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, &AMPROPSETID_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),
-- 
2.25.1




More information about the wine-devel mailing list