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