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