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