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