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