quartz/tests: Add some tests for IGraphBuilder_Connect().
[wine.git] / dlls / quartz / tests / filtergraph.c
blobc9083f9c10ecf2dcb3242a6fded7dfc0b3086524
1 /*
2 * Unit tests for Direct Show functions
4 * Copyright (C) 2004 Christian Costa
5 * Copyright (C) 2008 Alexander Dorofeyev
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.
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.
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
22 #define COBJMACROS
23 #define CONST_VTABLE
25 #include "dshow.h"
26 #include "wine/heap.h"
27 #include "wine/test.h"
29 typedef struct TestFilterImpl
31 IBaseFilter IBaseFilter_iface;
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;
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};
45 static WCHAR *load_resource(const WCHAR *name)
47 static WCHAR pathW[MAX_PATH];
48 DWORD written;
49 HANDLE file;
50 HRSRC res;
51 void *ptr;
53 GetTempPathW(ARRAY_SIZE(pathW), pathW);
54 lstrcatW(pathW, name);
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());
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 );
67 return pathW;
70 static IFilterGraph2 *create_graph(void)
72 IFilterGraph2 *ret;
73 HRESULT hr;
74 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (void **)&ret);
75 ok(hr == S_OK, "Failed to create FilterGraph: %#x\n", hr);
76 return ret;
79 #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c)
80 static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported)
82 IUnknown *iface = iface_ptr;
83 HRESULT hr, expected_hr;
84 IUnknown *unk;
86 expected_hr = supported ? S_OK : E_NOINTERFACE;
88 hr = IUnknown_QueryInterface(iface, iid, (void **)&unk);
89 ok_(__FILE__, line)(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
90 if (SUCCEEDED(hr))
91 IUnknown_Release(unk);
94 static void test_interfaces(void)
96 IFilterGraph2 *graph = create_graph();
98 check_interface(graph, &IID_IBasicAudio, TRUE);
99 check_interface(graph, &IID_IBasicVideo2, TRUE);
100 check_interface(graph, &IID_IFilterGraph2, TRUE);
101 check_interface(graph, &IID_IFilterMapper, TRUE);
102 check_interface(graph, &IID_IFilterMapper3, TRUE);
103 check_interface(graph, &IID_IGraphConfig, TRUE);
104 check_interface(graph, &IID_IGraphVersion, TRUE);
105 check_interface(graph, &IID_IMediaControl, TRUE);
106 check_interface(graph, &IID_IMediaEvent, TRUE);
107 check_interface(graph, &IID_IMediaFilter, TRUE);
108 check_interface(graph, &IID_IMediaEventSink, TRUE);
109 check_interface(graph, &IID_IMediaPosition, TRUE);
110 check_interface(graph, &IID_IMediaSeeking, TRUE);
111 check_interface(graph, &IID_IObjectWithSite, TRUE);
112 check_interface(graph, &IID_IVideoWindow, TRUE);
114 check_interface(graph, &IID_IBaseFilter, FALSE);
116 IFilterGraph2_Release(graph);
119 static void test_basic_video(IFilterGraph2 *graph)
121 IBasicVideo* pbv;
122 LONG video_width, video_height, window_width;
123 LONG left, top, width, height;
124 HRESULT hr;
126 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo, (void **)&pbv);
127 ok(hr==S_OK, "Cannot get IBasicVideo interface returned: %x\n", hr);
129 /* test get video size */
130 hr = IBasicVideo_GetVideoSize(pbv, NULL, NULL);
131 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
132 hr = IBasicVideo_GetVideoSize(pbv, &video_width, NULL);
133 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
134 hr = IBasicVideo_GetVideoSize(pbv, NULL, &video_height);
135 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
136 hr = IBasicVideo_GetVideoSize(pbv, &video_width, &video_height);
137 ok(hr==S_OK, "Cannot get video size returned: %x\n", hr);
139 /* test source position */
140 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, NULL, NULL);
141 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
142 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, NULL, NULL);
143 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
144 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, &width, &height);
145 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
146 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
147 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
148 ok(left == 0, "expected 0, got %d\n", left);
149 ok(top == 0, "expected 0, got %d\n", top);
150 ok(width == video_width, "expected %d, got %d\n", video_width, width);
151 ok(height == video_height, "expected %d, got %d\n", video_height, height);
153 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, 0, 0);
154 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
155 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width*2, video_height*2);
156 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
157 hr = IBasicVideo_put_SourceTop(pbv, -1);
158 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
159 hr = IBasicVideo_put_SourceTop(pbv, 0);
160 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
161 hr = IBasicVideo_put_SourceTop(pbv, 1);
162 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
164 hr = IBasicVideo_SetSourcePosition(pbv, video_width, 0, video_width, video_height);
165 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
166 hr = IBasicVideo_SetSourcePosition(pbv, 0, video_height, video_width, video_height);
167 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
168 hr = IBasicVideo_SetSourcePosition(pbv, -1, 0, video_width, video_height);
169 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
170 hr = IBasicVideo_SetSourcePosition(pbv, 0, -1, video_width, video_height);
171 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
172 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
173 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
174 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
175 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
177 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width, video_height+1);
178 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
179 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width+1, video_height);
180 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
182 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
183 ok(hr==S_OK, "Cannot set source position returned: %x\n", hr);
185 hr = IBasicVideo_get_SourceLeft(pbv, &left);
186 ok(hr==S_OK, "Cannot get source left returned: %x\n", hr);
187 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
188 hr = IBasicVideo_get_SourceTop(pbv, &top);
189 ok(hr==S_OK, "Cannot get source top returned: %x\n", hr);
190 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
191 hr = IBasicVideo_get_SourceWidth(pbv, &width);
192 ok(hr==S_OK, "Cannot get source width returned: %x\n", hr);
193 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
194 hr = IBasicVideo_get_SourceHeight(pbv, &height);
195 ok(hr==S_OK, "Cannot get source height returned: %x\n", hr);
196 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
198 hr = IBasicVideo_put_SourceLeft(pbv, video_width/3);
199 ok(hr==S_OK, "Cannot put source left returned: %x\n", hr);
200 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
201 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
202 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
203 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
205 hr = IBasicVideo_put_SourceTop(pbv, video_height/3);
206 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
207 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
208 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
209 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
210 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
212 hr = IBasicVideo_put_SourceWidth(pbv, video_width/4+1);
213 ok(hr==S_OK, "Cannot put source width returned: %x\n", hr);
214 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
215 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
216 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
217 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
219 hr = IBasicVideo_put_SourceHeight(pbv, video_height/4+1);
220 ok(hr==S_OK, "Cannot put source height returned: %x\n", hr);
221 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
222 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
223 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
224 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
226 /* test destination rectangle */
227 window_width = max(video_width, GetSystemMetrics(SM_CXMIN) - 2 * GetSystemMetrics(SM_CXFRAME));
229 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, NULL, NULL);
230 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
231 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, NULL, NULL);
232 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
233 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, &width, &height);
234 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
235 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
236 ok(hr == S_OK, "Cannot get destination position returned: %x\n", hr);
237 ok(left == 0, "expected 0, got %d\n", left);
238 ok(top == 0, "expected 0, got %d\n", top);
239 todo_wine ok(width == window_width, "expected %d, got %d\n", window_width, width);
240 todo_wine ok(height == video_height, "expected %d, got %d\n", video_height, height);
242 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, 0, 0);
243 ok(hr==E_INVALIDARG, "IBasicVideo_SetDestinationPosition returned: %x\n", hr);
244 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width*2, video_height*2);
245 ok(hr==S_OK, "Cannot put destination position returned: %x\n", hr);
247 hr = IBasicVideo_put_DestinationLeft(pbv, -1);
248 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
249 hr = IBasicVideo_put_DestinationLeft(pbv, 0);
250 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
251 hr = IBasicVideo_put_DestinationLeft(pbv, 1);
252 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
254 hr = IBasicVideo_SetDestinationPosition(pbv, video_width, 0, video_width, video_height);
255 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
256 hr = IBasicVideo_SetDestinationPosition(pbv, 0, video_height, video_width, video_height);
257 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
258 hr = IBasicVideo_SetDestinationPosition(pbv, -1, 0, video_width, video_height);
259 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
260 hr = IBasicVideo_SetDestinationPosition(pbv, 0, -1, video_width, video_height);
261 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
262 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
263 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
264 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
265 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
267 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height+1);
268 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
269 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width+1, video_height);
270 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
272 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
273 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
275 hr = IBasicVideo_get_DestinationLeft(pbv, &left);
276 ok(hr==S_OK, "Cannot get destination left returned: %x\n", hr);
277 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
278 hr = IBasicVideo_get_DestinationTop(pbv, &top);
279 ok(hr==S_OK, "Cannot get destination top returned: %x\n", hr);
280 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
281 hr = IBasicVideo_get_DestinationWidth(pbv, &width);
282 ok(hr==S_OK, "Cannot get destination width returned: %x\n", hr);
283 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
284 hr = IBasicVideo_get_DestinationHeight(pbv, &height);
285 ok(hr==S_OK, "Cannot get destination height returned: %x\n", hr);
286 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
288 hr = IBasicVideo_put_DestinationLeft(pbv, video_width/3);
289 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
290 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
291 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
292 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
293 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
295 hr = IBasicVideo_put_DestinationTop(pbv, video_height/3);
296 ok(hr==S_OK, "Cannot put destination top returned: %x\n", hr);
297 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
298 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
299 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
300 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
302 hr = IBasicVideo_put_DestinationWidth(pbv, video_width/4+1);
303 ok(hr==S_OK, "Cannot put destination width returned: %x\n", hr);
304 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
305 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
306 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
307 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
309 hr = IBasicVideo_put_DestinationHeight(pbv, video_height/4+1);
310 ok(hr==S_OK, "Cannot put destination height returned: %x\n", hr);
311 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
312 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
313 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
314 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
316 /* reset source rectangle */
317 hr = IBasicVideo_SetDefaultSourcePosition(pbv);
318 ok(hr==S_OK, "IBasicVideo_SetDefaultSourcePosition returned: %x\n", hr);
320 /* reset destination position */
321 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height);
322 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
324 IBasicVideo_Release(pbv);
327 static void test_media_seeking(IFilterGraph2 *graph)
329 IMediaSeeking *seeking;
330 IMediaFilter *filter;
331 LONGLONG pos, stop, duration;
332 GUID format;
333 HRESULT hr;
335 IFilterGraph2_SetDefaultSyncSource(graph);
336 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
337 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %08x\n", hr);
339 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
340 ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %08x\n", hr);
342 format = GUID_NULL;
343 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
344 ok(hr == S_OK, "GetTimeFormat failed: %#x\n", hr);
345 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "got %s\n", wine_dbgstr_guid(&format));
347 pos = 0xdeadbeef;
348 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, NULL);
349 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
350 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
352 pos = 0xdeadbeef;
353 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
354 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
355 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
357 pos = 0xdeadbeef;
358 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
359 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
360 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
362 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
363 ok(hr == S_OK, "GetCurrentPosition failed: %#x\n", hr);
364 ok(pos == 0, "got %s\n", wine_dbgstr_longlong(pos));
366 hr = IMediaSeeking_GetDuration(seeking, &duration);
367 ok(hr == S_OK, "GetDuration failed: %#x\n", hr);
368 ok(duration > 0, "got %s\n", wine_dbgstr_longlong(duration));
370 hr = IMediaSeeking_GetStopPosition(seeking, &stop);
371 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
372 ok(stop == duration || stop == duration + 1, "expected %s, got %s\n",
373 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
375 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
376 ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
377 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_NoPositioning, NULL, AM_SEEKING_ReturnTime);
378 ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
380 pos = 0;
381 hr = IMediaSeeking_SetPositions(seeking, &pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
382 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
384 IMediaFilter_SetSyncSource(filter, NULL);
385 pos = 0xdeadbeef;
386 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
387 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
388 ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
389 IFilterGraph2_SetDefaultSyncSource(graph);
391 IMediaSeeking_Release(seeking);
392 IMediaFilter_Release(filter);
395 static void test_state_change(IFilterGraph2 *graph)
397 IMediaControl *control;
398 OAFilterState state;
399 HRESULT hr;
401 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
402 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %x\n", hr);
404 hr = IMediaControl_GetState(control, 1000, &state);
405 ok(hr == S_OK, "GetState() failed: %x\n", hr);
406 ok(state == State_Stopped, "wrong state %d\n", state);
408 hr = IMediaControl_Run(control);
409 ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
410 hr = IMediaControl_GetState(control, INFINITE, &state);
411 ok(SUCCEEDED(hr), "GetState() failed: %x\n", hr);
412 ok(state == State_Running, "wrong state %d\n", state);
414 hr = IMediaControl_Stop(control);
415 ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
416 hr = IMediaControl_GetState(control, 1000, &state);
417 ok(hr == S_OK, "GetState() failed: %x\n", hr);
418 ok(state == State_Stopped, "wrong state %d\n", state);
420 hr = IMediaControl_Pause(control);
421 ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
422 hr = IMediaControl_GetState(control, 1000, &state);
423 ok(hr == S_OK, "GetState() failed: %x\n", hr);
424 ok(state == State_Paused, "wrong state %d\n", state);
426 hr = IMediaControl_Run(control);
427 ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
428 hr = IMediaControl_GetState(control, 1000, &state);
429 ok(hr == S_OK, "GetState() failed: %x\n", hr);
430 ok(state == State_Running, "wrong state %d\n", state);
432 hr = IMediaControl_Pause(control);
433 ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
434 hr = IMediaControl_GetState(control, 1000, &state);
435 ok(hr == S_OK, "GetState() failed: %x\n", hr);
436 ok(state == State_Paused, "wrong state %d\n", state);
438 hr = IMediaControl_Stop(control);
439 ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
440 hr = IMediaControl_GetState(control, 1000, &state);
441 ok(hr == S_OK, "GetState() failed: %x\n", hr);
442 ok(state == State_Stopped, "wrong state %d\n", state);
444 IMediaControl_Release(control);
447 static void test_media_event(IFilterGraph2 *graph)
449 IMediaEvent *media_event;
450 IMediaSeeking *seeking;
451 IMediaControl *control;
452 IMediaFilter *filter;
453 LONG_PTR lparam1, lparam2;
454 LONGLONG current, stop;
455 OAFilterState state;
456 int got_eos = 0;
457 HANDLE event;
458 HRESULT hr;
459 LONG code;
461 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
462 ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %#x\n", hr);
464 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
465 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %#x\n", hr);
467 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&media_event);
468 ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
470 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
471 ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
473 hr = IMediaControl_Stop(control);
474 ok(SUCCEEDED(hr), "Stop() failed: %#x\n", hr);
475 hr = IMediaControl_GetState(control, 1000, &state);
476 ok(hr == S_OK, "GetState() timed out\n");
478 hr = IMediaSeeking_GetDuration(seeking, &stop);
479 ok(hr == S_OK, "GetDuration() failed: %#x\n", hr);
480 current = 0;
481 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning, &stop, AM_SEEKING_AbsolutePositioning);
482 ok(hr == S_OK, "SetPositions() failed: %#x\n", hr);
484 hr = IMediaFilter_SetSyncSource(filter, NULL);
485 ok(hr == S_OK, "SetSyncSource() failed: %#x\n", hr);
487 hr = IMediaEvent_GetEventHandle(media_event, (OAEVENT *)&event);
488 ok(hr == S_OK, "GetEventHandle() failed: %#x\n", hr);
490 /* flush existing events */
491 while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK);
493 ok(WaitForSingleObject(event, 0) == WAIT_TIMEOUT, "event should not be signaled\n");
495 hr = IMediaControl_Run(control);
496 ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
498 while (!got_eos)
500 if (WaitForSingleObject(event, 1000) == WAIT_TIMEOUT)
501 break;
503 while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK)
505 if (code == EC_COMPLETE)
507 got_eos = 1;
508 break;
512 ok(got_eos, "didn't get EOS\n");
514 hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
515 ok(hr == S_OK, "GetCurrentPosition() failed: %#x\n", hr);
516 todo_wine
517 ok(current == stop, "expected %s, got %s\n", wine_dbgstr_longlong(stop), wine_dbgstr_longlong(current));
519 hr = IMediaControl_Stop(control);
520 ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
521 hr = IMediaControl_GetState(control, 1000, &state);
522 ok(hr == S_OK, "GetState() timed out\n");
524 hr = IFilterGraph2_SetDefaultSyncSource(graph);
525 ok(hr == S_OK, "SetDefaultSinkSource() failed: %#x\n", hr);
527 IMediaSeeking_Release(seeking);
528 IMediaEvent_Release(media_event);
529 IMediaControl_Release(control);
530 IMediaFilter_Release(filter);
533 static void rungraph(IFilterGraph2 *graph)
535 test_basic_video(graph);
536 test_media_seeking(graph);
537 test_state_change(graph);
538 test_media_event(graph);
541 static HRESULT test_graph_builder_connect_file(WCHAR *filename)
543 static const WCHAR outputW[] = {'O','u','t','p','u','t',0};
544 static const WCHAR inW[] = {'I','n',0};
545 IBaseFilter *source_filter, *video_filter;
546 IPin *pin_in, *pin_out;
547 IFilterGraph2 *graph;
548 IVideoWindow *window;
549 HRESULT hr;
551 graph = create_graph();
553 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IVideoWindow, (void **)&window);
554 ok(hr == S_OK, "Failed to create VideoRenderer: %#x\n", hr);
556 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &source_filter);
557 ok(hr == S_OK, "AddSourceFilter failed: %#x\n", hr);
559 hr = IVideoWindow_QueryInterface(window, &IID_IBaseFilter, (void **)&video_filter);
560 ok(hr == S_OK, "QueryInterface(IBaseFilter) failed: %#x\n", hr);
561 hr = IFilterGraph2_AddFilter(graph, video_filter, NULL);
562 ok(hr == S_OK, "AddFilter failed: %#x\n", hr);
564 hr = IBaseFilter_FindPin(source_filter, outputW, &pin_out);
565 ok(hr == S_OK, "FindPin failed: %#x\n", hr);
566 hr = IBaseFilter_FindPin(video_filter, inW, &pin_in);
567 ok(hr == S_OK, "FindPin failed: %#x\n", hr);
568 hr = IFilterGraph2_Connect(graph, pin_out, pin_in);
570 if (SUCCEEDED(hr))
571 rungraph(graph);
573 IPin_Release(pin_in);
574 IPin_Release(pin_out);
575 IBaseFilter_Release(source_filter);
576 IBaseFilter_Release(video_filter);
577 IVideoWindow_Release(window);
578 IFilterGraph2_Release(graph);
580 return hr;
583 static void test_render_run(const WCHAR *file)
585 IFilterGraph2 *graph;
586 HANDLE h;
587 HRESULT hr;
588 LONG refs;
589 WCHAR *filename = load_resource(file);
591 h = CreateFileW(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
592 if (h == INVALID_HANDLE_VALUE) {
593 skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(file));
594 DeleteFileW(filename);
595 return;
597 CloseHandle(h);
599 trace("running %s\n", wine_dbgstr_w(file));
601 graph = create_graph();
603 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
604 if (FAILED(hr))
606 skip("%s: codec not supported; skipping test\n", wine_dbgstr_w(file));
608 refs = IFilterGraph2_Release(graph);
609 ok(!refs, "Graph has %u references\n", refs);
611 hr = test_graph_builder_connect_file(filename);
612 todo_wine
613 ok(hr == VFW_E_CANNOT_CONNECT, "got %#x\n", hr);
615 else
617 ok(hr == S_OK || hr == VFW_S_AUDIO_NOT_RENDERED, "RenderFile failed: %x\n", hr);
618 rungraph(graph);
620 refs = IFilterGraph2_Release(graph);
621 ok(!refs, "Graph has %u references\n", refs);
623 hr = test_graph_builder_connect_file(filename);
624 ok(hr == S_OK || hr == VFW_S_PARTIAL_RENDER, "got %#x\n", hr);
627 /* check reference leaks */
628 h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
629 ok(h != INVALID_HANDLE_VALUE, "CreateFile failed: err=%d\n", GetLastError());
630 CloseHandle(h);
632 DeleteFileW(filename);
635 static void test_enum_filters(void)
637 IBaseFilter *filter1, *filter2, *filters[2];
638 IFilterGraph2 *graph = create_graph();
639 IEnumFilters *enum1, *enum2;
640 ULONG count, ref;
641 HRESULT hr;
643 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
644 &IID_IBaseFilter, (void **)&filter1);
645 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
646 &IID_IBaseFilter, (void **)&filter2);
648 hr = IFilterGraph2_EnumFilters(graph, &enum1);
649 ok(hr == S_OK, "Got hr %#x.\n", hr);
651 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
652 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
654 IFilterGraph2_AddFilter(graph, filter1, NULL);
655 IFilterGraph2_AddFilter(graph, filter2, NULL);
657 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
658 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
660 hr = IEnumFilters_Reset(enum1);
661 ok(hr == S_OK, "Got hr %#x.\n", hr);
663 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
664 ok(hr == S_OK, "Got hr %#x.\n", hr);
665 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
666 IBaseFilter_Release(filters[0]);
668 hr = IEnumFilters_Next(enum1, 1, filters, &count);
669 ok(hr == S_OK, "Got hr %#x.\n", hr);
670 ok(count == 1, "Got count %u.\n", count);
671 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
672 IBaseFilter_Release(filters[0]);
674 hr = IEnumFilters_Next(enum1, 1, filters, &count);
675 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
676 ok(count == 0, "Got count %u.\n", count);
678 hr = IEnumFilters_Reset(enum1);
679 ok(hr == S_OK, "Got hr %#x.\n", hr);
681 hr = IEnumFilters_Next(enum1, 2, filters, &count);
682 ok(hr == S_OK, "Got hr %#x.\n", hr);
683 ok(count == 2, "Got count %u.\n", count);
684 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
685 ok(filters[1] == filter1, "Got filter %p.\n", filters[1]);
686 IBaseFilter_Release(filters[0]);
687 IBaseFilter_Release(filters[1]);
689 IFilterGraph2_RemoveFilter(graph, filter1);
690 IFilterGraph2_AddFilter(graph, filter1, NULL);
692 hr = IEnumFilters_Next(enum1, 2, filters, &count);
693 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
695 hr = IEnumFilters_Reset(enum1);
696 ok(hr == S_OK, "Got hr %#x.\n", hr);
698 hr = IEnumFilters_Next(enum1, 2, filters, &count);
699 ok(hr == S_OK, "Got hr %#x.\n", hr);
700 ok(count == 2, "Got count %u.\n", count);
701 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
702 ok(filters[1] == filter2, "Got filter %p.\n", filters[1]);
703 IBaseFilter_Release(filters[0]);
704 IBaseFilter_Release(filters[1]);
706 hr = IEnumFilters_Reset(enum1);
707 ok(hr == S_OK, "Got hr %#x.\n", hr);
709 hr = IEnumFilters_Clone(enum1, &enum2);
710 ok(hr == S_OK, "Got hr %#x.\n", hr);
712 hr = IEnumFilters_Skip(enum2, 1);
713 ok(hr == S_OK, "Got hr %#x.\n", hr);
715 hr = IEnumFilters_Next(enum2, 2, filters, &count);
716 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
717 ok(count == 1, "Got count %u.\n", count);
718 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
719 IBaseFilter_Release(filters[0]);
721 hr = IEnumFilters_Skip(enum1, 3);
722 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
724 IEnumFilters_Release(enum2);
725 IEnumFilters_Release(enum1);
726 ref = IFilterGraph2_Release(graph);
727 ok(!ref, "Got outstanding refcount %d.\n", ref);
730 static DWORD WINAPI call_RenderFile_multithread(LPVOID lParam)
732 WCHAR *filename = load_resource(avifile);
733 IFilterGraph2 *graph = lParam;
734 HRESULT hr;
736 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
737 todo_wine
738 ok(SUCCEEDED(hr), "RenderFile failed: %x\n", hr);
740 if (SUCCEEDED(hr))
741 rungraph(graph);
743 return 0;
746 static void test_render_with_multithread(void)
748 IFilterGraph2 *graph;
749 HANDLE thread;
751 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
753 graph = create_graph();
755 thread = CreateThread(NULL, 0, call_RenderFile_multithread, graph, 0, NULL);
757 ok(WaitForSingleObject(thread, 1000) == WAIT_OBJECT_0, "wait failed\n");
758 IFilterGraph2_Release(graph);
759 CloseHandle(thread);
760 CoUninitialize();
763 struct testpin
765 IPin IPin_iface;
766 LONG ref;
767 PIN_DIRECTION dir;
768 IBaseFilter *filter;
769 IPin *peer;
770 AM_MEDIA_TYPE *mt;
772 IEnumMediaTypes IEnumMediaTypes_iface;
773 const AM_MEDIA_TYPE *types;
774 unsigned int type_count, enum_idx;
775 AM_MEDIA_TYPE *request_mt, *accept_mt;
777 HRESULT QueryInternalConnections_hr;
780 static inline struct testpin *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
782 return CONTAINING_RECORD(iface, struct testpin, IEnumMediaTypes_iface);
785 static HRESULT WINAPI testenummt_QueryInterface(IEnumMediaTypes *iface, REFIID iid, void **out)
787 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
788 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
790 *out = NULL;
791 return E_NOINTERFACE;
794 static ULONG WINAPI testenummt_AddRef(IEnumMediaTypes *iface)
796 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
797 return InterlockedIncrement(&pin->ref);
800 static ULONG WINAPI testenummt_Release(IEnumMediaTypes *iface)
802 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
803 return InterlockedDecrement(&pin->ref);
806 static HRESULT WINAPI testenummt_Next(IEnumMediaTypes *iface, ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
808 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
809 unsigned int i;
811 for (i = 0; i < count; ++i)
813 if (pin->enum_idx + i >= pin->type_count)
814 break;
816 out[i] = CoTaskMemAlloc(sizeof(*out[i]));
817 *out[i] = pin->types[pin->enum_idx + i];
820 if (fetched)
821 *fetched = i;
822 pin->enum_idx += i;
824 return (i == count) ? S_OK : S_FALSE;
827 static HRESULT WINAPI testenummt_Skip(IEnumMediaTypes *iface, ULONG count)
829 ok(0, "Unexpected call.\n");
830 return E_NOTIMPL;
833 static HRESULT WINAPI testenummt_Reset(IEnumMediaTypes *iface)
835 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
836 pin->enum_idx = 0;
837 return S_OK;
840 static HRESULT WINAPI testenummt_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **out)
842 ok(0, "Unexpected call.\n");
843 return E_NOTIMPL;
846 static const IEnumMediaTypesVtbl testenummt_vtbl =
848 testenummt_QueryInterface,
849 testenummt_AddRef,
850 testenummt_Release,
851 testenummt_Next,
852 testenummt_Skip,
853 testenummt_Reset,
854 testenummt_Clone,
857 static inline struct testpin *impl_from_IPin(IPin *iface)
859 return CONTAINING_RECORD(iface, struct testpin, IPin_iface);
862 static HRESULT WINAPI testpin_QueryInterface(IPin *iface, REFIID iid, void **out)
864 struct testpin *pin = impl_from_IPin(iface);
865 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
867 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IPin))
869 *out = &pin->IPin_iface;
870 IPin_AddRef(*out);
871 return S_OK;
874 *out = NULL;
875 return E_NOINTERFACE;
878 ULONG WINAPI testpin_AddRef(IPin *iface)
880 struct testpin *pin = impl_from_IPin(iface);
881 return InterlockedIncrement(&pin->ref);
884 ULONG WINAPI testpin_Release(IPin *iface)
886 struct testpin *pin = impl_from_IPin(iface);
887 return InterlockedDecrement(&pin->ref);
890 static HRESULT WINAPI testpin_Disconnect(IPin *iface)
892 struct testpin *pin = impl_from_IPin(iface);
893 if (winetest_debug > 1) trace("%p->Disconnect()\n", pin);
895 if (!pin->peer)
896 return S_FALSE;
898 IPin_Release(pin->peer);
899 pin->peer = NULL;
900 return S_OK;
903 static HRESULT WINAPI testpin_ConnectedTo(IPin *iface, IPin **peer)
905 struct testpin *pin = impl_from_IPin(iface);
906 if (winetest_debug > 1) trace("%p->ConnectedTo()\n", pin);
908 *peer = pin->peer;
909 if (*peer)
911 IPin_AddRef(*peer);
912 return S_OK;
914 return VFW_E_NOT_CONNECTED;
917 static HRESULT WINAPI testpin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mt)
919 ok(0, "Unexpected call.\n");
920 return E_NOTIMPL;
923 static HRESULT WINAPI testpin_QueryPinInfo(IPin *iface, PIN_INFO *info)
925 struct testpin *pin = impl_from_IPin(iface);
926 if (winetest_debug > 1) trace("%p->QueryPinInfo()\n", pin);
928 info->pFilter = pin->filter;
929 IBaseFilter_AddRef(pin->filter);
930 info->dir = pin->dir;
931 info->achName[0] = 0;
932 return S_OK;
936 static HRESULT WINAPI testpin_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
938 struct testpin *pin = impl_from_IPin(iface);
939 if (winetest_debug > 1) trace("%p->QueryDirection()\n", pin);
941 *dir = pin->dir;
942 return S_OK;
945 static HRESULT WINAPI testpin_QueryId(IPin *iface, WCHAR **id)
947 if (winetest_debug > 1) trace("%p->QueryId()\n", iface);
948 *id = CoTaskMemAlloc(1);
949 (*id)[0] = 0;
950 return S_OK;
953 static HRESULT WINAPI testpin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mt)
955 ok(0, "Unexpected call.\n");
956 return E_NOTIMPL;
959 static HRESULT WINAPI testpin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
961 struct testpin *pin = impl_from_IPin(iface);
962 if (winetest_debug > 1) trace("%p->EnumMediaTypes()\n", pin);
964 *out = &pin->IEnumMediaTypes_iface;
965 IEnumMediaTypes_AddRef(*out);
966 pin->enum_idx = 0;
967 return S_OK;
970 static HRESULT WINAPI testpin_QueryInternalConnections(IPin *iface, IPin **out, ULONG *count)
972 struct testpin *pin = impl_from_IPin(iface);
973 if (winetest_debug > 1) trace("%p->QueryInternalConnections()\n", pin);
975 *count = 0;
976 return pin->QueryInternalConnections_hr;
979 static HRESULT WINAPI testpin_BeginFlush(IPin *iface)
981 ok(0, "Unexpected call.\n");
982 return E_NOTIMPL;
985 static HRESULT WINAPI testpin_EndFlush(IPin * iface)
987 ok(0, "Unexpected call.\n");
988 return E_NOTIMPL;
991 static HRESULT WINAPI testpin_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
993 ok(0, "Unexpected call.\n");
994 return E_NOTIMPL;
997 static HRESULT WINAPI testpin_EndOfStream(IPin *iface)
999 ok(0, "Unexpected call.\n");
1000 return E_NOTIMPL;
1003 static HRESULT WINAPI no_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1005 ok(0, "Unexpected call.\n");
1006 return E_NOTIMPL;
1009 static HRESULT WINAPI no_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1011 ok(0, "Unexpected call.\n");
1012 return E_NOTIMPL;
1015 static HRESULT WINAPI no_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
1017 ok(0, "Unexpected call.\n");
1018 return E_NOTIMPL;
1021 static HRESULT WINAPI testsink_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1023 struct testpin *pin = impl_from_IPin(iface);
1024 if (winetest_debug > 1) trace("%p->ReceiveConnection(%p)\n", pin, peer);
1026 if (pin->accept_mt && memcmp(pin->accept_mt, mt, sizeof(*mt)))
1027 return VFW_E_TYPE_NOT_ACCEPTED;
1029 pin->peer = peer;
1030 IPin_AddRef(peer);
1031 return S_OK;
1034 static const IPinVtbl testsink_vtbl =
1036 testpin_QueryInterface,
1037 testpin_AddRef,
1038 testpin_Release,
1039 no_Connect,
1040 testsink_ReceiveConnection,
1041 testpin_Disconnect,
1042 testpin_ConnectedTo,
1043 testpin_ConnectionMediaType,
1044 testpin_QueryPinInfo,
1045 testpin_QueryDirection,
1046 testpin_QueryId,
1047 testpin_QueryAccept,
1048 no_EnumMediaTypes,
1049 testpin_QueryInternalConnections,
1050 testpin_EndOfStream,
1051 testpin_BeginFlush,
1052 testpin_EndFlush,
1053 testpin_NewSegment
1056 static void testpin_init(struct testpin *pin, const IPinVtbl *vtbl, PIN_DIRECTION dir)
1058 memset(pin, 0, sizeof(*pin));
1059 pin->IPin_iface.lpVtbl = vtbl;
1060 pin->IEnumMediaTypes_iface.lpVtbl = &testenummt_vtbl;
1061 pin->ref = 1;
1062 pin->dir = dir;
1063 pin->QueryInternalConnections_hr = E_NOTIMPL;
1066 static void testsink_init(struct testpin *pin)
1068 testpin_init(pin, &testsink_vtbl, PINDIR_INPUT);
1071 static HRESULT WINAPI testsource_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1073 struct testpin *pin = impl_from_IPin(iface);
1074 HRESULT hr;
1075 if (winetest_debug > 1) trace("%p->Connect(%p)\n", pin, peer);
1077 ok(!mt, "Got media type %p.\n", mt);
1079 if (SUCCEEDED(hr = IPin_ReceiveConnection(peer, &pin->IPin_iface, pin->request_mt)))
1081 pin->peer = peer;
1082 IPin_AddRef(peer);
1084 return hr;
1087 static const IPinVtbl testsource_vtbl =
1089 testpin_QueryInterface,
1090 testpin_AddRef,
1091 testpin_Release,
1092 testsource_Connect,
1093 no_ReceiveConnection,
1094 testpin_Disconnect,
1095 testpin_ConnectedTo,
1096 testpin_ConnectionMediaType,
1097 testpin_QueryPinInfo,
1098 testpin_QueryDirection,
1099 testpin_QueryId,
1100 testpin_QueryAccept,
1101 testpin_EnumMediaTypes,
1102 testpin_QueryInternalConnections,
1103 testpin_EndOfStream,
1104 testpin_BeginFlush,
1105 testpin_EndFlush,
1106 testpin_NewSegment
1109 static void testsource_init(struct testpin *pin, const AM_MEDIA_TYPE *types, int type_count)
1111 testpin_init(pin, &testsource_vtbl, PINDIR_OUTPUT);
1112 pin->types = types;
1113 pin->type_count = type_count;
1116 struct testfilter
1118 IBaseFilter IBaseFilter_iface;
1119 LONG ref;
1120 IFilterGraph *graph;
1121 WCHAR *name;
1122 IReferenceClock *clock;
1124 IEnumPins IEnumPins_iface;
1125 struct testpin *pins;
1126 unsigned int pin_count, enum_idx;
1129 static inline struct testfilter *impl_from_IEnumPins(IEnumPins *iface)
1131 return CONTAINING_RECORD(iface, struct testfilter, IEnumPins_iface);
1134 static HRESULT WINAPI testenumpins_QueryInterface(IEnumPins *iface, REFIID iid, void **out)
1136 ok(0, "Unexpected iid %s.\n", wine_dbgstr_guid(iid));
1137 return E_NOINTERFACE;
1140 static ULONG WINAPI testenumpins_AddRef(IEnumPins * iface)
1142 struct testfilter *filter = impl_from_IEnumPins(iface);
1143 return InterlockedIncrement(&filter->ref);
1146 static ULONG WINAPI testenumpins_Release(IEnumPins * iface)
1148 struct testfilter *filter = impl_from_IEnumPins(iface);
1149 return InterlockedDecrement(&filter->ref);
1152 static HRESULT WINAPI testenumpins_Next(IEnumPins *iface, ULONG count, IPin **out, ULONG *fetched)
1154 struct testfilter *filter = impl_from_IEnumPins(iface);
1155 unsigned int i;
1157 for (i = 0; i < count; ++i)
1159 if (filter->enum_idx + i >= filter->pin_count)
1160 break;
1162 out[i] = &filter->pins[filter->enum_idx + i].IPin_iface;
1163 IPin_AddRef(out[i]);
1166 if (fetched)
1167 *fetched = i;
1168 filter->enum_idx += i;
1170 return (i == count) ? S_OK : S_FALSE;
1173 static HRESULT WINAPI testenumpins_Skip(IEnumPins *iface, ULONG count)
1175 ok(0, "Unexpected call.\n");
1176 return E_NOTIMPL;
1179 static HRESULT WINAPI testenumpins_Reset(IEnumPins *iface)
1181 struct testfilter *filter = impl_from_IEnumPins(iface);
1182 filter->enum_idx = 0;
1183 return S_OK;
1186 static HRESULT WINAPI testenumpins_Clone(IEnumPins *iface, IEnumPins **out)
1188 ok(0, "Unexpected call.\n");
1189 return E_NOTIMPL;
1192 static const IEnumPinsVtbl testenumpins_vtbl =
1194 testenumpins_QueryInterface,
1195 testenumpins_AddRef,
1196 testenumpins_Release,
1197 testenumpins_Next,
1198 testenumpins_Skip,
1199 testenumpins_Reset,
1200 testenumpins_Clone,
1203 static inline struct testfilter *impl_from_IBaseFilter(IBaseFilter *iface)
1205 return CONTAINING_RECORD(iface, struct testfilter, IBaseFilter_iface);
1208 static HRESULT WINAPI testfilter_QueryInterface(IBaseFilter *iface, REFIID iid, void **out)
1210 struct testfilter *filter = impl_from_IBaseFilter(iface);
1211 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", filter, wine_dbgstr_guid(iid));
1213 if (IsEqualGUID(iid, &IID_IUnknown)
1214 || IsEqualGUID(iid, &IID_IPersist)
1215 || IsEqualGUID(iid, &IID_IMediaFilter)
1216 || IsEqualGUID(iid, &IID_IBaseFilter))
1218 *out = &filter->IBaseFilter_iface;
1219 IBaseFilter_AddRef(*out);
1220 return S_OK;
1223 *out = NULL;
1224 return E_NOINTERFACE;
1227 static ULONG WINAPI testfilter_AddRef(IBaseFilter *iface)
1229 struct testfilter *filter = impl_from_IBaseFilter(iface);
1230 return InterlockedIncrement(&filter->ref);
1233 static ULONG WINAPI testfilter_Release(IBaseFilter *iface)
1235 struct testfilter *filter = impl_from_IBaseFilter(iface);
1236 return InterlockedDecrement(&filter->ref);
1239 static HRESULT WINAPI testfilter_GetClassID(IBaseFilter *iface, CLSID *clsid)
1241 if (winetest_debug > 1) trace("%p->GetClassID()\n", iface);
1242 return S_OK;
1245 static HRESULT WINAPI testfilter_Stop(IBaseFilter *iface)
1247 if (winetest_debug > 1) trace("%p->Stop()\n", iface);
1248 return E_NOTIMPL;
1251 static HRESULT WINAPI testfilter_Pause(IBaseFilter *iface)
1253 ok(0, "Unexpected call.\n");
1254 return E_NOTIMPL;
1257 static HRESULT WINAPI testfilter_Run(IBaseFilter *iface, REFERENCE_TIME start)
1259 ok(0, "Unexpected call.\n");
1260 return E_NOTIMPL;
1263 static HRESULT WINAPI testfilter_GetState(IBaseFilter *iface, DWORD timeout, FILTER_STATE *state)
1265 if (winetest_debug > 1) trace("%p->GetState()\n", iface);
1266 return E_NOTIMPL;
1269 static HRESULT WINAPI testfilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
1271 struct testfilter *filter = impl_from_IBaseFilter(iface);
1272 if (winetest_debug > 1) trace("%p->SetSyncSource(%p)\n", filter, clock);
1274 if (filter->clock)
1275 IReferenceClock_Release(filter->clock);
1276 if (clock)
1277 IReferenceClock_AddRef(clock);
1278 filter->clock = clock;
1279 return S_OK;
1282 static HRESULT WINAPI testfilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock)
1284 ok(0, "Unexpected call.\n");
1285 return E_NOTIMPL;
1288 static HRESULT WINAPI testfilter_EnumPins(IBaseFilter *iface, IEnumPins **out)
1290 struct testfilter *filter = impl_from_IBaseFilter(iface);
1291 if (winetest_debug > 1) trace("%p->EnumPins()\n", filter);
1293 *out = &filter->IEnumPins_iface;
1294 IEnumPins_AddRef(*out);
1295 filter->enum_idx = 0;
1296 return S_OK;
1299 static HRESULT WINAPI testfilter_FindPin(IBaseFilter *iface, const WCHAR *id, IPin **pin)
1301 ok(0, "Unexpected call.\n");
1302 return E_NOTIMPL;
1305 static HRESULT WINAPI testfilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info)
1307 struct testfilter *filter = impl_from_IBaseFilter(iface);
1308 if (winetest_debug > 1) trace("%p->QueryFilterInfo()\n", filter);
1310 info->pGraph = filter->graph;
1311 if (filter->graph)
1312 IFilterGraph_AddRef(filter->graph);
1313 if (filter->name)
1314 lstrcpyW(info->achName, filter->name);
1315 else
1316 info->achName[0] = 0;
1317 return S_OK;
1320 static HRESULT WINAPI testfilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, const WCHAR *name)
1322 struct testfilter *filter = impl_from_IBaseFilter(iface);
1323 if (winetest_debug > 1) trace("%p->JoinFilterGraph(%p, %s)\n", filter, graph, wine_dbgstr_w(name));
1325 filter->graph = graph;
1326 heap_free(filter->name);
1327 if (name)
1329 filter->name = heap_alloc((lstrlenW(name)+1)*sizeof(WCHAR));
1330 lstrcpyW(filter->name, name);
1332 else
1333 filter->name = NULL;
1334 return S_OK;
1337 static HRESULT WINAPI testfilter_QueryVendorInfo(IBaseFilter * iface, WCHAR **info)
1339 return E_NOTIMPL;
1342 static const IBaseFilterVtbl testfilter_vtbl =
1344 testfilter_QueryInterface,
1345 testfilter_AddRef,
1346 testfilter_Release,
1347 testfilter_GetClassID,
1348 testfilter_Stop,
1349 testfilter_Pause,
1350 testfilter_Run,
1351 testfilter_GetState,
1352 testfilter_SetSyncSource,
1353 testfilter_GetSyncSource,
1354 testfilter_EnumPins,
1355 testfilter_FindPin,
1356 testfilter_QueryFilterInfo,
1357 testfilter_JoinFilterGraph,
1358 testfilter_QueryVendorInfo
1361 struct testfilter_cf
1363 IClassFactory IClassFactory_iface;
1364 struct testfilter *filter;
1367 static void testfilter_init(struct testfilter *filter, struct testpin *pins, int pin_count)
1369 unsigned int i;
1371 memset(filter, 0, sizeof(*filter));
1372 filter->IBaseFilter_iface.lpVtbl = &testfilter_vtbl;
1373 filter->IEnumPins_iface.lpVtbl = &testenumpins_vtbl;
1374 filter->ref = 1;
1375 filter->pins = pins;
1376 filter->pin_count = pin_count;
1377 for (i = 0; i < pin_count; i++)
1378 pins[i].filter = &filter->IBaseFilter_iface;
1381 static HRESULT WINAPI testfilter_cf_QueryInterface(IClassFactory *iface, REFIID iid, void **out)
1383 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory))
1385 *out = iface;
1386 return S_OK;
1389 *out = NULL;
1390 return E_NOINTERFACE;
1393 static ULONG WINAPI testfilter_cf_AddRef(IClassFactory *iface)
1395 return 2;
1398 static ULONG WINAPI testfilter_cf_Release(IClassFactory *iface)
1400 return 1;
1403 static HRESULT WINAPI testfilter_cf_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **out)
1405 struct testfilter_cf *factory = CONTAINING_RECORD(iface, struct testfilter_cf, IClassFactory_iface);
1407 return IBaseFilter_QueryInterface(&factory->filter->IBaseFilter_iface, iid, out);
1410 static HRESULT WINAPI testfilter_cf_LockServer(IClassFactory *iface, BOOL lock)
1412 return E_NOTIMPL;
1415 static IClassFactoryVtbl testfilter_cf_vtbl =
1417 testfilter_cf_QueryInterface,
1418 testfilter_cf_AddRef,
1419 testfilter_cf_Release,
1420 testfilter_cf_CreateInstance,
1421 testfilter_cf_LockServer,
1424 static void test_graph_builder_render(void)
1426 static const WCHAR testW[] = {'t','e','s','t',0};
1427 static const GUID sink1_clsid = {0x12345678};
1428 static const GUID sink2_clsid = {0x87654321};
1429 AM_MEDIA_TYPE source_type = {{0}};
1430 struct testpin source_pin, sink1_pin, sink2_pin, parser_pins[2];
1431 struct testfilter source, sink1, sink2, parser;
1432 struct testfilter_cf sink1_cf = { {&testfilter_cf_vtbl}, &sink1 };
1433 struct testfilter_cf sink2_cf = { {&testfilter_cf_vtbl}, &sink2 };
1435 IFilterGraph2 *graph = create_graph();
1436 REGFILTERPINS2 regpins = {0};
1437 REGPINTYPES regtypes = {0};
1438 REGFILTER2 regfilter = {0};
1439 IFilterMapper2 *mapper;
1440 DWORD cookie1, cookie2;
1441 HRESULT hr;
1442 ULONG ref;
1444 memset(&source_type.majortype, 0xcc, sizeof(GUID));
1445 testsource_init(&source_pin, &source_type, 1);
1446 testfilter_init(&source, &source_pin, 1);
1447 testsink_init(&sink1_pin);
1448 testfilter_init(&sink1, &sink1_pin, 1);
1449 testsink_init(&sink2_pin);
1450 testfilter_init(&sink2, &sink2_pin, 1);
1451 testsink_init(&parser_pins[0]);
1452 testsource_init(&parser_pins[1], &source_type, 1);
1453 testfilter_init(&parser, parser_pins, 2);
1455 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1456 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1457 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
1459 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1460 ok(hr == S_OK, "Got hr %#x.\n", hr);
1461 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1462 IFilterGraph2_Disconnect(graph, source_pin.peer);
1463 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1465 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1466 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1468 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1469 ok(hr == S_OK, "Got hr %#x.\n", hr);
1470 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1472 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1473 IFilterGraph2_Disconnect(graph, &sink1_pin.IPin_iface);
1475 /* No preference is given to smaller chains. */
1477 IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
1479 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1480 ok(hr == S_OK, "Got hr %#x.\n", hr);
1481 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1482 ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
1483 IFilterGraph2_Disconnect(graph, source_pin.peer);
1484 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1486 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1487 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1489 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1490 ok(hr == S_OK, "Got hr %#x.\n", hr);
1491 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1493 ref = IFilterGraph2_Release(graph);
1494 ok(!ref, "Got outstanding refcount %d.\n", ref);
1496 /* Test enumeration of filters from the registry. */
1498 graph = create_graph();
1499 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1501 CoRegisterClassObject(&sink1_clsid, (IUnknown *)&sink1_cf.IClassFactory_iface,
1502 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
1503 CoRegisterClassObject(&sink2_clsid, (IUnknown *)&sink2_cf.IClassFactory_iface,
1504 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
1506 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
1507 &IID_IFilterMapper2, (void **)&mapper);
1509 regfilter.dwVersion = 2;
1510 regfilter.dwMerit = MERIT_UNLIKELY;
1511 regfilter.cPins2 = 1;
1512 regfilter.rgPins2 = &regpins;
1513 regpins.dwFlags = 0;
1514 regpins.cInstances = 1;
1515 regpins.nMediaTypes = 1;
1516 regpins.lpMediaType = &regtypes;
1517 regtypes.clsMajorType = &source_type.majortype;
1518 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
1519 hr = IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, testW, NULL, NULL, NULL, &regfilter);
1520 if (hr == E_ACCESSDENIED)
1522 skip("Not enough permission to register filters.\n");
1523 goto out;
1525 ok(hr == S_OK, "Got hr %#x.\n", hr);
1527 regpins.dwFlags = REG_PINFLAG_B_RENDERER;
1528 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, testW, NULL, NULL, NULL, &regfilter);
1530 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1531 ok(hr == S_OK, "Got hr %#x.\n", hr);
1532 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
1533 "Got peer %p.\n", source_pin.peer);
1535 ref = IFilterGraph2_Release(graph);
1536 ok(!ref, "Got outstanding refcount %d.\n", ref);
1538 /* Preference is given to filters already in the graph. */
1540 graph = create_graph();
1541 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1542 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
1544 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1545 ok(hr == S_OK, "Got hr %#x.\n", hr);
1546 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1548 ref = IFilterGraph2_Release(graph);
1549 ok(!ref, "Got outstanding refcount %d.\n", ref);
1551 /* No preference is given to renderer filters. */
1553 graph = create_graph();
1554 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1556 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
1557 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
1559 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, testW, NULL, NULL, NULL, &regfilter);
1560 regpins.dwFlags = 0;
1561 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, testW, NULL, NULL, NULL, &regfilter);
1563 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1564 ok(hr == S_OK, "Got hr %#x.\n", hr);
1565 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
1566 "Got peer %p.\n", source_pin.peer);
1568 ref = IFilterGraph2_Release(graph);
1569 ok(!ref, "Got outstanding refcount %d.\n", ref);
1571 /* Preference is given to filters with higher merit. */
1573 graph = create_graph();
1574 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1576 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
1577 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
1579 regfilter.dwMerit = MERIT_UNLIKELY;
1580 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, testW, NULL, NULL, NULL, &regfilter);
1581 regfilter.dwMerit = MERIT_PREFERRED;
1582 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, testW, NULL, NULL, NULL, &regfilter);
1584 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1585 ok(hr == S_OK, "Got hr %#x.\n", hr);
1586 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1588 ref = IFilterGraph2_Release(graph);
1589 ok(!ref, "Got outstanding refcount %d.\n", ref);
1591 graph = create_graph();
1592 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1594 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
1595 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
1597 regfilter.dwMerit = MERIT_PREFERRED;
1598 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, testW, NULL, NULL, NULL, &regfilter);
1599 regfilter.dwMerit = MERIT_UNLIKELY;
1600 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, testW, NULL, NULL, NULL, &regfilter);
1602 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1603 ok(hr == S_OK, "Got hr %#x.\n", hr);
1604 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1606 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
1607 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
1609 out:
1610 CoRevokeClassObject(cookie1);
1611 CoRevokeClassObject(cookie2);
1612 IFilterMapper2_Release(mapper);
1613 ref = IFilterGraph2_Release(graph);
1614 ok(!ref, "Got outstanding refcount %d.\n", ref);
1615 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
1616 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
1617 ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
1618 ok(sink1_pin.ref == 1, "Got outstanding refcount %d.\n", sink1_pin.ref);
1619 ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
1620 ok(sink2_pin.ref == 1, "Got outstanding refcount %d.\n", sink2_pin.ref);
1621 ok(parser.ref == 1, "Got outstanding refcount %d.\n", parser.ref);
1622 ok(parser_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser_pins[0].ref);
1623 ok(parser_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser_pins[1].ref);
1626 static void test_graph_builder_connect(void)
1628 static const WCHAR testW[] = {'t','e','s','t',0};
1629 static const GUID parser1_clsid = {0x12345678};
1630 static const GUID parser2_clsid = {0x87654321};
1631 AM_MEDIA_TYPE source_type = {{0}}, sink_type = {{0}}, parser3_type = {{0}};
1632 struct testpin source_pin, sink_pin, sink2_pin, parser1_pins[3], parser2_pins[2], parser3_pins[2];
1633 struct testfilter source, sink, sink2, parser1, parser2, parser3;
1634 struct testfilter_cf parser1_cf = { {&testfilter_cf_vtbl}, &parser1 };
1635 struct testfilter_cf parser2_cf = { {&testfilter_cf_vtbl}, &parser2 };
1637 IFilterGraph2 *graph = create_graph();
1638 REGFILTERPINS2 regpins[2] = {{0}};
1639 REGPINTYPES regtypes = {0};
1640 REGFILTER2 regfilter = {0};
1641 IFilterMapper2 *mapper;
1642 DWORD cookie1, cookie2;
1643 HRESULT hr;
1644 ULONG ref;
1646 memset(&source_type.majortype, 0xcc, sizeof(GUID));
1647 memset(&sink_type.majortype, 0x66, sizeof(GUID));
1648 testsource_init(&source_pin, &source_type, 1);
1649 source_pin.request_mt = &source_type;
1650 testfilter_init(&source, &source_pin, 1);
1651 testsink_init(&sink_pin);
1652 testfilter_init(&sink, &sink_pin, 1);
1653 testsink_init(&sink2_pin);
1654 testfilter_init(&sink2, &sink2_pin, 1);
1656 testsink_init(&parser1_pins[0]);
1657 testsource_init(&parser1_pins[1], &sink_type, 1);
1658 parser1_pins[1].request_mt = &sink_type;
1659 testsource_init(&parser1_pins[2], &sink_type, 1);
1660 parser1_pins[2].request_mt = &sink_type;
1661 testfilter_init(&parser1, parser1_pins, 3);
1662 parser1.pin_count = 2;
1664 testsink_init(&parser2_pins[0]);
1665 testsource_init(&parser2_pins[1], &sink_type, 1);
1666 parser2_pins[1].request_mt = &sink_type;
1667 testfilter_init(&parser2, parser2_pins, 2);
1669 testsink_init(&parser3_pins[0]);
1670 testsource_init(&parser3_pins[1], &sink_type, 1);
1671 parser3_pins[1].request_mt = &parser3_type;
1672 testfilter_init(&parser3, parser3_pins, 2);
1674 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1675 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
1677 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1678 ok(hr == S_OK, "Got hr %#x.\n", hr);
1679 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1680 IFilterGraph2_Disconnect(graph, source_pin.peer);
1681 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1683 sink_pin.accept_mt = &sink_type;
1684 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1685 todo_wine
1686 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
1687 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
1689 /* Test usage of intermediate filters. Similarly to Render(), filters are
1690 * simply tried in enumeration order. */
1692 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
1693 IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
1695 sink_pin.accept_mt = NULL;
1696 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1697 ok(hr == S_OK, "Got hr %#x.\n", hr);
1698 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1699 IFilterGraph2_Disconnect(graph, source_pin.peer);
1700 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1702 sink_pin.accept_mt = &sink_type;
1703 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1704 todo_wine {
1705 ok(hr == S_OK, "Got hr %#x.\n", hr);
1706 ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1707 ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1709 IFilterGraph2_Disconnect(graph, source_pin.peer);
1710 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1711 IFilterGraph2_Disconnect(graph, sink_pin.peer);
1712 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
1714 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
1715 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
1717 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1718 todo_wine {
1719 ok(hr == S_OK, "Got hr %#x.\n", hr);
1720 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1721 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1723 IFilterGraph2_Disconnect(graph, source_pin.peer);
1724 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1725 IFilterGraph2_Disconnect(graph, sink_pin.peer);
1726 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
1728 /* No preference is given to smaller chains. */
1730 IFilterGraph2_AddFilter(graph, &parser3.IBaseFilter_iface, NULL);
1731 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1732 todo_wine {
1733 ok(hr == S_OK, "Got hr %#x.\n", hr);
1734 ok(source_pin.peer == &parser3_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1735 ok(parser3_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser3_pins[1].peer);
1736 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1738 IFilterGraph2_Disconnect(graph, source_pin.peer);
1739 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1740 IFilterGraph2_Disconnect(graph, parser3_pins[0].peer);
1741 IFilterGraph2_Disconnect(graph, &parser3_pins[0].IPin_iface);
1742 IFilterGraph2_Disconnect(graph, sink_pin.peer);
1743 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
1745 IFilterGraph2_RemoveFilter(graph, &parser3.IBaseFilter_iface);
1746 IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
1748 /* Extra source pins on an intermediate filter are not rendered. */
1750 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
1751 parser1.pin_count = 3;
1752 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
1753 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
1755 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1756 todo_wine {
1757 ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
1758 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1759 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1761 ok(!parser1_pins[2].peer, "Got peer %p.\n", parser1_pins[2].peer);
1762 IFilterGraph2_Disconnect(graph, source_pin.peer);
1763 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1764 IFilterGraph2_Disconnect(graph, sink_pin.peer);
1765 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
1766 parser1.pin_count = 2;
1768 /* QueryInternalConnections is not used to find output pins. */
1770 parser1_pins[1].QueryInternalConnections_hr = S_OK;
1771 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1772 todo_wine {
1773 ok(hr == S_OK, "Got hr %#x.\n", hr);
1774 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1775 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1777 IFilterGraph2_Disconnect(graph, source_pin.peer);
1778 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1779 IFilterGraph2_Disconnect(graph, sink_pin.peer);
1780 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
1782 ref = IFilterGraph2_Release(graph);
1783 ok(!ref, "Got outstanding refcount %d.\n", ref);
1785 /* Test enumeration of filters from the registry. */
1787 graph = create_graph();
1788 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1789 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
1791 CoRegisterClassObject(&parser1_clsid, (IUnknown *)&parser1_cf.IClassFactory_iface,
1792 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
1793 CoRegisterClassObject(&parser2_clsid, (IUnknown *)&parser2_cf.IClassFactory_iface,
1794 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
1796 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
1797 &IID_IFilterMapper2, (void **)&mapper);
1799 regfilter.dwVersion = 2;
1800 regfilter.dwMerit = MERIT_UNLIKELY;
1801 regfilter.cPins2 = 2;
1802 regfilter.rgPins2 = regpins;
1803 regpins[0].dwFlags = 0;
1804 regpins[0].cInstances = 1;
1805 regpins[0].nMediaTypes = 1;
1806 regpins[0].lpMediaType = &regtypes;
1807 regpins[1].dwFlags = REG_PINFLAG_B_OUTPUT;
1808 regpins[1].cInstances = 1;
1809 regpins[1].nMediaTypes = 1;
1810 regpins[1].lpMediaType = &regtypes;
1811 regtypes.clsMajorType = &source_type.majortype;
1812 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
1813 hr = IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, testW, NULL, NULL, NULL, &regfilter);
1814 if (hr == E_ACCESSDENIED)
1816 skip("Not enough permission to register filters.\n");
1817 goto out;
1819 ok(hr == S_OK, "Got hr %#x.\n", hr);
1821 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, testW, NULL, NULL, NULL, &regfilter);
1823 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1824 ok(hr == S_OK, "Got hr %#x.\n", hr);
1825 ok(source_pin.peer == &parser1_pins[0].IPin_iface
1826 || source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1827 ok(sink_pin.peer == &parser1_pins[1].IPin_iface
1828 || sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1830 ref = IFilterGraph2_Release(graph);
1831 ok(!ref, "Got outstanding refcount %d.\n", ref);
1833 /* Preference is given to filters already in the graph. */
1835 graph = create_graph();
1836 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1837 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
1838 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
1840 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1841 ok(hr == S_OK, "Got hr %#x.\n", hr);
1842 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1843 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1845 ref = IFilterGraph2_Release(graph);
1846 ok(!ref, "Got outstanding refcount %d.\n", ref);
1848 /* Preference is given to filters with higher merit. */
1850 graph = create_graph();
1851 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1852 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
1854 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
1855 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
1857 regfilter.dwMerit = MERIT_UNLIKELY;
1858 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, testW, NULL, NULL, NULL, &regfilter);
1859 regfilter.dwMerit = MERIT_PREFERRED;
1860 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, testW, NULL, NULL, NULL, &regfilter);
1862 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1863 ok(hr == S_OK, "Got hr %#x.\n", hr);
1864 ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1865 ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1867 ref = IFilterGraph2_Release(graph);
1868 ok(!ref, "Got outstanding refcount %d.\n", ref);
1870 graph = create_graph();
1871 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1872 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
1874 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
1875 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
1877 regfilter.dwMerit = MERIT_PREFERRED;
1878 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, testW, NULL, NULL, NULL, &regfilter);
1879 regfilter.dwMerit = MERIT_UNLIKELY;
1880 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, testW, NULL, NULL, NULL, &regfilter);
1882 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
1883 ok(hr == S_OK, "Got hr %#x.\n", hr);
1884 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1885 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
1887 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
1888 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
1890 out:
1891 CoRevokeClassObject(cookie1);
1892 CoRevokeClassObject(cookie2);
1893 IFilterMapper2_Release(mapper);
1894 ref = IFilterGraph2_Release(graph);
1895 ok(!ref, "Got outstanding refcount %d.\n", ref);
1896 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
1897 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
1898 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
1899 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
1900 ok(parser1.ref == 1, "Got outstanding refcount %d.\n", parser1.ref);
1901 ok(parser1_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[0].ref);
1902 ok(parser1_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[1].ref);
1903 ok(parser1_pins[2].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[2].ref);
1904 ok(parser2.ref == 1, "Got outstanding refcount %d.\n", parser2.ref);
1905 ok(parser2_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[0].ref);
1906 ok(parser2_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[1].ref);
1907 ok(parser3.ref == 1, "Got outstanding refcount %d.\n", parser3.ref);
1908 ok(parser3_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[0].ref);
1909 ok(parser3_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[1].ref);
1912 typedef struct IUnknownImpl
1914 IUnknown IUnknown_iface;
1915 int AddRef_called;
1916 int Release_called;
1917 } IUnknownImpl;
1919 static IUnknownImpl *IUnknownImpl_from_iface(IUnknown * iface)
1921 return CONTAINING_RECORD(iface, IUnknownImpl, IUnknown_iface);
1924 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
1926 ok(0, "QueryInterface should not be called for %s\n", wine_dbgstr_guid(riid));
1927 return E_NOINTERFACE;
1930 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown * iface)
1932 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
1933 This->AddRef_called++;
1934 return 2;
1937 static ULONG WINAPI IUnknownImpl_Release(IUnknown * iface)
1939 IUnknownImpl *This = IUnknownImpl_from_iface(iface);
1940 This->Release_called++;
1941 return 1;
1944 static CONST_VTBL IUnknownVtbl IUnknownImpl_Vtbl =
1946 IUnknownImpl_QueryInterface,
1947 IUnknownImpl_AddRef,
1948 IUnknownImpl_Release
1951 static void test_aggregate_filter_graph(void)
1953 HRESULT hr;
1954 IUnknown *pgraph;
1955 IUnknown *punk;
1956 IUnknownImpl unk_outer = { { &IUnknownImpl_Vtbl }, 0, 0 };
1958 hr = CoCreateInstance(&CLSID_FilterGraph, &unk_outer.IUnknown_iface, CLSCTX_INPROC_SERVER,
1959 &IID_IUnknown, (void **)&pgraph);
1960 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1961 ok(pgraph != &unk_outer.IUnknown_iface, "pgraph = %p, expected not %p\n", pgraph, &unk_outer.IUnknown_iface);
1963 hr = IUnknown_QueryInterface(pgraph, &IID_IUnknown, (void **)&punk);
1964 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1965 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1966 IUnknown_Release(punk);
1968 ok(unk_outer.AddRef_called == 0, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1969 ok(unk_outer.Release_called == 0, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1970 unk_outer.AddRef_called = 0;
1971 unk_outer.Release_called = 0;
1973 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper, (void **)&punk);
1974 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1975 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1976 IUnknown_Release(punk);
1978 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1979 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1980 unk_outer.AddRef_called = 0;
1981 unk_outer.Release_called = 0;
1983 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper2, (void **)&punk);
1984 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1985 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1986 IUnknown_Release(punk);
1988 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1989 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
1990 unk_outer.AddRef_called = 0;
1991 unk_outer.Release_called = 0;
1993 hr = IUnknown_QueryInterface(pgraph, &IID_IFilterMapper3, (void **)&punk);
1994 ok(hr == S_OK, "CoCreateInstance returned %x\n", hr);
1995 ok(punk != &unk_outer.IUnknown_iface, "punk = %p, expected not %p\n", punk, &unk_outer.IUnknown_iface);
1996 IUnknown_Release(punk);
1998 ok(unk_outer.AddRef_called == 1, "IUnknownImpl_AddRef called %d times\n", unk_outer.AddRef_called);
1999 ok(unk_outer.Release_called == 1, "IUnknownImpl_Release called %d times\n", unk_outer.Release_called);
2001 IUnknown_Release(pgraph);
2004 /* Test how methods from "control" interfaces (IBasicAudio, IBasicVideo,
2005 * IVideoWindow) are delegated to filters exposing those interfaces. */
2006 static void test_control_delegation(void)
2008 IFilterGraph2 *graph = create_graph();
2009 IBasicAudio *audio, *filter_audio;
2010 IBaseFilter *renderer;
2011 IVideoWindow *window;
2012 IBasicVideo *video;
2013 HRESULT hr;
2014 LONG val;
2016 /* IBasicAudio */
2018 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicAudio, (void **)&audio);
2019 ok(hr == S_OK, "got %#x\n", hr);
2021 hr = IBasicAudio_put_Volume(audio, -10);
2022 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2023 hr = IBasicAudio_get_Volume(audio, &val);
2024 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2025 hr = IBasicAudio_put_Balance(audio, 10);
2026 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2027 hr = IBasicAudio_get_Balance(audio, &val);
2028 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2030 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
2031 if (hr != VFW_E_NO_AUDIO_HARDWARE)
2033 ok(hr == S_OK, "got %#x\n", hr);
2035 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
2036 ok(hr == S_OK, "got %#x\n", hr);
2038 hr = IBasicAudio_put_Volume(audio, -10);
2039 ok(hr == S_OK, "got %#x\n", hr);
2040 hr = IBasicAudio_get_Volume(audio, &val);
2041 ok(hr == S_OK, "got %#x\n", hr);
2042 ok(val == -10, "got %d\n", val);
2043 hr = IBasicAudio_put_Balance(audio, 10);
2044 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2045 hr = IBasicAudio_get_Balance(audio, &val);
2046 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2047 if (hr == S_OK)
2048 ok(val == 10, "got balance %d\n", val);
2050 hr = IBaseFilter_QueryInterface(renderer, &IID_IBasicAudio, (void **)&filter_audio);
2051 ok(hr == S_OK, "got %#x\n", hr);
2053 hr = IBasicAudio_get_Volume(filter_audio, &val);
2054 ok(hr == S_OK, "got %#x\n", hr);
2055 ok(val == -10, "got volume %d\n", val);
2057 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2058 ok(hr == S_OK, "got %#x\n", hr);
2060 IBaseFilter_Release(renderer);
2061 IBasicAudio_Release(filter_audio);
2064 hr = IBasicAudio_put_Volume(audio, -10);
2065 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2066 hr = IBasicAudio_get_Volume(audio, &val);
2067 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2068 hr = IBasicAudio_put_Balance(audio, 10);
2069 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2070 hr = IBasicAudio_get_Balance(audio, &val);
2071 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2073 IBasicAudio_Release(audio);
2075 /* IBasicVideo and IVideoWindow */
2077 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo, (void **)&video);
2078 ok(hr == S_OK, "got %#x\n", hr);
2079 hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
2080 ok(hr == S_OK, "got %#x\n", hr);
2082 /* Unlike IBasicAudio, these return E_NOINTERFACE. */
2083 hr = IBasicVideo_get_BitRate(video, &val);
2084 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2085 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2086 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2088 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
2089 ok(hr == S_OK, "got %#x\n", hr);
2091 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
2092 ok(hr == S_OK, "got %#x\n", hr);
2094 hr = IBasicVideo_get_BitRate(video, &val);
2095 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2096 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2097 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2099 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2100 ok(hr == S_OK, "got %#x\n", hr);
2102 hr = IBasicVideo_get_BitRate(video, &val);
2103 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2104 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2105 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2107 IBaseFilter_Release(renderer);
2108 IBasicVideo_Release(video);
2109 IVideoWindow_Release(window);
2110 IFilterGraph2_Release(graph);
2113 static void test_add_remove_filter(void)
2115 static const WCHAR defaultid[] = {'0','0','0','1',0};
2116 static const WCHAR testid[] = {'t','e','s','t','i','d',0};
2117 struct testfilter filter;
2119 IFilterGraph2 *graph = create_graph();
2120 IBaseFilter *ret_filter;
2121 HRESULT hr;
2123 testfilter_init(&filter, NULL, 0);
2125 hr = IFilterGraph2_FindFilterByName(graph, testid, &ret_filter);
2126 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2127 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2129 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, testid);
2130 ok(hr == S_OK, "Got hr %#x.\n", hr);
2131 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2132 ok(!lstrcmpW(filter.name, testid), "Got name %s.\n", wine_dbgstr_w(filter.name));
2134 hr = IFilterGraph2_FindFilterByName(graph, testid, &ret_filter);
2135 ok(hr == S_OK, "Got hr %#x.\n", hr);
2136 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2137 IBaseFilter_Release(ret_filter);
2139 hr = IFilterGraph2_RemoveFilter(graph, &filter.IBaseFilter_iface);
2140 ok(hr == S_OK, "Got hr %#x.\n", hr);
2141 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2142 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2143 ok(!filter.clock, "Got clock %p,\n", filter.clock);
2144 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2146 hr = IFilterGraph2_FindFilterByName(graph, testid, &ret_filter);
2147 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2148 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2150 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, NULL);
2151 ok(hr == S_OK, "Got hr %#x.\n", hr);
2152 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2153 ok(!lstrcmpW(filter.name, defaultid), "Got name %s.\n", wine_dbgstr_w(filter.name));
2155 hr = IFilterGraph2_FindFilterByName(graph, defaultid, &ret_filter);
2156 ok(hr == S_OK, "Got hr %#x.\n", hr);
2157 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2158 IBaseFilter_Release(ret_filter);
2160 /* test releasing the filter graph while filters are still connected */
2161 hr = IFilterGraph2_Release(graph);
2162 ok(!hr, "Got outstanding refcount %d.\n", hr);
2163 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2164 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2165 ok(!filter.clock, "Got clock %p.\n", filter.clock);
2166 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2169 static HRESULT WINAPI test_connect_direct_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
2171 struct testpin *pin = impl_from_IPin(iface);
2172 if (winetest_debug > 1) trace("%p->Connect()\n", pin);
2174 pin->peer = peer;
2175 IPin_AddRef(peer);
2176 pin->mt = (AM_MEDIA_TYPE *)mt;
2177 return S_OK;
2180 static const IPinVtbl test_connect_direct_vtbl =
2182 testpin_QueryInterface,
2183 testpin_AddRef,
2184 testpin_Release,
2185 test_connect_direct_Connect,
2186 no_ReceiveConnection,
2187 testpin_Disconnect,
2188 testpin_ConnectedTo,
2189 testpin_ConnectionMediaType,
2190 testpin_QueryPinInfo,
2191 testpin_QueryDirection,
2192 testpin_QueryId,
2193 testpin_QueryAccept,
2194 no_EnumMediaTypes,
2195 testpin_QueryInternalConnections,
2196 testpin_EndOfStream,
2197 testpin_BeginFlush,
2198 testpin_EndFlush,
2199 testpin_NewSegment
2202 static void test_connect_direct_init(struct testpin *pin, PIN_DIRECTION dir)
2204 memset(pin, 0, sizeof(*pin));
2205 pin->IPin_iface.lpVtbl = &test_connect_direct_vtbl;
2206 pin->ref = 1;
2207 pin->dir = dir;
2210 static void test_connect_direct(void)
2212 struct testpin source_pin, sink_pin;
2213 struct testfilter source, sink;
2215 IFilterGraph2 *graph = create_graph();
2216 AM_MEDIA_TYPE mt;
2217 HRESULT hr;
2219 test_connect_direct_init(&source_pin, PINDIR_OUTPUT);
2220 test_connect_direct_init(&sink_pin, PINDIR_INPUT);
2221 testfilter_init(&source, &source_pin, 1);
2222 testfilter_init(&sink, &sink_pin, 1);
2224 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2225 ok(hr == S_OK, "Got hr %#x.\n", hr);
2227 hr = IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2228 ok(hr == S_OK, "Got hr %#x.\n", hr);
2230 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2231 ok(hr == S_OK, "Got hr %#x.\n", hr);
2232 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2233 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2234 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2236 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2237 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2238 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2239 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2241 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2242 ok(hr == S_OK, "Got hr %#x.\n", hr);
2243 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2244 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2246 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2247 ok(hr == S_OK, "Got hr %#x.\n", hr);
2248 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2249 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2250 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2252 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2253 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2254 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2255 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2257 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2258 ok(hr == S_OK, "Got hr %#x.\n", hr);
2259 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2260 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2262 /* Swap the pins when connecting. */
2263 hr = IFilterGraph2_ConnectDirect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface, NULL);
2264 ok(hr == S_OK, "Got hr %#x.\n", hr);
2265 todo_wine
2266 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2267 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2268 todo_wine
2269 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2271 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2272 todo_wine
2273 ok(hr == S_OK, "Got hr %#x.\n", hr);
2274 todo_wine
2275 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2276 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2278 hr = IFilterGraph2_Connect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface);
2279 ok(hr == S_OK, "Got hr %#x.\n", hr);
2280 todo_wine
2281 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2282 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2283 todo_wine
2284 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2286 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2287 todo_wine
2288 ok(hr == S_OK, "Got hr %#x.\n", hr);
2289 todo_wine
2290 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2291 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2293 /* Disconnect() does not disconnect the peer. */
2294 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2295 ok(hr == S_OK, "Got hr %#x.\n", hr);
2296 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2297 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2298 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2300 sink_pin.peer = &source_pin.IPin_iface;
2301 IPin_AddRef(sink_pin.peer);
2303 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2304 ok(hr == S_OK, "Got hr %#x.\n", hr);
2305 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2306 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2308 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2309 ok(hr == S_OK, "Got hr %#x.\n", hr);
2310 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2311 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2313 /* Test specifying the media type. */
2314 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2315 ok(hr == S_OK, "Got hr %#x.\n", hr);
2316 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2317 ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
2318 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2320 /* Both pins are disconnected when a filter is removed. */
2321 sink_pin.peer = &source_pin.IPin_iface;
2322 IPin_AddRef(sink_pin.peer);
2323 hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
2324 ok(hr == S_OK, "Got hr %#x.\n", hr);
2325 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2326 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2328 /* Or when the graph is destroyed. */
2329 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2330 ok(hr == S_OK, "Got hr %#x.\n", hr);
2332 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2333 ok(hr == S_OK, "Got hr %#x.\n", hr);
2334 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2335 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2336 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2337 IPin_AddRef(sink_pin.peer = &source_pin.IPin_iface);
2339 hr = IFilterGraph2_Release(graph);
2340 ok(!hr, "Got outstanding refcount %d.\n", hr);
2341 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
2342 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
2343 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
2344 todo_wine
2345 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
2346 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2347 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2350 START_TEST(filtergraph)
2352 CoInitializeEx(NULL, COINIT_MULTITHREADED);
2354 test_interfaces();
2355 test_render_run(avifile);
2356 test_render_run(mpegfile);
2357 test_enum_filters();
2358 test_graph_builder_render();
2359 test_graph_builder_connect();
2360 test_aggregate_filter_graph();
2361 test_control_delegation();
2362 test_add_remove_filter();
2363 test_connect_direct();
2365 CoUninitialize();
2366 test_render_with_multithread();