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