[QUARTZ_WINETEST]
[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->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
1560 * (one is "exact" match, other is "wildcard" match. Seems to depend
1561 * on the order in which filters are added to the graph, thus indicating
1562 * no preference given to exact match. */
1563 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1564 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1565 if (!pgraph2) return;
1566
1567 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1568 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1569 if (FAILED(hr)) goto out;
1570
1571 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1572 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1573
1574 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1575 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1576 if (FAILED(hr)) goto out;
1577
1578 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1579 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1580
1581 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1582 ptestfilter2 = NULL;
1583
1584 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1585 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1586 if (FAILED(hr)) goto out;
1587
1588 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1589 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1590
1591 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1592 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1593
1594 hr = get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1595
1596 IFilterGraph2_Release(pgraph2);
1597 pgraph2 = NULL;
1598 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1599 ptestfilter = NULL;
1600 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1601 ptestfilter2 = NULL;
1602
1603 if (hr == E_NOTIMPL)
1604 {
1605 win_skip("Needed functions are not implemented\n");
1606 return;
1607 }
1608
1609 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1610 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1611 if (!pgraph2) goto out;
1612
1613 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1614 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1615 if (FAILED(hr)) goto out;
1616
1617 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1618 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1619
1620 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1621 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1622 if (FAILED(hr)) goto out;
1623
1624 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1625 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1626
1627 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1628 ptestfilter2 = NULL;
1629
1630 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1631 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1632 if (FAILED(hr)) goto out;
1633
1634 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1635 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1636
1637 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1638 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1639
1640 hr = IFilterGraph2_Disconnect(pgraph2, NULL);
1641 ok(hr == E_POINTER, "IFilterGraph2_Disconnect failed. Expected E_POINTER, received %08x\n", hr);
1642
1643 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
1644 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
1645 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1646
1647 IFilterGraph2_Release(pgraph2);
1648 pgraph2 = NULL;
1649 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1650 ptestfilter = NULL;
1651 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1652 ptestfilter2 = NULL;
1653
1654 /* Test if any preference is given to existing renderer which renders the pin directly vs
1655 an existing renderer which renders the pin indirectly, through an additional middle filter,
1656 again trying different orders of creation. Native appears not to give a preference. */
1657
1658 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1659 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1660 if (!pgraph2) goto out;
1661
1662 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1663 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1664 if (FAILED(hr)) goto out;
1665
1666 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1667 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1668
1669 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1670 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1671 if (FAILED(hr)) goto out;
1672
1673 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1674 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1675
1676 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1677 ptestfilter2 = NULL;
1678
1679 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
1680 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1681 if (FAILED(hr)) goto out;
1682
1683 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1684 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1685
1686 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1687 ptestfilter2 = NULL;
1688
1689 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
1690 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1691 if (FAILED(hr)) goto out;
1692
1693 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
1694 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1695
1696 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1697 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1698
1699 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1700 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3") || !strcmp(ConnectedFilterName1, "TestfilterInstance2"),
1701 "unexpected connected filter: %s\n", ConnectedFilterName1);
1702
1703 IFilterGraph2_Release(pgraph2);
1704 pgraph2 = NULL;
1705 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1706 ptestfilter = NULL;
1707 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1708 ptestfilter2 = NULL;
1709
1710 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1711 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1712 if (!pgraph2) goto out;
1713
1714 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1715 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1716 if (FAILED(hr)) goto out;
1717
1718 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1719 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1720
1721 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
1722 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1723 if (FAILED(hr)) goto out;
1724
1725 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1726 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1727
1728 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1729 ptestfilter2 = NULL;
1730
1731 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
1732 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1733 if (FAILED(hr)) goto out;
1734
1735 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
1736 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1737
1738 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1739 ptestfilter2 = NULL;
1740
1741 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1742 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1743 if (FAILED(hr)) goto out;
1744
1745 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1746 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1747
1748 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1749 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1750
1751 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
1752 ok(!strcmp(ConnectedFilterName2, "TestfilterInstance3") || !strcmp(ConnectedFilterName2, "TestfilterInstance2"),
1753 "unexpected connected filter: %s\n", ConnectedFilterName2);
1754 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
1755 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
1756
1757 IFilterGraph2_Release(pgraph2);
1758 pgraph2 = NULL;
1759 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1760 ptestfilter = NULL;
1761 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1762 ptestfilter2 = NULL;
1763
1764 /* Test if renderers are tried before non-renderers (intermediary filters). */
1765 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1766 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1767 if (!pgraph2) goto out;
1768
1769 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pMapper2);
1770 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1771 if (!pMapper2) goto out;
1772
1773 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1774 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1775 if (FAILED(hr)) goto out;
1776
1777 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1778 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1779
1780 /* Register our filters with COM and with Filtermapper. */
1781 hr = CoRegisterClassObject(Filter1ClassFactory.clsid,
1782 (IUnknown *)&Filter1ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1783 REGCLS_MULTIPLEUSE, &cookie1);
1784 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1785 if (FAILED(hr)) goto out;
1786 hr = CoRegisterClassObject(Filter2ClassFactory.clsid,
1787 (IUnknown *)&Filter2ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1788 REGCLS_MULTIPLEUSE, &cookie2);
1789 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1790 if (FAILED(hr)) goto out;
1791 hr = CoRegisterClassObject(Filter3ClassFactory.clsid,
1792 (IUnknown *)&Filter3ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
1793 REGCLS_MULTIPLEUSE, &cookie3);
1794 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
1795 if (FAILED(hr)) goto out;
1796
1797 rgf2.dwVersion = 2;
1798 rgf2.dwMerit = MERIT_UNLIKELY;
1799 S2(U(rgf2)).cPins2 = 1;
1800 S2(U(rgf2)).rgPins2 = rgPins2;
1801 rgPins2[0].dwFlags = REG_PINFLAG_B_RENDERER;
1802 rgPins2[0].cInstances = 1;
1803 rgPins2[0].nMediaTypes = 1;
1804 rgPins2[0].lpMediaType = &rgPinType[0];
1805 rgPins2[0].nMediums = 0;
1806 rgPins2[0].lpMedium = NULL;
1807 rgPins2[0].clsPinCategory = NULL;
1808 rgPinType[0].clsMajorType = &MEDIATYPE_Video;
1809 rgPinType[0].clsMinorType = &mediasubtype1;
1810
1811 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter2, wszFilterInstanceName2, NULL,
1812 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1813 if (hr == E_ACCESSDENIED)
1814 skip("Not authorized to register filters\n");
1815 else
1816 {
1817 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1818
1819 rgf2.dwMerit = MERIT_PREFERRED;
1820 rgPinType[0].clsMinorType = &mediasubtype2;
1821
1822 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter4, wszFilterInstanceName4, NULL,
1823 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1824 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1825
1826 S2(U(rgf2)).cPins2 = 2;
1827 rgPins2[0].dwFlags = 0;
1828 rgPinType[0].clsMinorType = &mediasubtype1;
1829
1830 rgPins2[1].dwFlags = REG_PINFLAG_B_OUTPUT;
1831 rgPins2[1].cInstances = 1;
1832 rgPins2[1].nMediaTypes = 1;
1833 rgPins2[1].lpMediaType = &rgPinType[1];
1834 rgPins2[1].nMediums = 0;
1835 rgPins2[1].lpMedium = NULL;
1836 rgPins2[1].clsPinCategory = NULL;
1837 rgPinType[1].clsMajorType = &MEDIATYPE_Video;
1838 rgPinType[1].clsMinorType = &mediasubtype2;
1839
1840 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter3, wszFilterInstanceName3, NULL,
1841 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
1842 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
1843
1844 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1845 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1846
1847 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1848 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3"),
1849 "unexpected connected filter: %s\n", ConnectedFilterName1);
1850 }
1851
1852 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1853 &CLSID_TestFilter2);
1854 ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1855 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1856 &CLSID_TestFilter3);
1857 ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1858 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
1859 &CLSID_TestFilter4);
1860 ok(SUCCEEDED(hr), "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
1861
1862 out:
1863
1864 if (ptestfilter) IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1865 if (ptestfilter2) IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1866 if (pgraph2) IFilterGraph2_Release(pgraph2);
1867 if (pMapper2) IFilterMapper2_Release(pMapper2);
1868
1869 hr = CoRevokeClassObject(cookie1);
1870 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1871 hr = CoRevokeClassObject(cookie2);
1872 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1873 hr = CoRevokeClassObject(cookie3);
1874 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
1875 }
1876
1877 START_TEST(filtergraph)
1878 {
1879 HRESULT hr;
1880
1881 if (!winetest_interactive)
1882 {
1883 skip("Skipping filtergraph test, see ROSTESTS-116\n");
1884 return;
1885 }
1886
1887 CoInitializeEx(NULL, COINIT_MULTITHREADED);
1888 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
1889 &IID_IGraphBuilder, (LPVOID*)&pgraph);
1890 if (FAILED(hr)) {
1891 skip("Creating filtergraph returned %08x, skipping tests\n", hr);
1892 return;
1893 }
1894 test_render_run(avifile);
1895 test_render_run(mpegfile);
1896 test_graph_builder();
1897 test_graph_builder_addfilter();
1898 test_mediacontrol();
1899 test_filter_graph2();
1900 test_render_filter_priority();
1901 CoUninitialize();
1902 }