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