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