[QUARTZ]
[reactos.git] / rostests / winetests / quartz / filtergraph.c
1 /*
2 * Unit tests for Direct Show functions
3 *
4 * Copyright (C) 2004 Christian Costa
5 * Copyright (C) 2008 Alexander Dorofeyev
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #define COBJMACROS
23 #define CONST_VTABLE
24
25 #include "wine/test.h"
26 #include "dshow.h"
27 #include "control.h"
28
29 typedef struct TestFilterImpl
30 {
31 IBaseFilter IBaseFilter_iface;
32
33 LONG refCount;
34 CRITICAL_SECTION csFilter;
35 FILTER_STATE state;
36 FILTER_INFO filterInfo;
37 CLSID clsid;
38 IPin **ppPins;
39 UINT nPins;
40 } TestFilterImpl;
41
42 static const WCHAR avifile[] = {'t','e','s','t','.','a','v','i',0};
43 static const WCHAR mpegfile[] = {'t','e','s','t','.','m','p','g',0};
44
45 static IGraphBuilder *pgraph;
46
47 static int createfiltergraph(void)
48 {
49 return S_OK == CoCreateInstance(
50 &CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (LPVOID*)&pgraph);
51 }
52
53 static void rungraph(void)
54 {
55 HRESULT hr;
56 IMediaControl* pmc;
57 IMediaEvent* pme;
58 IMediaFilter* pmf;
59 HANDLE hEvent;
60
61 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaControl, (LPVOID*)&pmc);
62 ok(hr==S_OK, "Cannot get IMediaControl interface returned: %x\n", hr);
63
64 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaFilter, (LPVOID*)&pmf);
65 ok(hr==S_OK, "Cannot get IMediaFilter interface returned: %x\n", hr);
66
67 IMediaControl_Stop(pmc);
68
69 IMediaFilter_SetSyncSource(pmf, NULL);
70
71 IMediaFilter_Release(pmf);
72
73 hr = IMediaControl_Run(pmc);
74 ok(hr==S_FALSE, "Cannot run the graph returned: %x\n", hr);
75
76 Sleep(10);
77 /* Crash fun */
78 trace("run -> stop\n");
79 hr = IMediaControl_Stop(pmc);
80 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
81
82 IGraphBuilder_SetDefaultSyncSource(pgraph);
83
84 Sleep(10);
85 trace("stop -> pause\n");
86 hr = IMediaControl_Pause(pmc);
87 ok(hr==S_OK || hr == S_FALSE, "Cannot pause the graph returned: %x\n", hr);
88
89 Sleep(10);
90 trace("pause -> run\n");
91 hr = IMediaControl_Run(pmc);
92 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
93
94 Sleep(10);
95 trace("run -> pause\n");
96 hr = IMediaControl_Pause(pmc);
97 ok(hr==S_OK || hr == S_FALSE, "Cannot pause the graph returned: %x\n", hr);
98
99 Sleep(10);
100 trace("pause -> stop\n");
101 hr = IMediaControl_Stop(pmc);
102 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
103
104 Sleep(10);
105 trace("pause -> run\n");
106 hr = IMediaControl_Run(pmc);
107 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
108
109 trace("run -> stop\n");
110 hr = IMediaControl_Stop(pmc);
111 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
112
113 trace("stop -> run\n");
114 hr = IMediaControl_Run(pmc);
115 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
116
117 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaEvent, (LPVOID*)&pme);
118 ok(hr==S_OK, "Cannot get IMediaEvent interface returned: %x\n", hr);
119
120 hr = IMediaEvent_GetEventHandle(pme, (OAEVENT*)&hEvent);
121 ok(hr==S_OK, "Cannot get event handle returned: %x\n", hr);
122
123 /* WaitForSingleObject(hEvent, INFINITE); */
124 Sleep(20000);
125
126 hr = IMediaEvent_Release(pme);
127 ok(hr==2, "Releasing mediaevent returned: %x\n", hr);
128
129 hr = IMediaControl_Stop(pmc);
130 ok(hr==S_OK, "Cannot stop the graph returned: %x\n", hr);
131
132 hr = IMediaControl_Release(pmc);
133 ok(hr==1, "Releasing mediacontrol returned: %x\n", hr);
134 }
135
136 static void releasefiltergraph(void)
137 {
138 HRESULT hr;
139
140 hr = IGraphBuilder_Release(pgraph);
141 ok(hr==0, "Releasing filtergraph returned: %x\n", hr);
142 }
143
144 static void test_render_run(const WCHAR *file)
145 {
146 HANDLE h;
147 HRESULT hr;
148
149 if (!createfiltergraph())
150 return;
151
152 h = CreateFileW(file, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
153 if (h != INVALID_HANDLE_VALUE) {
154 CloseHandle(h);
155 hr = IGraphBuilder_RenderFile(pgraph, file, NULL);
156 ok(hr==S_OK, "RenderFile returned: %x\n", hr);
157 rungraph();
158 }
159
160 releasefiltergraph();
161 }
162
163 static void test_graph_builder(void)
164 {
165 HRESULT hr;
166 IBaseFilter *pF = NULL;
167 IBaseFilter *pF2 = NULL;
168 IPin *pIn = NULL;
169 IEnumPins *pEnum = NULL;
170 PIN_DIRECTION dir;
171 static const WCHAR testFilterW[] = {'t','e','s','t','F','i','l','t','e','r',0};
172 static const WCHAR fooBarW[] = {'f','o','o','B','a','r',0};
173
174 if (!createfiltergraph())
175 return;
176
177 /* create video filter */
178 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
179 &IID_IBaseFilter, (LPVOID*)&pF);
180 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
181 ok(pF != NULL, "pF is NULL\n");
182
183 hr = IGraphBuilder_AddFilter(pgraph, NULL, testFilterW);
184 ok(hr == E_POINTER, "IGraphBuilder_AddFilter returned %x\n", hr);
185
186 /* add the two filters to the graph */
187 hr = IGraphBuilder_AddFilter(pgraph, pF, testFilterW);
188 ok(hr == S_OK, "failed to add pF to the graph: %x\n", hr);
189
190 /* find the pins */
191 hr = IBaseFilter_EnumPins(pF, &pEnum);
192 ok(hr == S_OK, "IBaseFilter_EnumPins failed for pF: %x\n", hr);
193 ok(pEnum != NULL, "pEnum is NULL\n");
194 hr = IEnumPins_Next(pEnum, 1, &pIn, NULL);
195 ok(hr == S_OK, "IEnumPins_Next failed for pF: %x\n", hr);
196 ok(pIn != NULL, "pIn is NULL\n");
197 hr = IPin_QueryDirection(pIn, &dir);
198 ok(hr == S_OK, "IPin_QueryDirection failed: %x\n", hr);
199 ok(dir == PINDIR_INPUT, "pin has wrong direction\n");
200
201 hr = IGraphBuilder_FindFilterByName(pgraph, fooBarW, &pF2);
202 ok(hr == VFW_E_NOT_FOUND, "IGraphBuilder_FindFilterByName returned %x\n", hr);
203 ok(pF2 == NULL, "IGraphBuilder_FindFilterByName returned %p\n", pF2);
204 hr = IGraphBuilder_FindFilterByName(pgraph, testFilterW, &pF2);
205 ok(hr == S_OK, "IGraphBuilder_FindFilterByName returned %x\n", hr);
206 ok(pF2 != NULL, "IGraphBuilder_FindFilterByName returned NULL\n");
207 hr = IGraphBuilder_FindFilterByName(pgraph, testFilterW, NULL);
208 ok(hr == E_POINTER, "IGraphBuilder_FindFilterByName returned %x\n", hr);
209
210 hr = IGraphBuilder_Connect(pgraph, NULL, pIn);
211 ok(hr == E_POINTER, "IGraphBuilder_Connect returned %x\n", hr);
212
213 hr = IGraphBuilder_Connect(pgraph, pIn, NULL);
214 ok(hr == E_POINTER, "IGraphBuilder_Connect returned %x\n", hr);
215
216 hr = IGraphBuilder_Connect(pgraph, pIn, pIn);
217 ok(hr == VFW_E_CANNOT_CONNECT, "IGraphBuilder_Connect returned %x\n", hr);
218
219 if (pIn) IPin_Release(pIn);
220 if (pEnum) IEnumPins_Release(pEnum);
221 if (pF) IBaseFilter_Release(pF);
222 if (pF2) IBaseFilter_Release(pF2);
223
224 releasefiltergraph();
225 }
226
227 static void test_graph_builder_addfilter(void)
228 {
229 HRESULT hr;
230 IBaseFilter *pF = NULL;
231 static const WCHAR testFilterW[] = {'t','e','s','t','F','i','l','t','e','r',0};
232
233 if (!createfiltergraph())
234 return;
235
236 hr = IGraphBuilder_AddFilter(pgraph, NULL, testFilterW);
237 ok(hr == E_POINTER, "IGraphBuilder_AddFilter returned: %x\n", hr);
238
239 /* create video filter */
240 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
241 &IID_IBaseFilter, (LPVOID*)&pF);
242 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
243 ok(pF != NULL, "pF is NULL\n");
244 if (!pF) {
245 skip("failed to created filter, skipping\n");
246 return;
247 }
248
249 hr = IGraphBuilder_AddFilter(pgraph, pF, NULL);
250 ok(hr == S_OK, "IGraphBuilder_AddFilter returned: %x\n", hr);
251 IBaseFilter_Release(pF);
252 }
253
254 static void test_mediacontrol(void)
255 {
256 HRESULT hr;
257 LONGLONG pos = 0xdeadbeef;
258 IMediaSeeking *seeking = NULL;
259 IMediaFilter *filter = NULL;
260 IMediaControl *control = NULL;
261
262 IGraphBuilder_SetDefaultSyncSource(pgraph);
263 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaSeeking, (void**) &seeking);
264 ok(hr == S_OK, "QueryInterface IMediaControl failed: %08x\n", hr);
265 if (FAILED(hr))
266 return;
267
268 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaFilter, (void**) &filter);
269 ok(hr == S_OK, "QueryInterface IMediaFilter failed: %08x\n", hr);
270 if (FAILED(hr))
271 {
272 IMediaSeeking_Release(seeking);
273 return;
274 }
275
276 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaControl, (void**) &control);
277 ok(hr == S_OK, "QueryInterface IMediaControl failed: %08x\n", hr);
278 if (FAILED(hr))
279 {
280 IMediaSeeking_Release(seeking);
281 IMediaFilter_Release(filter);
282 return;
283 }
284
285 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
286 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
287 ok(pos == 0, "Position != 0 (%x%08x)\n", (DWORD)(pos >> 32), (DWORD)pos);
288
289 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
290 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
291 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_NoPositioning, NULL, AM_SEEKING_ReturnTime);
292 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
293
294 IMediaFilter_SetSyncSource(filter, NULL);
295 pos = 0xdeadbeef;
296 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
297 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
298 ok(pos == 0, "Position != 0 (%x%08x)\n", (DWORD)(pos >> 32), (DWORD)pos);
299
300 hr = IMediaControl_GetState(control, 1000, NULL);
301 ok(hr == E_POINTER, "GetState expected %08x, got %08x\n", E_POINTER, hr);
302
303 IMediaControl_Release(control);
304 IMediaSeeking_Release(seeking);
305 IMediaFilter_Release(filter);
306 releasefiltergraph();
307 }
308
309 static void test_filter_graph2(void)
310 {
311 HRESULT hr;
312 IFilterGraph2 *pF = NULL;
313
314 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
315 &IID_IFilterGraph2, (LPVOID*)&pF);
316 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
317 ok(pF != NULL, "pF is NULL\n");
318
319 hr = IFilterGraph2_Release(pF);
320 ok(hr == 0, "IFilterGraph2_Release returned: %x\n", hr);
321 }
322
323 /* IEnumMediaTypes implementation (supporting code for Render() test.) */
324 static void FreeMediaType(AM_MEDIA_TYPE * pMediaType)
325 {
326 if (pMediaType->pbFormat)
327 {
328 CoTaskMemFree(pMediaType->pbFormat);
329 pMediaType->pbFormat = NULL;
330 }
331 if (pMediaType->pUnk)
332 {
333 IUnknown_Release(pMediaType->pUnk);
334 pMediaType->pUnk = NULL;
335 }
336 }
337
338 static HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
339 {
340 *pDest = *pSrc;
341 if (!pSrc->pbFormat) return S_OK;
342 if (!(pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat)))
343 return E_OUTOFMEMORY;
344 memcpy(pDest->pbFormat, pSrc->pbFormat, pSrc->cbFormat);
345 if (pDest->pUnk)
346 IUnknown_AddRef(pDest->pUnk);
347 return S_OK;
348 }
349
350 static AM_MEDIA_TYPE * CreateMediaType(AM_MEDIA_TYPE const * pSrc)
351 {
352 AM_MEDIA_TYPE * pDest;
353
354 pDest = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
355 if (!pDest)
356 return NULL;
357
358 if (FAILED(CopyMediaType(pDest, pSrc)))
359 {
360 CoTaskMemFree(pDest);
361 return NULL;
362 }
363
364 return pDest;
365 }
366
367 static BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
368 {
369 return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) &&
370 ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL) || IsEqualGUID(&pmt2->subtype, &GUID_NULL))) || IsEqualGUID(&pmt1->subtype, &pmt2->subtype)));
371 }
372
373 static void DeleteMediaType(AM_MEDIA_TYPE * pMediaType)
374 {
375 FreeMediaType(pMediaType);
376 CoTaskMemFree(pMediaType);
377 }
378
379 typedef struct IEnumMediaTypesImpl
380 {
381 IEnumMediaTypes IEnumMediaTypes_iface;
382 LONG refCount;
383 AM_MEDIA_TYPE *pMediaTypes;
384 ULONG cMediaTypes;
385 ULONG uIndex;
386 } IEnumMediaTypesImpl;
387
388 static const struct IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl;
389
390 static inline IEnumMediaTypesImpl *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
391 {
392 return CONTAINING_RECORD(iface, IEnumMediaTypesImpl, IEnumMediaTypes_iface);
393 }
394
395 static HRESULT IEnumMediaTypesImpl_Construct(const AM_MEDIA_TYPE * pMediaTypes, ULONG cMediaTypes, IEnumMediaTypes ** ppEnum)
396 {
397 ULONG i;
398 IEnumMediaTypesImpl * pEnumMediaTypes = CoTaskMemAlloc(sizeof(IEnumMediaTypesImpl));
399
400 if (!pEnumMediaTypes)
401 {
402 *ppEnum = NULL;
403 return E_OUTOFMEMORY;
404 }
405 pEnumMediaTypes->IEnumMediaTypes_iface.lpVtbl = &IEnumMediaTypesImpl_Vtbl;
406 pEnumMediaTypes->refCount = 1;
407 pEnumMediaTypes->uIndex = 0;
408 pEnumMediaTypes->cMediaTypes = cMediaTypes;
409 pEnumMediaTypes->pMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * cMediaTypes);
410 for (i = 0; i < cMediaTypes; i++)
411 if (FAILED(CopyMediaType(&pEnumMediaTypes->pMediaTypes[i], &pMediaTypes[i])))
412 {
413 while (i--)
414 FreeMediaType(&pEnumMediaTypes->pMediaTypes[i]);
415 CoTaskMemFree(pEnumMediaTypes->pMediaTypes);
416 return E_OUTOFMEMORY;
417 }
418 *ppEnum = &pEnumMediaTypes->IEnumMediaTypes_iface;
419 return S_OK;
420 }
421
422 static HRESULT WINAPI IEnumMediaTypesImpl_QueryInterface(IEnumMediaTypes * iface, REFIID riid, LPVOID * ppv)
423 {
424 *ppv = NULL;
425
426 if (IsEqualIID(riid, &IID_IUnknown))
427 *ppv = iface;
428 else if (IsEqualIID(riid, &IID_IEnumMediaTypes))
429 *ppv = iface;
430
431 if (*ppv)
432 {
433 IUnknown_AddRef((IUnknown *)(*ppv));
434 return S_OK;
435 }
436
437 return E_NOINTERFACE;
438 }
439
440 static ULONG WINAPI IEnumMediaTypesImpl_AddRef(IEnumMediaTypes * iface)
441 {
442 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
443 ULONG refCount = InterlockedIncrement(&This->refCount);
444
445 return refCount;
446 }
447
448 static ULONG WINAPI IEnumMediaTypesImpl_Release(IEnumMediaTypes * iface)
449 {
450 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
451 ULONG refCount = InterlockedDecrement(&This->refCount);
452
453 if (!refCount)
454 {
455 int i;
456 for (i = 0; i < This->cMediaTypes; i++)
457 FreeMediaType(&This->pMediaTypes[i]);
458 CoTaskMemFree(This->pMediaTypes);
459 CoTaskMemFree(This);
460 }
461 return refCount;
462 }
463
464 static HRESULT WINAPI IEnumMediaTypesImpl_Next(IEnumMediaTypes * iface, ULONG cMediaTypes, AM_MEDIA_TYPE ** ppMediaTypes, ULONG * pcFetched)
465 {
466 ULONG cFetched;
467 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
468
469 cFetched = min(This->cMediaTypes, This->uIndex + cMediaTypes) - This->uIndex;
470
471 if (cFetched > 0)
472 {
473 ULONG i;
474 for (i = 0; i < cFetched; i++)
475 if (!(ppMediaTypes[i] = CreateMediaType(&This->pMediaTypes[This->uIndex + i])))
476 {
477 while (i--)
478 DeleteMediaType(ppMediaTypes[i]);
479 *pcFetched = 0;
480 return E_OUTOFMEMORY;
481 }
482 }
483
484 if ((cMediaTypes != 1) || pcFetched)
485 *pcFetched = cFetched;
486
487 This->uIndex += cFetched;
488
489 if (cFetched != cMediaTypes)
490 return S_FALSE;
491 return S_OK;
492 }
493
494 static HRESULT WINAPI IEnumMediaTypesImpl_Skip(IEnumMediaTypes * iface, ULONG cMediaTypes)
495 {
496 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
497
498 if (This->uIndex + cMediaTypes < This->cMediaTypes)
499 {
500 This->uIndex += cMediaTypes;
501 return S_OK;
502 }
503 return S_FALSE;
504 }
505
506 static HRESULT WINAPI IEnumMediaTypesImpl_Reset(IEnumMediaTypes * iface)
507 {
508 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
509
510 This->uIndex = 0;
511 return S_OK;
512 }
513
514 static HRESULT WINAPI IEnumMediaTypesImpl_Clone(IEnumMediaTypes * iface, IEnumMediaTypes ** ppEnum)
515 {
516 HRESULT hr;
517 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
518
519 hr = IEnumMediaTypesImpl_Construct(This->pMediaTypes, This->cMediaTypes, ppEnum);
520 if (FAILED(hr))
521 return hr;
522 return IEnumMediaTypes_Skip(*ppEnum, This->uIndex);
523 }
524
525 static const IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl =
526 {
527 IEnumMediaTypesImpl_QueryInterface,
528 IEnumMediaTypesImpl_AddRef,
529 IEnumMediaTypesImpl_Release,
530 IEnumMediaTypesImpl_Next,
531 IEnumMediaTypesImpl_Skip,
532 IEnumMediaTypesImpl_Reset,
533 IEnumMediaTypesImpl_Clone
534 };
535
536 /* Implementation of a very stripped down pin for the test filter. Just enough
537 functionality for connecting and Render() to work. */
538
539 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
540 {
541 lstrcpyW(pDest->achName, pSrc->achName);
542 pDest->dir = pSrc->dir;
543 pDest->pFilter = pSrc->pFilter;
544 }
545
546 typedef struct ITestPinImpl
547 {
548 IPin IPin_iface;
549 LONG refCount;
550 LPCRITICAL_SECTION pCritSec;
551 PIN_INFO pinInfo;
552 IPin * pConnectedTo;
553 AM_MEDIA_TYPE mtCurrent;
554 LPVOID pUserData;
555 } ITestPinImpl;
556
557 static inline ITestPinImpl *impl_from_IPin(IPin *iface)
558 {
559 return CONTAINING_RECORD(iface, ITestPinImpl, IPin_iface);
560 }
561
562 static HRESULT WINAPI TestFilter_Pin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
563 {
564 *ppv = NULL;
565
566 if (IsEqualIID(riid, &IID_IUnknown))
567 *ppv = iface;
568 else if (IsEqualIID(riid, &IID_IPin))
569 *ppv = iface;
570
571 if (*ppv)
572 {
573 IUnknown_AddRef((IUnknown *)(*ppv));
574 return S_OK;
575 }
576
577 return E_NOINTERFACE;
578 }
579
580 static ULONG WINAPI TestFilter_Pin_AddRef(IPin * iface)
581 {
582 ITestPinImpl *This = impl_from_IPin(iface);
583 ULONG refCount = InterlockedIncrement(&This->refCount);
584 return refCount;
585 }
586
587 static ULONG WINAPI TestFilter_Pin_Release(IPin * iface)
588 {
589 ITestPinImpl *This = impl_from_IPin(iface);
590 ULONG refCount = InterlockedDecrement(&This->refCount);
591
592 if (!refCount)
593 {
594 FreeMediaType(&This->mtCurrent);
595 CoTaskMemFree(This);
596 return 0;
597 }
598 else
599 return refCount;
600 }
601
602 static HRESULT WINAPI TestFilter_InputPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
603 {
604 return E_UNEXPECTED;
605 }
606
607 static HRESULT WINAPI TestFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
608 {
609 ITestPinImpl *This = impl_from_IPin(iface);
610 PIN_DIRECTION pindirReceive;
611 HRESULT hr = S_OK;
612
613 EnterCriticalSection(This->pCritSec);
614 {
615 if (!(IsEqualIID(&pmt->majortype, &This->mtCurrent.majortype) && (IsEqualIID(&pmt->subtype, &This->mtCurrent.subtype) ||
616 IsEqualIID(&GUID_NULL, &This->mtCurrent.subtype))))
617 hr = VFW_E_TYPE_NOT_ACCEPTED;
618
619 if (This->pConnectedTo)
620 hr = VFW_E_ALREADY_CONNECTED;
621
622 if (SUCCEEDED(hr))
623 {
624 IPin_QueryDirection(pReceivePin, &pindirReceive);
625
626 if (pindirReceive != PINDIR_OUTPUT)
627 {
628 hr = VFW_E_INVALID_DIRECTION;
629 }
630 }
631
632 if (SUCCEEDED(hr))
633 {
634 CopyMediaType(&This->mtCurrent, pmt);
635 This->pConnectedTo = pReceivePin;
636 IPin_AddRef(pReceivePin);
637 }
638 }
639 LeaveCriticalSection(This->pCritSec);
640
641 return hr;
642 }
643
644 static HRESULT WINAPI TestFilter_Pin_Disconnect(IPin * iface)
645 {
646 HRESULT hr;
647 ITestPinImpl *This = impl_from_IPin(iface);
648
649 EnterCriticalSection(This->pCritSec);
650 {
651 if (This->pConnectedTo)
652 {
653 IPin_Release(This->pConnectedTo);
654 This->pConnectedTo = NULL;
655 hr = S_OK;
656 }
657 else
658 hr = S_FALSE;
659 }
660 LeaveCriticalSection(This->pCritSec);
661
662 return hr;
663 }
664
665 static HRESULT WINAPI TestFilter_Pin_ConnectedTo(IPin * iface, IPin ** ppPin)
666 {
667 HRESULT hr;
668 ITestPinImpl *This = impl_from_IPin(iface);
669
670 EnterCriticalSection(This->pCritSec);
671 {
672 if (This->pConnectedTo)
673 {
674 *ppPin = This->pConnectedTo;
675 IPin_AddRef(*ppPin);
676 hr = S_OK;
677 }
678 else
679 {
680 hr = VFW_E_NOT_CONNECTED;
681 *ppPin = NULL;
682 }
683 }
684 LeaveCriticalSection(This->pCritSec);
685
686 return hr;
687 }
688
689 static HRESULT WINAPI TestFilter_Pin_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
690 {
691 HRESULT hr;
692 ITestPinImpl *This = impl_from_IPin(iface);
693
694 EnterCriticalSection(This->pCritSec);
695 {
696 if (This->pConnectedTo)
697 {
698 CopyMediaType(pmt, &This->mtCurrent);
699 hr = S_OK;
700 }
701 else
702 {
703 ZeroMemory(pmt, sizeof(*pmt));
704 hr = VFW_E_NOT_CONNECTED;
705 }
706 }
707 LeaveCriticalSection(This->pCritSec);
708
709 return hr;
710 }
711
712 static HRESULT WINAPI TestFilter_Pin_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
713 {
714 ITestPinImpl *This = impl_from_IPin(iface);
715
716 Copy_PinInfo(pInfo, &This->pinInfo);
717 IBaseFilter_AddRef(pInfo->pFilter);
718
719 return S_OK;
720 }
721
722 static HRESULT WINAPI TestFilter_Pin_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
723 {
724 ITestPinImpl *This = impl_from_IPin(iface);
725
726 *pPinDir = This->pinInfo.dir;
727
728 return S_OK;
729 }
730
731 static HRESULT WINAPI TestFilter_Pin_QueryId(IPin * iface, LPWSTR * Id)
732 {
733 return E_NOTIMPL;
734 }
735
736 static HRESULT WINAPI TestFilter_Pin_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
737 {
738 ITestPinImpl *This = impl_from_IPin(iface);
739
740 if (IsEqualIID(&pmt->majortype, &This->mtCurrent.majortype) && (IsEqualIID(&pmt->subtype, &This->mtCurrent.subtype) ||
741 IsEqualIID(&GUID_NULL, &This->mtCurrent.subtype)))
742 return S_OK;
743 else
744 return VFW_E_TYPE_NOT_ACCEPTED;
745 }
746
747 static HRESULT WINAPI TestFilter_Pin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
748 {
749 ITestPinImpl *This = impl_from_IPin(iface);
750
751 return IEnumMediaTypesImpl_Construct(&This->mtCurrent, 1, ppEnum);
752 }
753
754 static HRESULT WINAPI TestFilter_Pin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
755 {
756 return E_NOTIMPL;
757 }
758
759 static HRESULT WINAPI TestFilter_Pin_BeginFlush(IPin * iface)
760 {
761 return E_NOTIMPL;
762 }
763
764 static HRESULT WINAPI TestFilter_Pin_EndFlush(IPin * iface)
765 {
766 return E_NOTIMPL;
767 }
768
769 static HRESULT WINAPI TestFilter_Pin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
770 {
771 return E_NOTIMPL;
772 }
773
774 static HRESULT WINAPI TestFilter_Pin_EndOfStream(IPin * iface)
775 {
776 return E_NOTIMPL;
777 }
778
779 static const IPinVtbl TestFilter_InputPin_Vtbl =
780 {
781 TestFilter_Pin_QueryInterface,
782 TestFilter_Pin_AddRef,
783 TestFilter_Pin_Release,
784 TestFilter_InputPin_Connect,
785 TestFilter_InputPin_ReceiveConnection,
786 TestFilter_Pin_Disconnect,
787 TestFilter_Pin_ConnectedTo,
788 TestFilter_Pin_ConnectionMediaType,
789 TestFilter_Pin_QueryPinInfo,
790 TestFilter_Pin_QueryDirection,
791 TestFilter_Pin_QueryId,
792 TestFilter_Pin_QueryAccept,
793 TestFilter_Pin_EnumMediaTypes,
794 TestFilter_Pin_QueryInternalConnections,
795 TestFilter_Pin_EndOfStream,
796 TestFilter_Pin_BeginFlush,
797 TestFilter_Pin_EndFlush,
798 TestFilter_Pin_NewSegment
799 };
800
801 static HRESULT WINAPI TestFilter_OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
802 {
803 return E_UNEXPECTED;
804 }
805
806 /* Private helper function */
807 static HRESULT TestFilter_OutputPin_ConnectSpecific(ITestPinImpl * This, IPin * pReceivePin,
808 const AM_MEDIA_TYPE * pmt)
809 {
810 HRESULT hr;
811
812 This->pConnectedTo = pReceivePin;
813 IPin_AddRef(pReceivePin);
814
815 hr = IPin_ReceiveConnection(pReceivePin, &This->IPin_iface, pmt);
816
817 if (FAILED(hr))
818 {
819 IPin_Release(This->pConnectedTo);
820 This->pConnectedTo = NULL;
821 }
822
823 return hr;
824 }
825
826 static HRESULT WINAPI TestFilter_OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
827 {
828 ITestPinImpl *This = impl_from_IPin(iface);
829 HRESULT hr;
830
831 EnterCriticalSection(This->pCritSec);
832 {
833 /* if we have been a specific type to connect with, then we can either connect
834 * with that or fail. We cannot choose different AM_MEDIA_TYPE */
835 if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
836 hr = TestFilter_OutputPin_ConnectSpecific(This, pReceivePin, pmt);
837 else
838 {
839 if (( !pmt || CompareMediaTypes(pmt, &This->mtCurrent, TRUE) ) &&
840 (TestFilter_OutputPin_ConnectSpecific(This, pReceivePin, &This->mtCurrent) == S_OK))
841 hr = S_OK;
842 else hr = VFW_E_NO_ACCEPTABLE_TYPES;
843 } /* if negotiate media type */
844 } /* if succeeded */
845 LeaveCriticalSection(This->pCritSec);
846
847 return hr;
848 }
849
850 static const IPinVtbl TestFilter_OutputPin_Vtbl =
851 {
852 TestFilter_Pin_QueryInterface,
853 TestFilter_Pin_AddRef,
854 TestFilter_Pin_Release,
855 TestFilter_OutputPin_Connect,
856 TestFilter_OutputPin_ReceiveConnection,
857 TestFilter_Pin_Disconnect,
858 TestFilter_Pin_ConnectedTo,
859 TestFilter_Pin_ConnectionMediaType,
860 TestFilter_Pin_QueryPinInfo,
861 TestFilter_Pin_QueryDirection,
862 TestFilter_Pin_QueryId,
863 TestFilter_Pin_QueryAccept,
864 TestFilter_Pin_EnumMediaTypes,
865 TestFilter_Pin_QueryInternalConnections,
866 TestFilter_Pin_EndOfStream,
867 TestFilter_Pin_BeginFlush,
868 TestFilter_Pin_EndFlush,
869 TestFilter_Pin_NewSegment
870 };
871
872 static HRESULT TestFilter_Pin_Construct(const IPinVtbl *Pin_Vtbl, const PIN_INFO * pPinInfo, AM_MEDIA_TYPE *pinmt,
873 LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
874 {
875 ITestPinImpl * pPinImpl;
876
877 *ppPin = NULL;
878
879 pPinImpl = CoTaskMemAlloc(sizeof(ITestPinImpl));
880
881 if (!pPinImpl)
882 return E_OUTOFMEMORY;
883
884 pPinImpl->refCount = 1;
885 pPinImpl->pConnectedTo = NULL;
886 pPinImpl->pCritSec = pCritSec;
887 Copy_PinInfo(&pPinImpl->pinInfo, pPinInfo);
888 pPinImpl->mtCurrent = *pinmt;
889
890 pPinImpl->IPin_iface.lpVtbl = Pin_Vtbl;
891
892 *ppPin = &pPinImpl->IPin_iface;
893 return S_OK;
894 }
895
896 /* IEnumPins implementation */
897
898 typedef HRESULT (* FNOBTAINPIN)(TestFilterImpl *tf, ULONG pos, IPin **pin, DWORD *lastsynctick);
899
900 typedef struct IEnumPinsImpl
901 {
902 IEnumPins IEnumPins_iface;
903 LONG refCount;
904 ULONG uIndex;
905 TestFilterImpl *base;
906 FNOBTAINPIN receive_pin;
907 DWORD synctime;
908 } IEnumPinsImpl;
909
910 static const struct IEnumPinsVtbl IEnumPinsImpl_Vtbl;
911
912 static inline IEnumPinsImpl *impl_from_IEnumPins(IEnumPins *iface)
913 {
914 return CONTAINING_RECORD(iface, IEnumPinsImpl, IEnumPins_iface);
915 }
916
917 static HRESULT createenumpins(IEnumPins ** ppEnum, FNOBTAINPIN receive_pin, TestFilterImpl *base)
918 {
919 IEnumPinsImpl * pEnumPins;
920
921 if (!ppEnum)
922 return E_POINTER;
923
924 pEnumPins = CoTaskMemAlloc(sizeof(IEnumPinsImpl));
925 if (!pEnumPins)
926 {
927 *ppEnum = NULL;
928 return E_OUTOFMEMORY;
929 }
930 pEnumPins->IEnumPins_iface.lpVtbl = &IEnumPinsImpl_Vtbl;
931 pEnumPins->refCount = 1;
932 pEnumPins->uIndex = 0;
933 pEnumPins->receive_pin = receive_pin;
934 pEnumPins->base = base;
935 IBaseFilter_AddRef(&base->IBaseFilter_iface);
936 *ppEnum = &pEnumPins->IEnumPins_iface;
937
938 receive_pin(base, ~0, NULL, &pEnumPins->synctime);
939
940 return S_OK;
941 }
942
943 static HRESULT WINAPI IEnumPinsImpl_QueryInterface(IEnumPins * iface, REFIID riid, LPVOID * ppv)
944 {
945 *ppv = NULL;
946
947 if (IsEqualIID(riid, &IID_IUnknown))
948 *ppv = iface;
949 else if (IsEqualIID(riid, &IID_IEnumPins))
950 *ppv = iface;
951
952 if (*ppv)
953 {
954 IUnknown_AddRef((IUnknown *)(*ppv));
955 return S_OK;
956 }
957
958 return E_NOINTERFACE;
959 }
960
961 static ULONG WINAPI IEnumPinsImpl_AddRef(IEnumPins * iface)
962 {
963 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
964 ULONG refCount = InterlockedIncrement(&This->refCount);
965
966 return refCount;
967 }
968
969 static ULONG WINAPI IEnumPinsImpl_Release(IEnumPins * iface)
970 {
971 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
972 ULONG refCount = InterlockedDecrement(&This->refCount);
973
974 if (!refCount)
975 {
976 IBaseFilter_Release(&This->base->IBaseFilter_iface);
977 CoTaskMemFree(This);
978 return 0;
979 }
980 else
981 return refCount;
982 }
983
984 static HRESULT WINAPI IEnumPinsImpl_Next(IEnumPins * iface, ULONG cPins, IPin ** ppPins, ULONG * pcFetched)
985 {
986 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
987 DWORD synctime = This->synctime;
988 HRESULT hr = S_OK;
989 ULONG i = 0;
990
991 if (!ppPins)
992 return E_POINTER;
993
994 if (cPins > 1 && !pcFetched)
995 return E_INVALIDARG;
996
997 if (pcFetched)
998 *pcFetched = 0;
999
1000 while (i < cPins && hr == S_OK)
1001 {
1002 hr = This->receive_pin(This->base, This->uIndex + i, &ppPins[i], &synctime);
1003
1004 if (hr == S_OK)
1005 ++i;
1006
1007 if (synctime != This->synctime)
1008 break;
1009 }
1010
1011 if (!i && synctime != This->synctime)
1012 return VFW_E_ENUM_OUT_OF_SYNC;
1013
1014 if (pcFetched)
1015 *pcFetched = i;
1016 This->uIndex += i;
1017
1018 if (i < cPins)
1019 return S_FALSE;
1020 return S_OK;
1021 }
1022
1023 static HRESULT WINAPI IEnumPinsImpl_Skip(IEnumPins * iface, ULONG cPins)
1024 {
1025 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1026 DWORD synctime = This->synctime;
1027 HRESULT hr;
1028 IPin *pin = NULL;
1029
1030 hr = This->receive_pin(This->base, This->uIndex + cPins, &pin, &synctime);
1031 if (pin)
1032 IPin_Release(pin);
1033
1034 if (synctime != This->synctime)
1035 return VFW_E_ENUM_OUT_OF_SYNC;
1036
1037 if (hr == S_OK)
1038 This->uIndex += cPins;
1039
1040 return hr;
1041 }
1042
1043 static HRESULT WINAPI IEnumPinsImpl_Reset(IEnumPins * iface)
1044 {
1045 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1046
1047 This->receive_pin(This->base, ~0, NULL, &This->synctime);
1048
1049 This->uIndex = 0;
1050 return S_OK;
1051 }
1052
1053 static HRESULT WINAPI IEnumPinsImpl_Clone(IEnumPins * iface, IEnumPins ** ppEnum)
1054 {
1055 HRESULT hr;
1056 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1057
1058 hr = createenumpins(ppEnum, This->receive_pin, This->base);
1059 if (FAILED(hr))
1060 return hr;
1061 return IEnumPins_Skip(*ppEnum, This->uIndex);
1062 }
1063
1064 static const IEnumPinsVtbl IEnumPinsImpl_Vtbl =
1065 {
1066 IEnumPinsImpl_QueryInterface,
1067 IEnumPinsImpl_AddRef,
1068 IEnumPinsImpl_Release,
1069 IEnumPinsImpl_Next,
1070 IEnumPinsImpl_Skip,
1071 IEnumPinsImpl_Reset,
1072 IEnumPinsImpl_Clone
1073 };
1074
1075 /* Test filter implementation - a filter that has few predefined pins with single media type
1076 * that accept only this single media type. Enough for Render(). */
1077
1078 typedef struct TestFilterPinData
1079 {
1080 PIN_DIRECTION pinDir;
1081 const GUID *mediasubtype;
1082 } TestFilterPinData;
1083
1084 static const IBaseFilterVtbl TestFilter_Vtbl;
1085
1086 static inline TestFilterImpl *impl_from_IBaseFilter(IBaseFilter *iface)
1087 {
1088 return CONTAINING_RECORD(iface, TestFilterImpl, IBaseFilter_iface);
1089 }
1090
1091 static HRESULT createtestfilter(const CLSID* pClsid, const TestFilterPinData *pinData,
1092 TestFilterImpl **tf)
1093 {
1094 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
1095 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
1096 HRESULT hr;
1097 PIN_INFO pinInfo;
1098 TestFilterImpl* pTestFilter = NULL;
1099 UINT nPins, i;
1100 AM_MEDIA_TYPE mt;
1101
1102 pTestFilter = CoTaskMemAlloc(sizeof(TestFilterImpl));
1103 if (!pTestFilter) return E_OUTOFMEMORY;
1104
1105 pTestFilter->clsid = *pClsid;
1106 pTestFilter->IBaseFilter_iface.lpVtbl = &TestFilter_Vtbl;
1107 pTestFilter->refCount = 1;
1108 InitializeCriticalSection(&pTestFilter->csFilter);
1109 pTestFilter->state = State_Stopped;
1110
1111 ZeroMemory(&pTestFilter->filterInfo, sizeof(FILTER_INFO));
1112
1113 nPins = 0;
1114 while(pinData[nPins].mediasubtype) ++nPins;
1115
1116 pTestFilter->ppPins = CoTaskMemAlloc(nPins * sizeof(IPin *));
1117 if (!pTestFilter->ppPins)
1118 {
1119 hr = E_OUTOFMEMORY;
1120 goto error;
1121 }
1122 ZeroMemory(pTestFilter->ppPins, nPins * sizeof(IPin *));
1123
1124 for (i = 0; i < nPins; i++)
1125 {
1126 ZeroMemory(&mt, sizeof(mt));
1127 mt.majortype = MEDIATYPE_Video;
1128 mt.formattype = FORMAT_None;
1129 mt.subtype = *pinData[i].mediasubtype;
1130
1131 pinInfo.dir = pinData[i].pinDir;
1132 pinInfo.pFilter = &pTestFilter->IBaseFilter_iface;
1133 if (pinInfo.dir == PINDIR_INPUT)
1134 {
1135 lstrcpynW(pinInfo.achName, wcsInputPinName, sizeof(pinInfo.achName) / sizeof(pinInfo.achName[0]));
1136 hr = TestFilter_Pin_Construct(&TestFilter_InputPin_Vtbl, &pinInfo, &mt, &pTestFilter->csFilter,
1137 &pTestFilter->ppPins[i]);
1138
1139 }
1140 else
1141 {
1142 lstrcpynW(pinInfo.achName, wcsOutputPinName, sizeof(pinInfo.achName) / sizeof(pinInfo.achName[0]));
1143 hr = TestFilter_Pin_Construct(&TestFilter_OutputPin_Vtbl, &pinInfo, &mt, &pTestFilter->csFilter,
1144 &pTestFilter->ppPins[i]);
1145 }
1146 if (FAILED(hr) || !pTestFilter->ppPins[i]) goto error;
1147 }
1148
1149 pTestFilter->nPins = nPins;
1150 *tf = pTestFilter;
1151 return S_OK;
1152
1153 error:
1154
1155 if (pTestFilter->ppPins)
1156 {
1157 for (i = 0; i < nPins; i++)
1158 {
1159 if (pTestFilter->ppPins[i]) IPin_Release(pTestFilter->ppPins[i]);
1160 }
1161 }
1162 CoTaskMemFree(pTestFilter->ppPins);
1163 DeleteCriticalSection(&pTestFilter->csFilter);
1164 CoTaskMemFree(pTestFilter);
1165
1166 return hr;
1167 }
1168
1169 static HRESULT WINAPI TestFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
1170 {
1171 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1172
1173 *ppv = NULL;
1174
1175 if (IsEqualIID(riid, &IID_IUnknown))
1176 *ppv = This;
1177 else if (IsEqualIID(riid, &IID_IPersist))
1178 *ppv = This;
1179 else if (IsEqualIID(riid, &IID_IMediaFilter))
1180 *ppv = This;
1181 else if (IsEqualIID(riid, &IID_IBaseFilter))
1182 *ppv = This;
1183
1184 if (*ppv)
1185 {
1186 IUnknown_AddRef((IUnknown *)(*ppv));
1187 return S_OK;
1188 }
1189
1190 return E_NOINTERFACE;
1191 }
1192
1193 static ULONG WINAPI TestFilter_AddRef(IBaseFilter * iface)
1194 {
1195 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1196 ULONG refCount = InterlockedIncrement(&This->refCount);
1197
1198 return refCount;
1199 }
1200
1201 static ULONG WINAPI TestFilter_Release(IBaseFilter * iface)
1202 {
1203 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1204 ULONG refCount = InterlockedDecrement(&This->refCount);
1205
1206 if (!refCount)
1207 {
1208 ULONG i;
1209
1210 for (i = 0; i < This->nPins; i++)
1211 {
1212 IPin *pConnectedTo;
1213
1214 if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
1215 {
1216 IPin_Disconnect(pConnectedTo);
1217 IPin_Release(pConnectedTo);
1218 }
1219 IPin_Disconnect(This->ppPins[i]);
1220
1221 IPin_Release(This->ppPins[i]);
1222 }
1223
1224 CoTaskMemFree(This->ppPins);
1225
1226 DeleteCriticalSection(&This->csFilter);
1227
1228 CoTaskMemFree(This);
1229
1230 return 0;
1231 }
1232 else
1233 return refCount;
1234 }
1235 /** IPersist methods **/
1236
1237 static HRESULT WINAPI TestFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
1238 {
1239 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1240
1241 *pClsid = This->clsid;
1242
1243 return S_OK;
1244 }
1245
1246 /** IMediaFilter methods **/
1247
1248 static HRESULT WINAPI TestFilter_Stop(IBaseFilter * iface)
1249 {
1250 return E_NOTIMPL;
1251 }
1252
1253 static HRESULT WINAPI TestFilter_Pause(IBaseFilter * iface)
1254 {
1255 return E_NOTIMPL;
1256 }
1257
1258 static HRESULT WINAPI TestFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
1259 {
1260 return E_NOTIMPL;
1261 }
1262
1263 static HRESULT WINAPI TestFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
1264 {
1265 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1266
1267 EnterCriticalSection(&This->csFilter);
1268 {
1269 *pState = This->state;
1270 }
1271 LeaveCriticalSection(&This->csFilter);
1272
1273 return S_OK;
1274 }
1275
1276 static HRESULT WINAPI TestFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
1277 {
1278 return E_NOTIMPL;
1279 }
1280
1281 static HRESULT WINAPI TestFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
1282 {
1283 return E_NOTIMPL;
1284 }
1285
1286 /** IBaseFilter implementation **/
1287
1288 static HRESULT getpin_callback(TestFilterImpl *tf, ULONG pos, IPin **pin, DWORD *lastsynctick)
1289 {
1290 /* Our pins are static, not changing so setting static tick count is ok */
1291 *lastsynctick = 0;
1292
1293 if (pos >= tf->nPins)
1294 return S_FALSE;
1295
1296 *pin = tf->ppPins[pos];
1297 IPin_AddRef(*pin);
1298 return S_OK;
1299 }
1300
1301 static HRESULT WINAPI TestFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
1302 {
1303 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1304
1305 return createenumpins(ppEnum, getpin_callback, This);
1306 }
1307
1308 static HRESULT WINAPI TestFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
1309 {
1310 return E_NOTIMPL;
1311 }
1312
1313 static HRESULT WINAPI TestFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
1314 {
1315 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1316
1317 lstrcpyW(pInfo->achName, This->filterInfo.achName);
1318 pInfo->pGraph = This->filterInfo.pGraph;
1319
1320 if (pInfo->pGraph)
1321 IFilterGraph_AddRef(pInfo->pGraph);
1322
1323 return S_OK;
1324 }
1325
1326 static HRESULT WINAPI TestFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
1327 {
1328 HRESULT hr = S_OK;
1329 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1330
1331 EnterCriticalSection(&This->csFilter);
1332 {
1333 if (pName)
1334 lstrcpyW(This->filterInfo.achName, pName);
1335 else
1336 *This->filterInfo.achName = '\0';
1337 This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
1338 }
1339 LeaveCriticalSection(&This->csFilter);
1340
1341 return hr;
1342 }
1343
1344 static HRESULT WINAPI TestFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
1345 {
1346 return E_NOTIMPL;
1347 }
1348
1349 static const IBaseFilterVtbl TestFilter_Vtbl =
1350 {
1351 TestFilter_QueryInterface,
1352 TestFilter_AddRef,
1353 TestFilter_Release,
1354 TestFilter_GetClassID,
1355 TestFilter_Stop,
1356 TestFilter_Pause,
1357 TestFilter_Run,
1358 TestFilter_GetState,
1359 TestFilter_SetSyncSource,
1360 TestFilter_GetSyncSource,
1361 TestFilter_EnumPins,
1362 TestFilter_FindPin,
1363 TestFilter_QueryFilterInfo,
1364 TestFilter_JoinFilterGraph,
1365 TestFilter_QueryVendorInfo
1366 };
1367
1368 /* IClassFactory implementation */
1369
1370 typedef struct TestClassFactoryImpl
1371 {
1372 IClassFactory IClassFactory_iface;
1373 const TestFilterPinData *filterPinData;
1374 const CLSID *clsid;
1375 } TestClassFactoryImpl;
1376
1377 static inline TestClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
1378 {
1379 return CONTAINING_RECORD(iface, TestClassFactoryImpl, IClassFactory_iface);
1380 }
1381
1382 static HRESULT WINAPI Test_IClassFactory_QueryInterface(
1383 LPCLASSFACTORY iface,
1384 REFIID riid,
1385 LPVOID *ppvObj)
1386 {
1387 if (ppvObj == NULL) return E_POINTER;
1388
1389 if (IsEqualGUID(riid, &IID_IUnknown) ||
1390 IsEqualGUID(riid, &IID_IClassFactory))
1391 {
1392 *ppvObj = iface;
1393 IClassFactory_AddRef(iface);
1394 return S_OK;
1395 }
1396
1397 *ppvObj = NULL;
1398 return E_NOINTERFACE;
1399 }
1400
1401 static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface)
1402 {
1403 return 2; /* non-heap-based object */
1404 }
1405
1406 static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface)
1407 {
1408 return 1; /* non-heap-based object */
1409 }
1410
1411 static HRESULT WINAPI Test_IClassFactory_CreateInstance(
1412 LPCLASSFACTORY iface,
1413 LPUNKNOWN pUnkOuter,
1414 REFIID riid,
1415 LPVOID *ppvObj)
1416 {
1417 TestClassFactoryImpl *This = impl_from_IClassFactory(iface);
1418 HRESULT hr;
1419 TestFilterImpl *testfilter;
1420
1421 *ppvObj = NULL;
1422
1423 if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1424
1425 hr = createtestfilter(This->clsid, This->filterPinData, &testfilter);
1426 if (SUCCEEDED(hr)) {
1427 hr = IBaseFilter_QueryInterface(&testfilter->IBaseFilter_iface, riid, ppvObj);
1428 IBaseFilter_Release(&testfilter->IBaseFilter_iface);
1429 }
1430 return hr;
1431 }
1432
1433 static HRESULT WINAPI Test_IClassFactory_LockServer(
1434 LPCLASSFACTORY iface,
1435 BOOL fLock)
1436 {
1437 return S_OK;
1438 }
1439
1440 static IClassFactoryVtbl TestClassFactory_Vtbl =
1441 {
1442 Test_IClassFactory_QueryInterface,
1443 Test_IClassFactory_AddRef,
1444 Test_IClassFactory_Release,
1445 Test_IClassFactory_CreateInstance,
1446 Test_IClassFactory_LockServer
1447 };
1448
1449 static HRESULT get_connected_filter_name(TestFilterImpl *pFilter, char *FilterName)
1450 {
1451 IPin *pin = NULL;
1452 PIN_INFO pinInfo;
1453 FILTER_INFO filterInfo;
1454 HRESULT hr;
1455
1456 FilterName[0] = 0;
1457
1458 hr = IPin_ConnectedTo(pFilter->ppPins[0], &pin);
1459 ok(hr == S_OK, "IPin_ConnectedTo failed with %x\n", hr);
1460 if (FAILED(hr)) return hr;
1461
1462 hr = IPin_QueryPinInfo(pin, &pinInfo);
1463 ok(hr == S_OK, "IPin_QueryPinInfo failed with %x\n", hr);
1464 IPin_Release(pin);
1465 if (FAILED(hr)) return hr;
1466
1467 SetLastError(0xdeadbeef);
1468 hr = IBaseFilter_QueryFilterInfo(pinInfo.pFilter, &filterInfo);
1469 if (hr == S_OK && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1470 {
1471 IBaseFilter_Release(pinInfo.pFilter);
1472 return E_NOTIMPL;
1473 }
1474 ok(hr == S_OK, "IBaseFilter_QueryFilterInfo failed with %x\n", hr);
1475 IBaseFilter_Release(pinInfo.pFilter);
1476 if (FAILED(hr)) return hr;
1477
1478 IFilterGraph_Release(filterInfo.pGraph);
1479
1480 WideCharToMultiByte(CP_ACP, 0, filterInfo.achName, -1, FilterName, MAX_FILTER_NAME, NULL, NULL);
1481
1482 return S_OK;
1483 }
1484
1485 static void test_render_filter_priority(void)
1486 {
1487 /* Tests filter choice priorities in Render(). */
1488 DWORD cookie1 = 0, cookie2 = 0, cookie3 = 0;
1489 HRESULT hr;
1490 IFilterGraph2* pgraph2 = NULL;
1491 IFilterMapper2 *pMapper2 = NULL;
1492 TestFilterImpl *ptestfilter = NULL;
1493 TestFilterImpl *ptestfilter2 = NULL;
1494 static const CLSID CLSID_TestFilter2 = {
1495 0x37a4edb0,
1496 0x4d13,
1497 0x11dd,
1498 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1499 };
1500 static const CLSID CLSID_TestFilter3 = {
1501 0x37a4f2d8,
1502 0x4d13,
1503 0x11dd,
1504 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1505 };
1506 static const CLSID CLSID_TestFilter4 = {
1507 0x37a4f3b4,
1508 0x4d13,
1509 0x11dd,
1510 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1511 };
1512 static const GUID mediasubtype1 = {
1513 0x37a4f51c,
1514 0x4d13,
1515 0x11dd,
1516 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1517 };
1518 static const GUID mediasubtype2 = {
1519 0x37a4f5c6,
1520 0x4d13,
1521 0x11dd,
1522 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1523 };
1524 static const TestFilterPinData PinData1[] = {
1525 { PINDIR_OUTPUT, &mediasubtype1 },
1526 { 0, 0 }
1527 };
1528 static const TestFilterPinData PinData2[] = {
1529 { PINDIR_INPUT, &mediasubtype1 },
1530 { 0, 0 }
1531 };
1532 static const TestFilterPinData PinData3[] = {
1533 { PINDIR_INPUT, &GUID_NULL },
1534 { 0, 0 }
1535 };
1536 static const TestFilterPinData PinData4[] = {
1537 { PINDIR_INPUT, &mediasubtype1 },
1538 { PINDIR_OUTPUT, &mediasubtype2 },
1539 { 0, 0 }
1540 };
1541 static const TestFilterPinData PinData5[] = {
1542 { PINDIR_INPUT, &mediasubtype2 },
1543 { 0, 0 }
1544 };
1545 TestClassFactoryImpl Filter1ClassFactory = {
1546 { &TestClassFactory_Vtbl },
1547 PinData2, &CLSID_TestFilter2
1548 };
1549 TestClassFactoryImpl Filter2ClassFactory = {
1550 { &TestClassFactory_Vtbl },
1551 PinData4, &CLSID_TestFilter3
1552 };
1553 TestClassFactoryImpl Filter3ClassFactory = {
1554 { &TestClassFactory_Vtbl },
1555 PinData5, &CLSID_TestFilter4
1556 };
1557 char ConnectedFilterName1[MAX_FILTER_NAME];
1558 char ConnectedFilterName2[MAX_FILTER_NAME];
1559 REGFILTER2 rgf2;
1560 REGFILTERPINS2 rgPins2[2];
1561 REGPINTYPES rgPinType[2];
1562 static const WCHAR wszFilterInstanceName1[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1563 'n', 's', 't', 'a', 'n', 'c', 'e', '1', 0 };
1564 static const WCHAR wszFilterInstanceName2[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1565 'n', 's', 't', 'a', 'n', 'c', 'e', '2', 0 };
1566 static const WCHAR wszFilterInstanceName3[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1567 'n', 's', 't', 'a', 'n', 'c', 'e', '3', 0 };
1568 static const WCHAR wszFilterInstanceName4[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1569 'n', 's', 't', 'a', 'n', 'c', 'e', '4', 0 };
1570
1571 /* Test which renderer of two already added to the graph will be chosen
1572 * (one is "exact" match, other is "wildcard" match. Seems to depend
1573 * on the order in which filters are added to the graph, thus indicating
1574 * no preference given to exact match. */
1575 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1576 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1577 if (!pgraph2) return;
1578
1579 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1580 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1581 if (FAILED(hr)) goto out;
1582
1583 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1584 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1585
1586 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1587 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1588 if (FAILED(hr)) goto out;
1589
1590 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1591 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1592
1593 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1594 ptestfilter2 = NULL;
1595
1596 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1597 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1598 if (FAILED(hr)) goto out;
1599
1600 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1601 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1602
1603 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1604 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1605
1606 hr = get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1607
1608 IFilterGraph2_Release(pgraph2);
1609 pgraph2 = NULL;
1610 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1611 ptestfilter = NULL;
1612 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1613 ptestfilter2 = NULL;
1614
1615 if (hr == E_NOTIMPL)
1616 {
1617 win_skip("Needed functions are not implemented\n");
1618 return;
1619 }
1620
1621 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1622 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1623 if (!pgraph2) goto out;
1624
1625 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1626 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1627 if (FAILED(hr)) goto out;
1628
1629 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1630 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1631
1632 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1633 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1634 if (FAILED(hr)) goto out;
1635
1636 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1637 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1638
1639 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1640 ptestfilter2 = NULL;
1641
1642 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1643 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1644 if (FAILED(hr)) goto out;
1645
1646 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1647 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1648
1649 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1650 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1651
1652 hr = IFilterGraph2_Disconnect(pgraph2, NULL);
1653 ok(hr == E_POINTER, "IFilterGraph2_Disconnect failed. Expected E_POINTER, received %08x\n", hr);
1654
1655 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
1656 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
1657 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1658
1659 IFilterGraph2_Release(pgraph2);
1660 pgraph2 = NULL;
1661 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1662 ptestfilter = NULL;
1663 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1664 ptestfilter2 = NULL;
1665
1666 /* Test if any preference is given to existing renderer which renders the pin directly vs
1667 an existing renderer which renders the pin indirectly, through an additional middle filter,
1668 again trying different orders of creation. Native appears not to give a preference. */
1669
1670 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1671 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1672 if (!pgraph2) goto out;
1673
1674 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1675 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1676 if (FAILED(hr)) goto out;
1677
1678 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1679 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1680
1681 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1682 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1683 if (FAILED(hr)) goto out;
1684
1685 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1686 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1687
1688 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1689 ptestfilter2 = NULL;
1690
1691 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
1692 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1693 if (FAILED(hr)) goto out;
1694
1695 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1696 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1697
1698 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1699 ptestfilter2 = NULL;
1700
1701 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
1702 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1703 if (FAILED(hr)) goto out;
1704
1705 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
1706 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1707
1708 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1709 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1710
1711 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1712 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3") || !strcmp(ConnectedFilterName1, "TestfilterInstance2"),
1713 "unexpected connected filter: %s\n", ConnectedFilterName1);
1714
1715 IFilterGraph2_Release(pgraph2);
1716 pgraph2 = NULL;
1717 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1718 ptestfilter = NULL;
1719 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1720 ptestfilter2 = NULL;
1721
1722 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1723 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1724 if (!pgraph2) goto out;
1725
1726 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1727 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1728 if (FAILED(hr)) goto out;
1729
1730 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1731 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1732
1733 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
1734 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1735 if (FAILED(hr)) goto out;
1736
1737 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1738 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1739
1740 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1741 ptestfilter2 = NULL;
1742
1743 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
1744 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1745 if (FAILED(hr)) goto out;
1746
1747 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
1748 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1749
1750 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1751 ptestfilter2 = NULL;
1752
1753 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1754 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1755 if (FAILED(hr)) goto out;
1756
1757 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1758 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1759
1760 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1761 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1762
1763 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
1764 ok(!strcmp(ConnectedFilterName2, "TestfilterInstance3") || !strcmp(ConnectedFilterName2, "TestfilterInstance2"),
1765 "unexpected connected filter: %s\n", ConnectedFilterName2);
1766 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
1767 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1768
1769 IFilterGraph2_Release(pgraph2);
1770 pgraph2 = NULL;
1771 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1772 ptestfilter = NULL;
1773 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1774 ptestfilter2 = NULL;
1775
1776 /* Test if renderers are tried before non-renderers (intermediary filters). */
1777 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1778 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1779 if (!pgraph2) goto out;
1780
1781 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pMapper2);
1782 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1783 if (!pMapper2) goto out;
1784
1785 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1786 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1787 if (FAILED(hr)) goto out;
1788
1789 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1790 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1791
1792 /* Register our filters with COM and with Filtermapper. */
1793 hr = CoRegisterClassObject(Filter1ClassFactory.clsid,
1794 (IUnknown *)&Filter1ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1795 REGCLS_MULTIPLEUSE, &cookie1);
1796 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1797 if (FAILED(hr)) goto out;
1798 hr = CoRegisterClassObject(Filter2ClassFactory.clsid,
1799 (IUnknown *)&Filter2ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1800 REGCLS_MULTIPLEUSE, &cookie2);
1801 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1802 if (FAILED(hr)) goto out;
1803 hr = CoRegisterClassObject(Filter3ClassFactory.clsid,
1804 (IUnknown *)&Filter3ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1805 REGCLS_MULTIPLEUSE, &cookie3);
1806 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1807 if (FAILED(hr)) goto out;
1808
1809 rgf2.dwVersion = 2;
1810 rgf2.dwMerit = MERIT_UNLIKELY;
1811 S2(U(rgf2)).cPins2 = 1;
1812 S2(U(rgf2)).rgPins2 = rgPins2;
1813 rgPins2[0].dwFlags = REG_PINFLAG_B_RENDERER;
1814 rgPins2[0].cInstances = 1;
1815 rgPins2[0].nMediaTypes = 1;
1816 rgPins2[0].lpMediaType = &rgPinType[0];
1817 rgPins2[0].nMediums = 0;
1818 rgPins2[0].lpMedium = NULL;
1819 rgPins2[0].clsPinCategory = NULL;
1820 rgPinType[0].clsMajorType = &MEDIATYPE_Video;
1821 rgPinType[0].clsMinorType = &mediasubtype1;
1822
1823 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter2, wszFilterInstanceName2, NULL,
1824 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1825 if (hr == E_ACCESSDENIED)
1826 skip("Not authorized to register filters\n");
1827 else
1828 {
1829 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1830
1831 rgf2.dwMerit = MERIT_PREFERRED;
1832 rgPinType[0].clsMinorType = &mediasubtype2;
1833
1834 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter4, wszFilterInstanceName4, NULL,
1835 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1836 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1837
1838 S2(U(rgf2)).cPins2 = 2;
1839 rgPins2[0].dwFlags = 0;
1840 rgPinType[0].clsMinorType = &mediasubtype1;
1841
1842 rgPins2[1].dwFlags = REG_PINFLAG_B_OUTPUT;
1843 rgPins2[1].cInstances = 1;
1844 rgPins2[1].nMediaTypes = 1;
1845 rgPins2[1].lpMediaType = &rgPinType[1];
1846 rgPins2[1].nMediums = 0;
1847 rgPins2[1].lpMedium = NULL;
1848 rgPins2[1].clsPinCategory = NULL;
1849 rgPinType[1].clsMajorType = &MEDIATYPE_Video;
1850 rgPinType[1].clsMinorType = &mediasubtype2;
1851
1852 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter3, wszFilterInstanceName3, NULL,
1853 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1854 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1855
1856 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1857 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1858
1859 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1860 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3"),
1861 "unexpected connected filter: %s\n", ConnectedFilterName1);
1862
1863 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1864 &CLSID_TestFilter2);
1865 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1866 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1867 &CLSID_TestFilter3);
1868 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1869 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1870 &CLSID_TestFilter4);
1871 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1872 }
1873
1874 out:
1875
1876 if (ptestfilter) IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1877 if (ptestfilter2) IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1878 if (pgraph2) IFilterGraph2_Release(pgraph2);
1879 if (pMapper2) IFilterMapper2_Release(pMapper2);
1880
1881 hr = CoRevokeClassObject(cookie1);
1882 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1883 hr = CoRevokeClassObject(cookie2);
1884 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1885 hr = CoRevokeClassObject(cookie3);
1886 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1887 }
1888
1889 typedef struct IUnknownImpl
1890 {
1891 IUnknown IUnknown_iface;
1892 int AddRef_called;
1893 int Release_called;
1894 } IUnknownImpl;
1895
1896 static IUnknownImpl *IUnknownImpl_from_iface(IUnknown * iface)
1897 {
1898 return CONTAINING_RECORD(iface, IUnknownImpl, IUnknown_iface);
1899 }
1900
1901 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
1902 {
1903 ok(0, "QueryInterface should not be called for %s\n", wine_dbgstr_guid(riid));
1904 return E_NOINTERFACE;
1905 }
1906
1907 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown * iface)
1908 {
1909 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
1910 This->AddRef_called++;
1911 return 2;
1912 }
1913
1914 static ULONG WINAPI IUnknownImpl_Release(IUnknown * iface)
1915 {
1916 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
1917 This->Release_called++;
1918 return 1;
1919 }
1920
1921 static CONST_VTBL IUnknownVtbl IUnknownImpl_Vtbl =
1922 {
1923 IUnknownImpl_QueryInterface,
1924 IUnknownImpl_AddRef,
1925 IUnknownImpl_Release
1926 };
1927
1928 static void test_aggregate_filter_graph(void)
1929 {
1930 HRESULT hr;
1931 IUnknown *pgraph;
1932 IUnknown *punk;
1933 IUnknownImpl unk_outer = { { &IUnknownImpl_Vtbl }, 0, 0 };
1934
1935 hr = CoCreateInstance(&CLSID_FilterGraph, &unk_outer.IUnknown_iface, CLSCTX_INPROC_SERVER,
1936 &IID_IUnknown, (void **)&pgraph);
1937 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1938 ok(pgraph != &unk_outer.IUnknown_iface, "pgraph = %p, expected not %p\n", pgraph, &unk_outer.IUnknown_iface);
1939
1940 hr = IUnknown_QueryInterface(pgraph, &IID_IUnknown, (void **)&punk);
1941 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1942 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1943 IUnknown_Release(punk);
1944
1945 ok(unk_outer.AddRef_called == 0, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1946 ok(unk_outer.Release_called == 0, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1947 unk_outer.AddRef_called = 0;
1948 unk_outer.Release_called = 0;
1949
1950 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper, (void **)&punk);
1951 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1952 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1953 IUnknown_Release(punk);
1954
1955 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1956 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1957 unk_outer.AddRef_called = 0;
1958 unk_outer.Release_called = 0;
1959
1960 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper2, (void **)&punk);
1961 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1962 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1963 IUnknown_Release(punk);
1964
1965 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1966 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1967 unk_outer.AddRef_called = 0;
1968 unk_outer.Release_called = 0;
1969
1970 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper3, (void **)&punk);
1971 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1972 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1973 IUnknown_Release(punk);
1974
1975 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1976 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1977
1978 IUnknown_Release(pgraph);
1979 }
1980
1981 START_TEST(filtergraph)
1982 {
1983 HRESULT hr;
1984 CoInitializeEx(NULL, COINIT_MULTITHREADED);
1985 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
1986 &IID_IGraphBuilder, (LPVOID*)&pgraph);
1987 if (FAILED(hr)) {
1988 skip("Creating filtergraph returned %08x, skipping tests\n", hr);
1989 return;
1990 }
1991 IGraphBuilder_Release(pgraph);
1992 test_render_run(avifile);
1993 test_render_run(mpegfile);
1994 test_graph_builder();
1995 test_graph_builder_addfilter();
1996 test_mediacontrol();
1997 test_filter_graph2();
1998 test_render_filter_priority();
1999 test_aggregate_filter_graph();
2000 CoUninitialize();
2001 }