quartz/tests: Use CreateFileA instead of CreateFileW to please win9x.
[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 WINAPI 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     for (i = 0; i < nPins; i++)
1126     {
1127         if (pTestFilter->ppPins[i]) IPin_Release(pTestFilter->ppPins[i]);
1128     }
1129     CoTaskMemFree(pTestFilter->ppPins);
1130     DeleteCriticalSection(&pTestFilter->csFilter);
1131     CoTaskMemFree(pTestFilter);
1132
1133     return hr;
1134 }
1135
1136 static HRESULT WINAPI TestFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
1137 {
1138     TestFilterImpl *This = (TestFilterImpl *)iface;
1139
1140     *ppv = NULL;
1141
1142     if (IsEqualIID(riid, &IID_IUnknown))
1143         *ppv = (LPVOID)This;
1144     else if (IsEqualIID(riid, &IID_IPersist))
1145         *ppv = (LPVOID)This;
1146     else if (IsEqualIID(riid, &IID_IMediaFilter))
1147         *ppv = (LPVOID)This;
1148     else if (IsEqualIID(riid, &IID_IBaseFilter))
1149         *ppv = (LPVOID)This;
1150
1151     if (*ppv)
1152     {
1153         IUnknown_AddRef((IUnknown *)(*ppv));
1154         return S_OK;
1155     }
1156
1157     return E_NOINTERFACE;
1158 }
1159
1160 static ULONG WINAPI TestFilter_AddRef(IBaseFilter * iface)
1161 {
1162     TestFilterImpl *This = (TestFilterImpl *)iface;
1163     ULONG refCount = InterlockedIncrement(&This->refCount);
1164
1165     return refCount;
1166 }
1167
1168 static ULONG WINAPI TestFilter_Release(IBaseFilter * iface)
1169 {
1170     TestFilterImpl *This = (TestFilterImpl *)iface;
1171     ULONG refCount = InterlockedDecrement(&This->refCount);
1172
1173     if (!refCount)
1174     {
1175         ULONG i;
1176
1177         for (i = 0; i < This->nPins; i++)
1178         {
1179             IPin *pConnectedTo;
1180
1181             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
1182             {
1183                 IPin_Disconnect(pConnectedTo);
1184                 IPin_Release(pConnectedTo);
1185             }
1186             IPin_Disconnect(This->ppPins[i]);
1187
1188             IPin_Release(This->ppPins[i]);
1189         }
1190
1191         CoTaskMemFree(This->ppPins);
1192         This->lpVtbl = NULL;
1193
1194         DeleteCriticalSection(&This->csFilter);
1195
1196         CoTaskMemFree(This);
1197
1198         return 0;
1199     }
1200     else
1201         return refCount;
1202 }
1203 /** IPersist methods **/
1204
1205 static HRESULT WINAPI TestFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
1206 {
1207     TestFilterImpl *This = (TestFilterImpl *)iface;
1208
1209     *pClsid = This->clsid;
1210
1211     return S_OK;
1212 }
1213
1214 /** IMediaFilter methods **/
1215
1216 static HRESULT WINAPI TestFilter_Stop(IBaseFilter * iface)
1217 {
1218     return E_NOTIMPL;
1219 }
1220
1221 static HRESULT WINAPI TestFilter_Pause(IBaseFilter * iface)
1222 {
1223     return E_NOTIMPL;
1224 }
1225
1226 static HRESULT WINAPI TestFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
1227 {
1228     return E_NOTIMPL;
1229 }
1230
1231 static HRESULT WINAPI TestFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
1232 {
1233     TestFilterImpl *This = (TestFilterImpl *)iface;
1234
1235     EnterCriticalSection(&This->csFilter);
1236     {
1237         *pState = This->state;
1238     }
1239     LeaveCriticalSection(&This->csFilter);
1240
1241     return S_OK;
1242 }
1243
1244 static HRESULT WINAPI TestFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
1245 {
1246     return E_NOTIMPL;
1247 }
1248
1249 static HRESULT WINAPI TestFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
1250 {
1251     return E_NOTIMPL;
1252 }
1253
1254 /** IBaseFilter implementation **/
1255
1256 static HRESULT TestFilter_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
1257 {
1258     TestFilterImpl *This = (TestFilterImpl *)iface;
1259
1260     /* Our pins are static, not changing so setting static tick count is ok */
1261     *lastsynctick = 0;
1262
1263     if (pos >= This->nPins)
1264         return S_FALSE;
1265
1266     *pin = This->ppPins[pos];
1267     IPin_AddRef(*pin);
1268     return S_OK;
1269 }
1270
1271 static HRESULT WINAPI TestFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
1272 {
1273     return IEnumPinsImpl_Construct(ppEnum, TestFilter_GetPin, iface);
1274 }
1275
1276 static HRESULT WINAPI TestFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
1277 {
1278     return E_NOTIMPL;
1279 }
1280
1281 static HRESULT WINAPI TestFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
1282 {
1283     TestFilterImpl *This = (TestFilterImpl *)iface;
1284
1285     lstrcpyW(pInfo->achName, This->filterInfo.achName);
1286     pInfo->pGraph = This->filterInfo.pGraph;
1287
1288     if (pInfo->pGraph)
1289         IFilterGraph_AddRef(pInfo->pGraph);
1290
1291     return S_OK;
1292 }
1293
1294 static HRESULT WINAPI TestFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
1295 {
1296     HRESULT hr = S_OK;
1297     TestFilterImpl *This = (TestFilterImpl *)iface;
1298
1299     EnterCriticalSection(&This->csFilter);
1300     {
1301         if (pName)
1302             lstrcpyW(This->filterInfo.achName, pName);
1303         else
1304             *This->filterInfo.achName = '\0';
1305         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
1306     }
1307     LeaveCriticalSection(&This->csFilter);
1308
1309     return hr;
1310 }
1311
1312 static HRESULT WINAPI TestFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
1313 {
1314     return E_NOTIMPL;
1315 }
1316
1317 static const IBaseFilterVtbl TestFilter_Vtbl =
1318 {
1319     TestFilter_QueryInterface,
1320     TestFilter_AddRef,
1321     TestFilter_Release,
1322     TestFilter_GetClassID,
1323     TestFilter_Stop,
1324     TestFilter_Pause,
1325     TestFilter_Run,
1326     TestFilter_GetState,
1327     TestFilter_SetSyncSource,
1328     TestFilter_GetSyncSource,
1329     TestFilter_EnumPins,
1330     TestFilter_FindPin,
1331     TestFilter_QueryFilterInfo,
1332     TestFilter_JoinFilterGraph,
1333     TestFilter_QueryVendorInfo
1334 };
1335
1336 /* IClassFactory implementation */
1337
1338 typedef struct TestClassFactoryImpl
1339 {
1340     IClassFactoryVtbl *lpVtbl;
1341     const TestFilterPinData *filterPinData;
1342     const CLSID *clsid;
1343 } TestClassFactoryImpl;
1344
1345 static HRESULT WINAPI Test_IClassFactory_QueryInterface(
1346     LPCLASSFACTORY iface,
1347     REFIID riid,
1348     LPVOID *ppvObj)
1349 {
1350     if (ppvObj == NULL) return E_POINTER;
1351
1352     if (IsEqualGUID(riid, &IID_IUnknown) ||
1353         IsEqualGUID(riid, &IID_IClassFactory))
1354     {
1355         *ppvObj = (LPVOID)iface;
1356         IClassFactory_AddRef(iface);
1357         return S_OK;
1358     }
1359
1360     *ppvObj = NULL;
1361     return E_NOINTERFACE;
1362 }
1363
1364 static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface)
1365 {
1366     return 2; /* non-heap-based object */
1367 }
1368
1369 static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface)
1370 {
1371     return 1; /* non-heap-based object */
1372 }
1373
1374 static HRESULT WINAPI Test_IClassFactory_CreateInstance(
1375     LPCLASSFACTORY iface,
1376     LPUNKNOWN pUnkOuter,
1377     REFIID riid,
1378     LPVOID *ppvObj)
1379 {
1380     TestClassFactoryImpl *This = (TestClassFactoryImpl *)iface;
1381     HRESULT hr;
1382     IUnknown *punk = NULL;
1383
1384     *ppvObj = NULL;
1385
1386     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1387
1388     hr = TestFilter_Create(This->clsid, This->filterPinData, (LPVOID *) &punk);
1389     if (SUCCEEDED(hr)) {
1390         hr = IUnknown_QueryInterface(punk, riid, ppvObj);
1391         IUnknown_Release(punk);
1392     }
1393     return hr;
1394 }
1395
1396 static HRESULT WINAPI Test_IClassFactory_LockServer(
1397     LPCLASSFACTORY iface,
1398     BOOL fLock)
1399 {
1400     return S_OK;
1401 }
1402
1403 static IClassFactoryVtbl TestClassFactory_Vtbl =
1404 {
1405     Test_IClassFactory_QueryInterface,
1406     Test_IClassFactory_AddRef,
1407     Test_IClassFactory_Release,
1408     Test_IClassFactory_CreateInstance,
1409     Test_IClassFactory_LockServer
1410 };
1411
1412 static HRESULT get_connected_filter_name(TestFilterImpl *pFilter, char *FilterName)
1413 {
1414     IPin *pin = NULL;
1415     PIN_INFO pinInfo;
1416     FILTER_INFO filterInfo;
1417     HRESULT hr;
1418
1419     FilterName[0] = 0;
1420
1421     hr = IPin_ConnectedTo(pFilter->ppPins[0], &pin);
1422     ok(hr == S_OK, "IPin_ConnectedTo failed with %x\n", hr);
1423     if (FAILED(hr)) return hr;
1424
1425     hr = IPin_QueryPinInfo(pin, &pinInfo);
1426     ok(hr == S_OK, "IPin_QueryPinInfo failed with %x\n", hr);
1427     IPin_Release(pin);
1428     if (FAILED(hr)) return hr;
1429
1430     hr = IBaseFilter_QueryFilterInfo(pinInfo.pFilter, &filterInfo);
1431     ok(hr == S_OK, "IBaseFilter_QueryFilterInfo failed with %x\n", hr);
1432     IBaseFilter_Release(pinInfo.pFilter);
1433     if (FAILED(hr)) return hr;
1434
1435     IFilterGraph_Release(filterInfo.pGraph);
1436
1437     WideCharToMultiByte(CP_ACP, 0, filterInfo.achName, -1, FilterName, MAX_FILTER_NAME, NULL, NULL);
1438
1439     return S_OK;
1440 }
1441
1442 static void test_render_filter_priority(void)
1443 {
1444     /* Tests filter choice priorities in Render(). */
1445     DWORD cookie1, cookie2, cookie3;
1446     HRESULT hr;
1447     IFilterGraph2* pgraph2 = NULL;
1448     IFilterMapper2 *pMapper2 = NULL;
1449     IBaseFilter* ptestfilter = NULL;
1450     IBaseFilter* ptestfilter2 = NULL;
1451     static const CLSID CLSID_TestFilter2 = {
1452         0x37a4edb0,
1453         0x4d13,
1454         0x11dd,
1455         {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1456     };
1457     static const CLSID CLSID_TestFilter3 = {
1458         0x37a4f2d8,
1459         0x4d13,
1460         0x11dd,
1461         {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1462     };
1463     static const CLSID CLSID_TestFilter4 = {
1464         0x37a4f3b4,
1465         0x4d13,
1466         0x11dd,
1467         {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1468     };
1469     static const GUID mediasubtype1 = {
1470         0x37a4f51c,
1471         0x4d13,
1472         0x11dd,
1473         {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1474     };
1475     static const GUID mediasubtype2 = {
1476         0x37a4f5c6,
1477         0x4d13,
1478         0x11dd,
1479         {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1480     };
1481     static const TestFilterPinData PinData1[] = {
1482             { PINDIR_OUTPUT, &mediasubtype1 },
1483             { 0, 0 }
1484         };
1485     static const TestFilterPinData PinData2[] = {
1486             { PINDIR_INPUT,  &mediasubtype1 },
1487             { 0, 0 }
1488         };
1489     static const TestFilterPinData PinData3[] = {
1490             { PINDIR_INPUT,  &GUID_NULL },
1491             { 0, 0 }
1492         };
1493     static const TestFilterPinData PinData4[] = {
1494             { PINDIR_INPUT,  &mediasubtype1 },
1495             { PINDIR_OUTPUT, &mediasubtype2 },
1496             { 0, 0 }
1497         };
1498     static const TestFilterPinData PinData5[] = {
1499             { PINDIR_INPUT,  &mediasubtype2 },
1500             { 0, 0 }
1501         };
1502     TestClassFactoryImpl Filter1ClassFactory = { &TestClassFactory_Vtbl, PinData2, &CLSID_TestFilter2 };
1503     TestClassFactoryImpl Filter2ClassFactory = { &TestClassFactory_Vtbl, PinData4, &CLSID_TestFilter3 };
1504     TestClassFactoryImpl Filter3ClassFactory = { &TestClassFactory_Vtbl, PinData5, &CLSID_TestFilter4 };
1505     char ConnectedFilterName1[MAX_FILTER_NAME];
1506     char ConnectedFilterName2[MAX_FILTER_NAME];
1507     REGFILTER2 rgf2;
1508     REGFILTERPINS2 rgPins2[2];
1509     REGPINTYPES rgPinType[2];
1510     static const WCHAR wszFilterInstanceName1[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1511                                                         'n', 's', 't', 'a', 'n', 'c', 'e', '1', 0 };
1512     static const WCHAR wszFilterInstanceName2[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1513                                                         'n', 's', 't', 'a', 'n', 'c', 'e', '2', 0 };
1514     static const WCHAR wszFilterInstanceName3[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1515                                                         'n', 's', 't', 'a', 'n', 'c', 'e', '3', 0 };
1516     static const WCHAR wszFilterInstanceName4[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1517                                                         'n', 's', 't', 'a', 'n', 'c', 'e', '4', 0 };
1518
1519     /* Test which renderer of two already added to the graph will be chosen (one is "exact" match, other is
1520        "wildcard" match. Seems to very by order in which filters are added to the graph, thus indicating
1521        no preference given to exact match. */
1522     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1523     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1524     if (!pgraph2) goto out;
1525
1526     hr = TestFilter_Create(&GUID_NULL, PinData1, (LPVOID)&ptestfilter);
1527     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1528     if (FAILED(hr)) goto out;
1529
1530     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter, wszFilterInstanceName1);
1531     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1532
1533     hr = TestFilter_Create(&GUID_NULL, PinData2, (LPVOID)&ptestfilter2);
1534     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1535     if (FAILED(hr)) goto out;
1536
1537     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName2);
1538     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1539
1540     IBaseFilter_Release(ptestfilter2);
1541     ptestfilter2 = NULL;
1542
1543     hr = TestFilter_Create(&GUID_NULL, PinData3, (LPVOID)&ptestfilter2);
1544     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1545     if (FAILED(hr)) goto out;
1546
1547     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName3);
1548     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1549
1550     hr = IFilterGraph2_Render(pgraph2, ((TestFilterImpl*)ptestfilter)->ppPins[0]);
1551     ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1552
1553     get_connected_filter_name((TestFilterImpl*)ptestfilter, ConnectedFilterName1);
1554
1555     IFilterGraph2_Release(pgraph2);
1556     pgraph2 = NULL;
1557     IBaseFilter_Release(ptestfilter);
1558     ptestfilter = NULL;
1559     IBaseFilter_Release(ptestfilter2);
1560     ptestfilter2 = NULL;
1561
1562     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1563     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1564     if (!pgraph2) goto out;
1565
1566     hr = TestFilter_Create(&GUID_NULL, PinData1, (LPVOID)&ptestfilter);
1567     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1568     if (FAILED(hr)) goto out;
1569
1570     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter, wszFilterInstanceName1);
1571     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1572
1573     hr = TestFilter_Create(&GUID_NULL, PinData3, (LPVOID)&ptestfilter2);
1574     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1575     if (FAILED(hr)) goto out;
1576
1577     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName3);
1578     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1579
1580     IBaseFilter_Release(ptestfilter2);
1581     ptestfilter2 = NULL;
1582
1583     hr = TestFilter_Create(&GUID_NULL, PinData2, (LPVOID)&ptestfilter2);
1584     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1585     if (FAILED(hr)) goto out;
1586
1587     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName2);
1588     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1589
1590     hr = IFilterGraph2_Render(pgraph2, ((TestFilterImpl*)ptestfilter)->ppPins[0]);
1591     ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1592
1593     get_connected_filter_name((TestFilterImpl*)ptestfilter, ConnectedFilterName2);
1594     ok(lstrcmp(ConnectedFilterName1, ConnectedFilterName2),
1595         "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1596
1597     IFilterGraph2_Release(pgraph2);
1598     pgraph2 = NULL;
1599     IBaseFilter_Release(ptestfilter);
1600     ptestfilter = NULL;
1601     IBaseFilter_Release(ptestfilter2);
1602     ptestfilter2 = NULL;
1603
1604     /* Test if any preference is given to existing renderer which renders the pin directly vs
1605        an existing renderer which renders the pin indirectly, through an additional middle filter,
1606        again trying different orders of creation. Native appears not to give a preference. */
1607
1608     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1609     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1610     if (!pgraph2) goto out;
1611
1612     hr = TestFilter_Create(&GUID_NULL, PinData1, (LPVOID)&ptestfilter);
1613     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1614     if (FAILED(hr)) goto out;
1615
1616     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter, wszFilterInstanceName1);
1617     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1618
1619     hr = TestFilter_Create(&GUID_NULL, PinData2, (LPVOID)&ptestfilter2);
1620     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1621     if (FAILED(hr)) goto out;
1622
1623     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName2);
1624     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1625
1626     IBaseFilter_Release(ptestfilter2);
1627     ptestfilter2 = NULL;
1628
1629     hr = TestFilter_Create(&GUID_NULL, PinData4, (LPVOID)&ptestfilter2);
1630     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1631     if (FAILED(hr)) goto out;
1632
1633     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName3);
1634     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1635
1636     IBaseFilter_Release(ptestfilter2);
1637     ptestfilter2 = NULL;
1638
1639     hr = TestFilter_Create(&GUID_NULL, PinData5, (LPVOID)&ptestfilter2);
1640     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1641     if (FAILED(hr)) goto out;
1642
1643     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName4);
1644     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1645
1646     hr = IFilterGraph2_Render(pgraph2, ((TestFilterImpl*)ptestfilter)->ppPins[0]);
1647     ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1648
1649     get_connected_filter_name((TestFilterImpl*)ptestfilter, ConnectedFilterName1);
1650     ok(!lstrcmp(ConnectedFilterName1, "TestfilterInstance3") || !lstrcmp(ConnectedFilterName1, "TestfilterInstance2"),
1651             "unexpected connected filter: %s\n", ConnectedFilterName1);
1652
1653     IFilterGraph2_Release(pgraph2);
1654     pgraph2 = NULL;
1655     IBaseFilter_Release(ptestfilter);
1656     ptestfilter = NULL;
1657     IBaseFilter_Release(ptestfilter2);
1658     ptestfilter2 = NULL;
1659
1660     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1661     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1662     if (!pgraph2) goto out;
1663
1664     hr = TestFilter_Create(&GUID_NULL, PinData1, (LPVOID)&ptestfilter);
1665     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1666     if (FAILED(hr)) goto out;
1667
1668     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter, wszFilterInstanceName1);
1669     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1670
1671     hr = TestFilter_Create(&GUID_NULL, PinData4, (LPVOID)&ptestfilter2);
1672     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1673     if (FAILED(hr)) goto out;
1674
1675     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName3);
1676     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1677
1678     IBaseFilter_Release(ptestfilter2);
1679     ptestfilter2 = NULL;
1680
1681     hr = TestFilter_Create(&GUID_NULL, PinData5, (LPVOID)&ptestfilter2);
1682     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1683     if (FAILED(hr)) goto out;
1684
1685     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName4);
1686     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1687
1688     IBaseFilter_Release(ptestfilter2);
1689     ptestfilter2 = NULL;
1690
1691     hr = TestFilter_Create(&GUID_NULL, PinData2, (LPVOID)&ptestfilter2);
1692     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1693     if (FAILED(hr)) goto out;
1694
1695     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter2, wszFilterInstanceName2);
1696     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1697
1698     hr = IFilterGraph2_Render(pgraph2, ((TestFilterImpl*)ptestfilter)->ppPins[0]);
1699     ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1700
1701     get_connected_filter_name((TestFilterImpl*)ptestfilter, ConnectedFilterName2);
1702     ok(!lstrcmp(ConnectedFilterName2, "TestfilterInstance3") || !lstrcmp(ConnectedFilterName2, "TestfilterInstance2"),
1703             "unexpected connected filter: %s\n", ConnectedFilterName2);
1704     ok(lstrcmp(ConnectedFilterName1, ConnectedFilterName2),
1705         "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1706
1707     IFilterGraph2_Release(pgraph2);
1708     pgraph2 = NULL;
1709     IBaseFilter_Release(ptestfilter);
1710     ptestfilter = NULL;
1711     IBaseFilter_Release(ptestfilter2);
1712     ptestfilter2 = NULL;
1713
1714     /* Test if renderers are tried before non-renderers (intermediary filters). */
1715     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1716     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1717     if (!pgraph2) goto out;
1718
1719     hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pMapper2);
1720     ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1721     if (!pMapper2) goto out;
1722
1723     hr = TestFilter_Create(&GUID_NULL, PinData1, (LPVOID)&ptestfilter);
1724     ok(hr == S_OK, "TestFilter_Create failed with %08x\n", hr);
1725     if (FAILED(hr)) goto out;
1726
1727     hr = IFilterGraph2_AddFilter(pgraph2, ptestfilter, wszFilterInstanceName1);
1728     ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1729
1730     /* Register our filters with COM and with Filtermapper. */
1731     hr = CoRegisterClassObject(Filter1ClassFactory.clsid, (IUnknown *)&Filter1ClassFactory,
1732                                CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
1733     ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1734     hr = CoRegisterClassObject(Filter2ClassFactory.clsid, (IUnknown *)&Filter2ClassFactory,
1735                                CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
1736     ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1737     hr = CoRegisterClassObject(Filter3ClassFactory.clsid, (IUnknown *)&Filter3ClassFactory,
1738                                CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie3);
1739     ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1740
1741     rgf2.dwVersion = 2;
1742     rgf2.dwMerit = MERIT_UNLIKELY;
1743     S1(U(rgf2)).cPins2 = 1;
1744     S1(U(rgf2)).rgPins2 = rgPins2;
1745     rgPins2[0].dwFlags = REG_PINFLAG_B_RENDERER;
1746     rgPins2[0].cInstances = 1;
1747     rgPins2[0].nMediaTypes = 1;
1748     rgPins2[0].lpMediaType = &rgPinType[0];
1749     rgPins2[0].nMediums = 0;
1750     rgPins2[0].lpMedium = NULL;
1751     rgPins2[0].clsPinCategory = NULL;
1752     rgPinType[0].clsMajorType = &MEDIATYPE_Video;
1753     rgPinType[0].clsMinorType = &mediasubtype1;
1754
1755     hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter2, wszFilterInstanceName2, NULL,
1756                     &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1757     ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1758
1759     rgf2.dwMerit = MERIT_PREFERRED;
1760     rgPinType[0].clsMinorType = &mediasubtype2;
1761
1762     hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter4, wszFilterInstanceName4, NULL,
1763                     &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1764     ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1765
1766     S1(U(rgf2)).cPins2 = 2;
1767     rgPins2[0].dwFlags = 0;
1768     rgPinType[0].clsMinorType = &mediasubtype1;
1769
1770     rgPins2[1].dwFlags = REG_PINFLAG_B_OUTPUT;
1771     rgPins2[1].cInstances = 1;
1772     rgPins2[1].nMediaTypes = 1;
1773     rgPins2[1].lpMediaType = &rgPinType[1];
1774     rgPins2[1].nMediums = 0;
1775     rgPins2[1].lpMedium = NULL;
1776     rgPins2[1].clsPinCategory = NULL;
1777     rgPinType[1].clsMajorType = &MEDIATYPE_Video;
1778     rgPinType[1].clsMinorType = &mediasubtype2;
1779
1780     hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter3, wszFilterInstanceName3, NULL,
1781                     &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1782     ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1783
1784     hr = IFilterGraph2_Render(pgraph2, ((TestFilterImpl*)ptestfilter)->ppPins[0]);
1785     ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1786
1787     get_connected_filter_name((TestFilterImpl*)ptestfilter, ConnectedFilterName1);
1788     ok(!lstrcmp(ConnectedFilterName1, "TestfilterInstance3"),
1789             "unexpected connected filter: %s\n", ConnectedFilterName1);
1790
1791     hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1792             &CLSID_TestFilter2);
1793     ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1794     hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1795             &CLSID_TestFilter3);
1796     ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1797     hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1798              &CLSID_TestFilter4);
1799     ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1800
1801     out:
1802
1803     if (ptestfilter) IBaseFilter_Release(ptestfilter);
1804     if (ptestfilter2) IBaseFilter_Release(ptestfilter2);
1805     if (pgraph2) IFilterGraph2_Release(pgraph2);
1806     if (pMapper2) IFilterMapper2_Release(pMapper2);
1807
1808     hr = CoRevokeClassObject(cookie1);
1809     ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1810     hr = CoRevokeClassObject(cookie2);
1811     ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1812     hr = CoRevokeClassObject(cookie3);
1813     ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1814 }
1815
1816 START_TEST(filtergraph)
1817 {
1818     CoInitializeEx(NULL, COINIT_MULTITHREADED);
1819     test_render_run();
1820     test_graph_builder();
1821     test_graph_builder_addfilter();
1822     test_mediacontrol();
1823     test_filter_graph2();
1824     test_render_filter_priority();
1825     CoUninitialize();
1826 }