1 /*
2 * Unit tests for the avi splitter functions
3 *
4 * Copyright (C) 2007 Google (Lei Zhang)
5 * Copyright (C) 2008 Google (Maarten Lankhorst)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define COBJMACROS
23
24 #include "wine/test.h"
25 #include "dshow.h"
26 #include "tlhelp32.h"
27
28 static IUnknown *pAviSplitter = NULL;
29
create_avisplitter(void)30 static BOOL create_avisplitter(void)
31 {
32 HRESULT hr;
33
34 hr = CoCreateInstance(&CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER,
35 &IID_IUnknown, (LPVOID*)&pAviSplitter);
36 return (hr == S_OK && pAviSplitter != NULL);
37 }
38
release_avisplitter(void)39 static void release_avisplitter(void)
40 {
41 HRESULT hr;
42
43 Sleep(1000);
44 hr = IUnknown_Release(pAviSplitter);
45
46 /* Looks like wine has a reference leak somewhere on test_threads tests,
47 * it passes in windows
48 */
49 ok(hr == 0, "IUnknown_Release failed with %d\n", (INT)hr);
50
51 while (hr > 0)
52 hr = IUnknown_Release(pAviSplitter);
53 pAviSplitter = NULL;
54 }
55
test_query_interface(void)56 static void test_query_interface(void)
57 {
58 HRESULT hr;
59 ULONG ref;
60 IUnknown *iface= NULL;
61
62 #define TEST_INTERFACE(riid,expected) do { \
63 hr = IUnknown_QueryInterface(pAviSplitter, &riid, (void**)&iface); \
64 ok( hr == expected, #riid" should %s got %08X\n", expected==S_OK ? "exist" : "not be present", GetLastError() ); \
65 if (hr == S_OK) { \
66 ref = IUnknown_Release(iface); \
67 ok(ref == 1, "Reference is %u, expected 1\n", ref); \
68 } \
69 iface = NULL; \
70 } while(0)
71
72 TEST_INTERFACE(IID_IBaseFilter,S_OK);
73 TEST_INTERFACE(IID_IMediaSeeking,E_NOINTERFACE);
74 TEST_INTERFACE(IID_IKsPropertySet,E_NOINTERFACE);
75 TEST_INTERFACE(IID_IMediaPosition,E_NOINTERFACE);
76 TEST_INTERFACE(IID_IQualityControl,E_NOINTERFACE);
77 TEST_INTERFACE(IID_IQualProp,E_NOINTERFACE);
78 #undef TEST_INTERFACE
79 }
80
test_pin(IPin * pin)81 static void test_pin(IPin *pin)
82 {
83 IMemInputPin *mpin = NULL;
84
85 IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&mpin);
86
87 ok(mpin == NULL, "IMemInputPin found!\n");
88 if (mpin)
89 IMemInputPin_Release(mpin);
90 /* TODO */
91 }
92
test_basefilter(void)93 static void test_basefilter(void)
94 {
95 IEnumPins *pin_enum = NULL;
96 IBaseFilter *base = NULL;
97 IPin *pins[2];
98 ULONG ref;
99 HRESULT hr;
100
101 IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter, (void **)&base);
102 if (base == NULL)
103 {
104 /* test_query_interface handles this case */
105 skip("No IBaseFilter\n");
106 return;
107 }
108
109 hr = IBaseFilter_EnumPins(base, NULL);
110 ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
111
112 hr= IBaseFilter_EnumPins(base, &pin_enum);
113 ok(hr == S_OK, "hr = %08x and not S_OK\n", hr);
114
115 hr = IEnumPins_Next(pin_enum, 1, NULL, NULL);
116 ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
117
118 hr = IEnumPins_Next(pin_enum, 2, pins, NULL);
119 ok(hr == E_INVALIDARG, "hr = %08x and not E_INVALIDARG\n", hr);
120
121 pins[0] = (void *)0xdead;
122 pins[1] = (void *)0xdeed;
123
124 hr = IEnumPins_Next(pin_enum, 2, pins, &ref);
125 ok(hr == S_FALSE, "hr = %08x instead of S_FALSE\n", hr);
126 ok(pins[0] != (void *)0xdead && pins[0] != NULL,
127 "pins[0] = %p\n", pins[0]);
128 if (pins[0] != (void *)0xdead && pins[0] != NULL)
129 {
130 test_pin(pins[0]);
131 IPin_Release(pins[0]);
132 }
133
134 ok(pins[1] == (void *)0xdeed, "pins[1] = %p\n", pins[1]);
135
136 ref = IEnumPins_Release(pin_enum);
137 ok(ref == 0, "ref is %u and not 0!\n", ref);
138
139 IBaseFilter_Release(base);
140 }
141
test_filesourcefilter(void)142 static void test_filesourcefilter(void)
143 {
144 static const WCHAR prefix[] = {'w','i','n',0};
145 static const struct
146 {
147 const char *label;
148 const char *data;
149 DWORD size;
150 const GUID *subtype;
151 }
152 tests[] =
153 {
154 {
155 "AVI",
156 "\x52\x49\x46\x46xxxx\x41\x56\x49\x20",
157 12,
158 &MEDIASUBTYPE_Avi,
159 },
160 {
161 "MPEG1 System",
162 "\x00\x00\x01\xBA\x21\x00\x01\x00\x01\x80\x00\x01\x00\x00\x01\xBB",
163 16,
164 &MEDIASUBTYPE_MPEG1System,
165 },
166 {
167 "MPEG1 Video",
168 "\x00\x00\x01\xB3",
169 4,
170 &MEDIASUBTYPE_MPEG1Video,
171 },
172 {
173 "MPEG1 Audio",
174 "\xFF\xE0",
175 2,
176 &MEDIASUBTYPE_MPEG1Audio,
177 },
178 {
179 "MPEG2 Program",
180 "\x00\x00\x01\xBA\x40",
181 5,
182 &MEDIASUBTYPE_MPEG2_PROGRAM,
183 },
184 {
185 "WAVE",
186 "\x52\x49\x46\x46xxxx\x57\x41\x56\x45",
187 12,
188 &MEDIASUBTYPE_WAVE,
189 },
190 {
191 "unknown format",
192 "Hello World",
193 11,
194 NULL, /* FIXME: should be &MEDIASUBTYPE_NULL */
195 },
196 };
197 WCHAR path[MAX_PATH], temp[MAX_PATH];
198 IFileSourceFilter *filesource;
199 DWORD ret, written;
200 IBaseFilter *base;
201 AM_MEDIA_TYPE mt;
202 OLECHAR *olepath;
203 BOOL success;
204 HANDLE file;
205 HRESULT hr;
206 int i;
207
208 ret = GetTempPathW(MAX_PATH, temp);
209 ok(ret, "GetTempPathW failed with error %u\n", GetLastError());
210 ret = GetTempFileNameW(temp, prefix, 0, path);
211 ok(ret, "GetTempFileNameW failed with error %u\n", GetLastError());
212
213 for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
214 {
215 trace("Running test for %s\n", tests[i].label);
216
217 file = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
218 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
219 ok(file != INVALID_HANDLE_VALUE, "CreateFileW failed with error %u\n", GetLastError());
220 success = WriteFile(file, tests[i].data, tests[i].size, &written, NULL);
221 ok(success, "WriteFile failed with error %u\n", GetLastError());
222 ok(written == tests[i].size, "could not write test data\n");
223 CloseHandle(file);
224
225 hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
226 &IID_IBaseFilter, (void **)&base);
227 ok(hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
228 hr = IBaseFilter_QueryInterface(base, &IID_IFileSourceFilter, (void **)&filesource);
229 ok(hr == S_OK, "IBaseFilter_QueryInterface failed with %08x\n", hr);
230
231 olepath = (void *)0xdeadbeef;
232 hr = IFileSourceFilter_GetCurFile(filesource, &olepath, NULL);
233 ok(hr == S_OK, "expected S_OK, got %08x\n", hr);
234 ok(olepath == NULL, "expected NULL, got %p\n", olepath);
235
236 hr = IFileSourceFilter_Load(filesource, NULL, NULL);
237 ok(hr == E_POINTER, "expected E_POINTER, got %08x\n", hr);
238
239 hr = IFileSourceFilter_Load(filesource, path, NULL);
240 ok(hr == S_OK, "IFileSourceFilter_Load failed with %08x\n", hr);
241
242 hr = IFileSourceFilter_GetCurFile(filesource, NULL, &mt);
243 ok(hr == E_POINTER, "expected E_POINTER, got %08x\n", hr);
244
245 olepath = NULL;
246 hr = IFileSourceFilter_GetCurFile(filesource, &olepath, NULL);
247 ok(hr == S_OK, "expected S_OK, got %08x\n", hr);
248 CoTaskMemFree(olepath);
249
250 olepath = NULL;
251 memset(&mt, 0x11, sizeof(mt));
252 hr = IFileSourceFilter_GetCurFile(filesource, &olepath, &mt);
253 ok(hr == S_OK, "expected S_OK, got %08x\n", hr);
254 ok(!lstrcmpW(olepath, path),
255 "expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(olepath));
256 if (tests[i].subtype)
257 {
258 ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Stream),
259 "expected MEDIATYPE_Stream, got %s\n", wine_dbgstr_guid(&mt.majortype));
260 ok(IsEqualGUID(&mt.subtype, tests[i].subtype),
261 "expected %s, got %s\n", wine_dbgstr_guid(tests[i].subtype), wine_dbgstr_guid(&mt.subtype));
262 }
263 CoTaskMemFree(olepath);
264
265 IFileSourceFilter_Release(filesource);
266 IBaseFilter_Release(base);
267
268 success = DeleteFileW(path);
269 ok(success, "DeleteFileW failed with error %u\n", GetLastError());
270 }
271 }
272
273 static const WCHAR avifile[] = {'t','e','s','t','.','a','v','i',0};
274
load_resource(const WCHAR * name)275 static WCHAR *load_resource(const WCHAR *name)
276 {
277 static WCHAR pathW[MAX_PATH];
278 DWORD written;
279 HANDLE file;
280 HRSRC res;
281 void *ptr;
282
283 GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
284 lstrcatW(pathW, name);
285
286 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
287 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
288 GetLastError());
289
290 res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA);
291 ok( res != 0, "couldn't find resource\n" );
292 ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
293 WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
294 ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
295 CloseHandle( file );
296
297 return pathW;
298 }
299
test_filter_graph(void)300 static void test_filter_graph(void)
301 {
302 IFileSourceFilter *pfile = NULL;
303 IBaseFilter *preader = NULL, *pavi = NULL;
304 IEnumPins *enumpins = NULL;
305 IPin *filepin = NULL, *avipin = NULL;
306 HRESULT hr;
307 HANDLE file = NULL;
308 PIN_DIRECTION dir = PINDIR_OUTPUT;
309 char buffer[13];
310 DWORD readbytes;
311 FILTER_STATE state;
312
313 WCHAR *filename = load_resource(avifile);
314
315 file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
316 NULL, OPEN_EXISTING, 0, NULL);
317 if (file == INVALID_HANDLE_VALUE)
318 {
319 skip("Could not read test file \"%s\", skipping test\n", wine_dbgstr_w(filename));
320 DeleteFileW(filename);
321 return;
322 }
323
324 memset(buffer, 0, 13);
325 readbytes = 12;
326 ReadFile(file, buffer, readbytes, &readbytes, NULL);
327 CloseHandle(file);
328 if (strncmp(buffer, "RIFF", 4) || strcmp(buffer + 8, "AVI "))
329 {
330 skip("%s is not an avi riff file, not doing the avi splitter test\n",
331 wine_dbgstr_w(filename));
332 DeleteFileW(filename);
333 return;
334 }
335
336 hr = IUnknown_QueryInterface(pAviSplitter, &IID_IFileSourceFilter,
337 (void **)&pfile);
338 ok(hr == E_NOINTERFACE,
339 "Avi splitter returns unexpected error: %08x\n", hr);
340 if (pfile)
341 IFileSourceFilter_Release(pfile);
342 pfile = NULL;
343
344 hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
345 &IID_IBaseFilter, (LPVOID*)&preader);
346 ok(hr == S_OK, "Could not create asynchronous reader: %08x\n", hr);
347 if (hr != S_OK)
348 goto fail;
349
350 hr = IBaseFilter_QueryInterface(preader, &IID_IFileSourceFilter,
351 (void**)&pfile);
352 ok(hr == S_OK, "Could not get IFileSourceFilter: %08x\n", hr);
353 if (hr != S_OK)
354 goto fail;
355
356 hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter,
357 (void**)&pavi);
358 ok(hr == S_OK, "Could not get base filter: %08x\n", hr);
359 if (hr != S_OK)
360 goto fail;
361
362 hr = IFileSourceFilter_Load(pfile, filename, NULL);
363 if (hr != S_OK)
364 {
365 trace("Could not load file: %08x\n", hr);
366 goto fail;
367 }
368
369 hr = IBaseFilter_EnumPins(preader, &enumpins);
370 ok(hr == S_OK, "No enumpins: %08x\n", hr);
371 if (hr != S_OK)
372 goto fail;
373
374 hr = IEnumPins_Next(enumpins, 1, &filepin, NULL);
375 ok(hr == S_OK, "No pin: %08x\n", hr);
376 if (hr != S_OK)
377 goto fail;
378
379 IEnumPins_Release(enumpins);
380 enumpins = NULL;
381
382 hr = IBaseFilter_EnumPins(pavi, &enumpins);
383 ok(hr == S_OK, "No enumpins: %08x\n", hr);
384 if (hr != S_OK)
385 goto fail;
386
387 hr = IEnumPins_Next(enumpins, 1, &avipin, NULL);
388 ok(hr == S_OK, "No pin: %08x\n", hr);
389 if (hr != S_OK)
390 goto fail;
391
392 hr = IPin_Connect(filepin, avipin, NULL);
393 ok(hr == S_OK, "Could not connect: %08x\n", hr);
394 if (hr != S_OK)
395 goto fail;
396
397 IPin_Release(avipin);
398 avipin = NULL;
399
400 IEnumPins_Reset(enumpins);
401
402 /* Windows puts the pins in the order: Outputpins - Inputpin,
403 * wine does the reverse, just don't test it for now
404 * Hate to admit it, but windows way makes more sense
405 */
406 while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
407 {
408 IPin_QueryDirection(avipin, &dir);
409 if (dir == PINDIR_OUTPUT)
410 {
411 /* Well, connect it to a null renderer! */
412 IBaseFilter *pnull = NULL;
413 IEnumPins *nullenum = NULL;
414 IPin *nullpin = NULL;
415
416 hr = CoCreateInstance(&CLSID_NullRenderer, NULL,
417 CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pnull);
418 if (hr == REGDB_E_CLASSNOTREG)
419 {
420 win_skip("Null renderer not registered, skipping\n");
421 break;
422 }
423 ok(hr == S_OK, "Could not create null renderer: %08x\n", hr);
424
425 hr = IBaseFilter_EnumPins(pnull, &nullenum);
426 ok(hr == S_OK, "Failed to enum pins, hr %#x.\n", hr);
427 hr = IEnumPins_Next(nullenum, 1, &nullpin, NULL);
428 ok(hr == S_OK, "Failed to get next pin, hr %#x.\n", hr);
429 IEnumPins_Release(nullenum);
430 IPin_QueryDirection(nullpin, &dir);
431
432 hr = IPin_Connect(avipin, nullpin, NULL);
433 ok(hr == S_OK, "Failed to connect output pin: %08x\n", hr);
434 IPin_Release(nullpin);
435 if (hr != S_OK)
436 {
437 IBaseFilter_Release(pnull);
438 break;
439 }
440 IBaseFilter_Run(pnull, 0);
441 }
442
443 IPin_Release(avipin);
444 avipin = NULL;
445 }
446
447 if (avipin)
448 IPin_Release(avipin);
449 avipin = NULL;
450
451 if (hr != S_OK)
452 goto fail2;
453 /* At this point there is a minimalistic connected avi splitter that can
454 * be used for all sorts of source filter tests. However that still needs
455 * to be written at a later time.
456 *
457 * Interesting tests:
458 * - Can you disconnect an output pin while running?
459 * Expecting: Yes
460 * - Can you disconnect the pullpin while running?
461 * Expecting: No
462 * - Is the reference count incremented during playback or when connected?
463 * Does this happen once for every output pin? Or is there something else
464 * going on.
465 * Expecting: You tell me
466 */
467
468 IBaseFilter_Run(preader, 0);
469 IBaseFilter_Run(pavi, 0);
470 IBaseFilter_GetState(pavi, INFINITE, &state);
471
472 IBaseFilter_Pause(pavi);
473 IBaseFilter_Pause(preader);
474 IBaseFilter_Stop(pavi);
475 IBaseFilter_Stop(preader);
476 IBaseFilter_GetState(pavi, INFINITE, &state);
477 IBaseFilter_GetState(preader, INFINITE, &state);
478
479 fail2:
480 IEnumPins_Reset(enumpins);
481 while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
482 {
483 IPin *to = NULL;
484
485 IPin_QueryDirection(avipin, &dir);
486 IPin_ConnectedTo(avipin, &to);
487 if (to)
488 {
489 IPin_Release(to);
490
491 if (dir == PINDIR_OUTPUT)
492 {
493 PIN_INFO info;
494
495 hr = IPin_QueryPinInfo(to, &info);
496 ok(hr == S_OK, "Failed to query pin info, hr %#x.\n", hr);
497
498 /* Release twice: Once normal, second from the
499 * previous while loop
500 */
501 IBaseFilter_Stop(info.pFilter);
502 IPin_Disconnect(to);
503 IPin_Disconnect(avipin);
504 IBaseFilter_Release(info.pFilter);
505 IBaseFilter_Release(info.pFilter);
506 }
507 else
508 {
509 IPin_Disconnect(to);
510 IPin_Disconnect(avipin);
511 }
512 }
513 IPin_Release(avipin);
514 avipin = NULL;
515 }
516
517 fail:
518 if (hr != S_OK)
519 skip("Prerequisites not matched, skipping remainder of test\n");
520 if (enumpins)
521 IEnumPins_Release(enumpins);
522
523 if (avipin)
524 IPin_Release(avipin);
525 if (filepin)
526 {
527 IPin *to = NULL;
528
529 IPin_ConnectedTo(filepin, &to);
530 if (to)
531 {
532 IPin_Disconnect(filepin);
533 IPin_Disconnect(to);
534 }
535 IPin_Release(filepin);
536 }
537
538 if (preader)
539 IBaseFilter_Release(preader);
540 if (pavi)
541 IBaseFilter_Release(pavi);
542 if (pfile)
543 IFileSourceFilter_Release(pfile);
544
545 DeleteFileW(filename);
546 }
547
START_TEST(avisplitter)548 START_TEST(avisplitter)
549 {
550 CoInitialize(NULL);
551
552 if (!create_avisplitter())
553 {
554 skip("Could not create avisplitter\n");
555 return;
556 }
557
558 test_query_interface();
559 test_basefilter();
560 test_filesourcefilter();
561 test_filter_graph();
562
563 release_avisplitter();
564
565 CoUninitialize();
566 }
567