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