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