qcap/tests: Remove unused enumMediaTypes in test_smart_tee_filter() (Coverity).
[wine.git] / dlls / qcap / tests / smartteefilter.c
blob1364beef16107f959695b5cbe28814a4c9041e03
1 /*
2 * Smart tee filter unit tests
4 * Copyright 2015 Damjan Jovanovic
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
22 #include "dshow.h"
23 #include "wine/strmbase.h"
24 #include "wine/test.h"
26 static const WCHAR sink_id[] = {'I','n','p','u','t',0};
27 static const WCHAR capture_id[] = {'C','a','p','t','u','r','e',0};
28 static const WCHAR preview_id[] = {'P','r','e','v','i','e','w',0};
30 static HANDLE event;
32 static IBaseFilter *create_smart_tee(void)
34 IBaseFilter *filter = NULL;
35 HRESULT hr = CoCreateInstance(&CLSID_SmartTee, NULL, CLSCTX_INPROC_SERVER,
36 &IID_IBaseFilter, (void **)&filter);
37 ok(hr == S_OK, "Got hr %#x.\n", hr);
38 return filter;
41 static ULONG get_refcount(void *iface)
43 IUnknown *unknown = iface;
44 IUnknown_AddRef(unknown);
45 return IUnknown_Release(unknown);
48 static inline BOOL compare_media_types(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b)
50 return !memcmp(a, b, offsetof(AM_MEDIA_TYPE, pbFormat))
51 && !memcmp(a->pbFormat, b->pbFormat, a->cbFormat);
54 #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c)
55 static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported)
57 IUnknown *iface = iface_ptr;
58 HRESULT hr, expected_hr;
59 IUnknown *unk;
61 expected_hr = supported ? S_OK : E_NOINTERFACE;
63 hr = IUnknown_QueryInterface(iface, iid, (void **)&unk);
64 ok_(__FILE__, line)(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
65 if (SUCCEEDED(hr))
66 IUnknown_Release(unk);
69 static void test_interfaces(void)
71 IBaseFilter *filter = create_smart_tee();
72 ULONG ref;
73 IPin *pin;
75 check_interface(filter, &IID_IBaseFilter, TRUE);
76 check_interface(filter, &IID_IMediaFilter, TRUE);
77 check_interface(filter, &IID_IPersist, TRUE);
78 check_interface(filter, &IID_IUnknown, TRUE);
80 check_interface(filter, &IID_IAMFilterMiscFlags, FALSE);
81 check_interface(filter, &IID_IBasicAudio, FALSE);
82 check_interface(filter, &IID_IBasicVideo, FALSE);
83 check_interface(filter, &IID_IKsPropertySet, FALSE);
84 check_interface(filter, &IID_IMediaPosition, FALSE);
85 check_interface(filter, &IID_IMediaSeeking, FALSE);
86 check_interface(filter, &IID_IPersistPropertyBag, FALSE);
87 check_interface(filter, &IID_IPin, FALSE);
88 check_interface(filter, &IID_IQualityControl, FALSE);
89 check_interface(filter, &IID_IQualProp, FALSE);
90 check_interface(filter, &IID_IReferenceClock, FALSE);
91 check_interface(filter, &IID_IVideoWindow, FALSE);
93 IBaseFilter_FindPin(filter, sink_id, &pin);
95 check_interface(pin, &IID_IMemInputPin, TRUE);
96 check_interface(pin, &IID_IPin, TRUE);
97 todo_wine check_interface(pin, &IID_IQualityControl, TRUE);
98 check_interface(pin, &IID_IUnknown, TRUE);
100 check_interface(pin, &IID_IAMStreamConfig, FALSE);
101 check_interface(pin, &IID_IAMStreamControl, FALSE);
102 check_interface(pin, &IID_IKsPropertySet, FALSE);
103 check_interface(pin, &IID_IMediaPosition, FALSE);
104 check_interface(pin, &IID_IMediaSeeking, FALSE);
105 check_interface(pin, &IID_IPropertyBag, FALSE);
107 IPin_Release(pin);
109 IBaseFilter_FindPin(filter, capture_id, &pin);
111 todo_wine check_interface(pin, &IID_IAMStreamControl, TRUE);
112 check_interface(pin, &IID_IPin, TRUE);
113 todo_wine check_interface(pin, &IID_IQualityControl, TRUE);
114 check_interface(pin, &IID_IUnknown, TRUE);
116 check_interface(pin, &IID_IAMStreamConfig, FALSE);
117 check_interface(pin, &IID_IAsyncReader, FALSE);
118 check_interface(pin, &IID_IKsPropertySet, FALSE);
119 check_interface(pin, &IID_IMediaPosition, FALSE);
120 check_interface(pin, &IID_IMediaSeeking, FALSE);
121 check_interface(pin, &IID_IPropertyBag, FALSE);
123 IPin_Release(pin);
125 IBaseFilter_FindPin(filter, preview_id, &pin);
127 todo_wine check_interface(pin, &IID_IAMStreamControl, TRUE);
128 check_interface(pin, &IID_IPin, TRUE);
129 todo_wine check_interface(pin, &IID_IQualityControl, TRUE);
130 check_interface(pin, &IID_IUnknown, TRUE);
132 check_interface(pin, &IID_IAMStreamConfig, FALSE);
133 check_interface(pin, &IID_IAsyncReader, FALSE);
134 check_interface(pin, &IID_IKsPropertySet, FALSE);
135 check_interface(pin, &IID_IMediaPosition, FALSE);
136 check_interface(pin, &IID_IMediaSeeking, FALSE);
137 check_interface(pin, &IID_IPropertyBag, FALSE);
139 IPin_Release(pin);
141 ref = IBaseFilter_Release(filter);
142 ok(!ref, "Got unexpected refcount %d.\n", ref);
145 static const GUID test_iid = {0x33333333};
146 static LONG outer_ref = 1;
148 static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out)
150 if (IsEqualGUID(iid, &IID_IUnknown)
151 || IsEqualGUID(iid, &IID_IBaseFilter)
152 || IsEqualGUID(iid, &test_iid))
154 *out = (IUnknown *)0xdeadbeef;
155 return S_OK;
157 ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid));
158 return E_NOINTERFACE;
161 static ULONG WINAPI outer_AddRef(IUnknown *iface)
163 return InterlockedIncrement(&outer_ref);
166 static ULONG WINAPI outer_Release(IUnknown *iface)
168 return InterlockedDecrement(&outer_ref);
171 static const IUnknownVtbl outer_vtbl =
173 outer_QueryInterface,
174 outer_AddRef,
175 outer_Release,
178 static IUnknown test_outer = {&outer_vtbl};
180 static void test_aggregation(void)
182 IBaseFilter *filter, *filter2;
183 IUnknown *unk, *unk2;
184 HRESULT hr;
185 ULONG ref;
187 filter = (IBaseFilter *)0xdeadbeef;
188 hr = CoCreateInstance(&CLSID_SmartTee, &test_outer, CLSCTX_INPROC_SERVER,
189 &IID_IBaseFilter, (void **)&filter);
190 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
191 ok(!filter, "Got interface %p.\n", filter);
193 hr = CoCreateInstance(&CLSID_SmartTee, &test_outer, CLSCTX_INPROC_SERVER,
194 &IID_IUnknown, (void **)&unk);
195 ok(hr == S_OK, "Got hr %#x.\n", hr);
196 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
197 ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n");
198 ref = get_refcount(unk);
199 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
201 ref = IUnknown_AddRef(unk);
202 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
203 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
205 ref = IUnknown_Release(unk);
206 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
207 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
209 hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2);
210 ok(hr == S_OK, "Got hr %#x.\n", hr);
211 ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2);
212 IUnknown_Release(unk2);
214 hr = IUnknown_QueryInterface(unk, &IID_IBaseFilter, (void **)&filter);
215 ok(hr == S_OK, "Got hr %#x.\n", hr);
217 hr = IBaseFilter_QueryInterface(filter, &IID_IUnknown, (void **)&unk2);
218 ok(hr == S_OK, "Got hr %#x.\n", hr);
219 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
221 hr = IBaseFilter_QueryInterface(filter, &IID_IBaseFilter, (void **)&filter2);
222 ok(hr == S_OK, "Got hr %#x.\n", hr);
223 ok(filter2 == (IBaseFilter *)0xdeadbeef, "Got unexpected IBaseFilter %p.\n", filter2);
225 hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2);
226 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
227 ok(!unk2, "Got unexpected IUnknown %p.\n", unk2);
229 hr = IBaseFilter_QueryInterface(filter, &test_iid, (void **)&unk2);
230 ok(hr == S_OK, "Got hr %#x.\n", hr);
231 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
233 IBaseFilter_Release(filter);
234 ref = IUnknown_Release(unk);
235 ok(!ref, "Got unexpected refcount %d.\n", ref);
236 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
239 static void test_enum_pins(void)
241 IBaseFilter *filter = create_smart_tee();
242 IEnumPins *enum1, *enum2;
243 ULONG count, ref;
244 IPin *pins[4];
245 HRESULT hr;
247 ref = get_refcount(filter);
248 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
250 hr = IBaseFilter_EnumPins(filter, NULL);
251 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
253 hr = IBaseFilter_EnumPins(filter, &enum1);
254 ok(hr == S_OK, "Got hr %#x.\n", hr);
255 ref = get_refcount(filter);
256 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
257 ref = get_refcount(enum1);
258 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
260 hr = IEnumPins_Next(enum1, 1, NULL, NULL);
261 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
263 hr = IEnumPins_Next(enum1, 1, pins, NULL);
264 ok(hr == S_OK, "Got hr %#x.\n", hr);
265 ref = get_refcount(filter);
266 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
267 ref = get_refcount(pins[0]);
268 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
269 ref = get_refcount(enum1);
270 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
271 IPin_Release(pins[0]);
272 ref = get_refcount(filter);
273 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
275 hr = IEnumPins_Next(enum1, 1, pins, NULL);
276 ok(hr == S_OK, "Got hr %#x.\n", hr);
277 ref = get_refcount(filter);
278 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
279 ref = get_refcount(pins[0]);
280 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
281 ref = get_refcount(enum1);
282 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
283 IPin_Release(pins[0]);
284 ref = get_refcount(filter);
285 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
287 hr = IEnumPins_Next(enum1, 1, pins, NULL);
288 ok(hr == S_OK, "Got hr %#x.\n", hr);
289 ref = get_refcount(filter);
290 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
291 ref = get_refcount(pins[0]);
292 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
293 ref = get_refcount(enum1);
294 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
295 IPin_Release(pins[0]);
296 ref = get_refcount(filter);
297 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
299 hr = IEnumPins_Next(enum1, 1, pins, NULL);
300 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
302 hr = IEnumPins_Reset(enum1);
303 ok(hr == S_OK, "Got hr %#x.\n", hr);
305 hr = IEnumPins_Next(enum1, 1, pins, &count);
306 ok(hr == S_OK, "Got hr %#x.\n", hr);
307 ok(count == 1, "Got count %u.\n", count);
308 IPin_Release(pins[0]);
310 hr = IEnumPins_Next(enum1, 1, pins, &count);
311 ok(hr == S_OK, "Got hr %#x.\n", hr);
312 ok(count == 1, "Got count %u.\n", count);
313 IPin_Release(pins[0]);
315 hr = IEnumPins_Next(enum1, 1, pins, &count);
316 ok(hr == S_OK, "Got hr %#x.\n", hr);
317 ok(count == 1, "Got count %u.\n", count);
318 IPin_Release(pins[0]);
320 hr = IEnumPins_Next(enum1, 1, pins, &count);
321 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
322 ok(!count, "Got count %u.\n", count);
324 hr = IEnumPins_Reset(enum1);
325 ok(hr == S_OK, "Got hr %#x.\n", hr);
327 hr = IEnumPins_Next(enum1, 2, pins, NULL);
328 ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
330 hr = IEnumPins_Next(enum1, 2, pins, &count);
331 ok(hr == S_OK, "Got hr %#x.\n", hr);
332 ok(count == 2, "Got count %u.\n", count);
333 IPin_Release(pins[0]);
334 IPin_Release(pins[1]);
336 hr = IEnumPins_Next(enum1, 2, pins, &count);
337 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
338 ok(count == 1, "Got count %u.\n", count);
339 IPin_Release(pins[0]);
341 hr = IEnumPins_Reset(enum1);
342 ok(hr == S_OK, "Got hr %#x.\n", hr);
344 hr = IEnumPins_Next(enum1, 4, pins, &count);
345 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
346 ok(count == 3, "Got count %u.\n", count);
347 IPin_Release(pins[0]);
348 IPin_Release(pins[1]);
349 IPin_Release(pins[2]);
351 hr = IEnumPins_Reset(enum1);
352 ok(hr == S_OK, "Got hr %#x.\n", hr);
354 hr = IEnumPins_Clone(enum1, &enum2);
355 ok(hr == S_OK, "Got hr %#x.\n", hr);
357 hr = IEnumPins_Skip(enum1, 4);
358 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
360 hr = IEnumPins_Skip(enum1, 3);
361 ok(hr == S_OK, "Got hr %#x.\n", hr);
363 hr = IEnumPins_Skip(enum1, 1);
364 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
366 hr = IEnumPins_Next(enum1, 1, pins, NULL);
367 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
369 hr = IEnumPins_Next(enum2, 1, pins, NULL);
370 ok(hr == S_OK, "Got hr %#x.\n", hr);
371 IPin_Release(pins[0]);
373 IEnumPins_Release(enum2);
374 IEnumPins_Release(enum1);
375 ref = IBaseFilter_Release(filter);
376 ok(!ref, "Got outstanding refcount %d.\n", ref);
379 static void test_find_pin(void)
381 IBaseFilter *filter = create_smart_tee();
382 IEnumPins *enum_pins;
383 IPin *pin, *pin2;
384 HRESULT hr;
385 ULONG ref;
387 hr = IBaseFilter_EnumPins(filter, &enum_pins);
388 ok(hr == S_OK, "Got hr %#x.\n", hr);
390 hr = IBaseFilter_FindPin(filter, sink_id, &pin);
391 ok(hr == S_OK, "Got hr %#x.\n", hr);
392 hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL);
393 ok(hr == S_OK, "Got hr %#x.\n", hr);
394 ok(pin2 == pin, "Expected pin %p, got %p.\n", pin, pin2);
395 IPin_Release(pin2);
396 IPin_Release(pin);
398 hr = IBaseFilter_FindPin(filter, capture_id, &pin);
399 ok(hr == S_OK, "Got hr %#x.\n", hr);
400 hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL);
401 ok(hr == S_OK, "Got hr %#x.\n", hr);
402 ok(pin2 == pin, "Expected pin %p, got %p.\n", pin, pin2);
403 IPin_Release(pin2);
404 IPin_Release(pin);
406 hr = IBaseFilter_FindPin(filter, preview_id, &pin);
407 ok(hr == S_OK, "Got hr %#x.\n", hr);
408 hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL);
409 ok(hr == S_OK, "Got hr %#x.\n", hr);
410 ok(pin2 == pin, "Expected pin %p, got %p.\n", pin, pin2);
411 IPin_Release(pin2);
412 IPin_Release(pin);
414 IEnumPins_Release(enum_pins);
415 ref = IBaseFilter_Release(filter);
416 ok(!ref, "Got outstanding refcount %d.\n", ref);
419 static void test_pin_info(void)
421 IBaseFilter *filter = create_smart_tee();
422 PIN_DIRECTION dir;
423 PIN_INFO info;
424 ULONG count;
425 HRESULT hr;
426 WCHAR *id;
427 ULONG ref;
428 IPin *pin;
430 hr = IBaseFilter_FindPin(filter, sink_id, &pin);
431 ok(hr == S_OK, "Got hr %#x.\n", hr);
432 ref = get_refcount(filter);
433 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
434 ref = get_refcount(pin);
435 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
437 hr = IPin_QueryPinInfo(pin, &info);
438 ok(hr == S_OK, "Got hr %#x.\n", hr);
439 ok(info.pFilter == filter, "Expected filter %p, got %p.\n", filter, info.pFilter);
440 ok(info.dir == PINDIR_INPUT, "Got direction %d.\n", info.dir);
441 ok(!lstrcmpW(info.achName, sink_id), "Got name %s.\n", wine_dbgstr_w(info.achName));
442 ref = get_refcount(filter);
443 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
444 ref = get_refcount(pin);
445 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
446 IBaseFilter_Release(info.pFilter);
448 hr = IPin_QueryDirection(pin, &dir);
449 ok(hr == S_OK, "Got hr %#x.\n", hr);
450 ok(dir == PINDIR_INPUT, "Got direction %d.\n", dir);
452 hr = IPin_QueryId(pin, &id);
453 ok(hr == S_OK, "Got hr %#x.\n", hr);
454 ok(!lstrcmpW(id, sink_id), "Got id %s.\n", wine_dbgstr_w(id));
455 CoTaskMemFree(id);
457 hr = IPin_QueryInternalConnections(pin, NULL, &count);
458 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
460 IPin_Release(pin);
462 hr = IBaseFilter_FindPin(filter, capture_id, &pin);
463 ok(hr == S_OK, "Got hr %#x.\n", hr);
465 hr = IPin_QueryPinInfo(pin, &info);
466 ok(hr == S_OK, "Got hr %#x.\n", hr);
467 ok(info.pFilter == filter, "Expected filter %p, got %p.\n", filter, info.pFilter);
468 ok(info.dir == PINDIR_OUTPUT, "Got direction %d.\n", info.dir);
469 ok(!lstrcmpW(info.achName, capture_id), "Got name %s.\n", wine_dbgstr_w(info.achName));
470 ref = get_refcount(filter);
471 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
472 ref = get_refcount(pin);
473 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
474 IBaseFilter_Release(info.pFilter);
476 hr = IPin_QueryDirection(pin, &dir);
477 ok(hr == S_OK, "Got hr %#x.\n", hr);
478 ok(dir == PINDIR_OUTPUT, "Got direction %d.\n", dir);
480 hr = IPin_QueryId(pin, &id);
481 ok(hr == S_OK, "Got hr %#x.\n", hr);
482 ok(!lstrcmpW(id, capture_id), "Got id %s.\n", wine_dbgstr_w(id));
483 CoTaskMemFree(id);
485 hr = IPin_QueryInternalConnections(pin, NULL, &count);
486 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
488 IPin_Release(pin);
490 hr = IBaseFilter_FindPin(filter, preview_id, &pin);
491 ok(hr == S_OK, "Got hr %#x.\n", hr);
493 hr = IPin_QueryPinInfo(pin, &info);
494 ok(hr == S_OK, "Got hr %#x.\n", hr);
495 ok(info.pFilter == filter, "Expected filter %p, got %p.\n", filter, info.pFilter);
496 ok(info.dir == PINDIR_OUTPUT, "Got direction %d.\n", info.dir);
497 ok(!lstrcmpW(info.achName, preview_id), "Got name %s.\n", wine_dbgstr_w(info.achName));
498 ref = get_refcount(filter);
499 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
500 ref = get_refcount(pin);
501 ok(ref == 3, "Got unexpected refcount %d.\n", ref);
502 IBaseFilter_Release(info.pFilter);
504 hr = IPin_QueryDirection(pin, &dir);
505 ok(hr == S_OK, "Got hr %#x.\n", hr);
506 ok(dir == PINDIR_OUTPUT, "Got direction %d.\n", dir);
508 hr = IPin_QueryId(pin, &id);
509 ok(hr == S_OK, "Got hr %#x.\n", hr);
510 ok(!lstrcmpW(id, preview_id), "Got id %s.\n", wine_dbgstr_w(id));
511 CoTaskMemFree(id);
513 hr = IPin_QueryInternalConnections(pin, NULL, &count);
514 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
516 IPin_Release(pin);
518 ref = IBaseFilter_Release(filter);
519 ok(!ref, "Got outstanding refcount %d.\n", ref);
522 static void test_enum_media_types(void)
524 IBaseFilter *filter = create_smart_tee();
525 IEnumMediaTypes *enum1, *enum2;
526 AM_MEDIA_TYPE *mts[2];
527 ULONG ref, count;
528 HRESULT hr;
529 IPin *pin;
531 IBaseFilter_FindPin(filter, sink_id, &pin);
533 hr = IPin_EnumMediaTypes(pin, &enum1);
534 ok(hr == S_OK, "Got hr %#x.\n", hr);
536 hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL);
537 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
539 hr = IEnumMediaTypes_Next(enum1, 1, mts, &count);
540 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
541 ok(!count, "Got count %u.\n", count);
543 hr = IEnumMediaTypes_Reset(enum1);
544 ok(hr == S_OK, "Got hr %#x.\n", hr);
546 hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL);
547 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
549 hr = IEnumMediaTypes_Clone(enum1, &enum2);
550 ok(hr == S_OK, "Got hr %#x.\n", hr);
552 hr = IEnumMediaTypes_Skip(enum1, 1);
553 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
555 hr = IEnumMediaTypes_Next(enum2, 1, mts, NULL);
556 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
558 IEnumMediaTypes_Release(enum1);
559 IEnumMediaTypes_Release(enum2);
560 IPin_Release(pin);
562 ref = IBaseFilter_Release(filter);
563 ok(!ref, "Got outstanding refcount %d.\n", ref);
566 typedef struct {
567 IBaseFilter IBaseFilter_iface;
568 LONG ref;
569 BOOL isCapture;
570 DWORD receiveThreadId;
571 IPin IPin_iface;
572 IMemInputPin IMemInputPin_iface;
573 IMemAllocator *allocator;
574 IBaseFilter *nullRenderer;
575 IPin *nullRendererPin;
576 IMemInputPin *nullRendererMemInputPin;
577 } SinkFilter;
579 typedef struct {
580 IEnumPins IEnumPins_iface;
581 LONG ref;
582 ULONG index;
583 SinkFilter *filter;
584 } SinkEnumPins;
586 static SinkEnumPins* create_SinkEnumPins(SinkFilter *filter);
588 static inline SinkFilter* impl_from_SinkFilter_IBaseFilter(IBaseFilter *iface)
590 return CONTAINING_RECORD(iface, SinkFilter, IBaseFilter_iface);
593 static inline SinkFilter* impl_from_SinkFilter_IPin(IPin *iface)
595 return CONTAINING_RECORD(iface, SinkFilter, IPin_iface);
598 static inline SinkFilter* impl_from_SinkFilter_IMemInputPin(IMemInputPin *iface)
600 return CONTAINING_RECORD(iface, SinkFilter, IMemInputPin_iface);
603 static inline SinkEnumPins* impl_from_SinkFilter_IEnumPins(IEnumPins *iface)
605 return CONTAINING_RECORD(iface, SinkEnumPins, IEnumPins_iface);
608 static HRESULT WINAPI SinkFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv)
610 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
611 if(IsEqualIID(riid, &IID_IUnknown)) {
612 *ppv = &This->IBaseFilter_iface;
613 } else if(IsEqualIID(riid, &IID_IPersist)) {
614 *ppv = &This->IBaseFilter_iface;
615 } else if(IsEqualIID(riid, &IID_IMediaFilter)) {
616 *ppv = &This->IBaseFilter_iface;
617 } else if(IsEqualIID(riid, &IID_IBaseFilter)) {
618 *ppv = &This->IBaseFilter_iface;
619 } else {
620 trace("no interface for %s\n", wine_dbgstr_guid(riid));
621 *ppv = NULL;
622 return E_NOINTERFACE;
624 IUnknown_AddRef((IUnknown*)*ppv);
625 return S_OK;
628 static ULONG WINAPI SinkFilter_AddRef(IBaseFilter *iface)
630 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
631 return InterlockedIncrement(&This->ref);
634 static ULONG WINAPI SinkFilter_Release(IBaseFilter *iface)
636 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
637 ULONG ref = InterlockedDecrement(&This->ref);
638 if(!ref) {
639 if (This->allocator)
640 IMemAllocator_Release(This->allocator);
641 IMemInputPin_Release(This->nullRendererMemInputPin);
642 IPin_Release(This->nullRendererPin);
643 IBaseFilter_Release(This->nullRenderer);
644 CoTaskMemFree(This);
646 return ref;
649 static HRESULT WINAPI SinkFilter_GetClassID(IBaseFilter *iface, CLSID *pClassID)
651 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
652 return IBaseFilter_GetClassID(This->nullRenderer, pClassID);
655 static HRESULT WINAPI SinkFilter_Stop(IBaseFilter *iface)
657 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
658 return IBaseFilter_Stop(This->nullRenderer);
661 static HRESULT WINAPI SinkFilter_Pause(IBaseFilter *iface)
663 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
664 return IBaseFilter_Pause(This->nullRenderer);
667 static HRESULT WINAPI SinkFilter_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
669 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
670 return IBaseFilter_Run(This->nullRenderer, tStart);
673 static HRESULT WINAPI SinkFilter_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *state)
675 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
676 return IBaseFilter_GetState(This->nullRenderer, dwMilliSecsTimeout, state);
679 static HRESULT WINAPI SinkFilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *pClock)
681 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
682 return IBaseFilter_SetSyncSource(This->nullRenderer, pClock);
685 static HRESULT WINAPI SinkFilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **ppClock)
687 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
688 return IBaseFilter_GetSyncSource(This->nullRenderer, ppClock);
691 static HRESULT WINAPI SinkFilter_EnumPins(IBaseFilter *iface, IEnumPins **ppEnum)
693 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
694 SinkEnumPins *sinkEnumPins = create_SinkEnumPins(This);
695 if (sinkEnumPins) {
696 *ppEnum = &sinkEnumPins->IEnumPins_iface;
697 return S_OK;
699 else
700 return E_OUTOFMEMORY;
703 static HRESULT WINAPI SinkFilter_FindPin(IBaseFilter *iface, LPCWSTR id, IPin **ppPin)
705 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
706 HRESULT hr = IBaseFilter_FindPin(This->nullRenderer, id, ppPin);
707 if (SUCCEEDED(hr)) {
708 IPin_Release(*ppPin);
709 *ppPin = &This->IPin_iface;
710 IPin_AddRef(&This->IPin_iface);
712 return hr;
715 static HRESULT WINAPI SinkFilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *pInfo)
717 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
718 return IBaseFilter_QueryFilterInfo(This->nullRenderer, pInfo);
721 static HRESULT WINAPI SinkFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *pGraph, LPCWSTR pName)
723 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
724 return IBaseFilter_JoinFilterGraph(This->nullRenderer, pGraph, pName);
727 static HRESULT WINAPI SinkFilter_QueryVendorInfo(IBaseFilter *iface, LPWSTR *pVendorInfo)
729 SinkFilter *This = impl_from_SinkFilter_IBaseFilter(iface);
730 return IBaseFilter_QueryVendorInfo(This->nullRenderer, pVendorInfo);
733 static const IBaseFilterVtbl SinkFilterVtbl = {
734 SinkFilter_QueryInterface,
735 SinkFilter_AddRef,
736 SinkFilter_Release,
737 SinkFilter_GetClassID,
738 SinkFilter_Stop,
739 SinkFilter_Pause,
740 SinkFilter_Run,
741 SinkFilter_GetState,
742 SinkFilter_SetSyncSource,
743 SinkFilter_GetSyncSource,
744 SinkFilter_EnumPins,
745 SinkFilter_FindPin,
746 SinkFilter_QueryFilterInfo,
747 SinkFilter_JoinFilterGraph,
748 SinkFilter_QueryVendorInfo
751 static HRESULT WINAPI SinkEnumPins_QueryInterface(IEnumPins *iface, REFIID riid, void **ppv)
753 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
754 if(IsEqualIID(riid, &IID_IUnknown)) {
755 *ppv = &This->IEnumPins_iface;
756 } else if(IsEqualIID(riid, &IID_IEnumPins)) {
757 *ppv = &This->IEnumPins_iface;
758 } else {
759 trace("no interface for %s\n", wine_dbgstr_guid(riid));
760 *ppv = NULL;
761 return E_NOINTERFACE;
763 IUnknown_AddRef((IUnknown*)*ppv);
764 return S_OK;
767 static ULONG WINAPI SinkEnumPins_AddRef(IEnumPins *iface)
769 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
770 return InterlockedIncrement(&This->ref);
773 static ULONG WINAPI SinkEnumPins_Release(IEnumPins *iface)
775 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
776 ULONG ref;
777 ref = InterlockedDecrement(&This->ref);
778 if (ref == 0)
780 IBaseFilter_Release(&This->filter->IBaseFilter_iface);
781 CoTaskMemFree(This);
783 return ref;
786 static HRESULT WINAPI SinkEnumPins_Next(IEnumPins *iface, ULONG cPins, IPin **ppPins, ULONG *pcFetched)
788 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
789 if (!ppPins)
790 return E_POINTER;
791 if (cPins > 1 && !pcFetched)
792 return E_INVALIDARG;
793 if (pcFetched)
794 *pcFetched = 0;
795 if (cPins == 0)
796 return S_OK;
797 if (This->index == 0) {
798 ppPins[0] = &This->filter->IPin_iface;
799 IPin_AddRef(&This->filter->IPin_iface);
800 ++This->index;
801 if (pcFetched)
802 *pcFetched = 1;
803 return S_OK;
805 return S_FALSE;
808 static HRESULT WINAPI SinkEnumPins_Skip(IEnumPins *iface, ULONG cPins)
810 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
811 if (This->index + cPins >= 1)
812 return S_FALSE;
813 This->index += cPins;
814 return S_OK;
817 static HRESULT WINAPI SinkEnumPins_Reset(IEnumPins *iface)
819 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
820 This->index = 0;
821 return S_OK;
824 static HRESULT WINAPI SinkEnumPins_Clone(IEnumPins *iface, IEnumPins **ppEnum)
826 SinkEnumPins *This = impl_from_SinkFilter_IEnumPins(iface);
827 SinkEnumPins *clone = create_SinkEnumPins(This->filter);
828 if (clone == NULL)
829 return E_OUTOFMEMORY;
830 clone->index = This->index;
831 *ppEnum = &clone->IEnumPins_iface;
832 return S_OK;
835 static const IEnumPinsVtbl SinkEnumPinsVtbl = {
836 SinkEnumPins_QueryInterface,
837 SinkEnumPins_AddRef,
838 SinkEnumPins_Release,
839 SinkEnumPins_Next,
840 SinkEnumPins_Skip,
841 SinkEnumPins_Reset,
842 SinkEnumPins_Clone
845 static SinkEnumPins* create_SinkEnumPins(SinkFilter *filter)
847 SinkEnumPins *This;
848 This = CoTaskMemAlloc(sizeof(*This));
849 if (This == NULL) {
850 return NULL;
852 This->IEnumPins_iface.lpVtbl = &SinkEnumPinsVtbl;
853 This->ref = 1;
854 This->index = 0;
855 This->filter = filter;
856 IBaseFilter_AddRef(&filter->IBaseFilter_iface);
857 return This;
860 static HRESULT WINAPI SinkPin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
862 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
863 if(IsEqualIID(riid, &IID_IUnknown)) {
864 *ppv = &This->IPin_iface;
865 } else if(IsEqualIID(riid, &IID_IPin)) {
866 *ppv = &This->IPin_iface;
867 } else if(IsEqualIID(riid, &IID_IMemInputPin)) {
868 *ppv = &This->IMemInputPin_iface;
869 } else {
870 trace("no interface for %s\n", wine_dbgstr_guid(riid));
871 *ppv = NULL;
872 return E_NOINTERFACE;
874 IUnknown_AddRef((IUnknown*)*ppv);
875 return S_OK;
878 static ULONG WINAPI SinkPin_AddRef(IPin *iface)
880 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
881 return IBaseFilter_AddRef(&This->IBaseFilter_iface);
884 static ULONG WINAPI SinkPin_Release(IPin *iface)
886 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
887 return IBaseFilter_Release(&This->IBaseFilter_iface);
890 static HRESULT WINAPI SinkPin_Connect(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
892 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
893 return IPin_Connect(This->nullRendererPin, pReceivePin, pmt);
896 static HRESULT WINAPI SinkPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *pmt)
898 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
899 return IPin_ReceiveConnection(This->nullRendererPin, connector, pmt);
902 static HRESULT WINAPI SinkPin_Disconnect(IPin *iface)
904 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
905 return IPin_Disconnect(This->nullRendererPin);
908 static HRESULT WINAPI SinkPin_ConnectedTo(IPin *iface, IPin **pPin)
910 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
911 return IPin_ConnectedTo(This->nullRendererPin, pPin);
914 static HRESULT WINAPI SinkPin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *pmt)
916 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
917 return IPin_ConnectionMediaType(This->nullRendererPin, pmt);
920 static HRESULT WINAPI SinkPin_QueryPinInfo(IPin *iface, PIN_INFO *pInfo)
922 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
923 HRESULT hr = IPin_QueryPinInfo(This->nullRendererPin, pInfo);
924 if (SUCCEEDED(hr)) {
925 IBaseFilter_Release(pInfo->pFilter);
926 pInfo->pFilter = &This->IBaseFilter_iface;
927 IBaseFilter_AddRef(&This->IBaseFilter_iface);
929 return hr;
932 static HRESULT WINAPI SinkPin_QueryDirection(IPin *iface, PIN_DIRECTION *pPinDir)
934 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
935 return IPin_QueryDirection(This->nullRendererPin, pPinDir);
938 static HRESULT WINAPI SinkPin_QueryId(IPin *iface, LPWSTR *id)
940 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
941 return IPin_QueryId(This->nullRendererPin, id);
944 static HRESULT WINAPI SinkPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
946 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
947 return IPin_QueryAccept(This->nullRendererPin, pmt);
950 static HRESULT WINAPI SinkPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
952 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
953 return IPin_EnumMediaTypes(This->nullRendererPin, ppEnum);
956 static HRESULT WINAPI SinkPin_QueryInternalConnections(IPin *iface, IPin **apPin, ULONG *nPin)
958 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
959 return IPin_QueryInternalConnections(This->nullRendererPin, apPin, nPin);
962 static HRESULT WINAPI SinkPin_EndOfStream(IPin *iface)
964 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
965 return IPin_EndOfStream(This->nullRendererPin);
968 static HRESULT WINAPI SinkPin_BeginFlush(IPin *iface)
970 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
971 return IPin_BeginFlush(This->nullRendererPin);
974 static HRESULT WINAPI SinkPin_EndFlush(IPin *iface)
976 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
977 return IPin_EndFlush(This->nullRendererPin);
980 static HRESULT WINAPI SinkPin_NewSegment(IPin *iface, REFERENCE_TIME tStart,
981 REFERENCE_TIME tStop, double dRate)
983 SinkFilter *This = impl_from_SinkFilter_IPin(iface);
984 return IPin_NewSegment(This->nullRendererPin, tStart, tStop, dRate);
987 static const IPinVtbl SinkPinVtbl = {
988 SinkPin_QueryInterface,
989 SinkPin_AddRef,
990 SinkPin_Release,
991 SinkPin_Connect,
992 SinkPin_ReceiveConnection,
993 SinkPin_Disconnect,
994 SinkPin_ConnectedTo,
995 SinkPin_ConnectionMediaType,
996 SinkPin_QueryPinInfo,
997 SinkPin_QueryDirection,
998 SinkPin_QueryId,
999 SinkPin_QueryAccept,
1000 SinkPin_EnumMediaTypes,
1001 SinkPin_QueryInternalConnections,
1002 SinkPin_EndOfStream,
1003 SinkPin_BeginFlush,
1004 SinkPin_EndFlush,
1005 SinkPin_NewSegment
1008 static HRESULT WINAPI SinkMemInputPin_QueryInterface(IMemInputPin *iface, REFIID riid, void **ppv)
1010 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1011 return IPin_QueryInterface(&This->IPin_iface, riid, ppv);
1014 static ULONG WINAPI SinkMemInputPin_AddRef(IMemInputPin *iface)
1016 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1017 return IBaseFilter_AddRef(&This->IBaseFilter_iface);
1020 static ULONG WINAPI SinkMemInputPin_Release(IMemInputPin *iface)
1022 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1023 return IBaseFilter_Release(&This->IBaseFilter_iface);
1026 static HRESULT WINAPI SinkMemInputPin_GetAllocator(IMemInputPin *iface, IMemAllocator **ppAllocator)
1028 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1029 ok(0, "SmartTeeFilter never calls IMemInputPin_GetAllocator()\n");
1030 return IMemInputPin_GetAllocator(This->nullRendererMemInputPin, ppAllocator);
1033 static HRESULT WINAPI SinkMemInputPin_NotifyAllocator(IMemInputPin *iface, IMemAllocator *pAllocator,
1034 BOOL bReadOnly)
1036 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1037 This->allocator = pAllocator;
1038 IMemAllocator_AddRef(This->allocator);
1039 ok(bReadOnly, "bReadOnly isn't supposed to be FALSE\n");
1040 return IMemInputPin_NotifyAllocator(This->nullRendererMemInputPin, pAllocator, bReadOnly);
1043 static HRESULT WINAPI SinkMemInputPin_GetAllocatorRequirements(IMemInputPin *iface,
1044 ALLOCATOR_PROPERTIES *pProps)
1046 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1047 ok(0, "SmartTeeFilter never calls IMemInputPin_GetAllocatorRequirements()\n");
1048 return IMemInputPin_GetAllocatorRequirements(This->nullRendererMemInputPin, pProps);
1051 static HRESULT WINAPI SinkMemInputPin_Receive(IMemInputPin *iface, IMediaSample *pSample)
1053 LONG samplesProcessed;
1054 todo_wine ok(0, "SmartTeeFilter never calls IMemInputPin_Receive(), only IMemInputPin_ReceiveMultiple()\n");
1055 return IMemInputPin_ReceiveMultiple(iface, &pSample, 1, &samplesProcessed);
1058 static HRESULT WINAPI SinkMemInputPin_ReceiveMultiple(IMemInputPin *iface, IMediaSample **pSamples,
1059 LONG nSamples, LONG *nSamplesProcessed)
1061 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1062 IMediaSample *pSample;
1063 REFERENCE_TIME startTime, endTime;
1064 HRESULT hr;
1065 ok(nSamples == 1, "expected 1 sample, got %d\n", nSamples);
1066 pSample = pSamples[0];
1067 hr = IMediaSample_GetTime(pSample, &startTime, &endTime);
1068 if (This->isCapture)
1069 ok(SUCCEEDED(hr), "IMediaSample_GetTime() from Capture pin failed, hr=0x%08x\n", hr);
1070 else
1071 ok(hr == VFW_E_SAMPLE_TIME_NOT_SET, "IMediaSample_GetTime() from Preview pin returned hr=0x%08x\n", hr);
1072 This->receiveThreadId = GetCurrentThreadId();
1073 SetEvent(event);
1074 return IMemInputPin_ReceiveMultiple(This->nullRendererMemInputPin, pSamples,
1075 nSamples, nSamplesProcessed);
1078 static HRESULT WINAPI SinkMemInputPin_ReceiveCanBlock(IMemInputPin *iface)
1080 SinkFilter *This = impl_from_SinkFilter_IMemInputPin(iface);
1081 return IMemInputPin_ReceiveCanBlock(This->nullRendererMemInputPin);
1084 static const IMemInputPinVtbl SinkMemInputPinVtbl = {
1085 SinkMemInputPin_QueryInterface,
1086 SinkMemInputPin_AddRef,
1087 SinkMemInputPin_Release,
1088 SinkMemInputPin_GetAllocator,
1089 SinkMemInputPin_NotifyAllocator,
1090 SinkMemInputPin_GetAllocatorRequirements,
1091 SinkMemInputPin_Receive,
1092 SinkMemInputPin_ReceiveMultiple,
1093 SinkMemInputPin_ReceiveCanBlock
1096 static SinkFilter* create_SinkFilter(BOOL isCapture)
1098 SinkFilter *This = NULL;
1099 HRESULT hr;
1100 This = CoTaskMemAlloc(sizeof(*This));
1101 if (This) {
1102 memset(This, 0, sizeof(*This));
1103 This->IBaseFilter_iface.lpVtbl = &SinkFilterVtbl;
1104 This->ref = 1;
1105 This->isCapture = isCapture;
1106 This->IPin_iface.lpVtbl = &SinkPinVtbl;
1107 This->IMemInputPin_iface.lpVtbl = &SinkMemInputPinVtbl;
1108 hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
1109 &IID_IBaseFilter, (LPVOID*)&This->nullRenderer);
1110 if (SUCCEEDED(hr)) {
1111 IEnumPins *enumPins = NULL;
1112 hr = IBaseFilter_EnumPins(This->nullRenderer, &enumPins);
1113 if (SUCCEEDED(hr)) {
1114 hr = IEnumPins_Next(enumPins, 1, &This->nullRendererPin, NULL);
1115 IEnumPins_Release(enumPins);
1116 if (SUCCEEDED(hr)) {
1117 hr = IPin_QueryInterface(This->nullRendererPin, &IID_IMemInputPin,
1118 (LPVOID*)&This->nullRendererMemInputPin);
1119 if (SUCCEEDED(hr))
1120 return This;
1121 IPin_Release(This->nullRendererPin);
1124 IBaseFilter_Release(This->nullRenderer);
1126 CoTaskMemFree(This);
1128 return NULL;
1131 typedef struct {
1132 IBaseFilter IBaseFilter_iface;
1133 LONG ref;
1134 IPin IPin_iface;
1135 IKsPropertySet IKsPropertySet_iface;
1136 CRITICAL_SECTION cs;
1137 FILTER_STATE state;
1138 IReferenceClock *referenceClock;
1139 FILTER_INFO filterInfo;
1140 AM_MEDIA_TYPE mediaType;
1141 VIDEOINFOHEADER videoInfo;
1142 WAVEFORMATEX audioInfo;
1143 IPin *connectedTo;
1144 IMemInputPin *memInputPin;
1145 IMemAllocator *allocator;
1146 DWORD mediaThreadId;
1147 } SourceFilter;
1149 typedef struct {
1150 IEnumPins IEnumPins_iface;
1151 LONG ref;
1152 ULONG index;
1153 SourceFilter *filter;
1154 } SourceEnumPins;
1156 typedef struct {
1157 IEnumMediaTypes IEnumMediaTypes_iface;
1158 LONG ref;
1159 ULONG index;
1160 SourceFilter *filter;
1161 } SourceEnumMediaTypes;
1163 static const WCHAR sourcePinName[] = {'C','a','p','t','u','r','e',0};
1165 static SourceEnumPins* create_SourceEnumPins(SourceFilter *filter);
1166 static SourceEnumMediaTypes* create_SourceEnumMediaTypes(SourceFilter *filter);
1168 static inline SourceFilter* impl_from_SourceFilter_IBaseFilter(IBaseFilter *iface)
1170 return CONTAINING_RECORD(iface, SourceFilter, IBaseFilter_iface);
1173 static inline SourceFilter* impl_from_SourceFilter_IPin(IPin *iface)
1175 return CONTAINING_RECORD(iface, SourceFilter, IPin_iface);
1178 static inline SourceFilter* impl_from_SourceFilter_IKsPropertySet(IKsPropertySet *iface)
1180 return CONTAINING_RECORD(iface, SourceFilter, IKsPropertySet_iface);
1183 static inline SourceEnumPins* impl_from_SourceFilter_IEnumPins(IEnumPins *iface)
1185 return CONTAINING_RECORD(iface, SourceEnumPins, IEnumPins_iface);
1188 static inline SourceEnumMediaTypes* impl_from_SourceFilter_IEnumMediaTypes(IEnumMediaTypes *iface)
1190 return CONTAINING_RECORD(iface, SourceEnumMediaTypes, IEnumMediaTypes_iface);
1193 static HRESULT WINAPI SourceFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv)
1195 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1196 if(IsEqualIID(riid, &IID_IUnknown)) {
1197 *ppv = &This->IBaseFilter_iface;
1198 } else if(IsEqualIID(riid, &IID_IPersist)) {
1199 *ppv = &This->IBaseFilter_iface;
1200 } else if(IsEqualIID(riid, &IID_IMediaFilter)) {
1201 *ppv = &This->IBaseFilter_iface;
1202 } else if(IsEqualIID(riid, &IID_IBaseFilter)) {
1203 *ppv = &This->IBaseFilter_iface;
1204 } else {
1205 trace("no interface for %s\n", wine_dbgstr_guid(riid));
1206 *ppv = NULL;
1207 return E_NOINTERFACE;
1209 IUnknown_AddRef((IUnknown*)*ppv);
1210 return S_OK;
1213 static ULONG WINAPI SourceFilter_AddRef(IBaseFilter *iface)
1215 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1216 return InterlockedIncrement(&This->ref);
1219 static ULONG WINAPI SourceFilter_Release(IBaseFilter *iface)
1221 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1222 ULONG ref = InterlockedDecrement(&This->ref);
1223 if(!ref) {
1224 if (This->referenceClock)
1225 IReferenceClock_Release(This->referenceClock);
1226 if (This->connectedTo)
1227 IPin_Disconnect(&This->IPin_iface);
1228 DeleteCriticalSection(&This->cs);
1229 CoTaskMemFree(This);
1231 return ref;
1234 static HRESULT WINAPI SourceFilter_GetClassID(IBaseFilter *iface, CLSID *pClassID)
1236 *pClassID = CLSID_VfwCapture;
1237 return S_OK;
1240 static HRESULT WINAPI SourceFilter_Stop(IBaseFilter *iface)
1242 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1243 EnterCriticalSection(&This->cs);
1244 IMemAllocator_Decommit(This->allocator);
1245 This->state = State_Stopped;
1246 LeaveCriticalSection(&This->cs);
1247 return S_OK;
1250 static HRESULT WINAPI SourceFilter_Pause(IBaseFilter *iface)
1252 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1253 EnterCriticalSection(&This->cs);
1254 This->state = State_Paused;
1255 LeaveCriticalSection(&This->cs);
1256 return S_OK;
1259 static DWORD WINAPI media_thread(LPVOID param)
1261 SourceFilter *This = (SourceFilter*) param;
1262 HRESULT hr;
1263 IMediaSample *sample = NULL;
1264 REFERENCE_TIME startTime;
1265 REFERENCE_TIME endTime;
1266 BYTE *buffer;
1268 hr = IMemAllocator_GetBuffer(This->allocator, &sample, NULL, NULL, 0);
1269 ok(SUCCEEDED(hr), "IMemAllocator_GetBuffer() failed, hr=0x%08x\n", hr);
1270 if (SUCCEEDED(hr)) {
1271 startTime = 10;
1272 endTime = 20;
1273 hr = IMediaSample_SetTime(sample, &startTime, &endTime);
1274 ok(SUCCEEDED(hr), "IMediaSample_SetTime() failed, hr=0x%08x\n", hr);
1275 hr = IMediaSample_SetMediaType(sample, &This->mediaType);
1276 ok(SUCCEEDED(hr), "IMediaSample_SetMediaType() failed, hr=0x%08x\n", hr);
1278 hr = IMediaSample_GetPointer(sample, &buffer);
1279 ok(SUCCEEDED(hr), "IMediaSample_GetPointer() failed, hr=0x%08x\n", hr);
1280 if (SUCCEEDED(hr)) {
1281 /* 10 by 10 pixel 32 RGB */
1282 int i;
1283 for (i = 0; i < 100; i++)
1284 buffer[4*i] = i;
1287 hr = IMemInputPin_Receive(This->memInputPin, sample);
1288 ok(SUCCEEDED(hr), "delivering sample to SmartTeeFilter's Input pin failed, hr=0x%08x\n", hr);
1290 IMediaSample_Release(sample);
1292 return 0;
1295 static HRESULT WINAPI SourceFilter_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
1297 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1298 HRESULT hr;
1299 EnterCriticalSection(&This->cs);
1300 hr = IMemAllocator_Commit(This->allocator);
1301 if (SUCCEEDED(hr)) {
1302 HANDLE thread = CreateThread(NULL, 0, media_thread, This, 0, &This->mediaThreadId);
1303 ok(thread != NULL, "couldn't create media thread, GetLastError()=%u\n", GetLastError());
1304 if (thread != NULL) {
1305 CloseHandle(thread);
1306 This->state = State_Running;
1307 } else {
1308 IMemAllocator_Decommit(This->allocator);
1309 hr = E_FAIL;
1312 LeaveCriticalSection(&This->cs);
1313 return hr;
1316 static HRESULT WINAPI SourceFilter_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *state)
1318 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1319 EnterCriticalSection(&This->cs);
1320 *state = This->state;
1321 LeaveCriticalSection(&This->cs);
1322 return S_OK;
1325 static HRESULT WINAPI SourceFilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *pClock)
1327 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1328 EnterCriticalSection(&This->cs);
1329 if (This->referenceClock)
1330 IReferenceClock_Release(This->referenceClock);
1331 This->referenceClock = pClock;
1332 if (This->referenceClock)
1333 IReferenceClock_AddRef(This->referenceClock);
1334 LeaveCriticalSection(&This->cs);
1335 return S_OK;
1338 static HRESULT WINAPI SourceFilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **ppClock)
1340 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1341 EnterCriticalSection(&This->cs);
1342 *ppClock = This->referenceClock;
1343 if (This->referenceClock)
1344 IReferenceClock_AddRef(This->referenceClock);
1345 LeaveCriticalSection(&This->cs);
1346 return S_OK;
1349 static HRESULT WINAPI SourceFilter_EnumPins(IBaseFilter *iface, IEnumPins **ppEnum)
1351 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1352 SourceEnumPins *sourceEnumPins = create_SourceEnumPins(This);
1353 if (sourceEnumPins) {
1354 *ppEnum = &sourceEnumPins->IEnumPins_iface;
1355 return S_OK;
1357 else
1358 return E_OUTOFMEMORY;
1361 static HRESULT WINAPI SourceFilter_FindPin(IBaseFilter *iface, LPCWSTR id, IPin **ppPin)
1363 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1364 if (ppPin == NULL)
1365 return E_POINTER;
1366 *ppPin = NULL;
1367 if (lstrcmpW(id, sourcePinName) == 0) {
1368 *ppPin = &This->IPin_iface;
1369 IPin_AddRef(&This->IPin_iface);
1370 return S_OK;
1372 return VFW_E_NOT_FOUND;
1375 static HRESULT WINAPI SourceFilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *pInfo)
1377 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1378 if (!pInfo)
1379 return E_POINTER;
1380 EnterCriticalSection(&This->cs);
1381 *pInfo = This->filterInfo;
1382 if (This->filterInfo.pGraph)
1383 IFilterGraph_AddRef(This->filterInfo.pGraph);
1384 LeaveCriticalSection(&This->cs);
1385 return S_OK;
1388 static HRESULT WINAPI SourceFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *pGraph, LPCWSTR pName)
1390 SourceFilter *This = impl_from_SourceFilter_IBaseFilter(iface);
1391 EnterCriticalSection(&This->cs);
1392 if (pName)
1393 lstrcpyW(This->filterInfo.achName, pName);
1394 else
1395 This->filterInfo.achName[0] = 0;
1396 This->filterInfo.pGraph = pGraph;
1397 LeaveCriticalSection(&This->cs);
1398 return S_OK;
1401 static HRESULT WINAPI SourceFilter_QueryVendorInfo(IBaseFilter *iface, LPWSTR *pVendorInfo)
1403 return E_NOTIMPL;
1406 static const IBaseFilterVtbl SourceFilterVtbl = {
1407 SourceFilter_QueryInterface,
1408 SourceFilter_AddRef,
1409 SourceFilter_Release,
1410 SourceFilter_GetClassID,
1411 SourceFilter_Stop,
1412 SourceFilter_Pause,
1413 SourceFilter_Run,
1414 SourceFilter_GetState,
1415 SourceFilter_SetSyncSource,
1416 SourceFilter_GetSyncSource,
1417 SourceFilter_EnumPins,
1418 SourceFilter_FindPin,
1419 SourceFilter_QueryFilterInfo,
1420 SourceFilter_JoinFilterGraph,
1421 SourceFilter_QueryVendorInfo
1424 static HRESULT WINAPI SourceEnumPins_QueryInterface(IEnumPins *iface, REFIID riid, void **ppv)
1426 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1427 if(IsEqualIID(riid, &IID_IUnknown)) {
1428 *ppv = &This->IEnumPins_iface;
1429 } else if(IsEqualIID(riid, &IID_IEnumPins)) {
1430 *ppv = &This->IEnumPins_iface;
1431 } else {
1432 trace("no interface for %s\n", wine_dbgstr_guid(riid));
1433 *ppv = NULL;
1434 return E_NOINTERFACE;
1436 IUnknown_AddRef((IUnknown*)*ppv);
1437 return S_OK;
1440 static ULONG WINAPI SourceEnumPins_AddRef(IEnumPins *iface)
1442 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1443 return InterlockedIncrement(&This->ref);
1446 static ULONG WINAPI SourceEnumPins_Release(IEnumPins *iface)
1448 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1449 ULONG ref;
1450 ref = InterlockedDecrement(&This->ref);
1451 if (ref == 0)
1453 IBaseFilter_Release(&This->filter->IBaseFilter_iface);
1454 CoTaskMemFree(This);
1456 return ref;
1459 static HRESULT WINAPI SourceEnumPins_Next(IEnumPins *iface, ULONG cPins, IPin **ppPins, ULONG *pcFetched)
1461 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1462 if (!ppPins)
1463 return E_POINTER;
1464 if (cPins > 1 && !pcFetched)
1465 return E_INVALIDARG;
1466 if (pcFetched)
1467 *pcFetched = 0;
1468 if (cPins == 0)
1469 return S_OK;
1470 if (This->index == 0) {
1471 ppPins[0] = &This->filter->IPin_iface;
1472 IPin_AddRef(&This->filter->IPin_iface);
1473 ++This->index;
1474 if (pcFetched)
1475 *pcFetched = 1;
1476 return S_OK;
1478 return S_FALSE;
1481 static HRESULT WINAPI SourceEnumPins_Skip(IEnumPins *iface, ULONG cPins)
1483 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1484 if (This->index + cPins >= 1)
1485 return S_FALSE;
1486 This->index += cPins;
1487 return S_OK;
1490 static HRESULT WINAPI SourceEnumPins_Reset(IEnumPins *iface)
1492 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1493 This->index = 0;
1494 return S_OK;
1497 static HRESULT WINAPI SourceEnumPins_Clone(IEnumPins *iface, IEnumPins **ppEnum)
1499 SourceEnumPins *This = impl_from_SourceFilter_IEnumPins(iface);
1500 SourceEnumPins *clone = create_SourceEnumPins(This->filter);
1501 if (clone == NULL)
1502 return E_OUTOFMEMORY;
1503 clone->index = This->index;
1504 *ppEnum = &clone->IEnumPins_iface;
1505 return S_OK;
1508 static const IEnumPinsVtbl SourceEnumPinsVtbl = {
1509 SourceEnumPins_QueryInterface,
1510 SourceEnumPins_AddRef,
1511 SourceEnumPins_Release,
1512 SourceEnumPins_Next,
1513 SourceEnumPins_Skip,
1514 SourceEnumPins_Reset,
1515 SourceEnumPins_Clone
1518 static SourceEnumPins* create_SourceEnumPins(SourceFilter *filter)
1520 SourceEnumPins *This;
1521 This = CoTaskMemAlloc(sizeof(*This));
1522 if (This == NULL) {
1523 return NULL;
1525 This->IEnumPins_iface.lpVtbl = &SourceEnumPinsVtbl;
1526 This->ref = 1;
1527 This->index = 0;
1528 This->filter = filter;
1529 IBaseFilter_AddRef(&filter->IBaseFilter_iface);
1530 return This;
1533 static HRESULT WINAPI SourceEnumMediaTypes_QueryInterface(IEnumMediaTypes *iface, REFIID riid, void **ppv)
1535 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1536 if(IsEqualIID(riid, &IID_IUnknown)) {
1537 *ppv = &This->IEnumMediaTypes_iface;
1538 } else if(IsEqualIID(riid, &IID_IEnumMediaTypes)) {
1539 *ppv = &This->IEnumMediaTypes_iface;
1540 } else {
1541 trace("no interface for %s\n", wine_dbgstr_guid(riid));
1542 *ppv = NULL;
1543 return E_NOINTERFACE;
1545 IUnknown_AddRef((IUnknown*)*ppv);
1546 return S_OK;
1549 static ULONG WINAPI SourceEnumMediaTypes_AddRef(IEnumMediaTypes *iface)
1551 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1552 return InterlockedIncrement(&This->ref);
1555 static ULONG WINAPI SourceEnumMediaTypes_Release(IEnumMediaTypes *iface)
1557 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1558 ULONG ref;
1559 ref = InterlockedDecrement(&This->ref);
1560 if (ref == 0)
1562 IBaseFilter_Release(&This->filter->IBaseFilter_iface);
1563 CoTaskMemFree(This);
1565 return ref;
1568 static HRESULT WINAPI SourceEnumMediaTypes_Next(IEnumMediaTypes *iface, ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched)
1570 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1571 if (!ppMediaTypes)
1572 return E_POINTER;
1573 if (cMediaTypes > 1 && !pcFetched)
1574 return E_INVALIDARG;
1575 if (pcFetched)
1576 *pcFetched = 0;
1577 if (cMediaTypes == 0)
1578 return S_OK;
1579 if (This->index == 0) {
1580 ppMediaTypes[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1581 if (ppMediaTypes[0]) {
1582 *ppMediaTypes[0] = This->filter->mediaType;
1583 ppMediaTypes[0]->pbFormat = CoTaskMemAlloc(This->filter->mediaType.cbFormat);
1584 if (ppMediaTypes[0]->pbFormat) {
1585 memcpy(ppMediaTypes[0]->pbFormat, This->filter->mediaType.pbFormat, This->filter->mediaType.cbFormat);
1586 ++This->index;
1587 if (pcFetched)
1588 *pcFetched = 1;
1589 return S_OK;
1591 CoTaskMemFree(ppMediaTypes[0]);
1593 return E_OUTOFMEMORY;
1595 return S_FALSE;
1598 static HRESULT WINAPI SourceEnumMediaTypes_Skip(IEnumMediaTypes *iface, ULONG cMediaTypes)
1600 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1601 This->index += cMediaTypes;
1602 if (This->index >= 1)
1603 return S_FALSE;
1604 return S_OK;
1607 static HRESULT WINAPI SourceEnumMediaTypes_Reset(IEnumMediaTypes *iface)
1609 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1610 This->index = 0;
1611 return S_OK;
1614 static HRESULT WINAPI SourceEnumMediaTypes_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **ppEnum)
1616 SourceEnumMediaTypes *This = impl_from_SourceFilter_IEnumMediaTypes(iface);
1617 SourceEnumMediaTypes *clone = create_SourceEnumMediaTypes(This->filter);
1618 if (clone == NULL)
1619 return E_OUTOFMEMORY;
1620 clone->index = This->index;
1621 *ppEnum = &clone->IEnumMediaTypes_iface;
1622 return S_OK;
1625 static const IEnumMediaTypesVtbl SourceEnumMediaTypesVtbl = {
1626 SourceEnumMediaTypes_QueryInterface,
1627 SourceEnumMediaTypes_AddRef,
1628 SourceEnumMediaTypes_Release,
1629 SourceEnumMediaTypes_Next,
1630 SourceEnumMediaTypes_Skip,
1631 SourceEnumMediaTypes_Reset,
1632 SourceEnumMediaTypes_Clone
1635 static SourceEnumMediaTypes* create_SourceEnumMediaTypes(SourceFilter *filter)
1637 SourceEnumMediaTypes *This;
1638 This = CoTaskMemAlloc(sizeof(*This));
1639 if (This == NULL) {
1640 return NULL;
1642 This->IEnumMediaTypes_iface.lpVtbl = &SourceEnumMediaTypesVtbl;
1643 This->ref = 1;
1644 This->index = 0;
1645 This->filter = filter;
1646 IBaseFilter_AddRef(&filter->IBaseFilter_iface);
1647 return This;
1650 static HRESULT WINAPI SourcePin_QueryInterface(IPin *iface, REFIID riid, void **ppv)
1652 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1653 if(IsEqualIID(riid, &IID_IUnknown)) {
1654 *ppv = &This->IPin_iface;
1655 } else if(IsEqualIID(riid, &IID_IPin)) {
1656 *ppv = &This->IPin_iface;
1657 } else if(IsEqualIID(riid, &IID_IKsPropertySet)) {
1658 *ppv = &This->IKsPropertySet_iface;
1659 } else {
1660 trace("no interface for %s\n", wine_dbgstr_guid(riid));
1661 *ppv = NULL;
1662 return E_NOINTERFACE;
1664 IUnknown_AddRef((IUnknown*)*ppv);
1665 return S_OK;
1668 static ULONG WINAPI SourcePin_AddRef(IPin *iface)
1670 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1671 return IBaseFilter_AddRef(&This->IBaseFilter_iface);
1674 static ULONG WINAPI SourcePin_Release(IPin *iface)
1676 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1677 return IBaseFilter_Release(&This->IBaseFilter_iface);
1680 static HRESULT WINAPI SourcePin_Connect(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1682 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1683 HRESULT hr;
1685 if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->majortype, &MEDIATYPE_Video))
1686 return VFW_E_TYPE_NOT_ACCEPTED;
1687 if (pmt && !IsEqualGUID(&pmt->subtype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &MEDIASUBTYPE_RGB32))
1688 return VFW_E_TYPE_NOT_ACCEPTED;
1689 if (pmt && !IsEqualGUID(&pmt->formattype, &GUID_NULL))
1690 return VFW_E_TYPE_NOT_ACCEPTED;
1691 hr = IPin_ReceiveConnection(pReceivePin, &This->IPin_iface, &This->mediaType);
1692 ok(SUCCEEDED(hr), "SmartTeeFilter's Input pin's IPin_ReceiveConnection() failed with 0x%08x\n", hr);
1693 if (SUCCEEDED(hr)) {
1694 EnterCriticalSection(&This->cs);
1695 hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (void**)&This->memInputPin);
1696 if (SUCCEEDED(hr)) {
1697 hr = IMemInputPin_GetAllocator(This->memInputPin, &This->allocator);
1698 ok(SUCCEEDED(hr), "couldn't get allocator from SmartTeeFilter, hr=0x%08x\n", hr);
1699 if (SUCCEEDED(hr)) {
1700 ALLOCATOR_PROPERTIES requested, actual;
1701 ZeroMemory(&requested, sizeof(ALLOCATOR_PROPERTIES));
1702 IMemInputPin_GetAllocatorRequirements(This->memInputPin, &requested);
1703 if (requested.cBuffers < 3) requested.cBuffers = 3;
1704 if (requested.cbBuffer < 4096) requested.cbBuffer = 4096;
1705 if (requested.cbAlign < 1) requested.cbAlign = 1;
1706 if (requested.cbPrefix < 0) requested.cbPrefix = 0;
1707 hr = IMemAllocator_SetProperties(This->allocator, &requested, &actual);
1708 if (SUCCEEDED(hr)) {
1709 hr = IMemInputPin_NotifyAllocator(This->memInputPin, This->allocator, FALSE);
1710 if (SUCCEEDED(hr)) {
1711 This->connectedTo = pReceivePin;
1712 IPin_AddRef(pReceivePin);
1715 if (FAILED(hr)) {
1716 IMemAllocator_Release(This->allocator);
1717 This->allocator = NULL;
1720 if (FAILED(hr)) {
1721 IMemInputPin_Release(This->memInputPin);
1722 This->memInputPin = NULL;
1725 LeaveCriticalSection(&This->cs);
1727 if (FAILED(hr))
1728 IPin_Disconnect(pReceivePin);
1730 return hr;
1733 static HRESULT WINAPI SourcePin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *pmt)
1735 return E_UNEXPECTED;
1738 static HRESULT WINAPI SourcePin_Disconnect(IPin *iface)
1740 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1741 HRESULT hr;
1742 EnterCriticalSection(&This->cs);
1743 if (This->connectedTo) {
1744 if (This->state == State_Stopped) {
1745 IMemAllocator_Release(This->allocator);
1746 This->allocator = NULL;
1747 IMemInputPin_Release(This->memInputPin);
1748 This->memInputPin = NULL;
1749 IPin_Release(This->connectedTo);
1750 This->connectedTo = NULL;
1751 hr = S_OK;
1753 else
1754 hr = VFW_E_NOT_STOPPED;
1755 } else
1756 hr = S_FALSE;
1757 LeaveCriticalSection(&This->cs);
1758 return hr;
1761 static HRESULT WINAPI SourcePin_ConnectedTo(IPin *iface, IPin **pPin)
1763 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1764 HRESULT hr;
1765 if (!pPin)
1766 return E_POINTER;
1767 EnterCriticalSection(&This->cs);
1768 if (This->connectedTo) {
1769 *pPin = This->connectedTo;
1770 IPin_AddRef(This->connectedTo);
1771 hr = S_OK;
1772 } else
1773 hr = VFW_E_NOT_CONNECTED;
1774 LeaveCriticalSection(&This->cs);
1775 return hr;
1778 static HRESULT WINAPI SourcePin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *pmt)
1780 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1781 HRESULT hr;
1782 if (!pmt)
1783 return E_POINTER;
1784 EnterCriticalSection(&This->cs);
1785 if (This->connectedTo) {
1786 *pmt = This->mediaType;
1787 pmt->pbFormat = CoTaskMemAlloc(sizeof(This->videoInfo));
1788 if (pmt->pbFormat) {
1789 memcpy(pmt->pbFormat, &This->videoInfo, sizeof(This->videoInfo));
1790 hr = S_OK;
1791 } else {
1792 memset(pmt, 0, sizeof(*pmt));
1793 hr = E_OUTOFMEMORY;
1795 } else {
1796 memset(pmt, 0, sizeof(*pmt));
1797 hr = VFW_E_NOT_CONNECTED;
1799 LeaveCriticalSection(&This->cs);
1800 return hr;
1803 static HRESULT WINAPI SourcePin_QueryPinInfo(IPin *iface, PIN_INFO *pInfo)
1805 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1806 if (!pInfo)
1807 return E_POINTER;
1808 lstrcpyW(pInfo->achName, sourcePinName);
1809 pInfo->dir = PINDIR_OUTPUT;
1810 pInfo->pFilter = &This->IBaseFilter_iface;
1811 IBaseFilter_AddRef(&This->IBaseFilter_iface);
1812 return S_OK;
1815 static HRESULT WINAPI SourcePin_QueryDirection(IPin *iface, PIN_DIRECTION *pPinDir)
1817 if (!pPinDir)
1818 return E_POINTER;
1819 *pPinDir = PINDIR_OUTPUT;
1820 return S_OK;
1823 static HRESULT WINAPI SourcePin_QueryId(IPin *iface, LPWSTR *id)
1825 if (!id)
1826 return E_POINTER;
1827 *id = CoTaskMemAlloc((lstrlenW(sourcePinName) + 1)*sizeof(WCHAR));
1828 if (*id) {
1829 lstrcpyW(*id, sourcePinName);
1830 return S_OK;
1832 return E_OUTOFMEMORY;
1835 static HRESULT WINAPI SourcePin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
1837 return S_OK;
1840 static HRESULT WINAPI SourcePin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1842 SourceFilter *This = impl_from_SourceFilter_IPin(iface);
1843 SourceEnumMediaTypes *sourceEnumMediaTypes = create_SourceEnumMediaTypes(This);
1844 if (sourceEnumMediaTypes) {
1845 *ppEnum = &sourceEnumMediaTypes->IEnumMediaTypes_iface;
1846 return S_OK;
1848 else
1849 return E_OUTOFMEMORY;
1852 static HRESULT WINAPI SourcePin_QueryInternalConnections(IPin *iface, IPin **apPin, ULONG *nPin)
1854 return E_NOTIMPL;
1857 static HRESULT WINAPI SourcePin_EndOfStream(IPin *iface)
1859 return E_UNEXPECTED;
1862 static HRESULT WINAPI SourcePin_BeginFlush(IPin *iface)
1864 return E_UNEXPECTED;
1867 static HRESULT WINAPI SourcePin_EndFlush(IPin *iface)
1869 return E_UNEXPECTED;
1872 static HRESULT WINAPI SourcePin_NewSegment(IPin *iface, REFERENCE_TIME tStart,
1873 REFERENCE_TIME tStop, double dRate)
1875 return S_OK;
1878 static const IPinVtbl SourcePinVtbl = {
1879 SourcePin_QueryInterface,
1880 SourcePin_AddRef,
1881 SourcePin_Release,
1882 SourcePin_Connect,
1883 SourcePin_ReceiveConnection,
1884 SourcePin_Disconnect,
1885 SourcePin_ConnectedTo,
1886 SourcePin_ConnectionMediaType,
1887 SourcePin_QueryPinInfo,
1888 SourcePin_QueryDirection,
1889 SourcePin_QueryId,
1890 SourcePin_QueryAccept,
1891 SourcePin_EnumMediaTypes,
1892 SourcePin_QueryInternalConnections,
1893 SourcePin_EndOfStream,
1894 SourcePin_BeginFlush,
1895 SourcePin_EndFlush,
1896 SourcePin_NewSegment
1899 static HRESULT WINAPI SourceKSP_QueryInterface(IKsPropertySet *iface, REFIID riid, LPVOID *ppv)
1901 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1902 return IPin_QueryInterface(&This->IPin_iface, riid, ppv);
1905 static ULONG WINAPI SourceKSP_AddRef(IKsPropertySet *iface)
1907 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1908 return IBaseFilter_AddRef(&This->IBaseFilter_iface);
1911 static ULONG WINAPI SourceKSP_Release(IKsPropertySet *iface)
1913 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1914 return IBaseFilter_Release(&This->IBaseFilter_iface);
1917 static HRESULT WINAPI SourceKSP_Set(IKsPropertySet *iface, REFGUID guidPropSet, DWORD dwPropID,
1918 LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData)
1920 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1921 trace("(%p)->(%s, %u, %p, %u, %p, %u): stub\n", This, wine_dbgstr_guid(guidPropSet),
1922 dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData);
1923 return E_NOTIMPL;
1926 static HRESULT WINAPI SourceKSP_Get(IKsPropertySet *iface, REFGUID guidPropSet, DWORD dwPropID,
1927 LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
1928 DWORD cbPropData, DWORD *pcbReturned)
1930 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1931 trace("(%p)->(%s, %u, %p, %u, %p, %u, %p)\n", This, wine_dbgstr_guid(guidPropSet),
1932 dwPropID, pInstanceData, cbInstanceData, pPropData, cbPropData, pcbReturned);
1933 if (IsEqualIID(guidPropSet, &AMPROPSETID_Pin)) {
1934 if (pcbReturned)
1935 *pcbReturned = sizeof(GUID);
1936 if (pPropData) {
1937 LPGUID guid = pPropData;
1938 if (cbPropData >= sizeof(GUID))
1939 *guid = PIN_CATEGORY_CAPTURE;
1940 } else {
1941 if (!pcbReturned)
1942 return E_POINTER;
1944 return S_OK;
1946 return E_PROP_SET_UNSUPPORTED;
1949 static HRESULT WINAPI SourceKSP_QuerySupported(IKsPropertySet *iface, REFGUID guidPropSet,
1950 DWORD dwPropID, DWORD *pTypeSupport)
1952 SourceFilter *This = impl_from_SourceFilter_IKsPropertySet(iface);
1953 trace("(%p)->(%s, %u, %p): stub\n", This, wine_dbgstr_guid(guidPropSet),
1954 dwPropID, pTypeSupport);
1955 return E_NOTIMPL;
1958 static const IKsPropertySetVtbl SourceKSPVtbl =
1960 SourceKSP_QueryInterface,
1961 SourceKSP_AddRef,
1962 SourceKSP_Release,
1963 SourceKSP_Set,
1964 SourceKSP_Get,
1965 SourceKSP_QuerySupported
1968 static SourceFilter* create_SourceFilter(void)
1970 SourceFilter *This = NULL;
1971 This = CoTaskMemAlloc(sizeof(*This));
1972 if (This) {
1973 memset(This, 0, sizeof(*This));
1974 This->IBaseFilter_iface.lpVtbl = &SourceFilterVtbl;
1975 This->ref = 1;
1976 This->IPin_iface.lpVtbl = &SourcePinVtbl;
1977 This->IKsPropertySet_iface.lpVtbl = &SourceKSPVtbl;
1978 InitializeCriticalSection(&This->cs);
1979 return This;
1981 return NULL;
1984 static SourceFilter* create_video_SourceFilter(void)
1986 SourceFilter *This = create_SourceFilter();
1987 if (!This)
1988 return NULL;
1989 This->mediaType.majortype = MEDIATYPE_Video;
1990 This->mediaType.subtype = MEDIASUBTYPE_RGB32;
1991 This->mediaType.bFixedSizeSamples = FALSE;
1992 This->mediaType.bTemporalCompression = FALSE;
1993 This->mediaType.lSampleSize = 0;
1994 This->mediaType.formattype = FORMAT_VideoInfo;
1995 This->mediaType.pUnk = NULL;
1996 This->mediaType.cbFormat = sizeof(VIDEOINFOHEADER);
1997 This->mediaType.pbFormat = (BYTE*) &This->videoInfo;
1998 This->videoInfo.dwBitRate = 1000000;
1999 This->videoInfo.dwBitErrorRate = 0;
2000 This->videoInfo.AvgTimePerFrame = 400000;
2001 This->videoInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
2002 This->videoInfo.bmiHeader.biWidth = 10;
2003 This->videoInfo.bmiHeader.biHeight = 10;
2004 This->videoInfo.bmiHeader.biPlanes = 1;
2005 This->videoInfo.bmiHeader.biBitCount = 32;
2006 This->videoInfo.bmiHeader.biCompression = BI_RGB;
2007 This->videoInfo.bmiHeader.biSizeImage = 0;
2008 This->videoInfo.bmiHeader.biXPelsPerMeter = 96;
2009 This->videoInfo.bmiHeader.biYPelsPerMeter = 96;
2010 This->videoInfo.bmiHeader.biClrUsed = 0;
2011 This->videoInfo.bmiHeader.biClrImportant = 0;
2012 return This;
2015 static void test_smart_tee_filter_in_graph(IBaseFilter *smartTeeFilter, IPin *inputPin,
2016 IPin *capturePin, IPin *previewPin)
2018 HRESULT hr;
2019 IGraphBuilder *graphBuilder = NULL;
2020 IMediaControl *mediaControl = NULL;
2021 SourceFilter *sourceFilter = NULL;
2022 SinkFilter *captureSinkFilter = NULL;
2023 SinkFilter *previewSinkFilter = NULL;
2024 DWORD endTime;
2026 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2027 (LPVOID*)&graphBuilder);
2028 ok(SUCCEEDED(hr), "couldn't create graph builder, hr=0x%08x\n", hr);
2029 if (FAILED(hr))
2030 goto end;
2032 hr = IGraphBuilder_AddFilter(graphBuilder, smartTeeFilter, NULL);
2033 ok(SUCCEEDED(hr), "couldn't add smart tee filter to graph, hr=0x%08x\n", hr);
2034 if (FAILED(hr))
2035 goto end;
2037 captureSinkFilter = create_SinkFilter(TRUE);
2038 if (captureSinkFilter == NULL) {
2039 skip("couldn't create capture sink filter\n");
2040 goto end;
2042 hr = IGraphBuilder_AddFilter(graphBuilder, &captureSinkFilter->IBaseFilter_iface, NULL);
2043 if (FAILED(hr)) {
2044 skip("couldn't add capture sink filter to graph, hr=0x%08x\n", hr);
2045 goto end;
2048 previewSinkFilter = create_SinkFilter(FALSE);
2049 if (previewSinkFilter == NULL) {
2050 skip("couldn't create preview sink filter\n");
2051 goto end;
2053 hr = IGraphBuilder_AddFilter(graphBuilder, &previewSinkFilter->IBaseFilter_iface, NULL);
2054 if (FAILED(hr)) {
2055 skip("couldn't add preview sink filter to graph, hr=0x%08x\n", hr);
2056 goto end;
2059 hr = IGraphBuilder_Connect(graphBuilder, capturePin, &captureSinkFilter->IPin_iface);
2060 ok(hr == VFW_E_NOT_CONNECTED, "connecting Capture pin without first connecting Input pin returned 0x%08x\n", hr);
2061 hr = IGraphBuilder_Connect(graphBuilder, previewPin, &previewSinkFilter->IPin_iface);
2062 ok(hr == VFW_E_NOT_CONNECTED, "connecting Preview pin without first connecting Input pin returned 0x%08x\n", hr);
2064 sourceFilter = create_video_SourceFilter();
2065 if (sourceFilter == NULL) {
2066 skip("couldn't create source filter\n");
2067 goto end;
2069 hr = IGraphBuilder_AddFilter(graphBuilder, &sourceFilter->IBaseFilter_iface, NULL);
2070 ok(SUCCEEDED(hr), "couldn't add source filter to graph, hr=0x%08x\n", hr);
2071 if (FAILED(hr))
2072 goto end;
2074 hr = IGraphBuilder_Connect(graphBuilder, &sourceFilter->IPin_iface, inputPin);
2075 ok(SUCCEEDED(hr), "couldn't connect source filter to Input pin, hr=0x%08x\n", hr);
2076 if (FAILED(hr))
2077 goto end;
2078 hr = IGraphBuilder_Connect(graphBuilder, capturePin, &captureSinkFilter->IPin_iface);
2079 ok(SUCCEEDED(hr), "couldn't connect Capture pin to sink, hr=0x%08x\n", hr);
2080 if (FAILED(hr))
2081 goto end;
2082 hr = IGraphBuilder_Connect(graphBuilder, previewPin, &previewSinkFilter->IPin_iface);
2083 ok(SUCCEEDED(hr), "couldn't connect Preview pin to sink, hr=0x%08x\n", hr);
2084 if (FAILED(hr))
2085 goto end;
2087 ok(sourceFilter->allocator == captureSinkFilter->allocator, "input and capture allocators don't match\n");
2088 ok(sourceFilter->allocator == previewSinkFilter->allocator, "input and preview allocators don't match\n");
2090 hr = IGraphBuilder_QueryInterface(graphBuilder, &IID_IMediaControl, (void**)&mediaControl);
2091 ok(SUCCEEDED(hr), "couldn't get IMediaControl interface from IGraphBuilder, hr=0x%08x\n", hr);
2092 if (FAILED(hr))
2093 goto end;
2094 hr = IMediaControl_Run(mediaControl);
2095 ok(SUCCEEDED(hr), "IMediaControl_Run() failed, hr=0x%08x\n", hr);
2096 if (FAILED(hr))
2097 goto end;
2099 endTime = GetTickCount() + 5000;
2100 while (previewSinkFilter->receiveThreadId == 0 || captureSinkFilter->receiveThreadId == 0) {
2101 DWORD now = GetTickCount();
2102 if (now < endTime)
2103 WaitForSingleObject(event, endTime - now);
2104 else
2105 break;
2107 if (previewSinkFilter->receiveThreadId != 0 && captureSinkFilter->receiveThreadId != 0) {
2108 todo_wine ok(sourceFilter->mediaThreadId != captureSinkFilter->receiveThreadId,
2109 "sending thread should != capture receiving thread\n");
2110 todo_wine ok(sourceFilter->mediaThreadId != previewSinkFilter->receiveThreadId,
2111 "sending thread should != preview receiving thread\n");
2112 todo_wine ok(captureSinkFilter->receiveThreadId != previewSinkFilter->receiveThreadId,
2113 "capture receiving thread should != preview receiving thread\n");
2114 } else {
2115 ok(0, "timeout: threads did not receive sample in time\n");
2118 IMediaControl_Stop(mediaControl);
2120 end:
2121 if (mediaControl)
2122 IMediaControl_Release(mediaControl);
2123 if (graphBuilder)
2124 IGraphBuilder_Release(graphBuilder);
2125 if (sourceFilter)
2126 IBaseFilter_Release(&sourceFilter->IBaseFilter_iface);
2127 if (captureSinkFilter)
2128 IBaseFilter_Release(&captureSinkFilter->IBaseFilter_iface);
2129 if (previewSinkFilter)
2130 IBaseFilter_Release(&previewSinkFilter->IBaseFilter_iface);
2133 static void test_smart_tee_filter(void)
2135 HRESULT hr;
2136 IBaseFilter *smartTeeFilter = NULL;
2137 IEnumPins *enumPins = NULL;
2138 IPin *pin;
2139 IPin *inputPin = NULL;
2140 IPin *capturePin = NULL;
2141 IPin *previewPin = NULL;
2142 FILTER_INFO filterInfo;
2143 int pinNumber = 0;
2144 IMemInputPin *memInputPin = NULL;
2146 hr = CoCreateInstance(&CLSID_SmartTee, NULL, CLSCTX_INPROC_SERVER,
2147 &IID_IBaseFilter, (void**)&smartTeeFilter);
2148 ok(SUCCEEDED(hr), "couldn't create smart tee filter, hr=0x%08x\n", hr);
2149 if (FAILED(hr))
2150 goto end;
2152 hr = IBaseFilter_QueryFilterInfo(smartTeeFilter, &filterInfo);
2153 ok(SUCCEEDED(hr), "QueryFilterInfo failed, hr=0x%08x\n", hr);
2154 if (FAILED(hr))
2155 goto end;
2157 ok(!*filterInfo.achName,
2158 "filter's name is meant to be empty but it's %s\n", wine_dbgstr_w(filterInfo.achName));
2160 hr = IBaseFilter_EnumPins(smartTeeFilter, &enumPins);
2161 ok(SUCCEEDED(hr), "cannot enum filter pins, hr=0x%08x\n", hr);
2162 if (FAILED(hr))
2163 goto end;
2165 while (IEnumPins_Next(enumPins, 1, &pin, NULL) == S_OK)
2167 if (pinNumber == 0)
2169 inputPin = pin;
2170 IPin_AddRef(inputPin);
2172 else if (pinNumber == 1)
2174 capturePin = pin;
2175 IPin_AddRef(capturePin);
2177 else if (pinNumber == 2)
2179 previewPin = pin;
2180 IPin_AddRef(previewPin);
2182 else
2183 ok(0, "pin %d isn't supposed to exist\n", pinNumber);
2185 IPin_Release(pin);
2186 pinNumber++;
2189 ok(inputPin && capturePin && previewPin, "couldn't find all pins\n");
2190 if (!(inputPin && capturePin && previewPin))
2191 goto end;
2193 hr = IPin_QueryInterface(inputPin, &IID_IMemInputPin, (void**)&memInputPin);
2194 ok(SUCCEEDED(hr), "couldn't get mem input pin, hr=0x%08x\n", hr);
2195 if (FAILED(hr))
2196 goto end;
2197 hr = IMemInputPin_ReceiveCanBlock(memInputPin);
2198 ok(hr == S_OK, "unexpected IMemInputPin_ReceiveCanBlock() = 0x%08x\n", hr);
2200 test_smart_tee_filter_in_graph(smartTeeFilter, inputPin, capturePin, previewPin);
2202 end:
2203 if (inputPin)
2204 IPin_Release(inputPin);
2205 if (capturePin)
2206 IPin_Release(capturePin);
2207 if (previewPin)
2208 IPin_Release(previewPin);
2209 if (smartTeeFilter)
2210 IBaseFilter_Release(smartTeeFilter);
2211 if (enumPins)
2212 IEnumPins_Release(enumPins);
2213 if (memInputPin)
2214 IMemInputPin_Release(memInputPin);
2217 static void test_unconnected_filter_state(void)
2219 IBaseFilter *filter = create_smart_tee();
2220 FILTER_STATE state;
2221 HRESULT hr;
2222 ULONG ref;
2224 hr = IBaseFilter_GetState(filter, 0, &state);
2225 ok(hr == S_OK, "Got hr %#x.\n", hr);
2226 ok(state == State_Stopped, "Got state %u.\n", state);
2228 hr = IBaseFilter_Pause(filter);
2229 ok(hr == S_OK, "Got hr %#x.\n", hr);
2231 hr = IBaseFilter_GetState(filter, 0, &state);
2232 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
2233 ok(state == State_Paused, "Got state %u.\n", state);
2235 hr = IBaseFilter_Run(filter, 0);
2236 ok(hr == S_OK, "Got hr %#x.\n", hr);
2238 hr = IBaseFilter_GetState(filter, 0, &state);
2239 ok(hr == S_OK, "Got hr %#x.\n", hr);
2240 ok(state == State_Running, "Got state %u.\n", state);
2242 hr = IBaseFilter_Pause(filter);
2243 ok(hr == S_OK, "Got hr %#x.\n", hr);
2245 hr = IBaseFilter_GetState(filter, 0, &state);
2246 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
2247 ok(state == State_Paused, "Got state %u.\n", state);
2249 hr = IBaseFilter_Stop(filter);
2250 ok(hr == S_OK, "Got hr %#x.\n", hr);
2252 hr = IBaseFilter_GetState(filter, 0, &state);
2253 ok(hr == S_OK, "Got hr %#x.\n", hr);
2254 ok(state == State_Stopped, "Got state %u.\n", state);
2256 hr = IBaseFilter_Run(filter, 0);
2257 ok(hr == S_OK, "Got hr %#x.\n", hr);
2259 hr = IBaseFilter_GetState(filter, 0, &state);
2260 ok(hr == S_OK, "Got hr %#x.\n", hr);
2261 ok(state == State_Running, "Got state %u.\n", state);
2263 hr = IBaseFilter_Stop(filter);
2264 ok(hr == S_OK, "Got hr %#x.\n", hr);
2266 hr = IBaseFilter_GetState(filter, 0, &state);
2267 ok(hr == S_OK, "Got hr %#x.\n", hr);
2268 ok(state == State_Stopped, "Got state %u.\n", state);
2270 ref = IBaseFilter_Release(filter);
2271 ok(!ref, "Got outstanding refcount %d.\n", ref);
2274 struct testfilter
2276 struct strmbase_filter filter;
2277 struct strmbase_source source;
2278 struct strmbase_sink sink;
2279 const AM_MEDIA_TYPE *sink_mt;
2280 AM_MEDIA_TYPE source_mt;
2283 static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface)
2285 return CONTAINING_RECORD(iface, struct testfilter, filter);
2288 static struct strmbase_pin *testfilter_get_pin(struct strmbase_filter *iface, unsigned int index)
2290 struct testfilter *filter = impl_from_strmbase_filter(iface);
2291 if (!index)
2292 return &filter->source.pin;
2293 else if (index == 1)
2294 return &filter->sink.pin;
2295 return NULL;
2298 static void testfilter_destroy(struct strmbase_filter *iface)
2300 struct testfilter *filter = impl_from_strmbase_filter(iface);
2301 strmbase_source_cleanup(&filter->source);
2302 strmbase_sink_cleanup(&filter->sink);
2303 strmbase_filter_cleanup(&filter->filter);
2306 static const struct strmbase_filter_ops testfilter_ops =
2308 .filter_get_pin = testfilter_get_pin,
2309 .filter_destroy = testfilter_destroy,
2312 static HRESULT testsource_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
2314 return mt->bTemporalCompression ? S_OK : S_FALSE;
2317 static HRESULT testsource_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt)
2319 struct testfilter *filter = impl_from_strmbase_filter(iface->filter);
2320 if (!index)
2322 CopyMediaType(mt, &filter->source_mt);
2323 return S_OK;
2325 return VFW_S_NO_MORE_ITEMS;
2328 static void test_sink_allocator(IPin *pin)
2330 ALLOCATOR_PROPERTIES req_props = {1, 5000, 1, 0}, ret_props;
2331 IMemAllocator *req_allocator, *ret_allocator;
2332 IMemInputPin *input;
2333 HRESULT hr;
2335 IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&input);
2337 hr = IMemInputPin_GetAllocatorRequirements(input, &ret_props);
2338 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
2340 hr = IMemInputPin_GetAllocator(input, &ret_allocator);
2341 ok(hr == S_OK, "Got hr %#x.\n", hr);
2343 hr = IMemInputPin_NotifyAllocator(input, ret_allocator, TRUE);
2344 ok(hr == S_OK, "Got hr %#x.\n", hr);
2345 IMemAllocator_Release(ret_allocator);
2347 CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER,
2348 &IID_IMemAllocator, (void **)&req_allocator);
2350 hr = IMemInputPin_NotifyAllocator(input, req_allocator, TRUE);
2351 ok(hr == S_OK, "Got hr %#x.\n", hr);
2353 hr = IMemInputPin_GetAllocator(input, &ret_allocator);
2354 ok(hr == S_OK, "Got hr %#x.\n", hr);
2355 ok(ret_allocator == req_allocator, "Allocators didn't match.\n");
2356 ok(hr == S_OK, "Got hr %#x.\n", hr);
2357 IMemAllocator_Release(ret_allocator);
2359 hr = IMemAllocator_SetProperties(req_allocator, &req_props, &ret_props);
2360 ok(hr == S_OK, "Got hr %#x.\n", hr);
2362 hr = IMemAllocator_Commit(req_allocator);
2363 ok(hr == S_OK, "Got hr %#x.\n", hr);
2365 IMemAllocator_Release(req_allocator);
2366 IMemInputPin_Release(input);
2369 static HRESULT WINAPI testsource_AttemptConnection(struct strmbase_source *iface,
2370 IPin *peer, const AM_MEDIA_TYPE *mt)
2372 HRESULT hr;
2374 iface->pin.peer = peer;
2375 IPin_AddRef(peer);
2376 CopyMediaType(&iface->pin.mt, mt);
2378 if (FAILED(hr = IPin_ReceiveConnection(peer, &iface->pin.IPin_iface, mt)))
2380 ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr);
2381 IPin_Release(peer);
2382 iface->pin.peer = NULL;
2383 FreeMediaType(&iface->pin.mt);
2386 test_sink_allocator(peer);
2388 return hr;
2391 static const struct strmbase_source_ops testsource_ops =
2393 .base.pin_query_accept = testsource_query_accept,
2394 .base.pin_get_media_type = testsource_get_media_type,
2395 .pfnAttemptConnection = testsource_AttemptConnection,
2398 static HRESULT testsink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
2400 struct testfilter *filter = impl_from_strmbase_filter(iface->filter);
2402 if (IsEqualGUID(iid, &IID_IMemInputPin))
2403 *out = &filter->sink.IMemInputPin_iface;
2404 else
2405 return E_NOINTERFACE;
2407 IUnknown_AddRef((IUnknown *)*out);
2408 return S_OK;
2411 static HRESULT testsink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
2413 struct testfilter *filter = impl_from_strmbase_filter(iface->filter);
2414 if (filter->sink_mt && !compare_media_types(mt, filter->sink_mt))
2415 return S_FALSE;
2416 return S_OK;
2419 static HRESULT testsink_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt)
2421 struct testfilter *filter = impl_from_strmbase_filter(iface->filter);
2422 if (!index && filter->sink_mt)
2424 CopyMediaType(mt, filter->sink_mt);
2425 return S_OK;
2427 return VFW_S_NO_MORE_ITEMS;
2430 static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
2432 return S_OK;
2435 static const struct strmbase_sink_ops testsink_ops =
2437 .base.pin_query_interface = testsink_query_interface,
2438 .base.pin_query_accept = testsink_query_accept,
2439 .base.pin_get_media_type = testsink_get_media_type,
2440 .pfnReceive = testsink_Receive,
2443 static void testfilter_init(struct testfilter *filter)
2445 static const GUID clsid = {0xabacab};
2446 memset(filter, 0, sizeof(*filter));
2447 strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops);
2448 strmbase_source_init(&filter->source, &filter->filter, L"source", &testsource_ops);
2449 strmbase_sink_init(&filter->sink, &filter->filter, L"sink", &testsink_ops, NULL);
2452 static void test_source_media_types(AM_MEDIA_TYPE req_mt, const AM_MEDIA_TYPE *source_mt, IPin *source)
2454 IEnumMediaTypes *enummt;
2455 AM_MEDIA_TYPE *mts[3];
2456 ULONG count;
2457 HRESULT hr;
2459 hr = IPin_EnumMediaTypes(source, &enummt);
2460 ok(hr == S_OK, "Got hr %#x.\n", hr);
2461 hr = IEnumMediaTypes_Next(enummt, 3, mts, &count);
2462 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2463 todo_wine ok(count == 2, "Got %u types.\n", count);
2464 ok(compare_media_types(mts[0], &req_mt), "Media types didn't match.\n");
2465 if (count > 1)
2466 ok(compare_media_types(mts[1], source_mt), "Media types didn't match.\n");
2467 CoTaskMemFree(mts[0]);
2468 if (count > 1)
2469 CoTaskMemFree(mts[1]);
2470 IEnumMediaTypes_Release(enummt);
2472 hr = IPin_QueryAccept(source, &req_mt);
2473 ok(hr == S_OK, "Got hr %#x.\n", hr);
2475 req_mt.lSampleSize = 2;
2476 req_mt.bFixedSizeSamples = TRUE;
2477 hr = IPin_QueryAccept(source, &req_mt);
2478 ok(hr == S_OK, "Got hr %#x.\n", hr);
2480 req_mt.cbFormat = sizeof(count);
2481 req_mt.pbFormat = (BYTE *)&count;
2482 hr = IPin_QueryAccept(source, &req_mt);
2483 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2484 req_mt.cbFormat = 0;
2485 req_mt.pbFormat = NULL;
2487 req_mt.majortype = MEDIATYPE_Audio;
2488 hr = IPin_QueryAccept(source, &req_mt);
2489 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2490 req_mt.majortype = GUID_NULL;
2491 hr = IPin_QueryAccept(source, &req_mt);
2492 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2493 req_mt.majortype = MEDIATYPE_Stream;
2495 req_mt.subtype = MEDIASUBTYPE_PCM;
2496 hr = IPin_QueryAccept(source, &req_mt);
2497 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2498 req_mt.subtype = GUID_NULL;
2499 hr = IPin_QueryAccept(source, &req_mt);
2500 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2501 req_mt.subtype = MEDIASUBTYPE_Avi;
2503 req_mt.formattype = FORMAT_WaveFormatEx;
2504 hr = IPin_QueryAccept(source, &req_mt);
2505 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2506 req_mt.formattype = GUID_NULL;
2507 hr = IPin_QueryAccept(source, &req_mt);
2508 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2509 req_mt.formattype = FORMAT_None;
2511 req_mt.majortype = MEDIATYPE_Audio;
2512 req_mt.subtype = MEDIASUBTYPE_PCM;
2513 req_mt.formattype = test_iid;
2514 req_mt.cbFormat = sizeof(count);
2515 req_mt.pbFormat = (BYTE *)&count;
2516 req_mt.bTemporalCompression = TRUE;
2517 hr = IPin_QueryAccept(source, &req_mt);
2518 ok(hr == S_OK, "Got hr %#x.\n", hr);
2521 static void test_source_connection(AM_MEDIA_TYPE req_mt, IFilterGraph2 *graph,
2522 struct testfilter *testsink, IPin *source)
2524 const AM_MEDIA_TYPE sink_mt = req_mt;
2525 AM_MEDIA_TYPE mt;
2526 HRESULT hr;
2527 IPin *peer;
2529 peer = (IPin *)0xdeadbeef;
2530 hr = IPin_ConnectedTo(source, &peer);
2531 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2532 ok(!peer, "Got peer %p.\n", peer);
2534 hr = IPin_ConnectionMediaType(source, &mt);
2535 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2537 /* Exact connection. */
2539 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2540 ok(hr == S_OK, "Got hr %#x.\n", hr);
2542 hr = IPin_ConnectedTo(source, &peer);
2543 ok(hr == S_OK, "Got hr %#x.\n", hr);
2544 ok(peer == &testsink->sink.pin.IPin_iface, "Got peer %p.\n", peer);
2545 IPin_Release(peer);
2547 hr = IPin_ConnectionMediaType(source, &mt);
2548 ok(hr == S_OK, "Got hr %#x.\n", hr);
2549 ok(compare_media_types(&mt, &req_mt), "Media types didn't match.\n");
2550 ok(compare_media_types(&testsink->sink.pin.mt, &req_mt), "Media types didn't match.\n");
2552 hr = IFilterGraph2_Disconnect(graph, source);
2553 ok(hr == S_OK, "Got hr %#x.\n", hr);
2554 hr = IFilterGraph2_Disconnect(graph, source);
2555 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2556 ok(testsink->sink.pin.peer == source, "Got peer %p.\n", testsink->sink.pin.peer);
2557 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2559 req_mt.subtype = GUID_NULL;
2560 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2561 todo_wine ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr);
2562 if (hr == S_OK)
2564 IFilterGraph2_Disconnect(graph, source);
2565 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2567 req_mt.subtype = sink_mt.subtype;
2569 req_mt.majortype = MEDIATYPE_Audio;
2570 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2571 todo_wine ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr);
2572 if (hr == S_OK)
2574 IFilterGraph2_Disconnect(graph, source);
2575 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2578 /* Connection with wildcards. */
2580 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, NULL);
2581 ok(hr == S_OK, "Got hr %#x.\n", hr);
2582 ok(compare_media_types(&testsink->sink.pin.mt, &sink_mt), "Media types didn't match.\n");
2583 IFilterGraph2_Disconnect(graph, source);
2584 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2586 req_mt.majortype = GUID_NULL;
2587 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2588 ok(hr == S_OK, "Got hr %#x.\n", hr);
2589 ok(compare_media_types(&testsink->sink.pin.mt, &sink_mt), "Media types didn't match.\n");
2590 IFilterGraph2_Disconnect(graph, source);
2591 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2593 req_mt.subtype = MEDIASUBTYPE_RGB32;
2594 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2595 ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#x.\n", hr);
2597 req_mt.subtype = GUID_NULL;
2598 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2599 ok(hr == S_OK, "Got hr %#x.\n", hr);
2600 ok(compare_media_types(&testsink->sink.pin.mt, &sink_mt), "Media types didn't match.\n");
2601 IFilterGraph2_Disconnect(graph, source);
2602 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2604 req_mt.formattype = FORMAT_WaveFormatEx;
2605 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2606 ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#x.\n", hr);
2608 req_mt = sink_mt;
2609 req_mt.formattype = GUID_NULL;
2610 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2611 ok(hr == S_OK, "Got hr %#x.\n", hr);
2612 ok(compare_media_types(&testsink->sink.pin.mt, &sink_mt), "Media types didn't match.\n");
2613 IFilterGraph2_Disconnect(graph, source);
2614 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2616 req_mt.subtype = MEDIASUBTYPE_RGB32;
2617 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2618 ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#x.\n", hr);
2620 req_mt.subtype = GUID_NULL;
2621 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2622 ok(hr == S_OK, "Got hr %#x.\n", hr);
2623 ok(compare_media_types(&testsink->sink.pin.mt, &sink_mt), "Media types didn't match.\n");
2624 IFilterGraph2_Disconnect(graph, source);
2625 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2627 req_mt.majortype = MEDIATYPE_Audio;
2628 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &req_mt);
2629 ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#x.\n", hr);
2631 testsink->sink_mt = &req_mt;
2632 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, NULL);
2633 todo_wine ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#x.\n", hr);
2634 if (hr == S_OK)
2636 IFilterGraph2_Disconnect(graph, source);
2637 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2640 req_mt = sink_mt;
2641 req_mt.lSampleSize = 3;
2642 hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, NULL);
2643 ok(hr == S_OK, "Got hr %#x.\n", hr);
2644 ok(compare_media_types(&testsink->sink.pin.mt, &req_mt), "Media types didn't match.\n");
2645 IFilterGraph2_Disconnect(graph, source);
2646 IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface);
2648 testsink->sink_mt = NULL;
2651 static void test_connect_pin(void)
2653 AM_MEDIA_TYPE req_mt =
2655 .majortype = MEDIATYPE_Stream,
2656 .subtype = MEDIASUBTYPE_Avi,
2657 .formattype = FORMAT_None,
2658 .lSampleSize = 1,
2660 IBaseFilter *filter = create_smart_tee();
2661 struct testfilter testsource, testsink;
2662 IPin *sink, *capture, *preview, *peer;
2663 AM_MEDIA_TYPE mt, *mts[3];
2664 IEnumMediaTypes *enummt;
2665 IFilterGraph2 *graph;
2666 HRESULT hr;
2667 ULONG ref;
2669 testfilter_init(&testsource);
2670 testfilter_init(&testsink);
2671 CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
2672 &IID_IFilterGraph2, (void **)&graph);
2673 IFilterGraph2_AddFilter(graph, &testsource.filter.IBaseFilter_iface, L"source");
2674 IFilterGraph2_AddFilter(graph, &testsink.filter.IBaseFilter_iface, L"sink");
2675 IFilterGraph2_AddFilter(graph, filter, L"sample grabber");
2676 IBaseFilter_FindPin(filter, L"Input", &sink);
2677 IBaseFilter_FindPin(filter, L"Capture", &capture);
2678 IBaseFilter_FindPin(filter, L"Preview", &preview);
2680 testsource.source_mt.majortype = MEDIATYPE_Video;
2681 testsource.source_mt.subtype = MEDIASUBTYPE_RGB8;
2682 testsource.source_mt.formattype = FORMAT_VideoInfo;
2684 hr = IPin_EnumMediaTypes(sink, &enummt);
2685 ok(hr == S_OK, "Got hr %#x.\n", hr);
2686 hr = IEnumMediaTypes_Next(enummt, 1, mts, NULL);
2687 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2688 IEnumMediaTypes_Release(enummt);
2690 hr = IPin_EnumMediaTypes(capture, &enummt);
2691 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2692 hr = IPin_EnumMediaTypes(preview, &enummt);
2693 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2695 hr = IPin_QueryAccept(sink, &req_mt);
2696 ok(hr == S_OK, "Got hr %#x.\n", hr);
2697 hr = IPin_QueryAccept(capture, &req_mt);
2698 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2699 hr = IPin_QueryAccept(preview, &req_mt);
2700 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2702 /* Test sink connection. */
2704 peer = (IPin *)0xdeadbeef;
2705 hr = IPin_ConnectedTo(sink, &peer);
2706 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2707 ok(!peer, "Got peer %p.\n", peer);
2709 hr = IPin_ConnectionMediaType(sink, &mt);
2710 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2712 hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &req_mt);
2713 ok(hr == S_OK, "Got hr %#x.\n", hr);
2715 hr = IPin_ConnectedTo(sink, &peer);
2716 ok(hr == S_OK, "Got hr %#x.\n", hr);
2717 ok(peer == &testsource.source.pin.IPin_iface, "Got peer %p.\n", peer);
2718 IPin_Release(peer);
2720 hr = IPin_ConnectionMediaType(sink, &mt);
2721 ok(hr == S_OK, "Got hr %#x.\n", hr);
2722 ok(compare_media_types(&mt, &req_mt), "Media types didn't match.\n");
2724 hr = IPin_EnumMediaTypes(sink, &enummt);
2725 ok(hr == S_OK, "Got hr %#x.\n", hr);
2726 hr = IEnumMediaTypes_Next(enummt, 1, mts, NULL);
2727 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2728 IEnumMediaTypes_Release(enummt);
2730 test_source_media_types(req_mt, &testsource.source_mt, capture);
2731 test_source_media_types(req_mt, &testsource.source_mt, preview);
2732 test_source_connection(req_mt, graph, &testsink, capture);
2733 test_source_connection(req_mt, graph, &testsink, preview);
2735 hr = IFilterGraph2_Disconnect(graph, sink);
2736 ok(hr == S_OK, "Got hr %#x.\n", hr);
2737 hr = IFilterGraph2_Disconnect(graph, sink);
2738 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2739 ok(testsource.source.pin.peer == sink, "Got peer %p.\n", testsource.source.pin.peer);
2740 IFilterGraph2_Disconnect(graph, &testsource.source.pin.IPin_iface);
2742 peer = (IPin *)0xdeadbeef;
2743 hr = IPin_ConnectedTo(sink, &peer);
2744 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2745 ok(!peer, "Got peer %p.\n", peer);
2747 hr = IPin_ConnectionMediaType(sink, &mt);
2748 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2750 IPin_Release(sink);
2751 IPin_Release(capture);
2752 IPin_Release(preview);
2753 ref = IFilterGraph2_Release(graph);
2754 ok(!ref, "Got outstanding refcount %d.\n", ref);
2755 ref = IBaseFilter_Release(filter);
2756 ok(!ref, "Got outstanding refcount %d.\n", ref);
2757 ref = IBaseFilter_Release(&testsource.filter.IBaseFilter_iface);
2758 ok(!ref, "Got outstanding refcount %d.\n", ref);
2759 ref = IBaseFilter_Release(&testsink.filter.IBaseFilter_iface);
2760 ok(!ref, "Got outstanding refcount %d.\n", ref);
2763 START_TEST(smartteefilter)
2765 CoInitialize(NULL);
2767 event = CreateEventW(NULL, FALSE, FALSE, NULL);
2769 test_interfaces();
2770 test_aggregation();
2771 test_enum_pins();
2772 test_find_pin();
2773 test_pin_info();
2774 test_enum_media_types();
2775 test_unconnected_filter_state();
2776 test_connect_pin();
2778 test_smart_tee_filter();
2780 CloseHandle(event);
2781 CoUninitialize();