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