[QUARTZ_WINETEST] Sync with Wine 3.0. CORE-14225
[reactos.git] / modules / 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 WCHAR *load_resource(const WCHAR *name)
46 {
47 static WCHAR pathW[MAX_PATH];
48 DWORD written;
49 HANDLE file;
50 HRSRC res;
51 void *ptr;
52
53 GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
54 lstrcatW(pathW, name);
55
56 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
57 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
58 GetLastError());
59
60 res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA);
61 ok( res != 0, "couldn't find resource\n" );
62 ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
63 WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
64 ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
65 CloseHandle( file );
66
67 return pathW;
68 }
69
70 static IGraphBuilder *pgraph;
71
72 static int createfiltergraph(void)
73 {
74 return S_OK == CoCreateInstance(
75 &CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (LPVOID*)&pgraph);
76 }
77
78 static void test_basic_video(void)
79 {
80 IBasicVideo* pbv;
81 LONG video_width, video_height, window_width;
82 LONG left, top, width, height;
83 HRESULT hr;
84
85 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IBasicVideo, (LPVOID*)&pbv);
86 ok(hr==S_OK, "Cannot get IBasicVideo interface returned: %x\n", hr);
87
88 /* test get video size */
89 hr = IBasicVideo_GetVideoSize(pbv, NULL, NULL);
90 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
91 hr = IBasicVideo_GetVideoSize(pbv, &video_width, NULL);
92 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
93 hr = IBasicVideo_GetVideoSize(pbv, NULL, &video_height);
94 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
95 hr = IBasicVideo_GetVideoSize(pbv, &video_width, &video_height);
96 ok(hr==S_OK, "Cannot get video size returned: %x\n", hr);
97
98 /* test source position */
99 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, NULL, NULL);
100 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
101 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, NULL, NULL);
102 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
103 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, &width, &height);
104 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
105 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
106 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
107 ok(left == 0, "expected 0, got %d\n", left);
108 ok(top == 0, "expected 0, got %d\n", top);
109 ok(width == video_width, "expected %d, got %d\n", video_width, width);
110 ok(height == video_height, "expected %d, got %d\n", video_height, height);
111
112 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, 0, 0);
113 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
114 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width*2, video_height*2);
115 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
116 hr = IBasicVideo_put_SourceTop(pbv, -1);
117 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
118 hr = IBasicVideo_put_SourceTop(pbv, 0);
119 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
120 hr = IBasicVideo_put_SourceTop(pbv, 1);
121 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
122
123 hr = IBasicVideo_SetSourcePosition(pbv, video_width, 0, video_width, video_height);
124 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
125 hr = IBasicVideo_SetSourcePosition(pbv, 0, video_height, video_width, video_height);
126 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
127 hr = IBasicVideo_SetSourcePosition(pbv, -1, 0, video_width, video_height);
128 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
129 hr = IBasicVideo_SetSourcePosition(pbv, 0, -1, video_width, video_height);
130 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
131 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
132 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
133 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
134 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
135
136 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width, video_height+1);
137 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
138 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width+1, video_height);
139 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
140
141 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
142 ok(hr==S_OK, "Cannot set source position returned: %x\n", hr);
143
144 hr = IBasicVideo_get_SourceLeft(pbv, &left);
145 ok(hr==S_OK, "Cannot get source left returned: %x\n", hr);
146 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
147 hr = IBasicVideo_get_SourceTop(pbv, &top);
148 ok(hr==S_OK, "Cannot get source top returned: %x\n", hr);
149 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
150 hr = IBasicVideo_get_SourceWidth(pbv, &width);
151 ok(hr==S_OK, "Cannot get source width returned: %x\n", hr);
152 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
153 hr = IBasicVideo_get_SourceHeight(pbv, &height);
154 ok(hr==S_OK, "Cannot get source height returned: %x\n", hr);
155 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
156
157 hr = IBasicVideo_put_SourceLeft(pbv, video_width/3);
158 ok(hr==S_OK, "Cannot put source left returned: %x\n", hr);
159 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
160 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
161 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
162 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
163
164 hr = IBasicVideo_put_SourceTop(pbv, video_height/3);
165 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
166 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
167 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
168 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
169 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
170
171 hr = IBasicVideo_put_SourceWidth(pbv, video_width/4+1);
172 ok(hr==S_OK, "Cannot put source width returned: %x\n", hr);
173 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
174 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
175 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
176 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
177
178 hr = IBasicVideo_put_SourceHeight(pbv, video_height/4+1);
179 ok(hr==S_OK, "Cannot put source height returned: %x\n", hr);
180 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
181 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
182 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
183 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
184
185 /* test destination rectangle */
186 window_width = max(video_width, GetSystemMetrics(SM_CXMIN) - 2 * GetSystemMetrics(SM_CXFRAME));
187
188 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, NULL, NULL);
189 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
190 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, NULL, NULL);
191 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
192 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, &width, &height);
193 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
194 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
195 ok(hr == S_OK, "Cannot get destination position returned: %x\n", hr);
196 ok(left == 0, "expected 0, got %d\n", left);
197 ok(top == 0, "expected 0, got %d\n", top);
198 todo_wine ok(width == window_width, "expected %d, got %d\n", window_width, width);
199 todo_wine ok(height == video_height, "expected %d, got %d\n", video_height, height);
200
201 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, 0, 0);
202 ok(hr==E_INVALIDARG, "IBasicVideo_SetDestinationPosition returned: %x\n", hr);
203 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width*2, video_height*2);
204 ok(hr==S_OK, "Cannot put destination position returned: %x\n", hr);
205
206 hr = IBasicVideo_put_DestinationLeft(pbv, -1);
207 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
208 hr = IBasicVideo_put_DestinationLeft(pbv, 0);
209 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
210 hr = IBasicVideo_put_DestinationLeft(pbv, 1);
211 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
212
213 hr = IBasicVideo_SetDestinationPosition(pbv, video_width, 0, video_width, video_height);
214 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
215 hr = IBasicVideo_SetDestinationPosition(pbv, 0, video_height, video_width, video_height);
216 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
217 hr = IBasicVideo_SetDestinationPosition(pbv, -1, 0, video_width, video_height);
218 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
219 hr = IBasicVideo_SetDestinationPosition(pbv, 0, -1, video_width, video_height);
220 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
221 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
222 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
223 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
224 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
225
226 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height+1);
227 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
228 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width+1, video_height);
229 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
230
231 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
232 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
233
234 hr = IBasicVideo_get_DestinationLeft(pbv, &left);
235 ok(hr==S_OK, "Cannot get destination left returned: %x\n", hr);
236 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
237 hr = IBasicVideo_get_DestinationTop(pbv, &top);
238 ok(hr==S_OK, "Cannot get destination top returned: %x\n", hr);
239 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
240 hr = IBasicVideo_get_DestinationWidth(pbv, &width);
241 ok(hr==S_OK, "Cannot get destination width returned: %x\n", hr);
242 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
243 hr = IBasicVideo_get_DestinationHeight(pbv, &height);
244 ok(hr==S_OK, "Cannot get destination height returned: %x\n", hr);
245 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
246
247 hr = IBasicVideo_put_DestinationLeft(pbv, video_width/3);
248 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
249 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
250 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
251 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
252 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
253
254 hr = IBasicVideo_put_DestinationTop(pbv, video_height/3);
255 ok(hr==S_OK, "Cannot put destination top returned: %x\n", hr);
256 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
257 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
258 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
259 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
260
261 hr = IBasicVideo_put_DestinationWidth(pbv, video_width/4+1);
262 ok(hr==S_OK, "Cannot put destination width returned: %x\n", hr);
263 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
264 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
265 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
266 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
267
268 hr = IBasicVideo_put_DestinationHeight(pbv, video_height/4+1);
269 ok(hr==S_OK, "Cannot put destination height returned: %x\n", hr);
270 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
271 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
272 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
273 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
274
275 /* reset source rectangle */
276 hr = IBasicVideo_SetDefaultSourcePosition(pbv);
277 ok(hr==S_OK, "IBasicVideo_SetDefaultSourcePosition returned: %x\n", hr);
278
279 /* reset destination position */
280 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height);
281 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
282
283 IBasicVideo_Release(pbv);
284 }
285
286 static void rungraph(void)
287 {
288 HRESULT hr;
289 IMediaControl* pmc;
290 IMediaEvent* pme;
291 IMediaFilter* pmf;
292 HANDLE hEvent;
293
294 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaControl, (LPVOID*)&pmc);
295 ok(hr==S_OK, "Cannot get IMediaControl interface returned: %x\n", hr);
296
297 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaFilter, (LPVOID*)&pmf);
298 ok(hr==S_OK, "Cannot get IMediaFilter interface returned: %x\n", hr);
299
300 IMediaControl_Stop(pmc);
301
302 IMediaFilter_SetSyncSource(pmf, NULL);
303
304 IMediaFilter_Release(pmf);
305
306 test_basic_video();
307
308 hr = IMediaControl_Run(pmc);
309 ok(hr==S_FALSE, "Cannot run the graph returned: %x\n", hr);
310
311 Sleep(10);
312 /* Crash fun */
313 trace("run -> stop\n");
314 hr = IMediaControl_Stop(pmc);
315 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
316
317 IGraphBuilder_SetDefaultSyncSource(pgraph);
318
319 Sleep(10);
320 trace("stop -> pause\n");
321 hr = IMediaControl_Pause(pmc);
322 ok(hr==S_OK || hr == S_FALSE, "Cannot pause the graph returned: %x\n", hr);
323
324 Sleep(10);
325 trace("pause -> run\n");
326 hr = IMediaControl_Run(pmc);
327 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
328
329 Sleep(10);
330 trace("run -> pause\n");
331 hr = IMediaControl_Pause(pmc);
332 ok(hr==S_OK || hr == S_FALSE, "Cannot pause the graph returned: %x\n", hr);
333
334 Sleep(10);
335 trace("pause -> stop\n");
336 hr = IMediaControl_Stop(pmc);
337 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
338
339 Sleep(10);
340 trace("pause -> run\n");
341 hr = IMediaControl_Run(pmc);
342 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
343
344 trace("run -> stop\n");
345 hr = IMediaControl_Stop(pmc);
346 ok(hr==S_OK || hr == S_FALSE, "Cannot stop the graph returned: %x\n", hr);
347
348 trace("stop -> run\n");
349 hr = IMediaControl_Run(pmc);
350 ok(hr==S_OK || hr == S_FALSE, "Cannot start the graph returned: %x\n", hr);
351
352 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaEvent, (LPVOID*)&pme);
353 ok(hr==S_OK, "Cannot get IMediaEvent interface returned: %x\n", hr);
354
355 hr = IMediaEvent_GetEventHandle(pme, (OAEVENT*)&hEvent);
356 ok(hr==S_OK, "Cannot get event handle returned: %x\n", hr);
357
358 ok(WaitForSingleObject(hEvent, 2000) == WAIT_OBJECT_0, "Wait failed\n");
359
360 hr = IMediaEvent_Release(pme);
361 ok(hr==2, "Releasing mediaevent returned: %x\n", hr);
362
363 hr = IMediaControl_Stop(pmc);
364 ok(hr==S_OK, "Cannot stop the graph returned: %x\n", hr);
365
366 hr = IMediaControl_Release(pmc);
367 ok(hr==1, "Releasing mediacontrol returned: %x\n", hr);
368 }
369
370 static void releasefiltergraph(void)
371 {
372 HRESULT hr;
373
374 hr = IGraphBuilder_Release(pgraph);
375 ok(hr==0, "Releasing filtergraph returned: %x\n", hr);
376 }
377
378 static void test_render_run(const WCHAR *file)
379 {
380 HANDLE h;
381 HRESULT hr;
382
383 WCHAR *filename = load_resource(file);
384
385 h = CreateFileW(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
386 if (h == INVALID_HANDLE_VALUE) {
387 skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(file));
388 DeleteFileW(filename);
389 return;
390 }
391 CloseHandle(h);
392
393 if (!createfiltergraph())
394 {
395 DeleteFileW(filename);
396 return;
397 }
398
399 hr = IGraphBuilder_RenderFile(pgraph, filename, NULL);
400 if (hr == VFW_E_CANNOT_RENDER)
401 skip("%s: codec not supported; skipping test\n", wine_dbgstr_w(file));
402 else
403 {
404 ok(hr == S_OK || hr == VFW_S_AUDIO_NOT_RENDERED, "RenderFile failed: %x\n", hr);
405 rungraph();
406 }
407
408 releasefiltergraph();
409
410 /* check reference leaks */
411 h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
412 ok(h != INVALID_HANDLE_VALUE, "CreateFile failed: err=%d\n", GetLastError());
413 CloseHandle(h);
414
415 DeleteFileW(filename);
416 }
417
418 static DWORD WINAPI call_RenderFile_multithread(LPVOID lParam)
419 {
420 IFilterGraph2 *filter_graph = lParam;
421 HRESULT hr;
422 WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0};
423 HANDLE handle;
424
425 handle = CreateFileW(mp3file, 0, 0, NULL, CREATE_ALWAYS, 0, NULL);
426 if (handle == INVALID_HANDLE_VALUE)
427 {
428 skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(mp3file));
429 return 1;
430 }
431 CloseHandle(handle);
432
433 hr = IFilterGraph2_RenderFile(filter_graph, mp3file, NULL);
434 todo_wine ok(hr == VFW_E_CANNOT_RENDER || /* xp or older + DirectX 9 */
435 hr == VFW_E_NO_TRANSPORT || /* win7 or newer */
436 broken(hr == CLASS_E_CLASSNOTAVAILABLE), /* xp or older + DirectX 8 or older */
437 "Expected 0x%08x or 0x%08x, returned 0x%08x\n", VFW_E_CANNOT_RENDER, VFW_E_NO_TRANSPORT, hr);
438
439 DeleteFileW(mp3file);
440 return 0;
441 }
442
443 static void test_render_with_multithread(void)
444 {
445 HRESULT hr;
446 HMODULE hmod;
447 static HRESULT (WINAPI *pDllGetClassObject)(REFCLSID rclsid, REFIID riid, void **out);
448 IClassFactory *classfactory = NULL;
449 static IGraphBuilder *graph_builder;
450 static IFilterGraph2 *filter_graph;
451 HANDLE thread;
452
453 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
454
455 hmod = LoadLibraryA("quartz.dll");
456 if (!hmod)
457 {
458 skip("Fail to load quartz.dll.\n");
459 return;
460 }
461
462 pDllGetClassObject = (void*)GetProcAddress(hmod, "DllGetClassObject");
463 if (!pDllGetClassObject)
464 {
465 skip("Fail to get DllGetClassObject.\n");
466 return;
467 }
468
469 hr = pDllGetClassObject(&CLSID_FilterGraph, &IID_IClassFactory, (void **)&classfactory);
470 ok(hr == S_OK, "DllGetClassObject failed 0x%08x\n", hr);
471 if (FAILED(hr))
472 {
473 skip("Can't create IClassFactory 0x%08x.\n", hr);
474 return;
475 }
476
477 hr = IClassFactory_CreateInstance(classfactory, NULL, &IID_IUnknown, (LPVOID*)&graph_builder);
478 ok(hr == S_OK, "IClassFactory_CreateInstance failed 0x%08x\n", hr);
479
480 hr = IGraphBuilder_QueryInterface(graph_builder, &IID_IFilterGraph2, (void**)&filter_graph);
481 ok(hr == S_OK, "IGraphBuilder_QueryInterface failed 0x%08x\n", hr);
482
483 thread = CreateThread(NULL, 0, call_RenderFile_multithread, filter_graph, 0, NULL);
484
485 WaitForSingleObject(thread, 1000);
486 IFilterGraph2_Release(filter_graph);
487 IGraphBuilder_Release(graph_builder);
488 IClassFactory_Release(classfactory);
489 CloseHandle(thread);
490 CoUninitialize();
491 return;
492 }
493
494 static void test_graph_builder(void)
495 {
496 HRESULT hr;
497 IBaseFilter *pF = NULL;
498 IBaseFilter *pF2 = NULL;
499 IPin *pIn = NULL;
500 IEnumPins *pEnum = NULL;
501 PIN_DIRECTION dir;
502 static const WCHAR testFilterW[] = {'t','e','s','t','F','i','l','t','e','r',0};
503 static const WCHAR fooBarW[] = {'f','o','o','B','a','r',0};
504
505 if (!createfiltergraph())
506 return;
507
508 /* create video filter */
509 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
510 &IID_IBaseFilter, (LPVOID*)&pF);
511 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
512 ok(pF != NULL, "pF is NULL\n");
513
514 hr = IGraphBuilder_AddFilter(pgraph, NULL, testFilterW);
515 ok(hr == E_POINTER, "IGraphBuilder_AddFilter returned %x\n", hr);
516
517 /* add the two filters to the graph */
518 hr = IGraphBuilder_AddFilter(pgraph, pF, testFilterW);
519 ok(hr == S_OK, "failed to add pF to the graph: %x\n", hr);
520
521 /* find the pins */
522 hr = IBaseFilter_EnumPins(pF, &pEnum);
523 ok(hr == S_OK, "IBaseFilter_EnumPins failed for pF: %x\n", hr);
524 ok(pEnum != NULL, "pEnum is NULL\n");
525 hr = IEnumPins_Next(pEnum, 1, &pIn, NULL);
526 ok(hr == S_OK, "IEnumPins_Next failed for pF: %x\n", hr);
527 ok(pIn != NULL, "pIn is NULL\n");
528 hr = IPin_QueryDirection(pIn, &dir);
529 ok(hr == S_OK, "IPin_QueryDirection failed: %x\n", hr);
530 ok(dir == PINDIR_INPUT, "pin has wrong direction\n");
531
532 hr = IGraphBuilder_FindFilterByName(pgraph, fooBarW, &pF2);
533 ok(hr == VFW_E_NOT_FOUND, "IGraphBuilder_FindFilterByName returned %x\n", hr);
534 ok(pF2 == NULL, "IGraphBuilder_FindFilterByName returned %p\n", pF2);
535 hr = IGraphBuilder_FindFilterByName(pgraph, testFilterW, &pF2);
536 ok(hr == S_OK, "IGraphBuilder_FindFilterByName returned %x\n", hr);
537 ok(pF2 != NULL, "IGraphBuilder_FindFilterByName returned NULL\n");
538 hr = IGraphBuilder_FindFilterByName(pgraph, testFilterW, NULL);
539 ok(hr == E_POINTER, "IGraphBuilder_FindFilterByName returned %x\n", hr);
540
541 hr = IGraphBuilder_Connect(pgraph, NULL, pIn);
542 ok(hr == E_POINTER, "IGraphBuilder_Connect returned %x\n", hr);
543
544 hr = IGraphBuilder_Connect(pgraph, pIn, NULL);
545 ok(hr == E_POINTER, "IGraphBuilder_Connect returned %x\n", hr);
546
547 hr = IGraphBuilder_Connect(pgraph, pIn, pIn);
548 ok(hr == VFW_E_CANNOT_CONNECT, "IGraphBuilder_Connect returned %x\n", hr);
549
550 if (pIn) IPin_Release(pIn);
551 if (pEnum) IEnumPins_Release(pEnum);
552 if (pF) IBaseFilter_Release(pF);
553 if (pF2) IBaseFilter_Release(pF2);
554
555 releasefiltergraph();
556 }
557
558 static void test_graph_builder_addfilter(void)
559 {
560 HRESULT hr;
561 IBaseFilter *pF = NULL;
562 static const WCHAR testFilterW[] = {'t','e','s','t','F','i','l','t','e','r',0};
563
564 if (!createfiltergraph())
565 return;
566
567 hr = IGraphBuilder_AddFilter(pgraph, NULL, testFilterW);
568 ok(hr == E_POINTER, "IGraphBuilder_AddFilter returned: %x\n", hr);
569
570 /* create video filter */
571 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
572 &IID_IBaseFilter, (LPVOID*)&pF);
573 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
574 ok(pF != NULL, "pF is NULL\n");
575 if (!pF) {
576 skip("failed to created filter, skipping\n");
577 return;
578 }
579
580 hr = IGraphBuilder_AddFilter(pgraph, pF, NULL);
581 ok(hr == S_OK, "IGraphBuilder_AddFilter returned: %x\n", hr);
582 IBaseFilter_Release(pF);
583 }
584
585 static void test_mediacontrol(void)
586 {
587 HRESULT hr;
588 LONGLONG pos = 0xdeadbeef;
589 GUID format = GUID_NULL;
590 IMediaSeeking *seeking = NULL;
591 IMediaFilter *filter = NULL;
592 IMediaControl *control = NULL;
593
594 IGraphBuilder_SetDefaultSyncSource(pgraph);
595 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaSeeking, (void**) &seeking);
596 ok(hr == S_OK, "QueryInterface IMediaControl failed: %08x\n", hr);
597 if (FAILED(hr))
598 return;
599
600 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaFilter, (void**) &filter);
601 ok(hr == S_OK, "QueryInterface IMediaFilter failed: %08x\n", hr);
602 if (FAILED(hr))
603 {
604 IMediaSeeking_Release(seeking);
605 return;
606 }
607
608 hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaControl, (void**) &control);
609 ok(hr == S_OK, "QueryInterface IMediaControl failed: %08x\n", hr);
610 if (FAILED(hr))
611 {
612 IMediaSeeking_Release(seeking);
613 IMediaFilter_Release(filter);
614 return;
615 }
616
617 format = GUID_NULL;
618 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
619 ok(hr == S_OK, "GetTimeFormat failed: %08x\n", hr);
620 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "GetTimeFormat: unexpected format %s\n", wine_dbgstr_guid(&format));
621
622 pos = 0xdeadbeef;
623 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, NULL);
624 ok(hr == S_OK, "ConvertTimeFormat failed: %08x\n", hr);
625 ok(pos == 0x123456789a, "ConvertTimeFormat: expected 123456789a, got (%s)\n", wine_dbgstr_longlong(pos));
626
627 pos = 0xdeadbeef;
628 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
629 ok(hr == S_OK, "ConvertTimeFormat failed: %08x\n", hr);
630 ok(pos == 0x123456789a, "ConvertTimeFormat: expected 123456789a, got (%s)\n", wine_dbgstr_longlong(pos));
631
632 pos = 0xdeadbeef;
633 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
634 ok(hr == S_OK, "ConvertTimeFormat failed: %08x\n", hr);
635 ok(pos == 0x123456789a, "ConvertTimeFormat: expected 123456789a, got (%s)\n", wine_dbgstr_longlong(pos));
636
637 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
638 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
639 ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
640
641 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
642 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
643 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_NoPositioning, NULL, AM_SEEKING_ReturnTime);
644 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
645
646 IMediaFilter_SetSyncSource(filter, NULL);
647 pos = 0xdeadbeef;
648 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
649 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
650 ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
651
652 hr = IMediaControl_GetState(control, 1000, NULL);
653 ok(hr == E_POINTER, "GetState expected %08x, got %08x\n", E_POINTER, hr);
654
655 IMediaControl_Release(control);
656 IMediaSeeking_Release(seeking);
657 IMediaFilter_Release(filter);
658 releasefiltergraph();
659 }
660
661 static void test_filter_graph2(void)
662 {
663 HRESULT hr;
664 IFilterGraph2 *pF = NULL;
665
666 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
667 &IID_IFilterGraph2, (LPVOID*)&pF);
668 ok(hr == S_OK, "CoCreateInstance failed with %x\n", hr);
669 ok(pF != NULL, "pF is NULL\n");
670
671 hr = IFilterGraph2_Release(pF);
672 ok(hr == 0, "IFilterGraph2_Release returned: %x\n", hr);
673 }
674
675 /* IEnumMediaTypes implementation (supporting code for Render() test.) */
676 static void FreeMediaType(AM_MEDIA_TYPE * pMediaType)
677 {
678 if (pMediaType->pbFormat)
679 {
680 CoTaskMemFree(pMediaType->pbFormat);
681 pMediaType->pbFormat = NULL;
682 }
683 if (pMediaType->pUnk)
684 {
685 IUnknown_Release(pMediaType->pUnk);
686 pMediaType->pUnk = NULL;
687 }
688 }
689
690 static HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
691 {
692 *pDest = *pSrc;
693 if (!pSrc->pbFormat) return S_OK;
694 if (!(pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat)))
695 return E_OUTOFMEMORY;
696 memcpy(pDest->pbFormat, pSrc->pbFormat, pSrc->cbFormat);
697 if (pDest->pUnk)
698 IUnknown_AddRef(pDest->pUnk);
699 return S_OK;
700 }
701
702 static AM_MEDIA_TYPE * CreateMediaType(AM_MEDIA_TYPE const * pSrc)
703 {
704 AM_MEDIA_TYPE * pDest;
705
706 pDest = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
707 if (!pDest)
708 return NULL;
709
710 if (FAILED(CopyMediaType(pDest, pSrc)))
711 {
712 CoTaskMemFree(pDest);
713 return NULL;
714 }
715
716 return pDest;
717 }
718
719 static BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
720 {
721 return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) &&
722 ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL) || IsEqualGUID(&pmt2->subtype, &GUID_NULL))) || IsEqualGUID(&pmt1->subtype, &pmt2->subtype)));
723 }
724
725 static void DeleteMediaType(AM_MEDIA_TYPE * pMediaType)
726 {
727 FreeMediaType(pMediaType);
728 CoTaskMemFree(pMediaType);
729 }
730
731 typedef struct IEnumMediaTypesImpl
732 {
733 IEnumMediaTypes IEnumMediaTypes_iface;
734 LONG refCount;
735 AM_MEDIA_TYPE *pMediaTypes;
736 ULONG cMediaTypes;
737 ULONG uIndex;
738 } IEnumMediaTypesImpl;
739
740 static const struct IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl;
741
742 static inline IEnumMediaTypesImpl *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
743 {
744 return CONTAINING_RECORD(iface, IEnumMediaTypesImpl, IEnumMediaTypes_iface);
745 }
746
747 static HRESULT IEnumMediaTypesImpl_Construct(const AM_MEDIA_TYPE * pMediaTypes, ULONG cMediaTypes, IEnumMediaTypes ** ppEnum)
748 {
749 ULONG i;
750 IEnumMediaTypesImpl * pEnumMediaTypes = CoTaskMemAlloc(sizeof(IEnumMediaTypesImpl));
751
752 if (!pEnumMediaTypes)
753 {
754 *ppEnum = NULL;
755 return E_OUTOFMEMORY;
756 }
757 pEnumMediaTypes->IEnumMediaTypes_iface.lpVtbl = &IEnumMediaTypesImpl_Vtbl;
758 pEnumMediaTypes->refCount = 1;
759 pEnumMediaTypes->uIndex = 0;
760 pEnumMediaTypes->cMediaTypes = cMediaTypes;
761 pEnumMediaTypes->pMediaTypes = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE) * cMediaTypes);
762 for (i = 0; i < cMediaTypes; i++)
763 if (FAILED(CopyMediaType(&pEnumMediaTypes->pMediaTypes[i], &pMediaTypes[i])))
764 {
765 while (i--)
766 FreeMediaType(&pEnumMediaTypes->pMediaTypes[i]);
767 CoTaskMemFree(pEnumMediaTypes->pMediaTypes);
768 return E_OUTOFMEMORY;
769 }
770 *ppEnum = &pEnumMediaTypes->IEnumMediaTypes_iface;
771 return S_OK;
772 }
773
774 static HRESULT WINAPI IEnumMediaTypesImpl_QueryInterface(IEnumMediaTypes * iface, REFIID riid, LPVOID * ppv)
775 {
776 *ppv = NULL;
777
778 if (IsEqualIID(riid, &IID_IUnknown))
779 *ppv = iface;
780 else if (IsEqualIID(riid, &IID_IEnumMediaTypes))
781 *ppv = iface;
782
783 if (*ppv)
784 {
785 IUnknown_AddRef((IUnknown *)(*ppv));
786 return S_OK;
787 }
788
789 return E_NOINTERFACE;
790 }
791
792 static ULONG WINAPI IEnumMediaTypesImpl_AddRef(IEnumMediaTypes * iface)
793 {
794 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
795 ULONG refCount = InterlockedIncrement(&This->refCount);
796
797 return refCount;
798 }
799
800 static ULONG WINAPI IEnumMediaTypesImpl_Release(IEnumMediaTypes * iface)
801 {
802 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
803 ULONG refCount = InterlockedDecrement(&This->refCount);
804
805 if (!refCount)
806 {
807 int i;
808 for (i = 0; i < This->cMediaTypes; i++)
809 FreeMediaType(&This->pMediaTypes[i]);
810 CoTaskMemFree(This->pMediaTypes);
811 CoTaskMemFree(This);
812 }
813 return refCount;
814 }
815
816 static HRESULT WINAPI IEnumMediaTypesImpl_Next(IEnumMediaTypes * iface, ULONG cMediaTypes, AM_MEDIA_TYPE ** ppMediaTypes, ULONG * pcFetched)
817 {
818 ULONG cFetched;
819 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
820
821 cFetched = min(This->cMediaTypes, This->uIndex + cMediaTypes) - This->uIndex;
822
823 if (cFetched > 0)
824 {
825 ULONG i;
826 for (i = 0; i < cFetched; i++)
827 if (!(ppMediaTypes[i] = CreateMediaType(&This->pMediaTypes[This->uIndex + i])))
828 {
829 while (i--)
830 DeleteMediaType(ppMediaTypes[i]);
831 *pcFetched = 0;
832 return E_OUTOFMEMORY;
833 }
834 }
835
836 if ((cMediaTypes != 1) || pcFetched)
837 *pcFetched = cFetched;
838
839 This->uIndex += cFetched;
840
841 if (cFetched != cMediaTypes)
842 return S_FALSE;
843 return S_OK;
844 }
845
846 static HRESULT WINAPI IEnumMediaTypesImpl_Skip(IEnumMediaTypes * iface, ULONG cMediaTypes)
847 {
848 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
849
850 if (This->uIndex + cMediaTypes < This->cMediaTypes)
851 {
852 This->uIndex += cMediaTypes;
853 return S_OK;
854 }
855 return S_FALSE;
856 }
857
858 static HRESULT WINAPI IEnumMediaTypesImpl_Reset(IEnumMediaTypes * iface)
859 {
860 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
861
862 This->uIndex = 0;
863 return S_OK;
864 }
865
866 static HRESULT WINAPI IEnumMediaTypesImpl_Clone(IEnumMediaTypes * iface, IEnumMediaTypes ** ppEnum)
867 {
868 HRESULT hr;
869 IEnumMediaTypesImpl *This = impl_from_IEnumMediaTypes(iface);
870
871 hr = IEnumMediaTypesImpl_Construct(This->pMediaTypes, This->cMediaTypes, ppEnum);
872 if (FAILED(hr))
873 return hr;
874 return IEnumMediaTypes_Skip(*ppEnum, This->uIndex);
875 }
876
877 static const IEnumMediaTypesVtbl IEnumMediaTypesImpl_Vtbl =
878 {
879 IEnumMediaTypesImpl_QueryInterface,
880 IEnumMediaTypesImpl_AddRef,
881 IEnumMediaTypesImpl_Release,
882 IEnumMediaTypesImpl_Next,
883 IEnumMediaTypesImpl_Skip,
884 IEnumMediaTypesImpl_Reset,
885 IEnumMediaTypesImpl_Clone
886 };
887
888 /* Implementation of a very stripped down pin for the test filter. Just enough
889 functionality for connecting and Render() to work. */
890
891 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
892 {
893 lstrcpyW(pDest->achName, pSrc->achName);
894 pDest->dir = pSrc->dir;
895 pDest->pFilter = pSrc->pFilter;
896 }
897
898 typedef struct ITestPinImpl
899 {
900 IPin IPin_iface;
901 LONG refCount;
902 LPCRITICAL_SECTION pCritSec;
903 PIN_INFO pinInfo;
904 IPin * pConnectedTo;
905 AM_MEDIA_TYPE mtCurrent;
906 LPVOID pUserData;
907 } ITestPinImpl;
908
909 static inline ITestPinImpl *impl_from_IPin(IPin *iface)
910 {
911 return CONTAINING_RECORD(iface, ITestPinImpl, IPin_iface);
912 }
913
914 static HRESULT WINAPI TestFilter_Pin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
915 {
916 *ppv = NULL;
917
918 if (IsEqualIID(riid, &IID_IUnknown))
919 *ppv = iface;
920 else if (IsEqualIID(riid, &IID_IPin))
921 *ppv = iface;
922
923 if (*ppv)
924 {
925 IUnknown_AddRef((IUnknown *)(*ppv));
926 return S_OK;
927 }
928
929 return E_NOINTERFACE;
930 }
931
932 static ULONG WINAPI TestFilter_Pin_AddRef(IPin * iface)
933 {
934 ITestPinImpl *This = impl_from_IPin(iface);
935 ULONG refCount = InterlockedIncrement(&This->refCount);
936 return refCount;
937 }
938
939 static ULONG WINAPI TestFilter_Pin_Release(IPin * iface)
940 {
941 ITestPinImpl *This = impl_from_IPin(iface);
942 ULONG refCount = InterlockedDecrement(&This->refCount);
943
944 if (!refCount)
945 {
946 FreeMediaType(&This->mtCurrent);
947 CoTaskMemFree(This);
948 return 0;
949 }
950 else
951 return refCount;
952 }
953
954 static HRESULT WINAPI TestFilter_InputPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
955 {
956 return E_UNEXPECTED;
957 }
958
959 static HRESULT WINAPI TestFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
960 {
961 ITestPinImpl *This = impl_from_IPin(iface);
962 PIN_DIRECTION pindirReceive;
963 HRESULT hr = S_OK;
964
965 EnterCriticalSection(This->pCritSec);
966 {
967 if (!(IsEqualIID(&pmt->majortype, &This->mtCurrent.majortype) && (IsEqualIID(&pmt->subtype, &This->mtCurrent.subtype) ||
968 IsEqualIID(&GUID_NULL, &This->mtCurrent.subtype))))
969 hr = VFW_E_TYPE_NOT_ACCEPTED;
970
971 if (This->pConnectedTo)
972 hr = VFW_E_ALREADY_CONNECTED;
973
974 if (SUCCEEDED(hr))
975 {
976 IPin_QueryDirection(pReceivePin, &pindirReceive);
977
978 if (pindirReceive != PINDIR_OUTPUT)
979 {
980 hr = VFW_E_INVALID_DIRECTION;
981 }
982 }
983
984 if (SUCCEEDED(hr))
985 {
986 CopyMediaType(&This->mtCurrent, pmt);
987 This->pConnectedTo = pReceivePin;
988 IPin_AddRef(pReceivePin);
989 }
990 }
991 LeaveCriticalSection(This->pCritSec);
992
993 return hr;
994 }
995
996 static HRESULT WINAPI TestFilter_Pin_Disconnect(IPin * iface)
997 {
998 HRESULT hr;
999 ITestPinImpl *This = impl_from_IPin(iface);
1000
1001 EnterCriticalSection(This->pCritSec);
1002 {
1003 if (This->pConnectedTo)
1004 {
1005 IPin_Release(This->pConnectedTo);
1006 This->pConnectedTo = NULL;
1007 hr = S_OK;
1008 }
1009 else
1010 hr = S_FALSE;
1011 }
1012 LeaveCriticalSection(This->pCritSec);
1013
1014 return hr;
1015 }
1016
1017 static HRESULT WINAPI TestFilter_Pin_ConnectedTo(IPin * iface, IPin ** ppPin)
1018 {
1019 HRESULT hr;
1020 ITestPinImpl *This = impl_from_IPin(iface);
1021
1022 EnterCriticalSection(This->pCritSec);
1023 {
1024 if (This->pConnectedTo)
1025 {
1026 *ppPin = This->pConnectedTo;
1027 IPin_AddRef(*ppPin);
1028 hr = S_OK;
1029 }
1030 else
1031 {
1032 hr = VFW_E_NOT_CONNECTED;
1033 *ppPin = NULL;
1034 }
1035 }
1036 LeaveCriticalSection(This->pCritSec);
1037
1038 return hr;
1039 }
1040
1041 static HRESULT WINAPI TestFilter_Pin_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
1042 {
1043 HRESULT hr;
1044 ITestPinImpl *This = impl_from_IPin(iface);
1045
1046 EnterCriticalSection(This->pCritSec);
1047 {
1048 if (This->pConnectedTo)
1049 {
1050 CopyMediaType(pmt, &This->mtCurrent);
1051 hr = S_OK;
1052 }
1053 else
1054 {
1055 ZeroMemory(pmt, sizeof(*pmt));
1056 hr = VFW_E_NOT_CONNECTED;
1057 }
1058 }
1059 LeaveCriticalSection(This->pCritSec);
1060
1061 return hr;
1062 }
1063
1064 static HRESULT WINAPI TestFilter_Pin_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
1065 {
1066 ITestPinImpl *This = impl_from_IPin(iface);
1067
1068 Copy_PinInfo(pInfo, &This->pinInfo);
1069 IBaseFilter_AddRef(pInfo->pFilter);
1070
1071 return S_OK;
1072 }
1073
1074 static HRESULT WINAPI TestFilter_Pin_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
1075 {
1076 ITestPinImpl *This = impl_from_IPin(iface);
1077
1078 *pPinDir = This->pinInfo.dir;
1079
1080 return S_OK;
1081 }
1082
1083 static HRESULT WINAPI TestFilter_Pin_QueryId(IPin * iface, LPWSTR * Id)
1084 {
1085 return E_NOTIMPL;
1086 }
1087
1088 static HRESULT WINAPI TestFilter_Pin_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
1089 {
1090 ITestPinImpl *This = impl_from_IPin(iface);
1091
1092 if (IsEqualIID(&pmt->majortype, &This->mtCurrent.majortype) && (IsEqualIID(&pmt->subtype, &This->mtCurrent.subtype) ||
1093 IsEqualIID(&GUID_NULL, &This->mtCurrent.subtype)))
1094 return S_OK;
1095 else
1096 return VFW_E_TYPE_NOT_ACCEPTED;
1097 }
1098
1099 static HRESULT WINAPI TestFilter_Pin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
1100 {
1101 ITestPinImpl *This = impl_from_IPin(iface);
1102
1103 return IEnumMediaTypesImpl_Construct(&This->mtCurrent, 1, ppEnum);
1104 }
1105
1106 static HRESULT WINAPI TestFilter_Pin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
1107 {
1108 return E_NOTIMPL;
1109 }
1110
1111 static HRESULT WINAPI TestFilter_Pin_BeginFlush(IPin * iface)
1112 {
1113 return E_NOTIMPL;
1114 }
1115
1116 static HRESULT WINAPI TestFilter_Pin_EndFlush(IPin * iface)
1117 {
1118 return E_NOTIMPL;
1119 }
1120
1121 static HRESULT WINAPI TestFilter_Pin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1122 {
1123 return E_NOTIMPL;
1124 }
1125
1126 static HRESULT WINAPI TestFilter_Pin_EndOfStream(IPin * iface)
1127 {
1128 return E_NOTIMPL;
1129 }
1130
1131 static const IPinVtbl TestFilter_InputPin_Vtbl =
1132 {
1133 TestFilter_Pin_QueryInterface,
1134 TestFilter_Pin_AddRef,
1135 TestFilter_Pin_Release,
1136 TestFilter_InputPin_Connect,
1137 TestFilter_InputPin_ReceiveConnection,
1138 TestFilter_Pin_Disconnect,
1139 TestFilter_Pin_ConnectedTo,
1140 TestFilter_Pin_ConnectionMediaType,
1141 TestFilter_Pin_QueryPinInfo,
1142 TestFilter_Pin_QueryDirection,
1143 TestFilter_Pin_QueryId,
1144 TestFilter_Pin_QueryAccept,
1145 TestFilter_Pin_EnumMediaTypes,
1146 TestFilter_Pin_QueryInternalConnections,
1147 TestFilter_Pin_EndOfStream,
1148 TestFilter_Pin_BeginFlush,
1149 TestFilter_Pin_EndFlush,
1150 TestFilter_Pin_NewSegment
1151 };
1152
1153 static HRESULT WINAPI TestFilter_OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
1154 {
1155 return E_UNEXPECTED;
1156 }
1157
1158 /* Private helper function */
1159 static HRESULT TestFilter_OutputPin_ConnectSpecific(ITestPinImpl * This, IPin * pReceivePin,
1160 const AM_MEDIA_TYPE * pmt)
1161 {
1162 HRESULT hr;
1163
1164 This->pConnectedTo = pReceivePin;
1165 IPin_AddRef(pReceivePin);
1166
1167 hr = IPin_ReceiveConnection(pReceivePin, &This->IPin_iface, pmt);
1168
1169 if (FAILED(hr))
1170 {
1171 IPin_Release(This->pConnectedTo);
1172 This->pConnectedTo = NULL;
1173 }
1174
1175 return hr;
1176 }
1177
1178 static HRESULT WINAPI TestFilter_OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
1179 {
1180 ITestPinImpl *This = impl_from_IPin(iface);
1181 HRESULT hr;
1182
1183 EnterCriticalSection(This->pCritSec);
1184 {
1185 /* if we have been a specific type to connect with, then we can either connect
1186 * with that or fail. We cannot choose different AM_MEDIA_TYPE */
1187 if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
1188 hr = TestFilter_OutputPin_ConnectSpecific(This, pReceivePin, pmt);
1189 else
1190 {
1191 if (( !pmt || CompareMediaTypes(pmt, &This->mtCurrent, TRUE) ) &&
1192 (TestFilter_OutputPin_ConnectSpecific(This, pReceivePin, &This->mtCurrent) == S_OK))
1193 hr = S_OK;
1194 else hr = VFW_E_NO_ACCEPTABLE_TYPES;
1195 } /* if negotiate media type */
1196 } /* if succeeded */
1197 LeaveCriticalSection(This->pCritSec);
1198
1199 return hr;
1200 }
1201
1202 static const IPinVtbl TestFilter_OutputPin_Vtbl =
1203 {
1204 TestFilter_Pin_QueryInterface,
1205 TestFilter_Pin_AddRef,
1206 TestFilter_Pin_Release,
1207 TestFilter_OutputPin_Connect,
1208 TestFilter_OutputPin_ReceiveConnection,
1209 TestFilter_Pin_Disconnect,
1210 TestFilter_Pin_ConnectedTo,
1211 TestFilter_Pin_ConnectionMediaType,
1212 TestFilter_Pin_QueryPinInfo,
1213 TestFilter_Pin_QueryDirection,
1214 TestFilter_Pin_QueryId,
1215 TestFilter_Pin_QueryAccept,
1216 TestFilter_Pin_EnumMediaTypes,
1217 TestFilter_Pin_QueryInternalConnections,
1218 TestFilter_Pin_EndOfStream,
1219 TestFilter_Pin_BeginFlush,
1220 TestFilter_Pin_EndFlush,
1221 TestFilter_Pin_NewSegment
1222 };
1223
1224 static HRESULT TestFilter_Pin_Construct(const IPinVtbl *Pin_Vtbl, const PIN_INFO * pPinInfo, AM_MEDIA_TYPE *pinmt,
1225 LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
1226 {
1227 ITestPinImpl * pPinImpl;
1228
1229 *ppPin = NULL;
1230
1231 pPinImpl = CoTaskMemAlloc(sizeof(ITestPinImpl));
1232
1233 if (!pPinImpl)
1234 return E_OUTOFMEMORY;
1235
1236 pPinImpl->refCount = 1;
1237 pPinImpl->pConnectedTo = NULL;
1238 pPinImpl->pCritSec = pCritSec;
1239 Copy_PinInfo(&pPinImpl->pinInfo, pPinInfo);
1240 pPinImpl->mtCurrent = *pinmt;
1241
1242 pPinImpl->IPin_iface.lpVtbl = Pin_Vtbl;
1243
1244 *ppPin = &pPinImpl->IPin_iface;
1245 return S_OK;
1246 }
1247
1248 /* IEnumPins implementation */
1249
1250 typedef HRESULT (* FNOBTAINPIN)(TestFilterImpl *tf, ULONG pos, IPin **pin, DWORD *lastsynctick);
1251
1252 typedef struct IEnumPinsImpl
1253 {
1254 IEnumPins IEnumPins_iface;
1255 LONG refCount;
1256 ULONG uIndex;
1257 TestFilterImpl *base;
1258 FNOBTAINPIN receive_pin;
1259 DWORD synctime;
1260 } IEnumPinsImpl;
1261
1262 static const struct IEnumPinsVtbl IEnumPinsImpl_Vtbl;
1263
1264 static inline IEnumPinsImpl *impl_from_IEnumPins(IEnumPins *iface)
1265 {
1266 return CONTAINING_RECORD(iface, IEnumPinsImpl, IEnumPins_iface);
1267 }
1268
1269 static HRESULT createenumpins(IEnumPins ** ppEnum, FNOBTAINPIN receive_pin, TestFilterImpl *base)
1270 {
1271 IEnumPinsImpl * pEnumPins;
1272
1273 if (!ppEnum)
1274 return E_POINTER;
1275
1276 pEnumPins = CoTaskMemAlloc(sizeof(IEnumPinsImpl));
1277 if (!pEnumPins)
1278 {
1279 *ppEnum = NULL;
1280 return E_OUTOFMEMORY;
1281 }
1282 pEnumPins->IEnumPins_iface.lpVtbl = &IEnumPinsImpl_Vtbl;
1283 pEnumPins->refCount = 1;
1284 pEnumPins->uIndex = 0;
1285 pEnumPins->receive_pin = receive_pin;
1286 pEnumPins->base = base;
1287 IBaseFilter_AddRef(&base->IBaseFilter_iface);
1288 *ppEnum = &pEnumPins->IEnumPins_iface;
1289
1290 receive_pin(base, ~0, NULL, &pEnumPins->synctime);
1291
1292 return S_OK;
1293 }
1294
1295 static HRESULT WINAPI IEnumPinsImpl_QueryInterface(IEnumPins * iface, REFIID riid, LPVOID * ppv)
1296 {
1297 *ppv = NULL;
1298
1299 if (IsEqualIID(riid, &IID_IUnknown))
1300 *ppv = iface;
1301 else if (IsEqualIID(riid, &IID_IEnumPins))
1302 *ppv = iface;
1303
1304 if (*ppv)
1305 {
1306 IUnknown_AddRef((IUnknown *)(*ppv));
1307 return S_OK;
1308 }
1309
1310 return E_NOINTERFACE;
1311 }
1312
1313 static ULONG WINAPI IEnumPinsImpl_AddRef(IEnumPins * iface)
1314 {
1315 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1316 ULONG refCount = InterlockedIncrement(&This->refCount);
1317
1318 return refCount;
1319 }
1320
1321 static ULONG WINAPI IEnumPinsImpl_Release(IEnumPins * iface)
1322 {
1323 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1324 ULONG refCount = InterlockedDecrement(&This->refCount);
1325
1326 if (!refCount)
1327 {
1328 IBaseFilter_Release(&This->base->IBaseFilter_iface);
1329 CoTaskMemFree(This);
1330 return 0;
1331 }
1332 else
1333 return refCount;
1334 }
1335
1336 static HRESULT WINAPI IEnumPinsImpl_Next(IEnumPins * iface, ULONG cPins, IPin ** ppPins, ULONG * pcFetched)
1337 {
1338 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1339 DWORD synctime = This->synctime;
1340 HRESULT hr = S_OK;
1341 ULONG i = 0;
1342
1343 if (!ppPins)
1344 return E_POINTER;
1345
1346 if (cPins > 1 && !pcFetched)
1347 return E_INVALIDARG;
1348
1349 if (pcFetched)
1350 *pcFetched = 0;
1351
1352 while (i < cPins && hr == S_OK)
1353 {
1354 hr = This->receive_pin(This->base, This->uIndex + i, &ppPins[i], &synctime);
1355
1356 if (hr == S_OK)
1357 ++i;
1358
1359 if (synctime != This->synctime)
1360 break;
1361 }
1362
1363 if (!i && synctime != This->synctime)
1364 return VFW_E_ENUM_OUT_OF_SYNC;
1365
1366 if (pcFetched)
1367 *pcFetched = i;
1368 This->uIndex += i;
1369
1370 if (i < cPins)
1371 return S_FALSE;
1372 return S_OK;
1373 }
1374
1375 static HRESULT WINAPI IEnumPinsImpl_Skip(IEnumPins * iface, ULONG cPins)
1376 {
1377 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1378 DWORD synctime = This->synctime;
1379 HRESULT hr;
1380 IPin *pin = NULL;
1381
1382 hr = This->receive_pin(This->base, This->uIndex + cPins, &pin, &synctime);
1383 if (pin)
1384 IPin_Release(pin);
1385
1386 if (synctime != This->synctime)
1387 return VFW_E_ENUM_OUT_OF_SYNC;
1388
1389 if (hr == S_OK)
1390 This->uIndex += cPins;
1391
1392 return hr;
1393 }
1394
1395 static HRESULT WINAPI IEnumPinsImpl_Reset(IEnumPins * iface)
1396 {
1397 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1398
1399 This->receive_pin(This->base, ~0, NULL, &This->synctime);
1400
1401 This->uIndex = 0;
1402 return S_OK;
1403 }
1404
1405 static HRESULT WINAPI IEnumPinsImpl_Clone(IEnumPins * iface, IEnumPins ** ppEnum)
1406 {
1407 HRESULT hr;
1408 IEnumPinsImpl *This = impl_from_IEnumPins(iface);
1409
1410 hr = createenumpins(ppEnum, This->receive_pin, This->base);
1411 if (FAILED(hr))
1412 return hr;
1413 return IEnumPins_Skip(*ppEnum, This->uIndex);
1414 }
1415
1416 static const IEnumPinsVtbl IEnumPinsImpl_Vtbl =
1417 {
1418 IEnumPinsImpl_QueryInterface,
1419 IEnumPinsImpl_AddRef,
1420 IEnumPinsImpl_Release,
1421 IEnumPinsImpl_Next,
1422 IEnumPinsImpl_Skip,
1423 IEnumPinsImpl_Reset,
1424 IEnumPinsImpl_Clone
1425 };
1426
1427 /* Test filter implementation - a filter that has few predefined pins with single media type
1428 * that accept only this single media type. Enough for Render(). */
1429
1430 typedef struct TestFilterPinData
1431 {
1432 PIN_DIRECTION pinDir;
1433 const GUID *mediasubtype;
1434 } TestFilterPinData;
1435
1436 static const IBaseFilterVtbl TestFilter_Vtbl;
1437
1438 static inline TestFilterImpl *impl_from_IBaseFilter(IBaseFilter *iface)
1439 {
1440 return CONTAINING_RECORD(iface, TestFilterImpl, IBaseFilter_iface);
1441 }
1442
1443 static HRESULT createtestfilter(const CLSID* pClsid, const TestFilterPinData *pinData,
1444 TestFilterImpl **tf)
1445 {
1446 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
1447 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
1448 HRESULT hr;
1449 PIN_INFO pinInfo;
1450 TestFilterImpl* pTestFilter = NULL;
1451 UINT nPins, i;
1452 AM_MEDIA_TYPE mt;
1453
1454 pTestFilter = CoTaskMemAlloc(sizeof(TestFilterImpl));
1455 if (!pTestFilter) return E_OUTOFMEMORY;
1456
1457 pTestFilter->clsid = *pClsid;
1458 pTestFilter->IBaseFilter_iface.lpVtbl = &TestFilter_Vtbl;
1459 pTestFilter->refCount = 1;
1460 InitializeCriticalSection(&pTestFilter->csFilter);
1461 pTestFilter->state = State_Stopped;
1462
1463 ZeroMemory(&pTestFilter->filterInfo, sizeof(FILTER_INFO));
1464
1465 nPins = 0;
1466 while(pinData[nPins].mediasubtype) ++nPins;
1467
1468 pTestFilter->ppPins = CoTaskMemAlloc(nPins * sizeof(IPin *));
1469 if (!pTestFilter->ppPins)
1470 {
1471 hr = E_OUTOFMEMORY;
1472 goto error;
1473 }
1474 ZeroMemory(pTestFilter->ppPins, nPins * sizeof(IPin *));
1475
1476 for (i = 0; i < nPins; i++)
1477 {
1478 ZeroMemory(&mt, sizeof(mt));
1479 mt.majortype = MEDIATYPE_Video;
1480 mt.formattype = FORMAT_None;
1481 mt.subtype = *pinData[i].mediasubtype;
1482
1483 pinInfo.dir = pinData[i].pinDir;
1484 pinInfo.pFilter = &pTestFilter->IBaseFilter_iface;
1485 if (pinInfo.dir == PINDIR_INPUT)
1486 {
1487 lstrcpynW(pinInfo.achName, wcsInputPinName, sizeof(pinInfo.achName) / sizeof(pinInfo.achName[0]));
1488 hr = TestFilter_Pin_Construct(&TestFilter_InputPin_Vtbl, &pinInfo, &mt, &pTestFilter->csFilter,
1489 &pTestFilter->ppPins[i]);
1490
1491 }
1492 else
1493 {
1494 lstrcpynW(pinInfo.achName, wcsOutputPinName, sizeof(pinInfo.achName) / sizeof(pinInfo.achName[0]));
1495 hr = TestFilter_Pin_Construct(&TestFilter_OutputPin_Vtbl, &pinInfo, &mt, &pTestFilter->csFilter,
1496 &pTestFilter->ppPins[i]);
1497 }
1498 if (FAILED(hr) || !pTestFilter->ppPins[i]) goto error;
1499 }
1500
1501 pTestFilter->nPins = nPins;
1502 *tf = pTestFilter;
1503 return S_OK;
1504
1505 error:
1506
1507 if (pTestFilter->ppPins)
1508 {
1509 for (i = 0; i < nPins; i++)
1510 {
1511 if (pTestFilter->ppPins[i]) IPin_Release(pTestFilter->ppPins[i]);
1512 }
1513 }
1514 CoTaskMemFree(pTestFilter->ppPins);
1515 DeleteCriticalSection(&pTestFilter->csFilter);
1516 CoTaskMemFree(pTestFilter);
1517
1518 return hr;
1519 }
1520
1521 static HRESULT WINAPI TestFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
1522 {
1523 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1524
1525 *ppv = NULL;
1526
1527 if (IsEqualIID(riid, &IID_IUnknown))
1528 *ppv = This;
1529 else if (IsEqualIID(riid, &IID_IPersist))
1530 *ppv = This;
1531 else if (IsEqualIID(riid, &IID_IMediaFilter))
1532 *ppv = This;
1533 else if (IsEqualIID(riid, &IID_IBaseFilter))
1534 *ppv = This;
1535
1536 if (*ppv)
1537 {
1538 IUnknown_AddRef((IUnknown *)(*ppv));
1539 return S_OK;
1540 }
1541
1542 return E_NOINTERFACE;
1543 }
1544
1545 static ULONG WINAPI TestFilter_AddRef(IBaseFilter * iface)
1546 {
1547 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1548 ULONG refCount = InterlockedIncrement(&This->refCount);
1549
1550 return refCount;
1551 }
1552
1553 static ULONG WINAPI TestFilter_Release(IBaseFilter * iface)
1554 {
1555 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1556 ULONG refCount = InterlockedDecrement(&This->refCount);
1557
1558 if (!refCount)
1559 {
1560 ULONG i;
1561
1562 for (i = 0; i < This->nPins; i++)
1563 {
1564 IPin *pConnectedTo;
1565
1566 if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
1567 {
1568 IPin_Disconnect(pConnectedTo);
1569 IPin_Release(pConnectedTo);
1570 }
1571 IPin_Disconnect(This->ppPins[i]);
1572
1573 IPin_Release(This->ppPins[i]);
1574 }
1575
1576 CoTaskMemFree(This->ppPins);
1577
1578 DeleteCriticalSection(&This->csFilter);
1579
1580 CoTaskMemFree(This);
1581
1582 return 0;
1583 }
1584 else
1585 return refCount;
1586 }
1587 /** IPersist methods **/
1588
1589 static HRESULT WINAPI TestFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
1590 {
1591 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1592
1593 *pClsid = This->clsid;
1594
1595 return S_OK;
1596 }
1597
1598 /** IMediaFilter methods **/
1599
1600 static HRESULT WINAPI TestFilter_Stop(IBaseFilter * iface)
1601 {
1602 return E_NOTIMPL;
1603 }
1604
1605 static HRESULT WINAPI TestFilter_Pause(IBaseFilter * iface)
1606 {
1607 return E_NOTIMPL;
1608 }
1609
1610 static HRESULT WINAPI TestFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
1611 {
1612 return E_NOTIMPL;
1613 }
1614
1615 static HRESULT WINAPI TestFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
1616 {
1617 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1618
1619 EnterCriticalSection(&This->csFilter);
1620 {
1621 *pState = This->state;
1622 }
1623 LeaveCriticalSection(&This->csFilter);
1624
1625 return S_OK;
1626 }
1627
1628 static HRESULT WINAPI TestFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
1629 {
1630 return E_NOTIMPL;
1631 }
1632
1633 static HRESULT WINAPI TestFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
1634 {
1635 return E_NOTIMPL;
1636 }
1637
1638 /** IBaseFilter implementation **/
1639
1640 static HRESULT getpin_callback(TestFilterImpl *tf, ULONG pos, IPin **pin, DWORD *lastsynctick)
1641 {
1642 /* Our pins are static, not changing so setting static tick count is ok */
1643 *lastsynctick = 0;
1644
1645 if (pos >= tf->nPins)
1646 return S_FALSE;
1647
1648 *pin = tf->ppPins[pos];
1649 IPin_AddRef(*pin);
1650 return S_OK;
1651 }
1652
1653 static HRESULT WINAPI TestFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
1654 {
1655 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1656
1657 return createenumpins(ppEnum, getpin_callback, This);
1658 }
1659
1660 static HRESULT WINAPI TestFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
1661 {
1662 return E_NOTIMPL;
1663 }
1664
1665 static HRESULT WINAPI TestFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
1666 {
1667 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1668
1669 lstrcpyW(pInfo->achName, This->filterInfo.achName);
1670 pInfo->pGraph = This->filterInfo.pGraph;
1671
1672 if (pInfo->pGraph)
1673 IFilterGraph_AddRef(pInfo->pGraph);
1674
1675 return S_OK;
1676 }
1677
1678 static HRESULT WINAPI TestFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
1679 {
1680 HRESULT hr = S_OK;
1681 TestFilterImpl *This = impl_from_IBaseFilter(iface);
1682
1683 EnterCriticalSection(&This->csFilter);
1684 {
1685 if (pName)
1686 lstrcpyW(This->filterInfo.achName, pName);
1687 else
1688 *This->filterInfo.achName = '\0';
1689 This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
1690 }
1691 LeaveCriticalSection(&This->csFilter);
1692
1693 return hr;
1694 }
1695
1696 static HRESULT WINAPI TestFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
1697 {
1698 return E_NOTIMPL;
1699 }
1700
1701 static const IBaseFilterVtbl TestFilter_Vtbl =
1702 {
1703 TestFilter_QueryInterface,
1704 TestFilter_AddRef,
1705 TestFilter_Release,
1706 TestFilter_GetClassID,
1707 TestFilter_Stop,
1708 TestFilter_Pause,
1709 TestFilter_Run,
1710 TestFilter_GetState,
1711 TestFilter_SetSyncSource,
1712 TestFilter_GetSyncSource,
1713 TestFilter_EnumPins,
1714 TestFilter_FindPin,
1715 TestFilter_QueryFilterInfo,
1716 TestFilter_JoinFilterGraph,
1717 TestFilter_QueryVendorInfo
1718 };
1719
1720 /* IClassFactory implementation */
1721
1722 typedef struct TestClassFactoryImpl
1723 {
1724 IClassFactory IClassFactory_iface;
1725 const TestFilterPinData *filterPinData;
1726 const CLSID *clsid;
1727 } TestClassFactoryImpl;
1728
1729 static inline TestClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
1730 {
1731 return CONTAINING_RECORD(iface, TestClassFactoryImpl, IClassFactory_iface);
1732 }
1733
1734 static HRESULT WINAPI Test_IClassFactory_QueryInterface(
1735 LPCLASSFACTORY iface,
1736 REFIID riid,
1737 LPVOID *ppvObj)
1738 {
1739 if (ppvObj == NULL) return E_POINTER;
1740
1741 if (IsEqualGUID(riid, &IID_IUnknown) ||
1742 IsEqualGUID(riid, &IID_IClassFactory))
1743 {
1744 *ppvObj = iface;
1745 IClassFactory_AddRef(iface);
1746 return S_OK;
1747 }
1748
1749 *ppvObj = NULL;
1750 return E_NOINTERFACE;
1751 }
1752
1753 static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface)
1754 {
1755 return 2; /* non-heap-based object */
1756 }
1757
1758 static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface)
1759 {
1760 return 1; /* non-heap-based object */
1761 }
1762
1763 static HRESULT WINAPI Test_IClassFactory_CreateInstance(
1764 LPCLASSFACTORY iface,
1765 LPUNKNOWN pUnkOuter,
1766 REFIID riid,
1767 LPVOID *ppvObj)
1768 {
1769 TestClassFactoryImpl *This = impl_from_IClassFactory(iface);
1770 HRESULT hr;
1771 TestFilterImpl *testfilter;
1772
1773 *ppvObj = NULL;
1774
1775 if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1776
1777 hr = createtestfilter(This->clsid, This->filterPinData, &testfilter);
1778 if (SUCCEEDED(hr)) {
1779 hr = IBaseFilter_QueryInterface(&testfilter->IBaseFilter_iface, riid, ppvObj);
1780 IBaseFilter_Release(&testfilter->IBaseFilter_iface);
1781 }
1782 return hr;
1783 }
1784
1785 static HRESULT WINAPI Test_IClassFactory_LockServer(
1786 LPCLASSFACTORY iface,
1787 BOOL fLock)
1788 {
1789 return S_OK;
1790 }
1791
1792 static IClassFactoryVtbl TestClassFactory_Vtbl =
1793 {
1794 Test_IClassFactory_QueryInterface,
1795 Test_IClassFactory_AddRef,
1796 Test_IClassFactory_Release,
1797 Test_IClassFactory_CreateInstance,
1798 Test_IClassFactory_LockServer
1799 };
1800
1801 static HRESULT get_connected_filter_name(TestFilterImpl *pFilter, char *FilterName)
1802 {
1803 IPin *pin = NULL;
1804 PIN_INFO pinInfo;
1805 FILTER_INFO filterInfo;
1806 HRESULT hr;
1807
1808 FilterName[0] = 0;
1809
1810 hr = IPin_ConnectedTo(pFilter->ppPins[0], &pin);
1811 ok(hr == S_OK, "IPin_ConnectedTo failed with %x\n", hr);
1812 if (FAILED(hr)) return hr;
1813
1814 hr = IPin_QueryPinInfo(pin, &pinInfo);
1815 ok(hr == S_OK, "IPin_QueryPinInfo failed with %x\n", hr);
1816 IPin_Release(pin);
1817 if (FAILED(hr)) return hr;
1818
1819 SetLastError(0xdeadbeef);
1820 hr = IBaseFilter_QueryFilterInfo(pinInfo.pFilter, &filterInfo);
1821 if (hr == S_OK && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1822 {
1823 IBaseFilter_Release(pinInfo.pFilter);
1824 return E_NOTIMPL;
1825 }
1826 ok(hr == S_OK, "IBaseFilter_QueryFilterInfo failed with %x\n", hr);
1827 IBaseFilter_Release(pinInfo.pFilter);
1828 if (FAILED(hr)) return hr;
1829
1830 IFilterGraph_Release(filterInfo.pGraph);
1831
1832 WideCharToMultiByte(CP_ACP, 0, filterInfo.achName, -1, FilterName, MAX_FILTER_NAME, NULL, NULL);
1833
1834 return S_OK;
1835 }
1836
1837 static void test_render_filter_priority(void)
1838 {
1839 /* Tests filter choice priorities in Render(). */
1840 DWORD cookie1 = 0, cookie2 = 0, cookie3 = 0;
1841 HRESULT hr;
1842 IFilterGraph2* pgraph2 = NULL;
1843 IFilterMapper2 *pMapper2 = NULL;
1844 TestFilterImpl *ptestfilter = NULL;
1845 TestFilterImpl *ptestfilter2 = NULL;
1846 static const CLSID CLSID_TestFilter2 = {
1847 0x37a4edb0,
1848 0x4d13,
1849 0x11dd,
1850 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1851 };
1852 static const CLSID CLSID_TestFilter3 = {
1853 0x37a4f2d8,
1854 0x4d13,
1855 0x11dd,
1856 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1857 };
1858 static const CLSID CLSID_TestFilter4 = {
1859 0x37a4f3b4,
1860 0x4d13,
1861 0x11dd,
1862 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1863 };
1864 static const GUID mediasubtype1 = {
1865 0x37a4f51c,
1866 0x4d13,
1867 0x11dd,
1868 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1869 };
1870 static const GUID mediasubtype2 = {
1871 0x37a4f5c6,
1872 0x4d13,
1873 0x11dd,
1874 {0xe8, 0x9b, 0x00, 0x19, 0x66, 0x2f, 0xf0, 0xce}
1875 };
1876 static const TestFilterPinData PinData1[] = {
1877 { PINDIR_OUTPUT, &mediasubtype1 },
1878 { 0, 0 }
1879 };
1880 static const TestFilterPinData PinData2[] = {
1881 { PINDIR_INPUT, &mediasubtype1 },
1882 { 0, 0 }
1883 };
1884 static const TestFilterPinData PinData3[] = {
1885 { PINDIR_INPUT, &GUID_NULL },
1886 { 0, 0 }
1887 };
1888 static const TestFilterPinData PinData4[] = {
1889 { PINDIR_INPUT, &mediasubtype1 },
1890 { PINDIR_OUTPUT, &mediasubtype2 },
1891 { 0, 0 }
1892 };
1893 static const TestFilterPinData PinData5[] = {
1894 { PINDIR_INPUT, &mediasubtype2 },
1895 { 0, 0 }
1896 };
1897 TestClassFactoryImpl Filter1ClassFactory = {
1898 { &TestClassFactory_Vtbl },
1899 PinData2, &CLSID_TestFilter2
1900 };
1901 TestClassFactoryImpl Filter2ClassFactory = {
1902 { &TestClassFactory_Vtbl },
1903 PinData4, &CLSID_TestFilter3
1904 };
1905 TestClassFactoryImpl Filter3ClassFactory = {
1906 { &TestClassFactory_Vtbl },
1907 PinData5, &CLSID_TestFilter4
1908 };
1909 char ConnectedFilterName1[MAX_FILTER_NAME];
1910 char ConnectedFilterName2[MAX_FILTER_NAME];
1911 REGFILTER2 rgf2;
1912 REGFILTERPINS2 rgPins2[2];
1913 REGPINTYPES rgPinType[2];
1914 static const WCHAR wszFilterInstanceName1[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1915 'n', 's', 't', 'a', 'n', 'c', 'e', '1', 0 };
1916 static const WCHAR wszFilterInstanceName2[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1917 'n', 's', 't', 'a', 'n', 'c', 'e', '2', 0 };
1918 static const WCHAR wszFilterInstanceName3[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1919 'n', 's', 't', 'a', 'n', 'c', 'e', '3', 0 };
1920 static const WCHAR wszFilterInstanceName4[] = {'T', 'e', 's', 't', 'f', 'i', 'l', 't', 'e', 'r', 'I',
1921 'n', 's', 't', 'a', 'n', 'c', 'e', '4', 0 };
1922
1923 /* Test which renderer of two already added to the graph will be chosen
1924 * (one is "exact" match, other is "wildcard" match. Seems to depend
1925 * on the order in which filters are added to the graph, thus indicating
1926 * no preference given to exact match. */
1927 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1928 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1929 if (!pgraph2) return;
1930
1931 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1932 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1933 if (FAILED(hr)) goto out;
1934
1935 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1936 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1937
1938 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1939 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1940 if (FAILED(hr)) goto out;
1941
1942 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1943 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1944
1945 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1946 ptestfilter2 = NULL;
1947
1948 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1949 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1950 if (FAILED(hr)) goto out;
1951
1952 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1953 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1954
1955 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
1956 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
1957
1958 hr = get_connected_filter_name(ptestfilter, ConnectedFilterName1);
1959
1960 IFilterGraph2_Release(pgraph2);
1961 pgraph2 = NULL;
1962 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
1963 ptestfilter = NULL;
1964 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1965 ptestfilter2 = NULL;
1966
1967 if (hr == E_NOTIMPL)
1968 {
1969 win_skip("Needed functions are not implemented\n");
1970 return;
1971 }
1972
1973 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
1974 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1975 if (!pgraph2) goto out;
1976
1977 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
1978 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1979 if (FAILED(hr)) goto out;
1980
1981 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
1982 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1983
1984 hr = createtestfilter(&GUID_NULL, PinData3, &ptestfilter2);
1985 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1986 if (FAILED(hr)) goto out;
1987
1988 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
1989 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
1990
1991 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
1992 ptestfilter2 = NULL;
1993
1994 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
1995 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
1996 if (FAILED(hr)) goto out;
1997
1998 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
1999 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2000
2001 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
2002 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
2003
2004 hr = IFilterGraph2_Disconnect(pgraph2, NULL);
2005 ok(hr == E_POINTER, "IFilterGraph2_Disconnect failed. Expected E_POINTER, received %08x\n", hr);
2006
2007 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
2008 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
2009 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
2010
2011 IFilterGraph2_Release(pgraph2);
2012 pgraph2 = NULL;
2013 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
2014 ptestfilter = NULL;
2015 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2016 ptestfilter2 = NULL;
2017
2018 /* Test if any preference is given to existing renderer which renders the pin directly vs
2019 an existing renderer which renders the pin indirectly, through an additional middle filter,
2020 again trying different orders of creation. Native appears not to give a preference. */
2021
2022 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
2023 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
2024 if (!pgraph2) goto out;
2025
2026 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
2027 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2028 if (FAILED(hr)) goto out;
2029
2030 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
2031 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2032
2033 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
2034 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2035 if (FAILED(hr)) goto out;
2036
2037 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
2038 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2039
2040 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2041 ptestfilter2 = NULL;
2042
2043 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
2044 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2045 if (FAILED(hr)) goto out;
2046
2047 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
2048 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2049
2050 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2051 ptestfilter2 = NULL;
2052
2053 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
2054 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2055 if (FAILED(hr)) goto out;
2056
2057 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
2058 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2059
2060 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
2061 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
2062
2063 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
2064 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3") || !strcmp(ConnectedFilterName1, "TestfilterInstance2"),
2065 "unexpected connected filter: %s\n", ConnectedFilterName1);
2066
2067 IFilterGraph2_Release(pgraph2);
2068 pgraph2 = NULL;
2069 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
2070 ptestfilter = NULL;
2071 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2072 ptestfilter2 = NULL;
2073
2074 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
2075 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
2076 if (!pgraph2) goto out;
2077
2078 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
2079 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2080 if (FAILED(hr)) goto out;
2081
2082 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
2083 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2084
2085 hr = createtestfilter(&GUID_NULL, PinData4, &ptestfilter2);
2086 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2087 if (FAILED(hr)) goto out;
2088
2089 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName3);
2090 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2091
2092 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2093 ptestfilter2 = NULL;
2094
2095 hr = createtestfilter(&GUID_NULL, PinData5, &ptestfilter2);
2096 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2097 if (FAILED(hr)) goto out;
2098
2099 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName4);
2100 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2101
2102 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2103 ptestfilter2 = NULL;
2104
2105 hr = createtestfilter(&GUID_NULL, PinData2, &ptestfilter2);
2106 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2107 if (FAILED(hr)) goto out;
2108
2109 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter2->IBaseFilter_iface, wszFilterInstanceName2);
2110 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2111
2112 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
2113 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
2114
2115 get_connected_filter_name(ptestfilter, ConnectedFilterName2);
2116 ok(!strcmp(ConnectedFilterName2, "TestfilterInstance3") || !strcmp(ConnectedFilterName2, "TestfilterInstance2"),
2117 "unexpected connected filter: %s\n", ConnectedFilterName2);
2118 ok(strcmp(ConnectedFilterName1, ConnectedFilterName2),
2119 "expected connected filters to be different but got %s both times\n", ConnectedFilterName1);
2120
2121 IFilterGraph2_Release(pgraph2);
2122 pgraph2 = NULL;
2123 IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
2124 ptestfilter = NULL;
2125 IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2126 ptestfilter2 = NULL;
2127
2128 /* Test if renderers are tried before non-renderers (intermediary filters). */
2129 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (LPVOID*)&pgraph2);
2130 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
2131 if (!pgraph2) goto out;
2132
2133 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterMapper2, (LPVOID*)&pMapper2);
2134 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
2135 if (!pMapper2) goto out;
2136
2137 hr = createtestfilter(&GUID_NULL, PinData1, &ptestfilter);
2138 ok(hr == S_OK, "createtestfilter failed with %08x\n", hr);
2139 if (FAILED(hr)) goto out;
2140
2141 hr = IFilterGraph2_AddFilter(pgraph2, &ptestfilter->IBaseFilter_iface, wszFilterInstanceName1);
2142 ok(hr == S_OK, "IFilterGraph2_AddFilter failed with %08x\n", hr);
2143
2144 /* Register our filters with COM and with Filtermapper. */
2145 hr = CoRegisterClassObject(Filter1ClassFactory.clsid,
2146 (IUnknown *)&Filter1ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
2147 REGCLS_MULTIPLEUSE, &cookie1);
2148 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
2149 if (FAILED(hr)) goto out;
2150 hr = CoRegisterClassObject(Filter2ClassFactory.clsid,
2151 (IUnknown *)&Filter2ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
2152 REGCLS_MULTIPLEUSE, &cookie2);
2153 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
2154 if (FAILED(hr)) goto out;
2155 hr = CoRegisterClassObject(Filter3ClassFactory.clsid,
2156 (IUnknown *)&Filter3ClassFactory.IClassFactory_iface, CLSCTX_INPROC_SERVER,
2157 REGCLS_MULTIPLEUSE, &cookie3);
2158 ok(hr == S_OK, "CoRegisterClassObject failed with %08x\n", hr);
2159 if (FAILED(hr)) goto out;
2160
2161 rgf2.dwVersion = 2;
2162 rgf2.dwMerit = MERIT_UNLIKELY;
2163 S2(U(rgf2)).cPins2 = 1;
2164 S2(U(rgf2)).rgPins2 = rgPins2;
2165 rgPins2[0].dwFlags = REG_PINFLAG_B_RENDERER;
2166 rgPins2[0].cInstances = 1;
2167 rgPins2[0].nMediaTypes = 1;
2168 rgPins2[0].lpMediaType = &rgPinType[0];
2169 rgPins2[0].nMediums = 0;
2170 rgPins2[0].lpMedium = NULL;
2171 rgPins2[0].clsPinCategory = NULL;
2172 rgPinType[0].clsMajorType = &MEDIATYPE_Video;
2173 rgPinType[0].clsMinorType = &mediasubtype1;
2174
2175 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter2, wszFilterInstanceName2, NULL,
2176 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
2177 if (hr == E_ACCESSDENIED)
2178 skip("Not authorized to register filters\n");
2179 else
2180 {
2181 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
2182
2183 rgf2.dwMerit = MERIT_PREFERRED;
2184 rgPinType[0].clsMinorType = &mediasubtype2;
2185
2186 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter4, wszFilterInstanceName4, NULL,
2187 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
2188 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
2189
2190 S2(U(rgf2)).cPins2 = 2;
2191 rgPins2[0].dwFlags = 0;
2192 rgPinType[0].clsMinorType = &mediasubtype1;
2193
2194 rgPins2[1].dwFlags = REG_PINFLAG_B_OUTPUT;
2195 rgPins2[1].cInstances = 1;
2196 rgPins2[1].nMediaTypes = 1;
2197 rgPins2[1].lpMediaType = &rgPinType[1];
2198 rgPins2[1].nMediums = 0;
2199 rgPins2[1].lpMedium = NULL;
2200 rgPins2[1].clsPinCategory = NULL;
2201 rgPinType[1].clsMajorType = &MEDIATYPE_Video;
2202 rgPinType[1].clsMinorType = &mediasubtype2;
2203
2204 hr = IFilterMapper2_RegisterFilter(pMapper2, &CLSID_TestFilter3, wszFilterInstanceName3, NULL,
2205 &CLSID_LegacyAmFilterCategory, NULL, &rgf2);
2206 ok(hr == S_OK, "IFilterMapper2_RegisterFilter failed with %x\n", hr);
2207
2208 hr = IFilterGraph2_Render(pgraph2, ptestfilter->ppPins[0]);
2209 ok(hr == S_OK, "IFilterGraph2_Render failed with %08x\n", hr);
2210
2211 get_connected_filter_name(ptestfilter, ConnectedFilterName1);
2212 ok(!strcmp(ConnectedFilterName1, "TestfilterInstance3"),
2213 "unexpected connected filter: %s\n", ConnectedFilterName1);
2214
2215 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
2216 &CLSID_TestFilter2);
2217 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
2218 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
2219 &CLSID_TestFilter3);
2220 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
2221 hr = IFilterMapper2_UnregisterFilter(pMapper2, &CLSID_LegacyAmFilterCategory, NULL,
2222 &CLSID_TestFilter4);
2223 ok(hr == S_OK, "IFilterMapper2_UnregisterFilter failed with %x\n", hr);
2224 }
2225
2226 out:
2227
2228 if (ptestfilter) IBaseFilter_Release(&ptestfilter->IBaseFilter_iface);
2229 if (ptestfilter2) IBaseFilter_Release(&ptestfilter2->IBaseFilter_iface);
2230 if (pgraph2) IFilterGraph2_Release(pgraph2);
2231 if (pMapper2) IFilterMapper2_Release(pMapper2);
2232
2233 hr = CoRevokeClassObject(cookie1);
2234 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
2235 hr = CoRevokeClassObject(cookie2);
2236 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
2237 hr = CoRevokeClassObject(cookie3);
2238 ok(hr == S_OK, "CoRevokeClassObject failed with %08x\n", hr);
2239 }
2240
2241 typedef struct IUnknownImpl
2242 {
2243 IUnknown IUnknown_iface;
2244 int AddRef_called;
2245 int Release_called;
2246 } IUnknownImpl;
2247
2248 static IUnknownImpl *IUnknownImpl_from_iface(IUnknown * iface)
2249 {
2250 return CONTAINING_RECORD(iface, IUnknownImpl, IUnknown_iface);
2251 }
2252
2253 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
2254 {
2255 ok(0, "QueryInterface should not be called for %s\n", wine_dbgstr_guid(riid));
2256 return E_NOINTERFACE;
2257 }
2258
2259 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown * iface)
2260 {
2261 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
2262 This->AddRef_called++;
2263 return 2;
2264 }
2265
2266 static ULONG WINAPI IUnknownImpl_Release(IUnknown * iface)
2267 {
2268 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
2269 This->Release_called++;
2270 return 1;
2271 }
2272
2273 static CONST_VTBL IUnknownVtbl IUnknownImpl_Vtbl =
2274 {
2275 IUnknownImpl_QueryInterface,
2276 IUnknownImpl_AddRef,
2277 IUnknownImpl_Release
2278 };
2279
2280 static void test_aggregate_filter_graph(void)
2281 {
2282 HRESULT hr;
2283 IUnknown *pgraph;
2284 IUnknown *punk;
2285 IUnknownImpl unk_outer = { { &IUnknownImpl_Vtbl }, 0, 0 };
2286
2287 hr = CoCreateInstance(&CLSID_FilterGraph, &unk_outer.IUnknown_iface, CLSCTX_INPROC_SERVER,
2288 &IID_IUnknown, (void **)&pgraph);
2289 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
2290 ok(pgraph != &unk_outer.IUnknown_iface, "pgraph = %p, expected not %p\n", pgraph, &unk_outer.IUnknown_iface);
2291
2292 hr = IUnknown_QueryInterface(pgraph, &IID_IUnknown, (void **)&punk);
2293 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
2294 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
2295 IUnknown_Release(punk);
2296
2297 ok(unk_outer.AddRef_called == 0, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
2298 ok(unk_outer.Release_called == 0, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
2299 unk_outer.AddRef_called = 0;
2300 unk_outer.Release_called = 0;
2301
2302 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper, (void **)&punk);
2303 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
2304 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
2305 IUnknown_Release(punk);
2306
2307 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
2308 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
2309 unk_outer.AddRef_called = 0;
2310 unk_outer.Release_called = 0;
2311
2312 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper2, (void **)&punk);
2313 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
2314 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
2315 IUnknown_Release(punk);
2316
2317 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
2318 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
2319 unk_outer.AddRef_called = 0;
2320 unk_outer.Release_called = 0;
2321
2322 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper3, (void **)&punk);
2323 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
2324 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
2325 IUnknown_Release(punk);
2326
2327 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
2328 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
2329
2330 IUnknown_Release(pgraph);
2331 }
2332
2333 START_TEST(filtergraph)
2334 {
2335 HRESULT hr;
2336 CoInitializeEx(NULL, COINIT_MULTITHREADED);
2337 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
2338 &IID_IGraphBuilder, (LPVOID*)&pgraph);
2339 if (FAILED(hr)) {
2340 skip("Creating filtergraph returned %08x, skipping tests\n", hr);
2341 return;
2342 }
2343 IGraphBuilder_Release(pgraph);
2344 test_render_run(avifile);
2345 test_render_run(mpegfile);
2346 test_graph_builder();
2347 test_graph_builder_addfilter();
2348 test_mediacontrol();
2349 test_filter_graph2();
2350 test_render_filter_priority();
2351 test_aggregate_filter_graph();
2352 CoUninitialize();
2353 test_render_with_multithread();
2354 }