quartz/filtergraph: Always try to query IMediaSeeking if it's not cached yet.
[wine.git] / dlls / quartz / tests / filtergraph.c
blobd1c081681a3107624a3841b957b0ae1d66850103
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 todo_wine
534 ok(current == stop, "expected %s, got %s\n", wine_dbgstr_longlong(stop), wine_dbgstr_longlong(current));
536 hr = IMediaControl_Stop(control);
537 ok(SUCCEEDED(hr), "Run() failed: %#x\n", hr);
538 hr = IMediaControl_GetState(control, 1000, &state);
539 ok(hr == S_OK, "GetState() timed out\n");
541 hr = IFilterGraph2_SetDefaultSyncSource(graph);
542 ok(hr == S_OK, "SetDefaultSinkSource() failed: %#x\n", hr);
544 IMediaSeeking_Release(seeking);
545 IMediaEvent_Release(media_event);
546 IMediaControl_Release(control);
547 IMediaFilter_Release(filter);
550 static void rungraph(IFilterGraph2 *graph, BOOL video)
552 if (video)
553 test_basic_video(graph);
554 test_media_seeking(graph);
555 test_state_change(graph);
556 test_media_event(graph);
559 static HRESULT test_graph_builder_connect_file(WCHAR *filename, BOOL audio, BOOL video)
561 IBaseFilter *source_filter, *renderer;
562 IPin *pin_in, *pin_out;
563 IFilterGraph2 *graph;
564 IEnumPins *enumpins;
565 HRESULT hr;
567 if (video)
568 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
569 &IID_IBaseFilter, (void **)&renderer);
570 else
571 hr = CoCreateInstance(&CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER,
572 &IID_IBaseFilter, (void **)&renderer);
573 if (hr == VFW_E_NO_AUDIO_HARDWARE)
574 return VFW_E_CANNOT_CONNECT;
575 ok(hr == S_OK, "Got hr %#x.\n", hr);
577 graph = create_graph();
579 IBaseFilter_EnumPins(renderer, &enumpins);
580 IEnumPins_Next(enumpins, 1, &pin_in, NULL);
581 IEnumPins_Release(enumpins);
583 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &source_filter);
584 ok(hr == S_OK, "AddSourceFilter failed: %#x\n", hr);
586 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
587 ok(hr == S_OK, "AddFilter failed: %#x\n", hr);
589 hr = IBaseFilter_FindPin(source_filter, L"Output", &pin_out);
590 ok(hr == S_OK, "FindPin failed: %#x\n", hr);
591 hr = IFilterGraph2_Connect(graph, pin_out, pin_in);
593 if (SUCCEEDED(hr))
594 rungraph(graph, video);
596 IPin_Release(pin_in);
597 IPin_Release(pin_out);
598 IBaseFilter_Release(source_filter);
599 IBaseFilter_Release(renderer);
600 IFilterGraph2_Release(graph);
602 return hr;
605 static void test_render_run(const WCHAR *file, BOOL audio, BOOL video)
607 IFilterGraph2 *graph;
608 HANDLE h;
609 HRESULT hr;
610 LONG refs;
611 WCHAR *filename = load_resource(file);
613 h = CreateFileW(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
614 if (h == INVALID_HANDLE_VALUE) {
615 skip("Could not read test file %s, skipping test\n", wine_dbgstr_w(file));
616 DeleteFileW(filename);
617 return;
619 CloseHandle(h);
621 trace("running %s\n", wine_dbgstr_w(file));
623 graph = create_graph();
625 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
626 if (FAILED(hr))
628 skip("%s: codec not supported; skipping test\n", wine_dbgstr_w(file));
630 refs = IFilterGraph2_Release(graph);
631 ok(!refs, "Graph has %u references\n", refs);
633 hr = test_graph_builder_connect_file(filename, audio, video);
634 ok(hr == VFW_E_CANNOT_CONNECT, "got %#x\n", hr);
636 else
638 if (audio)
639 ok(hr == S_OK || hr == VFW_S_AUDIO_NOT_RENDERED, "Got hr %#x.\n", hr);
640 else
641 ok(hr == S_OK, "Got hr %#x.\n", hr);
642 rungraph(graph, video);
644 refs = IFilterGraph2_Release(graph);
645 ok(!refs, "Graph has %u references\n", refs);
647 hr = test_graph_builder_connect_file(filename, audio, video);
648 if (audio && video)
649 todo_wine ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
650 else
651 ok(hr == S_OK, "Got hr %#x.\n", hr);
654 /* check reference leaks */
655 h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
656 ok(h != INVALID_HANDLE_VALUE, "CreateFile failed: err=%d\n", GetLastError());
657 CloseHandle(h);
659 DeleteFileW(filename);
662 static void test_enum_filters(void)
664 IBaseFilter *filter1, *filter2, *filters[2];
665 IFilterGraph2 *graph = create_graph();
666 IEnumFilters *enum1, *enum2;
667 ULONG count, ref;
668 HRESULT hr;
670 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
671 &IID_IBaseFilter, (void **)&filter1);
672 CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
673 &IID_IBaseFilter, (void **)&filter2);
675 hr = IFilterGraph2_EnumFilters(graph, &enum1);
676 ok(hr == S_OK, "Got hr %#x.\n", hr);
678 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
679 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
681 IFilterGraph2_AddFilter(graph, filter1, NULL);
682 IFilterGraph2_AddFilter(graph, filter2, NULL);
684 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
685 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
687 hr = IEnumFilters_Reset(enum1);
688 ok(hr == S_OK, "Got hr %#x.\n", hr);
690 hr = IEnumFilters_Next(enum1, 1, filters, NULL);
691 ok(hr == S_OK, "Got hr %#x.\n", hr);
692 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
693 IBaseFilter_Release(filters[0]);
695 hr = IEnumFilters_Next(enum1, 1, filters, &count);
696 ok(hr == S_OK, "Got hr %#x.\n", hr);
697 ok(count == 1, "Got count %u.\n", count);
698 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
699 IBaseFilter_Release(filters[0]);
701 hr = IEnumFilters_Next(enum1, 1, filters, &count);
702 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
703 ok(count == 0, "Got count %u.\n", count);
705 hr = IEnumFilters_Reset(enum1);
706 ok(hr == S_OK, "Got hr %#x.\n", hr);
708 hr = IEnumFilters_Next(enum1, 2, filters, &count);
709 ok(hr == S_OK, "Got hr %#x.\n", hr);
710 ok(count == 2, "Got count %u.\n", count);
711 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
712 ok(filters[1] == filter1, "Got filter %p.\n", filters[1]);
713 IBaseFilter_Release(filters[0]);
714 IBaseFilter_Release(filters[1]);
716 IFilterGraph2_RemoveFilter(graph, filter1);
717 IFilterGraph2_AddFilter(graph, filter1, NULL);
719 hr = IEnumFilters_Next(enum1, 2, filters, &count);
720 ok(hr == VFW_E_ENUM_OUT_OF_SYNC, "Got hr %#x.\n", hr);
722 hr = IEnumFilters_Reset(enum1);
723 ok(hr == S_OK, "Got hr %#x.\n", hr);
725 hr = IEnumFilters_Next(enum1, 2, filters, &count);
726 ok(hr == S_OK, "Got hr %#x.\n", hr);
727 ok(count == 2, "Got count %u.\n", count);
728 ok(filters[0] == filter1, "Got filter %p.\n", filters[0]);
729 ok(filters[1] == filter2, "Got filter %p.\n", filters[1]);
730 IBaseFilter_Release(filters[0]);
731 IBaseFilter_Release(filters[1]);
733 hr = IEnumFilters_Reset(enum1);
734 ok(hr == S_OK, "Got hr %#x.\n", hr);
736 hr = IEnumFilters_Clone(enum1, &enum2);
737 ok(hr == S_OK, "Got hr %#x.\n", hr);
739 hr = IEnumFilters_Skip(enum2, 1);
740 ok(hr == S_OK, "Got hr %#x.\n", hr);
742 hr = IEnumFilters_Next(enum2, 2, filters, &count);
743 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
744 ok(count == 1, "Got count %u.\n", count);
745 ok(filters[0] == filter2, "Got filter %p.\n", filters[0]);
746 IBaseFilter_Release(filters[0]);
748 hr = IEnumFilters_Skip(enum1, 3);
749 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
751 IEnumFilters_Release(enum2);
752 IEnumFilters_Release(enum1);
753 ref = IFilterGraph2_Release(graph);
754 ok(!ref, "Got outstanding refcount %d.\n", ref);
757 static DWORD WINAPI call_RenderFile_multithread(LPVOID lParam)
759 WCHAR *filename = load_resource(L"test.avi");
760 IFilterGraph2 *graph = lParam;
761 HRESULT hr;
763 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
764 ok(hr == S_OK, "Got hr %#x.\n", hr);
766 rungraph(graph, TRUE);
768 return 0;
771 static void test_render_with_multithread(void)
773 IFilterGraph2 *graph;
774 HANDLE thread;
776 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
778 graph = create_graph();
780 thread = CreateThread(NULL, 0, call_RenderFile_multithread, graph, 0, NULL);
782 ok(!WaitForSingleObject(thread, 10000), "Wait timed out.\n");
783 IFilterGraph2_Release(graph);
784 CloseHandle(thread);
785 CoUninitialize();
788 struct testpin
790 IPin IPin_iface;
791 LONG ref;
792 PIN_DIRECTION dir;
793 IBaseFilter *filter;
794 IPin *peer;
795 AM_MEDIA_TYPE *mt;
796 WCHAR name[10];
797 WCHAR id[10];
799 IEnumMediaTypes IEnumMediaTypes_iface;
800 const AM_MEDIA_TYPE *types;
801 unsigned int type_count, enum_idx;
802 AM_MEDIA_TYPE *request_mt, *accept_mt;
804 HRESULT Connect_hr;
805 HRESULT EnumMediaTypes_hr;
806 HRESULT QueryInternalConnections_hr;
809 static inline struct testpin *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
811 return CONTAINING_RECORD(iface, struct testpin, IEnumMediaTypes_iface);
814 static HRESULT WINAPI testenummt_QueryInterface(IEnumMediaTypes *iface, REFIID iid, void **out)
816 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
817 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
819 *out = NULL;
820 return E_NOINTERFACE;
823 static ULONG WINAPI testenummt_AddRef(IEnumMediaTypes *iface)
825 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
826 return InterlockedIncrement(&pin->ref);
829 static ULONG WINAPI testenummt_Release(IEnumMediaTypes *iface)
831 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
832 return InterlockedDecrement(&pin->ref);
835 static HRESULT WINAPI testenummt_Next(IEnumMediaTypes *iface, ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
837 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
838 unsigned int i;
840 for (i = 0; i < count; ++i)
842 if (pin->enum_idx + i >= pin->type_count)
843 break;
845 out[i] = CoTaskMemAlloc(sizeof(*out[i]));
846 *out[i] = pin->types[pin->enum_idx + i];
849 if (fetched)
850 *fetched = i;
851 pin->enum_idx += i;
853 return (i == count) ? S_OK : S_FALSE;
856 static HRESULT WINAPI testenummt_Skip(IEnumMediaTypes *iface, ULONG count)
858 ok(0, "Unexpected call.\n");
859 return E_NOTIMPL;
862 static HRESULT WINAPI testenummt_Reset(IEnumMediaTypes *iface)
864 struct testpin *pin = impl_from_IEnumMediaTypes(iface);
865 pin->enum_idx = 0;
866 return S_OK;
869 static HRESULT WINAPI testenummt_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **out)
871 ok(0, "Unexpected call.\n");
872 return E_NOTIMPL;
875 static const IEnumMediaTypesVtbl testenummt_vtbl =
877 testenummt_QueryInterface,
878 testenummt_AddRef,
879 testenummt_Release,
880 testenummt_Next,
881 testenummt_Skip,
882 testenummt_Reset,
883 testenummt_Clone,
886 static inline struct testpin *impl_from_IPin(IPin *iface)
888 return CONTAINING_RECORD(iface, struct testpin, IPin_iface);
891 static HRESULT WINAPI testpin_QueryInterface(IPin *iface, REFIID iid, void **out)
893 struct testpin *pin = impl_from_IPin(iface);
894 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", pin, wine_dbgstr_guid(iid));
896 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IPin))
898 *out = &pin->IPin_iface;
899 IPin_AddRef(*out);
900 return S_OK;
903 *out = NULL;
904 return E_NOINTERFACE;
907 static ULONG WINAPI testpin_AddRef(IPin *iface)
909 struct testpin *pin = impl_from_IPin(iface);
910 return InterlockedIncrement(&pin->ref);
913 static ULONG WINAPI testpin_Release(IPin *iface)
915 struct testpin *pin = impl_from_IPin(iface);
916 return InterlockedDecrement(&pin->ref);
919 static HRESULT WINAPI testpin_Disconnect(IPin *iface)
921 struct testpin *pin = impl_from_IPin(iface);
922 if (winetest_debug > 1) trace("%p->Disconnect()\n", pin);
924 if (!pin->peer)
925 return S_FALSE;
927 IPin_Release(pin->peer);
928 pin->peer = NULL;
929 return S_OK;
932 static HRESULT WINAPI testpin_ConnectedTo(IPin *iface, IPin **peer)
934 struct testpin *pin = impl_from_IPin(iface);
935 if (winetest_debug > 1) trace("%p->ConnectedTo()\n", pin);
937 *peer = pin->peer;
938 if (*peer)
940 IPin_AddRef(*peer);
941 return S_OK;
943 return VFW_E_NOT_CONNECTED;
946 static HRESULT WINAPI testpin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mt)
948 ok(0, "Unexpected call.\n");
949 return E_NOTIMPL;
952 static HRESULT WINAPI testpin_QueryPinInfo(IPin *iface, PIN_INFO *info)
954 struct testpin *pin = impl_from_IPin(iface);
955 if (winetest_debug > 1) trace("%p->QueryPinInfo()\n", pin);
957 info->pFilter = pin->filter;
958 IBaseFilter_AddRef(pin->filter);
959 info->dir = pin->dir;
960 wcscpy(info->achName, pin->name);
961 return S_OK;
965 static HRESULT WINAPI testpin_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
967 struct testpin *pin = impl_from_IPin(iface);
968 if (winetest_debug > 1) trace("%p->QueryDirection()\n", pin);
970 *dir = pin->dir;
971 return S_OK;
974 static HRESULT WINAPI testpin_QueryId(IPin *iface, WCHAR **id)
976 struct testpin *pin = impl_from_IPin(iface);
977 if (winetest_debug > 1) trace("%p->QueryId()\n", iface);
978 *id = CoTaskMemAlloc(11);
979 wcscpy(*id, pin->id);
980 return S_OK;
983 static HRESULT WINAPI testpin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mt)
985 ok(0, "Unexpected call.\n");
986 return E_NOTIMPL;
989 static HRESULT WINAPI testpin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
991 struct testpin *pin = impl_from_IPin(iface);
992 if (winetest_debug > 1) trace("%p->EnumMediaTypes()\n", pin);
994 if (FAILED(pin->EnumMediaTypes_hr))
995 return pin->EnumMediaTypes_hr;
997 *out = &pin->IEnumMediaTypes_iface;
998 IEnumMediaTypes_AddRef(*out);
999 pin->enum_idx = 0;
1000 return S_OK;
1003 static HRESULT WINAPI testpin_QueryInternalConnections(IPin *iface, IPin **out, ULONG *count)
1005 struct testpin *pin = impl_from_IPin(iface);
1006 if (winetest_debug > 1) trace("%p->QueryInternalConnections()\n", pin);
1008 *count = 0;
1009 return pin->QueryInternalConnections_hr;
1012 static HRESULT WINAPI testpin_BeginFlush(IPin *iface)
1014 ok(0, "Unexpected call.\n");
1015 return E_NOTIMPL;
1018 static HRESULT WINAPI testpin_EndFlush(IPin * iface)
1020 ok(0, "Unexpected call.\n");
1021 return E_NOTIMPL;
1024 static HRESULT WINAPI testpin_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
1026 ok(0, "Unexpected call.\n");
1027 return E_NOTIMPL;
1030 static HRESULT WINAPI testpin_EndOfStream(IPin *iface)
1032 ok(0, "Unexpected call.\n");
1033 return E_NOTIMPL;
1036 static HRESULT WINAPI no_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1038 ok(0, "Unexpected call.\n");
1039 return E_NOTIMPL;
1042 static HRESULT WINAPI no_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1044 ok(0, "Unexpected call.\n");
1045 return E_NOTIMPL;
1048 static HRESULT WINAPI no_EnumMediaTypes(IPin *iface, IEnumMediaTypes **out)
1050 ok(0, "Unexpected call.\n");
1051 return E_NOTIMPL;
1054 static HRESULT WINAPI testsink_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1056 struct testpin *pin = impl_from_IPin(iface);
1057 if (winetest_debug > 1) trace("%p->ReceiveConnection(%p)\n", pin, peer);
1059 if (pin->accept_mt && memcmp(pin->accept_mt, mt, sizeof(*mt)))
1060 return VFW_E_TYPE_NOT_ACCEPTED;
1062 pin->peer = peer;
1063 IPin_AddRef(peer);
1064 return S_OK;
1067 static const IPinVtbl testsink_vtbl =
1069 testpin_QueryInterface,
1070 testpin_AddRef,
1071 testpin_Release,
1072 no_Connect,
1073 testsink_ReceiveConnection,
1074 testpin_Disconnect,
1075 testpin_ConnectedTo,
1076 testpin_ConnectionMediaType,
1077 testpin_QueryPinInfo,
1078 testpin_QueryDirection,
1079 testpin_QueryId,
1080 testpin_QueryAccept,
1081 no_EnumMediaTypes,
1082 testpin_QueryInternalConnections,
1083 testpin_EndOfStream,
1084 testpin_BeginFlush,
1085 testpin_EndFlush,
1086 testpin_NewSegment
1089 static void testpin_init(struct testpin *pin, const IPinVtbl *vtbl, PIN_DIRECTION dir)
1091 memset(pin, 0, sizeof(*pin));
1092 pin->IPin_iface.lpVtbl = vtbl;
1093 pin->IEnumMediaTypes_iface.lpVtbl = &testenummt_vtbl;
1094 pin->ref = 1;
1095 pin->dir = dir;
1096 pin->Connect_hr = S_OK;
1097 pin->EnumMediaTypes_hr = S_OK;
1098 pin->QueryInternalConnections_hr = E_NOTIMPL;
1101 static void testsink_init(struct testpin *pin)
1103 testpin_init(pin, &testsink_vtbl, PINDIR_INPUT);
1106 static HRESULT WINAPI testsource_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
1108 struct testpin *pin = impl_from_IPin(iface);
1109 HRESULT hr;
1110 if (winetest_debug > 1) trace("%p->Connect(%p)\n", pin, peer);
1112 if (FAILED(pin->Connect_hr))
1113 return pin->Connect_hr;
1115 ok(!mt, "Got media type %p.\n", mt);
1117 if (SUCCEEDED(hr = IPin_ReceiveConnection(peer, &pin->IPin_iface, pin->request_mt)))
1119 pin->peer = peer;
1120 IPin_AddRef(peer);
1121 return pin->Connect_hr;
1123 return hr;
1126 static const IPinVtbl testsource_vtbl =
1128 testpin_QueryInterface,
1129 testpin_AddRef,
1130 testpin_Release,
1131 testsource_Connect,
1132 no_ReceiveConnection,
1133 testpin_Disconnect,
1134 testpin_ConnectedTo,
1135 testpin_ConnectionMediaType,
1136 testpin_QueryPinInfo,
1137 testpin_QueryDirection,
1138 testpin_QueryId,
1139 testpin_QueryAccept,
1140 testpin_EnumMediaTypes,
1141 testpin_QueryInternalConnections,
1142 testpin_EndOfStream,
1143 testpin_BeginFlush,
1144 testpin_EndFlush,
1145 testpin_NewSegment
1148 static void testsource_init(struct testpin *pin, const AM_MEDIA_TYPE *types, int type_count)
1150 testpin_init(pin, &testsource_vtbl, PINDIR_OUTPUT);
1151 pin->types = types;
1152 pin->type_count = type_count;
1155 struct testfilter
1157 IBaseFilter IBaseFilter_iface;
1158 LONG ref;
1159 IFilterGraph *graph;
1160 WCHAR *name;
1161 IReferenceClock *clock;
1162 FILTER_STATE state;
1163 REFERENCE_TIME start_time;
1165 IEnumPins IEnumPins_iface;
1166 struct testpin *pins;
1167 unsigned int pin_count, enum_idx;
1169 HRESULT state_hr, seek_hr;
1171 IAMFilterMiscFlags IAMFilterMiscFlags_iface;
1172 ULONG misc_flags;
1174 IMediaSeeking IMediaSeeking_iface;
1175 LONG seeking_ref;
1176 DWORD seek_caps;
1177 BOOL support_testguid;
1178 LONGLONG seek_duration, seek_current, seek_stop;
1179 double seek_rate;
1181 IReferenceClock IReferenceClock_iface;
1183 IFileSourceFilter IFileSourceFilter_iface;
1184 WCHAR filename[MAX_PATH];
1187 static inline struct testfilter *impl_from_IEnumPins(IEnumPins *iface)
1189 return CONTAINING_RECORD(iface, struct testfilter, IEnumPins_iface);
1192 static HRESULT WINAPI testenumpins_QueryInterface(IEnumPins *iface, REFIID iid, void **out)
1194 ok(0, "Unexpected iid %s.\n", wine_dbgstr_guid(iid));
1195 return E_NOINTERFACE;
1198 static ULONG WINAPI testenumpins_AddRef(IEnumPins * iface)
1200 struct testfilter *filter = impl_from_IEnumPins(iface);
1201 return InterlockedIncrement(&filter->ref);
1204 static ULONG WINAPI testenumpins_Release(IEnumPins * iface)
1206 struct testfilter *filter = impl_from_IEnumPins(iface);
1207 return InterlockedDecrement(&filter->ref);
1210 static HRESULT WINAPI testenumpins_Next(IEnumPins *iface, ULONG count, IPin **out, ULONG *fetched)
1212 struct testfilter *filter = impl_from_IEnumPins(iface);
1213 unsigned int i;
1215 for (i = 0; i < count; ++i)
1217 if (filter->enum_idx + i >= filter->pin_count)
1218 break;
1220 out[i] = &filter->pins[filter->enum_idx + i].IPin_iface;
1221 IPin_AddRef(out[i]);
1224 if (fetched)
1225 *fetched = i;
1226 filter->enum_idx += i;
1228 return (i == count) ? S_OK : S_FALSE;
1231 static HRESULT WINAPI testenumpins_Skip(IEnumPins *iface, ULONG count)
1233 ok(0, "Unexpected call.\n");
1234 return E_NOTIMPL;
1237 static HRESULT WINAPI testenumpins_Reset(IEnumPins *iface)
1239 struct testfilter *filter = impl_from_IEnumPins(iface);
1240 filter->enum_idx = 0;
1241 return S_OK;
1244 static HRESULT WINAPI testenumpins_Clone(IEnumPins *iface, IEnumPins **out)
1246 ok(0, "Unexpected call.\n");
1247 return E_NOTIMPL;
1250 static const IEnumPinsVtbl testenumpins_vtbl =
1252 testenumpins_QueryInterface,
1253 testenumpins_AddRef,
1254 testenumpins_Release,
1255 testenumpins_Next,
1256 testenumpins_Skip,
1257 testenumpins_Reset,
1258 testenumpins_Clone,
1261 static inline struct testfilter *impl_from_IBaseFilter(IBaseFilter *iface)
1263 return CONTAINING_RECORD(iface, struct testfilter, IBaseFilter_iface);
1266 static HRESULT WINAPI testfilter_QueryInterface(IBaseFilter *iface, REFIID iid, void **out)
1268 struct testfilter *filter = impl_from_IBaseFilter(iface);
1269 if (winetest_debug > 1) trace("%p->QueryInterface(%s)\n", filter, wine_dbgstr_guid(iid));
1271 if (IsEqualGUID(iid, &IID_IUnknown)
1272 || IsEqualGUID(iid, &IID_IPersist)
1273 || IsEqualGUID(iid, &IID_IMediaFilter)
1274 || IsEqualGUID(iid, &IID_IBaseFilter))
1276 *out = &filter->IBaseFilter_iface;
1278 else if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags) && filter->IAMFilterMiscFlags_iface.lpVtbl)
1280 *out = &filter->IAMFilterMiscFlags_iface;
1282 else if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl)
1284 *out = &filter->IMediaSeeking_iface;
1286 else if (IsEqualGUID(iid, &IID_IReferenceClock) && filter->IReferenceClock_iface.lpVtbl)
1288 *out = &filter->IReferenceClock_iface;
1290 else if (IsEqualGUID(iid, &IID_IFileSourceFilter) && filter->IFileSourceFilter_iface.lpVtbl)
1292 *out = &filter->IFileSourceFilter_iface;
1294 else
1296 *out = NULL;
1297 return E_NOINTERFACE;
1300 IUnknown_AddRef((IUnknown *)*out);
1301 return S_OK;
1304 static ULONG WINAPI testfilter_AddRef(IBaseFilter *iface)
1306 struct testfilter *filter = impl_from_IBaseFilter(iface);
1307 return InterlockedIncrement(&filter->ref);
1310 static ULONG WINAPI testfilter_Release(IBaseFilter *iface)
1312 struct testfilter *filter = impl_from_IBaseFilter(iface);
1313 return InterlockedDecrement(&filter->ref);
1316 static HRESULT WINAPI testfilter_GetClassID(IBaseFilter *iface, CLSID *clsid)
1318 if (winetest_debug > 1) trace("%p->GetClassID()\n", iface);
1319 memset(clsid, 0xde, sizeof(*clsid));
1320 return S_OK;
1323 /* Downstream filters are always stopped before any filters they are connected
1324 * to upstream. Native actually implements this by topologically sorting filters
1325 * as they are connected. */
1326 static void check_state_transition(struct testfilter *filter, FILTER_STATE expect)
1328 FILTER_STATE state;
1329 unsigned int i;
1330 PIN_INFO info;
1332 for (i = 0; i < filter->pin_count; ++i)
1334 if (filter->pins[i].peer)
1336 IPin_QueryPinInfo(filter->pins[i].peer, &info);
1337 IBaseFilter_GetState(info.pFilter, 0, &state);
1338 if (filter->pins[i].dir == PINDIR_OUTPUT)
1339 ok(state == expect, "Expected state %d for downstream filter %p, got %d.\n",
1340 expect, info.pFilter, state);
1341 else
1342 ok(state == filter->state, "Expected state %d for upstream filter %p, got %d.\n",
1343 filter->state, info.pFilter, state);
1344 IBaseFilter_Release(info.pFilter);
1349 static HRESULT WINAPI testfilter_Stop(IBaseFilter *iface)
1351 struct testfilter *filter = impl_from_IBaseFilter(iface);
1352 if (winetest_debug > 1) trace("%p->Stop()\n", filter);
1354 check_state_transition(filter, State_Stopped);
1356 filter->state = State_Stopped;
1357 return filter->state_hr;
1360 static HRESULT WINAPI testfilter_Pause(IBaseFilter *iface)
1362 struct testfilter *filter = impl_from_IBaseFilter(iface);
1363 if (winetest_debug > 1) trace("%p->Pause()\n", filter);
1365 check_state_transition(filter, State_Paused);
1367 filter->state = State_Paused;
1368 return filter->state_hr;
1371 static HRESULT WINAPI testfilter_Run(IBaseFilter *iface, REFERENCE_TIME start)
1373 struct testfilter *filter = impl_from_IBaseFilter(iface);
1374 if (winetest_debug > 1) trace("%p->Run(%s)\n", filter, wine_dbgstr_longlong(start));
1376 check_state_transition(filter, State_Running);
1378 filter->state = State_Running;
1379 filter->start_time = start;
1380 return filter->state_hr;
1383 static HRESULT WINAPI testfilter_GetState(IBaseFilter *iface, DWORD timeout, FILTER_STATE *state)
1385 struct testfilter *filter = impl_from_IBaseFilter(iface);
1386 if (winetest_debug > 1) trace("%p->GetState(%u)\n", filter, timeout);
1388 *state = filter->state;
1389 return filter->state_hr;
1392 static HRESULT WINAPI testfilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
1394 struct testfilter *filter = impl_from_IBaseFilter(iface);
1395 if (winetest_debug > 1) trace("%p->SetSyncSource(%p)\n", filter, clock);
1397 if (filter->clock)
1398 IReferenceClock_Release(filter->clock);
1399 if (clock)
1400 IReferenceClock_AddRef(clock);
1401 filter->clock = clock;
1402 return S_OK;
1405 static HRESULT WINAPI testfilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock)
1407 ok(0, "Unexpected call.\n");
1408 return E_NOTIMPL;
1411 static HRESULT WINAPI testfilter_EnumPins(IBaseFilter *iface, IEnumPins **out)
1413 struct testfilter *filter = impl_from_IBaseFilter(iface);
1414 if (winetest_debug > 1) trace("%p->EnumPins()\n", filter);
1416 *out = &filter->IEnumPins_iface;
1417 IEnumPins_AddRef(*out);
1418 filter->enum_idx = 0;
1419 return S_OK;
1422 static HRESULT WINAPI testfilter_FindPin(IBaseFilter *iface, const WCHAR *id, IPin **pin)
1424 ok(0, "Unexpected call.\n");
1425 return E_NOTIMPL;
1428 static HRESULT WINAPI testfilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info)
1430 struct testfilter *filter = impl_from_IBaseFilter(iface);
1431 if (winetest_debug > 1) trace("%p->QueryFilterInfo()\n", filter);
1433 info->pGraph = filter->graph;
1434 if (filter->graph)
1435 IFilterGraph_AddRef(filter->graph);
1436 if (filter->name)
1437 wcscpy(info->achName, filter->name);
1438 else
1439 info->achName[0] = 0;
1440 return S_OK;
1443 static HRESULT WINAPI testfilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, const WCHAR *name)
1445 struct testfilter *filter = impl_from_IBaseFilter(iface);
1446 if (winetest_debug > 1) trace("%p->JoinFilterGraph(%p, %s)\n", filter, graph, wine_dbgstr_w(name));
1448 filter->graph = graph;
1449 heap_free(filter->name);
1450 if (name)
1452 filter->name = heap_alloc((wcslen(name) + 1) * sizeof(WCHAR));
1453 wcscpy(filter->name, name);
1455 else
1456 filter->name = NULL;
1457 return S_OK;
1460 static HRESULT WINAPI testfilter_QueryVendorInfo(IBaseFilter * iface, WCHAR **info)
1462 return E_NOTIMPL;
1465 static const IBaseFilterVtbl testfilter_vtbl =
1467 testfilter_QueryInterface,
1468 testfilter_AddRef,
1469 testfilter_Release,
1470 testfilter_GetClassID,
1471 testfilter_Stop,
1472 testfilter_Pause,
1473 testfilter_Run,
1474 testfilter_GetState,
1475 testfilter_SetSyncSource,
1476 testfilter_GetSyncSource,
1477 testfilter_EnumPins,
1478 testfilter_FindPin,
1479 testfilter_QueryFilterInfo,
1480 testfilter_JoinFilterGraph,
1481 testfilter_QueryVendorInfo
1484 static struct testfilter *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
1486 return CONTAINING_RECORD(iface, struct testfilter, IAMFilterMiscFlags_iface);
1489 static HRESULT WINAPI testmiscflags_QueryInterface(IAMFilterMiscFlags *iface, REFIID iid, void **out)
1491 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1492 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1495 static ULONG WINAPI testmiscflags_AddRef(IAMFilterMiscFlags *iface)
1497 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1498 return InterlockedIncrement(&filter->ref);
1501 static ULONG WINAPI testmiscflags_Release(IAMFilterMiscFlags *iface)
1503 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1504 return InterlockedDecrement(&filter->ref);
1507 static ULONG WINAPI testmiscflags_GetMiscFlags(IAMFilterMiscFlags *iface)
1509 struct testfilter *filter = impl_from_IAMFilterMiscFlags(iface);
1510 if (winetest_debug > 1) trace("%p->GetMiscFlags()\n", filter);
1511 return filter->misc_flags;
1514 static const IAMFilterMiscFlagsVtbl testmiscflags_vtbl =
1516 testmiscflags_QueryInterface,
1517 testmiscflags_AddRef,
1518 testmiscflags_Release,
1519 testmiscflags_GetMiscFlags,
1522 static struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface)
1524 return CONTAINING_RECORD(iface, struct testfilter, IMediaSeeking_iface);
1527 static HRESULT WINAPI testseek_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out)
1529 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1530 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1533 static ULONG WINAPI testseek_AddRef(IMediaSeeking *iface)
1535 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1536 InterlockedIncrement(&filter->seeking_ref);
1537 return InterlockedIncrement(&filter->ref);
1540 static ULONG WINAPI testseek_Release(IMediaSeeking *iface)
1542 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1543 InterlockedDecrement(&filter->seeking_ref);
1544 return InterlockedDecrement(&filter->ref);
1547 static HRESULT WINAPI testseek_GetCapabilities(IMediaSeeking *iface, DWORD *caps)
1549 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1550 if (winetest_debug > 1) trace("%p->GetCapabilities()\n", iface);
1551 *caps = filter->seek_caps;
1552 return S_OK;
1555 static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *caps)
1557 ok(0, "Unexpected call.\n");
1558 return E_NOTIMPL;
1561 static HRESULT WINAPI testseek_IsFormatSupported(IMediaSeeking *iface, const GUID *format)
1563 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1564 if (winetest_debug > 1) trace("%p->IsFormatSupported(%s)\n", iface, wine_dbgstr_guid(format));
1565 if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
1566 return S_FALSE;
1567 return S_OK;
1570 static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *format)
1572 if (winetest_debug > 1) trace("%p->QueryPreferredFormat()\n", iface);
1573 return E_NOTIMPL;
1576 static HRESULT WINAPI testseek_GetTimeFormat(IMediaSeeking *iface, GUID *format)
1578 ok(0, "Unexpected call.\n");
1579 return E_NOTIMPL;
1582 static HRESULT WINAPI testseek_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format)
1584 if (winetest_debug > 1) trace("%p->IsUsingTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
1585 return S_OK;
1588 static HRESULT WINAPI testseek_SetTimeFormat(IMediaSeeking *iface, const GUID *format)
1590 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1591 if (winetest_debug > 1) trace("%p->SetTimeFormat(%s)\n", iface, wine_dbgstr_guid(format));
1592 if (IsEqualGUID(format, &testguid) && !filter->support_testguid)
1593 return E_INVALIDARG;
1594 return S_OK;
1597 static HRESULT WINAPI testseek_GetDuration(IMediaSeeking *iface, LONGLONG *duration)
1599 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1600 if (winetest_debug > 1) trace("%p->GetDuration()\n", iface);
1601 *duration = filter->seek_duration;
1602 return S_OK;
1605 static HRESULT WINAPI testseek_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop)
1607 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1608 if (winetest_debug > 1) trace("%p->GetStopPosition()\n", iface);
1609 *stop = filter->seek_stop;
1610 return filter->seek_hr;
1613 static HRESULT WINAPI testseek_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current)
1615 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1616 if (winetest_debug > 1) trace("%p->GetCurrentPosition()\n", iface);
1617 ok(!filter->clock, "GetCurrentPosition() should only be called if there is no sync source.\n");
1618 *current = 0xdeadbeef;
1619 return S_OK;
1622 static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target,
1623 const GUID *target_format, LONGLONG source, const GUID *source_format)
1625 ok(0, "Unexpected call.\n");
1626 return E_NOTIMPL;
1629 static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *current,
1630 DWORD current_flags, LONGLONG *stop, DWORD stop_flags )
1632 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1633 if (winetest_debug > 1) trace("%p->SetPositions(%s, %#x, %s, %#x)\n",
1634 iface, wine_dbgstr_longlong(*current), current_flags, wine_dbgstr_longlong(*stop), stop_flags);
1635 ok(filter->state != State_Running, "Filter should be paused or stopped while seeking.\n");
1636 filter->seek_current = *current;
1637 filter->seek_stop = *stop;
1638 *current = 12340000;
1639 *stop = 43210000;
1640 return filter->seek_hr;
1643 static HRESULT WINAPI testseek_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop)
1645 ok(0, "Unexpected call.\n");
1646 return E_NOTIMPL;
1649 static HRESULT WINAPI testseek_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest)
1651 ok(0, "Unexpected call.\n");
1652 return E_NOTIMPL;
1655 static HRESULT WINAPI testseek_SetRate(IMediaSeeking *iface, double rate)
1657 struct testfilter *filter = impl_from_IMediaSeeking(iface);
1658 if (winetest_debug > 1) trace("%p->SetRate(%.16e)\n", iface, rate);
1659 filter->seek_rate = rate;
1660 return S_OK;
1663 static HRESULT WINAPI testseek_GetRate(IMediaSeeking *iface, double *rate)
1665 ok(0, "Unexpected call.\n");
1666 return E_NOTIMPL;
1669 static HRESULT WINAPI testseek_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll)
1671 if (winetest_debug > 1) trace("%p->GetPreroll()\n", iface);
1672 return E_NOTIMPL;
1675 static const IMediaSeekingVtbl testseek_vtbl =
1677 testseek_QueryInterface,
1678 testseek_AddRef,
1679 testseek_Release,
1680 testseek_GetCapabilities,
1681 testseek_CheckCapabilities,
1682 testseek_IsFormatSupported,
1683 testseek_QueryPreferredFormat,
1684 testseek_GetTimeFormat,
1685 testseek_IsUsingTimeFormat,
1686 testseek_SetTimeFormat,
1687 testseek_GetDuration,
1688 testseek_GetStopPosition,
1689 testseek_GetCurrentPosition,
1690 testseek_ConvertTimeFormat,
1691 testseek_SetPositions,
1692 testseek_GetPositions,
1693 testseek_GetAvailable,
1694 testseek_SetRate,
1695 testseek_GetRate,
1696 testseek_GetPreroll,
1699 static struct testfilter *impl_from_IReferenceClock(IReferenceClock *iface)
1701 return CONTAINING_RECORD(iface, struct testfilter, IReferenceClock_iface);
1704 static HRESULT WINAPI testclock_QueryInterface(IReferenceClock *iface, REFIID iid, void **out)
1706 struct testfilter *filter = impl_from_IReferenceClock(iface);
1707 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1710 static ULONG WINAPI testclock_AddRef(IReferenceClock *iface)
1712 struct testfilter *filter = impl_from_IReferenceClock(iface);
1713 return InterlockedIncrement(&filter->ref);
1716 static ULONG WINAPI testclock_Release(IReferenceClock *iface)
1718 struct testfilter *filter = impl_from_IReferenceClock(iface);
1719 return InterlockedDecrement(&filter->ref);
1722 static HRESULT WINAPI testclock_GetTime(IReferenceClock *iface, REFERENCE_TIME *time)
1724 ok(0, "Unexpected call.\n");
1725 return E_NOTIMPL;
1728 static HRESULT WINAPI testclock_AdviseTime(IReferenceClock *iface,
1729 REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
1731 ok(0, "Unexpected call.\n");
1732 return E_NOTIMPL;
1735 static HRESULT WINAPI testclock_AdvisePeriodic(IReferenceClock *iface,
1736 REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie)
1738 ok(0, "Unexpected call.\n");
1739 return E_NOTIMPL;
1742 static HRESULT WINAPI testclock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
1744 ok(0, "Unexpected call.\n");
1745 return E_NOTIMPL;
1748 static const IReferenceClockVtbl testclock_vtbl =
1750 testclock_QueryInterface,
1751 testclock_AddRef,
1752 testclock_Release,
1753 testclock_GetTime,
1754 testclock_AdviseTime,
1755 testclock_AdvisePeriodic,
1756 testclock_Unadvise,
1759 static struct testfilter *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
1761 return CONTAINING_RECORD(iface, struct testfilter, IFileSourceFilter_iface);
1764 static HRESULT WINAPI testfilesource_QueryInterface(IFileSourceFilter *iface, REFIID iid, void **out)
1766 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1767 return IBaseFilter_QueryInterface(&filter->IBaseFilter_iface, iid, out);
1770 static ULONG WINAPI testfilesource_AddRef(IFileSourceFilter *iface)
1772 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1773 return InterlockedIncrement(&filter->ref);
1776 static ULONG WINAPI testfilesource_Release(IFileSourceFilter *iface)
1778 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1779 return InterlockedDecrement(&filter->ref);
1782 static HRESULT WINAPI testfilesource_Load(IFileSourceFilter *iface,
1783 const WCHAR *filename, const AM_MEDIA_TYPE *mt)
1785 struct testfilter *filter = impl_from_IFileSourceFilter(iface);
1786 if (winetest_debug > 1) trace("%p->Load()\n", iface);
1788 wcscpy(filter->filename, filename);
1790 return S_OK;
1793 static HRESULT WINAPI testfilesource_GetCurFile(IFileSourceFilter *iface,
1794 WCHAR **filename, AM_MEDIA_TYPE *mt)
1796 ok(0, "Unexpected call.\n");
1797 return E_NOTIMPL;
1800 static const IFileSourceFilterVtbl testfilesource_vtbl =
1802 testfilesource_QueryInterface,
1803 testfilesource_AddRef,
1804 testfilesource_Release,
1805 testfilesource_Load,
1806 testfilesource_GetCurFile,
1809 struct testfilter_cf
1811 IClassFactory IClassFactory_iface;
1812 struct testfilter *filter;
1815 static void testfilter_init(struct testfilter *filter, struct testpin *pins, int pin_count)
1817 unsigned int i;
1819 memset(filter, 0, sizeof(*filter));
1820 filter->IBaseFilter_iface.lpVtbl = &testfilter_vtbl;
1821 filter->IEnumPins_iface.lpVtbl = &testenumpins_vtbl;
1822 filter->ref = 1;
1823 filter->pins = pins;
1824 filter->pin_count = pin_count;
1825 for (i = 0; i < pin_count; i++)
1826 pins[i].filter = &filter->IBaseFilter_iface;
1827 filter->state = State_Stopped;
1830 static HRESULT WINAPI testfilter_cf_QueryInterface(IClassFactory *iface, REFIID iid, void **out)
1832 if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory))
1834 *out = iface;
1835 return S_OK;
1838 *out = NULL;
1839 return E_NOINTERFACE;
1842 static ULONG WINAPI testfilter_cf_AddRef(IClassFactory *iface)
1844 return 2;
1847 static ULONG WINAPI testfilter_cf_Release(IClassFactory *iface)
1849 return 1;
1852 static HRESULT WINAPI testfilter_cf_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **out)
1854 struct testfilter_cf *factory = CONTAINING_RECORD(iface, struct testfilter_cf, IClassFactory_iface);
1856 return IBaseFilter_QueryInterface(&factory->filter->IBaseFilter_iface, iid, out);
1859 static HRESULT WINAPI testfilter_cf_LockServer(IClassFactory *iface, BOOL lock)
1861 return E_NOTIMPL;
1864 static IClassFactoryVtbl testfilter_cf_vtbl =
1866 testfilter_cf_QueryInterface,
1867 testfilter_cf_AddRef,
1868 testfilter_cf_Release,
1869 testfilter_cf_CreateInstance,
1870 testfilter_cf_LockServer,
1873 static void test_graph_builder_render(void)
1875 static const GUID sink1_clsid = {0x12345678};
1876 static const GUID sink2_clsid = {0x87654321};
1877 AM_MEDIA_TYPE source_type = {{0}};
1878 struct testpin source_pin, sink1_pin, sink2_pin, parser_pins[2];
1879 struct testfilter source, sink1, sink2, parser;
1880 struct testfilter_cf sink1_cf = { {&testfilter_cf_vtbl}, &sink1 };
1881 struct testfilter_cf sink2_cf = { {&testfilter_cf_vtbl}, &sink2 };
1883 IFilterGraph2 *graph = create_graph();
1884 REGFILTERPINS2 regpins = {0};
1885 REGPINTYPES regtypes = {0};
1886 REGFILTER2 regfilter = {0};
1887 IFilterMapper2 *mapper;
1888 DWORD cookie1, cookie2;
1889 HRESULT hr;
1890 ULONG ref;
1892 memset(&source_type.majortype, 0xcc, sizeof(GUID));
1893 testsource_init(&source_pin, &source_type, 1);
1894 testfilter_init(&source, &source_pin, 1);
1895 testsink_init(&sink1_pin);
1896 testfilter_init(&sink1, &sink1_pin, 1);
1897 testsink_init(&sink2_pin);
1898 testfilter_init(&sink2, &sink2_pin, 1);
1899 testsink_init(&parser_pins[0]);
1900 testsource_init(&parser_pins[1], &source_type, 1);
1901 testfilter_init(&parser, parser_pins, 2);
1903 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1904 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1905 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
1907 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1908 ok(hr == S_OK, "Got hr %#x.\n", hr);
1909 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1910 IFilterGraph2_Disconnect(graph, source_pin.peer);
1911 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1913 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1914 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1916 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1917 ok(hr == S_OK, "Got hr %#x.\n", hr);
1918 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1920 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1921 IFilterGraph2_Disconnect(graph, &sink1_pin.IPin_iface);
1923 /* No preference is given to smaller chains. */
1925 IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
1927 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1928 ok(hr == S_OK, "Got hr %#x.\n", hr);
1929 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1930 ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
1931 IFilterGraph2_Disconnect(graph, source_pin.peer);
1932 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1933 IFilterGraph2_Disconnect(graph, parser_pins[0].peer);
1934 IFilterGraph2_Disconnect(graph, &parser_pins[0].IPin_iface);
1936 IFilterGraph2_RemoveFilter(graph, &sink1.IBaseFilter_iface);
1937 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
1939 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1940 ok(hr == S_OK, "Got hr %#x.\n", hr);
1941 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
1942 IFilterGraph2_Disconnect(graph, source_pin.peer);
1943 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1945 /* A pin whose name (not ID) begins with a tilde is not rendered. */
1947 IFilterGraph2_RemoveFilter(graph, &sink2.IBaseFilter_iface);
1948 IFilterGraph2_RemoveFilter(graph, &parser.IBaseFilter_iface);
1949 IFilterGraph2_AddFilter(graph, &parser.IBaseFilter_iface, NULL);
1951 parser_pins[1].name[0] = '~';
1952 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1953 ok(hr == S_OK, "Got hr %#x.\n", hr);
1954 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1955 ok(!parser_pins[1].peer, "Got peer %p.\n", parser_pins[1].peer);
1956 ok(!sink1_pin.peer, "Got peer %p.\n", sink1_pin.peer);
1957 IFilterGraph2_Disconnect(graph, source_pin.peer);
1958 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1960 parser_pins[1].name[0] = 0;
1961 parser_pins[1].id[0] = '~';
1962 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
1963 ok(hr == S_OK, "Got hr %#x.\n", hr);
1964 ok(source_pin.peer == &parser_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
1965 ok(parser_pins[1].peer == &sink1_pin.IPin_iface, "Got peer %p.\n", parser_pins[1].peer);
1966 IFilterGraph2_Disconnect(graph, source_pin.peer);
1967 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
1969 ref = IFilterGraph2_Release(graph);
1970 ok(!ref, "Got outstanding refcount %d.\n", ref);
1972 /* Test enumeration of filters from the registry. */
1974 graph = create_graph();
1975 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
1977 CoRegisterClassObject(&sink1_clsid, (IUnknown *)&sink1_cf.IClassFactory_iface,
1978 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
1979 CoRegisterClassObject(&sink2_clsid, (IUnknown *)&sink2_cf.IClassFactory_iface,
1980 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
1982 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
1983 &IID_IFilterMapper2, (void **)&mapper);
1985 regfilter.dwVersion = 2;
1986 regfilter.dwMerit = MERIT_UNLIKELY;
1987 regfilter.cPins2 = 1;
1988 regfilter.rgPins2 = &regpins;
1989 regpins.dwFlags = 0;
1990 regpins.cInstances = 1;
1991 regpins.nMediaTypes = 1;
1992 regpins.lpMediaType = &regtypes;
1993 regtypes.clsMajorType = &source_type.majortype;
1994 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
1995 hr = IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
1996 if (hr == E_ACCESSDENIED)
1998 skip("Not enough permission to register filters.\n");
1999 goto out;
2001 ok(hr == S_OK, "Got hr %#x.\n", hr);
2003 regpins.dwFlags = REG_PINFLAG_B_RENDERER;
2004 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2006 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2007 ok(hr == S_OK, "Got hr %#x.\n", hr);
2008 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
2009 "Got peer %p.\n", source_pin.peer);
2011 ref = IFilterGraph2_Release(graph);
2012 ok(!ref, "Got outstanding refcount %d.\n", ref);
2014 /* Preference is given to filters already in the graph. */
2016 graph = create_graph();
2017 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2018 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
2020 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2021 ok(hr == S_OK, "Got hr %#x.\n", hr);
2022 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2024 ref = IFilterGraph2_Release(graph);
2025 ok(!ref, "Got outstanding refcount %d.\n", ref);
2027 /* No preference is given to renderer filters. */
2029 graph = create_graph();
2030 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2032 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2033 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2035 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2036 regpins.dwFlags = 0;
2037 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2039 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2040 ok(hr == S_OK, "Got hr %#x.\n", hr);
2041 ok(source_pin.peer == &sink2_pin.IPin_iface || source_pin.peer == &sink1_pin.IPin_iface,
2042 "Got peer %p.\n", source_pin.peer);
2044 ref = IFilterGraph2_Release(graph);
2045 ok(!ref, "Got outstanding refcount %d.\n", ref);
2047 /* Preference is given to filters with higher merit. */
2049 graph = create_graph();
2050 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2052 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2053 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2055 regfilter.dwMerit = MERIT_UNLIKELY;
2056 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2057 regfilter.dwMerit = MERIT_PREFERRED;
2058 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2060 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2061 ok(hr == S_OK, "Got hr %#x.\n", hr);
2062 ok(source_pin.peer == &sink2_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2064 ref = IFilterGraph2_Release(graph);
2065 ok(!ref, "Got outstanding refcount %d.\n", ref);
2067 graph = create_graph();
2068 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2070 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2071 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2073 regfilter.dwMerit = MERIT_PREFERRED;
2074 IFilterMapper2_RegisterFilter(mapper, &sink1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2075 regfilter.dwMerit = MERIT_UNLIKELY;
2076 IFilterMapper2_RegisterFilter(mapper, &sink2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2078 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
2079 ok(hr == S_OK, "Got hr %#x.\n", hr);
2080 ok(source_pin.peer == &sink1_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2082 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink1_clsid);
2083 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &sink2_clsid);
2085 out:
2086 CoRevokeClassObject(cookie1);
2087 CoRevokeClassObject(cookie2);
2088 IFilterMapper2_Release(mapper);
2089 ref = IFilterGraph2_Release(graph);
2090 ok(!ref, "Got outstanding refcount %d.\n", ref);
2091 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
2092 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
2093 ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
2094 ok(sink1_pin.ref == 1, "Got outstanding refcount %d.\n", sink1_pin.ref);
2095 ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
2096 ok(sink2_pin.ref == 1, "Got outstanding refcount %d.\n", sink2_pin.ref);
2097 ok(parser.ref == 1, "Got outstanding refcount %d.\n", parser.ref);
2098 ok(parser_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser_pins[0].ref);
2099 ok(parser_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser_pins[1].ref);
2102 static void test_graph_builder_connect(void)
2104 static const GUID parser1_clsid = {0x12345678};
2105 static const GUID parser2_clsid = {0x87654321};
2106 AM_MEDIA_TYPE source_type = {{0}}, sink_type = {{0}}, parser3_type = {{0}};
2107 struct testpin source_pin, sink_pin, sink2_pin, parser1_pins[3], parser2_pins[2], parser3_pins[2];
2108 struct testfilter source, sink, sink2, parser1, parser2, parser3;
2109 struct testfilter_cf parser1_cf = { {&testfilter_cf_vtbl}, &parser1 };
2110 struct testfilter_cf parser2_cf = { {&testfilter_cf_vtbl}, &parser2 };
2112 IFilterGraph2 *graph = create_graph();
2113 REGFILTERPINS2 regpins[2] = {{0}};
2114 REGPINTYPES regtypes = {0};
2115 REGFILTER2 regfilter = {0};
2116 IFilterMapper2 *mapper;
2117 DWORD cookie1, cookie2;
2118 HRESULT hr;
2119 ULONG ref;
2121 memset(&source_type.majortype, 0xcc, sizeof(GUID));
2122 memset(&sink_type.majortype, 0x66, sizeof(GUID));
2123 testsource_init(&source_pin, &source_type, 1);
2124 source_pin.request_mt = &source_type;
2125 testfilter_init(&source, &source_pin, 1);
2126 testsink_init(&sink_pin);
2127 testfilter_init(&sink, &sink_pin, 1);
2128 testsink_init(&sink2_pin);
2129 testfilter_init(&sink2, &sink2_pin, 1);
2131 testsink_init(&parser1_pins[0]);
2132 testsource_init(&parser1_pins[1], &sink_type, 1);
2133 parser1_pins[1].request_mt = &sink_type;
2134 testsource_init(&parser1_pins[2], &sink_type, 1);
2135 parser1_pins[2].request_mt = &sink_type;
2136 testfilter_init(&parser1, parser1_pins, 3);
2137 parser1.pin_count = 2;
2139 testsink_init(&parser2_pins[0]);
2140 testsource_init(&parser2_pins[1], &sink_type, 1);
2141 parser2_pins[1].request_mt = &sink_type;
2142 testfilter_init(&parser2, parser2_pins, 2);
2144 testsink_init(&parser3_pins[0]);
2145 testsource_init(&parser3_pins[1], &sink_type, 1);
2146 parser3_pins[1].request_mt = &parser3_type;
2147 testfilter_init(&parser3, parser3_pins, 2);
2149 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2150 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2152 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2153 ok(hr == S_OK, "Got hr %#x.\n", hr);
2154 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2155 IFilterGraph2_Disconnect(graph, source_pin.peer);
2156 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2158 for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
2159 ++source_pin.Connect_hr)
2161 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2162 ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
2163 hr, source_pin.Connect_hr);
2164 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2165 IFilterGraph2_Disconnect(graph, source_pin.peer);
2166 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2168 source_pin.Connect_hr = S_OK;
2170 sink_pin.accept_mt = &sink_type;
2171 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2172 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2173 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2175 for (source_pin.Connect_hr = 0x80040200; source_pin.Connect_hr <= 0x800402ff;
2176 ++source_pin.Connect_hr)
2178 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2179 if (source_pin.Connect_hr == VFW_E_NOT_CONNECTED
2180 || source_pin.Connect_hr == VFW_E_NO_AUDIO_HARDWARE)
2181 ok(hr == source_pin.Connect_hr, "Got hr %#x for Connect() hr %#x.\n",
2182 hr, source_pin.Connect_hr);
2183 else
2184 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x for Connect() hr %#x.\n",
2185 hr, source_pin.Connect_hr);
2186 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2187 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2189 source_pin.Connect_hr = S_OK;
2191 for (source_pin.EnumMediaTypes_hr = 0x80040200; source_pin.EnumMediaTypes_hr <= 0x800402ff;
2192 ++source_pin.EnumMediaTypes_hr)
2194 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2195 ok(hr == source_pin.EnumMediaTypes_hr, "Got hr %#x for EnumMediaTypes() hr %#x.\n",
2196 hr, source_pin.EnumMediaTypes_hr);
2197 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2198 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2200 source_pin.EnumMediaTypes_hr = S_OK;
2202 /* Test usage of intermediate filters. Similarly to Render(), filters are
2203 * simply tried in enumeration order. */
2205 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2206 IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
2208 sink_pin.accept_mt = NULL;
2209 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2210 ok(hr == S_OK, "Got hr %#x.\n", 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 sink_pin.accept_mt = &sink_type;
2216 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2217 ok(hr == S_OK, "Got hr %#x.\n", hr);
2218 ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2219 ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2220 IFilterGraph2_Disconnect(graph, source_pin.peer);
2221 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2222 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2223 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2225 for (source_pin.Connect_hr = 0x00040200; source_pin.Connect_hr <= 0x000402ff;
2226 ++source_pin.Connect_hr)
2228 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2229 ok(hr == S_OK, "Got hr %#x for Connect() hr %#x.\n", hr, source_pin.Connect_hr);
2230 ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2231 ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2232 IFilterGraph2_Disconnect(graph, source_pin.peer);
2233 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2234 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2235 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2237 source_pin.Connect_hr = S_OK;
2239 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
2240 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2242 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2243 ok(hr == S_OK, "Got hr %#x.\n", hr);
2244 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2245 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2246 IFilterGraph2_Disconnect(graph, source_pin.peer);
2247 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2248 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2249 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2251 /* No preference is given to smaller chains. */
2253 IFilterGraph2_AddFilter(graph, &parser3.IBaseFilter_iface, NULL);
2254 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2255 ok(hr == S_OK, "Got hr %#x.\n", hr);
2256 ok(source_pin.peer == &parser3_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2257 ok(parser3_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser3_pins[1].peer);
2258 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2259 IFilterGraph2_Disconnect(graph, source_pin.peer);
2260 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2261 IFilterGraph2_Disconnect(graph, parser3_pins[0].peer);
2262 IFilterGraph2_Disconnect(graph, &parser3_pins[0].IPin_iface);
2263 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2264 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2266 IFilterGraph2_RemoveFilter(graph, &parser3.IBaseFilter_iface);
2267 IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
2269 /* Extra source pins on an intermediate filter are not rendered. */
2271 IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
2272 parser1.pin_count = 3;
2273 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2274 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
2276 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2277 todo_wine
2278 ok(hr == VFW_S_PARTIAL_RENDER, "Got hr %#x.\n", hr);
2279 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2280 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2281 ok(!parser1_pins[2].peer, "Got peer %p.\n", parser1_pins[2].peer);
2282 IFilterGraph2_Disconnect(graph, source_pin.peer);
2283 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2284 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2285 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2286 parser1.pin_count = 2;
2288 /* QueryInternalConnections is not used to find output pins. */
2290 parser1_pins[1].QueryInternalConnections_hr = S_OK;
2291 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2292 ok(hr == S_OK, "Got hr %#x.\n", hr);
2293 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2294 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2295 IFilterGraph2_Disconnect(graph, source_pin.peer);
2296 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2297 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2298 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2299 parser1_pins[1].QueryInternalConnections_hr = E_NOTIMPL;
2301 /* A pin whose name (not ID) begins with a tilde is not connected. */
2303 parser1_pins[1].name[0] = '~';
2304 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2305 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2306 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2308 parser1.pin_count = 3;
2309 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2310 ok(hr == S_OK, "Got hr %#x.\n", hr);
2311 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2312 ok(sink_pin.peer == &parser1_pins[2].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2313 IFilterGraph2_Disconnect(graph, source_pin.peer);
2314 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2315 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2316 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2317 parser1.pin_count = 2;
2319 parser1_pins[1].name[0] = 0;
2320 parser1_pins[1].id[0] = '~';
2321 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2322 ok(hr == S_OK, "Got hr %#x.\n", hr);
2323 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2324 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2325 IFilterGraph2_Disconnect(graph, source_pin.peer);
2326 IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2327 IFilterGraph2_Disconnect(graph, sink_pin.peer);
2328 IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2330 hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
2331 ok(hr == VFW_E_CANNOT_CONNECT, "Got hr %#x.\n", hr);
2333 parser1_pins[0].QueryInternalConnections_hr = S_OK;
2334 hr = IFilterGraph2_Connect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface);
2335 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
2336 parser1_pins[0].QueryInternalConnections_hr = E_NOTIMPL;
2338 ref = IFilterGraph2_Release(graph);
2339 ok(!ref, "Got outstanding refcount %d.\n", ref);
2341 /* Test enumeration of filters from the registry. */
2343 graph = create_graph();
2344 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2345 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2347 CoRegisterClassObject(&parser1_clsid, (IUnknown *)&parser1_cf.IClassFactory_iface,
2348 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie1);
2349 CoRegisterClassObject(&parser2_clsid, (IUnknown *)&parser2_cf.IClassFactory_iface,
2350 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie2);
2352 CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
2353 &IID_IFilterMapper2, (void **)&mapper);
2355 regfilter.dwVersion = 2;
2356 regfilter.dwMerit = MERIT_UNLIKELY;
2357 regfilter.cPins2 = 2;
2358 regfilter.rgPins2 = regpins;
2359 regpins[0].dwFlags = 0;
2360 regpins[0].cInstances = 1;
2361 regpins[0].nMediaTypes = 1;
2362 regpins[0].lpMediaType = &regtypes;
2363 regpins[1].dwFlags = REG_PINFLAG_B_OUTPUT;
2364 regpins[1].cInstances = 1;
2365 regpins[1].nMediaTypes = 1;
2366 regpins[1].lpMediaType = &regtypes;
2367 regtypes.clsMajorType = &source_type.majortype;
2368 regtypes.clsMinorType = &MEDIASUBTYPE_NULL;
2369 hr = IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2370 if (hr == E_ACCESSDENIED)
2372 skip("Not enough permission to register filters.\n");
2373 goto out;
2375 ok(hr == S_OK, "Got hr %#x.\n", hr);
2377 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2379 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2380 ok(hr == S_OK, "Got hr %#x.\n", hr);
2381 ok(source_pin.peer == &parser1_pins[0].IPin_iface
2382 || source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2383 ok(sink_pin.peer == &parser1_pins[1].IPin_iface
2384 || sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2386 ref = IFilterGraph2_Release(graph);
2387 ok(!ref, "Got outstanding refcount %d.\n", ref);
2389 /* Preference is given to filters already in the graph. */
2391 graph = create_graph();
2392 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2393 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2394 IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2396 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2397 ok(hr == S_OK, "Got hr %#x.\n", hr);
2398 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2399 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2401 ref = IFilterGraph2_Release(graph);
2402 ok(!ref, "Got outstanding refcount %d.\n", ref);
2404 /* Preference is given to filters with higher merit. */
2406 graph = create_graph();
2407 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2408 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2410 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2411 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2413 regfilter.dwMerit = MERIT_UNLIKELY;
2414 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2415 regfilter.dwMerit = MERIT_PREFERRED;
2416 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2418 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2419 ok(hr == S_OK, "Got hr %#x.\n", hr);
2420 ok(source_pin.peer == &parser2_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2421 ok(sink_pin.peer == &parser2_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2423 ref = IFilterGraph2_Release(graph);
2424 ok(!ref, "Got outstanding refcount %d.\n", ref);
2426 graph = create_graph();
2427 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2428 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2430 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2431 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2433 regfilter.dwMerit = MERIT_PREFERRED;
2434 IFilterMapper2_RegisterFilter(mapper, &parser1_clsid, L"test", NULL, NULL, NULL, &regfilter);
2435 regfilter.dwMerit = MERIT_UNLIKELY;
2436 IFilterMapper2_RegisterFilter(mapper, &parser2_clsid, L"test", NULL, NULL, NULL, &regfilter);
2438 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2439 ok(hr == S_OK, "Got hr %#x.\n", hr);
2440 ok(source_pin.peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", source_pin.peer);
2441 ok(sink_pin.peer == &parser1_pins[1].IPin_iface, "Got peer %p.\n", sink_pin.peer);
2443 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser1_clsid);
2444 IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &parser2_clsid);
2446 out:
2447 CoRevokeClassObject(cookie1);
2448 CoRevokeClassObject(cookie2);
2449 IFilterMapper2_Release(mapper);
2450 ref = IFilterGraph2_Release(graph);
2451 ok(!ref, "Got outstanding refcount %d.\n", ref);
2452 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
2453 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
2454 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
2455 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
2456 ok(parser1.ref == 1, "Got outstanding refcount %d.\n", parser1.ref);
2457 ok(parser1_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[0].ref);
2458 ok(parser1_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[1].ref);
2459 ok(parser1_pins[2].ref == 1, "Got outstanding refcount %d.\n", parser1_pins[2].ref);
2460 ok(parser2.ref == 1, "Got outstanding refcount %d.\n", parser2.ref);
2461 ok(parser2_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[0].ref);
2462 ok(parser2_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser2_pins[1].ref);
2463 ok(parser3.ref == 1, "Got outstanding refcount %d.\n", parser3.ref);
2464 ok(parser3_pins[0].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[0].ref);
2465 ok(parser3_pins[1].ref == 1, "Got outstanding refcount %d.\n", parser3_pins[1].ref);
2468 static const GUID test_iid = {0x33333333};
2469 static LONG outer_ref = 1;
2471 static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out)
2473 if (IsEqualGUID(iid, &IID_IUnknown)
2474 || IsEqualGUID(iid, &IID_IFilterGraph2)
2475 || IsEqualGUID(iid, &test_iid))
2477 *out = (IUnknown *)0xdeadbeef;
2478 return S_OK;
2480 ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid));
2481 return E_NOINTERFACE;
2484 static ULONG WINAPI outer_AddRef(IUnknown *iface)
2486 return InterlockedIncrement(&outer_ref);
2489 static ULONG WINAPI outer_Release(IUnknown *iface)
2491 return InterlockedDecrement(&outer_ref);
2494 static const IUnknownVtbl outer_vtbl =
2496 outer_QueryInterface,
2497 outer_AddRef,
2498 outer_Release,
2501 static IUnknown test_outer = {&outer_vtbl};
2503 static void test_aggregation(void)
2505 IFilterGraph2 *graph, *graph2;
2506 IFilterMapper2 *mapper;
2507 IUnknown *unk, *unk2;
2508 HRESULT hr;
2509 ULONG ref;
2511 graph = (IFilterGraph2 *)0xdeadbeef;
2512 hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
2513 &IID_IFilterGraph2, (void **)&graph);
2514 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
2515 ok(!graph, "Got interface %p.\n", graph);
2517 hr = CoCreateInstance(&CLSID_FilterGraph, &test_outer, CLSCTX_INPROC_SERVER,
2518 &IID_IUnknown, (void **)&unk);
2519 ok(hr == S_OK, "Got hr %#x.\n", hr);
2520 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2521 ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n");
2522 ref = get_refcount(unk);
2523 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2525 ref = IUnknown_AddRef(unk);
2526 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2527 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2529 ref = IUnknown_Release(unk);
2530 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2531 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2533 hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2);
2534 ok(hr == S_OK, "Got hr %#x.\n", hr);
2535 ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2);
2536 IUnknown_Release(unk2);
2538 hr = IUnknown_QueryInterface(unk, &IID_IFilterGraph2, (void **)&graph);
2539 ok(hr == S_OK, "Got hr %#x.\n", hr);
2541 hr = IFilterGraph2_QueryInterface(graph, &IID_IUnknown, (void **)&unk2);
2542 ok(hr == S_OK, "Got hr %#x.\n", hr);
2543 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
2545 hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterGraph2, (void **)&graph2);
2546 ok(hr == S_OK, "Got hr %#x.\n", hr);
2547 ok(graph2 == (IFilterGraph2 *)0xdeadbeef, "Got unexpected IFilterGraph2 %p.\n", graph2);
2549 hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2);
2550 ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
2551 ok(!unk2, "Got unexpected IUnknown %p.\n", unk2);
2553 hr = IFilterGraph2_QueryInterface(graph, &test_iid, (void **)&unk2);
2554 ok(hr == S_OK, "Got hr %#x.\n", hr);
2555 ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2);
2557 IFilterGraph2_Release(graph);
2558 ref = IUnknown_Release(unk);
2559 ok(!ref, "Got unexpected refcount %d.\n", ref);
2560 ok(outer_ref == 1, "Got unexpected refcount %d.\n", outer_ref);
2562 /* Test the aggregated filter mapper. */
2564 graph = create_graph();
2566 ref = get_refcount(graph);
2567 ok(ref == 1, "Got unexpected refcount %d.\n", ref);
2569 hr = IFilterGraph2_QueryInterface(graph, &IID_IFilterMapper2, (void **)&mapper);
2570 ok(hr == S_OK, "Got hr %#x.\n", hr);
2572 ref = get_refcount(graph);
2573 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2574 ref = get_refcount(mapper);
2575 ok(ref == 2, "Got unexpected refcount %d.\n", ref);
2577 hr = IFilterMapper2_QueryInterface(mapper, &IID_IFilterGraph2, (void **)&graph2);
2578 ok(hr == S_OK, "Got hr %#x.\n", hr);
2579 ok(graph2 == graph, "Got unexpected IFilterGraph2 %p.\n", graph2);
2580 IFilterGraph2_Release(graph2);
2582 IFilterMapper2_Release(mapper);
2583 ref = IFilterGraph2_Release(graph);
2584 ok(!ref, "Got unexpected refcount %d.\n", ref);
2587 /* Test how methods from "control" interfaces (IBasicAudio, IBasicVideo,
2588 * IVideoWindow) are delegated to filters exposing those interfaces. */
2589 static void test_control_delegation(void)
2591 IFilterGraph2 *graph = create_graph();
2592 IBasicAudio *audio, *filter_audio;
2593 IBaseFilter *renderer;
2594 IVideoWindow *window;
2595 IBasicVideo2 *video;
2596 ITypeInfo *typeinfo;
2597 TYPEATTR *typeattr;
2598 ULONG count;
2599 HRESULT hr;
2600 LONG val;
2602 /* IBasicAudio */
2604 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicAudio, (void **)&audio);
2605 ok(hr == S_OK, "got %#x\n", hr);
2607 hr = IBasicAudio_GetTypeInfoCount(audio, &count);
2608 ok(hr == S_OK, "Got hr %#x.\n", hr);
2609 ok(count == 1, "Got count %u.\n", count);
2611 hr = IBasicAudio_put_Volume(audio, -10);
2612 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2613 hr = IBasicAudio_get_Volume(audio, &val);
2614 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2615 hr = IBasicAudio_put_Balance(audio, 10);
2616 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2617 hr = IBasicAudio_get_Balance(audio, &val);
2618 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2620 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
2621 if (hr != VFW_E_NO_AUDIO_HARDWARE)
2623 ok(hr == S_OK, "got %#x\n", hr);
2625 hr = IFilterGraph2_AddFilter(graph, renderer, NULL);
2626 ok(hr == S_OK, "got %#x\n", hr);
2628 hr = IBasicAudio_put_Volume(audio, -10);
2629 ok(hr == S_OK, "got %#x\n", hr);
2630 hr = IBasicAudio_get_Volume(audio, &val);
2631 ok(hr == S_OK, "got %#x\n", hr);
2632 ok(val == -10, "got %d\n", val);
2633 hr = IBasicAudio_put_Balance(audio, 10);
2634 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2635 hr = IBasicAudio_get_Balance(audio, &val);
2636 ok(hr == S_OK || hr == VFW_E_MONO_AUDIO_HW, "got %#x\n", hr);
2637 if (hr == S_OK)
2638 ok(val == 10, "got balance %d\n", val);
2640 hr = IBaseFilter_QueryInterface(renderer, &IID_IBasicAudio, (void **)&filter_audio);
2641 ok(hr == S_OK, "got %#x\n", hr);
2643 hr = IBasicAudio_get_Volume(filter_audio, &val);
2644 ok(hr == S_OK, "got %#x\n", hr);
2645 ok(val == -10, "got volume %d\n", val);
2647 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2648 ok(hr == S_OK, "got %#x\n", hr);
2650 IBaseFilter_Release(renderer);
2651 IBasicAudio_Release(filter_audio);
2654 hr = IBasicAudio_put_Volume(audio, -10);
2655 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2656 hr = IBasicAudio_get_Volume(audio, &val);
2657 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2658 hr = IBasicAudio_put_Balance(audio, 10);
2659 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2660 hr = IBasicAudio_get_Balance(audio, &val);
2661 ok(hr == E_NOTIMPL, "got %#x\n", hr);
2663 IBasicAudio_Release(audio);
2665 /* IBasicVideo and IVideoWindow */
2667 hr = IFilterGraph2_QueryInterface(graph, &IID_IBasicVideo2, (void **)&video);
2668 ok(hr == S_OK, "got %#x\n", hr);
2669 hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
2670 ok(hr == S_OK, "got %#x\n", hr);
2672 /* Unlike IBasicAudio, these return E_NOINTERFACE. */
2673 hr = IBasicVideo2_get_BitRate(video, &val);
2674 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2676 hr = IBasicVideo2_GetTypeInfoCount(video, &count);
2677 ok(hr == S_OK, "Got hr %#x.\n", hr);
2678 ok(count == 1, "Got count %u.\n", count);
2680 hr = IBasicVideo2_GetTypeInfo(video, 0, 0, &typeinfo);
2681 ok(hr == S_OK, "Got hr %#x.\n", hr);
2682 hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr);
2683 ok(hr == S_OK, "Got hr %#x.\n", hr);
2684 ok(typeattr->typekind == TKIND_DISPATCH, "Got kind %u.\n", typeattr->typekind);
2685 ok(IsEqualGUID(&typeattr->guid, &IID_IBasicVideo), "Got IID %s.\n", wine_dbgstr_guid(&typeattr->guid));
2686 ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr);
2687 ITypeInfo_Release(typeinfo);
2689 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2690 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2692 hr = IVideoWindow_GetTypeInfoCount(window, &count);
2693 ok(hr == S_OK, "Got hr %#x.\n", hr);
2694 ok(count == 1, "Got count %u.\n", count);
2696 hr = CoCreateInstance(&CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&renderer);
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 = IBasicVideo2_get_BitRate(video, &val);
2703 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2704 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2705 ok(hr == VFW_E_NOT_CONNECTED, "got %#x\n", hr);
2707 hr = IFilterGraph2_RemoveFilter(graph, renderer);
2708 ok(hr == S_OK, "got %#x\n", hr);
2710 hr = IBasicVideo2_get_BitRate(video, &val);
2711 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2712 hr = IVideoWindow_SetWindowForeground(window, OAFALSE);
2713 ok(hr == E_NOINTERFACE, "got %#x\n", hr);
2715 IBaseFilter_Release(renderer);
2716 IBasicVideo2_Release(video);
2717 IVideoWindow_Release(window);
2718 IFilterGraph2_Release(graph);
2721 static void test_add_remove_filter(void)
2723 struct testfilter filter;
2725 IFilterGraph2 *graph = create_graph();
2726 IBaseFilter *ret_filter;
2727 HRESULT hr;
2729 testfilter_init(&filter, NULL, 0);
2731 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2732 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2733 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2735 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, L"testid");
2736 ok(hr == S_OK, "Got hr %#x.\n", hr);
2737 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2738 ok(!wcscmp(filter.name, L"testid"), "Got name %s.\n", wine_dbgstr_w(filter.name));
2740 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2741 ok(hr == S_OK, "Got hr %#x.\n", hr);
2742 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2743 IBaseFilter_Release(ret_filter);
2745 hr = IFilterGraph2_RemoveFilter(graph, &filter.IBaseFilter_iface);
2746 ok(hr == S_OK, "Got hr %#x.\n", hr);
2747 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2748 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2749 ok(!filter.clock, "Got clock %p,\n", filter.clock);
2750 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2752 hr = IFilterGraph2_FindFilterByName(graph, L"testid", &ret_filter);
2753 ok(hr == VFW_E_NOT_FOUND, "Got hr %#x.\n", hr);
2754 ok(!ret_filter, "Got filter %p.\n", ret_filter);
2756 hr = IFilterGraph2_AddFilter(graph, &filter.IBaseFilter_iface, NULL);
2757 ok(hr == S_OK, "Got hr %#x.\n", hr);
2758 ok(filter.graph == (IFilterGraph *)graph, "Got graph %p.\n", filter.graph);
2759 ok(!wcscmp(filter.name, L"0001"), "Got name %s.\n", wine_dbgstr_w(filter.name));
2761 hr = IFilterGraph2_FindFilterByName(graph, L"0001", &ret_filter);
2762 ok(hr == S_OK, "Got hr %#x.\n", hr);
2763 ok(ret_filter == &filter.IBaseFilter_iface, "Got filter %p.\n", ret_filter);
2764 IBaseFilter_Release(ret_filter);
2766 /* test releasing the filter graph while filters are still connected */
2767 hr = IFilterGraph2_Release(graph);
2768 ok(!hr, "Got outstanding refcount %d.\n", hr);
2769 ok(!filter.graph, "Got graph %p.\n", filter.graph);
2770 ok(!filter.name, "Got name %s.\n", wine_dbgstr_w(filter.name));
2771 ok(!filter.clock, "Got clock %p.\n", filter.clock);
2772 ok(filter.ref == 1, "Got outstanding refcount %d.\n", filter.ref);
2775 static HRESULT WINAPI test_connect_direct_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
2777 struct testpin *pin = impl_from_IPin(iface);
2778 if (winetest_debug > 1) trace("%p->Connect()\n", pin);
2780 pin->peer = peer;
2781 IPin_AddRef(peer);
2782 pin->mt = (AM_MEDIA_TYPE *)mt;
2783 return S_OK;
2786 static const IPinVtbl test_connect_direct_vtbl =
2788 testpin_QueryInterface,
2789 testpin_AddRef,
2790 testpin_Release,
2791 test_connect_direct_Connect,
2792 no_ReceiveConnection,
2793 testpin_Disconnect,
2794 testpin_ConnectedTo,
2795 testpin_ConnectionMediaType,
2796 testpin_QueryPinInfo,
2797 testpin_QueryDirection,
2798 testpin_QueryId,
2799 testpin_QueryAccept,
2800 no_EnumMediaTypes,
2801 testpin_QueryInternalConnections,
2802 testpin_EndOfStream,
2803 testpin_BeginFlush,
2804 testpin_EndFlush,
2805 testpin_NewSegment
2808 static void test_connect_direct_init(struct testpin *pin, PIN_DIRECTION dir)
2810 testpin_init(pin, &test_connect_direct_vtbl, dir);
2813 static void test_connect_direct(void)
2815 struct testpin source_pin, sink_pin, parser1_pins[2], parser2_pins[2];
2816 struct testfilter source, sink, parser1, parser2;
2818 IFilterGraph2 *graph = create_graph();
2819 AM_MEDIA_TYPE mt;
2820 HRESULT hr;
2822 test_connect_direct_init(&source_pin, PINDIR_OUTPUT);
2823 testfilter_init(&source, &source_pin, 1);
2824 test_connect_direct_init(&sink_pin, PINDIR_INPUT);
2825 testfilter_init(&sink, &sink_pin, 1);
2826 test_connect_direct_init(&parser1_pins[0], PINDIR_INPUT);
2827 test_connect_direct_init(&parser1_pins[1], PINDIR_OUTPUT);
2828 testfilter_init(&parser1, parser1_pins, 2);
2829 test_connect_direct_init(&parser2_pins[0], PINDIR_INPUT);
2830 test_connect_direct_init(&parser2_pins[1], PINDIR_OUTPUT);
2831 testfilter_init(&parser2, parser2_pins, 2);
2833 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
2834 ok(hr == S_OK, "Got hr %#x.\n", hr);
2836 hr = IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
2837 ok(hr == S_OK, "Got hr %#x.\n", hr);
2839 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2840 ok(hr == S_OK, "Got hr %#x.\n", hr);
2841 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2842 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2843 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2845 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2846 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2847 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2848 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2850 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2851 ok(hr == S_OK, "Got hr %#x.\n", hr);
2852 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2853 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2855 hr = IFilterGraph2_Connect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface);
2856 ok(hr == S_OK, "Got hr %#x.\n", hr);
2857 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2858 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2859 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2861 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2862 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
2863 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2864 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2866 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2867 ok(hr == S_OK, "Got hr %#x.\n", hr);
2868 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2869 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2871 /* Swap the pins when connecting. */
2872 hr = IFilterGraph2_ConnectDirect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface, NULL);
2873 ok(hr == S_OK, "Got hr %#x.\n", hr);
2874 todo_wine
2875 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2876 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2877 todo_wine
2878 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2880 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2881 todo_wine
2882 ok(hr == S_OK, "Got hr %#x.\n", hr);
2883 todo_wine
2884 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2885 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2887 hr = IFilterGraph2_Connect(graph, &sink_pin.IPin_iface, &source_pin.IPin_iface);
2888 ok(hr == S_OK, "Got hr %#x.\n", hr);
2889 todo_wine
2890 ok(sink_pin.peer == &source_pin.IPin_iface, "Got peer %p.\n", sink_pin.peer);
2891 ok(!sink_pin.mt, "Got mt %p.\n", sink_pin.mt);
2892 todo_wine
2893 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2895 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2896 todo_wine
2897 ok(hr == S_OK, "Got hr %#x.\n", hr);
2898 todo_wine
2899 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2900 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2902 /* Disconnect() does not disconnect the peer. */
2903 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
2904 ok(hr == S_OK, "Got hr %#x.\n", hr);
2905 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2906 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2907 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2909 sink_pin.peer = &source_pin.IPin_iface;
2910 IPin_AddRef(sink_pin.peer);
2912 hr = IFilterGraph2_Disconnect(graph, &sink_pin.IPin_iface);
2913 ok(hr == S_OK, "Got hr %#x.\n", hr);
2914 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2915 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2917 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2918 ok(hr == S_OK, "Got hr %#x.\n", hr);
2919 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
2920 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2922 /* Test specifying the media type. */
2923 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2924 ok(hr == S_OK, "Got hr %#x.\n", hr);
2925 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2926 ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
2927 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2928 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2929 ok(hr == S_OK, "Got hr %#x.\n", hr);
2931 /* Test Reconnect[Ex](). */
2933 hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
2934 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2935 hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
2936 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2938 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2939 ok(hr == S_OK, "Got hr %#x.\n", hr);
2940 hr = IFilterGraph2_Reconnect(graph, &source_pin.IPin_iface);
2941 ok(hr == S_OK, "Got hr %#x.\n", hr);
2942 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2943 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2944 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2945 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2946 ok(hr == S_OK, "Got hr %#x.\n", hr);
2948 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2949 ok(hr == S_OK, "Got hr %#x.\n", hr);
2950 sink_pin.peer = &source_pin.IPin_iface;
2951 IPin_AddRef(sink_pin.peer);
2952 hr = IFilterGraph2_Reconnect(graph, &sink_pin.IPin_iface);
2953 ok(hr == S_OK, "Got hr %#x.\n", hr);
2954 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2955 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2956 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2957 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2958 ok(hr == S_OK, "Got hr %#x.\n", hr);
2960 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
2961 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2962 hr = IFilterGraph2_ReconnectEx(graph, &sink_pin.IPin_iface, NULL);
2963 ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#x.\n", hr);
2965 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2966 ok(hr == S_OK, "Got hr %#x.\n", hr);
2967 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, NULL);
2968 ok(hr == S_OK, "Got hr %#x.\n", hr);
2969 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2970 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
2971 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2972 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2973 ok(hr == S_OK, "Got hr %#x.\n", hr);
2975 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
2976 ok(hr == S_OK, "Got hr %#x.\n", hr);
2977 hr = IFilterGraph2_ReconnectEx(graph, &source_pin.IPin_iface, &mt);
2978 ok(hr == S_OK, "Got hr %#x.\n", hr);
2979 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
2980 ok(source_pin.mt == &mt, "Got mt %p.\n", source_pin.mt);
2981 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
2982 hr = IFilterGraph2_Disconnect(graph, &source_pin.IPin_iface);
2983 ok(hr == S_OK, "Got hr %#x.\n", hr);
2985 /* ConnectDirect() protects against cyclical connections. */
2986 hr = IFilterGraph2_AddFilter(graph, &parser1.IBaseFilter_iface, NULL);
2987 ok(hr == S_OK, "Got hr %#x.\n", hr);
2988 hr = IFilterGraph2_AddFilter(graph, &parser2.IBaseFilter_iface, NULL);
2989 ok(hr == S_OK, "Got hr %#x.\n", hr);
2991 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
2992 ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
2994 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
2995 ok(hr == S_OK, "Got hr %#x.\n", hr);
2996 hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
2997 todo_wine ok(hr == VFW_E_CIRCULAR_GRAPH, "Got hr %#x.\n", hr);
2998 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
2999 IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
3001 parser1_pins[0].QueryInternalConnections_hr = S_OK;
3002 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3003 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3004 ok(!parser1_pins[0].peer, "Got peer %p.\n", parser1_pins[0].peer);
3005 todo_wine ok(parser1_pins[1].peer == &parser1_pins[0].IPin_iface, "Got peer %p.\n", parser1_pins[1].peer);
3006 IFilterGraph2_Disconnect(graph, &parser1_pins[0].IPin_iface);
3007 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
3009 hr = IFilterGraph2_ConnectDirect(graph, &parser1_pins[1].IPin_iface, &parser2_pins[0].IPin_iface, NULL);
3010 ok(hr == S_OK, "Got hr %#x.\n", hr);
3011 hr = IFilterGraph2_ConnectDirect(graph, &parser2_pins[1].IPin_iface, &parser1_pins[0].IPin_iface, NULL);
3012 ok(hr == S_OK, "Got hr %#x.\n", hr);
3013 IFilterGraph2_Disconnect(graph, &parser1_pins[1].IPin_iface);
3014 IFilterGraph2_Disconnect(graph, &parser2_pins[0].IPin_iface);
3016 hr = IFilterGraph2_RemoveFilter(graph, &parser1.IBaseFilter_iface);
3017 ok(hr == S_OK, "Got hr %#x.\n", hr);
3018 hr = IFilterGraph2_RemoveFilter(graph, &parser2.IBaseFilter_iface);
3019 ok(hr == S_OK, "Got hr %#x.\n", hr);
3021 /* Both pins are disconnected when a filter is removed. */
3022 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, &mt);
3023 ok(hr == S_OK, "Got hr %#x.\n", hr);
3024 sink_pin.peer = &source_pin.IPin_iface;
3025 IPin_AddRef(sink_pin.peer);
3026 hr = IFilterGraph2_RemoveFilter(graph, &source.IBaseFilter_iface);
3027 ok(hr == S_OK, "Got hr %#x.\n", hr);
3028 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
3029 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3031 /* Or when the graph is destroyed. */
3032 hr = IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3033 ok(hr == S_OK, "Got hr %#x.\n", hr);
3035 hr = IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3036 ok(hr == S_OK, "Got hr %#x.\n", hr);
3037 ok(source_pin.peer == &sink_pin.IPin_iface, "Got peer %p.\n", source_pin.peer);
3038 ok(!source_pin.mt, "Got mt %p.\n", source_pin.mt);
3039 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3040 IPin_AddRef(sink_pin.peer = &source_pin.IPin_iface);
3042 hr = IFilterGraph2_Release(graph);
3043 ok(!hr, "Got outstanding refcount %d.\n", hr);
3044 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
3045 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
3046 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
3047 todo_wine
3048 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
3049 ok(!source_pin.peer, "Got peer %p.\n", source_pin.peer);
3050 ok(!sink_pin.peer, "Got peer %p.\n", sink_pin.peer);
3053 static void test_sync_source(void)
3055 struct testfilter filter1, filter2;
3057 IFilterGraph2 *graph = create_graph();
3058 IReferenceClock *systemclock, *clock;
3059 IMediaFilter *filter;
3060 HRESULT hr;
3061 ULONG ref;
3063 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3065 testfilter_init(&filter1, NULL, 0);
3066 testfilter_init(&filter2, NULL, 0);
3068 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3069 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
3071 ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
3072 ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
3074 CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER,
3075 &IID_IReferenceClock, (void **)&systemclock);
3077 hr = IMediaFilter_SetSyncSource(filter, systemclock);
3078 ok(hr == S_OK, "Got hr %#x.\n", hr);
3079 ok(filter1.clock == systemclock, "Got clock %p.\n", filter1.clock);
3080 ok(filter2.clock == systemclock, "Got clock %p.\n", filter2.clock);
3082 hr = IMediaFilter_GetSyncSource(filter, &clock);
3083 ok(hr == S_OK, "Got hr %#x.\n", hr);
3084 ok(clock == systemclock, "Got clock %p.\n", clock);
3085 IReferenceClock_Release(clock);
3087 hr = IMediaFilter_SetSyncSource(filter, NULL);
3088 ok(hr == S_OK, "Got hr %#x.\n", hr);
3089 ok(!filter1.clock, "Got clock %p.\n", filter1.clock);
3090 ok(!filter2.clock, "Got clock %p.\n", filter2.clock);
3092 hr = IMediaFilter_GetSyncSource(filter, &clock);
3093 todo_wine
3094 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3095 ok(!clock, "Got clock %p.\n", clock);
3097 IReferenceClock_Release(systemclock);
3098 IMediaFilter_Release(filter);
3099 ref = IFilterGraph2_Release(graph);
3100 ok(!ref, "Got outstanding refcount %d\n", ref);
3101 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
3102 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
3105 #define check_filter_state(a, b) check_filter_state_(__LINE__, a, b)
3106 static void check_filter_state_(unsigned int line, IFilterGraph2 *graph, FILTER_STATE expect)
3108 IMediaFilter *mediafilter;
3109 IEnumFilters *filterenum;
3110 IMediaControl *control;
3111 OAFilterState oastate;
3112 IBaseFilter *filter;
3113 FILTER_STATE state;
3114 HRESULT hr;
3116 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&mediafilter);
3117 hr = IMediaFilter_GetState(mediafilter, 1000, &state);
3118 ok_(__FILE__, line)(hr == S_OK, "IMediaFilter_GetState() returned %#x.\n", hr);
3119 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3120 IMediaFilter_Release(mediafilter);
3122 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3123 hr = IMediaControl_GetState(control, 1000, &oastate);
3124 ok_(__FILE__, line)(hr == S_OK, "IMediaControl_GetState() returned %#x.\n", hr);
3125 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3126 IMediaControl_Release(control);
3128 IFilterGraph2_EnumFilters(graph, &filterenum);
3129 while (IEnumFilters_Next(filterenum, 1, &filter, NULL) == S_OK)
3131 hr = IBaseFilter_GetState(filter, 1000, &state);
3132 ok_(__FILE__, line)(hr == S_OK, "IBaseFilter_GetState() returned %#x.\n", hr);
3133 ok_(__FILE__, line)(state == expect, "Expected state %u, got %u.\n", expect, state);
3134 IBaseFilter_Release(filter);
3136 IEnumFilters_Release(filterenum);
3140 static void test_filter_state(void)
3142 struct testfilter source, sink, dummy;
3143 struct testpin source_pin, sink_pin;
3145 IFilterGraph2 *graph = create_graph();
3146 REFERENCE_TIME start_time;
3147 IReferenceClock *clock;
3148 IMediaControl *control;
3149 IMediaFilter *filter;
3150 OAFilterState state;
3151 HRESULT hr;
3152 ULONG ref;
3154 testsource_init(&source_pin, NULL, 0);
3155 testsink_init(&sink_pin);
3156 testfilter_init(&source, &source_pin, 1);
3157 testfilter_init(&sink, &sink_pin, 1);
3158 testfilter_init(&dummy, NULL, 0);
3160 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3161 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3163 source_pin.filter = &source.IBaseFilter_iface;
3164 sink_pin.filter = &sink.IBaseFilter_iface;
3166 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3167 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
3168 IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, NULL);
3169 IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3171 check_filter_state(graph, State_Stopped);
3173 hr = IMediaControl_Pause(control);
3174 ok(hr == S_OK, "Got hr %#x.\n", hr);
3175 check_filter_state(graph, State_Paused);
3177 /* Pausing sets the default sync source, if it's not already set. */
3179 hr = IMediaFilter_GetSyncSource(filter, &clock);
3180 ok(hr == S_OK, "Got hr %#x.\n", hr);
3181 ok(!!clock, "Reference clock not set.\n");
3182 ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
3183 ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
3185 hr = IReferenceClock_GetTime(clock, &start_time);
3186 ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
3187 hr = IMediaControl_Run(control);
3188 ok(hr == S_OK, "Got hr %#x.\n", hr);
3189 check_filter_state(graph, State_Running);
3190 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3191 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3192 "Expected time near %s, got %s.\n",
3193 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3194 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3195 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3197 hr = IMediaControl_Pause(control);
3198 ok(hr == S_OK, "Got hr %#x.\n", hr);
3199 check_filter_state(graph, State_Paused);
3201 hr = IMediaControl_Stop(control);
3202 ok(hr == S_OK, "Got hr %#x.\n", hr);
3203 check_filter_state(graph, State_Stopped);
3205 hr = IMediaControl_Run(control);
3206 ok(hr == S_OK, "Got hr %#x.\n", hr);
3207 check_filter_state(graph, State_Running);
3209 hr = IMediaControl_Stop(control);
3210 ok(hr == S_OK, "Got hr %#x.\n", hr);
3211 check_filter_state(graph, State_Stopped);
3213 hr = IMediaControl_Pause(control);
3214 ok(hr == S_OK, "Got hr %#x.\n", hr);
3215 check_filter_state(graph, State_Paused);
3217 hr = IMediaControl_StopWhenReady(control);
3218 ok(hr == S_OK, "Got hr %#x.\n", hr);
3219 check_filter_state(graph, State_Stopped);
3221 hr = IMediaControl_Run(control);
3222 ok(hr == S_OK, "Got hr %#x.\n", hr);
3223 check_filter_state(graph, State_Running);
3225 hr = IMediaControl_StopWhenReady(control);
3226 ok(hr == S_OK, "Got hr %#x.\n", hr);
3227 check_filter_state(graph, State_Stopped);
3229 hr = IMediaControl_StopWhenReady(control);
3230 ok(hr == S_OK, "Got hr %#x.\n", hr);
3231 check_filter_state(graph, State_Stopped);
3233 IReferenceClock_Release(clock);
3234 IMediaFilter_Release(filter);
3235 IMediaControl_Release(control);
3236 IFilterGraph2_Release(graph);
3238 /* Test same methods using IMediaFilter. */
3240 graph = create_graph();
3241 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3242 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3244 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3245 IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL);
3246 IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
3248 hr = IMediaFilter_Pause(filter);
3249 ok(hr == S_OK, "Got hr %#x.\n", hr);
3250 check_filter_state(graph, State_Paused);
3252 hr = IMediaFilter_GetSyncSource(filter, &clock);
3253 ok(hr == S_OK, "Got hr %#x.\n", hr);
3254 ok(!!clock, "Reference clock not set.\n");
3255 ok(source.clock == clock, "Expected %p, got %p.\n", clock, source.clock);
3256 ok(sink.clock == clock, "Expected %p, got %p.\n", clock, sink.clock);
3258 hr = IMediaFilter_Run(filter, 0xdeadbeef);
3259 ok(hr == S_OK, "Got hr %#x.\n", hr);
3260 check_filter_state(graph, State_Running);
3261 ok(source.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3262 ok(sink.start_time == 0xdeadbeef, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
3264 hr = IMediaFilter_Pause(filter);
3265 ok(hr == S_OK, "Got hr %#x.\n", hr);
3266 check_filter_state(graph, State_Paused);
3268 hr = IMediaFilter_Run(filter, 0xdeadf00d);
3269 ok(hr == S_OK, "Got hr %#x.\n", hr);
3270 check_filter_state(graph, State_Running);
3271 ok(source.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3272 ok(sink.start_time == 0xdeadf00d, "Got time %s.\n", wine_dbgstr_longlong(sink.start_time));
3274 hr = IMediaFilter_Pause(filter);
3275 ok(hr == S_OK, "Got hr %#x.\n", hr);
3276 check_filter_state(graph, State_Paused);
3278 hr = IMediaFilter_Stop(filter);
3279 ok(hr == S_OK, "Got hr %#x.\n", hr);
3280 check_filter_state(graph, State_Stopped);
3282 hr = IReferenceClock_GetTime(clock, &start_time);
3283 ok(SUCCEEDED(hr), "Got hr %#x.\n", hr);
3284 hr = IMediaFilter_Run(filter, 0);
3285 ok(hr == S_OK, "Got hr %#x.\n", hr);
3286 check_filter_state(graph, State_Running);
3287 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3288 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3289 "Expected time near %s, got %s.\n",
3290 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3291 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3292 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3294 Sleep(600);
3295 hr = IMediaFilter_Pause(filter);
3296 ok(hr == S_OK, "Got hr %#x.\n", hr);
3297 check_filter_state(graph, State_Paused);
3299 hr = IMediaFilter_Run(filter, 0);
3300 ok(hr == S_OK, "Got hr %#x.\n", hr);
3301 check_filter_state(graph, State_Running);
3302 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3303 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3304 "Expected time near %s, got %s.\n",
3305 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3306 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3307 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3309 hr = IMediaFilter_Pause(filter);
3310 ok(hr == S_OK, "Got hr %#x.\n", hr);
3311 check_filter_state(graph, State_Paused);
3312 Sleep(600);
3314 start_time += 550 * 10000;
3315 hr = IMediaFilter_Run(filter, 0);
3316 ok(hr == S_OK, "Got hr %#x.\n", hr);
3317 check_filter_state(graph, State_Running);
3318 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
3319 ok(source.start_time >= start_time && source.start_time < start_time + 500 * 10000,
3320 "Expected time near %s, got %s.\n",
3321 wine_dbgstr_longlong(start_time), wine_dbgstr_longlong(source.start_time));
3322 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3323 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3325 hr = IMediaFilter_Stop(filter);
3326 ok(hr == S_OK, "Got hr %#x.\n", hr);
3327 check_filter_state(graph, State_Stopped);
3329 /* Test removing the sync source. */
3331 IReferenceClock_Release(clock);
3332 IMediaFilter_SetSyncSource(filter, NULL);
3334 hr = IMediaControl_Run(control);
3335 ok(hr == S_OK, "Got hr %#x.\n", hr);
3336 check_filter_state(graph, State_Running);
3337 todo_wine
3338 ok(source.start_time > 0 && source.start_time < 500 * 10000,
3339 "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3340 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3341 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3343 hr = IMediaControl_Stop(control);
3344 ok(hr == S_OK, "Got hr %#x.\n", hr);
3345 check_filter_state(graph, State_Stopped);
3347 sink.state_hr = S_FALSE;
3348 hr = IMediaControl_Pause(control);
3349 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3351 sink.state_hr = VFW_S_STATE_INTERMEDIATE;
3352 hr = IMediaControl_GetState(control, 0, &state);
3353 ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#x.\n", hr);
3354 ok(state == State_Paused, "Got state %u.\n", state);
3356 sink.state_hr = VFW_S_CANT_CUE;
3357 hr = IMediaControl_GetState(control, 0, &state);
3358 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3359 ok(state == State_Paused, "Got state %u.\n", state);
3361 sink.state_hr = VFW_S_STATE_INTERMEDIATE;
3362 source.state_hr = VFW_S_CANT_CUE;
3363 hr = IMediaControl_GetState(control, 0, &state);
3364 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3365 ok(state == State_Paused, "Got state %u.\n", state);
3367 sink.state_hr = VFW_S_CANT_CUE;
3368 source.state_hr = VFW_S_STATE_INTERMEDIATE;
3369 hr = IMediaControl_GetState(control, 0, &state);
3370 ok(hr == VFW_S_CANT_CUE, "Got hr %#x.\n", hr);
3371 ok(state == State_Paused, "Got state %u.\n", state);
3373 sink.state_hr = source.state_hr = S_OK;
3375 /* Destroying the graph while it's running stops all filters. */
3377 hr = IMediaFilter_Run(filter, 0);
3378 ok(hr == S_OK, "Got hr %#x.\n", hr);
3379 check_filter_state(graph, State_Running);
3380 todo_wine
3381 ok(source.start_time > 0 && source.start_time < 500 * 10000,
3382 "Got time %s.\n", wine_dbgstr_longlong(source.start_time));
3383 ok(sink.start_time == source.start_time, "Expected time %s, got %s.\n",
3384 wine_dbgstr_longlong(source.start_time), wine_dbgstr_longlong(sink.start_time));
3386 IMediaFilter_Release(filter);
3387 IMediaControl_Release(control);
3388 ref = IFilterGraph2_Release(graph);
3389 ok(!ref, "Got outstanding refcount %d.\n", ref);
3390 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
3391 ok(sink.ref == 1, "Got outstanding refcount %d.\n", sink.ref);
3392 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
3393 ok(sink_pin.ref == 1, "Got outstanding refcount %d.\n", sink_pin.ref);
3394 ok(source.state == State_Stopped, "Got state %u.\n", source.state);
3395 ok(sink.state == State_Stopped, "Got state %u.\n", sink.state);
3398 /* Helper function to check whether a filter is considered a renderer, i.e.
3399 * whether its EC_COMPLETE notification will be passed on to the application. */
3400 static HRESULT check_ec_complete(IFilterGraph2 *graph, IBaseFilter *filter)
3402 IMediaEventSink *eventsink;
3403 LONG_PTR param1, param2;
3404 IMediaControl *control;
3405 IMediaEvent *eventsrc;
3406 HRESULT hr, ret_hr;
3407 LONG code;
3409 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3410 IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
3411 IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
3413 IMediaControl_Run(control);
3415 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3416 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3418 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)filter);
3419 ok(hr == S_OK, "Got hr %#x.\n", hr);
3421 ret_hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3422 if (ret_hr == S_OK)
3424 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3425 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3426 ok(!param2, "Got param2 %#lx.\n", param2);
3427 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3428 ok(hr == S_OK, "Got hr %#x.\n", hr);
3430 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3431 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3434 IMediaControl_Stop(control);
3436 IMediaControl_Release(control);
3437 IMediaEvent_Release(eventsrc);
3438 IMediaEventSink_Release(eventsink);
3439 return ret_hr;
3442 static void test_ec_complete(void)
3444 struct testpin filter1_pin, filter2_pin, filter3_pin, source_pins[3];
3445 struct testfilter filter1, filter2, filter3, source;
3447 IFilterGraph2 *graph = create_graph();
3448 IMediaEventSink *eventsink;
3449 LONG_PTR param1, param2;
3450 IMediaControl *control;
3451 IMediaEvent *eventsrc;
3452 HRESULT hr;
3453 LONG code;
3455 testsink_init(&filter1_pin);
3456 testsink_init(&filter2_pin);
3457 testsink_init(&filter3_pin);
3458 testfilter_init(&filter1, &filter1_pin, 1);
3459 testfilter_init(&filter2, &filter2_pin, 1);
3460 testfilter_init(&filter3, &filter3_pin, 1);
3461 testsource_init(&source_pins[0], NULL, 0);
3462 testsource_init(&source_pins[1], NULL, 0);
3463 testsource_init(&source_pins[2], NULL, 0);
3464 testfilter_init(&source, source_pins, 3);
3466 filter1.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
3467 filter2.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl;
3468 filter1.misc_flags = filter2.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER;
3470 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3471 IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc);
3472 IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink);
3474 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3475 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
3476 IFilterGraph2_AddFilter(graph, &filter3.IBaseFilter_iface, NULL);
3477 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
3478 IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
3479 IFilterGraph2_ConnectDirect(graph, &source_pins[1].IPin_iface, &filter2_pin.IPin_iface, NULL);
3480 IFilterGraph2_ConnectDirect(graph, &source_pins[2].IPin_iface, &filter3_pin.IPin_iface, NULL);
3482 /* EC_COMPLETE is only delivered to the user after all renderers deliver it. */
3484 IMediaControl_Run(control);
3486 while ((hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0)) == S_OK)
3488 ok(code != EC_COMPLETE, "Got unexpected EC_COMPLETE.\n");
3489 IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3491 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3493 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
3494 ok(hr == S_OK, "Got hr %#x.\n", hr);
3496 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3497 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3499 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter2.IBaseFilter_iface);
3500 ok(hr == S_OK, "Got hr %#x.\n", hr);
3502 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3503 ok(hr == S_OK, "Got hr %#x.\n", hr);
3504 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3505 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3506 ok(!param2, "Got param2 %#lx.\n", param2);
3507 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3508 ok(hr == S_OK, "Got hr %#x.\n", hr);
3510 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3511 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3513 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
3514 ok(hr == S_OK, "Got hr %#x.\n", hr);
3516 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3517 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3519 IMediaControl_Stop(control);
3521 /* Test CancelDefaultHandling(). */
3523 IMediaControl_Run(control);
3525 hr = IMediaEvent_CancelDefaultHandling(eventsrc, EC_COMPLETE);
3526 ok(hr == S_OK, "Got hr %#x.\n", hr);
3528 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3529 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3531 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter1.IBaseFilter_iface);
3532 ok(hr == S_OK, "Got hr %#x.\n", hr);
3534 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3535 ok(hr == S_OK, "Got hr %#x.\n", hr);
3536 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3537 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3538 ok(param2 == (LONG_PTR)&filter1.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
3539 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3540 ok(hr == S_OK, "Got hr %#x.\n", hr);
3542 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3543 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3545 hr = IMediaEventSink_Notify(eventsink, EC_COMPLETE, S_OK, (LONG_PTR)&filter3.IBaseFilter_iface);
3546 ok(hr == S_OK, "Got hr %#x.\n", hr);
3548 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 0);
3549 ok(hr == S_OK, "Got hr %#x.\n", hr);
3550 ok(code == EC_COMPLETE, "Got code %#x.\n", code);
3551 ok(param1 == S_OK, "Got param1 %#lx.\n", param1);
3552 ok(param2 == (LONG_PTR)&filter3.IBaseFilter_iface, "Got param2 %#lx.\n", param2);
3553 hr = IMediaEvent_FreeEventParams(eventsrc, code, param1, param2);
3554 ok(hr == S_OK, "Got hr %#x.\n", hr);
3556 hr = IMediaEvent_GetEvent(eventsrc, &code, &param1, &param2, 50);
3557 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3559 IMediaControl_Stop(control);
3560 hr = IMediaEvent_RestoreDefaultHandling(eventsrc, EC_COMPLETE);
3561 ok(hr == S_OK, "Got hr %#x.\n", hr);
3563 /* A filter counts as a renderer if it (1) exposes IAMFilterMiscFlags and
3564 * reports itself as a renderer, or (2) exposes IMediaSeeking and has no
3565 * output pins. Despite MSDN, QueryInternalConnections() does not seem to
3566 * be used. */
3568 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3569 IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
3570 IFilterGraph2_RemoveFilter(graph, &filter3.IBaseFilter_iface);
3571 filter1.misc_flags = 0;
3572 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3573 IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
3575 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3576 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3578 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3579 filter1_pin.dir = PINDIR_INPUT;
3580 filter1.IAMFilterMiscFlags_iface.lpVtbl = NULL;
3581 filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3582 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3583 IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
3585 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3586 ok(hr == S_OK, "Got hr %#x.\n", hr);
3588 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3589 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3590 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3592 filter1_pin.dir = PINDIR_OUTPUT;
3593 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3595 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3596 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3598 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3599 IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
3600 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3602 filter1.IMediaSeeking_iface.lpVtbl = NULL;
3603 filter1_pin.dir = PINDIR_INPUT;
3604 filter1.pin_count = 1;
3605 filter1_pin.QueryInternalConnections_hr = S_OK;
3606 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3607 IFilterGraph2_ConnectDirect(graph, &source_pins[0].IPin_iface, &filter1_pin.IPin_iface, NULL);
3609 hr = check_ec_complete(graph, &filter1.IBaseFilter_iface);
3610 ok(hr == E_ABORT, "Got hr %#x.\n", hr);
3612 IMediaControl_Release(control);
3613 IMediaEvent_Release(eventsrc);
3614 IMediaEventSink_Release(eventsink);
3615 hr = IFilterGraph2_Release(graph);
3616 ok(!hr, "Got outstanding refcount %d.\n", hr);
3617 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
3618 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
3619 ok(filter3.ref == 1, "Got outstanding refcount %d.\n", filter3.ref);
3622 /* Remove and re-add the filter, to flush the graph's internal
3623 * IMediaSeeking cache. Don't expose IMediaSeeking when adding, to show
3624 * that it's only queried when needed. */
3625 static void flush_cached_seeking(IFilterGraph2 *graph, struct testfilter *filter)
3627 IFilterGraph2_RemoveFilter(graph, &filter->IBaseFilter_iface);
3628 filter->IMediaSeeking_iface.lpVtbl = NULL;
3629 IFilterGraph2_AddFilter(graph, &filter->IBaseFilter_iface, NULL);
3630 filter->IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3633 static void test_graph_seeking(void)
3635 struct testfilter filter1, filter2;
3637 LONGLONG time, current, stop, earliest, latest;
3638 IFilterGraph2 *graph = create_graph();
3639 IMediaControl *control;
3640 IMediaSeeking *seeking;
3641 IMediaFilter *filter;
3642 unsigned int i;
3643 double rate;
3644 GUID format;
3645 HRESULT hr;
3646 DWORD caps;
3647 ULONG ref;
3649 static const GUID *const all_formats[] =
3651 NULL,
3652 &TIME_FORMAT_NONE,
3653 &TIME_FORMAT_FRAME,
3654 &TIME_FORMAT_SAMPLE,
3655 &TIME_FORMAT_FIELD,
3656 &TIME_FORMAT_BYTE,
3657 &TIME_FORMAT_MEDIA_TIME,
3658 &testguid,
3661 static const GUID *const unsupported_formats[] =
3663 &TIME_FORMAT_FRAME,
3664 &TIME_FORMAT_SAMPLE,
3665 &TIME_FORMAT_FIELD,
3666 &TIME_FORMAT_BYTE,
3667 &testguid,
3670 testfilter_init(&filter1, NULL, 0);
3671 testfilter_init(&filter2, NULL, 0);
3673 IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
3674 IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking);
3675 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
3677 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
3678 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3679 todo_wine ok(!caps, "Got caps %#x.\n", caps);
3681 caps = 0;
3682 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
3683 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3684 ok(!caps, "Got caps %#x.\n", caps);
3686 caps = AM_SEEKING_CanSeekAbsolute;
3687 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
3688 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3689 todo_wine ok(!caps, "Got caps %#x.\n", caps);
3691 hr = IMediaSeeking_IsFormatSupported(seeking, NULL);
3692 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3694 for (i = 0; i < ARRAY_SIZE(all_formats); ++i)
3696 hr = IMediaSeeking_IsFormatSupported(seeking, all_formats[i]);
3697 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(all_formats[i]));
3700 hr = IMediaSeeking_QueryPreferredFormat(seeking, NULL);
3701 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3702 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
3703 ok(hr == S_OK, "Got hr %#x.\n", hr);
3704 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3706 hr = IMediaSeeking_GetTimeFormat(seeking, NULL);
3707 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3708 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
3709 ok(hr == S_OK, "Got hr %#x.\n", hr);
3710 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3712 hr = IMediaSeeking_IsUsingTimeFormat(seeking, NULL);
3713 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3714 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
3715 ok(hr == S_OK, "Got hr %#x.\n", hr);
3716 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
3717 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3719 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_NONE);
3720 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3722 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
3723 ok(hr == S_OK, "Got hr %#x.\n", hr);
3724 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3726 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
3727 ok(hr == S_OK, "Got hr %#x.\n", hr);
3728 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_NONE);
3729 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3731 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
3732 ok(hr == S_OK, "Got hr %#x.\n", hr);
3733 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3735 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
3736 ok(hr == S_OK, "Got hr %#x.\n", hr);
3738 for (i = 0; i < ARRAY_SIZE(unsupported_formats); ++i)
3740 hr = IMediaSeeking_SetTimeFormat(seeking, unsupported_formats[i]);
3741 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x for format %s.\n", hr, wine_dbgstr_guid(unsupported_formats[i]));
3744 time = 0xdeadbeef;
3745 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, NULL);
3746 ok(hr == S_OK, "Got hr %#x.\n", hr);
3747 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3749 time = 0xdeadbeef;
3750 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, NULL);
3751 ok(hr == S_OK, "Got hr %#x.\n", hr);
3752 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3754 time = 0xdeadbeef;
3755 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
3756 ok(hr == S_OK, "Got hr %#x.\n", hr);
3757 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3759 time = 0xdeadbeef;
3760 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
3761 ok(hr == S_OK, "Got hr %#x.\n", hr);
3762 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3764 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_MEDIA_TIME);
3765 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3766 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, NULL);
3767 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3768 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_MEDIA_TIME, 0x123456789a, &TIME_FORMAT_NONE);
3769 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3770 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, NULL, 0x123456789a, &TIME_FORMAT_NONE);
3771 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3773 time = 0xdeadbeef;
3774 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &TIME_FORMAT_NONE, 0x123456789a, &TIME_FORMAT_NONE);
3775 ok(hr == S_OK, "Got hr %#x.\n", hr);
3776 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3778 time = 0xdeadbeef;
3779 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
3780 ok(hr == S_OK, "Got hr %#x.\n", hr);
3781 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3783 hr = IMediaSeeking_GetDuration(seeking, NULL);
3784 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3785 hr = IMediaSeeking_GetDuration(seeking, &time);
3786 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3788 hr = IMediaSeeking_GetStopPosition(seeking, NULL);
3789 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3790 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3791 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3793 hr = IMediaSeeking_SetPositions(seeking, &current, 0, &stop, 0);
3794 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3796 hr = IMediaSeeking_GetPositions(seeking, NULL, NULL);
3797 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3798 hr = IMediaSeeking_GetPositions(seeking, NULL, &stop);
3799 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3800 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
3801 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3802 current = 0xdeadbeef;
3803 hr = IMediaSeeking_GetPositions(seeking, &current, NULL);
3804 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3805 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
3807 hr = IMediaSeeking_GetCurrentPosition(seeking, NULL);
3808 ok(hr == E_POINTER, "Got hr %#x.\n", hr);
3809 current = 0xdeadbeef;
3810 hr = IMediaSeeking_GetCurrentPosition(seeking, &current);
3811 ok(hr == S_OK, "Got hr %#x.\n", hr);
3812 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(time));
3814 hr = IMediaSeeking_GetAvailable(seeking, &earliest, &latest);
3815 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3817 hr = IMediaSeeking_SetRate(seeking, 1.0);
3818 ok(hr == S_OK, "Got hr %#x.\n", hr);
3819 hr = IMediaSeeking_SetRate(seeking, 2.0);
3820 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3822 hr = IMediaSeeking_GetRate(seeking, &rate);
3823 ok(hr == S_OK, "Got hr %#x.\n", hr);
3824 ok(rate == 1.0, "Got rate %.16e.\n", rate);
3826 hr = IMediaSeeking_GetPreroll(seeking, &time);
3827 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3829 /* Try with filters added. Note that a filter need only expose
3830 * IMediaSeeking—no other heuristics are used to determine if it is a
3831 * renderer. */
3833 IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
3834 IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
3835 filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3836 filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
3838 filter1.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
3839 filter2.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration;
3840 hr = IMediaSeeking_GetCapabilities(seeking, &caps);
3841 ok(hr == S_OK, "Got hr %#x.\n", hr);
3842 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
3843 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
3844 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
3846 flush_cached_seeking(graph, &filter1);
3847 flush_cached_seeking(graph, &filter2);
3849 caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos;
3850 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
3851 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3852 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
3854 caps = AM_SEEKING_CanDoSegments;
3855 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
3856 ok(hr == S_OK, "Got hr %#x.\n", hr);
3857 ok(caps == AM_SEEKING_CanDoSegments, "Got caps %#x.\n", caps);
3859 caps = AM_SEEKING_CanGetCurrentPos;
3860 hr = IMediaSeeking_CheckCapabilities(seeking, &caps);
3861 ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3862 ok(!caps, "Got caps %#x.\n", caps);
3864 flush_cached_seeking(graph, &filter1);
3865 flush_cached_seeking(graph, &filter2);
3867 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
3868 ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3870 filter1.support_testguid = TRUE;
3871 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
3872 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3874 filter1.support_testguid = FALSE;
3875 filter2.support_testguid = TRUE;
3876 hr = IMediaSeeking_IsFormatSupported(seeking, &testguid);
3877 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3879 /* Filters are not consulted about preferred formats. */
3880 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
3881 ok(hr == S_OK, "Got hr %#x.\n", hr);
3882 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3884 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
3885 ok(hr == S_OK, "Got hr %#x.\n", hr);
3886 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3888 filter2.support_testguid = FALSE;
3889 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
3890 todo_wine ok(hr == E_FAIL, "Got hr %#x.\n", hr);
3892 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
3893 ok(hr == S_OK, "Got hr %#x.\n", hr);
3894 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3896 filter1.support_testguid = TRUE;
3897 hr = IMediaSeeking_SetTimeFormat(seeking, &testguid);
3898 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3900 hr = IMediaSeeking_GetTimeFormat(seeking, &format);
3901 ok(hr == S_OK, "Got hr %#x.\n", hr);
3902 todo_wine ok(IsEqualGUID(&format, &testguid), "Got format %s.\n", wine_dbgstr_guid(&format));
3904 hr = IMediaSeeking_QueryPreferredFormat(seeking, &format);
3905 ok(hr == S_OK, "Got hr %#x.\n", hr);
3906 ok(IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME), "Got format %s.\n", wine_dbgstr_guid(&format));
3908 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
3909 todo_wine ok(hr == S_FALSE, "Got hr %#x.\n", hr);
3910 hr = IMediaSeeking_IsUsingTimeFormat(seeking, &testguid);
3911 todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
3913 hr = IMediaSeeking_SetTimeFormat(seeking, &TIME_FORMAT_MEDIA_TIME);
3914 ok(hr == S_OK, "Got hr %#x.\n", hr);
3916 time = 0xdeadbeef;
3917 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &testguid);
3918 ok(hr == S_OK, "Got hr %#x.\n", hr);
3919 ok(time == 0x123456789a, "Got time %s.\n", wine_dbgstr_longlong(time));
3921 hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE);
3922 todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3924 flush_cached_seeking(graph, &filter1);
3925 flush_cached_seeking(graph, &filter2);
3927 filter1.seek_duration = 0x12345;
3928 filter2.seek_duration = 0x23456;
3929 hr = IMediaSeeking_GetDuration(seeking, &time);
3930 ok(hr == S_OK, "Got hr %#x.\n", hr);
3931 ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
3933 filter2.seek_duration = 0x12345;
3934 filter1.seek_duration = 0x23456;
3935 hr = IMediaSeeking_GetDuration(seeking, &time);
3936 ok(hr == S_OK, "Got hr %#x.\n", hr);
3937 ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
3939 flush_cached_seeking(graph, &filter1);
3940 flush_cached_seeking(graph, &filter2);
3942 filter1.seek_stop = 0x54321;
3943 filter2.seek_stop = 0x65432;
3944 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3945 ok(hr == S_OK, "Got hr %#x.\n", hr);
3946 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
3948 filter2.seek_stop = 0x54321;
3949 filter1.seek_stop = 0x65432;
3950 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3951 ok(hr == S_OK, "Got hr %#x.\n", hr);
3952 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
3954 filter1.seek_hr = filter2.seek_hr = 0xbeef;
3955 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3956 ok(hr == S_OK, "Got hr %#x.\n", hr);
3957 ok(time == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(time));
3959 filter1.seek_hr = E_NOTIMPL;
3960 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3961 ok(hr == S_OK, "Got hr %#x.\n", hr);
3962 ok(time == 0x54321, "Got time %s.\n", wine_dbgstr_longlong(time));
3964 filter1.seek_hr = 0xdeadbeef;
3965 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3966 ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
3968 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
3969 hr = IMediaSeeking_GetStopPosition(seeking, &time);
3970 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
3971 filter1.seek_hr = filter2.seek_hr = S_OK;
3973 flush_cached_seeking(graph, &filter1);
3974 flush_cached_seeking(graph, &filter2);
3976 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
3977 ok(hr == S_OK, "Got hr %#x.\n", hr);
3978 ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
3980 flush_cached_seeking(graph, &filter1);
3981 flush_cached_seeking(graph, &filter2);
3983 current = stop = 0xdeadbeef;
3984 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
3985 ok(hr == S_OK, "Got hr %#x.\n", hr);
3986 ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
3987 ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
3989 flush_cached_seeking(graph, &filter1);
3990 flush_cached_seeking(graph, &filter2);
3992 current = 0x123;
3993 stop = 0x321;
3994 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
3995 &stop, AM_SEEKING_AbsolutePositioning);
3996 ok(hr == S_OK, "Got hr %#x.\n", hr);
3997 ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
3998 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
3999 ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4000 ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4001 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4002 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4004 filter1.seek_hr = filter2.seek_hr = 0xbeef;
4005 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4006 &stop, AM_SEEKING_AbsolutePositioning);
4007 ok(hr == S_OK, "Got hr %#x.\n", hr);
4009 filter1.seek_hr = E_NOTIMPL;
4010 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4011 &stop, AM_SEEKING_AbsolutePositioning);
4012 ok(hr == S_OK, "Got hr %#x.\n", hr);
4014 filter1.seek_hr = 0xdeadbeef;
4015 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4016 &stop, AM_SEEKING_AbsolutePositioning);
4017 ok(hr == 0xdeadbeef, "Got hr %#x.\n", hr);
4019 filter1.seek_hr = filter2.seek_hr = E_NOTIMPL;
4020 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4021 &stop, AM_SEEKING_AbsolutePositioning);
4022 ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
4023 filter1.seek_hr = filter2.seek_hr = S_OK;
4025 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4026 ok(hr == S_OK, "Got hr %#x.\n", hr);
4027 ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
4029 current = stop = 0xdeadbeef;
4030 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4031 ok(hr == S_OK, "Got hr %#x.\n", hr);
4032 ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
4033 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
4035 current = 0x123;
4036 stop = 0x321;
4037 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime,
4038 &stop, AM_SEEKING_AbsolutePositioning);
4039 ok(hr == S_OK, "Got hr %#x.\n", hr);
4040 ok(current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(current));
4041 ok(stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(stop));
4042 ok(filter1.seek_current == 12340000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4043 ok(filter1.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4044 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4045 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4047 current = 0x123;
4048 stop = 0x321;
4049 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4050 &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime);
4051 ok(hr == S_OK, "Got hr %#x.\n", hr);
4052 ok(current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(current));
4053 ok(stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4054 ok(filter1.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_current));
4055 ok(filter1.seek_stop == 43210000, "Got time %s.\n", wine_dbgstr_longlong(filter1.seek_stop));
4056 ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current));
4057 ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
4059 flush_cached_seeking(graph, &filter1);
4060 flush_cached_seeking(graph, &filter2);
4062 hr = IMediaSeeking_SetRate(seeking, 2.0);
4063 ok(hr == S_OK, "Got hr %#x.\n", hr);
4064 todo_wine ok(filter1.seek_rate == 2.0, "Got rate %.16e.\n", filter1.seek_rate);
4065 todo_wine ok(filter2.seek_rate == 2.0, "Got rate %.16e.\n", filter2.seek_rate);
4067 hr = IMediaSeeking_SetRate(seeking, 1.0);
4068 ok(hr == S_OK, "Got hr %#x.\n", hr);
4069 todo_wine ok(filter1.seek_rate == 1.0, "Got rate %.16e.\n", filter1.seek_rate);
4070 todo_wine ok(filter2.seek_rate == 1.0, "Got rate %.16e.\n", filter2.seek_rate);
4072 hr = IMediaSeeking_SetRate(seeking, -1.0);
4073 ok(hr == S_OK, "Got hr %#x.\n", hr);
4074 todo_wine ok(filter1.seek_rate == -1.0, "Got rate %.16e.\n", filter1.seek_rate);
4075 todo_wine ok(filter2.seek_rate == -1.0, "Got rate %.16e.\n", filter2.seek_rate);
4077 flush_cached_seeking(graph, &filter1);
4078 flush_cached_seeking(graph, &filter2);
4080 hr = IMediaSeeking_GetRate(seeking, &rate);
4081 ok(hr == S_OK, "Got hr %#x.\n", hr);
4082 todo_wine ok(rate == -1.0, "Got rate %.16e.\n", rate);
4084 hr = IMediaSeeking_SetRate(seeking, 1.0);
4085 ok(hr == S_OK, "Got hr %#x.\n", hr);
4087 /* Test how retrieving the current position behaves while the graph is
4088 * running. Apparently the graph caches the last position returned by
4089 * SetPositions() and then adds the clock offset to the stream start. */
4091 hr = IMediaControl_Run(control);
4092 ok(hr == S_OK, "Got hr %#x.\n", hr);
4094 /* Note that if the graph is running, it is paused while seeking. */
4095 current = 0;
4096 stop = 9000 * 10000;
4097 hr = IMediaSeeking_SetPositions(seeking, &current, AM_SEEKING_AbsolutePositioning,
4098 &stop, AM_SEEKING_AbsolutePositioning);
4099 ok(hr == S_OK, "Got hr %#x.\n", hr);
4101 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4102 ok(hr == S_OK, "Got hr %#x.\n", hr);
4103 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4104 ok(compare_time(time, 1234 * 10000, 40 * 10000),
4105 "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(time));
4106 current = stop = 0xdeadbeef;
4107 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4108 ok(hr == S_OK, "Got hr %#x.\n", hr);
4109 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4110 ok(compare_time(current, 1234 * 10000, 40 * 10000),
4111 "Expected about 1234ms, got %s.\n", wine_dbgstr_longlong(current));
4112 ok(stop == 9000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4114 /* This remains true even if NoFlush is specified. */
4115 current = 1000 * 10000;
4116 stop = 8000 * 10000;
4117 hr = IMediaSeeking_SetPositions(seeking, &current,
4118 AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush,
4119 &stop, AM_SEEKING_AbsolutePositioning | AM_SEEKING_NoFlush);
4120 ok(hr == S_OK, "Got hr %#x.\n", hr);
4122 Sleep(100);
4124 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4125 ok(hr == S_OK, "Got hr %#x.\n", hr);
4126 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4127 ok(compare_time(time, 1334 * 10000, 80 * 10000),
4128 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
4129 current = stop = 0xdeadbeef;
4130 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4131 ok(hr == S_OK, "Got hr %#x.\n", hr);
4132 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4133 ok(compare_time(current, 1334 * 10000, 80 * 10000),
4134 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
4135 ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4137 hr = IMediaControl_Pause(control);
4138 ok(hr == S_OK, "Got hr %#x.\n", hr);
4140 Sleep(100);
4141 hr = IMediaControl_Run(control);
4142 ok(hr == S_OK, "Got hr %#x.\n", hr);
4144 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4145 ok(hr == S_OK, "Got hr %#x.\n", hr);
4146 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4147 ok(compare_time(time, 1334 * 10000, 80 * 10000),
4148 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(time));
4149 current = stop = 0xdeadbeef;
4150 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4151 ok(hr == S_OK, "Got hr %#x.\n", hr);
4152 if (winetest_interactive) /* Timing problems make this test too liable to fail. */
4153 ok(compare_time(current, 1334 * 10000, 80 * 10000),
4154 "Expected about 1334ms, got %s.\n", wine_dbgstr_longlong(current));
4155 ok(stop == 8000 * 10000, "Got time %s.\n", wine_dbgstr_longlong(stop));
4157 hr = IMediaControl_Stop(control);
4158 ok(hr == S_OK, "Got hr %#x.\n", hr);
4159 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4160 ok(hr == S_OK, "Got hr %#x.\n", hr);
4161 ok(time == 12340000, "Got time %s.\n", wine_dbgstr_longlong(time));
4163 hr = IMediaFilter_SetSyncSource(filter, NULL);
4164 ok(hr == S_OK, "Got hr %#x.\n", hr);
4166 hr = IMediaControl_Run(control);
4167 ok(hr == S_OK, "Got hr %#x.\n", hr);
4169 Sleep(100);
4170 hr = IMediaSeeking_GetCurrentPosition(seeking, &time);
4171 ok(hr == S_OK, "Got hr %#x.\n", hr);
4172 todo_wine ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
4173 current = stop = 0xdeadbeef;
4174 hr = IMediaSeeking_GetPositions(seeking, &current, &stop);
4175 ok(hr == S_OK, "Got hr %#x.\n", hr);
4176 todo_wine ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current));
4177 ok(!stop, "Got time %s.\n", wine_dbgstr_longlong(stop));
4179 hr = IMediaControl_Stop(control);
4180 ok(hr == S_OK, "Got hr %#x.\n", hr);
4182 IMediaFilter_Release(filter);
4183 IMediaControl_Release(control);
4184 IMediaSeeking_Release(seeking);
4186 ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4187 ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4189 ref = IFilterGraph2_Release(graph);
4190 ok(!ref, "Got outstanding refcount %d.\n", hr);
4191 ok(filter1.ref == 1, "Got outstanding refcount %d.\n", filter1.ref);
4192 ok(filter2.ref == 1, "Got outstanding refcount %d.\n", filter2.ref);
4193 ok(filter1.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref);
4194 ok(filter2.seeking_ref == 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
4197 static void test_default_sync_source(void)
4199 struct testpin source_pin, sink1_pin, sink2_pin;
4200 struct testfilter source, sink1, sink2;
4202 IFilterGraph2 *graph = create_graph();
4203 IReferenceClock *clock;
4204 IMediaFilter *filter;
4205 HRESULT hr;
4206 ULONG ref;
4208 testsink_init(&sink1_pin);
4209 testsink_init(&sink2_pin);
4210 testsource_init(&source_pin, NULL, 0);
4211 testfilter_init(&source, &source_pin, 1);
4212 testfilter_init(&sink1, &sink1_pin, 1);
4213 testfilter_init(&sink2, &sink2_pin, 1);
4215 IFilterGraph2_AddFilter(graph, &sink1.IBaseFilter_iface, NULL);
4216 IFilterGraph2_AddFilter(graph, &sink2.IBaseFilter_iface, NULL);
4217 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL);
4218 IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink1_pin.IPin_iface, NULL);
4220 IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
4222 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4223 ok(hr == S_OK, "Got hr %#x.\n", hr);
4225 hr = IMediaFilter_GetSyncSource(filter, &clock);
4226 ok(hr == S_OK, "Got hr %#x.\n", hr);
4227 ok(!!clock, "Reference clock not set.\n");
4228 IReferenceClock_Release(clock);
4230 source.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4232 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4233 ok(hr == S_OK, "Got hr %#x.\n", hr);
4235 hr = IMediaFilter_GetSyncSource(filter, &clock);
4236 ok(hr == S_OK, "Got hr %#x.\n", hr);
4237 ok(clock == &source.IReferenceClock_iface, "Got unexpected clock.\n");
4238 IReferenceClock_Release(clock);
4240 /* The documentation says that connected filters are preferred, but this
4241 * does not in fact seem to be the case. */
4243 sink2.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4245 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4246 ok(hr == S_OK, "Got hr %#x.\n", hr);
4248 hr = IMediaFilter_GetSyncSource(filter, &clock);
4249 ok(hr == S_OK, "Got hr %#x.\n", hr);
4250 todo_wine ok(clock == &sink2.IReferenceClock_iface, "Got unexpected clock.\n");
4251 IReferenceClock_Release(clock);
4253 sink1.IReferenceClock_iface.lpVtbl = &testclock_vtbl;
4255 hr = IFilterGraph2_SetDefaultSyncSource(graph);
4256 ok(hr == S_OK, "Got hr %#x.\n", hr);
4258 hr = IMediaFilter_GetSyncSource(filter, &clock);
4259 ok(hr == S_OK, "Got hr %#x.\n", hr);
4260 todo_wine ok(clock == &sink1.IReferenceClock_iface, "Got unexpected clock.\n");
4261 IReferenceClock_Release(clock);
4263 IMediaFilter_Release(filter);
4264 ref = IFilterGraph2_Release(graph);
4265 ok(!ref, "Got outstanding refcount %d.\n", ref);
4266 ok(sink1.ref == 1, "Got outstanding refcount %d.\n", sink1.ref);
4267 ok(sink2.ref == 1, "Got outstanding refcount %d.\n", sink2.ref);
4268 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
4271 static void test_add_source_filter(void)
4273 static const char bogus_data[20] = {0xde, 0xad, 0xbe, 0xef};
4274 static const char midi_data[20] = {'M','T','h','d'};
4276 IFilterGraph2 *graph = create_graph();
4277 IFileSourceFilter *filesource;
4278 IBaseFilter *filter, *filter2;
4279 FILTER_INFO filter_info;
4280 const WCHAR *filename;
4281 WCHAR *ret_filename;
4282 AM_MEDIA_TYPE mt;
4283 CLSID clsid;
4284 HRESULT hr;
4285 ULONG ref;
4286 BOOL ret;
4287 HKEY key;
4289 /* Test a file which should be registered by extension. */
4291 filename = create_file(L"test.mp3", midi_data, sizeof(midi_data));
4292 hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter);
4293 ok(hr == S_OK, "Got hr %#x.\n", hr);
4295 hr = IBaseFilter_GetClassID(filter, &clsid);
4296 ok(hr == S_OK, "Got hr %#x.\n", hr);
4297 ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
4298 hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
4299 ok(hr == S_OK, "Got hr %#x.\n", hr);
4300 ok(!wcscmp(filter_info.achName, L"test"), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
4301 IFilterGraph_Release(filter_info.pGraph);
4303 hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
4304 ok(hr == S_OK, "Got hr %#x.\n", hr);
4305 hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
4306 ok(hr == S_OK, "Got hr %#x.\n", hr);
4307 ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
4308 wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
4309 ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
4310 ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_MPEG1Audio), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
4311 IFileSourceFilter_Release(filesource);
4313 hr = IFilterGraph2_AddSourceFilter(graph, filename, L"test", &filter2);
4314 ok(hr == S_OK, "Got hr %#x.\n", hr);
4315 ok(filter2 != filter, "Filters shouldn't match.\n");
4316 hr = IFilterGraph2_RemoveFilter(graph, filter2);
4317 ok(hr == S_OK, "Got hr %#x.\n", hr);
4318 ref = IBaseFilter_Release(filter2);
4319 ok(!ref, "Got outstanding refcount %d.\n", ref);
4321 hr = IFilterGraph2_RemoveFilter(graph, filter);
4322 ok(hr == S_OK, "Got hr %#x.\n", hr);
4323 ref = IBaseFilter_Release(filter);
4324 ok(!ref, "Got outstanding refcount %d.\n", ref);
4325 ret = DeleteFileW(filename);
4326 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4328 /* Test a file which should be registered by signature. */
4330 filename = create_file(L"test.avi", midi_data, sizeof(midi_data));
4331 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
4332 ok(hr == S_OK, "Got hr %#x.\n", hr);
4334 hr = IBaseFilter_GetClassID(filter, &clsid);
4335 ok(hr == S_OK, "Got hr %#x.\n", hr);
4336 ok(IsEqualGUID(&clsid, &CLSID_AsyncReader), "Got filter %s.\n", wine_dbgstr_guid(&clsid));
4337 hr = IBaseFilter_QueryFilterInfo(filter, &filter_info);
4338 ok(hr == S_OK, "Got hr %#x.\n", hr);
4339 todo_wine ok(!wcscmp(filter_info.achName, filename), "Got unexpected name %s.\n", wine_dbgstr_w(filter_info.achName));
4340 IFilterGraph_Release(filter_info.pGraph);
4342 hr = IBaseFilter_QueryInterface(filter, &IID_IFileSourceFilter, (void **)&filesource);
4343 ok(hr == S_OK, "Got hr %#x.\n", hr);
4344 hr = IFileSourceFilter_GetCurFile(filesource, &ret_filename, &mt);
4345 ok(hr == S_OK, "Got hr %#x.\n", hr);
4346 ok(!wcscmp(ret_filename, filename), "Expected filename %s, got %s.\n",
4347 wine_dbgstr_w(filename), wine_dbgstr_w(ret_filename));
4348 ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype));
4349 ok(IsEqualGUID(&mt.subtype, &MEDIATYPE_Midi), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype));
4350 IFileSourceFilter_Release(filesource);
4352 hr = IFilterGraph2_RemoveFilter(graph, filter);
4353 ok(hr == S_OK, "Got hr %#x.\n", hr);
4354 ref = IBaseFilter_Release(filter);
4355 ok(!ref, "Got outstanding refcount %d.\n", ref);
4356 ret = DeleteFileW(filename);
4357 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4359 if (!RegCreateKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
4360 "\\{bccddeef-0000-0000-0000-000000000000}", &key))
4362 static const GUID testfilter_clsid = {0x12345678};
4363 struct testpin testfilter_pin;
4364 struct testfilter testfilter;
4365 struct testfilter_cf cf = {{&testfilter_cf_vtbl}, &testfilter};
4366 DWORD cookie;
4368 testsource_init(&testfilter_pin, NULL, 0);
4369 testfilter_init(&testfilter, &testfilter_pin, 1);
4370 testfilter.IFileSourceFilter_iface.lpVtbl = &testfilesource_vtbl;
4372 CoRegisterClassObject(&testfilter_clsid, (IUnknown *)&cf.IClassFactory_iface,
4373 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie);
4374 RegSetValueExA(key, "0", 0, REG_SZ, (const BYTE *)"0,4,,deadbeef", 14);
4375 RegSetValueExA(key, "Source Filter", 0, REG_SZ, (const BYTE *)"{12345678-0000-0000-0000-000000000000}", 39);
4377 filename = create_file(L"test.avi", bogus_data, sizeof(bogus_data));
4378 hr = IFilterGraph2_AddSourceFilter(graph, filename, NULL, &filter);
4379 ok(hr == S_OK, "Got hr %#x.\n", hr);
4380 ok(filter == &testfilter.IBaseFilter_iface, "Got unexpected filter %p.\n", filter);
4382 hr = IFilterGraph2_RemoveFilter(graph, filter);
4383 ok(hr == S_OK, "Got hr %#x.\n", hr);
4384 IBaseFilter_Release(filter);
4385 ref = IBaseFilter_Release(&testfilter.IBaseFilter_iface);
4386 ok(!ref, "Got outstanding refcount %d.\n", ref);
4387 ret = DeleteFileW(filename);
4388 ok(ret, "Failed to delete %s, error %u.\n", wine_dbgstr_w(filename), GetLastError());
4389 RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}"
4390 "\\{bccddeef-0000-0000-0000-000000000000}");
4391 RegDeleteKeyA(HKEY_CLASSES_ROOT, "Media Type\\{abbccdde-0000-0000-0000-000000000000}");
4392 CoRevokeClassObject(cookie);
4394 else
4395 skip("Not enough permission to register media types.\n");
4397 ref = IFilterGraph2_Release(graph);
4398 ok(!ref, "Got outstanding refcount %d.\n", ref);
4401 static HWND get_renderer_hwnd(IFilterGraph2 *graph)
4403 IEnumFilters *enum_filters;
4404 IEnumPins *enum_pins;
4405 IBaseFilter *filter;
4406 IOverlay *overlay;
4407 HWND hwnd = NULL;
4408 HRESULT hr;
4409 IPin *pin;
4411 hr = IFilterGraph2_EnumFilters(graph, &enum_filters);
4412 ok(hr == S_OK, "Got hr %#x.\n", hr);
4414 while (IEnumFilters_Next(enum_filters, 1, &filter, NULL) == S_OK)
4416 hr = IBaseFilter_EnumPins(filter, &enum_pins);
4417 ok(hr == S_OK, "Got hr %#x.\n", hr);
4418 hr = IEnumPins_Next(enum_pins, 1, &pin, NULL);
4419 ok(hr == S_OK, "Got hr %#x.\n", hr);
4420 IEnumPins_Release(enum_pins);
4422 if (SUCCEEDED(IPin_QueryInterface(pin, &IID_IOverlay, (void **)&overlay)))
4424 hr = IOverlay_GetWindowHandle(overlay, &hwnd);
4425 ok(hr == S_OK, "Got hr %#x.\n", hr);
4426 IOverlay_Release(overlay);
4429 IPin_Release(pin);
4430 IBaseFilter_Release(filter);
4433 IEnumFilters_Release(enum_filters);
4435 return hwnd;
4438 static BOOL expect_parent_message = TRUE;
4440 static LRESULT CALLBACK parent_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
4442 ok(expect_parent_message, "Got unexpected message %#x.\n", msg);
4443 return DefWindowProcA(hwnd, msg, wparam, lparam);
4446 static void test_window_threading(void)
4448 static const WNDCLASSA class =
4450 .lpfnWndProc = parent_proc,
4451 .lpszClassName = "quartz_test_parent",
4453 WCHAR *filename = load_resource(L"test.avi");
4454 IFilterGraph2 *graph = create_graph();
4455 IVideoWindow *window;
4456 HWND hwnd, parent;
4457 HRESULT hr;
4458 DWORD tid;
4459 ULONG ref;
4460 BOOL ret;
4462 RegisterClassA(&class);
4464 parent = CreateWindowA("quartz_test_parent", NULL, WS_OVERLAPPEDWINDOW,
4465 50, 50, 150, 150, NULL, NULL, NULL, NULL);
4466 ok(!!parent, "Failed to create parent window.\n");
4468 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
4469 if (FAILED(hr))
4471 skip("Cannot render test file, hr %#x.\n", hr);
4472 IFilterGraph2_Release(graph);
4473 DeleteFileW(filename);
4474 return;
4476 ok(hr == S_OK, "Got hr %#x.\n", hr);
4478 if ((hwnd = get_renderer_hwnd(graph)))
4480 tid = GetWindowThreadProcessId(hwnd, NULL);
4481 ok(tid != GetCurrentThreadId(), "Window should have been created on a separate thread.\n");
4483 /* The thread should be processing messages, or this will hang. */
4484 SendMessageA(hwnd, WM_NULL, 0, 0);
4486 /* Media Player Classic deadlocks if the parent is sent any messages
4487 * while the video window is released. In particular, we must not send
4488 * WM_PARENTNOTIFY. This is not achieved through window styles. */
4489 hr = IFilterGraph2_QueryInterface(graph, &IID_IVideoWindow, (void **)&window);
4490 ok(hr == S_OK, "Got hr %#x.\n", hr);
4491 hr = IVideoWindow_put_Owner(window, (OAHWND)parent);
4492 ok(hr == S_OK, "Got hr %#x.\n", hr);
4493 IVideoWindow_Release(window);
4494 ok(!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_NOPARENTNOTIFY), "Window has WS_EX_NOPARENTNOTIFY.\n");
4496 else
4497 skip("Could not find renderer window.\n");
4499 SetActiveWindow(parent);
4500 expect_parent_message = FALSE;
4501 ref = IFilterGraph2_Release(graph);
4502 ok(!ref, "Got outstanding refcount %d.\n", ref);
4503 expect_parent_message = TRUE;
4505 hwnd = GetActiveWindow();
4506 ok(hwnd == parent, "Parent window lost focus, active window %p.\n", hwnd);
4508 hr = CoCreateInstance(&CLSID_FilterGraphNoThread, NULL, CLSCTX_INPROC_SERVER,
4509 &IID_IFilterGraph2, (void **)&graph);
4510 ok(hr == S_OK, "Got hr %#x.\n", hr);
4512 hr = IFilterGraph2_RenderFile(graph, filename, NULL);
4513 ok(hr == S_OK, "Got hr %#x.\n", hr);
4515 if ((hwnd = get_renderer_hwnd(graph)))
4517 tid = GetWindowThreadProcessId(hwnd, NULL);
4518 ok(tid == GetCurrentThreadId(), "Window should be created on main thread.\n");
4520 else
4521 skip("Could not find renderer window.\n");
4523 ref = IFilterGraph2_Release(graph);
4524 ok(!ref, "Got outstanding refcount %d.\n", ref);
4526 DestroyWindow(parent);
4527 UnregisterClassA("quartz_test_parent", GetModuleHandleA(NULL));
4528 ret = DeleteFileW(filename);
4529 ok(ret, "Failed to delete file, error %u.\n", GetLastError());
4532 /* Hyperdevotion Noire needs to be able to Render() from UYVY. */
4533 static void test_autoplug_uyvy(void)
4535 static const VIDEOINFOHEADER source_format =
4537 .bmiHeader.biSize = sizeof(BITMAPINFOHEADER),
4538 .bmiHeader.biWidth = 600,
4539 .bmiHeader.biHeight = 400,
4540 .bmiHeader.biCompression = mmioFOURCC('U','Y','V','Y'),
4541 .bmiHeader.biBitCount = 16,
4543 AM_MEDIA_TYPE source_type =
4545 .majortype = MEDIATYPE_Video,
4546 .subtype = MEDIASUBTYPE_UYVY,
4547 .formattype = FORMAT_VideoInfo,
4548 .cbFormat = sizeof(source_format),
4549 .pbFormat = (BYTE *)&source_format,
4552 IFilterGraph2 *graph = create_graph();
4553 struct testpin source_pin;
4554 struct testfilter source;
4555 HRESULT hr;
4556 ULONG ref;
4558 testsource_init(&source_pin, NULL, 0);
4559 testfilter_init(&source, &source_pin, 1);
4560 source_pin.request_mt = &source_type;
4562 IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, L"source");
4564 /* Windows 2008 doesn't seem to have an UYVY decoder, and the testbot chalks
4565 * failure to decode up to missing audio hardware, even though we're not
4566 * trying to render audio. */
4567 hr = IFilterGraph2_Render(graph, &source_pin.IPin_iface);
4568 todo_wine ok(hr == S_OK || hr == VFW_E_NO_AUDIO_HARDWARE, "Got hr %#x.\n", hr);
4570 ref = IFilterGraph2_Release(graph);
4571 ok(!ref, "Got outstanding refcount %d.\n", ref);
4572 ok(source.ref == 1, "Got outstanding refcount %d.\n", source.ref);
4573 ok(source_pin.ref == 1, "Got outstanding refcount %d.\n", source_pin.ref);
4576 START_TEST(filtergraph)
4578 CoInitializeEx(NULL, COINIT_MULTITHREADED);
4580 test_interfaces();
4581 test_render_run(L"test.avi", FALSE, TRUE);
4582 test_render_run(L"test.mpg", TRUE, TRUE);
4583 test_render_run(L"test.mp3", TRUE, FALSE);
4584 test_render_run(L"test.wav", TRUE, FALSE);
4585 test_enum_filters();
4586 test_graph_builder_render();
4587 test_graph_builder_connect();
4588 test_aggregation();
4589 test_control_delegation();
4590 test_add_remove_filter();
4591 test_connect_direct();
4592 test_sync_source();
4593 test_filter_state();
4594 test_ec_complete();
4595 test_graph_seeking();
4596 test_default_sync_source();
4597 test_add_source_filter();
4598 test_window_threading();
4599 test_autoplug_uyvy();
4601 CoUninitialize();
4602 test_render_with_multithread();