2 * Capture graph builder unit tests
4 * Copyright 2019 Zebediah Figura
5 * Copyright 2020 Zebediah Figura for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/strmbase.h"
25 #include "wine/test.h"
27 static ICaptureGraphBuilder2
*create_capture_graph(void)
29 ICaptureGraphBuilder2
*ret
;
30 HRESULT hr
= CoCreateInstance(&CLSID_CaptureGraphBuilder2
, NULL
,
31 CLSCTX_INPROC_SERVER
, &IID_ICaptureGraphBuilder2
, (void **)&ret
);
32 ok(hr
== S_OK
, "Failed to create capture graph builder, hr %#x.\n", hr
);
36 static const GUID testiid
= {0x11111111}, testtype
= {0x22222222};
40 struct strmbase_source pin
;
41 IKsPropertySet IKsPropertySet_iface
;
48 struct strmbase_sink pin
;
54 struct strmbase_filter filter
;
55 struct testsource source1
, source2
;
56 struct testsink sink1
, sink2
;
57 BOOL filter_has_iface
;
59 const GUID
*sink_type
;
62 static inline struct testfilter
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
64 return CONTAINING_RECORD(iface
, struct testfilter
, filter
);
67 static HRESULT
testfilter_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
69 struct testfilter
*filter
= impl_from_strmbase_filter(iface
);
71 ok(!IsEqualGUID(iid
, &IID_IKsPropertySet
), "Unexpected query for IKsPropertySet.\n");
73 if (filter
->filter_has_iface
&& IsEqualGUID(iid
, &testiid
))
74 *out
= &iface
->IBaseFilter_iface
;
78 IUnknown_AddRef((IUnknown
*)*out
);
82 static struct strmbase_pin
*testfilter_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
84 struct testfilter
*filter
= impl_from_strmbase_filter(iface
);
87 return &filter
->source1
.pin
.pin
;
89 return &filter
->sink1
.pin
.pin
;
91 return &filter
->source2
.pin
.pin
;
93 return &filter
->sink2
.pin
.pin
;
97 static void testfilter_destroy(struct strmbase_filter
*iface
)
99 struct testfilter
*filter
= impl_from_strmbase_filter(iface
);
100 strmbase_source_cleanup(&filter
->source1
.pin
);
101 strmbase_source_cleanup(&filter
->source2
.pin
);
102 strmbase_sink_cleanup(&filter
->sink1
.pin
);
103 strmbase_sink_cleanup(&filter
->sink2
.pin
);
104 strmbase_filter_cleanup(&filter
->filter
);
107 static const struct strmbase_filter_ops testfilter_ops
=
109 .filter_query_interface
= testfilter_query_interface
,
110 .filter_get_pin
= testfilter_get_pin
,
111 .filter_destroy
= testfilter_destroy
,
114 static struct testsource
*impl_from_IKsPropertySet(IKsPropertySet
*iface
)
116 return CONTAINING_RECORD(iface
, struct testsource
, IKsPropertySet_iface
);
119 static HRESULT WINAPI
property_set_QueryInterface(IKsPropertySet
*iface
, REFIID iid
, void **out
)
121 struct testsource
*pin
= impl_from_IKsPropertySet(iface
);
122 return IPin_QueryInterface(&pin
->pin
.pin
.IPin_iface
, iid
, out
);
125 static ULONG WINAPI
property_set_AddRef(IKsPropertySet
*iface
)
127 struct testsource
*pin
= impl_from_IKsPropertySet(iface
);
128 return IPin_AddRef(&pin
->pin
.pin
.IPin_iface
);
131 static ULONG WINAPI
property_set_Release(IKsPropertySet
*iface
)
133 struct testsource
*pin
= impl_from_IKsPropertySet(iface
);
134 return IPin_Release(&pin
->pin
.pin
.IPin_iface
);
137 static HRESULT WINAPI
property_set_Set(IKsPropertySet
*iface
, REFGUID set
, DWORD id
,
138 void *instance_data
, DWORD instance_size
, void *property_data
, DWORD property_size
)
140 ok(0, "Unexpected call.\n");
144 static HRESULT WINAPI
property_set_Get(IKsPropertySet
*iface
, REFGUID set
, DWORD id
,
145 void *instance_data
, DWORD instance_size
, void *property_data
, DWORD property_size
, DWORD
*ret_size
)
147 struct testsource
*pin
= impl_from_IKsPropertySet(iface
);
149 if (winetest_debug
> 1) trace("%s->Get()\n", debugstr_w(pin
->pin
.pin
.name
));
151 ok(IsEqualGUID(set
, &ROPSETID_Pin
), "Got set %s.\n", debugstr_guid(set
));
152 ok(id
== AMPROPERTY_PIN_CATEGORY
, "Got id %#x.\n", id
);
153 ok(!instance_data
, "Got instance data %p.\n", instance_data
);
154 ok(!instance_size
, "Got instance size %u.\n", instance_size
);
155 ok(property_size
== sizeof(GUID
), "Got property size %u.\n", property_size
);
156 ok(!!ret_size
, "Expected non-NULL return size.\n");
157 memcpy(property_data
, &pin
->category
, sizeof(GUID
));
161 static HRESULT WINAPI
property_set_QuerySupported(IKsPropertySet
*iface
, REFGUID set
, DWORD id
, DWORD
*flags
)
163 ok(0, "Unexpected call.\n");
167 static const IKsPropertySetVtbl property_set_vtbl
=
169 property_set_QueryInterface
,
171 property_set_Release
,
174 property_set_QuerySupported
,
177 static struct testsource
*impl_source_from_strmbase_pin(struct strmbase_pin
*iface
)
179 return CONTAINING_RECORD(iface
, struct testsource
, pin
.pin
);
182 static HRESULT
testsource_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
184 struct testsource
*pin
= impl_source_from_strmbase_pin(iface
);
186 if (pin
->has_iface
&& IsEqualGUID(iid
, &testiid
))
187 *out
= &pin
->pin
.pin
.IPin_iface
;
188 else if (pin
->IKsPropertySet_iface
.lpVtbl
&& IsEqualGUID(iid
, &IID_IKsPropertySet
))
189 *out
= &pin
->IKsPropertySet_iface
;
191 return E_NOINTERFACE
;
193 IUnknown_AddRef((IUnknown
*)*out
);
197 static HRESULT
testsource_get_media_type(struct strmbase_pin
*iface
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
199 struct testfilter
*filter
= impl_from_strmbase_filter(iface
->filter
);
203 memset(mt
, 0, sizeof(*mt
));
204 mt
->majortype
= filter
->source_type
;
207 return VFW_S_NO_MORE_ITEMS
;
210 static HRESULT WINAPI
testsource_DecideBufferSize(struct strmbase_source
*iface
,
211 IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
213 props
->cBuffers
= props
->cbAlign
= props
->cbBuffer
= 1;
217 static const struct strmbase_source_ops testsource_ops
=
219 .base
.pin_query_interface
= testsource_query_interface
,
220 .base
.pin_get_media_type
= testsource_get_media_type
,
221 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
222 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
223 .pfnDecideBufferSize
= testsource_DecideBufferSize
,
226 static struct testsink
*impl_sink_from_strmbase_pin(struct strmbase_pin
*iface
)
228 return CONTAINING_RECORD(iface
, struct testsink
, pin
.pin
);
231 static HRESULT
testsink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
233 struct testsink
*pin
= impl_sink_from_strmbase_pin(iface
);
235 ok(!IsEqualGUID(iid
, &IID_IKsPropertySet
), "Unexpected query for IKsPropertySet.\n");
237 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
238 *out
= &pin
->pin
.IMemInputPin_iface
;
239 else if (pin
->has_iface
&& IsEqualGUID(iid
, &testiid
))
240 *out
= &pin
->pin
.pin
.IPin_iface
;
242 return E_NOINTERFACE
;
244 IUnknown_AddRef((IUnknown
*)*out
);
248 static HRESULT
testsink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
250 struct testfilter
*filter
= impl_from_strmbase_filter(iface
->filter
);
252 if (filter
->sink_type
&& !IsEqualGUID(&mt
->majortype
, filter
->sink_type
))
257 static const struct strmbase_sink_ops testsink_ops
=
259 .base
.pin_query_interface
= testsink_query_interface
,
260 .base
.pin_query_accept
= testsink_query_accept
,
263 static void reset_interfaces(struct testfilter
*filter
)
265 filter
->filter_has_iface
= filter
->sink1
.has_iface
= filter
->sink2
.has_iface
= TRUE
;
266 filter
->source1
.has_iface
= filter
->source2
.has_iface
= TRUE
;
269 static void testfilter_init(struct testfilter
*filter
)
271 static const GUID clsid
= {0xabacab};
272 memset(filter
, 0, sizeof(*filter
));
273 strmbase_filter_init(&filter
->filter
, NULL
, &clsid
, &testfilter_ops
);
274 strmbase_source_init(&filter
->source1
.pin
, &filter
->filter
, L
"source1", &testsource_ops
);
275 strmbase_source_init(&filter
->source2
.pin
, &filter
->filter
, L
"source2", &testsource_ops
);
276 strmbase_sink_init(&filter
->sink1
.pin
, &filter
->filter
, L
"sink1", &testsink_ops
, NULL
);
277 strmbase_sink_init(&filter
->sink2
.pin
, &filter
->filter
, L
"sink2", &testsink_ops
, NULL
);
278 reset_interfaces(filter
);
281 static void test_find_interface(void)
283 static const AM_MEDIA_TYPE mt1
=
285 .majortype
= {0x111},
287 .formattype
= {0x333},
289 static const AM_MEDIA_TYPE mt2
=
291 .majortype
= {0x444},
293 .formattype
= {0x666},
295 static const GUID bogus_majortype
= {0x777};
297 ICaptureGraphBuilder2
*capture_graph
= create_capture_graph();
298 struct testfilter filter1
, filter2
, filter3
;
299 IGraphBuilder
*graph
;
310 tests_from_filter2
[] =
312 {&filter2
.filter_has_iface
, &filter2
.filter
.IBaseFilter_iface
},
313 {&filter2
.source1
.has_iface
, &filter2
.source1
.pin
.pin
.IPin_iface
},
314 {&filter3
.sink1
.has_iface
, &filter3
.sink1
.pin
.pin
.IPin_iface
},
315 {&filter3
.filter_has_iface
, &filter3
.filter
.IBaseFilter_iface
},
316 {&filter3
.source1
.has_iface
, &filter3
.source1
.pin
.pin
.IPin_iface
},
317 {&filter3
.source2
.has_iface
, &filter3
.source2
.pin
.pin
.IPin_iface
},
318 {&filter2
.source2
.has_iface
, &filter2
.source2
.pin
.pin
.IPin_iface
},
319 {&filter2
.sink1
.has_iface
, &filter2
.sink1
.pin
.pin
.IPin_iface
},
320 {&filter1
.source1
.has_iface
, &filter1
.source1
.pin
.pin
.IPin_iface
},
321 {&filter1
.filter_has_iface
, &filter1
.filter
.IBaseFilter_iface
},
322 {&filter1
.sink1
.has_iface
, &filter1
.sink1
.pin
.pin
.IPin_iface
},
323 {&filter1
.sink2
.has_iface
, &filter1
.sink2
.pin
.pin
.IPin_iface
},
324 {&filter2
.sink2
.has_iface
, &filter2
.sink2
.pin
.pin
.IPin_iface
},
325 }, tests_from_filter1
[] =
327 {&filter1
.filter_has_iface
, &filter1
.filter
.IBaseFilter_iface
},
328 {&filter1
.source1
.has_iface
, &filter1
.source1
.pin
.pin
.IPin_iface
},
329 {&filter2
.sink1
.has_iface
, &filter2
.sink1
.pin
.pin
.IPin_iface
},
330 {&filter2
.filter_has_iface
, &filter2
.filter
.IBaseFilter_iface
},
331 {&filter2
.source1
.has_iface
, &filter2
.source1
.pin
.pin
.IPin_iface
},
332 {&filter3
.sink1
.has_iface
, &filter3
.sink1
.pin
.pin
.IPin_iface
},
333 {&filter3
.filter_has_iface
, &filter3
.filter
.IBaseFilter_iface
},
334 {&filter3
.source1
.has_iface
, &filter3
.source1
.pin
.pin
.IPin_iface
},
335 {&filter3
.source2
.has_iface
, &filter3
.source2
.pin
.pin
.IPin_iface
},
336 {&filter2
.source2
.has_iface
, &filter2
.source2
.pin
.pin
.IPin_iface
},
337 {&filter1
.source2
.has_iface
, &filter1
.source2
.pin
.pin
.IPin_iface
},
338 {&filter1
.sink1
.has_iface
, &filter1
.sink1
.pin
.pin
.IPin_iface
},
339 {&filter1
.sink2
.has_iface
, &filter1
.sink2
.pin
.pin
.IPin_iface
},
340 }, look_upstream_tests
[] =
342 {&filter2
.sink1
.has_iface
, &filter2
.sink1
.pin
.pin
.IPin_iface
},
343 {&filter1
.source1
.has_iface
, &filter1
.source1
.pin
.pin
.IPin_iface
},
344 {&filter1
.filter_has_iface
, &filter1
.filter
.IBaseFilter_iface
},
345 {&filter1
.sink1
.has_iface
, &filter1
.sink1
.pin
.pin
.IPin_iface
},
346 {&filter1
.sink2
.has_iface
, &filter1
.sink2
.pin
.pin
.IPin_iface
},
347 {&filter2
.sink2
.has_iface
, &filter2
.sink2
.pin
.pin
.IPin_iface
},
348 }, look_downstream_tests
[] =
350 {&filter2
.source1
.has_iface
, &filter2
.source1
.pin
.pin
.IPin_iface
},
351 {&filter3
.sink1
.has_iface
, &filter3
.sink1
.pin
.pin
.IPin_iface
},
352 {&filter3
.filter_has_iface
, &filter3
.filter
.IBaseFilter_iface
},
353 {&filter3
.source1
.has_iface
, &filter3
.source1
.pin
.pin
.IPin_iface
},
354 {&filter3
.source2
.has_iface
, &filter3
.source2
.pin
.pin
.IPin_iface
},
355 {&filter2
.source2
.has_iface
, &filter2
.source2
.pin
.pin
.IPin_iface
},
356 }, category_tests
[] =
358 {&filter3
.filter_has_iface
, &filter3
.filter
.IBaseFilter_iface
},
359 {&filter3
.source1
.has_iface
, &filter3
.source1
.pin
.pin
.IPin_iface
},
360 {&filter3
.source2
.has_iface
, &filter3
.source2
.pin
.pin
.IPin_iface
},
361 {&filter2
.sink1
.has_iface
, &filter2
.sink1
.pin
.pin
.IPin_iface
},
362 {&filter1
.source1
.has_iface
, &filter1
.source1
.pin
.pin
.IPin_iface
},
363 {&filter1
.filter_has_iface
, &filter1
.filter
.IBaseFilter_iface
},
364 {&filter1
.sink1
.has_iface
, &filter1
.sink1
.pin
.pin
.IPin_iface
},
365 {&filter1
.sink2
.has_iface
, &filter1
.sink2
.pin
.pin
.IPin_iface
},
366 {&filter2
.sink2
.has_iface
, &filter2
.sink2
.pin
.pin
.IPin_iface
},
369 CoCreateInstance(&CLSID_FilterGraph
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IGraphBuilder
, (void **)&graph
);
370 hr
= ICaptureGraphBuilder2_SetFiltergraph(capture_graph
, graph
);
371 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
373 testfilter_init(&filter1
);
374 IGraphBuilder_AddFilter(graph
, &filter1
.filter
.IBaseFilter_iface
, L
"filter1");
375 testfilter_init(&filter2
);
376 IGraphBuilder_AddFilter(graph
, &filter2
.filter
.IBaseFilter_iface
, L
"filter2");
377 testfilter_init(&filter3
);
378 IGraphBuilder_AddFilter(graph
, &filter3
.filter
.IBaseFilter_iface
, L
"filter3");
380 hr
= IGraphBuilder_ConnectDirect(graph
, &filter1
.source1
.pin
.pin
.IPin_iface
, &filter2
.sink1
.pin
.pin
.IPin_iface
, &mt1
);
381 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
382 hr
= IGraphBuilder_ConnectDirect(graph
, &filter2
.source1
.pin
.pin
.IPin_iface
, &filter3
.sink1
.pin
.pin
.IPin_iface
, &mt2
);
383 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
385 /* Test search order without any restrictions applied. */
387 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, NULL
, &bogus_majortype
,
388 NULL
, &testiid
, (void **)&unk
);
389 ok(hr
== E_POINTER
, "got hr %#x.\n", hr
);
391 for (i
= 0; i
< ARRAY_SIZE(tests_from_filter2
); ++i
)
393 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, NULL
, &bogus_majortype
,
394 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
395 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
396 ok(unk
== tests_from_filter2
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
397 IUnknown_Release(unk
);
398 *tests_from_filter2
[i
].expose
= FALSE
;
401 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, NULL
, &bogus_majortype
,
402 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
403 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
405 reset_interfaces(&filter1
);
406 reset_interfaces(&filter2
);
407 reset_interfaces(&filter3
);
409 for (i
= 0; i
< ARRAY_SIZE(tests_from_filter1
); ++i
)
411 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, NULL
, &bogus_majortype
,
412 &filter1
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
413 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
414 ok(unk
== tests_from_filter1
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
415 IUnknown_Release(unk
);
416 *tests_from_filter1
[i
].expose
= FALSE
;
419 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, NULL
, &bogus_majortype
,
420 &filter1
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
421 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
423 /* Test with upstream/downstream flags. */
425 reset_interfaces(&filter1
);
426 reset_interfaces(&filter2
);
427 reset_interfaces(&filter3
);
429 for (i
= 0; i
< ARRAY_SIZE(look_upstream_tests
); ++i
)
431 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &LOOK_UPSTREAM_ONLY
, &bogus_majortype
,
432 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
433 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
434 ok(unk
== look_upstream_tests
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
435 IUnknown_Release(unk
);
436 *look_upstream_tests
[i
].expose
= FALSE
;
439 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &LOOK_UPSTREAM_ONLY
, &bogus_majortype
,
440 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
441 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
443 reset_interfaces(&filter1
);
444 reset_interfaces(&filter2
);
445 reset_interfaces(&filter3
);
447 for (i
= 0; i
< ARRAY_SIZE(look_downstream_tests
); ++i
)
449 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &LOOK_DOWNSTREAM_ONLY
, &bogus_majortype
,
450 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
451 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
452 ok(unk
== look_downstream_tests
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
453 IUnknown_Release(unk
);
454 *look_downstream_tests
[i
].expose
= FALSE
;
457 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &LOOK_DOWNSTREAM_ONLY
, &bogus_majortype
,
458 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
459 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
461 /* Test with a category flag. */
463 reset_interfaces(&filter1
);
464 reset_interfaces(&filter2
);
465 reset_interfaces(&filter3
);
467 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
468 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
469 ok(hr
== S_OK
, "Got hr %#x\n", hr
);
470 ok(unk
== (IUnknown
*)&filter2
.filter
.IBaseFilter_iface
, "Got wrong interface %p.\n", unk
);
471 IUnknown_Release(unk
);
472 filter2
.filter_has_iface
= FALSE
;
474 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
475 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
476 ok(hr
== E_NOINTERFACE
, "Got hr %#x.\n", hr
);
478 filter2
.source1
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
479 filter2
.source1
.category
= PIN_CATEGORY_CAPTURE
;
480 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
481 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
482 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
483 ok(unk
== (IUnknown
*)&filter2
.source1
.pin
.pin
.IPin_iface
, "Got wrong interface %p.\n", unk
);
484 IUnknown_Release(unk
);
485 filter2
.source1
.has_iface
= FALSE
;
487 /* Native returns the filter3 sink next, but suffers from a bug wherein it
488 * releases a reference to the wrong pin. */
489 filter3
.sink1
.has_iface
= FALSE
;
491 for (i
= 0; i
< ARRAY_SIZE(category_tests
); ++i
)
493 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
494 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
495 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
496 ok(unk
== category_tests
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
497 IUnknown_Release(unk
);
498 *category_tests
[i
].expose
= FALSE
;
501 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
502 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
503 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
505 /* Test with a media type. */
507 filter1
.source_type
= filter2
.source_type
= testtype
;
509 reset_interfaces(&filter1
);
510 reset_interfaces(&filter2
);
511 reset_interfaces(&filter3
);
513 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, &bogus_majortype
,
514 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
515 ok(hr
== S_OK
, "Got hr %#x\n", hr
);
516 ok(unk
== (IUnknown
*)&filter2
.filter
.IBaseFilter_iface
, "Got wrong interface %p.\n", unk
);
517 IUnknown_Release(unk
);
518 filter2
.filter_has_iface
= FALSE
;
520 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, &bogus_majortype
,
521 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
522 ok(hr
== E_NOINTERFACE
, "Got hr %#x.\n", hr
);
524 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, &mt2
.majortype
,
525 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
526 ok(hr
== E_NOINTERFACE
, "Got hr %#x.\n", hr
);
528 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, &testtype
,
529 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
530 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
531 ok(unk
== (IUnknown
*)&filter2
.source1
.pin
.pin
.IPin_iface
, "Got wrong interface %p.\n", unk
);
532 IUnknown_Release(unk
);
533 filter2
.source1
.has_iface
= FALSE
;
535 filter3
.sink1
.has_iface
= FALSE
;
537 for (i
= 0; i
< ARRAY_SIZE(category_tests
); ++i
)
539 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
540 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
541 ok(hr
== S_OK
, "Test %u: got hr %#x.\n", i
, hr
);
542 ok(unk
== category_tests
[i
].iface
, "Test %u: got wrong interface %p.\n", i
, unk
);
543 IUnknown_Release(unk
);
544 *category_tests
[i
].expose
= FALSE
;
547 hr
= ICaptureGraphBuilder2_FindInterface(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
548 &filter2
.filter
.IBaseFilter_iface
, &testiid
, (void **)&unk
);
549 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
551 ref
= ICaptureGraphBuilder2_Release(capture_graph
);
552 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
553 ref
= IGraphBuilder_Release(graph
);
554 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
555 ref
= IBaseFilter_Release(&filter1
.filter
.IBaseFilter_iface
);
556 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
557 ref
= IBaseFilter_Release(&filter2
.filter
.IBaseFilter_iface
);
558 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
559 ref
= IBaseFilter_Release(&filter3
.filter
.IBaseFilter_iface
);
560 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
563 static void test_find_pin(void)
565 static const AM_MEDIA_TYPE mt
=
567 .majortype
= {0x111},
569 .formattype
= {0x333},
572 ICaptureGraphBuilder2
*capture_graph
= create_capture_graph();
573 struct testfilter filter1
, filter2
;
574 IGraphBuilder
*graph
;
579 CoCreateInstance(&CLSID_FilterGraph
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IGraphBuilder
, (void **)&graph
);
580 hr
= ICaptureGraphBuilder2_SetFiltergraph(capture_graph
, graph
);
581 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
583 testfilter_init(&filter1
);
584 testfilter_init(&filter2
);
585 IGraphBuilder_AddFilter(graph
, &filter1
.filter
.IBaseFilter_iface
, L
"filter1");
586 IGraphBuilder_AddFilter(graph
, &filter2
.filter
.IBaseFilter_iface
, L
"filter2");
588 hr
= IGraphBuilder_ConnectDirect(graph
, &filter1
.source1
.pin
.pin
.IPin_iface
,
589 &filter2
.sink1
.pin
.pin
.IPin_iface
, &mt
);
590 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
592 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
593 PINDIR_INPUT
, NULL
, NULL
, FALSE
, 0, &pin
);
594 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
595 ok(pin
== &filter1
.sink1
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
598 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
599 PINDIR_INPUT
, NULL
, NULL
, FALSE
, 1, &pin
);
600 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
601 ok(pin
== &filter1
.sink2
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
604 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
605 PINDIR_INPUT
, NULL
, NULL
, FALSE
, 2, &pin
);
606 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
608 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
609 PINDIR_OUTPUT
, NULL
, NULL
, FALSE
, 0, &pin
);
610 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
611 ok(pin
== &filter1
.source1
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
614 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
615 PINDIR_OUTPUT
, NULL
, NULL
, FALSE
, 1, &pin
);
616 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
617 ok(pin
== &filter1
.source2
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
620 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
621 PINDIR_OUTPUT
, NULL
, NULL
, FALSE
, 2, &pin
);
622 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
624 /* Test the unconnected flag. */
626 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
627 PINDIR_OUTPUT
, NULL
, NULL
, TRUE
, 0, &pin
);
628 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
629 ok(pin
== &filter1
.source2
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
632 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
633 PINDIR_OUTPUT
, NULL
, NULL
, TRUE
, 1, &pin
);
634 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
636 /* Test categories. */
638 filter1
.source1
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
639 filter1
.source1
.category
= PIN_CATEGORY_CAPTURE
;
641 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
642 PINDIR_OUTPUT
, &PIN_CATEGORY_CAPTURE
, NULL
, FALSE
, 0, &pin
);
643 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
644 ok(pin
== &filter1
.source1
.pin
.pin
.IPin_iface
, "Got wrong pin.\n");
647 hr
= ICaptureGraphBuilder2_FindPin(capture_graph
, (IUnknown
*)&filter1
.filter
.IBaseFilter_iface
,
648 PINDIR_OUTPUT
, &PIN_CATEGORY_CAPTURE
, NULL
, FALSE
, 1, &pin
);
649 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
651 ref
= ICaptureGraphBuilder2_Release(capture_graph
);
652 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
653 ref
= IGraphBuilder_Release(graph
);
654 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
655 ref
= IBaseFilter_Release(&filter1
.filter
.IBaseFilter_iface
);
656 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
657 ref
= IBaseFilter_Release(&filter2
.filter
.IBaseFilter_iface
);
658 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
661 static void disconnect_pins(IGraphBuilder
*graph
, struct testsource
*pin
)
664 hr
= IGraphBuilder_Disconnect(graph
, pin
->pin
.pin
.peer
);
665 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
666 hr
= IGraphBuilder_Disconnect(graph
, &pin
->pin
.pin
.IPin_iface
);
667 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
670 static void check_smart_tee_pin_(int line
, IPin
*pin
, const WCHAR
*name
)
675 IPin_QueryPinInfo(pin
, &info
);
676 ok_(__FILE__
, line
)(!wcscmp(info
.achName
, name
), "Got name %s.\n", debugstr_w(info
.achName
));
677 IBaseFilter_GetClassID(info
.pFilter
, &clsid
);
678 ok_(__FILE__
, line
)(IsEqualGUID(&clsid
, &CLSID_SmartTee
), "Got CLSID %s.\n", debugstr_guid(&clsid
));
679 IBaseFilter_Release(info
.pFilter
);
681 #define check_smart_tee_pin(pin, name) check_smart_tee_pin_(__LINE__, pin, name)
683 static void test_render_stream(void)
685 static const GUID source_type
= {0x1111};
686 static const GUID sink1_type
= {0x8888};
687 static const GUID bad_type
= {0x4444};
689 ICaptureGraphBuilder2
*capture_graph
= create_capture_graph();
690 struct testfilter source
, transform
, sink
, identity
;
691 IGraphBuilder
*graph
;
695 CoCreateInstance(&CLSID_FilterGraph
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IGraphBuilder
, (void **)&graph
);
696 hr
= ICaptureGraphBuilder2_SetFiltergraph(capture_graph
, graph
);
697 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
699 testfilter_init(&source
);
700 testfilter_init(&transform
);
701 testfilter_init(&sink
);
702 testfilter_init(&identity
);
703 IGraphBuilder_AddFilter(graph
, &source
.filter
.IBaseFilter_iface
, L
"source");
704 IGraphBuilder_AddFilter(graph
, &transform
.filter
.IBaseFilter_iface
, L
"transform");
705 IGraphBuilder_AddFilter(graph
, &sink
.filter
.IBaseFilter_iface
, L
"sink");
707 source
.source_type
= source_type
;
708 transform
.sink_type
= &source_type
;
709 transform
.source_type
= sink1_type
;
710 sink
.sink_type
= &sink1_type
;
712 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
713 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
714 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
715 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
716 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
717 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
718 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
720 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
721 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
722 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
723 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
724 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
725 todo_wine
ok(source
.source2
.pin
.pin
.peer
== &transform
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
726 todo_wine
ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
728 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
729 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
730 todo_wine
ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
732 todo_wine
disconnect_pins(graph
, &source
.source2
);
733 todo_wine
disconnect_pins(graph
, &transform
.source2
);
735 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
736 (IUnknown
*)&transform
.source2
.pin
.pin
.IPin_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
737 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
738 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
739 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
740 ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
741 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
743 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
744 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
745 ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
747 disconnect_pins(graph
, &transform
.source2
);
749 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
750 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &transform
.filter
.IBaseFilter_iface
);
751 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
752 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
753 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
754 todo_wine
ok(source
.source2
.pin
.pin
.peer
== &transform
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
756 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
757 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
758 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
759 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
760 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
761 todo_wine
ok(source
.source2
.pin
.pin
.peer
== &transform
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
762 todo_wine
ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
763 todo_wine
disconnect_pins(graph
, &source
.source2
);
764 todo_wine
disconnect_pins(graph
, &transform
.source2
);
766 /* Test from a source pin. */
767 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
768 (IUnknown
*)&source
.source1
.pin
.pin
.IPin_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
769 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
770 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
771 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
772 todo_wine
ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
773 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
774 todo_wine
disconnect_pins(graph
, &transform
.source2
);
776 /* Only the first eligible source is tried. */
777 source
.source_type
= bad_type
;
778 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
779 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
780 ok(hr
== VFW_E_CANNOT_CONNECT
, "Got hr %#x.\n", hr
);
781 source
.source_type
= source_type
;
783 disconnect_pins(graph
, &transform
.source1
);
784 disconnect_pins(graph
, &source
.source1
);
786 /* Test intermediate filters. */
788 IGraphBuilder_AddFilter(graph
, &identity
.filter
.IBaseFilter_iface
, L
"identity");
789 identity
.source_type
= source_type
;
790 identity
.sink_type
= &source_type
;
791 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
792 (IUnknown
*)&source
.filter
.IBaseFilter_iface
,
793 &identity
.filter
.IBaseFilter_iface
, &sink
.filter
.IBaseFilter_iface
);
794 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
795 ok(source
.source1
.pin
.pin
.peer
== &identity
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
796 ok(identity
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
797 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
798 disconnect_pins(graph
, &source
.source1
);
799 disconnect_pins(graph
, &identity
.source1
);
800 disconnect_pins(graph
, &transform
.source1
);
802 identity
.sink_type
= &bad_type
;
803 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
804 (IUnknown
*)&source
.filter
.IBaseFilter_iface
,
805 &identity
.filter
.IBaseFilter_iface
, &sink
.filter
.IBaseFilter_iface
);
806 todo_wine
ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
807 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
808 ok(!identity
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
809 ok(!transform
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
811 identity
.source_type
= sink1_type
;
812 identity
.sink_type
= &sink1_type
;
813 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
814 (IUnknown
*)&source
.filter
.IBaseFilter_iface
,
815 &identity
.filter
.IBaseFilter_iface
, &sink
.filter
.IBaseFilter_iface
);
816 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
817 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
818 ok(transform
.source1
.pin
.pin
.peer
== &identity
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
819 ok(identity
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
820 disconnect_pins(graph
, &source
.source1
);
821 disconnect_pins(graph
, &transform
.source1
);
822 disconnect_pins(graph
, &identity
.source1
);
824 identity
.source_type
= bad_type
;
825 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, NULL
,
826 (IUnknown
*)&source
.filter
.IBaseFilter_iface
,
827 &identity
.filter
.IBaseFilter_iface
, &sink
.filter
.IBaseFilter_iface
);
828 ok(hr
== VFW_E_CANNOT_CONNECT
, "Got hr %#x.\n", hr
);
829 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
830 ok(transform
.source1
.pin
.pin
.peer
== &identity
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
831 ok(!identity
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
832 disconnect_pins(graph
, &source
.source1
);
833 disconnect_pins(graph
, &transform
.source1
);
835 /* Test media types. */
837 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, &bad_type
,
838 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
839 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
840 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
841 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
842 ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
843 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
845 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, &sink1_type
,
846 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
847 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
848 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
849 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
850 ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
851 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
853 identity
.source_type
= sink1_type
;
854 identity
.sink_type
= &sink1_type
;
855 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, &sink1_type
,
856 (IUnknown
*)&source
.filter
.IBaseFilter_iface
,
857 &identity
.filter
.IBaseFilter_iface
, &sink
.filter
.IBaseFilter_iface
);
858 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
859 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
860 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
861 ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
862 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
864 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, &source_type
,
865 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
866 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
867 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
868 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
869 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
870 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
872 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, NULL
, &sink1_type
,
873 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
874 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
875 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
876 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
877 ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
878 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
880 disconnect_pins(graph
, &source
.source1
);
881 disconnect_pins(graph
, &transform
.source1
);
882 disconnect_pins(graph
, &transform
.source2
);
884 /* Test categories. */
886 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
887 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
888 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
889 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
890 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
891 ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
892 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
894 source
.source1
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
895 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
896 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
897 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
898 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
899 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
900 ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
901 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
903 source
.source1
.category
= PIN_CATEGORY_CC
;
904 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
905 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
906 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
907 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
908 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
909 ok(!source
.source2
.pin
.pin
.peer
, "Pin should not be connected.\n");
910 ok(!sink
.sink2
.pin
.pin
.peer
, "Pin should not be connected.\n");
912 disconnect_pins(graph
, &transform
.source1
);
914 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
915 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
916 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
917 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
918 ok(!transform
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
920 transform
.source1
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
921 transform
.source1
.category
= PIN_CATEGORY_CC
;
922 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
923 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
924 todo_wine
ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
925 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
926 todo_wine
ok(!transform
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
927 todo_wine
ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
929 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
930 (IUnknown
*)&source
.source1
.pin
.pin
.IPin_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
931 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
933 disconnect_pins(graph
, &source
.source1
);
935 /* Test the CAPTURE and PREVIEW categories. */
937 source
.source1
.IKsPropertySet_iface
.lpVtbl
= transform
.source1
.IKsPropertySet_iface
.lpVtbl
= NULL
;
938 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
939 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
940 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
941 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
942 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
943 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
945 source
.source1
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
946 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
947 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
948 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
949 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
950 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
951 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
953 source
.source1
.category
= PIN_CATEGORY_PREVIEW
;
954 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
955 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
956 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
957 ok(!source
.source1
.pin
.pin
.peer
, "Pin should not be connected.\n");
958 todo_wine
ok(!sink
.sink1
.pin
.pin
.peer
, "Pin should not be connected.\n");
960 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
961 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
962 todo_wine
ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
963 todo_wine
ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
964 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
965 disconnect_pins(graph
, &transform
.source1
);
967 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
968 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
969 todo_wine
ok(hr
== E_FAIL
, "Got hr %#x.\n", hr
);
970 todo_wine
disconnect_pins(graph
, &source
.source1
);
972 source
.source1
.category
= PIN_CATEGORY_CAPTURE
;
973 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
974 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
975 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
976 check_smart_tee_pin(source
.source1
.pin
.pin
.peer
, L
"Input");
977 check_smart_tee_pin(transform
.sink1
.pin
.pin
.peer
, L
"Capture");
978 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
980 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
981 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
982 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
984 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
985 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
986 ok(hr
== VFW_S_NOPREVIEWPIN
, "Got hr %#x.\n", hr
);
987 check_smart_tee_pin(source
.source1
.pin
.pin
.peer
, L
"Input");
988 check_smart_tee_pin(transform
.sink1
.pin
.pin
.peer
, L
"Capture");
989 check_smart_tee_pin(transform
.sink2
.pin
.pin
.peer
, L
"Preview");
990 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
991 ok(transform
.source2
.pin
.pin
.peer
== &sink
.sink2
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
993 disconnect_pins(graph
, &source
.source1
);
994 IGraphBuilder_RemoveFilter(graph
, &transform
.filter
.IBaseFilter_iface
);
995 IGraphBuilder_AddFilter(graph
, &transform
.filter
.IBaseFilter_iface
, L
"transform");
997 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CC
, NULL
,
998 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
999 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
1001 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
1002 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1003 ok(hr
== VFW_S_NOPREVIEWPIN
, "Got hr %#x.\n", hr
);
1004 check_smart_tee_pin(source
.source1
.pin
.pin
.peer
, L
"Input");
1005 check_smart_tee_pin(transform
.sink1
.pin
.pin
.peer
, L
"Preview");
1006 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1008 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
1009 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1010 ok(hr
== E_INVALIDARG
, "Got hr %#x.\n", hr
);
1012 disconnect_pins(graph
, &source
.source1
);
1013 IGraphBuilder_RemoveFilter(graph
, &transform
.filter
.IBaseFilter_iface
);
1014 IGraphBuilder_AddFilter(graph
, &transform
.filter
.IBaseFilter_iface
, L
"transform");
1016 /* Test from the pin. */
1018 source
.source1
.category
= PIN_CATEGORY_CAPTURE
;
1019 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
1020 (IUnknown
*)&source
.source1
.pin
.pin
.IPin_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1021 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
1022 check_smart_tee_pin(source
.source1
.pin
.pin
.peer
, L
"Input");
1023 check_smart_tee_pin(transform
.sink1
.pin
.pin
.peer
, L
"Capture");
1024 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1025 disconnect_pins(graph
, &source
.source1
);
1026 IGraphBuilder_RemoveFilter(graph
, &transform
.filter
.IBaseFilter_iface
);
1027 IGraphBuilder_AddFilter(graph
, &transform
.filter
.IBaseFilter_iface
, L
"transform");
1029 /* Test when both CAPTURE and PREVIEW are available. */
1031 source
.source2
.IKsPropertySet_iface
.lpVtbl
= &property_set_vtbl
;
1032 source
.source2
.category
= PIN_CATEGORY_PREVIEW
;
1034 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
1035 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1036 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
1037 ok(source
.source1
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1038 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1039 disconnect_pins(graph
, &source
.source1
);
1040 disconnect_pins(graph
, &transform
.source1
);
1042 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_PREVIEW
, NULL
,
1043 (IUnknown
*)&source
.filter
.IBaseFilter_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1044 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
1045 ok(source
.source2
.pin
.pin
.peer
== &transform
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1046 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1047 disconnect_pins(graph
, &source
.source2
);
1048 disconnect_pins(graph
, &transform
.source1
);
1050 hr
= ICaptureGraphBuilder2_RenderStream(capture_graph
, &PIN_CATEGORY_CAPTURE
, NULL
,
1051 (IUnknown
*)&source
.source1
.pin
.pin
.IPin_iface
, NULL
, &sink
.filter
.IBaseFilter_iface
);
1052 ok(hr
== S_OK
, "Got hr %#x.\n", hr
);
1053 check_smart_tee_pin(source
.source1
.pin
.pin
.peer
, L
"Input");
1054 check_smart_tee_pin(transform
.sink1
.pin
.pin
.peer
, L
"Capture");
1055 ok(transform
.source1
.pin
.pin
.peer
== &sink
.sink1
.pin
.pin
.IPin_iface
, "Got wrong connection.\n");
1056 disconnect_pins(graph
, &source
.source1
);
1057 disconnect_pins(graph
, &transform
.source1
);
1059 ref
= ICaptureGraphBuilder2_Release(capture_graph
);
1060 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
1061 ref
= IGraphBuilder_Release(graph
);
1062 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
1063 ref
= IBaseFilter_Release(&source
.filter
.IBaseFilter_iface
);
1064 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
1065 ref
= IBaseFilter_Release(&transform
.filter
.IBaseFilter_iface
);
1066 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
1067 ref
= IBaseFilter_Release(&sink
.filter
.IBaseFilter_iface
);
1068 ok(!ref
, "Got outstanding refcount %d.\n", ref
);
1071 START_TEST(capturegraph
)
1073 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
1075 test_find_interface();
1077 test_render_stream();