quartz: Correct return value handling in IMediaSeeking::GetDuration().
[wine.git] / dlls / quartz / tests / filtergraph.c
blobb6e68f3f6b91b7af497095e1d841fcc6659d89ac
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 static const GUID testguid = {0xabbccdde};
31 typedef struct TestFilterImpl
33 IBaseFilter IBaseFilter_iface;
35 LONG refCount;
36 CRITICAL_SECTION csFilter;
37 FILTER_STATE state;
38 FILTER_INFO filterInfo;
39 CLSID clsid;
40 IPin **ppPins;
41 UINT nPins;
42 } TestFilterImpl;
44 static BOOL compare_time(ULONGLONG x, ULONGLONG y, unsigned int max_diff)
46 ULONGLONG diff = x > y ? x - y : y - x;
47 return diff <= max_diff;
50 static WCHAR *create_file(const WCHAR *name, const char *data, DWORD size)
52 static WCHAR pathW[MAX_PATH];
53 DWORD written;
54 HANDLE file;
56 GetTempPathW(ARRAY_SIZE(pathW), pathW);
57 wcscat(pathW, name);
58 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
59 ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %u.\n",
60 wine_dbgstr_w(pathW), GetLastError());
61 WriteFile(file, data, size, &written, NULL);
62 ok(written = size, "Failed to write file data, error %u.\n", GetLastError());
63 CloseHandle(file);
65 return pathW;
68 static WCHAR *load_resource(const WCHAR *name)
70 HRSRC res;
71 void *ptr;
73 res = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA);
74 ok(!!res, "Failed to find resource %s, error %u.\n", wine_dbgstr_w(name), GetLastError());
75 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
76 return create_file(name, ptr, SizeofResource(GetModuleHandleA(NULL), res));
79 static IFilterGraph2 *create_graph(void)
81 IFilterGraph2 *ret;
82 HRESULT hr;
83 hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IFilterGraph2, (void **)&ret);
84 ok(hr == S_OK, "Failed to create FilterGraph: %#x\n", hr);
85 return ret;
88 static ULONG get_refcount(void *iface)
90 IUnknown *unknown = iface;
91 IUnknown_AddRef(unknown);
92 return IUnknown_Release(unknown);
95 #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c)
96 static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported)
98 IUnknown *iface = iface_ptr;
99 HRESULT hr, expected_hr;
100 IUnknown *unk;
102 expected_hr = supported ? S_OK : E_NOINTERFACE;
104 hr = IUnknown_QueryInterface(iface, iid, (void **)&unk);
105 ok_(__FILE__, line)(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
106 if (SUCCEEDED(hr))
107 IUnknown_Release(unk);
110 static void test_interfaces(void)
112 IFilterGraph2 *graph = create_graph();
114 check_interface(graph, &IID_IBasicAudio, TRUE);
115 check_interface(graph, &IID_IBasicVideo2, TRUE);
116 check_interface(graph, &IID_IFilterGraph2, TRUE);
117 check_interface(graph, &IID_IFilterMapper, TRUE);
118 check_interface(graph, &IID_IFilterMapper3, TRUE);
119 check_interface(graph, &IID_IGraphConfig, TRUE);
120 check_interface(graph, &IID_IGraphVersion, TRUE);
121 check_interface(graph, &IID_IMediaControl, TRUE);
122 check_interface(graph, &IID_IMediaEvent, TRUE);
123 check_interface(graph, &IID_IMediaFilter, TRUE);
124 check_interface(graph, &IID_IMediaEventSink, TRUE);
125 check_interface(graph, &IID_IMediaPosition, TRUE);
126 check_interface(graph, &IID_IMediaSeeking, TRUE);
127 check_interface(graph, &IID_IObjectWithSite, TRUE);
128 check_interface(graph, &IID_IVideoFrameStep, TRUE);
129 check_interface(graph, &IID_IVideoWindow, TRUE);
131 check_interface(graph, &IID_IBaseFilter, FALSE);
133 IFilterGraph2_Release(graph);
136 static void test_basic_video(IFilterGraph2 *graph)
138 IBasicVideo* pbv;
139 LONG video_width, video_height, window_width;
140 LONG left, top, width, height;
141 HRESULT hr;
143 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo, (void **)&pbv);
144 ok(hr==S_OK, "Cannot get IBasicVideo interface returned: %x\n", hr);
146 /* test get video size */
147 hr = IBasicVideo_GetVideoSize(pbv, NULL, NULL);
148 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
149 hr = IBasicVideo_GetVideoSize(pbv, &video_width, NULL);
150 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
151 hr = IBasicVideo_GetVideoSize(pbv, NULL, &video_height);
152 ok(hr==E_POINTER, "IBasicVideo_GetVideoSize returned: %x\n", hr);
153 hr = IBasicVideo_GetVideoSize(pbv, &video_width, &video_height);
154 ok(hr==S_OK, "Cannot get video size returned: %x\n", hr);
156 /* test source position */
157 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, NULL, NULL);
158 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
159 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, NULL, NULL);
160 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
161 hr = IBasicVideo_GetSourcePosition(pbv, NULL, NULL, &width, &height);
162 ok(hr == E_POINTER, "IBasicVideo_GetSourcePosition returned: %x\n", hr);
163 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
164 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
165 ok(left == 0, "expected 0, got %d\n", left);
166 ok(top == 0, "expected 0, got %d\n", top);
167 ok(width == video_width, "expected %d, got %d\n", video_width, width);
168 ok(height == video_height, "expected %d, got %d\n", video_height, height);
170 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, 0, 0);
171 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
172 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width*2, video_height*2);
173 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
174 hr = IBasicVideo_put_SourceTop(pbv, -1);
175 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
176 hr = IBasicVideo_put_SourceTop(pbv, 0);
177 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
178 hr = IBasicVideo_put_SourceTop(pbv, 1);
179 ok(hr==E_INVALIDARG, "IBasicVideo_put_SourceTop returned: %x\n", hr);
181 hr = IBasicVideo_SetSourcePosition(pbv, video_width, 0, video_width, video_height);
182 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
183 hr = IBasicVideo_SetSourcePosition(pbv, 0, video_height, video_width, video_height);
184 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
185 hr = IBasicVideo_SetSourcePosition(pbv, -1, 0, video_width, video_height);
186 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
187 hr = IBasicVideo_SetSourcePosition(pbv, 0, -1, video_width, video_height);
188 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
189 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
190 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
191 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width, video_height);
192 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
194 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width, video_height+1);
195 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
196 hr = IBasicVideo_SetSourcePosition(pbv, 0, 0, video_width+1, video_height);
197 ok(hr==E_INVALIDARG, "IBasicVideo_SetSourcePosition returned: %x\n", hr);
199 hr = IBasicVideo_SetSourcePosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
200 ok(hr==S_OK, "Cannot set source position returned: %x\n", hr);
202 hr = IBasicVideo_get_SourceLeft(pbv, &left);
203 ok(hr==S_OK, "Cannot get source left returned: %x\n", hr);
204 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
205 hr = IBasicVideo_get_SourceTop(pbv, &top);
206 ok(hr==S_OK, "Cannot get source top returned: %x\n", hr);
207 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
208 hr = IBasicVideo_get_SourceWidth(pbv, &width);
209 ok(hr==S_OK, "Cannot get source width returned: %x\n", hr);
210 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
211 hr = IBasicVideo_get_SourceHeight(pbv, &height);
212 ok(hr==S_OK, "Cannot get source height returned: %x\n", hr);
213 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
215 hr = IBasicVideo_put_SourceLeft(pbv, video_width/3);
216 ok(hr==S_OK, "Cannot put source left returned: %x\n", hr);
217 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
218 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
219 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
220 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
222 hr = IBasicVideo_put_SourceTop(pbv, video_height/3);
223 ok(hr==S_OK, "Cannot put source top returned: %x\n", hr);
224 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
225 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
226 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
227 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
229 hr = IBasicVideo_put_SourceWidth(pbv, video_width/4+1);
230 ok(hr==S_OK, "Cannot put source width returned: %x\n", hr);
231 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
232 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
233 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
234 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
236 hr = IBasicVideo_put_SourceHeight(pbv, video_height/4+1);
237 ok(hr==S_OK, "Cannot put source height returned: %x\n", hr);
238 hr = IBasicVideo_GetSourcePosition(pbv, &left, &top, &width, &height);
239 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
240 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
241 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
243 /* test destination rectangle */
244 window_width = max(video_width, GetSystemMetrics(SM_CXMIN) - 2 * GetSystemMetrics(SM_CXFRAME));
246 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, NULL, NULL);
247 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
248 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, NULL, NULL);
249 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
250 hr = IBasicVideo_GetDestinationPosition(pbv, NULL, NULL, &width, &height);
251 ok(hr == E_POINTER, "IBasicVideo_GetDestinationPosition returned: %x\n", hr);
252 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
253 ok(hr == S_OK, "Cannot get destination position returned: %x\n", hr);
254 ok(left == 0, "expected 0, got %d\n", left);
255 ok(top == 0, "expected 0, got %d\n", top);
256 ok(width == window_width, "expected %d, got %d\n", window_width, width);
257 ok(height == video_height, "expected %d, got %d\n", video_height, height);
259 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, 0, 0);
260 ok(hr==E_INVALIDARG, "IBasicVideo_SetDestinationPosition returned: %x\n", hr);
261 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width*2, video_height*2);
262 ok(hr==S_OK, "Cannot put destination position returned: %x\n", hr);
264 hr = IBasicVideo_put_DestinationLeft(pbv, -1);
265 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
266 hr = IBasicVideo_put_DestinationLeft(pbv, 0);
267 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
268 hr = IBasicVideo_put_DestinationLeft(pbv, 1);
269 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
271 hr = IBasicVideo_SetDestinationPosition(pbv, video_width, 0, video_width, video_height);
272 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
273 hr = IBasicVideo_SetDestinationPosition(pbv, 0, video_height, video_width, video_height);
274 ok(hr==S_OK, "Cannot set destinaiton position returned: %x\n", hr);
275 hr = IBasicVideo_SetDestinationPosition(pbv, -1, 0, video_width, video_height);
276 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
277 hr = IBasicVideo_SetDestinationPosition(pbv, 0, -1, video_width, video_height);
278 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
279 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
280 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
281 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width, video_height);
282 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
284 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height+1);
285 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
286 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width+1, video_height);
287 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
289 hr = IBasicVideo_SetDestinationPosition(pbv, video_width/2, video_height/2, video_width/3+1, video_height/3+1);
290 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
292 hr = IBasicVideo_get_DestinationLeft(pbv, &left);
293 ok(hr==S_OK, "Cannot get destination left returned: %x\n", hr);
294 ok(left==video_width/2, "expected %d, got %d\n", video_width/2, left);
295 hr = IBasicVideo_get_DestinationTop(pbv, &top);
296 ok(hr==S_OK, "Cannot get destination top returned: %x\n", hr);
297 ok(top==video_height/2, "expected %d, got %d\n", video_height/2, top);
298 hr = IBasicVideo_get_DestinationWidth(pbv, &width);
299 ok(hr==S_OK, "Cannot get destination width returned: %x\n", hr);
300 ok(width==video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
301 hr = IBasicVideo_get_DestinationHeight(pbv, &height);
302 ok(hr==S_OK, "Cannot get destination height returned: %x\n", hr);
303 ok(height==video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
305 hr = IBasicVideo_put_DestinationLeft(pbv, video_width/3);
306 ok(hr==S_OK, "Cannot put destination left returned: %x\n", hr);
307 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
308 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
309 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
310 ok(width == video_width/3+1, "expected %d, got %d\n", video_width/3+1, width);
312 hr = IBasicVideo_put_DestinationTop(pbv, video_height/3);
313 ok(hr==S_OK, "Cannot put destination top returned: %x\n", hr);
314 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
315 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
316 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
317 ok(height == video_height/3+1, "expected %d, got %d\n", video_height/3+1, height);
319 hr = IBasicVideo_put_DestinationWidth(pbv, video_width/4+1);
320 ok(hr==S_OK, "Cannot put destination width returned: %x\n", hr);
321 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
322 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
323 ok(left == video_width/3, "expected %d, got %d\n", video_width/3, left);
324 ok(width == video_width/4+1, "expected %d, got %d\n", video_width/4+1, width);
326 hr = IBasicVideo_put_DestinationHeight(pbv, video_height/4+1);
327 ok(hr==S_OK, "Cannot put destination height returned: %x\n", hr);
328 hr = IBasicVideo_GetDestinationPosition(pbv, &left, &top, &width, &height);
329 ok(hr == S_OK, "Cannot get source position returned: %x\n", hr);
330 ok(top == video_height/3, "expected %d, got %d\n", video_height/3, top);
331 ok(height == video_height/4+1, "expected %d, got %d\n", video_height/4+1, height);
333 /* reset source rectangle */
334 hr = IBasicVideo_SetDefaultSourcePosition(pbv);
335 ok(hr==S_OK, "IBasicVideo_SetDefaultSourcePosition returned: %x\n", hr);
337 /* reset destination position */
338 hr = IBasicVideo_SetDestinationPosition(pbv, 0, 0, video_width, video_height);
339 ok(hr==S_OK, "Cannot set destination position returned: %x\n", hr);
341 IBasicVideo_Release(pbv);
344 static void test_media_seeking(IFilterGraph2 *graph)
346 IMediaSeeking *seeking;
347 IMediaFilter *filter;
348 LONGLONG pos, stop, duration;
349 GUID format;
350 HRESULT hr;
352 IFilterGraph2_SetDefaultSyncSource(graph);
353 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
354 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %08x\n", hr);
356 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
357 ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %08x\n", hr);
359 format = GUID_NULL;
360 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
361 ok(hr == S_OK, "GetTimeFormat failed: %#x\n", hr);
362 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "got %s\n", wine_dbgstr_guid(&format));
364 pos = 0xdeadbeef;
365 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, NULL);
366 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
367 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
369 pos = 0xdeadbeef;
370 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
371 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
372 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
374 pos = 0xdeadbeef;
375 hr = IMediaSeeking_ConvertTimeFormat(seeking, &pos, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
376 ok(hr == S_OK, "ConvertTimeFormat failed: %#x\n", hr);
377 ok(pos == 0x123456789a, "got %s\n", wine_dbgstr_longlong(pos));
379 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
380 ok(hr == S_OK, "GetCurrentPosition failed: %#x\n", hr);
381 ok(pos == 0, "got %s\n", wine_dbgstr_longlong(pos));
383 hr = IMediaSeeking_GetDuration(seeking, &duration);
384 ok(hr == S_OK, "GetDuration failed: %#x\n", hr);
385 ok(duration > 0, "got %s\n", wine_dbgstr_longlong(duration));
387 hr = IMediaSeeking_GetStopPosition(seeking, &stop);
388 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
389 ok(stop == duration || stop == duration + 1, "expected %s, got %s\n",
390 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(stop));
392 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_ReturnTime, NULL, AM_SEEKING_NoPositioning);
393 ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
394 hr = IMediaSeeking_SetPositions(seeking, NULL, AM_SEEKING_NoPositioning, NULL, AM_SEEKING_ReturnTime);
395 ok(hr == S_OK, "SetPositions failed: %#x\n", hr);
397 pos = 0;
398 hr = IMediaSeeking_SetPositions(seeking, &pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
399 ok(hr == S_OK, "SetPositions failed: %08x\n", hr);
401 IMediaFilter_SetSyncSource(filter, NULL);
402 pos = 0xdeadbeef;
403 hr = IMediaSeeking_GetCurrentPosition(seeking, &pos);
404 ok(hr == S_OK, "GetCurrentPosition failed: %08x\n", hr);
405 ok(pos == 0, "Position != 0 (%s)\n", wine_dbgstr_longlong(pos));
406 IFilterGraph2_SetDefaultSyncSource(graph);
408 IMediaSeeking_Release(seeking);
409 IMediaFilter_Release(filter);
412 static void test_state_change(IFilterGraph2 *graph)
414 IMediaControl *control;
415 OAFilterState state;
416 HRESULT hr;
418 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
419 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %x\n", hr);
421 hr = IMediaControl_GetState(control, 1000, &state);
422 ok(hr == S_OK, "GetState() failed: %x\n", hr);
423 ok(state == State_Stopped, "wrong state %d\n", state);
425 hr = IMediaControl_Run(control);
426 ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
427 hr = IMediaControl_GetState(control, INFINITE, &state);
428 ok(SUCCEEDED(hr), "GetState() failed: %x\n", hr);
429 ok(state == State_Running, "wrong state %d\n", state);
431 hr = IMediaControl_Stop(control);
432 ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
433 hr = IMediaControl_GetState(control, 1000, &state);
434 ok(hr == S_OK, "GetState() failed: %x\n", hr);
435 ok(state == State_Stopped, "wrong state %d\n", state);
437 hr = IMediaControl_Pause(control);
438 ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
439 hr = IMediaControl_GetState(control, 1000, &state);
440 ok(hr == S_OK, "GetState() failed: %x\n", hr);
441 ok(state == State_Paused, "wrong state %d\n", state);
443 hr = IMediaControl_Run(control);
444 ok(SUCCEEDED(hr), "Run() failed: %x\n", hr);
445 hr = IMediaControl_GetState(control, 1000, &state);
446 ok(hr == S_OK, "GetState() failed: %x\n", hr);
447 ok(state == State_Running, "wrong state %d\n", state);
449 hr = IMediaControl_Pause(control);
450 ok(SUCCEEDED(hr), "Pause() failed: %x\n", hr);
451 hr = IMediaControl_GetState(control, 1000, &state);
452 ok(hr == S_OK, "GetState() failed: %x\n", hr);
453 ok(state == State_Paused, "wrong state %d\n", state);
455 hr = IMediaControl_Stop(control);
456 ok(SUCCEEDED(hr), "Stop() failed: %x\n", hr);
457 hr = IMediaControl_GetState(control, 1000, &state);
458 ok(hr == S_OK, "GetState() failed: %x\n", hr);
459 ok(state == State_Stopped, "wrong state %d\n", state);
461 IMediaControl_Release(control);
464 static void test_media_event(IFilterGraph2 *graph)
466 IMediaEvent *media_event;
467 IMediaSeeking *seeking;
468 IMediaControl *control;
469 IMediaFilter *filter;
470 LONG_PTR lparam1, lparam2;
471 LONGLONG current, stop;
472 OAFilterState state;
473 int got_eos = 0;
474 HANDLE event;
475 HRESULT hr;
476 LONG code;
478 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
479 ok(hr == S_OK, "QueryInterface(IMediaFilter) failed: %#x\n", hr);
481 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
482 ok(hr == S_OK, "QueryInterface(IMediaControl) failed: %#x\n", hr);
484 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&media_event);
485 ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
487 hr = IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
488 ok(hr == S_OK, "QueryInterface(IMediaEvent) failed: %#x\n", hr);
490 hr = IMediaControl_Stop(control);
491 ok(SUCCEEDED(hr), "Stop() failed: %#x\n", hr);
492 hr = IMediaControl_GetState(control, 1000, &state);
493 ok(hr == S_OK, "GetState() timed out\n");
495 hr = IMediaSeeking_GetDuration(seeking, &stop);
496 ok(hr == S_OK, "GetDuration() failed: %#x\n", hr);
497 current = 0;
498 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning, &stop, AM_SEEKING_AbsolutePositioning);
499 ok(hr == S_OK, "SetPositions() failed: %#x\n", hr);
501 hr = IMediaFilter_SetSyncSource(filter, NULL);
502 ok(hr == S_OK, "SetSyncSource() failed: %#x\n", hr);
504 hr = IMediaEvent_GetEventHandle(media_event, (OAEVENT *)&event);
505 ok(hr == S_OK, "GetEventHandle() failed: %#x\n", hr);
507 /* flush existing events */
508 while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK);
510 ok(WaitForSingleObject(event, 0) == WAIT_TIMEOUT, "event should not be signaled\n");
512 hr = IMediaControl_Run(control);
513 ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
515 while (!got_eos)
517 if (WaitForSingleObject(event, 5000) == WAIT_TIMEOUT)
518 break;
520 while ((hr = IMediaEvent_GetEvent(media_event, &code, &lparam1, &lparam2, 0)) == S_OK)
522 if (code == EC_COMPLETE)
524 got_eos = 1;
525 break;
529 ok(got_eos, "didn't get EOS\n");
531 hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
532 ok(hr == S_OK, "GetCurrentPosition() failed: %#x\n", hr);
533 ok(current == stop, "expected %s, got %s\n", wine_dbgstr_longlong(stop), wine_dbgstr_longlong(current));
535 hr = IMediaControl_Stop(control);
536 ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
537 hr = IMediaControl_GetState(control, 1000, &state);
538 ok(hr == S_OK, "GetState() timed out\n");
540 hr = IFilterGraph2_SetDefaultSyncSource(graph);
541 ok(hr == S_OK, "SetDefaultSinkSource() failed: %#x\n", hr);
543 IMediaSeeking_Release(seeking);
544 IMediaEvent_Release(media_event);
545 IMediaControl_Release(control);
546 IMediaFilter_Release(filter);
549 static void rungraph(IFilterGraph2 *graph, BOOL video)
551 if (video)
552 test_basic_video(graph);
553 test_media_seeking(graph);
554 test_state_change(graph);
555 test_media_event(graph);
558 static HRESULT test_graph_builder_connect_file(WCHAR *filename, BOOL audio, BOOL video)
560 IBaseFilter *source_filter, *renderer;
561 IPin *pin_in, *pin_out;
562 IFilterGraph2 *graph;
563 IEnumPins *enumpins;
564 HRESULT hr;
566 if (video)
567 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
568 &IID_IBaseFilter, (void **)&renderer);
569 else
570 hr = CoCreateInstance(&CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER,
571 &IID_IBaseFilter, (void **)&renderer);
572 if (hr == VFW_E_NO_AUDIO_HARDWARE)
573 return VFW_E_CANNOT_CONNECT;
574 ok(hr == S_OK, "Got hr %#x.\n", hr);
576 graph = create_graph();
578 IBaseFilter_EnumPins(renderer, &enumpins);
579 IEnumPins_Next(enumpins, 1, &pin_in, NULL);
580 IEnumPins_Release(enumpins);
582 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &source_filter);
583 ok(hr == S_OK, "AddSourceFilter failed: %#x\n", hr);
585 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
586 ok(hr == S_OK, "AddFilter failed: %#x\n", hr);
588 hr = IBaseFilter_FindPin(source_filter, L"Output", &pin_out);
589 ok(hr == S_OK, "FindPin failed: %#x\n", hr);
590 hr = IFilterGraph2_Connect(graph, pin_out, pin_in);
592 if (SUCCEEDED(hr))
593 rungraph(graph, video);
595 IPin_Release(pin_in);
596 IPin_Release(pin_out);
597 IBaseFilter_Release(source_filter);
598 IBaseFilter_Release(renderer);
599 IFilterGraph2_Release(graph);
601 return hr;
604 static void test_render_run(const WCHAR *file, BOOL audio, BOOL video)
606 IFilterGraph2 *graph;
607 HANDLE h;
608 HRESULT hr;
609 LONG refs;
610 WCHAR *filename = load_resource(file);
612 h = CreateFileW(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
613 if (h == INVALID_HANDLE_VALUE) {
614 skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(file));
615 DeleteFileW(filename);
616 return;
618 CloseHandle(h);
620 trace("running %s\n", wine_dbgstr_w(file));
622 graph = create_graph();
624 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
625 if (FAILED(hr))
627 skip("%s: codec not supported; skipping test\n", wine_dbgstr_w(file));
629 refs = IFilterGraph2_Release(graph);
630 ok(!refs, "Graph has %u references\n", refs);
632 hr = test_graph_builder_connect_file(filename, audio, video);
633 ok(hr == VFW_E_CANNOT_CONNECT, "got %#x\n", hr);
635 else
637 if (audio)
638 ok(hr == S_OK || hr == VFW_S_AUDIO_NOT_RENDERED, "Got hr %#x.\n", hr);
639 else
640 ok(hr == S_OK, "Got hr %#x.\n", hr);
641 rungraph(graph, video);
643 refs = IFilterGraph2_Release(graph);
644 ok(!refs, "Graph has %u references\n", refs);
646 hr = test_graph_builder_connect_file(filename, audio, video);
647 if (audio && video)
648 todo_wine ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
649 else
650 ok(hr == S_OK, "Got hr %#x.\n", hr);
653 /* check reference leaks */
654 h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
655 ok(h != INVALID_HANDLE_VALUE, "CreateFile failed: err=%d\n", GetLastError());
656 CloseHandle(h);
658 DeleteFileW(filename);
661 static void test_enum_filters(void)
663 IBaseFilter *filter1, *filter2, *filters[2];
664 IFilterGraph2 *graph = create_graph();
665 IEnumFilters *enum1, *enum2;
666 ULONG count, ref;
667 HRESULT hr;
669 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
670 &IID_IBaseFilter, (void **)&filter1);
671 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
672 &IID_IBaseFilter, (void **)&filter2);
674 hr = IFilterGraph2_EnumFilters(graph, &enum1);
675 ok(hr == S_OK, "Got hr %#x.\n", hr);
677 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
678 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
680 IFilterGraph2_AddFilter(graph, filter1, NULL);
681 IFilterGraph2_AddFilter(graph, filter2, NULL);
683 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
684 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
686 hr = IEnumFilters_Reset(enum1);
687 ok(hr == S_OK, "Got hr %#x.\n", hr);
689 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
690 ok(hr == S_OK, "Got hr %#x.\n", hr);
691 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
692 IBaseFilter_Release(filters[0]);
694 hr = IEnumFilters_Next(enum1, 1, filters, &count);
695 ok(hr == S_OK, "Got hr %#x.\n", hr);
696 ok(count == 1, "Got count %u.\n", count);
697 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
698 IBaseFilter_Release(filters[0]);
700 hr = IEnumFilters_Next(enum1, 1, filters, &count);
701 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
702 ok(count == 0, "Got count %u.\n", count);
704 hr = IEnumFilters_Reset(enum1);
705 ok(hr == S_OK, "Got hr %#x.\n", hr);
707 hr = IEnumFilters_Next(enum1, 2, filters, &count);
708 ok(hr == S_OK, "Got hr %#x.\n", hr);
709 ok(count == 2, "Got count %u.\n", count);
710 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
711 ok(filters[1] == filter1, "Got filter %p.\n", filters[1]);
712 IBaseFilter_Release(filters[0]);
713 IBaseFilter_Release(filters[1]);
715 IFilterGraph2_RemoveFilter(graph, filter1);
716 IFilterGraph2_AddFilter(graph, filter1, NULL);
718 hr = IEnumFilters_Next(enum1, 2, filters, &count);
719 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
721 hr = IEnumFilters_Reset(enum1);
722 ok(hr == S_OK, "Got hr %#x.\n", hr);
724 hr = IEnumFilters_Next(enum1, 2, filters, &count);
725 ok(hr == S_OK, "Got hr %#x.\n", hr);
726 ok(count == 2, "Got count %u.\n", count);
727 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
728 ok(filters[1] == filter2, "Got filter %p.\n", filters[1]);
729 IBaseFilter_Release(filters[0]);
730 IBaseFilter_Release(filters[1]);
732 hr = IEnumFilters_Reset(enum1);
733 ok(hr == S_OK, "Got hr %#x.\n", hr);
735 hr = IEnumFilters_Clone(enum1, &enum2);
736 ok(hr == S_OK, "Got hr %#x.\n", hr);
738 hr = IEnumFilters_Skip(enum2, 1);
739 ok(hr == S_OK, "Got hr %#x.\n", hr);
741 hr = IEnumFilters_Next(enum2, 2, filters, &count);
742 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
743 ok(count == 1, "Got count %u.\n", count);
744 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
745 IBaseFilter_Release(filters[0]);
747 hr = IEnumFilters_Skip(enum1, 3);
748 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
750 IEnumFilters_Release(enum2);
751 IEnumFilters_Release(enum1);
752 ref = IFilterGraph2_Release(graph);
753 ok(!ref, "Got outstanding refcount %d.\n", ref);
756 static DWORD WINAPI call_RenderFile_multithread(LPVOID lParam)
758 WCHAR *filename = load_resource(L"test.avi");
759 IFilterGraph2 *graph = lParam;
760 HRESULT hr;
762 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
763 ok(hr == S_OK, "Got hr %#x.\n", hr);
765 rungraph(graph, TRUE);
767 return 0;
770 static void test_render_with_multithread(void)
772 IFilterGraph2 *graph;
773 HANDLE thread;
775 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
777 graph = create_graph();
779 thread = CreateThread(NULL, 0, call_RenderFile_multithread, graph, 0, NULL);
781 ok(!WaitForSingleObject(thread, 10000), "Wait timed out.\n");
782 IFilterGraph2_Release(graph);
783 CloseHandle(thread);
784 CoUninitialize();
787 struct testpin
789 IPin IPin_iface;
790 LONG ref;
791 PIN_DIRECTION dir;
792 struct testfilter *filter;
793 IPin *peer;
794 AM_MEDIA_TYPE *mt;
795 WCHAR name[10];
796 WCHAR id[10];
798 IEnumMediaTypes IEnumMediaTypes_iface;
799 const AM_MEDIA_TYPE *types;
800 unsigned int type_count, enum_idx;
801 AM_MEDIA_TYPE *request_mt, *accept_mt;
802 const struct testpin *require_connected_pin;
804 BOOL require_stopped_disconnect;
806 HRESULT Connect_hr;
807 HRESULT EnumMediaTypes_hr;
808 HRESULT QueryInternalConnections_hr;
811 struct testfilter
813 IBaseFilter IBaseFilter_iface;
814 LONG ref;
815 IFilterGraph *graph;
816 WCHAR *name;
817 IReferenceClock *clock;
819 IEnumPins IEnumPins_iface;
820 struct testpin *pins;
821 unsigned int pin_count, enum_idx;
823 FILTER_STATE state;
824 REFERENCE_TIME start_time;
825 HRESULT state_hr, GetState_hr, seek_hr;
826 FILTER_STATE expect_stop_prev, expect_run_prev;
828 IAMFilterMiscFlags IAMFilterMiscFlags_iface;
829 ULONG misc_flags;
831 IMediaSeeking IMediaSeeking_iface;
832 LONG seeking_ref;
833 DWORD seek_caps;
834 BOOL support_testguid, support_media_time;
835 GUID time_format;
836 LONGLONG seek_duration, seek_current, seek_stop;
837 double seek_rate;
839 IReferenceClock IReferenceClock_iface;
841 IFileSourceFilter IFileSourceFilter_iface;
842 WCHAR filename[MAX_PATH];
845 static inline struct testpin *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
847 return CONTAINING_RECORD(iface, struct testpin, IEnumMediaTypes_iface);
850 static HRESULT WINAPI testenummt_QueryInterface(IEnumMediaTypes *iface, REFIID iid, void **out)
852 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
853 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
855 *out = NULL;
856 return E_NOINTERFACE;
859 static ULONG WINAPI testenummt_AddRef(IEnumMediaTypes *iface)
861 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
862 return InterlockedIncrement(&pin->ref);
865 static ULONG WINAPI testenummt_Release(IEnumMediaTypes *iface)
867 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
868 return InterlockedDecrement(&pin->ref);
871 static HRESULT WINAPI testenummt_Next(IEnumMediaTypes *iface, ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
873 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
874 unsigned int i;
876 for (i = 0; i < count; ++i)
878 if (pin->enum_idx + i >= pin->type_count)
879 break;
881 out[i] = CoTaskMemAlloc(sizeof(*out[i]));
882 *out[i] = pin->types[pin->enum_idx + i];
885 if (fetched)
886 *fetched = i;
887 pin->enum_idx += i;
889 return (i == count) ? S_OK : S_FALSE;
892 static HRESULT WINAPI testenummt_Skip(IEnumMediaTypes *iface, ULONG count)
894 ok(0, "Unexpected call.\n");
895 return E_NOTIMPL;
898 static HRESULT WINAPI testenummt_Reset(IEnumMediaTypes *iface)
900 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
901 pin->enum_idx = 0;
902 return S_OK;
905 static HRESULT WINAPI testenummt_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **out)
907 ok(0, "Unexpected call.\n");
908 return E_NOTIMPL;
911 static const IEnumMediaTypesVtbl testenummt_vtbl =
913 testenummt_QueryInterface,
914 testenummt_AddRef,
915 testenummt_Release,
916 testenummt_Next,
917 testenummt_Skip,
918 testenummt_Reset,
919 testenummt_Clone,
922 static inline struct testpin *impl_from_IPin(IPin *iface)
924 return CONTAINING_RECORD(iface, struct testpin, IPin_iface);
927 static HRESULT WINAPI testpin_QueryInterface(IPin *iface, REFIID iid, void **out)
929 struct testpin *pin = impl_from_IPin(iface);
930 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
932 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IPin))
934 *out = &pin->IPin_iface;
935 IPin_AddRef(*out);
936 return S_OK;
939 *out = NULL;
940 return E_NOINTERFACE;
943 static ULONG WINAPI testpin_AddRef(IPin *iface)
945 struct testpin *pin = impl_from_IPin(iface);
946 return InterlockedIncrement(&pin->ref);
949 static ULONG WINAPI testpin_Release(IPin *iface)
951 struct testpin *pin = impl_from_IPin(iface);
952 return InterlockedDecrement(&pin->ref);
955 static HRESULT WINAPI testpin_Disconnect(IPin *iface)
957 struct testpin *pin = impl_from_IPin(iface);
958 if (winetest_debug > 1) trace("%p->Disconnect()\n", pin);
960 if (!pin->peer)
961 return S_FALSE;
963 if (pin->require_stopped_disconnect && pin->filter->state != State_Stopped)
964 return VFW_E_NOT_STOPPED;
966 IPin_Release(pin->peer);
967 pin->peer = NULL;
968 return S_OK;
971 static HRESULT WINAPI testpin_ConnectedTo(IPin *iface, IPin **peer)
973 struct testpin *pin = impl_from_IPin(iface);
974 if (winetest_debug > 1) trace("%p->ConnectedTo()\n", pin);
976 *peer = pin->peer;
977 if (*peer)
979 IPin_AddRef(*peer);
980 return S_OK;
982 return VFW_E_NOT_CONNECTED;
985 static HRESULT WINAPI testpin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mt)
987 ok(0, "Unexpected call.\n");
988 return E_NOTIMPL;
991 static HRESULT WINAPI testpin_QueryPinInfo(IPin *iface, PIN_INFO *info)
993 struct testpin *pin = impl_from_IPin(iface);
994 if (winetest_debug > 1) trace("%p->QueryPinInfo()\n", pin);
996 info->pFilter = &pin->filter->IBaseFilter_iface;
997 IBaseFilter_AddRef(info->pFilter);
998 info->dir = pin->dir;
999 wcscpy(info->achName, pin->name);
1000 return S_OK;
1004 static HRESULT WINAPI testpin_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
1006 struct testpin *pin = impl_from_IPin(iface);
1007 if (winetest_debug > 1) trace("%p->QueryDirection()\n", pin);
1009 *dir = pin->dir;
1010 return S_OK;
1013 static HRESULT WINAPI testpin_QueryId(IPin *iface, WCHAR **id)
1015 struct testpin *pin = impl_from_IPin(iface);
1016 if (winetest_debug > 1) trace("%p->QueryId()\n", iface);
1017 *id = CoTaskMemAlloc(11);
1018 wcscpy(*id, pin->id);
1019 return S_OK;
1022 static HRESULT WINAPI testpin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mt)
1024 ok(0, "Unexpected call.\n");
1025 return E_NOTIMPL;
1028 static HRESULT WINAPI testpin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
1030 struct testpin *pin = impl_from_IPin(iface);
1031 if (winetest_debug > 1) trace("%p->EnumMediaTypes()\n", pin);
1033 if (FAILED(pin->EnumMediaTypes_hr))
1034 return pin->EnumMediaTypes_hr;
1036 *out = &pin->IEnumMediaTypes_iface;
1037 IEnumMediaTypes_AddRef(*out);
1038 pin->enum_idx = 0;
1039 return S_OK;
1042 static HRESULT WINAPI testpin_QueryInternalConnections(IPin *iface, IPin **out, ULONG *count)
1044 struct testpin *pin = impl_from_IPin(iface);
1045 if (winetest_debug > 1) trace("%p->QueryInternalConnections()\n", pin);
1047 *count = 0;
1048 return pin->QueryInternalConnections_hr;
1051 static HRESULT WINAPI testpin_BeginFlush(IPin *iface)
1053 ok(0, "Unexpected call.\n");
1054 return E_NOTIMPL;
1057 static HRESULT WINAPI testpin_EndFlush(IPin * iface)
1059 ok(0, "Unexpected call.\n");
1060 return E_NOTIMPL;
1063 static HRESULT WINAPI testpin_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
1065 ok(0, "Unexpected call.\n");
1066 return E_NOTIMPL;
1069 static HRESULT WINAPI testpin_EndOfStream(IPin *iface)
1071 ok(0, "Unexpected call.\n");
1072 return E_NOTIMPL;
1075 static HRESULT WINAPI no_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1077 ok(0, "Unexpected call.\n");
1078 return E_NOTIMPL;
1081 static HRESULT WINAPI no_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1083 ok(0, "Unexpected call.\n");
1084 return E_NOTIMPL;
1087 static HRESULT WINAPI no_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
1089 ok(0, "Unexpected call.\n");
1090 return E_NOTIMPL;
1093 static HRESULT WINAPI testsink_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1095 struct testpin *pin = impl_from_IPin(iface);
1096 if (winetest_debug > 1) trace("%p->ReceiveConnection(%p)\n", pin, peer);
1098 if (pin->accept_mt && memcmp(pin->accept_mt, mt, sizeof(*mt)))
1099 return VFW_E_TYPE_NOT_ACCEPTED;
1101 if (pin->require_connected_pin && !pin->require_connected_pin->peer)
1102 return VFW_E_TYPE_NOT_ACCEPTED;
1104 pin->peer = peer;
1105 IPin_AddRef(peer);
1106 return S_OK;
1109 static const IPinVtbl testsink_vtbl =
1111 testpin_QueryInterface,
1112 testpin_AddRef,
1113 testpin_Release,
1114 no_Connect,
1115 testsink_ReceiveConnection,
1116 testpin_Disconnect,
1117 testpin_ConnectedTo,
1118 testpin_ConnectionMediaType,
1119 testpin_QueryPinInfo,
1120 testpin_QueryDirection,
1121 testpin_QueryId,
1122 testpin_QueryAccept,
1123 no_EnumMediaTypes,
1124 testpin_QueryInternalConnections,
1125 testpin_EndOfStream,
1126 testpin_BeginFlush,
1127 testpin_EndFlush,
1128 testpin_NewSegment
1131 static void testpin_init(struct testpin *pin, const IPinVtbl *vtbl, PIN_DIRECTION dir)
1133 memset(pin, 0, sizeof(*pin));
1134 pin->IPin_iface.lpVtbl = vtbl;
1135 pin->IEnumMediaTypes_iface.lpVtbl = &testenummt_vtbl;
1136 pin->ref = 1;
1137 pin->dir = dir;
1138 pin->Connect_hr = S_OK;
1139 pin->EnumMediaTypes_hr = S_OK;
1140 pin->QueryInternalConnections_hr = E_NOTIMPL;
1143 static void testsink_init(struct testpin *pin)
1145 testpin_init(pin, &testsink_vtbl, PINDIR_INPUT);
1148 static HRESULT WINAPI testsource_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1150 struct testpin *pin = impl_from_IPin(iface);
1151 HRESULT hr;
1152 if (winetest_debug > 1) trace("%p->Connect(%p)\n", pin, peer);
1154 if (FAILED(pin->Connect_hr))
1155 return pin->Connect_hr;
1157 if (pin->require_connected_pin && !pin->require_connected_pin->peer)
1158 return VFW_E_NO_ACCEPTABLE_TYPES;
1160 ok(!mt, "Got media type %p.\n", mt);
1162 if (SUCCEEDED(hr = IPin_ReceiveConnection(peer, &pin->IPin_iface, pin->request_mt)))
1164 pin->peer = peer;
1165 IPin_AddRef(peer);
1166 return pin->Connect_hr;
1168 return hr;
1171 static const IPinVtbl testsource_vtbl =
1173 testpin_QueryInterface,
1174 testpin_AddRef,
1175 testpin_Release,
1176 testsource_Connect,
1177 no_ReceiveConnection,
1178 testpin_Disconnect,
1179 testpin_ConnectedTo,
1180 testpin_ConnectionMediaType,
1181 testpin_QueryPinInfo,
1182 testpin_QueryDirection,
1183 testpin_QueryId,
1184 testpin_QueryAccept,
1185 testpin_EnumMediaTypes,
1186 testpin_QueryInternalConnections,
1187 testpin_EndOfStream,
1188 testpin_BeginFlush,
1189 testpin_EndFlush,
1190 testpin_NewSegment
1193 static void testsource_init(struct testpin *pin, const AM_MEDIA_TYPE *types, int type_count)
1195 testpin_init(pin, &testsource_vtbl, PINDIR_OUTPUT);
1196 pin->types = types;
1197 pin->type_count = type_count;
1200 static inline struct testfilter *impl_from_IEnumPins(IEnumPins *iface)
1202 return CONTAINING_RECORD(iface, struct testfilter, IEnumPins_iface);
1205 static HRESULT WINAPI testenumpins_QueryInterface(IEnumPins *iface, REFIID iid, void **out)
1207 ok(0, "Unexpected iid %s.\n", wine_dbgstr_guid(iid));
1208 return E_NOINTERFACE;
1211 static ULONG WINAPI testenumpins_AddRef(IEnumPins * iface)
1213 struct testfilter *filter = impl_from_IEnumPins(iface);
1214 return InterlockedIncrement(&filter->ref);
1217 static ULONG WINAPI testenumpins_Release(IEnumPins * iface)
1219 struct testfilter *filter = impl_from_IEnumPins(iface);
1220 return InterlockedDecrement(&filter->ref);
1223 static HRESULT WINAPI testenumpins_Next(IEnumPins *iface, ULONG count, IPin **out, ULONG *fetched)
1225 struct testfilter *filter = impl_from_IEnumPins(iface);
1226 unsigned int i;
1228 for (i = 0; i < count; ++i)
1230 if (filter->enum_idx + i >= filter->pin_count)
1231 break;
1233 out[i] = &filter->pins[filter->enum_idx + i].IPin_iface;
1234 IPin_AddRef(out[i]);
1237 if (fetched)
1238 *fetched = i;
1239 filter->enum_idx += i;
1241 return (i == count) ? S_OK : S_FALSE;
1244 static HRESULT WINAPI testenumpins_Skip(IEnumPins *iface, ULONG count)
1246 ok(0, "Unexpected call.\n");
1247 return E_NOTIMPL;
1250 static HRESULT WINAPI testenumpins_Reset(IEnumPins *iface)
1252 struct testfilter *filter = impl_from_IEnumPins(iface);
1253 filter->enum_idx = 0;
1254 return S_OK;
1257 static HRESULT WINAPI testenumpins_Clone(IEnumPins *iface, IEnumPins **out)
1259 ok(0, "Unexpected call.\n");
1260 return E_NOTIMPL;
1263 static const IEnumPinsVtbl testenumpins_vtbl =
1265 testenumpins_QueryInterface,
1266 testenumpins_AddRef,
1267 testenumpins_Release,
1268 testenumpins_Next,
1269 testenumpins_Skip,
1270 testenumpins_Reset,
1271 testenumpins_Clone,
1274 static inline struct testfilter *impl_from_IBaseFilter(IBaseFilter *iface)
1276 return CONTAINING_RECORD(iface, struct testfilter, IBaseFilter_iface);
1279 static HRESULT WINAPI testfilter_QueryInterface(IBaseFilter *iface, REFIID iid, void **out)
1281 struct testfilter *filter = impl_from_IBaseFilter(iface);
1282 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", filter, wine_dbgstr_guid(iid));
1284 if (IsEqualGUID(iid, &IID_IUnknown)
1285 || IsEqualGUID(iid, &IID_IPersist)
1286 || IsEqualGUID(iid, &IID_IMediaFilter)
1287 || IsEqualGUID(iid, &IID_IBaseFilter))
1289 *out = &filter->IBaseFilter_iface;
1291 else if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags) && filter->IAMFilterMiscFlags_iface.lpVtbl)
1293 *out = &filter->IAMFilterMiscFlags_iface;
1295 else if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl)
1297 *out = &filter->IMediaSeeking_iface;
1299 else if (IsEqualGUID(iid, &IID_IReferenceClock) && filter->IReferenceClock_iface.lpVtbl)
1301 *out = &filter->IReferenceClock_iface;
1303 else if (IsEqualGUID(iid, &IID_IFileSourceFilter) && filter->IFileSourceFilter_iface.lpVtbl)
1305 *out = &filter->IFileSourceFilter_iface;
1307 else
1309 *out = NULL;
1310 return E_NOINTERFACE;
1313 IUnknown_AddRef((IUnknown *)*out);
1314 return S_OK;
1317 static ULONG WINAPI testfilter_AddRef(IBaseFilter *iface)
1319 struct testfilter *filter = impl_from_IBaseFilter(iface);
1320 return InterlockedIncrement(&filter->ref);
1323 static ULONG WINAPI testfilter_Release(IBaseFilter *iface)
1325 struct testfilter *filter = impl_from_IBaseFilter(iface);
1326 return InterlockedDecrement(&filter->ref);
1329 static HRESULT WINAPI testfilter_GetClassID(IBaseFilter *iface, CLSID *clsid)
1331 if (winetest_debug > 1) trace("%p->GetClassID()\n", iface);
1332 memset(clsid, 0xde, sizeof(*clsid));
1333 return S_OK;
1336 /* Downstream filters are always stopped before any filters they are connected
1337 * to upstream. Native actually implements this by topologically sorting filters
1338 * as they are connected. */
1339 static void check_state_transition(struct testfilter *filter, FILTER_STATE expect)
1341 FILTER_STATE state;
1342 unsigned int i;
1343 PIN_INFO info;
1345 for (i = 0; i < filter->pin_count; ++i)
1347 if (filter->pins[i].peer)
1349 IPin_QueryPinInfo(filter->pins[i].peer, &info);
1350 IBaseFilter_GetState(info.pFilter, 0, &state);
1351 if (filter->pins[i].dir == PINDIR_OUTPUT)
1352 ok(state == expect, "Expected state %d for downstream filter %p, got %d.\n",
1353 expect, info.pFilter, state);
1354 else
1355 ok(state == filter->state, "Expected state %d for upstream filter %p, got %d.\n",
1356 filter->state, info.pFilter, state);
1357 IBaseFilter_Release(info.pFilter);
1362 static HRESULT WINAPI testfilter_Stop(IBaseFilter *iface)
1364 struct testfilter *filter = impl_from_IBaseFilter(iface);
1365 if (winetest_debug > 1) trace("%p->Stop()\n", filter);
1367 todo_wine_if (filter->expect_stop_prev == State_Running)
1368 ok(filter->state == filter->expect_stop_prev, "Expected previous state %#x, got %#x.\n",
1369 filter->expect_stop_prev, filter->state);
1371 check_state_transition(filter, State_Stopped);
1373 filter->state = State_Stopped;
1374 return filter->state_hr;
1377 static HRESULT WINAPI testfilter_Pause(IBaseFilter *iface)
1379 struct testfilter *filter = impl_from_IBaseFilter(iface);
1380 if (winetest_debug > 1) trace("%p->Pause()\n", filter);
1382 check_state_transition(filter, State_Paused);
1384 filter->state = State_Paused;
1385 return filter->state_hr;
1388 static HRESULT WINAPI testfilter_Run(IBaseFilter *iface, REFERENCE_TIME start)
1390 struct testfilter *filter = impl_from_IBaseFilter(iface);
1391 if (winetest_debug > 1) trace("%p->Run(%s)\n", filter, wine_dbgstr_longlong(start));
1393 ok(filter->state == filter->expect_run_prev, "Expected previous state %#x, got %#x.\n",
1394 filter->expect_run_prev, filter->state);
1396 check_state_transition(filter, State_Running);
1398 filter->state = State_Running;
1399 filter->start_time = start;
1400 return filter->state_hr;
1403 static HRESULT WINAPI testfilter_GetState(IBaseFilter *iface, DWORD timeout, FILTER_STATE *state)
1405 struct testfilter *filter = impl_from_IBaseFilter(iface);
1406 if (winetest_debug > 1) trace("%p->GetState(%u)\n", filter, timeout);
1408 *state = filter->state;
1409 return filter->GetState_hr;
1412 static HRESULT WINAPI testfilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
1414 struct testfilter *filter = impl_from_IBaseFilter(iface);
1415 if (winetest_debug > 1) trace("%p->SetSyncSource(%p)\n", filter, clock);
1417 if (filter->clock)
1418 IReferenceClock_Release(filter->clock);
1419 if (clock)
1420 IReferenceClock_AddRef(clock);
1421 filter->clock = clock;
1422 return S_OK;
1425 static HRESULT WINAPI testfilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock)
1427 ok(0, "Unexpected call.\n");
1428 return E_NOTIMPL;
1431 static HRESULT WINAPI testfilter_EnumPins(IBaseFilter *iface, IEnumPins **out)
1433 struct testfilter *filter = impl_from_IBaseFilter(iface);
1434 if (winetest_debug > 1) trace("%p->EnumPins()\n", filter);
1436 *out = &filter->IEnumPins_iface;
1437 IEnumPins_AddRef(*out);
1438 filter->enum_idx = 0;
1439 return S_OK;
1442 static HRESULT WINAPI testfilter_FindPin(IBaseFilter *iface, const WCHAR *id, IPin **pin)
1444 ok(0, "Unexpected call.\n");
1445 return E_NOTIMPL;
1448 static HRESULT WINAPI testfilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info)
1450 struct testfilter *filter = impl_from_IBaseFilter(iface);
1451 if (winetest_debug > 1) trace("%p->QueryFilterInfo()\n", filter);
1453 info->pGraph = filter->graph;
1454 if (filter->graph)
1455 IFilterGraph_AddRef(filter->graph);
1456 if (filter->name)
1457 wcscpy(info->achName, filter->name);
1458 else
1459 info->achName[0] = 0;
1460 return S_OK;
1463 static HRESULT WINAPI testfilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, const WCHAR *name)
1465 struct testfilter *filter = impl_from_IBaseFilter(iface);
1466 if (winetest_debug > 1) trace("%p->JoinFilterGraph(%p, %s)\n", filter, graph, wine_dbgstr_w(name));
1468 filter->graph = graph;
1469 heap_free(filter->name);
1470 if (name)
1472 filter->name = heap_alloc((wcslen(name) + 1) * sizeof(WCHAR));
1473 wcscpy(filter->name, name);
1475 else
1476 filter->name = NULL;
1477 return S_OK;
1480 static HRESULT WINAPI testfilter_QueryVendorInfo(IBaseFilter * iface, WCHAR **info)
1482 return E_NOTIMPL;
1485 static const IBaseFilterVtbl testfilter_vtbl =
1487 testfilter_QueryInterface,
1488 testfilter_AddRef,
1489 testfilter_Release,
1490 testfilter_GetClassID,
1491 testfilter_Stop,
1492 testfilter_Pause,
1493 testfilter_Run,
1494 testfilter_GetState,
1495 testfilter_SetSyncSource,
1496 testfilter_GetSyncSource,
1497 testfilter_EnumPins,
1498 testfilter_FindPin,
1499 testfilter_QueryFilterInfo,
1500 testfilter_JoinFilterGraph,
1501 testfilter_QueryVendorInfo
1504 static struct testfilter *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
1506 return CONTAINING_RECORD(iface, struct testfilter, IAMFilterMiscFlags_iface);
1509 static HRESULT WINAPI testmiscflags_QueryInterface(IAMFilterMiscFlags *iface, REFIID iid, void **out)
1511 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1512 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1515 static ULONG WINAPI testmiscflags_AddRef(IAMFilterMiscFlags *iface)
1517 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1518 return InterlockedIncrement(&filter->ref);
1521 static ULONG WINAPI testmiscflags_Release(IAMFilterMiscFlags *iface)
1523 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1524 return InterlockedDecrement(&filter->ref);
1527 static ULONG WINAPI testmiscflags_GetMiscFlags(IAMFilterMiscFlags *iface)
1529 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1530 if (winetest_debug > 1) trace("%p->GetMiscFlags()\n", filter);
1531 return filter->misc_flags;
1534 static const IAMFilterMiscFlagsVtbl testmiscflags_vtbl =
1536 testmiscflags_QueryInterface,
1537 testmiscflags_AddRef,
1538 testmiscflags_Release,
1539 testmiscflags_GetMiscFlags,
1542 static struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface)
1544 return CONTAINING_RECORD(iface, struct testfilter, IMediaSeeking_iface);
1547 static HRESULT WINAPI testseek_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out)
1549 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1550 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1553 static ULONG WINAPI testseek_AddRef(IMediaSeeking *iface)
1555 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1556 InterlockedIncrement(&filter->seeking_ref);
1557 return InterlockedIncrement(&filter->ref);
1560 static ULONG WINAPI testseek_Release(IMediaSeeking *iface)
1562 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1563 InterlockedDecrement(&filter->seeking_ref);
1564 return InterlockedDecrement(&filter->ref);
1567 static HRESULT WINAPI testseek_GetCapabilities(IMediaSeeking *iface, DWORD *caps)
1569 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1570 if (winetest_debug > 1) trace("%p->GetCapabilities()\n", iface);
1571 *caps = filter->seek_caps;
1572 return S_OK;
1575 static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *caps)
1577 ok(0, "Unexpected call.\n");
1578 return E_NOTIMPL;
1581 static HRESULT WINAPI testseek_IsFormatSupported(IMediaSeeking *iface, const GUID *format)
1583 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1584 if (winetest_debug > 1) trace("%p->IsFormatSupported(%s)\n", iface, wine_dbgstr_guid(format));
1585 if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
1586 return S_FALSE;
1587 if (IsEqualGUID(format, &TIME_FORMAT_MEDIA_TIME) && !filter->support_media_time)
1588 return S_FALSE;
1589 return S_OK;
1592 static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *format)
1594 if (winetest_debug > 1) trace("%p->QueryPreferredFormat()\n", iface);
1595 return E_NOTIMPL;
1598 static HRESULT WINAPI testseek_GetTimeFormat(IMediaSeeking *iface, GUID *format)
1600 ok(0, "Unexpected call.\n");
1601 return E_NOTIMPL;
1604 static HRESULT WINAPI testseek_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format)
1606 if (winetest_debug > 1) trace("%p->IsUsingTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
1607 return S_OK;
1610 static HRESULT WINAPI testseek_SetTimeFormat(IMediaSeeking *iface, const GUID *format)
1612 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1613 if (winetest_debug > 1) trace("%p->SetTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
1614 ok(!IsEqualGUID(format, &GUID_NULL), "Got unexpected GUID_NULL.\n");
1615 if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
1616 return E_INVALIDARG;
1617 filter->time_format = *format;
1618 return S_OK;
1621 static HRESULT WINAPI testseek_GetDuration(IMediaSeeking *iface, LONGLONG *duration)
1623 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1624 if (winetest_debug > 1) trace("%p->GetDuration()\n", iface);
1625 *duration = filter->seek_duration;
1626 return filter->seek_hr;
1629 static HRESULT WINAPI testseek_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop)
1631 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1632 if (winetest_debug > 1) trace("%p->GetStopPosition()\n", iface);
1633 *stop = filter->seek_stop;
1634 return filter->seek_hr;
1637 static HRESULT WINAPI testseek_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current)
1639 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1640 if (winetest_debug > 1) trace("%p->GetCurrentPosition()\n", iface);
1641 ok(!filter->clock, "GetCurrentPosition() should only be called if there is no sync source.\n");
1642 *current = 0xdeadbeef;
1643 return S_OK;
1646 static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target,
1647 const GUID *target_format, LONGLONG source, const GUID *source_format)
1649 ok(0, "Unexpected call.\n");
1650 return E_NOTIMPL;
1653 static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *current,
1654 DWORD current_flags, LONGLONG *stop, DWORD stop_flags )
1656 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1657 if (winetest_debug > 1) trace("%p->SetPositions(%s, %#x, %s, %#x)\n",
1658 iface, wine_dbgstr_longlong(*current), current_flags, wine_dbgstr_longlong(*stop), stop_flags);
1659 ok(filter->state != State_Running, "Filter should be paused or stopped while seeking.\n");
1660 filter->seek_current = *current;
1661 filter->seek_stop = *stop;
1662 *current = 12340000;
1663 *stop = 43210000;
1664 return filter->seek_hr;
1667 static HRESULT WINAPI testseek_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop)
1669 ok(0, "Unexpected call.\n");
1670 return E_NOTIMPL;
1673 static HRESULT WINAPI testseek_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest)
1675 ok(0, "Unexpected call.\n");
1676 return E_NOTIMPL;
1679 static HRESULT WINAPI testseek_SetRate(IMediaSeeking *iface, double rate)
1681 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1682 if (winetest_debug > 1) trace("%p->SetRate(%.16e)\n", iface, rate);
1683 filter->seek_rate = rate;
1684 return S_OK;
1687 static HRESULT WINAPI testseek_GetRate(IMediaSeeking *iface, double *rate)
1689 ok(0, "Unexpected call.\n");
1690 return E_NOTIMPL;
1693 static HRESULT WINAPI testseek_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll)
1695 if (winetest_debug > 1) trace("%p->GetPreroll()\n", iface);
1696 return E_NOTIMPL;
1699 static const IMediaSeekingVtbl testseek_vtbl =
1701 testseek_QueryInterface,
1702 testseek_AddRef,
1703 testseek_Release,
1704 testseek_GetCapabilities,
1705 testseek_CheckCapabilities,
1706 testseek_IsFormatSupported,
1707 testseek_QueryPreferredFormat,
1708 testseek_GetTimeFormat,
1709 testseek_IsUsingTimeFormat,
1710 testseek_SetTimeFormat,
1711 testseek_GetDuration,
1712 testseek_GetStopPosition,
1713 testseek_GetCurrentPosition,
1714 testseek_ConvertTimeFormat,
1715 testseek_SetPositions,
1716 testseek_GetPositions,
1717 testseek_GetAvailable,
1718 testseek_SetRate,
1719 testseek_GetRate,
1720 testseek_GetPreroll,
1723 static struct testfilter *impl_from_IReferenceClock(IReferenceClock *iface)
1725 return CONTAINING_RECORD(iface, struct testfilter, IReferenceClock_iface);
1728 static HRESULT WINAPI testclock_QueryInterface(IReferenceClock *iface, REFIID iid, void **out)
1730 struct testfilter *filter = impl_from_IReferenceClock(iface);
1731 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1734 static ULONG WINAPI testclock_AddRef(IReferenceClock *iface)
1736 struct testfilter *filter = impl_from_IReferenceClock(iface);
1737 return InterlockedIncrement(&filter->ref);
1740 static ULONG WINAPI testclock_Release(IReferenceClock *iface)
1742 struct testfilter *filter = impl_from_IReferenceClock(iface);
1743 return InterlockedDecrement(&filter->ref);
1746 static HRESULT WINAPI testclock_GetTime(IReferenceClock *iface, REFERENCE_TIME *time)
1748 ok(0, "Unexpected call.\n");
1749 return E_NOTIMPL;
1752 static HRESULT WINAPI testclock_AdviseTime(IReferenceClock *iface,
1753 REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
1755 ok(0, "Unexpected call.\n");
1756 return E_NOTIMPL;
1759 static HRESULT WINAPI testclock_AdvisePeriodic(IReferenceClock *iface,
1760 REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie)
1762 ok(0, "Unexpected call.\n");
1763 return E_NOTIMPL;
1766 static HRESULT WINAPI testclock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
1768 ok(0, "Unexpected call.\n");
1769 return E_NOTIMPL;
1772 static const IReferenceClockVtbl testclock_vtbl =
1774 testclock_QueryInterface,
1775 testclock_AddRef,
1776 testclock_Release,
1777 testclock_GetTime,
1778 testclock_AdviseTime,
1779 testclock_AdvisePeriodic,
1780 testclock_Unadvise,
1783 static struct testfilter *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
1785 return CONTAINING_RECORD(iface, struct testfilter, IFileSourceFilter_iface);
1788 static HRESULT WINAPI testfilesource_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out)
1790 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1791 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1794 static ULONG WINAPI testfilesource_AddRef(IFileSourceFilter *iface)
1796 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1797 return InterlockedIncrement(&filter->ref);
1800 static ULONG WINAPI testfilesource_Release(IFileSourceFilter *iface)
1802 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1803 return InterlockedDecrement(&filter->ref);
1806 static HRESULT WINAPI testfilesource_Load(IFileSourceFilter *iface,
1807 const WCHAR *filename, const AM_MEDIA_TYPE *mt)
1809 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1810 if (winetest_debug > 1) trace("%p->Load()\n", iface);
1812 wcscpy(filter->filename, filename);
1814 return S_OK;
1817 static HRESULT WINAPI testfilesource_GetCurFile(IFileSourceFilter *iface,
1818 WCHAR **filename, AM_MEDIA_TYPE *mt)
1820 ok(0, "Unexpected call.\n");
1821 return E_NOTIMPL;
1824 static const IFileSourceFilterVtbl testfilesource_vtbl =
1826 testfilesource_QueryInterface,
1827 testfilesource_AddRef,
1828 testfilesource_Release,
1829 testfilesource_Load,
1830 testfilesource_GetCurFile,
1833 struct testfilter_cf
1835 IClassFactory IClassFactory_iface;
1836 struct testfilter *filter;
1839 static void testfilter_init(struct testfilter *filter, struct testpin *pins, int pin_count)
1841 unsigned int i;
1843 memset(filter, 0, sizeof(*filter));
1844 filter->IBaseFilter_iface.lpVtbl = &testfilter_vtbl;
1845 filter->IEnumPins_iface.lpVtbl = &testenumpins_vtbl;
1846 filter->ref = 1;
1847 filter->pins = pins;
1848 filter->pin_count = pin_count;
1849 for (i = 0; i < pin_count; i++)
1850 pins[i].filter = filter;
1852 filter->state = State_Stopped;
1853 filter->expect_stop_prev = filter->expect_run_prev = State_Paused;
1854 filter->support_media_time = TRUE;
1855 filter->time_format = TIME_FORMAT_MEDIA_TIME;
1858 static HRESULT WINAPI testfilter_cf_QueryInterface(IClassFactory *iface, REFIID iid, void **out)
1860 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory))
1862 *out = iface;
1863 return S_OK;
1866 *out = NULL;
1867 return E_NOINTERFACE;
1870 static ULONG WINAPI testfilter_cf_AddRef(IClassFactory *iface)
1872 return 2;
1875 static ULONG WINAPI testfilter_cf_Release(IClassFactory *iface)
1877 return 1;
1880 static HRESULT WINAPI testfilter_cf_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **out)
1882 struct testfilter_cf *factory = CONTAINING_RECORD(iface, struct testfilter_cf, IClassFactory_iface);
1884 return IBaseFilter_QueryInterface(&factory->filter->IBaseFilter_iface, iid, out);
1887 static HRESULT WINAPI testfilter_cf_LockServer(IClassFactory *iface, BOOL lock)
1889 return E_NOTIMPL;
1892 static IClassFactoryVtbl testfilter_cf_vtbl =
1894 testfilter_cf_QueryInterface,
1895 testfilter_cf_AddRef,
1896 testfilter_cf_Release,
1897 testfilter_cf_CreateInstance,
1898 testfilter_cf_LockServer,
1901 static void test_graph_builder_render(void)
1903 static const GUID sink1_clsid = {0x12345678};
1904 static const GUID sink2_clsid = {0x87654321};
1905 AM_MEDIA_TYPE source_type = {{0}};
1906 struct testpin source_pin, sink1_pin, sink2_pin, parser_pins[2];
1907 struct testfilter source, sink1, sink2, parser;
1908 struct testfilter_cf sink1_cf = { {&testfilter_cf_vtbl}, &sink1 };
1909 struct testfilter_cf sink2_cf = { {&testfilter_cf_vtbl}, &sink2 };
1911 IFilterGraph2 *graph = create_graph();
1912 REGFILTERPINS2 regpins = {0};
1913 REGPINTYPES regtypes = {0};
1914 REGFILTER2 regfilter = {0};
1915 IFilterMapper2 *mapper;
1916 DWORD cookie1, cookie2;
1917 HRESULT hr;
1918 ULONG ref;
1920 memset(&source_type.majortype, 0xcc, sizeof(GUID));
1921 testsource_init(&source_pin, &source_type, 1);
1922 testfilter_init(&source, &source_pin, 1);
1923 testsink_init(&sink1_pin);
1924 testfilter_init(&sink1, &sink1_pin, 1);
1925 testsink_init(&sink2_pin);
1926 testfilter_init(&sink2, &sink2_pin, 1);
1927 testsink_init(&parser_pins[0]);
1928 testsource_init(&parser_pins[1], &source_type, 1);
1929 testfilter_init(&parser, parser_pins, 2);
1931 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1932 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1933 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
1935 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1936 ok(hr == S_OK, "Got hr %#x.\n", hr);
1937 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1938 IFilterGraph2_Disconnect(graph, source_pin.peer);
1939 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1941 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1942 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1944 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1945 ok(hr == S_OK, "Got hr %#x.\n", hr);
1946 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1948 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1949 IFilterGraph2_Disconnect(graph, &sink1_pin.IPin_iface);
1951 /* No preference is given to smaller chains. */
1953 IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
1955 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1956 ok(hr == S_OK, "Got hr %#x.\n", hr);
1957 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1958 ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
1959 IFilterGraph2_Disconnect(graph, source_pin.peer);
1960 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1961 IFilterGraph2_Disconnect(graph, parser_pins[0].peer);
1962 IFilterGraph2_Disconnect(graph, &parser_pins[0].IPin_iface);
1964 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1965 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1967 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1968 ok(hr == S_OK, "Got hr %#x.\n", hr);
1969 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1970 IFilterGraph2_Disconnect(graph, source_pin.peer);
1971 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1973 /* A pin whose name (not ID) begins with a tilde is not rendered. */
1975 IFilterGraph2_RemoveFilter(graph, &sink2.IBaseFilter_iface);
1976 IFilterGraph2_RemoveFilter(graph, &parser.IBaseFilter_iface);
1977 IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
1979 parser_pins[1].name[0] = '~';
1980 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1981 ok(hr == S_OK, "Got hr %#x.\n", hr);
1982 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1983 ok(!parser_pins[1].peer, "Got peer %p.\n", parser_pins[1].peer);
1984 ok(!sink1_pin.peer, "Got peer %p.\n", sink1_pin.peer);
1985 IFilterGraph2_Disconnect(graph, source_pin.peer);
1986 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1988 parser_pins[1].name[0] = 0;
1989 parser_pins[1].id[0] = '~';
1990 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1991 ok(hr == S_OK, "Got hr %#x.\n", hr);
1992 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1993 ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
1994 IFilterGraph2_Disconnect(graph, source_pin.peer);
1995 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1997 ref = IFilterGraph2_Release(graph);
1998 ok(!ref, "Got outstanding refcount %d.\n", ref);
2000 /* Test enumeration of filters from the registry. */
2002 CoRegisterClassObject(&sink1_clsid, (IUnknown *)&sink1_cf.IClassFactory_iface,
2003 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
2004 CoRegisterClassObject(&sink2_clsid, (IUnknown *)&sink2_cf.IClassFactory_iface,
2005 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
2007 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
2008 &IID_IFilterMapper2, (void **)&mapper);
2010 regfilter.dwVersion = 2;
2011 regfilter.dwMerit = MERIT_UNLIKELY;
2012 regfilter.cPins2 = 1;
2013 regfilter.rgPins2 = &regpins;
2014 regpins.dwFlags = 0;
2015 regpins.cInstances = 1;
2016 regpins.nMediaTypes = 1;
2017 regpins.lpMediaType = &regtypes;
2018 regtypes.clsMajorType = &source_type.majortype;
2019 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
2020 hr = IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2021 if (hr == E_ACCESSDENIED)
2023 skip("Not enough permission to register filters.\n");
2024 goto out;
2026 ok(hr == S_OK, "Got hr %#x.\n", hr);
2028 graph = create_graph();
2029 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2031 regpins.dwFlags = REG_PINFLAG_B_RENDERER;
2032 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2034 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2035 ok(hr == S_OK, "Got hr %#x.\n", hr);
2036 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
2037 "Got peer %p.\n", source_pin.peer);
2039 ref = IFilterGraph2_Release(graph);
2040 ok(!ref, "Got outstanding refcount %d.\n", ref);
2042 /* Preference is given to filters already in the graph. */
2044 graph = create_graph();
2045 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2046 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
2048 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2049 ok(hr == S_OK, "Got hr %#x.\n", hr);
2050 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2052 ref = IFilterGraph2_Release(graph);
2053 ok(!ref, "Got outstanding refcount %d.\n", ref);
2055 /* No preference is given to renderer filters. */
2057 graph = create_graph();
2058 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2060 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2061 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2063 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2064 regpins.dwFlags = 0;
2065 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2067 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2068 ok(hr == S_OK, "Got hr %#x.\n", hr);
2069 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
2070 "Got peer %p.\n", source_pin.peer);
2072 ref = IFilterGraph2_Release(graph);
2073 ok(!ref, "Got outstanding refcount %d.\n", ref);
2075 /* Preference is given to filters with higher merit. */
2077 graph = create_graph();
2078 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2080 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2081 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2083 regfilter.dwMerit = MERIT_UNLIKELY;
2084 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2085 regfilter.dwMerit = MERIT_PREFERRED;
2086 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2088 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2089 ok(hr == S_OK, "Got hr %#x.\n", hr);
2090 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2092 ref = IFilterGraph2_Release(graph);
2093 ok(!ref, "Got outstanding refcount %d.\n", ref);
2095 graph = create_graph();
2096 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2098 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2099 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2101 regfilter.dwMerit = MERIT_PREFERRED;
2102 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2103 regfilter.dwMerit = MERIT_UNLIKELY;
2104 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2106 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2107 ok(hr == S_OK, "Got hr %#x.\n", hr);
2108 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2110 ref = IFilterGraph2_Release(graph);
2111 ok(!ref, "Got outstanding refcount %d.\n", ref);
2113 /* Test AM_RENDEREX_RENDERTOEXISTINGRENDERERS. */
2115 graph = create_graph();
2116 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2118 hr = IFilterGraph2_RenderEx(graph, &source_pin.IPin_iface, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
2119 ok(hr == VFW_E_CANNOT_RENDER, "Got hr %#x.\n", hr);
2121 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
2123 hr = IFilterGraph2_RenderEx(graph, &source_pin.IPin_iface, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
2124 ok(hr == S_OK, "Got hr %#x.\n", hr);
2125 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2127 ref = IFilterGraph2_Release(graph);
2128 ok(!ref, "Got outstanding refcount %d.\n", ref);
2130 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2131 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2133 out:
2134 CoRevokeClassObject(cookie1);
2135 CoRevokeClassObject(cookie2);
2136 IFilterMapper2_Release(mapper);
2137 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
2138 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
2139 ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
2140 ok(sink1_pin.ref == 1, "Got outstanding refcount %d.\n", sink1_pin.ref);
2141 ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
2142 ok(sink2_pin.ref == 1, "Got outstanding refcount %d.\n", sink2_pin.ref);
2143 ok(parser.ref == 1, "Got outstanding refcount %d.\n", parser.ref);
2144 ok(parser_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser_pins[0].ref);
2145 ok(parser_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser_pins[1].ref);
2148 static void test_graph_builder_connect(void)
2150 static const GUID parser1_clsid = {0x12345678};
2151 static const GUID parser2_clsid = {0x87654321};
2152 AM_MEDIA_TYPE source_types[2] = {{{0}}}, sink_type = {{0}}, parser3_type = {{0}};
2153 struct testpin source_pin, sink_pin, sink2_pin, parser1_pins[3], parser2_pins[2], parser3_pins[2];
2154 struct testfilter source, sink, sink2, parser1, parser2, parser3;
2155 struct testfilter_cf parser1_cf = { {&testfilter_cf_vtbl}, &parser1 };
2156 struct testfilter_cf parser2_cf = { {&testfilter_cf_vtbl}, &parser2 };
2158 IFilterGraph2 *graph = create_graph();
2159 REGFILTERPINS2 regpins[2] = {{0}};
2160 REGPINTYPES regtypes = {0};
2161 REGFILTER2 regfilter = {0};
2162 IFilterMapper2 *mapper;
2163 DWORD cookie1, cookie2;
2164 HRESULT hr;
2165 ULONG ref;
2167 memset(&source_types[0].majortype, 0xcc, sizeof(GUID));
2168 memset(&source_types[1].majortype, 0xdd, sizeof(GUID));
2169 memset(&sink_type.majortype, 0x66, sizeof(GUID));
2170 testsource_init(&source_pin, source_types, 2);
2171 source_pin.request_mt = &source_types[1];
2172 testfilter_init(&source, &source_pin, 1);
2173 testsink_init(&sink_pin);
2174 testfilter_init(&sink, &sink_pin, 1);
2175 testsink_init(&sink2_pin);
2176 testfilter_init(&sink2, &sink2_pin, 1);
2178 testsink_init(&parser1_pins[0]);
2179 testsource_init(&parser1_pins[1], &sink_type, 1);
2180 parser1_pins[1].request_mt = &sink_type;
2181 testsource_init(&parser1_pins[2], &sink_type, 1);
2182 parser1_pins[2].request_mt = &sink_type;
2183 testfilter_init(&parser1, parser1_pins, 3);
2184 parser1.pin_count = 2;
2186 testsource_init(&parser2_pins[0], &sink_type, 1);
2187 testsink_init(&parser2_pins[1]);
2188 parser2_pins[0].request_mt = &sink_type;
2189 testfilter_init(&parser2, parser2_pins, 2);
2191 testsink_init(&parser3_pins[0]);
2192 testsource_init(&parser3_pins[1], &sink_type, 1);
2193 parser3_pins[1].request_mt = &parser3_type;
2194 testfilter_init(&parser3, parser3_pins, 2);
2196 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2197 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2199 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2200 ok(hr == S_OK, "Got hr %#x.\n", hr);
2201 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2202 IFilterGraph2_Disconnect(graph, source_pin.peer);
2203 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2205 for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
2206 ++source_pin.Connect_hr)
2208 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2209 ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
2210 hr, source_pin.Connect_hr);
2211 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2212 IFilterGraph2_Disconnect(graph, source_pin.peer);
2213 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2215 source_pin.Connect_hr = S_OK;
2217 sink_pin.accept_mt = &sink_type;
2218 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2219 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2220 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2222 for (source_pin.Connect_hr = 0x80040200; source_pin.Connect_hr <= 0x800402ff;
2223 ++source_pin.Connect_hr)
2225 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2226 if (source_pin.Connect_hr == VFW_E_NOT_CONNECTED
2227 || source_pin.Connect_hr == VFW_E_NO_AUDIO_HARDWARE)
2228 ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
2229 hr, source_pin.Connect_hr);
2230 else
2231 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x for Connect() hr %#x.\n",
2232 hr, source_pin.Connect_hr);
2233 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2234 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2236 source_pin.Connect_hr = S_OK;
2238 for (source_pin.EnumMediaTypes_hr = 0x80040200; source_pin.EnumMediaTypes_hr <= 0x800402ff;
2239 ++source_pin.EnumMediaTypes_hr)
2241 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2242 ok(hr == source_pin.EnumMediaTypes_hr, "Got hr %#x for EnumMediaTypes() hr %#x.\n",
2243 hr, source_pin.EnumMediaTypes_hr);
2244 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2245 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2247 source_pin.EnumMediaTypes_hr = S_OK;
2249 /* Test usage of intermediate filters. Similarly to Render(), filters are
2250 * simply tried in enumeration order. */
2252 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2253 IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
2255 sink_pin.accept_mt = NULL;
2256 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2257 ok(hr == S_OK, "Got hr %#x.\n", hr);
2258 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2259 IFilterGraph2_Disconnect(graph, source_pin.peer);
2260 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2262 sink_pin.accept_mt = &sink_type;
2263 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2264 ok(hr == S_OK, "Got hr %#x.\n", hr);
2265 ok(source_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", source_pin.peer);
2266 ok(sink_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2267 IFilterGraph2_Disconnect(graph, source_pin.peer);
2268 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2269 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2270 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2272 for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
2273 ++source_pin.Connect_hr)
2275 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2276 ok(hr == S_OK, "Got hr %#x for Connect() hr %#x.\n", hr, source_pin.Connect_hr);
2277 ok(source_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", source_pin.peer);
2278 ok(sink_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2279 IFilterGraph2_Disconnect(graph, source_pin.peer);
2280 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2281 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2282 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2284 source_pin.Connect_hr = S_OK;
2286 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
2287 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2289 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2290 ok(hr == S_OK, "Got hr %#x.\n", hr);
2291 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2292 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2293 IFilterGraph2_Disconnect(graph, source_pin.peer);
2294 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2295 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2296 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2298 /* No preference is given to smaller chains. */
2300 IFilterGraph2_AddFilter(graph, &parser3.IBaseFilter_iface, NULL);
2301 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2302 ok(hr == S_OK, "Got hr %#x.\n", hr);
2303 ok(source_pin.peer == &parser3_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2304 ok(parser3_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser3_pins[1].peer);
2305 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2306 IFilterGraph2_Disconnect(graph, source_pin.peer);
2307 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2308 IFilterGraph2_Disconnect(graph, parser3_pins[0].peer);
2309 IFilterGraph2_Disconnect(graph, &parser3_pins[0].IPin_iface);
2310 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2311 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2313 IFilterGraph2_RemoveFilter(graph, &parser3.IBaseFilter_iface);
2314 IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
2316 /* Extra source pins on an intermediate filter are not rendered. */
2318 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
2319 parser1.pin_count = 3;
2320 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2321 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
2323 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2324 todo_wine
2325 ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
2326 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2327 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2328 ok(!parser1_pins[2].peer, "Got peer %p.\n", parser1_pins[2].peer);
2329 IFilterGraph2_Disconnect(graph, source_pin.peer);
2330 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2331 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2332 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2333 parser1.pin_count = 2;
2335 /* QueryInternalConnections is not used to find output pins. */
2337 parser1_pins[1].QueryInternalConnections_hr = S_OK;
2338 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2339 ok(hr == S_OK, "Got hr %#x.\n", hr);
2340 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2341 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2342 IFilterGraph2_Disconnect(graph, source_pin.peer);
2343 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2344 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2345 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2346 parser1_pins[1].QueryInternalConnections_hr = E_NOTIMPL;
2348 /* A pin whose name (not ID) begins with a tilde is not connected. */
2350 parser1_pins[1].name[0] = '~';
2351 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2352 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2353 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2355 parser1.pin_count = 3;
2356 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2357 ok(hr == S_OK, "Got hr %#x.\n", hr);
2358 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2359 ok(sink_pin.peer == &parser1_pins[2].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2360 IFilterGraph2_Disconnect(graph, source_pin.peer);
2361 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2362 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2363 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2364 parser1.pin_count = 2;
2366 parser1_pins[1].name[0] = 0;
2367 parser1_pins[1].id[0] = '~';
2368 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2369 ok(hr == S_OK, "Got hr %#x.\n", hr);
2370 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2371 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2372 IFilterGraph2_Disconnect(graph, source_pin.peer);
2373 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2374 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2375 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2377 hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
2378 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2380 parser1_pins[0].QueryInternalConnections_hr = S_OK;
2381 hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
2382 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
2383 parser1_pins[0].QueryInternalConnections_hr = E_NOTIMPL;
2385 ref = IFilterGraph2_Release(graph);
2386 ok(!ref, "Got outstanding refcount %d.\n", ref);
2388 /* The graph connects from source to sink, not from sink to source. */
2390 graph = create_graph();
2391 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, L"source");
2392 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, L"parser");
2393 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, L"sink");
2395 parser1_pins[0].require_connected_pin = &parser1_pins[1];
2397 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2398 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2399 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2400 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2402 parser1_pins[0].require_connected_pin = NULL;
2403 parser1_pins[1].require_connected_pin = &parser1_pins[0];
2405 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2406 ok(hr == S_OK, "Got hr %#x.\n", hr);
2407 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2408 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2410 parser1_pins[1].require_connected_pin = NULL;
2412 ref = IFilterGraph2_Release(graph);
2413 ok(!ref, "Got outstanding refcount %d.\n", ref);
2415 /* Test enumeration of filters from the registry. */
2417 graph = create_graph();
2418 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2419 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2421 CoRegisterClassObject(&parser1_clsid, (IUnknown *)&parser1_cf.IClassFactory_iface,
2422 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
2423 CoRegisterClassObject(&parser2_clsid, (IUnknown *)&parser2_cf.IClassFactory_iface,
2424 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
2426 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
2427 &IID_IFilterMapper2, (void **)&mapper);
2429 regfilter.dwVersion = 2;
2430 regfilter.dwMerit = MERIT_UNLIKELY;
2431 regfilter.cPins2 = 2;
2432 regfilter.rgPins2 = regpins;
2433 regpins[0].dwFlags = 0;
2434 regpins[0].cInstances = 1;
2435 regpins[0].nMediaTypes = 1;
2436 regpins[0].lpMediaType = &regtypes;
2437 regpins[1].dwFlags = REG_PINFLAG_B_OUTPUT;
2438 regpins[1].cInstances = 1;
2439 regpins[1].nMediaTypes = 1;
2440 regpins[1].lpMediaType = &regtypes;
2441 regtypes.clsMajorType = &source_types[1].majortype;
2442 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
2443 hr = IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2444 if (hr == E_ACCESSDENIED)
2446 skip("Not enough permission to register filters.\n");
2447 goto out;
2449 ok(hr == S_OK, "Got hr %#x.\n", hr);
2451 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2453 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2454 ok(hr == S_OK, "Got hr %#x.\n", hr);
2455 ok(source_pin.peer == &parser1_pins[0].IPin_iface
2456 || source_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", source_pin.peer);
2457 ok(sink_pin.peer == &parser1_pins[1].IPin_iface
2458 || sink_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2460 ref = IFilterGraph2_Release(graph);
2461 ok(!ref, "Got outstanding refcount %d.\n", ref);
2463 /* Preference is given to filters already in the graph. */
2465 graph = create_graph();
2466 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2467 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2468 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2470 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2471 ok(hr == S_OK, "Got hr %#x.\n", hr);
2472 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2473 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2475 ref = IFilterGraph2_Release(graph);
2476 ok(!ref, "Got outstanding refcount %d.\n", ref);
2478 /* Preference is given to filters with higher merit. */
2480 graph = create_graph();
2481 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2482 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2484 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2485 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2487 regfilter.dwMerit = MERIT_UNLIKELY;
2488 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2489 regfilter.dwMerit = MERIT_PREFERRED;
2490 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2492 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2493 ok(hr == S_OK, "Got hr %#x.\n", hr);
2494 ok(source_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", source_pin.peer);
2495 ok(sink_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2497 ref = IFilterGraph2_Release(graph);
2498 ok(!ref, "Got outstanding refcount %d.\n", ref);
2500 graph = create_graph();
2501 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2502 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2504 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2505 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2507 regfilter.dwMerit = MERIT_PREFERRED;
2508 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2509 regfilter.dwMerit = MERIT_UNLIKELY;
2510 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2512 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2513 ok(hr == S_OK, "Got hr %#x.\n", hr);
2514 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2515 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2517 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2518 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2520 out:
2521 CoRevokeClassObject(cookie1);
2522 CoRevokeClassObject(cookie2);
2523 IFilterMapper2_Release(mapper);
2524 ref = IFilterGraph2_Release(graph);
2525 ok(!ref, "Got outstanding refcount %d.\n", ref);
2526 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
2527 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
2528 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
2529 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
2530 ok(parser1.ref == 1, "Got outstanding refcount %d.\n", parser1.ref);
2531 ok(parser1_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[0].ref);
2532 ok(parser1_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[1].ref);
2533 ok(parser1_pins[2].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[2].ref);
2534 ok(parser2.ref == 1, "Got outstanding refcount %d.\n", parser2.ref);
2535 ok(parser2_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[0].ref);
2536 ok(parser2_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[1].ref);
2537 ok(parser3.ref == 1, "Got outstanding refcount %d.\n", parser3.ref);
2538 ok(parser3_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[0].ref);
2539 ok(parser3_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[1].ref);
2542 static const GUID test_iid = {0x33333333};
2543 static LONG outer_ref = 1;
2545 static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out)
2547 if (IsEqualGUID(iid, &IID_IUnknown)
2548 || IsEqualGUID(iid, &IID_IFilterGraph2)
2549 || IsEqualGUID(iid, &test_iid))
2551 *out = (IUnknown *)0xdeadbeef;
2552 return S_OK;
2554 ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid));
2555 return E_NOINTERFACE;
2558 static ULONG WINAPI outer_AddRef(IUnknown *iface)
2560 return InterlockedIncrement(&outer_ref);
2563 static ULONG WINAPI outer_Release(IUnknown *iface)
2565 return InterlockedDecrement(&outer_ref);
2568 static const IUnknownVtbl outer_vtbl =
2570 outer_QueryInterface,
2571 outer_AddRef,
2572 outer_Release,
2575 static IUnknown test_outer = {&outer_vtbl};
2577 static void test_aggregation(void)
2579 IFilterGraph2 *graph, *graph2;
2580 IFilterMapper2 *mapper;
2581 IUnknown *unk, *unk2;
2582 HRESULT hr;
2583 ULONG ref;
2585 graph = (IFilterGraph2 *)0xdeadbeef;
2586 hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
2587 &IID_IFilterGraph2, (void **)&graph);
2588 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
2589 ok(!graph, "Got interface %p.\n", graph);
2591 hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
2592 &IID_IUnknown, (void **)&unk);
2593 ok(hr == S_OK, "Got hr %#x.\n", hr);
2594 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2595 ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n");
2596 ref = get_refcount(unk);
2597 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2599 ref = IUnknown_AddRef(unk);
2600 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2601 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2603 ref = IUnknown_Release(unk);
2604 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2605 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2607 hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2);
2608 ok(hr == S_OK, "Got hr %#x.\n", hr);
2609 ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2);
2610 IUnknown_Release(unk2);
2612 hr = IUnknown_QueryInterface(unk, &IID_IFilterGraph2, (void **)&graph);
2613 ok(hr == S_OK, "Got hr %#x.\n", hr);
2615 hr = IFilterGraph2_QueryInterface(graph, &IID_IUnknown, (void **)&unk2);
2616 ok(hr == S_OK, "Got hr %#x.\n", hr);
2617 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
2619 hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterGraph2, (void **)&graph2);
2620 ok(hr == S_OK, "Got hr %#x.\n", hr);
2621 ok(graph2 == (IFilterGraph2 *)0xdeadbeef, "Got unexpected IFilterGraph2 %p.\n", graph2);
2623 hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2);
2624 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
2625 ok(!unk2, "Got unexpected IUnknown %p.\n", unk2);
2627 hr = IFilterGraph2_QueryInterface(graph, &test_iid, (void **)&unk2);
2628 ok(hr == S_OK, "Got hr %#x.\n", hr);
2629 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
2631 IFilterGraph2_Release(graph);
2632 ref = IUnknown_Release(unk);
2633 ok(!ref, "Got unexpected refcount %d.\n", ref);
2634 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2636 /* Test the aggregated filter mapper. */
2638 graph = create_graph();
2640 ref = get_refcount(graph);
2641 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2643 hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterMapper2, (void **)&mapper);
2644 ok(hr == S_OK, "Got hr %#x.\n", hr);
2646 ref = get_refcount(graph);
2647 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2648 ref = get_refcount(mapper);
2649 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2651 hr = IFilterMapper2_QueryInterface(mapper, &IID_IFilterGraph2, (void **)&graph2);
2652 ok(hr == S_OK, "Got hr %#x.\n", hr);
2653 ok(graph2 == graph, "Got unexpected IFilterGraph2 %p.\n", graph2);
2654 IFilterGraph2_Release(graph2);
2656 IFilterMapper2_Release(mapper);
2657 ref = IFilterGraph2_Release(graph);
2658 ok(!ref, "Got unexpected refcount %d.\n", ref);
2661 /* Test how methods from "control" interfaces (IBasicAudio, IBasicVideo,
2662 * IVideoWindow) are delegated to filters exposing those interfaces. */
2663 static void test_control_delegation(void)
2665 IFilterGraph2 *graph = create_graph();
2666 IBasicAudio *audio, *filter_audio;
2667 IBaseFilter *renderer;
2668 IVideoWindow *window;
2669 IBasicVideo2 *video;
2670 ITypeInfo *typeinfo;
2671 TYPEATTR *typeattr;
2672 ULONG count;
2673 HRESULT hr;
2674 LONG val;
2676 /* IBasicAudio */
2678 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicAudio, (void **)&audio);
2679 ok(hr == S_OK, "got %#x\n", hr);
2681 hr = IBasicAudio_GetTypeInfoCount(audio, &count);
2682 ok(hr == S_OK, "Got hr %#x.\n", hr);
2683 ok(count == 1, "Got count %u.\n", count);
2685 hr = IBasicAudio_put_Volume(audio, -10);
2686 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2687 hr = IBasicAudio_get_Volume(audio, &val);
2688 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2689 hr = IBasicAudio_put_Balance(audio, 10);
2690 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2691 hr = IBasicAudio_get_Balance(audio, &val);
2692 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2694 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
2695 if (hr != VFW_E_NO_AUDIO_HARDWARE)
2697 ok(hr == S_OK, "got %#x\n", hr);
2699 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
2700 ok(hr == S_OK, "got %#x\n", hr);
2702 hr = IBasicAudio_put_Volume(audio, -10);
2703 ok(hr == S_OK, "got %#x\n", hr);
2704 hr = IBasicAudio_get_Volume(audio, &val);
2705 ok(hr == S_OK, "got %#x\n", hr);
2706 ok(val == -10, "got %d\n", val);
2707 hr = IBasicAudio_put_Balance(audio, 10);
2708 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2709 hr = IBasicAudio_get_Balance(audio, &val);
2710 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2711 if (hr == S_OK)
2712 ok(val == 10, "got balance %d\n", val);
2714 hr = IBaseFilter_QueryInterface(renderer, &IID_IBasicAudio, (void **)&filter_audio);
2715 ok(hr == S_OK, "got %#x\n", hr);
2717 hr = IBasicAudio_get_Volume(filter_audio, &val);
2718 ok(hr == S_OK, "got %#x\n", hr);
2719 ok(val == -10, "got volume %d\n", val);
2721 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2722 ok(hr == S_OK, "got %#x\n", hr);
2724 IBaseFilter_Release(renderer);
2725 IBasicAudio_Release(filter_audio);
2728 hr = IBasicAudio_put_Volume(audio, -10);
2729 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2730 hr = IBasicAudio_get_Volume(audio, &val);
2731 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2732 hr = IBasicAudio_put_Balance(audio, 10);
2733 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2734 hr = IBasicAudio_get_Balance(audio, &val);
2735 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2737 IBasicAudio_Release(audio);
2739 /* IBasicVideo and IVideoWindow */
2741 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo2, (void **)&video);
2742 ok(hr == S_OK, "got %#x\n", hr);
2743 hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
2744 ok(hr == S_OK, "got %#x\n", hr);
2746 /* Unlike IBasicAudio, these return E_NOINTERFACE. */
2747 hr = IBasicVideo2_get_BitRate(video, &val);
2748 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2750 hr = IBasicVideo2_GetTypeInfoCount(video, &count);
2751 ok(hr == S_OK, "Got hr %#x.\n", hr);
2752 ok(count == 1, "Got count %u.\n", count);
2754 hr = IBasicVideo2_GetTypeInfo(video, 0, 0, &typeinfo);
2755 ok(hr == S_OK, "Got hr %#x.\n", hr);
2756 hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr);
2757 ok(hr == S_OK, "Got hr %#x.\n", hr);
2758 ok(typeattr->typekind == TKIND_DISPATCH, "Got kind %u.\n", typeattr->typekind);
2759 ok(IsEqualGUID(&typeattr->guid, &IID_IBasicVideo), "Got IID %s.\n", wine_dbgstr_guid(&typeattr->guid));
2760 ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr);
2761 ITypeInfo_Release(typeinfo);
2763 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2764 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2766 hr = IVideoWindow_GetTypeInfoCount(window, &count);
2767 ok(hr == S_OK, "Got hr %#x.\n", hr);
2768 ok(count == 1, "Got count %u.\n", count);
2770 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
2771 ok(hr == S_OK, "got %#x\n", hr);
2773 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
2774 ok(hr == S_OK, "got %#x\n", hr);
2776 hr = IBasicVideo2_get_BitRate(video, &val);
2777 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2778 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2779 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2781 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2782 ok(hr == S_OK, "got %#x\n", hr);
2784 hr = IBasicVideo2_get_BitRate(video, &val);
2785 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2786 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2787 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2789 IBaseFilter_Release(renderer);
2790 IBasicVideo2_Release(video);
2791 IVideoWindow_Release(window);
2792 IFilterGraph2_Release(graph);
2795 static void test_add_remove_filter(void)
2797 struct testfilter filter;
2799 IFilterGraph2 *graph = create_graph();
2800 IBaseFilter *ret_filter;
2801 HRESULT hr;
2803 testfilter_init(&filter, NULL, 0);
2805 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2806 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2807 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2809 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, L"testid");
2810 ok(hr == S_OK, "Got hr %#x.\n", hr);
2811 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2812 ok(!wcscmp(filter.name, L"testid"), "Got name %s.\n", wine_dbgstr_w(filter.name));
2814 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2815 ok(hr == S_OK, "Got hr %#x.\n", hr);
2816 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2817 IBaseFilter_Release(ret_filter);
2819 hr = IFilterGraph2_RemoveFilter(graph, &filter.IBaseFilter_iface);
2820 ok(hr == S_OK, "Got hr %#x.\n", hr);
2821 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2822 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2823 ok(!filter.clock, "Got clock %p,\n", filter.clock);
2824 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2826 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2827 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2828 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2830 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, NULL);
2831 ok(hr == S_OK, "Got hr %#x.\n", hr);
2832 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2833 ok(!wcscmp(filter.name, L"0001"), "Got name %s.\n", wine_dbgstr_w(filter.name));
2835 hr = IFilterGraph2_FindFilterByName(graph, L"0001", &ret_filter);
2836 ok(hr == S_OK, "Got hr %#x.\n", hr);
2837 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2838 IBaseFilter_Release(ret_filter);
2840 /* test releasing the filter graph while filters are still connected */
2841 hr = IFilterGraph2_Release(graph);
2842 ok(!hr, "Got outstanding refcount %d.\n", hr);
2843 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2844 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2845 ok(!filter.clock, "Got clock %p.\n", filter.clock);
2846 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2849 static HRESULT WINAPI test_connect_direct_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
2851 struct testpin *pin = impl_from_IPin(iface);
2852 if (winetest_debug > 1) trace("%p->Connect()\n", pin);
2854 pin->peer = peer;
2855 IPin_AddRef(peer);
2856 pin->mt = (AM_MEDIA_TYPE *)mt;
2857 return S_OK;
2860 static const IPinVtbl test_connect_direct_vtbl =
2862 testpin_QueryInterface,
2863 testpin_AddRef,
2864 testpin_Release,
2865 test_connect_direct_Connect,
2866 no_ReceiveConnection,
2867 testpin_Disconnect,
2868 testpin_ConnectedTo,
2869 testpin_ConnectionMediaType,
2870 testpin_QueryPinInfo,
2871 testpin_QueryDirection,
2872 testpin_QueryId,
2873 testpin_QueryAccept,
2874 no_EnumMediaTypes,
2875 testpin_QueryInternalConnections,
2876 testpin_EndOfStream,
2877 testpin_BeginFlush,
2878 testpin_EndFlush,
2879 testpin_NewSegment
2882 static void test_connect_direct_init(struct testpin *pin, PIN_DIRECTION dir)
2884 testpin_init(pin, &test_connect_direct_vtbl, dir);
2887 static void test_connect_direct(void)
2889 struct testpin source_pin, sink_pin, parser1_pins[2], parser2_pins[2];
2890 struct testfilter source, sink, parser1, parser2;
2892 IFilterGraph2 *graph = create_graph();
2893 IMediaControl *control;
2894 AM_MEDIA_TYPE mt;
2895 HRESULT hr;
2897 test_connect_direct_init(&source_pin, PINDIR_OUTPUT);
2898 testfilter_init(&source, &source_pin, 1);
2899 test_connect_direct_init(&sink_pin, PINDIR_INPUT);
2900 testfilter_init(&sink, &sink_pin, 1);
2901 test_connect_direct_init(&parser1_pins[0], PINDIR_INPUT);
2902 test_connect_direct_init(&parser1_pins[1], PINDIR_OUTPUT);
2903 testfilter_init(&parser1, parser1_pins, 2);
2904 test_connect_direct_init(&parser2_pins[0], PINDIR_INPUT);
2905 test_connect_direct_init(&parser2_pins[1], PINDIR_OUTPUT);
2906 testfilter_init(&parser2, parser2_pins, 2);
2908 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2909 ok(hr == S_OK, "Got hr %#x.\n", hr);
2911 hr = IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2912 ok(hr == S_OK, "Got hr %#x.\n", hr);
2914 /* The filter graph does not prevent connection while it is running; only
2915 * individual filters do. */
2916 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
2917 hr = IMediaControl_Pause(control);
2918 ok(hr == S_OK, "Got hr %#x.\n", hr);
2920 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2921 ok(hr == S_OK, "Got hr %#x.\n", hr);
2922 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2923 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2924 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2926 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2927 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2928 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2929 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2931 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2932 ok(hr == S_OK, "Got hr %#x.\n", hr);
2933 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2934 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2936 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2937 ok(hr == S_OK, "Got hr %#x.\n", hr);
2938 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2939 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2940 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2942 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2943 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2944 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2945 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2947 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2948 ok(hr == S_OK, "Got hr %#x.\n", hr);
2949 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2950 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2952 /* Swap the pins when connecting. */
2953 hr = IFilterGraph2_ConnectDirect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface, NULL);
2954 ok(hr == S_OK, "Got hr %#x.\n", hr);
2955 todo_wine
2956 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2957 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2958 todo_wine
2959 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2961 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2962 todo_wine
2963 ok(hr == S_OK, "Got hr %#x.\n", hr);
2964 todo_wine
2965 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2966 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2968 hr = IFilterGraph2_Connect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface);
2969 ok(hr == S_OK, "Got hr %#x.\n", hr);
2970 todo_wine
2971 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2972 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2973 todo_wine
2974 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2976 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2977 todo_wine
2978 ok(hr == S_OK, "Got hr %#x.\n", hr);
2979 todo_wine
2980 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2981 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2983 /* Disconnect() does not disconnect the peer. */
2984 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2985 ok(hr == S_OK, "Got hr %#x.\n", hr);
2986 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2987 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2988 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2990 sink_pin.peer = &source_pin.IPin_iface;
2991 IPin_AddRef(sink_pin.peer);
2993 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2994 ok(hr == S_OK, "Got hr %#x.\n", hr);
2995 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2996 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2998 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2999 ok(hr == S_OK, "Got hr %#x.\n", hr);
3000 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
3001 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3003 /* Test specifying the media type. */
3004 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3005 ok(hr == S_OK, "Got hr %#x.\n", hr);
3006 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3007 ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
3008 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3009 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
3010 ok(hr == S_OK, "Got hr %#x.\n", hr);
3012 /* Test Reconnect[Ex](). */
3014 hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
3015 todo_wine ok(hr == VFW_E_WRONG_STATE, "Got hr %#x.\n", hr);
3016 hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
3017 todo_wine ok(hr == VFW_E_WRONG_STATE, "Got hr %#x.\n", hr);
3019 hr = IMediaControl_Stop(control);
3020 ok(hr == S_OK, "Got hr %#x.\n", hr);
3022 hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
3023 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
3024 hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
3025 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
3027 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3028 ok(hr == S_OK, "Got hr %#x.\n", hr);
3029 hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
3030 ok(hr == S_OK, "Got hr %#x.\n", hr);
3031 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3032 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
3033 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3034 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
3035 ok(hr == S_OK, "Got hr %#x.\n", hr);
3037 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3038 ok(hr == S_OK, "Got hr %#x.\n", hr);
3039 sink_pin.peer = &source_pin.IPin_iface;
3040 IPin_AddRef(sink_pin.peer);
3041 hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
3042 ok(hr == S_OK, "Got hr %#x.\n", hr);
3043 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3044 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
3045 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3046 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
3047 ok(hr == S_OK, "Got hr %#x.\n", hr);
3049 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
3050 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
3051 hr = IFilterGraph2_ReconnectEx(graph, &sink_pin.IPin_iface, NULL);
3052 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
3054 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3055 ok(hr == S_OK, "Got hr %#x.\n", hr);
3056 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
3057 ok(hr == S_OK, "Got hr %#x.\n", hr);
3058 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3059 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
3060 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3061 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
3062 ok(hr == S_OK, "Got hr %#x.\n", hr);
3064 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3065 ok(hr == S_OK, "Got hr %#x.\n", hr);
3066 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, &mt);
3067 ok(hr == S_OK, "Got hr %#x.\n", hr);
3068 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3069 ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
3070 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3071 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
3072 ok(hr == S_OK, "Got hr %#x.\n", hr);
3074 /* ConnectDirect() protects against cyclical connections. */
3075 hr = IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
3076 ok(hr == S_OK, "Got hr %#x.\n", hr);
3077 hr = IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
3078 ok(hr == S_OK, "Got hr %#x.\n", hr);
3080 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3081 ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
3083 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
3084 ok(hr == S_OK, "Got hr %#x.\n", hr);
3085 hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3086 todo_wine ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
3087 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
3088 IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
3090 parser1_pins[0].QueryInternalConnections_hr = S_OK;
3091 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3092 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3093 ok(!parser1_pins[0].peer, "Got peer %p.\n", parser1_pins[0].peer);
3094 todo_wine ok(parser1_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser1_pins[1].peer);
3095 IFilterGraph2_Disconnect(graph, &parser1_pins[0].IPin_iface);
3096 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
3098 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
3099 ok(hr == S_OK, "Got hr %#x.\n", hr);
3100 hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3101 ok(hr == S_OK, "Got hr %#x.\n", hr);
3102 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
3103 IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
3105 hr = IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
3106 ok(hr == S_OK, "Got hr %#x.\n", hr);
3107 hr = IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
3108 ok(hr == S_OK, "Got hr %#x.\n", hr);
3110 /* Both pins are disconnected when a filter is removed. */
3111 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3112 ok(hr == S_OK, "Got hr %#x.\n", hr);
3113 sink_pin.peer = &source_pin.IPin_iface;
3114 IPin_AddRef(sink_pin.peer);
3115 hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
3116 ok(hr == S_OK, "Got hr %#x.\n", hr);
3117 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
3118 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3120 /* If the filter cannot be disconnected, then RemoveFilter() fails. */
3122 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3123 ok(hr == S_OK, "Got hr %#x.\n", hr);
3124 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3125 ok(hr == S_OK, "Got hr %#x.\n", hr);
3126 sink_pin.peer = &source_pin.IPin_iface;
3127 IPin_AddRef(sink_pin.peer);
3128 hr = IMediaControl_Pause(control);
3129 ok(hr == S_OK, "Got hr %#x.\n", hr);
3131 source_pin.require_stopped_disconnect = TRUE;
3132 hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
3133 ok(hr == VFW_E_NOT_STOPPED, "Got hr %#x.\n", hr);
3134 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3135 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3137 sink_pin.peer = &source_pin.IPin_iface;
3138 IPin_AddRef(sink_pin.peer);
3139 source_pin.require_stopped_disconnect = FALSE;
3140 sink_pin.require_stopped_disconnect = TRUE;
3141 hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
3142 ok(hr == VFW_E_NOT_STOPPED, "Got hr %#x.\n", hr);
3143 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3144 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
3146 /* Filters are stopped, and pins disconnected, when the graph is destroyed. */
3148 IMediaControl_Release(control);
3149 hr = IFilterGraph2_Release(graph);
3150 ok(!hr, "Got outstanding refcount %d.\n", hr);
3151 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
3152 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
3153 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
3154 todo_wine
3155 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
3156 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
3157 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3160 static void test_sync_source(void)
3162 struct testfilter filter1, filter2;
3164 IFilterGraph2 *graph = create_graph();
3165 IReferenceClock *systemclock, *clock;
3166 IMediaFilter *filter;
3167 HRESULT hr;
3168 ULONG ref;
3170 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3172 testfilter_init(&filter1, NULL, 0);
3173 testfilter_init(&filter2, NULL, 0);
3175 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3176 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
3178 ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
3179 ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
3181 CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER,
3182 &IID_IReferenceClock, (void **)&systemclock);
3184 hr = IMediaFilter_SetSyncSource(filter, systemclock);
3185 ok(hr == S_OK, "Got hr %#x.\n", hr);
3186 ok(filter1.clock == systemclock, "Got clock %p.\n", filter1.clock);
3187 ok(filter2.clock == systemclock, "Got clock %p.\n", filter2.clock);
3189 hr = IMediaFilter_GetSyncSource(filter, &clock);
3190 ok(hr == S_OK, "Got hr %#x.\n", hr);
3191 ok(clock == systemclock, "Got clock %p.\n", clock);
3192 IReferenceClock_Release(clock);
3194 hr = IMediaFilter_SetSyncSource(filter, NULL);
3195 ok(hr == S_OK, "Got hr %#x.\n", hr);
3196 ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
3197 ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
3199 hr = IMediaFilter_GetSyncSource(filter, &clock);
3200 todo_wine
3201 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3202 ok(!clock, "Got clock %p.\n", clock);
3204 IReferenceClock_Release(systemclock);
3205 IMediaFilter_Release(filter);
3206 ref = IFilterGraph2_Release(graph);
3207 ok(!ref, "Got outstanding refcount %d\n", ref);
3208 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
3209 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
3212 #define check_filter_state(a, b) check_filter_state_(__LINE__, a, b)
3213 static void check_filter_state_(unsigned int line, IFilterGraph2 *graph, FILTER_STATE expect)
3215 IMediaFilter *mediafilter;
3216 IEnumFilters *filterenum;
3217 IMediaControl *control;
3218 OAFilterState oastate;
3219 IBaseFilter *filter;
3220 FILTER_STATE state;
3221 HRESULT hr;
3223 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&mediafilter);
3224 hr = IMediaFilter_GetState(mediafilter, 1000, &state);
3225 ok_(__FILE__, line)(hr == S_OK, "IMediaFilter_GetState() returned %#x.\n", hr);
3226 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3227 IMediaFilter_Release(mediafilter);
3229 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3230 hr = IMediaControl_GetState(control, 1000, &oastate);
3231 ok_(__FILE__, line)(hr == S_OK, "IMediaControl_GetState() returned %#x.\n", hr);
3232 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3233 IMediaControl_Release(control);
3235 IFilterGraph2_EnumFilters(graph, &filterenum);
3236 while (IEnumFilters_Next(filterenum, 1, &filter, NULL) == S_OK)
3238 hr = IBaseFilter_GetState(filter, 1000, &state);
3239 ok_(__FILE__, line)(hr == S_OK, "IBaseFilter_GetState() returned %#x.\n", hr);
3240 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3241 IBaseFilter_Release(filter);
3243 IEnumFilters_Release(filterenum);
3247 static void test_filter_state(void)
3249 struct testfilter source, sink, dummy;
3250 struct testpin source_pin, sink_pin;
3252 IFilterGraph2 *graph = create_graph();
3253 REFERENCE_TIME start_time, time;
3254 IReferenceClock *clock;
3255 IMediaControl *control;
3256 IMediaSeeking *seeking;
3257 FILTER_STATE mf_state;
3258 IMediaFilter *filter;
3259 OAFilterState state;
3260 HRESULT hr;
3261 ULONG ref;
3263 testsource_init(&source_pin, NULL, 0);
3264 testsink_init(&sink_pin);
3265 testfilter_init(&source, &source_pin, 1);
3266 testfilter_init(&sink, &sink_pin, 1);
3267 testfilter_init(&dummy, NULL, 0);
3268 sink.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3270 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3271 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3273 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3274 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
3275 IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, NULL);
3276 /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */
3277 /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */
3278 IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3280 check_filter_state(graph, State_Stopped);
3282 hr = IMediaControl_Pause(control);
3283 ok(hr == S_OK, "Got hr %#x.\n", hr);
3284 check_filter_state(graph, State_Paused);
3286 /* Pausing sets the default sync source, if it's not already set. */
3288 hr = IMediaFilter_GetSyncSource(filter, &clock);
3289 ok(hr == S_OK, "Got hr %#x.\n", hr);
3290 ok(!!clock, "Reference clock not set.\n");
3291 ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
3292 ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
3294 hr = IReferenceClock_GetTime(clock, &start_time);
3295 ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
3296 hr = IMediaControl_Run(control);
3297 ok(hr == S_OK, "Got hr %#x.\n", hr);
3298 check_filter_state(graph, State_Running);
3299 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3300 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3301 "Expected time near %s, got %s.\n",
3302 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3303 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3304 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3306 hr = IMediaControl_Pause(control);
3307 ok(hr == S_OK, "Got hr %#x.\n", hr);
3308 check_filter_state(graph, State_Paused);
3310 sink.state = State_Stopped;
3311 hr = IMediaControl_GetState(control, 0, &state);
3312 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3313 ok(state == State_Paused, "Got state %u.\n", state);
3315 sink.state = State_Running;
3316 hr = IMediaControl_GetState(control, 0, &state);
3317 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3318 ok(state == State_Paused, "Got state %u.\n", state);
3320 sink.state = State_Paused;
3321 hr = IMediaControl_GetState(control, 0, &state);
3322 ok(hr == S_OK, "Got hr %#x.\n", hr);
3323 ok(state == State_Paused, "Got state %u.\n", state);
3325 hr = IMediaControl_Stop(control);
3326 ok(hr == S_OK, "Got hr %#x.\n", hr);
3327 check_filter_state(graph, State_Stopped);
3329 hr = IMediaControl_Run(control);
3330 ok(hr == S_OK, "Got hr %#x.\n", hr);
3331 check_filter_state(graph, State_Running);
3333 sink.state = State_Stopped;
3334 hr = IMediaControl_GetState(control, 0, &state);
3335 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3336 ok(state == State_Running, "Got state %u.\n", state);
3338 sink.state = State_Paused;
3339 hr = IMediaControl_GetState(control, 0, &state);
3340 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3341 ok(state == State_Running, "Got state %u.\n", state);
3343 sink.state = State_Running;
3344 hr = IMediaControl_GetState(control, 0, &state);
3345 ok(hr == S_OK, "Got hr %#x.\n", hr);
3346 ok(state == State_Running, "Got state %u.\n", state);
3348 hr = IMediaControl_Stop(control);
3349 ok(hr == S_OK, "Got hr %#x.\n", hr);
3350 check_filter_state(graph, State_Stopped);
3352 sink.state = State_Running;
3353 hr = IMediaControl_GetState(control, 0, &state);
3354 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3355 ok(state == State_Stopped, "Got state %u.\n", state);
3357 sink.state = State_Paused;
3358 hr = IMediaControl_GetState(control, 0, &state);
3359 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3360 ok(state == State_Stopped, "Got state %u.\n", state);
3362 sink.state = State_Stopped;
3363 hr = IMediaControl_GetState(control, 0, &state);
3364 ok(hr == S_OK, "Got hr %#x.\n", hr);
3365 ok(state == State_Stopped, "Got state %u.\n", state);
3367 hr = IMediaControl_Pause(control);
3368 ok(hr == S_OK, "Got hr %#x.\n", hr);
3369 check_filter_state(graph, State_Paused);
3371 hr = IMediaControl_StopWhenReady(control);
3372 ok(hr == S_OK, "Got hr %#x.\n", hr);
3373 check_filter_state(graph, State_Stopped);
3375 hr = IMediaControl_Run(control);
3376 ok(hr == S_OK, "Got hr %#x.\n", hr);
3377 check_filter_state(graph, State_Running);
3379 hr = IMediaControl_StopWhenReady(control);
3380 ok(hr == S_OK, "Got hr %#x.\n", hr);
3381 check_filter_state(graph, State_Stopped);
3383 hr = IMediaControl_StopWhenReady(control);
3384 ok(hr == S_OK, "Got hr %#x.\n", hr);
3385 check_filter_state(graph, State_Stopped);
3387 IReferenceClock_Release(clock);
3388 IMediaFilter_Release(filter);
3389 IMediaControl_Release(control);
3390 IFilterGraph2_Release(graph);
3392 /* Test same methods using IMediaFilter. */
3394 graph = create_graph();
3395 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3396 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3397 IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
3399 /* Add the filters in reverse order this time. */
3400 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
3401 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3402 /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */
3403 /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */
3404 IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3406 hr = IMediaFilter_Pause(filter);
3407 ok(hr == S_OK, "Got hr %#x.\n", hr);
3408 check_filter_state(graph, State_Paused);
3410 hr = IMediaFilter_GetSyncSource(filter, &clock);
3411 ok(hr == S_OK, "Got hr %#x.\n", hr);
3412 ok(!!clock, "Reference clock not set.\n");
3413 ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
3414 ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
3416 hr = IMediaFilter_Run(filter, 0xdeadbeef);
3417 ok(hr == S_OK, "Got hr %#x.\n", hr);
3418 check_filter_state(graph, State_Running);
3419 ok(source.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3420 ok(sink.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
3422 hr = IMediaFilter_Pause(filter);
3423 ok(hr == S_OK, "Got hr %#x.\n", hr);
3424 check_filter_state(graph, State_Paused);
3426 hr = IMediaFilter_Run(filter, 0xdeadf00d);
3427 ok(hr == S_OK, "Got hr %#x.\n", hr);
3428 check_filter_state(graph, State_Running);
3429 ok(source.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3430 ok(sink.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
3432 hr = IMediaFilter_Pause(filter);
3433 ok(hr == S_OK, "Got hr %#x.\n", hr);
3434 check_filter_state(graph, State_Paused);
3436 hr = IMediaFilter_Stop(filter);
3437 ok(hr == S_OK, "Got hr %#x.\n", hr);
3438 check_filter_state(graph, State_Stopped);
3440 source.expect_run_prev = sink.expect_run_prev = State_Stopped;
3441 hr = IReferenceClock_GetTime(clock, &start_time);
3442 ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
3443 hr = IMediaFilter_Run(filter, 0);
3444 ok(hr == S_OK, "Got hr %#x.\n", hr);
3445 check_filter_state(graph, State_Running);
3446 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3447 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3448 "Expected time near %s, got %s.\n",
3449 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3450 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3451 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3453 Sleep(600);
3454 hr = IMediaFilter_Pause(filter);
3455 ok(hr == S_OK, "Got hr %#x.\n", hr);
3456 check_filter_state(graph, State_Paused);
3458 source.expect_run_prev = sink.expect_run_prev = State_Paused;
3459 hr = IMediaFilter_Run(filter, 0);
3460 ok(hr == S_OK, "Got hr %#x.\n", hr);
3461 check_filter_state(graph, State_Running);
3462 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3463 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3464 "Expected time near %s, got %s.\n",
3465 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3466 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3467 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3469 hr = IMediaFilter_Pause(filter);
3470 ok(hr == S_OK, "Got hr %#x.\n", hr);
3471 check_filter_state(graph, State_Paused);
3472 Sleep(600);
3474 start_time += 550 * 10000;
3475 hr = IMediaFilter_Run(filter, 0);
3476 ok(hr == S_OK, "Got hr %#x.\n", hr);
3477 check_filter_state(graph, State_Running);
3478 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3479 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3480 "Expected time near %s, got %s.\n",
3481 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3482 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3483 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3485 hr = IMediaFilter_Stop(filter);
3486 ok(hr == S_OK, "Got hr %#x.\n", hr);
3487 check_filter_state(graph, State_Stopped);
3489 /* Test removing the sync source. */
3491 IReferenceClock_Release(clock);
3492 IMediaFilter_SetSyncSource(filter, NULL);
3494 hr = IMediaControl_Run(control);
3495 ok(hr == S_OK, "Got hr %#x.\n", hr);
3496 check_filter_state(graph, State_Running);
3497 todo_wine
3498 ok(source.start_time > 0 && source.start_time < 500 * 10000,
3499 "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3500 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3501 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3503 hr = IMediaControl_Stop(control);
3504 ok(hr == S_OK, "Got hr %#x.\n", hr);
3505 check_filter_state(graph, State_Stopped);
3507 /* Test asynchronous state change. */
3509 sink.state_hr = S_FALSE;
3510 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3511 hr = IMediaControl_Pause(control);
3512 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3514 hr = IMediaControl_GetState(control, 0, &state);
3515 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3516 ok(state == State_Paused, "Got state %u.\n", state);
3518 sink.state_hr = sink.GetState_hr = S_OK;
3519 hr = IMediaControl_GetState(control, 0, &state);
3520 ok(hr == S_OK, "Got hr %#x.\n", hr);
3521 ok(state == State_Paused, "Got state %u.\n", state);
3523 sink.state_hr = S_FALSE;
3524 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3525 hr = IMediaControl_Stop(control);
3526 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3528 hr = IMediaControl_GetState(control, 0, &state);
3529 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3530 ok(state == State_Stopped, "Got state %u.\n", state);
3532 sink.state_hr = sink.GetState_hr = S_OK;
3533 hr = IMediaControl_GetState(control, 0, &state);
3534 ok(hr == S_OK, "Got hr %#x.\n", hr);
3535 ok(state == State_Stopped, "Got state %u.\n", state);
3537 /* Renderers are expected to block completing a state change into paused
3538 * until they receive a sample. Because the graph can transition from
3539 * stopped -> paused -> running in one call, which itself needs to be
3540 * asynchronous, it actually waits on a separate thread for all filters
3541 * to be ready, then calls IMediaFilter::Run() once they are.
3543 * However, IMediaControl::GetState() will return VFW_S_STATE_INTERMEDIATE
3544 * if filters haven't caught up to the graph yet. To make matters worse, it
3545 * doesn't take the above into account, meaning that it'll gladly return
3546 * VFW_S_STATE_INTERMEDIATE even if passed an infinite timeout. */
3548 sink.state_hr = S_FALSE;
3549 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3550 hr = IMediaControl_Run(control);
3551 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3553 hr = IMediaControl_GetState(control, 0, &state);
3554 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3555 ok(state == State_Running, "Got state %u.\n", state);
3556 ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
3557 ok(source.state == State_Paused, "Got state %u.\n", source.state);
3559 /* SetPositions() does not pause the graph in this case, since it is
3560 * already in a paused state. */
3561 time = 0;
3562 hr = IMediaSeeking_SetPositions(seeking, &time, AM_SEEKING_AbsolutePositioning,
3563 NULL, AM_SEEKING_NoPositioning);
3564 ok(hr == S_OK, "Got hr %#x.\n", hr);
3566 hr = IMediaControl_Run(control);
3567 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3569 hr = IMediaControl_GetState(control, 0, &state);
3570 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3571 ok(state == State_Running, "Got state %u.\n", state);
3572 ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
3573 ok(source.state == State_Paused, "Got state %u.\n", source.state);
3575 sink.state_hr = sink.GetState_hr = S_OK;
3577 while ((hr = IMediaControl_GetState(control, INFINITE, &state)) == VFW_S_STATE_INTERMEDIATE)
3579 ok(state == State_Running, "Got state %u.\n", state);
3580 ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
3581 ok(source.state == State_Paused, "Got state %u.\n", source.state);
3582 Sleep(10);
3584 ok(hr == S_OK, "Got hr %#x.\n", hr);
3585 ok(state == State_Running, "Got state %u.\n", state);
3586 ok(sink.state == State_Running, "Got state %u.\n", sink.state);
3587 ok(source.state == State_Running, "Got state %u.\n", source.state);
3589 /* The above logic does not apply to the running -> paused -> stopped
3590 * transition. The filter graph will stop a filter regardless of whether
3591 * it's completely paused. Inasmuch as stopping the filter is like flushing
3592 * it—i.e. it has to succeed—this makes sense. */
3594 sink.state_hr = S_FALSE;
3595 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3596 hr = IMediaControl_Stop(control);
3597 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3598 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3599 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3601 hr = IMediaControl_GetState(control, 0, &state);
3602 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3603 ok(state == State_Stopped, "Got state %u.\n", state);
3605 hr = IMediaControl_Stop(control);
3606 ok(hr == S_OK, "Got hr %#x.\n", hr);
3608 sink.state_hr = sink.GetState_hr = S_OK;
3609 hr = IMediaControl_GetState(control, 0, &state);
3610 ok(hr == S_OK, "Got hr %#x.\n", hr);
3611 ok(state == State_Stopped, "Got state %u.\n", state);
3613 /* Try an asynchronous stopped->paused->running transition, but pause or
3614 * stop the graph before our filter is completely paused. */
3616 sink.state_hr = S_FALSE;
3617 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3618 hr = IMediaControl_Run(control);
3619 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3621 hr = IMediaControl_Pause(control);
3622 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3624 sink.state_hr = sink.GetState_hr = S_OK;
3625 hr = IMediaControl_GetState(control, 0, &state);
3626 ok(hr == S_OK, "Got hr %#x.\n", hr);
3627 ok(state == State_Paused, "Got state %u.\n", state);
3628 ok(sink.state == State_Paused, "Got state %u.\n", sink.state);
3629 ok(source.state == State_Paused, "Got state %u.\n", source.state);
3631 hr = IMediaControl_Stop(control);
3632 ok(hr == S_OK, "Got hr %#x.\n", hr);
3634 sink.state_hr = S_FALSE;
3635 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3636 hr = IMediaControl_Run(control);
3637 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3639 hr = IMediaControl_Stop(control);
3640 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3641 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3642 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3644 sink.state_hr = sink.GetState_hr = S_OK;
3645 hr = IMediaControl_GetState(control, 0, &state);
3646 ok(hr == S_OK, "Got hr %#x.\n", hr);
3647 ok(state == State_Stopped, "Got state %u.\n", state);
3648 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3649 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3651 /* Same, but tear down the graph instead. */
3653 sink.state_hr = S_FALSE;
3654 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3655 hr = IMediaControl_Run(control);
3656 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3658 IMediaFilter_Release(filter);
3659 IMediaControl_Release(control);
3660 IMediaSeeking_Release(seeking);
3661 ref = IFilterGraph2_Release(graph);
3662 ok(!ref, "Got outstanding refcount %d.\n", ref);
3664 graph = create_graph();
3665 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3666 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3667 IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
3668 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
3669 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3670 IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3672 /* This logic doesn't apply when using IMediaFilter methods directly. */
3674 source.expect_run_prev = sink.expect_run_prev = State_Stopped;
3675 sink.state_hr = S_FALSE;
3676 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3677 hr = IMediaFilter_Run(filter, 0);
3678 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3680 hr = IMediaFilter_GetState(filter, 0, &mf_state);
3681 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3682 ok(mf_state == State_Running, "Got state %u.\n", mf_state);
3683 ok(sink.state == State_Running, "Got state %u.\n", sink.state);
3684 ok(source.state == State_Running, "Got state %u.\n", source.state);
3686 sink.state_hr = sink.GetState_hr = S_OK;
3687 hr = IMediaFilter_GetState(filter, 0, &mf_state);
3688 ok(hr == S_OK, "Got hr %#x.\n", hr);
3689 ok(mf_state == State_Running, "Got state %u.\n", mf_state);
3690 ok(sink.state == State_Running, "Got state %u.\n", sink.state);
3691 ok(source.state == State_Running, "Got state %u.\n", source.state);
3693 hr = IMediaFilter_Stop(filter);
3694 ok(hr == S_OK, "Got hr %#x.\n", hr);
3695 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3696 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3698 source.expect_run_prev = sink.expect_run_prev = State_Paused;
3700 /* Test VFW_S_CANT_CUE. */
3702 sink.GetState_hr = VFW_S_CANT_CUE;
3703 hr = IMediaControl_Pause(control);
3704 ok(hr == S_OK, "Got hr %#x.\n", hr);
3706 hr = IMediaControl_GetState(control, 0, &state);
3707 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3708 ok(state == State_Paused, "Got state %u.\n", state);
3710 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3711 source.GetState_hr = VFW_S_CANT_CUE;
3712 hr = IMediaControl_GetState(control, 0, &state);
3713 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3714 ok(state == State_Paused, "Got state %u.\n", state);
3716 sink.GetState_hr = VFW_S_CANT_CUE;
3717 source.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3718 hr = IMediaControl_GetState(control, 0, &state);
3719 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3720 ok(state == State_Paused, "Got state %u.\n", state);
3722 sink.GetState_hr = source.GetState_hr = S_OK;
3724 hr = IMediaControl_Stop(control);
3725 ok(hr == S_OK, "Got hr %#x.\n", hr);
3727 sink.state_hr = S_FALSE;
3728 sink.GetState_hr = VFW_S_STATE_INTERMEDIATE;
3729 source.GetState_hr = VFW_S_CANT_CUE;
3730 hr = IMediaControl_Run(control);
3731 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3732 ok(sink.state == State_Running, "Got state %u.\n", sink.state);
3733 ok(source.state == State_Running, "Got state %u.\n", source.state);
3735 hr = IMediaControl_GetState(control, 0, &state);
3736 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3737 ok(state == State_Running, "Got state %u.\n", state);
3738 ok(sink.state == State_Running, "Got state %u.\n", sink.state);
3739 ok(source.state == State_Running, "Got state %u.\n", source.state);
3741 sink.state_hr = sink.GetState_hr = source.GetState_hr = S_OK;
3743 hr = IMediaControl_Stop(control);
3744 ok(hr == S_OK, "Got hr %#x.\n", hr);
3746 /* Add and remove a filter while the graph is running. */
3748 hr = IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, L"dummy");
3749 ok(hr == S_OK, "Got hr %#x.\n", hr);
3750 ok(dummy.state == State_Stopped, "Got state %#x.\n", dummy.state);
3752 hr = IMediaControl_Pause(control);
3753 ok(hr == S_OK, "Got hr %#x.\n", hr);
3754 check_filter_state(graph, State_Paused);
3755 ok(dummy.state == State_Paused, "Got state %#x.\n", dummy.state);
3757 hr = IFilterGraph2_RemoveFilter(graph, &dummy.IBaseFilter_iface);
3758 ok(hr == S_OK, "Got hr %#x.\n", hr);
3759 ok(dummy.state == State_Paused, "Got state %#x.\n", dummy.state);
3761 hr = IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, L"dummy");
3762 ok(hr == S_OK, "Got hr %#x.\n", hr);
3763 ok(dummy.state == State_Paused, "Got state %#x.\n", dummy.state);
3765 hr = IMediaControl_Stop(control);
3766 ok(hr == S_OK, "Got hr %#x.\n", hr);
3767 check_filter_state(graph, State_Stopped);
3769 hr = IFilterGraph2_RemoveFilter(graph, &dummy.IBaseFilter_iface);
3770 ok(hr == S_OK, "Got hr %#x.\n", hr);
3771 ok(dummy.state == State_Stopped, "Got state %#x.\n", dummy.state);
3773 /* Destroying the graph while it's running stops all filters. */
3775 hr = IMediaControl_Run(control);
3776 ok(hr == S_OK, "Got hr %#x.\n", hr);
3777 check_filter_state(graph, State_Running);
3779 source.expect_stop_prev = sink.expect_stop_prev = State_Running;
3780 IMediaFilter_Release(filter);
3781 IMediaControl_Release(control);
3782 IMediaSeeking_Release(seeking);
3783 ref = IFilterGraph2_Release(graph);
3784 ok(!ref, "Got outstanding refcount %d.\n", ref);
3785 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
3786 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
3787 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
3788 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
3789 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3790 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3793 /* Helper function to check whether a filter is considered a renderer, i.e.
3794 * whether its EC_COMPLETE notification will be passed on to the application. */
3795 static HRESULT check_ec_complete(IFilterGraph2 *graph, IBaseFilter *filter)
3797 IMediaEventSink *eventsink;
3798 LONG_PTR param1, param2;
3799 IMediaControl *control;
3800 IMediaEvent *eventsrc;
3801 HRESULT hr, ret_hr;
3802 LONG code;
3804 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3805 IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
3806 IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
3808 IMediaControl_Run(control);
3810 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3811 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3813 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)filter);
3814 ok(hr == S_OK, "Got hr %#x.\n", hr);
3816 ret_hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3817 if (ret_hr == S_OK)
3819 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3820 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3821 ok(!param2, "Got param2 %#lx.\n", param2);
3822 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3823 ok(hr == S_OK, "Got hr %#x.\n", hr);
3825 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3826 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3829 IMediaControl_Stop(control);
3831 IMediaControl_Release(control);
3832 IMediaEvent_Release(eventsrc);
3833 IMediaEventSink_Release(eventsink);
3834 return ret_hr;
3837 static void test_ec_complete(void)
3839 struct testpin filter1_pin, filter2_pin, filter3_pin;
3840 struct testfilter filter1, filter2, filter3;
3842 IFilterGraph2 *graph = create_graph();
3843 IMediaEventSink *eventsink;
3844 LONG_PTR param1, param2;
3845 IMediaControl *control;
3846 IMediaEvent *eventsrc;
3847 HRESULT hr;
3848 LONG code;
3850 testsink_init(&filter1_pin);
3851 testsink_init(&filter2_pin);
3852 testsink_init(&filter3_pin);
3853 testfilter_init(&filter1, &filter1_pin, 1);
3854 testfilter_init(&filter2, &filter2_pin, 1);
3855 testfilter_init(&filter3, &filter3_pin, 1);
3857 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3858 IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
3859 IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
3861 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3862 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
3863 IFilterGraph2_AddFilter(graph, &filter3.IBaseFilter_iface, NULL);
3865 /* EC_COMPLETE is only delivered to the user after all renderers deliver it. */
3867 filter1.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
3868 filter2.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
3869 filter3.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
3870 filter1.misc_flags = filter2.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER;
3872 IMediaControl_Run(control);
3874 filter3.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER;
3876 while ((hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0)) == S_OK)
3878 ok(code != EC_COMPLETE, "Got unexpected EC_COMPLETE.\n");
3879 IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3881 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3883 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
3884 ok(hr == S_OK, "Got hr %#x.\n", hr);
3886 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3887 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3889 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
3890 ok(hr == S_OK, "Got hr %#x.\n", hr);
3892 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3893 ok(hr == S_OK, "Got hr %#x.\n", hr);
3894 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3895 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3896 ok(!param2, "Got param2 %#lx.\n", param2);
3897 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3898 ok(hr == S_OK, "Got hr %#x.\n", hr);
3900 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3901 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3903 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
3904 ok(hr == S_OK, "Got hr %#x.\n", hr);
3906 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3907 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3909 IMediaControl_Stop(control);
3911 /* Test CancelDefaultHandling(). */
3913 IMediaControl_Run(control);
3915 hr = IMediaEvent_CancelDefaultHandling(eventsrc, EC_COMPLETE);
3916 ok(hr == S_OK, "Got hr %#x.\n", hr);
3918 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3919 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3921 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
3922 ok(hr == S_OK, "Got hr %#x.\n", hr);
3924 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3925 ok(hr == S_OK, "Got hr %#x.\n", hr);
3926 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3927 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3928 ok(param2 == (LONG_PTR)&filter1.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
3929 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3930 ok(hr == S_OK, "Got hr %#x.\n", hr);
3932 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3933 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3935 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
3936 ok(hr == S_OK, "Got hr %#x.\n", hr);
3938 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3939 ok(hr == S_OK, "Got hr %#x.\n", hr);
3940 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3941 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3942 ok(param2 == (LONG_PTR)&filter3.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
3943 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3944 ok(hr == S_OK, "Got hr %#x.\n", hr);
3946 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3947 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3949 IMediaControl_Stop(control);
3950 hr = IMediaEvent_RestoreDefaultHandling(eventsrc, EC_COMPLETE);
3951 ok(hr == S_OK, "Got hr %#x.\n", hr);
3953 /* A filter counts as a renderer if it (1) exposes IAMFilterMiscFlags and
3954 * reports itself as a renderer, or (2) exposes IMediaSeeking and has no
3955 * output pins. Despite MSDN, QueryInternalConnections() does not seem to
3956 * be used. */
3958 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3959 IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
3960 IFilterGraph2_RemoveFilter(graph, &filter3.IBaseFilter_iface);
3961 filter1.misc_flags = 0;
3962 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3964 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3965 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3967 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3968 filter1_pin.dir = PINDIR_INPUT;
3969 filter1.IAMFilterMiscFlags_iface.lpVtbl = NULL;
3970 filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3971 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3973 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3974 ok(hr == S_OK, "Got hr %#x.\n", hr);
3976 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3977 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3978 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3980 filter1_pin.dir = PINDIR_OUTPUT;
3981 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3983 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3984 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3986 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3987 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3988 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3990 filter1_pin.dir = PINDIR_INPUT;
3991 filter1.support_media_time = FALSE;
3992 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3994 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3995 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3997 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3998 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
4000 filter1.IMediaSeeking_iface.lpVtbl = NULL;
4001 filter1_pin.dir = PINDIR_INPUT;
4002 filter1.pin_count = 1;
4003 filter1_pin.QueryInternalConnections_hr = S_OK;
4004 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
4006 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
4007 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
4009 IMediaControl_Release(control);
4010 IMediaEvent_Release(eventsrc);
4011 IMediaEventSink_Release(eventsink);
4012 hr = IFilterGraph2_Release(graph);
4013 ok(!hr, "Got outstanding refcount %d.\n", hr);
4014 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
4015 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
4016 ok(filter3.ref == 1, "Got outstanding refcount %d.\n", filter3.ref);
4019 /* Remove and re-add the filter, to flush the graph's internal
4020 * IMediaSeeking cache. Don't expose IMediaSeeking when adding, to show
4021 * that it's only queried when needed. */
4022 static void flush_cached_seeking(IFilterGraph2 *graph, struct testfilter *filter)
4024 IFilterGraph2_RemoveFilter(graph, &filter->IBaseFilter_iface);
4025 filter->IMediaSeeking_iface.lpVtbl = NULL;
4026 IFilterGraph2_AddFilter(graph, &filter->IBaseFilter_iface, NULL);
4027 filter->IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
4030 static void test_graph_seeking(void)
4032 struct testfilter filter1, filter2;
4034 LONGLONG time, current, stop, earliest, latest;
4035 IFilterGraph2 *graph = create_graph();
4036 IMediaEventSink *eventsink;
4037 IMediaControl *control;
4038 IMediaSeeking *seeking;
4039 IMediaFilter *filter;
4040 unsigned int i;
4041 double rate;
4042 GUID format;
4043 HRESULT hr;
4044 DWORD caps;
4045 ULONG ref;
4047 static const GUID *const all_formats[] =
4049 NULL,
4050 &TIME_FORMAT_NONE,
4051 &TIME_FORMAT_FRAME,
4052 &TIME_FORMAT_SAMPLE,
4053 &TIME_FORMAT_FIELD,
4054 &TIME_FORMAT_BYTE,
4055 &TIME_FORMAT_MEDIA_TIME,
4056 &testguid,
4059 static const GUID *const unsupported_formats[] =
4061 &TIME_FORMAT_FRAME,
4062 &TIME_FORMAT_SAMPLE,
4063 &TIME_FORMAT_FIELD,
4064 &TIME_FORMAT_BYTE,
4065 &testguid,
4068 testfilter_init(&filter1, NULL, 0);
4069 testfilter_init(&filter2, NULL, 0);
4071 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
4072 IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
4073 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
4074 IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
4076 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
4077 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4078 todo_wine ok(!caps, "Got caps %#x.\n", caps);
4080 caps = 0;
4081 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
4082 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
4083 ok(!caps, "Got caps %#x.\n", caps);
4085 caps = AM_SEEKING_CanSeekAbsolute;
4086 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
4087 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
4088 todo_wine ok(!caps, "Got caps %#x.\n", caps);
4090 hr = IMediaSeeking_IsFormatSupported(seeking, NULL);
4091 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4093 for (i = 0; i < ARRAY_SIZE(all_formats); ++i)
4095 hr = IMediaSeeking_IsFormatSupported(seeking, all_formats[i]);
4096 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(all_formats[i]));
4099 hr = IMediaSeeking_QueryPreferredFormat(seeking, NULL);
4100 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4101 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
4102 ok(hr == S_OK, "Got hr %#x.\n", hr);
4103 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4105 hr = IMediaSeeking_GetTimeFormat(seeking, NULL);
4106 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4107 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4108 ok(hr == S_OK, "Got hr %#x.\n", hr);
4109 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4111 hr = IMediaSeeking_IsUsingTimeFormat(seeking, NULL);
4112 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4113 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
4114 ok(hr == S_OK, "Got hr %#x.\n", hr);
4115 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
4116 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
4118 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_NONE);
4119 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4121 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
4122 ok(hr == S_OK, "Got hr %#x.\n", hr);
4123 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4125 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
4126 ok(hr == S_OK, "Got hr %#x.\n", hr);
4127 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
4128 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
4130 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4131 ok(hr == S_OK, "Got hr %#x.\n", hr);
4132 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4134 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
4135 ok(hr == S_OK, "Got hr %#x.\n", hr);
4137 for (i = 0; i < ARRAY_SIZE(unsupported_formats); ++i)
4139 hr = IMediaSeeking_SetTimeFormat(seeking, unsupported_formats[i]);
4140 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(unsupported_formats[i]));
4143 time = 0xdeadbeef;
4144 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, NULL);
4145 ok(hr == S_OK, "Got hr %#x.\n", hr);
4146 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4148 time = 0xdeadbeef;
4149 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
4150 ok(hr == S_OK, "Got hr %#x.\n", hr);
4151 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4153 time = 0xdeadbeef;
4154 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
4155 ok(hr == S_OK, "Got hr %#x.\n", hr);
4156 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4158 time = 0xdeadbeef;
4159 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
4160 ok(hr == S_OK, "Got hr %#x.\n", hr);
4161 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4163 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
4164 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4165 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, NULL);
4166 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4167 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_NONE);
4168 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4169 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_NONE);
4170 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4172 time = 0xdeadbeef;
4173 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_NONE);
4174 ok(hr == S_OK, "Got hr %#x.\n", hr);
4175 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4177 time = 0xdeadbeef;
4178 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
4179 ok(hr == S_OK, "Got hr %#x.\n", hr);
4180 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4182 hr = IMediaSeeking_GetDuration(seeking, NULL);
4183 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4184 hr = IMediaSeeking_GetDuration(seeking, &time);
4185 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4187 hr = IMediaSeeking_GetStopPosition(seeking, NULL);
4188 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4189 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4190 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4192 hr = IMediaSeeking_SetPositions(seeking, &current, 0, &stop, 0);
4193 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4195 hr = IMediaSeeking_GetPositions(seeking, NULL, NULL);
4196 ok(hr == S_OK, "Got hr %#x.\n", hr);
4197 hr = IMediaSeeking_GetPositions(seeking, NULL, &stop);
4198 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4199 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4200 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4201 current = 0xdeadbeef;
4202 hr = IMediaSeeking_GetPositions(seeking, &current, NULL);
4203 ok(hr == S_OK, "Got hr %#x.\n", hr);
4204 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
4206 hr = IMediaSeeking_GetCurrentPosition(seeking, NULL);
4207 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
4208 current = 0xdeadbeef;
4209 hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
4210 ok(hr == S_OK, "Got hr %#x.\n", hr);
4211 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
4213 hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
4214 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4216 hr = IMediaSeeking_SetRate(seeking, 1.0);
4217 ok(hr == S_OK, "Got hr %#x.\n", hr);
4218 hr = IMediaSeeking_SetRate(seeking, 2.0);
4219 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4221 hr = IMediaSeeking_GetRate(seeking, &rate);
4222 ok(hr == S_OK, "Got hr %#x.\n", hr);
4223 ok(rate == 1.0, "Got rate %.16e.\n", rate);
4225 hr = IMediaSeeking_GetPreroll(seeking, &time);
4226 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4228 /* Try with filters added. Note that a filter need only expose
4229 * IMediaSeeking—no other heuristics are used to determine if it is a
4230 * renderer. */
4232 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
4233 filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
4234 filter1.support_media_time = FALSE;
4235 filter1.support_testguid = TRUE;
4237 hr = IMediaSeeking_GetDuration(seeking, &time);
4238 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4240 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
4241 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4243 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
4244 filter1.support_media_time = TRUE;
4245 filter1.support_testguid = FALSE;
4247 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
4248 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
4249 filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
4250 filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
4252 filter1.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
4253 filter2.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration;
4254 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
4255 ok(hr == S_OK, "Got hr %#x.\n", hr);
4256 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
4257 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4258 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4260 flush_cached_seeking(graph, &filter1);
4261 flush_cached_seeking(graph, &filter2);
4263 caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
4264 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
4265 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
4266 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
4268 caps = AM_SEEKING_CanDoSegments;
4269 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
4270 ok(hr == S_OK, "Got hr %#x.\n", hr);
4271 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
4273 caps = AM_SEEKING_CanGetCurrentPos;
4274 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
4275 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
4276 ok(!caps, "Got caps %#x.\n", caps);
4278 flush_cached_seeking(graph, &filter1);
4279 flush_cached_seeking(graph, &filter2);
4281 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
4282 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
4284 filter1.support_testguid = TRUE;
4285 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
4286 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4288 filter1.support_testguid = FALSE;
4289 filter2.support_testguid = TRUE;
4290 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
4291 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4293 /* Filters are not consulted about preferred formats. */
4294 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
4295 ok(hr == S_OK, "Got hr %#x.\n", hr);
4296 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4298 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4299 ok(hr == S_OK, "Got hr %#x.\n", hr);
4300 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4302 filter2.support_testguid = FALSE;
4303 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
4304 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
4306 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4307 ok(hr == S_OK, "Got hr %#x.\n", hr);
4308 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4310 filter1.support_testguid = TRUE;
4311 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
4312 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4313 todo_wine ok(IsEqualGUID(&filter1.time_format, &testguid), "Got format %s.\n",
4314 debugstr_guid(&filter1.time_format));
4315 ok(IsEqualGUID(&filter2.time_format, &TIME_FORMAT_MEDIA_TIME),
4316 "Got format %s.\n", debugstr_guid(&filter2.time_format));
4318 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4319 ok(hr == S_OK, "Got hr %#x.\n", hr);
4320 todo_wine ok(IsEqualGUID(&format, &testguid), "Got format %s.\n", wine_dbgstr_guid(&format));
4322 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
4323 ok(hr == S_OK, "Got hr %#x.\n", hr);
4324 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4326 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
4327 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
4328 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &testguid);
4329 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4331 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
4332 ok(hr == S_OK, "Got hr %#x.\n", hr);
4333 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
4334 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4335 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4337 filter2.support_testguid = TRUE;
4338 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
4339 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4340 ok(IsEqualGUID(&filter1.time_format, &TIME_FORMAT_MEDIA_TIME),
4341 "Got format %s.\n", debugstr_guid(&filter1.time_format));
4342 todo_wine ok(IsEqualGUID(&filter2.time_format, &testguid),
4343 "Got format %s.\n", debugstr_guid(&filter2.time_format));
4345 filter1.support_media_time = FALSE;
4346 flush_cached_seeking(graph, &filter1);
4348 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
4349 ok(hr == S_OK, "Got hr %#x.\n", hr);
4350 ok(caps == (AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration), "Got caps %#x.\n", caps);
4351 ok(!filter1.seeking_ref, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4352 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4354 filter1.support_media_time = TRUE;
4355 filter1.support_testguid = FALSE;
4356 flush_cached_seeking(graph, &filter1);
4358 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
4359 ok(hr == S_OK, "Got hr %#x.\n", hr);
4360 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
4361 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4362 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4364 ok(IsEqualGUID(&filter1.time_format, &TIME_FORMAT_MEDIA_TIME),
4365 "Got format %s.\n", debugstr_guid(&filter1.time_format));
4366 todo_wine ok(IsEqualGUID(&filter2.time_format, &testguid),
4367 "Got format %s.\n", debugstr_guid(&filter2.time_format));
4368 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4369 ok(hr == S_OK, "Got hr %#x.\n", hr);
4370 todo_wine ok(IsEqualGUID(&format, &testguid), "Got format %s.\n", wine_dbgstr_guid(&format));
4372 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_NONE);
4373 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
4375 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
4376 ok(hr == S_OK, "Got hr %#x.\n", hr);
4377 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
4379 time = 0xdeadbeef;
4380 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
4381 ok(hr == S_OK, "Got hr %#x.\n", hr);
4382 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
4384 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE);
4385 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4387 flush_cached_seeking(graph, &filter1);
4388 flush_cached_seeking(graph, &filter2);
4390 filter1.seek_duration = 0x12345;
4391 filter2.seek_duration = 0x23456;
4392 hr = IMediaSeeking_GetDuration(seeking, &time);
4393 ok(hr == S_OK, "Got hr %#x.\n", hr);
4394 ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
4396 filter2.seek_duration = 0x12345;
4397 filter1.seek_duration = 0x23456;
4398 hr = IMediaSeeking_GetDuration(seeking, &time);
4399 ok(hr == S_OK, "Got hr %#x.\n", hr);
4400 ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
4402 filter1.seek_hr = filter2.seek_hr = 0xbeef;
4403 hr = IMediaSeeking_GetDuration(seeking, &time);
4404 ok(hr == S_OK, "Got hr %#x.\n", hr);
4405 ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
4407 filter1.seek_hr = E_NOTIMPL;
4408 filter2.seek_hr = S_OK;
4409 hr = IMediaSeeking_GetDuration(seeking, &time);
4410 ok(hr == S_OK, "Got hr %#x.\n", hr);
4411 ok(time == 0x12345, "Got time %s.\n", wine_dbgstr_longlong(time));
4413 filter1.seek_hr = 0xdeadbeef;
4414 hr = IMediaSeeking_GetDuration(seeking, &time);
4415 ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
4417 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
4418 hr = IMediaSeeking_GetDuration(seeking, &time);
4419 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4420 filter1.seek_hr = filter2.seek_hr = S_OK;
4422 flush_cached_seeking(graph, &filter1);
4423 flush_cached_seeking(graph, &filter2);
4425 filter1.seek_stop = 0x54321;
4426 filter2.seek_stop = 0x65432;
4427 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4428 ok(hr == S_OK, "Got hr %#x.\n", hr);
4429 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
4431 filter2.seek_stop = 0x54321;
4432 filter1.seek_stop = 0x65432;
4433 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4434 ok(hr == S_OK, "Got hr %#x.\n", hr);
4435 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
4437 filter1.seek_hr = filter2.seek_hr = 0xbeef;
4438 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4439 ok(hr == S_OK, "Got hr %#x.\n", hr);
4440 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
4442 filter1.seek_hr = E_NOTIMPL;
4443 filter2.seek_hr = S_OK;
4444 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4445 ok(hr == S_OK, "Got hr %#x.\n", hr);
4446 ok(time == 0x54321, "Got time %s.\n", wine_dbgstr_longlong(time));
4448 filter1.seek_hr = 0xdeadbeef;
4449 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4450 ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
4452 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
4453 hr = IMediaSeeking_GetStopPosition(seeking, &time);
4454 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4455 filter1.seek_hr = filter2.seek_hr = S_OK;
4457 flush_cached_seeking(graph, &filter1);
4458 flush_cached_seeking(graph, &filter2);
4460 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4461 ok(hr == S_OK, "Got hr %#x.\n", hr);
4462 ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
4464 flush_cached_seeking(graph, &filter1);
4465 flush_cached_seeking(graph, &filter2);
4467 current = stop = 0xdeadbeef;
4468 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4469 ok(hr == S_OK, "Got hr %#x.\n", hr);
4470 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
4471 ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
4473 flush_cached_seeking(graph, &filter1);
4474 flush_cached_seeking(graph, &filter2);
4476 current = 0x123;
4477 stop = 0x321;
4478 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4479 &stop, AM_SEEKING_AbsolutePositioning);
4480 ok(hr == S_OK, "Got hr %#x.\n", hr);
4481 ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
4482 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
4483 ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4484 ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4485 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4486 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4488 filter1.seek_hr = filter2.seek_hr = 0xbeef;
4489 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4490 &stop, AM_SEEKING_AbsolutePositioning);
4491 ok(hr == S_OK, "Got hr %#x.\n", hr);
4493 filter1.seek_hr = E_NOTIMPL;
4494 filter2.seek_hr = S_OK;
4495 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4496 &stop, AM_SEEKING_AbsolutePositioning);
4497 ok(hr == S_OK, "Got hr %#x.\n", hr);
4499 filter1.seek_hr = 0xdeadbeef;
4500 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4501 &stop, AM_SEEKING_AbsolutePositioning);
4502 ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
4504 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
4505 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4506 &stop, AM_SEEKING_AbsolutePositioning);
4507 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4508 filter1.seek_hr = filter2.seek_hr = S_OK;
4510 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4511 ok(hr == S_OK, "Got hr %#x.\n", hr);
4512 ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
4514 current = stop = 0xdeadbeef;
4515 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4516 ok(hr == S_OK, "Got hr %#x.\n", hr);
4517 ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
4518 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
4520 current = 0x123;
4521 stop = 0x321;
4522 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
4523 &stop, AM_SEEKING_AbsolutePositioning);
4524 ok(hr == S_OK, "Got hr %#x.\n", hr);
4525 ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
4526 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
4527 ok(filter1.seek_current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4528 ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4529 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4530 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4532 current = 0x123;
4533 stop = 0x321;
4534 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4535 &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
4536 ok(hr == S_OK, "Got hr %#x.\n", hr);
4537 ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
4538 ok(stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4539 ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4540 ok(filter1.seek_stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4541 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4542 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4544 flush_cached_seeking(graph, &filter1);
4545 flush_cached_seeking(graph, &filter2);
4547 hr = IMediaSeeking_SetRate(seeking, 2.0);
4548 ok(hr == S_OK, "Got hr %#x.\n", hr);
4549 todo_wine ok(filter1.seek_rate == 2.0, "Got rate %.16e.\n", filter1.seek_rate);
4550 todo_wine ok(filter2.seek_rate == 2.0, "Got rate %.16e.\n", filter2.seek_rate);
4552 hr = IMediaSeeking_SetRate(seeking, 1.0);
4553 ok(hr == S_OK, "Got hr %#x.\n", hr);
4554 todo_wine ok(filter1.seek_rate == 1.0, "Got rate %.16e.\n", filter1.seek_rate);
4555 todo_wine ok(filter2.seek_rate == 1.0, "Got rate %.16e.\n", filter2.seek_rate);
4557 hr = IMediaSeeking_SetRate(seeking, -1.0);
4558 ok(hr == S_OK, "Got hr %#x.\n", hr);
4559 todo_wine ok(filter1.seek_rate == -1.0, "Got rate %.16e.\n", filter1.seek_rate);
4560 todo_wine ok(filter2.seek_rate == -1.0, "Got rate %.16e.\n", filter2.seek_rate);
4562 flush_cached_seeking(graph, &filter1);
4563 flush_cached_seeking(graph, &filter2);
4565 hr = IMediaSeeking_GetRate(seeking, &rate);
4566 ok(hr == S_OK, "Got hr %#x.\n", hr);
4567 todo_wine ok(rate == -1.0, "Got rate %.16e.\n", rate);
4569 hr = IMediaSeeking_SetRate(seeking, 1.0);
4570 ok(hr == S_OK, "Got hr %#x.\n", hr);
4572 /* Test how retrieving the current position behaves while the graph is
4573 * running. Apparently the graph caches the last position returned by
4574 * SetPositions() and then adds the clock offset to the stream start. */
4576 hr = IMediaControl_Run(control);
4577 ok(hr == S_OK, "Got hr %#x.\n", hr);
4579 /* Note that if the graph is running, it is paused while seeking. */
4580 current = 0;
4581 stop = 9000 * 10000;
4582 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4583 &stop, AM_SEEKING_AbsolutePositioning);
4584 ok(hr == S_OK, "Got hr %#x.\n", hr);
4586 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4587 ok(hr == S_OK, "Got hr %#x.\n", hr);
4588 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4589 ok(compare_time(time, 1234 * 10000, 40 * 10000),
4590 "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(time));
4591 current = stop = 0xdeadbeef;
4592 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4593 ok(hr == S_OK, "Got hr %#x.\n", hr);
4594 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4595 ok(compare_time(current, 1234 * 10000, 40 * 10000),
4596 "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(current));
4597 ok(stop == 9000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4599 /* This remains true even if NoFlush is specified. */
4600 current = 1000 * 10000;
4601 stop = 8000 * 10000;
4602 hr = IMediaSeeking_SetPositions(seeking, &current,
4603 AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush,
4604 &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush);
4605 ok(hr == S_OK, "Got hr %#x.\n", hr);
4607 Sleep(100);
4609 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4610 ok(hr == S_OK, "Got hr %#x.\n", hr);
4611 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4612 ok(compare_time(time, 1334 * 10000, 80 * 10000),
4613 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
4614 current = stop = 0xdeadbeef;
4615 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4616 ok(hr == S_OK, "Got hr %#x.\n", hr);
4617 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4618 ok(compare_time(current, 1334 * 10000, 80 * 10000),
4619 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
4620 ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4622 hr = IMediaControl_Pause(control);
4623 ok(hr == S_OK, "Got hr %#x.\n", hr);
4625 Sleep(100);
4626 hr = IMediaControl_Run(control);
4627 ok(hr == S_OK, "Got hr %#x.\n", hr);
4629 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4630 ok(hr == S_OK, "Got hr %#x.\n", hr);
4631 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4632 ok(compare_time(time, 1334 * 10000, 80 * 10000),
4633 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
4634 current = stop = 0xdeadbeef;
4635 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4636 ok(hr == S_OK, "Got hr %#x.\n", hr);
4637 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4638 ok(compare_time(current, 1334 * 10000, 80 * 10000),
4639 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
4640 ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4642 hr = IMediaControl_Stop(control);
4643 ok(hr == S_OK, "Got hr %#x.\n", hr);
4644 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4645 ok(hr == S_OK, "Got hr %#x.\n", hr);
4646 ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
4648 hr = IMediaFilter_SetSyncSource(filter, NULL);
4649 ok(hr == S_OK, "Got hr %#x.\n", hr);
4651 hr = IMediaControl_Run(control);
4652 ok(hr == S_OK, "Got hr %#x.\n", hr);
4654 Sleep(100);
4655 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4656 ok(hr == S_OK, "Got hr %#x.\n", hr);
4657 todo_wine ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
4658 current = stop = 0xdeadbeef;
4659 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4660 ok(hr == S_OK, "Got hr %#x.\n", hr);
4661 todo_wine ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
4662 ok(!stop, "Got time %s.\n", wine_dbgstr_longlong(stop));
4664 hr = IMediaControl_Stop(control);
4665 ok(hr == S_OK, "Got hr %#x.\n", hr);
4667 /* GetCurrentPositions() will return the stop position once all renderers
4668 * report EC_COMPLETE. Atelier Sophie depends on this behaviour. */
4670 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4671 ok(hr == S_OK, "Got hr %#x.\n", hr);
4673 filter1.seek_stop = 5000 * 10000;
4674 filter2.seek_stop = 6000 * 10000;
4676 hr = IMediaControl_Run(control);
4677 ok(hr == S_OK, "Got hr %#x.\n", hr);
4679 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4680 ok(hr == S_OK, "Got hr %#x.\n", hr);
4681 ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
4683 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
4684 ok(hr == S_OK, "Got hr %#x.\n", hr);
4686 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4687 ok(hr == S_OK, "Got hr %#x.\n", hr);
4688 ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
4690 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
4691 ok(hr == S_OK, "Got hr %#x.\n", hr);
4693 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4694 ok(hr == S_OK, "Got hr %#x.\n", hr);
4695 ok(time == 6000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
4697 hr = IMediaControl_Stop(control);
4698 ok(hr == S_OK, "Got hr %#x.\n", hr);
4700 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
4701 filter1.seek_stop = filter2.seek_stop = 0xdeadbeef;
4703 hr = IMediaControl_Run(control);
4704 ok(hr == S_OK, "Got hr %#x.\n", hr);
4706 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4707 ok(hr == S_OK, "Got hr %#x.\n", hr);
4708 ok(time < 5000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
4710 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
4711 ok(hr == S_OK, "Got hr %#x.\n", hr);
4713 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
4714 ok(hr == S_OK, "Got hr %#x.\n", hr);
4716 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4717 ok(hr == S_OK, "Got hr %#x.\n", hr);
4718 ok(time == 6000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(time));
4720 hr = IMediaControl_Stop(control);
4721 ok(hr == S_OK, "Got hr %#x.\n", hr);
4723 IMediaFilter_Release(filter);
4724 IMediaControl_Release(control);
4725 IMediaSeeking_Release(seeking);
4726 IMediaEventSink_Release(eventsink);
4728 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4729 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4731 ref = IFilterGraph2_Release(graph);
4732 ok(!ref, "Got outstanding refcount %d.\n", hr);
4733 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
4734 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
4735 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4736 ok(filter2.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4739 static void test_default_sync_source(void)
4741 struct testpin source_pin, sink1_pin, sink2_pin;
4742 struct testfilter source, sink1, sink2;
4744 IFilterGraph2 *graph = create_graph();
4745 IReferenceClock *clock;
4746 IMediaFilter *filter;
4747 HRESULT hr;
4748 ULONG ref;
4750 testsink_init(&sink1_pin);
4751 testsink_init(&sink2_pin);
4752 testsource_init(&source_pin, NULL, 0);
4753 testfilter_init(&source, &source_pin, 1);
4754 testfilter_init(&sink1, &sink1_pin, 1);
4755 testfilter_init(&sink2, &sink2_pin, 1);
4757 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
4758 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
4759 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
4760 IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink1_pin.IPin_iface, NULL);
4762 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
4764 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4765 ok(hr == S_OK, "Got hr %#x.\n", hr);
4767 hr = IMediaFilter_GetSyncSource(filter, &clock);
4768 ok(hr == S_OK, "Got hr %#x.\n", hr);
4769 ok(!!clock, "Reference clock not set.\n");
4770 IReferenceClock_Release(clock);
4772 source.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4774 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4775 ok(hr == S_OK, "Got hr %#x.\n", hr);
4777 hr = IMediaFilter_GetSyncSource(filter, &clock);
4778 ok(hr == S_OK, "Got hr %#x.\n", hr);
4779 ok(clock == &source.IReferenceClock_iface, "Got unexpected clock.\n");
4780 IReferenceClock_Release(clock);
4782 /* The documentation says that connected filters are preferred, but this
4783 * does not in fact seem to be the case. */
4785 sink2.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4787 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4788 ok(hr == S_OK, "Got hr %#x.\n", hr);
4790 hr = IMediaFilter_GetSyncSource(filter, &clock);
4791 ok(hr == S_OK, "Got hr %#x.\n", hr);
4792 todo_wine ok(clock == &sink2.IReferenceClock_iface, "Got unexpected clock.\n");
4793 IReferenceClock_Release(clock);
4795 sink1.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4797 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4798 ok(hr == S_OK, "Got hr %#x.\n", hr);
4800 hr = IMediaFilter_GetSyncSource(filter, &clock);
4801 ok(hr == S_OK, "Got hr %#x.\n", hr);
4802 todo_wine ok(clock == &sink1.IReferenceClock_iface, "Got unexpected clock.\n");
4803 IReferenceClock_Release(clock);
4805 IMediaFilter_Release(filter);
4806 ref = IFilterGraph2_Release(graph);
4807 ok(!ref, "Got outstanding refcount %d.\n", ref);
4808 ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
4809 ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
4810 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
4813 static void test_add_source_filter(void)
4815 static const char bogus_data[20] = {0xde, 0xad, 0xbe, 0xef};
4816 static const char midi_data[20] = {'M','T','h','d'};
4818 IFilterGraph2 *graph = create_graph();
4819 IFileSourceFilter *filesource;
4820 IBaseFilter *filter, *filter2;
4821 FILTER_INFO filter_info;
4822 const WCHAR *filename;
4823 WCHAR *ret_filename;
4824 AM_MEDIA_TYPE mt;
4825 CLSID clsid;
4826 HRESULT hr;
4827 ULONG ref;
4828 BOOL ret;
4829 HKEY key;
4831 /* Test a file which should be registered by extension. */
4833 filename = create_file(L"test.mp3", midi_data, sizeof(midi_data));
4834 hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter);
4835 ok(hr == S_OK, "Got hr %#x.\n", hr);
4837 hr = IBaseFilter_GetClassID(filter, &clsid);
4838 ok(hr == S_OK, "Got hr %#x.\n", hr);
4839 ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
4840 hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
4841 ok(hr == S_OK, "Got hr %#x.\n", hr);
4842 ok(!wcscmp(filter_info.achName, L"test"), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
4843 IFilterGraph_Release(filter_info.pGraph);
4845 hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
4846 ok(hr == S_OK, "Got hr %#x.\n", hr);
4847 hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
4848 ok(hr == S_OK, "Got hr %#x.\n", hr);
4849 ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
4850 wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
4851 ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
4852 ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_MPEG1Audio), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
4853 IFileSourceFilter_Release(filesource);
4855 hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter2);
4856 ok(hr == S_OK, "Got hr %#x.\n", hr);
4857 ok(filter2 != filter, "Filters shouldn't match.\n");
4858 hr = IFilterGraph2_RemoveFilter(graph, filter2);
4859 ok(hr == S_OK, "Got hr %#x.\n", hr);
4860 ref = IBaseFilter_Release(filter2);
4861 ok(!ref, "Got outstanding refcount %d.\n", ref);
4863 hr = IFilterGraph2_RemoveFilter(graph, filter);
4864 ok(hr == S_OK, "Got hr %#x.\n", hr);
4865 ref = IBaseFilter_Release(filter);
4866 ok(!ref, "Got outstanding refcount %d.\n", ref);
4867 ret = DeleteFileW(filename);
4868 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4870 /* Test a file which should be registered by signature. */
4872 filename = create_file(L"test.avi", midi_data, sizeof(midi_data));
4873 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
4874 ok(hr == S_OK, "Got hr %#x.\n", hr);
4876 hr = IBaseFilter_GetClassID(filter, &clsid);
4877 ok(hr == S_OK, "Got hr %#x.\n", hr);
4878 ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
4879 hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
4880 ok(hr == S_OK, "Got hr %#x.\n", hr);
4881 todo_wine ok(!wcscmp(filter_info.achName, filename), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
4882 IFilterGraph_Release(filter_info.pGraph);
4884 hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
4885 ok(hr == S_OK, "Got hr %#x.\n", hr);
4886 hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
4887 ok(hr == S_OK, "Got hr %#x.\n", hr);
4888 ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
4889 wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
4890 ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
4891 ok(IsEqualGUID(&mt.subtype, &MEDIATYPE_Midi), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
4892 IFileSourceFilter_Release(filesource);
4894 hr = IFilterGraph2_RemoveFilter(graph, filter);
4895 ok(hr == S_OK, "Got hr %#x.\n", hr);
4896 ref = IBaseFilter_Release(filter);
4897 ok(!ref, "Got outstanding refcount %d.\n", ref);
4898 ret = DeleteFileW(filename);
4899 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4901 if (!RegCreateKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
4902 "\\{bccddeef-0000-0000-0000-000000000000}", &key))
4904 static const GUID testfilter_clsid = {0x12345678};
4905 struct testpin testfilter_pin;
4906 struct testfilter testfilter;
4907 struct testfilter_cf cf = {{&testfilter_cf_vtbl}, &testfilter};
4908 DWORD cookie;
4910 testsource_init(&testfilter_pin, NULL, 0);
4911 testfilter_init(&testfilter, &testfilter_pin, 1);
4912 testfilter.IFileSourceFilter_iface.lpVtbl = &testfilesource_vtbl;
4914 CoRegisterClassObject(&testfilter_clsid, (IUnknown *)&cf.IClassFactory_iface,
4915 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie);
4916 RegSetValueExA(key, "0", 0, REG_SZ, (const BYTE *)"0,4,,deadbeef", 14);
4917 RegSetValueExA(key, "Source Filter", 0, REG_SZ, (const BYTE *)"{12345678-0000-0000-0000-000000000000}", 39);
4919 filename = create_file(L"test.avi", bogus_data, sizeof(bogus_data));
4920 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
4921 ok(hr == S_OK, "Got hr %#x.\n", hr);
4922 ok(filter == &testfilter.IBaseFilter_iface, "Got unexpected filter %p.\n", filter);
4924 hr = IFilterGraph2_RemoveFilter(graph, filter);
4925 ok(hr == S_OK, "Got hr %#x.\n", hr);
4926 IBaseFilter_Release(filter);
4927 ref = IBaseFilter_Release(&testfilter.IBaseFilter_iface);
4928 ok(!ref, "Got outstanding refcount %d.\n", ref);
4929 ret = DeleteFileW(filename);
4930 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4931 RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
4932 "\\{bccddeef-0000-0000-0000-000000000000}");
4933 RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}");
4934 CoRevokeClassObject(cookie);
4936 else
4937 skip("Not enough permission to register media types.\n");
4939 ref = IFilterGraph2_Release(graph);
4940 ok(!ref, "Got outstanding refcount %d.\n", ref);
4943 static HWND get_renderer_hwnd(IFilterGraph2 *graph)
4945 IEnumFilters *enum_filters;
4946 IEnumPins *enum_pins;
4947 IBaseFilter *filter;
4948 IOverlay *overlay;
4949 HWND hwnd = NULL;
4950 HRESULT hr;
4951 IPin *pin;
4953 hr = IFilterGraph2_EnumFilters(graph, &enum_filters);
4954 ok(hr == S_OK, "Got hr %#x.\n", hr);
4956 while (IEnumFilters_Next(enum_filters, 1, &filter, NULL) == S_OK)
4958 hr = IBaseFilter_EnumPins(filter, &enum_pins);
4959 ok(hr == S_OK, "Got hr %#x.\n", hr);
4960 hr = IEnumPins_Next(enum_pins, 1, &pin, NULL);
4961 ok(hr == S_OK, "Got hr %#x.\n", hr);
4962 IEnumPins_Release(enum_pins);
4964 if (SUCCEEDED(IPin_QueryInterface(pin, &IID_IOverlay, (void **)&overlay)))
4966 hr = IOverlay_GetWindowHandle(overlay, &hwnd);
4967 ok(hr == S_OK, "Got hr %#x.\n", hr);
4968 IOverlay_Release(overlay);
4971 IPin_Release(pin);
4972 IBaseFilter_Release(filter);
4975 IEnumFilters_Release(enum_filters);
4977 return hwnd;
4980 static BOOL expect_parent_message = TRUE;
4982 static LRESULT CALLBACK parent_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
4984 ok(expect_parent_message, "Got unexpected message %#x.\n", msg);
4985 return DefWindowProcA(hwnd, msg, wparam, lparam);
4988 static void test_window_threading(void)
4990 static const WNDCLASSA class =
4992 .lpfnWndProc = parent_proc,
4993 .lpszClassName = "quartz_test_parent",
4995 WCHAR *filename = load_resource(L"test.avi");
4996 IFilterGraph2 *graph = create_graph();
4997 IVideoWindow *window;
4998 HWND hwnd, parent;
4999 HRESULT hr;
5000 DWORD tid;
5001 ULONG ref;
5002 BOOL ret;
5004 RegisterClassA(&class);
5006 parent = CreateWindowA("quartz_test_parent", NULL, WS_OVERLAPPEDWINDOW,
5007 50, 50, 150, 150, NULL, NULL, NULL, NULL);
5008 ok(!!parent, "Failed to create parent window.\n");
5010 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
5011 if (FAILED(hr))
5013 skip("Cannot render test file, hr %#x.\n", hr);
5014 IFilterGraph2_Release(graph);
5015 DeleteFileW(filename);
5016 return;
5018 ok(hr == S_OK, "Got hr %#x.\n", hr);
5020 if ((hwnd = get_renderer_hwnd(graph)))
5022 tid = GetWindowThreadProcessId(hwnd, NULL);
5023 ok(tid != GetCurrentThreadId(), "Window should have been created on a separate thread.\n");
5025 /* The thread should be processing messages, or this will hang. */
5026 SendMessageA(hwnd, WM_NULL, 0, 0);
5028 /* Media Player Classic deadlocks if the parent is sent any messages
5029 * while the video window is released. In particular, we must not send
5030 * WM_PARENTNOTIFY. This is not achieved through window styles. */
5031 hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
5032 ok(hr == S_OK, "Got hr %#x.\n", hr);
5033 hr = IVideoWindow_put_Owner(window, (OAHWND)parent);
5034 ok(hr == S_OK, "Got hr %#x.\n", hr);
5035 IVideoWindow_Release(window);
5036 ok(!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_NOPARENTNOTIFY), "Window has WS_EX_NOPARENTNOTIFY.\n");
5038 else
5039 skip("Could not find renderer window.\n");
5041 SetActiveWindow(parent);
5042 expect_parent_message = FALSE;
5043 ref = IFilterGraph2_Release(graph);
5044 ok(!ref, "Got outstanding refcount %d.\n", ref);
5045 expect_parent_message = TRUE;
5047 hwnd = GetActiveWindow();
5048 ok(hwnd == parent, "Parent window lost focus, active window %p.\n", hwnd);
5050 hr = CoCreateInstance(&CLSID_FilterGraphNoThread, NULL, CLSCTX_INPROC_SERVER,
5051 &IID_IFilterGraph2, (void **)&graph);
5052 ok(hr == S_OK, "Got hr %#x.\n", hr);
5054 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
5055 ok(hr == S_OK, "Got hr %#x.\n", hr);
5057 if ((hwnd = get_renderer_hwnd(graph)))
5059 tid = GetWindowThreadProcessId(hwnd, NULL);
5060 ok(tid == GetCurrentThreadId(), "Window should be created on main thread.\n");
5062 else
5063 skip("Could not find renderer window.\n");
5065 ref = IFilterGraph2_Release(graph);
5066 ok(!ref, "Got outstanding refcount %d.\n", ref);
5068 DestroyWindow(parent);
5069 UnregisterClassA("quartz_test_parent", GetModuleHandleA(NULL));
5070 ret = DeleteFileW(filename);
5071 ok(ret, "Failed to delete file, error %u.\n", GetLastError());
5074 /* Hyperdevotion Noire needs to be able to Render() from UYVY. */
5075 static void test_autoplug_uyvy(void)
5077 static const VIDEOINFOHEADER source_format =
5079 .bmiHeader.biSize = sizeof(BITMAPINFOHEADER),
5080 .bmiHeader.biWidth = 600,
5081 .bmiHeader.biHeight = 400,
5082 .bmiHeader.biCompression = mmioFOURCC('U','Y','V','Y'),
5083 .bmiHeader.biBitCount = 16,
5085 AM_MEDIA_TYPE source_type =
5087 .majortype = MEDIATYPE_Video,
5088 .subtype = MEDIASUBTYPE_UYVY,
5089 .formattype = FORMAT_VideoInfo,
5090 .cbFormat = sizeof(source_format),
5091 .pbFormat = (BYTE *)&source_format,
5094 IFilterGraph2 *graph = create_graph();
5095 struct testpin source_pin;
5096 struct testfilter source;
5097 HRESULT hr;
5098 ULONG ref;
5100 testsource_init(&source_pin, NULL, 0);
5101 testfilter_init(&source, &source_pin, 1);
5102 source_pin.request_mt = &source_type;
5104 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, L"source");
5106 /* Windows 2008 doesn't seem to have an UYVY decoder, and the testbot chalks
5107 * failure to decode up to missing audio hardware, even though we're not
5108 * trying to render audio. */
5109 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
5110 todo_wine ok(hr == S_OK || hr == VFW_E_NO_AUDIO_HARDWARE, "Got hr %#x.\n", hr);
5112 ref = IFilterGraph2_Release(graph);
5113 ok(!ref, "Got outstanding refcount %d.\n", ref);
5114 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
5115 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
5118 START_TEST(filtergraph)
5120 CoInitializeEx(NULL, COINIT_MULTITHREADED);
5122 test_interfaces();
5123 test_render_run(L"test.avi", FALSE, TRUE);
5124 test_render_run(L"test.mpg", TRUE, TRUE);
5125 test_render_run(L"test.mp3", TRUE, FALSE);
5126 test_render_run(L"test.wav", TRUE, FALSE);
5127 test_enum_filters();
5128 test_graph_builder_render();
5129 test_graph_builder_connect();
5130 test_aggregation();
5131 test_control_delegation();
5132 test_add_remove_filter();
5133 test_connect_direct();
5134 test_sync_source();
5135 test_filter_state();
5136 test_ec_complete();
5137 test_graph_seeking();
5138 test_default_sync_source();
5139 test_add_source_filter();
5140 test_window_threading();
5141 test_autoplug_uyvy();
5143 CoUninitialize();
5144 test_render_with_multithread();