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