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 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 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 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 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 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 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 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 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 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