1 /* 2 * File Source Filter 3 * 4 * Copyright 2003 Robert Shearman 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #define NONAMELESSUNION 22 #define NONAMELESSSTRUCT 23 24 #include "quartz_private.h" 25 26 #include "wine/debug.h" 27 #include "wine/unicode.h" 28 #include "pin.h" 29 #include "uuids.h" 30 #include "vfwmsgs.h" 31 #include "winbase.h" 32 #include "winreg.h" 33 #include "shlwapi.h" 34 #include <assert.h> 35 36 WINE_DEFAULT_DEBUG_CHANNEL(quartz); 37 38 static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 }; 39 40 typedef struct AsyncReader 41 { 42 BaseFilter filter; 43 IFileSourceFilter IFileSourceFilter_iface; 44 IAMFilterMiscFlags IAMFilterMiscFlags_iface; 45 46 IPin * pOutputPin; 47 LPOLESTR pszFileName; 48 AM_MEDIA_TYPE * pmt; 49 } AsyncReader; 50 51 static inline AsyncReader *impl_from_BaseFilter(BaseFilter *iface) 52 { 53 return CONTAINING_RECORD(iface, AsyncReader, filter); 54 } 55 56 static inline AsyncReader *impl_from_IBaseFilter(IBaseFilter *iface) 57 { 58 return CONTAINING_RECORD(iface, AsyncReader, filter.IBaseFilter_iface); 59 } 60 61 static inline AsyncReader *impl_from_IFileSourceFilter(IFileSourceFilter *iface) 62 { 63 return CONTAINING_RECORD(iface, AsyncReader, IFileSourceFilter_iface); 64 } 65 66 static inline AsyncReader *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) 67 { 68 return CONTAINING_RECORD(iface, AsyncReader, IAMFilterMiscFlags_iface); 69 } 70 71 static const IBaseFilterVtbl AsyncReader_Vtbl; 72 static const IFileSourceFilterVtbl FileSource_Vtbl; 73 static const IAsyncReaderVtbl FileAsyncReader_Vtbl; 74 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl; 75 76 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin); 77 78 static const WCHAR mediatype_name[] = { 79 'M', 'e', 'd', 'i', 'a', ' ', 'T', 'y', 'p', 'e', 0 }; 80 static const WCHAR subtype_name[] = { 81 'S', 'u', 'b', 't', 'y', 'p', 'e', 0 }; 82 static const WCHAR source_filter_name[] = { 83 'S','o','u','r','c','e',' ','F','i','l','t','e','r',0}; 84 85 static HRESULT process_extensions(HKEY hkeyExtensions, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType, GUID * sourceFilter) 86 { 87 WCHAR *extension; 88 LONG l; 89 HKEY hsub; 90 WCHAR keying[39]; 91 DWORD size; 92 93 if (!pszFileName) 94 return E_POINTER; 95 96 /* Get the part of the name that matters */ 97 extension = PathFindExtensionW(pszFileName); 98 if (*extension != '.') 99 return E_FAIL; 100 101 l = RegOpenKeyExW(hkeyExtensions, extension, 0, KEY_READ, &hsub); 102 if (l) 103 return E_FAIL; 104 105 if (majorType) 106 { 107 size = sizeof(keying); 108 l = RegQueryValueExW(hsub, mediatype_name, NULL, NULL, (LPBYTE)keying, &size); 109 if (!l) 110 CLSIDFromString(keying, majorType); 111 } 112 113 if (minorType) 114 { 115 size = sizeof(keying); 116 if (!l) 117 l = RegQueryValueExW(hsub, subtype_name, NULL, NULL, (LPBYTE)keying, &size); 118 if (!l) 119 CLSIDFromString(keying, minorType); 120 } 121 122 if (sourceFilter) 123 { 124 size = sizeof(keying); 125 if (!l) 126 l = RegQueryValueExW(hsub, source_filter_name, NULL, NULL, (LPBYTE)keying, &size); 127 if (!l) 128 CLSIDFromString(keying, sourceFilter); 129 } 130 131 RegCloseKey(hsub); 132 133 if (!l) 134 return S_OK; 135 return E_FAIL; 136 } 137 138 static unsigned char byte_from_hex_char(WCHAR wHex) 139 { 140 switch (tolowerW(wHex)) 141 { 142 case '0': 143 case '1': 144 case '2': 145 case '3': 146 case '4': 147 case '5': 148 case '6': 149 case '7': 150 case '8': 151 case '9': 152 return (wHex - '0') & 0xf; 153 case 'a': 154 case 'b': 155 case 'c': 156 case 'd': 157 case 'e': 158 case 'f': 159 return (wHex - 'a' + 10) & 0xf; 160 default: 161 return 0; 162 } 163 } 164 165 static HRESULT process_pattern_string(LPCWSTR wszPatternString, IAsyncReader * pReader) 166 { 167 ULONG ulOffset; 168 ULONG ulBytes; 169 BYTE * pbMask; 170 BYTE * pbValue; 171 BYTE * pbFile; 172 HRESULT hr = S_OK; 173 ULONG strpos; 174 175 TRACE("\t\tPattern string: %s\n", debugstr_w(wszPatternString)); 176 177 /* format: "offset, bytestocompare, mask, value" */ 178 179 ulOffset = strtolW(wszPatternString, NULL, 10); 180 181 if (!(wszPatternString = strchrW(wszPatternString, ','))) 182 return E_INVALIDARG; 183 184 wszPatternString++; /* skip ',' */ 185 186 ulBytes = strtolW(wszPatternString, NULL, 10); 187 188 pbMask = HeapAlloc(GetProcessHeap(), 0, ulBytes); 189 pbValue = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBytes); 190 pbFile = HeapAlloc(GetProcessHeap(), 0, ulBytes); 191 192 /* default mask is match everything */ 193 memset(pbMask, 0xFF, ulBytes); 194 195 if (!(wszPatternString = strchrW(wszPatternString, ','))) 196 hr = E_INVALIDARG; 197 198 if (hr == S_OK) 199 { 200 wszPatternString++; /* skip ',' */ 201 while (!isxdigitW(*wszPatternString) && (*wszPatternString != ',')) wszPatternString++; 202 203 for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++) 204 { 205 if ((strpos % 2) == 1) /* odd numbered position */ 206 pbMask[strpos / 2] |= byte_from_hex_char(*wszPatternString); 207 else 208 pbMask[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4; 209 } 210 211 if (!(wszPatternString = strchrW(wszPatternString, ','))) 212 hr = E_INVALIDARG; 213 else 214 wszPatternString++; /* skip ',' */ 215 } 216 217 if (hr == S_OK) 218 { 219 for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++) 220 ; 221 222 for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++) 223 { 224 if ((strpos % 2) == 1) /* odd numbered position */ 225 pbValue[strpos / 2] |= byte_from_hex_char(*wszPatternString); 226 else 227 pbValue[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4; 228 } 229 } 230 231 if (hr == S_OK) 232 hr = IAsyncReader_SyncRead(pReader, ulOffset, ulBytes, pbFile); 233 234 if (hr == S_OK) 235 { 236 ULONG i; 237 for (i = 0; i < ulBytes; i++) 238 if ((pbFile[i] & pbMask[i]) != pbValue[i]) 239 { 240 hr = S_FALSE; 241 break; 242 } 243 } 244 245 HeapFree(GetProcessHeap(), 0, pbMask); 246 HeapFree(GetProcessHeap(), 0, pbValue); 247 HeapFree(GetProcessHeap(), 0, pbFile); 248 249 /* if we encountered no errors with this string, and there is a following tuple, then we 250 * have to match that as well to succeed */ 251 if ((hr == S_OK) && (wszPatternString = strchrW(wszPatternString, ','))) 252 return process_pattern_string(wszPatternString + 1, pReader); 253 else 254 return hr; 255 } 256 257 HRESULT GetClassMediaFile(IAsyncReader * pReader, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType, GUID * sourceFilter) 258 { 259 HKEY hkeyMediaType = NULL; 260 LONG lRet; 261 HRESULT hr = S_OK; 262 BOOL bFound = FALSE; 263 static const WCHAR wszMediaType[] = {'M','e','d','i','a',' ','T','y','p','e',0}; 264 265 TRACE("(%p, %s, %p, %p)\n", pReader, debugstr_w(pszFileName), majorType, minorType); 266 267 if(majorType) 268 *majorType = GUID_NULL; 269 if(minorType) 270 *minorType = GUID_NULL; 271 if(sourceFilter) 272 *sourceFilter = GUID_NULL; 273 274 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMediaType, 0, KEY_READ, &hkeyMediaType); 275 hr = HRESULT_FROM_WIN32(lRet); 276 277 if (SUCCEEDED(hr)) 278 { 279 DWORD indexMajor; 280 281 for (indexMajor = 0; !bFound; indexMajor++) 282 { 283 HKEY hkeyMajor; 284 WCHAR wszMajorKeyName[CHARS_IN_GUID]; 285 DWORD dwKeyNameLength = sizeof(wszMajorKeyName) / sizeof(wszMajorKeyName[0]); 286 static const WCHAR wszExtensions[] = {'E','x','t','e','n','s','i','o','n','s',0}; 287 288 if (RegEnumKeyExW(hkeyMediaType, indexMajor, wszMajorKeyName, &dwKeyNameLength, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 289 break; 290 if (RegOpenKeyExW(hkeyMediaType, wszMajorKeyName, 0, KEY_READ, &hkeyMajor) != ERROR_SUCCESS) 291 break; 292 TRACE("%s\n", debugstr_w(wszMajorKeyName)); 293 if (!strcmpW(wszExtensions, wszMajorKeyName)) 294 { 295 if (process_extensions(hkeyMajor, pszFileName, majorType, minorType, sourceFilter) == S_OK) 296 bFound = TRUE; 297 } 298 /* We need a reader interface to check bytes */ 299 else if (pReader) 300 { 301 DWORD indexMinor; 302 303 for (indexMinor = 0; !bFound; indexMinor++) 304 { 305 HKEY hkeyMinor; 306 WCHAR wszMinorKeyName[CHARS_IN_GUID]; 307 DWORD dwMinorKeyNameLen = sizeof(wszMinorKeyName) / sizeof(wszMinorKeyName[0]); 308 WCHAR wszSourceFilterKeyName[CHARS_IN_GUID]; 309 DWORD dwSourceFilterKeyNameLen = sizeof(wszSourceFilterKeyName); 310 DWORD maxValueLen; 311 DWORD indexValue; 312 313 if (RegEnumKeyExW(hkeyMajor, indexMinor, wszMinorKeyName, &dwMinorKeyNameLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 314 break; 315 316 if (RegOpenKeyExW(hkeyMajor, wszMinorKeyName, 0, KEY_READ, &hkeyMinor) != ERROR_SUCCESS) 317 break; 318 319 TRACE("\t%s\n", debugstr_w(wszMinorKeyName)); 320 321 if (RegQueryInfoKeyW(hkeyMinor, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &maxValueLen, NULL, NULL) != ERROR_SUCCESS) 322 break; 323 324 for (indexValue = 0; !bFound; indexValue++) 325 { 326 DWORD dwType; 327 WCHAR wszValueName[14]; /* longest name we should encounter will be "Source Filter" */ 328 LPWSTR wszPatternString = HeapAlloc(GetProcessHeap(), 0, maxValueLen); 329 DWORD dwValueNameLen = sizeof(wszValueName) / sizeof(wszValueName[0]); /* remember this is in chars */ 330 DWORD dwDataLen = maxValueLen; /* remember this is in bytes */ 331 332 if (RegEnumValueW(hkeyMinor, indexValue, wszValueName, &dwValueNameLen, NULL, &dwType, (LPBYTE)wszPatternString, &dwDataLen) != ERROR_SUCCESS) 333 { 334 HeapFree(GetProcessHeap(), 0, wszPatternString); 335 break; 336 } 337 338 if (strcmpW(wszValueName, source_filter_name)==0) { 339 HeapFree(GetProcessHeap(), 0, wszPatternString); 340 continue; 341 } 342 343 /* if it is not the source filter value */ 344 if (process_pattern_string(wszPatternString, pReader) == S_OK) 345 { 346 HeapFree(GetProcessHeap(), 0, wszPatternString); 347 if (majorType && FAILED(CLSIDFromString(wszMajorKeyName, majorType))) 348 break; 349 if (minorType && FAILED(CLSIDFromString(wszMinorKeyName, minorType))) 350 break; 351 if (sourceFilter) 352 { 353 /* Look up the source filter key */ 354 if (RegQueryValueExW(hkeyMinor, source_filter_name, NULL, NULL, (LPBYTE)wszSourceFilterKeyName, &dwSourceFilterKeyNameLen)) 355 break; 356 if (FAILED(CLSIDFromString(wszSourceFilterKeyName, sourceFilter))) 357 break; 358 } 359 bFound = TRUE; 360 } else 361 HeapFree(GetProcessHeap(), 0, wszPatternString); 362 } 363 CloseHandle(hkeyMinor); 364 } 365 } 366 CloseHandle(hkeyMajor); 367 } 368 } 369 CloseHandle(hkeyMediaType); 370 371 if (SUCCEEDED(hr) && !bFound) 372 { 373 ERR("Media class not found\n"); 374 hr = E_FAIL; 375 } 376 else if (bFound) 377 { 378 TRACE("Found file's class:\n"); 379 if(majorType) 380 TRACE("\tmajor = %s\n", qzdebugstr_guid(majorType)); 381 if(minorType) 382 TRACE("\tsubtype = %s\n", qzdebugstr_guid(minorType)); 383 if(sourceFilter) 384 TRACE("\tsource filter = %s\n", qzdebugstr_guid(sourceFilter)); 385 } 386 387 return hr; 388 } 389 390 static IPin* WINAPI AsyncReader_GetPin(BaseFilter *iface, int pos) 391 { 392 AsyncReader *This = impl_from_BaseFilter(iface); 393 394 TRACE("%p->(%d)\n", This, pos); 395 396 if (pos >= 1 || !This->pOutputPin) 397 return NULL; 398 399 IPin_AddRef(This->pOutputPin); 400 return This->pOutputPin; 401 } 402 403 static LONG WINAPI AsyncReader_GetPinCount(BaseFilter *iface) 404 { 405 AsyncReader *This = impl_from_BaseFilter(iface); 406 407 TRACE("%p->()\n", This); 408 409 if (!This->pOutputPin) 410 return 0; 411 else 412 return 1; 413 } 414 415 static const BaseFilterFuncTable BaseFuncTable = { 416 AsyncReader_GetPin, 417 AsyncReader_GetPinCount 418 }; 419 420 HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv) 421 { 422 AsyncReader *pAsyncRead; 423 424 if( pUnkOuter ) 425 return CLASS_E_NOAGGREGATION; 426 427 pAsyncRead = CoTaskMemAlloc(sizeof(AsyncReader)); 428 429 if (!pAsyncRead) 430 return E_OUTOFMEMORY; 431 432 BaseFilter_Init(&pAsyncRead->filter, &AsyncReader_Vtbl, &CLSID_AsyncReader, (DWORD_PTR)(__FILE__ ": AsyncReader.csFilter"), &BaseFuncTable); 433 434 pAsyncRead->IFileSourceFilter_iface.lpVtbl = &FileSource_Vtbl; 435 pAsyncRead->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_Vtbl; 436 pAsyncRead->pOutputPin = NULL; 437 438 pAsyncRead->pszFileName = NULL; 439 pAsyncRead->pmt = NULL; 440 441 *ppv = pAsyncRead; 442 443 TRACE("-- created at %p\n", pAsyncRead); 444 445 return S_OK; 446 } 447 448 /** IUnknown methods **/ 449 450 static HRESULT WINAPI AsyncReader_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) 451 { 452 AsyncReader *This = impl_from_IBaseFilter(iface); 453 454 TRACE("%p->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv); 455 456 *ppv = NULL; 457 458 if (IsEqualIID(riid, &IID_IUnknown)) 459 *ppv = &This->filter.IBaseFilter_iface; 460 else if (IsEqualIID(riid, &IID_IPersist)) 461 *ppv = &This->filter.IBaseFilter_iface; 462 else if (IsEqualIID(riid, &IID_IMediaFilter)) 463 *ppv = &This->filter.IBaseFilter_iface; 464 else if (IsEqualIID(riid, &IID_IBaseFilter)) 465 *ppv = &This->filter.IBaseFilter_iface; 466 else if (IsEqualIID(riid, &IID_IFileSourceFilter)) 467 *ppv = &This->IFileSourceFilter_iface; 468 else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags)) 469 *ppv = &This->IAMFilterMiscFlags_iface; 470 471 if (*ppv) 472 { 473 IUnknown_AddRef((IUnknown *)(*ppv)); 474 return S_OK; 475 } 476 477 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking) && 478 !IsEqualIID(riid, &IID_IVideoWindow) && !IsEqualIID(riid, &IID_IBasicAudio)) 479 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 480 481 return E_NOINTERFACE; 482 } 483 484 static ULONG WINAPI AsyncReader_Release(IBaseFilter * iface) 485 { 486 AsyncReader *This = impl_from_IBaseFilter(iface); 487 ULONG refCount = InterlockedDecrement(&This->filter.refCount); 488 489 TRACE("%p->() Release from %d\n", This, refCount + 1); 490 491 if (!refCount) 492 { 493 if (This->pOutputPin) 494 { 495 IPin *pConnectedTo; 496 if(SUCCEEDED(IPin_ConnectedTo(This->pOutputPin, &pConnectedTo))) 497 { 498 IPin_Disconnect(pConnectedTo); 499 IPin_Release(pConnectedTo); 500 } 501 IPin_Disconnect(This->pOutputPin); 502 IPin_Release(This->pOutputPin); 503 } 504 CoTaskMemFree(This->pszFileName); 505 if (This->pmt) 506 FreeMediaType(This->pmt); 507 BaseFilter_Destroy(&This->filter); 508 CoTaskMemFree(This); 509 return 0; 510 } 511 else 512 return refCount; 513 } 514 515 /** IMediaFilter methods **/ 516 517 static HRESULT WINAPI AsyncReader_Stop(IBaseFilter * iface) 518 { 519 AsyncReader *This = impl_from_IBaseFilter(iface); 520 521 TRACE("%p->()\n", This); 522 523 This->filter.state = State_Stopped; 524 525 return S_OK; 526 } 527 528 static HRESULT WINAPI AsyncReader_Pause(IBaseFilter * iface) 529 { 530 AsyncReader *This = impl_from_IBaseFilter(iface); 531 532 TRACE("%p->()\n", This); 533 534 This->filter.state = State_Paused; 535 536 return S_OK; 537 } 538 539 static HRESULT WINAPI AsyncReader_Run(IBaseFilter * iface, REFERENCE_TIME tStart) 540 { 541 AsyncReader *This = impl_from_IBaseFilter(iface); 542 543 TRACE("%p->(%s)\n", This, wine_dbgstr_longlong(tStart)); 544 545 This->filter.state = State_Running; 546 547 return S_OK; 548 } 549 550 /** IBaseFilter methods **/ 551 552 static HRESULT WINAPI AsyncReader_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin) 553 { 554 AsyncReader *This = impl_from_IBaseFilter(iface); 555 TRACE("%p->(%s, %p)\n", This, debugstr_w(Id), ppPin); 556 557 if (!Id || !ppPin) 558 return E_POINTER; 559 560 if (strcmpW(Id, wszOutputPinName)) 561 { 562 *ppPin = NULL; 563 return VFW_E_NOT_FOUND; 564 } 565 566 *ppPin = This->pOutputPin; 567 IPin_AddRef(*ppPin); 568 return S_OK; 569 } 570 571 static const IBaseFilterVtbl AsyncReader_Vtbl = 572 { 573 AsyncReader_QueryInterface, 574 BaseFilterImpl_AddRef, 575 AsyncReader_Release, 576 BaseFilterImpl_GetClassID, 577 AsyncReader_Stop, 578 AsyncReader_Pause, 579 AsyncReader_Run, 580 BaseFilterImpl_GetState, 581 BaseFilterImpl_SetSyncSource, 582 BaseFilterImpl_GetSyncSource, 583 BaseFilterImpl_EnumPins, 584 AsyncReader_FindPin, 585 BaseFilterImpl_QueryFilterInfo, 586 BaseFilterImpl_JoinFilterGraph, 587 BaseFilterImpl_QueryVendorInfo 588 }; 589 590 static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv) 591 { 592 AsyncReader *This = impl_from_IFileSourceFilter(iface); 593 594 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); 595 } 596 597 static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface) 598 { 599 AsyncReader *This = impl_from_IFileSourceFilter(iface); 600 601 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); 602 } 603 604 static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface) 605 { 606 AsyncReader *This = impl_from_IFileSourceFilter(iface); 607 608 return IBaseFilter_Release(&This->filter.IBaseFilter_iface); 609 } 610 611 static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt) 612 { 613 HRESULT hr; 614 HANDLE hFile; 615 IAsyncReader * pReader = NULL; 616 AsyncReader *This = impl_from_IFileSourceFilter(iface); 617 618 TRACE("%p->(%s, %p)\n", This, debugstr_w(pszFileName), pmt); 619 620 if (!pszFileName) 621 return E_POINTER; 622 623 /* open file */ 624 /* FIXME: check the sharing values that native uses */ 625 hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 626 627 if (hFile == INVALID_HANDLE_VALUE) 628 { 629 return HRESULT_FROM_WIN32(GetLastError()); 630 } 631 632 /* create pin */ 633 hr = FileAsyncReader_Construct(hFile, &This->filter.IBaseFilter_iface, &This->filter.csFilter, &This->pOutputPin); 634 BaseFilterImpl_IncrementPinVersion(&This->filter); 635 636 if (SUCCEEDED(hr)) 637 hr = IPin_QueryInterface(This->pOutputPin, &IID_IAsyncReader, (LPVOID *)&pReader); 638 639 /* store file name & media type */ 640 if (SUCCEEDED(hr)) 641 { 642 CoTaskMemFree(This->pszFileName); 643 if (This->pmt) 644 FreeMediaType(This->pmt); 645 646 This->pszFileName = CoTaskMemAlloc((strlenW(pszFileName) + 1) * sizeof(WCHAR)); 647 strcpyW(This->pszFileName, pszFileName); 648 649 This->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); 650 if (!pmt) 651 { 652 This->pmt->bFixedSizeSamples = TRUE; 653 This->pmt->bTemporalCompression = FALSE; 654 This->pmt->cbFormat = 0; 655 This->pmt->pbFormat = NULL; 656 This->pmt->pUnk = NULL; 657 This->pmt->lSampleSize = 0; 658 This->pmt->formattype = FORMAT_None; 659 hr = GetClassMediaFile(pReader, pszFileName, &This->pmt->majortype, &This->pmt->subtype, NULL); 660 if (FAILED(hr)) 661 { 662 This->pmt->majortype = MEDIATYPE_Stream; 663 This->pmt->subtype = MEDIASUBTYPE_NULL; 664 hr = S_OK; 665 } 666 } 667 else 668 CopyMediaType(This->pmt, pmt); 669 } 670 671 if (pReader) 672 IAsyncReader_Release(pReader); 673 674 if (FAILED(hr)) 675 { 676 if (This->pOutputPin) 677 { 678 IPin_Release(This->pOutputPin); 679 This->pOutputPin = NULL; 680 } 681 682 CoTaskMemFree(This->pszFileName); 683 if (This->pmt) 684 FreeMediaType(This->pmt); 685 This->pszFileName = NULL; 686 This->pmt = NULL; 687 688 CloseHandle(hFile); 689 } 690 691 /* FIXME: check return codes */ 692 return hr; 693 } 694 695 static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter * iface, LPOLESTR * ppszFileName, AM_MEDIA_TYPE * pmt) 696 { 697 AsyncReader *This = impl_from_IFileSourceFilter(iface); 698 699 TRACE("%p->(%p, %p)\n", This, ppszFileName, pmt); 700 701 if (!ppszFileName) 702 return E_POINTER; 703 704 /* copy file name & media type if available, otherwise clear the outputs */ 705 if (This->pszFileName) 706 { 707 *ppszFileName = CoTaskMemAlloc((strlenW(This->pszFileName) + 1) * sizeof(WCHAR)); 708 strcpyW(*ppszFileName, This->pszFileName); 709 } 710 else 711 *ppszFileName = NULL; 712 713 if (pmt) 714 { 715 if (This->pmt) 716 CopyMediaType(pmt, This->pmt); 717 else 718 ZeroMemory(pmt, sizeof(*pmt)); 719 } 720 721 return S_OK; 722 } 723 724 static const IFileSourceFilterVtbl FileSource_Vtbl = 725 { 726 FileSource_QueryInterface, 727 FileSource_AddRef, 728 FileSource_Release, 729 FileSource_Load, 730 FileSource_GetCurFile 731 }; 732 733 734 /* the dwUserData passed back to user */ 735 typedef struct DATAREQUEST 736 { 737 IMediaSample * pSample; /* sample passed to us by user */ 738 DWORD_PTR dwUserData; /* user data passed to us */ 739 OVERLAPPED ovl; /* our overlapped structure */ 740 } DATAREQUEST; 741 742 typedef struct FileAsyncReader 743 { 744 BaseOutputPin pin; 745 IAsyncReader IAsyncReader_iface; 746 747 ALLOCATOR_PROPERTIES allocProps; 748 HANDLE hFile; 749 BOOL bFlushing; 750 /* Why would you need more? Every sample has its own handle */ 751 LONG queued_number; 752 LONG samples; 753 LONG oldest_sample; 754 CRITICAL_SECTION csList; /* critical section to prevent concurrency issues */ 755 DATAREQUEST *sample_list; 756 757 /* Have a handle for every sample, and then one more as flushing handle */ 758 HANDLE *handle_list; 759 } FileAsyncReader; 760 761 static inline FileAsyncReader *impl_from_IPin(IPin *iface) 762 { 763 return CONTAINING_RECORD(iface, FileAsyncReader, pin.pin.IPin_iface); 764 } 765 766 static inline FileAsyncReader *impl_from_BasePin(BasePin *iface) 767 { 768 return CONTAINING_RECORD(iface, FileAsyncReader, pin.pin); 769 } 770 771 static inline FileAsyncReader *impl_from_BaseOutputPin(BaseOutputPin *iface) 772 { 773 return CONTAINING_RECORD(iface, FileAsyncReader, pin); 774 } 775 776 static inline BaseOutputPin *impl_BaseOutputPin_from_BasePin(BasePin *iface) 777 { 778 return CONTAINING_RECORD(iface, BaseOutputPin, pin); 779 } 780 781 static inline FileAsyncReader *impl_from_IAsyncReader(IAsyncReader *iface) 782 { 783 return CONTAINING_RECORD(iface, FileAsyncReader, IAsyncReader_iface); 784 } 785 786 static HRESULT WINAPI FileAsyncReaderPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt) 787 { 788 FileAsyncReader *This = impl_from_IPin(iface); 789 AM_MEDIA_TYPE *pmt_filter = impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter)->pmt; 790 791 FIXME("(%p, %p)\n", iface, pmt); 792 793 if (IsEqualGUID(&pmt->majortype, &pmt_filter->majortype) && 794 IsEqualGUID(&pmt->subtype, &pmt_filter->subtype) && 795 IsEqualGUID(&pmt->formattype, &FORMAT_None)) 796 return S_OK; 797 798 return S_FALSE; 799 } 800 801 static HRESULT WINAPI FileAsyncReaderPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt) 802 { 803 FileAsyncReader *This = impl_from_BasePin(iface); 804 if (iPosition < 0) 805 return E_INVALIDARG; 806 if (iPosition > 0) 807 return VFW_S_NO_MORE_ITEMS; 808 CopyMediaType(pmt, impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter)->pmt); 809 return S_OK; 810 } 811 812 /* overridden pin functions */ 813 814 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) 815 { 816 FileAsyncReader *This = impl_from_IPin(iface); 817 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv); 818 819 *ppv = NULL; 820 821 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) 822 *ppv = &This->pin.pin.IPin_iface; 823 else if (IsEqualIID(riid, &IID_IAsyncReader)) 824 *ppv = &This->IAsyncReader_iface; 825 826 if (*ppv) 827 { 828 IUnknown_AddRef((IUnknown *)*ppv); 829 return S_OK; 830 } 831 832 if (!IsEqualIID(riid, &IID_IMediaSeeking)) 833 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 834 835 return E_NOINTERFACE; 836 } 837 838 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface) 839 { 840 FileAsyncReader *This = impl_from_IPin(iface); 841 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount); 842 int x; 843 844 TRACE("(%p)->() Release from %d\n", This, refCount + 1); 845 846 if (!refCount) 847 { 848 CoTaskMemFree(This->sample_list); 849 if (This->handle_list) 850 { 851 for (x = 0; x <= This->samples; ++x) 852 CloseHandle(This->handle_list[x]); 853 CoTaskMemFree(This->handle_list); 854 } 855 CloseHandle(This->hFile); 856 This->csList.DebugInfo->Spare[0] = 0; 857 DeleteCriticalSection(&This->csList); 858 BaseOutputPin_Destroy(&This->pin); 859 return 0; 860 } 861 return refCount; 862 } 863 864 static const IPinVtbl FileAsyncReaderPin_Vtbl = 865 { 866 FileAsyncReaderPin_QueryInterface, 867 BasePinImpl_AddRef, 868 FileAsyncReaderPin_Release, 869 BaseOutputPinImpl_Connect, 870 BaseOutputPinImpl_ReceiveConnection, 871 BasePinImpl_Disconnect, 872 BasePinImpl_ConnectedTo, 873 BasePinImpl_ConnectionMediaType, 874 BasePinImpl_QueryPinInfo, 875 BasePinImpl_QueryDirection, 876 BasePinImpl_QueryId, 877 FileAsyncReaderPin_QueryAccept, 878 BasePinImpl_EnumMediaTypes, 879 BasePinImpl_QueryInternalConnections, 880 BaseOutputPinImpl_EndOfStream, 881 BaseOutputPinImpl_BeginFlush, 882 BaseOutputPinImpl_EndFlush, 883 BasePinImpl_NewSegment 884 }; 885 886 /* Function called as a helper to IPin_Connect */ 887 /* specific AM_MEDIA_TYPE - it cannot be NULL */ 888 /* this differs from standard OutputPin_AttemptConnection only in that it 889 * doesn't need the IMemInputPin interface on the receiving pin */ 890 static HRESULT WINAPI FileAsyncReaderPin_AttemptConnection(BasePin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 891 { 892 BaseOutputPin *This = impl_BaseOutputPin_from_BasePin(iface); 893 HRESULT hr; 894 895 TRACE("%p->(%p, %p)\n", This, pReceivePin, pmt); 896 dump_AM_MEDIA_TYPE(pmt); 897 898 /* FIXME: call queryacceptproc */ 899 900 This->pin.pConnectedTo = pReceivePin; 901 IPin_AddRef(pReceivePin); 902 CopyMediaType(&This->pin.mtCurrent, pmt); 903 904 hr = IPin_ReceiveConnection(pReceivePin, &iface->IPin_iface, pmt); 905 906 if (FAILED(hr)) 907 { 908 IPin_Release(This->pin.pConnectedTo); 909 This->pin.pConnectedTo = NULL; 910 FreeMediaType(&This->pin.mtCurrent); 911 } 912 913 TRACE(" -- %x\n", hr); 914 return hr; 915 } 916 917 static HRESULT WINAPI FileAsyncReaderPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) 918 { 919 FileAsyncReader *This = impl_from_BaseOutputPin(iface); 920 ALLOCATOR_PROPERTIES actual; 921 922 if (ppropInputRequest->cbAlign && ppropInputRequest->cbAlign != This->allocProps.cbAlign) 923 FIXME("Requested Buffer cbAlign mismatch %i,%i\n",This->allocProps.cbAlign, ppropInputRequest->cbAlign); 924 if (ppropInputRequest->cbPrefix) 925 FIXME("Requested Buffer cbPrefix mismatch %i,%i\n",This->allocProps.cbPrefix, ppropInputRequest->cbPrefix); 926 if (ppropInputRequest->cbBuffer) 927 FIXME("Requested Buffer cbBuffer mismatch %i,%i\n",This->allocProps.cbBuffer, ppropInputRequest->cbBuffer); 928 if (ppropInputRequest->cBuffers) 929 FIXME("Requested Buffer cBuffers mismatch %i,%i\n",This->allocProps.cBuffers, ppropInputRequest->cBuffers); 930 931 return IMemAllocator_SetProperties(pAlloc, &This->allocProps, &actual); 932 } 933 934 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = { 935 { 936 NULL, 937 FileAsyncReaderPin_AttemptConnection, 938 BasePinImpl_GetMediaTypeVersion, 939 FileAsyncReaderPin_GetMediaType 940 }, 941 FileAsyncReaderPin_DecideBufferSize, 942 BaseOutputPinImpl_DecideAllocator, 943 BaseOutputPinImpl_BreakConnect 944 }; 945 946 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin) 947 { 948 PIN_INFO piOutput; 949 HRESULT hr; 950 951 *ppPin = NULL; 952 piOutput.dir = PINDIR_OUTPUT; 953 piOutput.pFilter = pBaseFilter; 954 strcpyW(piOutput.achName, wszOutputPinName); 955 hr = BaseOutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, &output_BaseOutputFuncTable, pCritSec, ppPin); 956 957 if (SUCCEEDED(hr)) 958 { 959 FileAsyncReader *pPinImpl = (FileAsyncReader *)*ppPin; 960 pPinImpl->IAsyncReader_iface.lpVtbl = &FileAsyncReader_Vtbl; 961 pPinImpl->hFile = hFile; 962 pPinImpl->bFlushing = FALSE; 963 pPinImpl->sample_list = NULL; 964 pPinImpl->handle_list = NULL; 965 pPinImpl->queued_number = 0; 966 InitializeCriticalSection(&pPinImpl->csList); 967 pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList"); 968 } 969 return hr; 970 } 971 972 /* IAsyncReader */ 973 974 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv) 975 { 976 FileAsyncReader *This = impl_from_IAsyncReader(iface); 977 978 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv); 979 } 980 981 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface) 982 { 983 FileAsyncReader *This = impl_from_IAsyncReader(iface); 984 985 return IPin_AddRef(&This->pin.pin.IPin_iface); 986 } 987 988 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface) 989 { 990 FileAsyncReader *This = impl_from_IAsyncReader(iface); 991 992 return IPin_Release(&This->pin.pin.IPin_iface); 993 } 994 995 #define DEF_ALIGNMENT 1 996 997 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual) 998 { 999 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1000 1001 HRESULT hr = S_OK; 1002 1003 TRACE("%p->(%p, %p, %p)\n", This, pPreferred, pProps, ppActual); 1004 1005 if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0) 1006 pProps->cbAlign = DEF_ALIGNMENT; 1007 1008 if (pPreferred) 1009 { 1010 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps); 1011 /* FIXME: check we are still aligned */ 1012 if (SUCCEEDED(hr)) 1013 { 1014 IMemAllocator_AddRef(pPreferred); 1015 *ppActual = pPreferred; 1016 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr); 1017 goto done; 1018 } 1019 } 1020 1021 pPreferred = NULL; 1022 1023 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred); 1024 1025 if (SUCCEEDED(hr)) 1026 { 1027 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps); 1028 /* FIXME: check we are still aligned */ 1029 if (SUCCEEDED(hr)) 1030 { 1031 *ppActual = pPreferred; 1032 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr); 1033 } 1034 } 1035 1036 done: 1037 if (SUCCEEDED(hr)) 1038 { 1039 CoTaskMemFree(This->sample_list); 1040 if (This->handle_list) 1041 { 1042 int x; 1043 for (x = 0; x <= This->samples; ++x) 1044 CloseHandle(This->handle_list[x]); 1045 CoTaskMemFree(This->handle_list); 1046 } 1047 1048 This->samples = pProps->cBuffers; 1049 This->oldest_sample = 0; 1050 TRACE("Samples: %u\n", This->samples); 1051 This->sample_list = CoTaskMemAlloc(sizeof(This->sample_list[0]) * pProps->cBuffers); 1052 This->handle_list = CoTaskMemAlloc(sizeof(HANDLE) * pProps->cBuffers * 2); 1053 1054 if (This->sample_list && This->handle_list) 1055 { 1056 int x; 1057 ZeroMemory(This->sample_list, sizeof(This->sample_list[0]) * pProps->cBuffers); 1058 for (x = 0; x < This->samples; ++x) 1059 { 1060 This->sample_list[x].ovl.hEvent = This->handle_list[x] = CreateEventW(NULL, 0, 0, NULL); 1061 if (x + 1 < This->samples) 1062 This->handle_list[This->samples + 1 + x] = This->handle_list[x]; 1063 } 1064 This->handle_list[This->samples] = CreateEventW(NULL, 1, 0, NULL); 1065 This->allocProps = *pProps; 1066 } 1067 else 1068 { 1069 hr = E_OUTOFMEMORY; 1070 CoTaskMemFree(This->sample_list); 1071 CoTaskMemFree(This->handle_list); 1072 This->samples = 0; 1073 This->sample_list = NULL; 1074 This->handle_list = NULL; 1075 } 1076 } 1077 1078 if (FAILED(hr)) 1079 { 1080 *ppActual = NULL; 1081 if (pPreferred) 1082 IMemAllocator_Release(pPreferred); 1083 } 1084 1085 TRACE("-- %x\n", hr); 1086 return hr; 1087 } 1088 1089 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples. 1090 * however, this would be quite complicated to do and may be a bit error prone */ 1091 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser) 1092 { 1093 HRESULT hr = S_OK; 1094 REFERENCE_TIME Start; 1095 REFERENCE_TIME Stop; 1096 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1097 LPBYTE pBuffer = NULL; 1098 1099 TRACE("%p->(%p, %lx)\n", This, pSample, dwUser); 1100 1101 if (!pSample) 1102 return E_POINTER; 1103 1104 /* get start and stop positions in bytes */ 1105 if (SUCCEEDED(hr)) 1106 hr = IMediaSample_GetTime(pSample, &Start, &Stop); 1107 1108 if (SUCCEEDED(hr)) 1109 hr = IMediaSample_GetPointer(pSample, &pBuffer); 1110 1111 EnterCriticalSection(&This->csList); 1112 if (This->bFlushing) 1113 { 1114 LeaveCriticalSection(&This->csList); 1115 return VFW_E_WRONG_STATE; 1116 } 1117 1118 if (SUCCEEDED(hr)) 1119 { 1120 DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start); 1121 DATAREQUEST *pDataRq; 1122 int x; 1123 1124 /* Try to insert above the waiting sample if possible */ 1125 for (x = This->oldest_sample; x < This->samples; ++x) 1126 { 1127 if (!This->sample_list[x].pSample) 1128 break; 1129 } 1130 1131 if (x >= This->samples) 1132 for (x = 0; x < This->oldest_sample; ++x) 1133 { 1134 if (!This->sample_list[x].pSample) 1135 break; 1136 } 1137 1138 /* There must be a sample we have found */ 1139 assert(x < This->samples); 1140 ++This->queued_number; 1141 1142 pDataRq = This->sample_list + x; 1143 1144 pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start); 1145 pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8)); 1146 pDataRq->dwUserData = dwUser; 1147 1148 /* we violate traditional COM rules here by maintaining 1149 * a reference to the sample, but not calling AddRef, but 1150 * that's what MSDN says to do */ 1151 pDataRq->pSample = pSample; 1152 1153 /* this is definitely not how it is implemented on Win9x 1154 * as they do not support async reads on files, but it is 1155 * sooo much easier to use this than messing around with threads! 1156 */ 1157 if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl)) 1158 hr = HRESULT_FROM_WIN32(GetLastError()); 1159 1160 /* ERROR_IO_PENDING is not actually an error since this is what we want! */ 1161 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) 1162 hr = S_OK; 1163 } 1164 1165 LeaveCriticalSection(&This->csList); 1166 1167 TRACE("-- %x\n", hr); 1168 return hr; 1169 } 1170 1171 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser) 1172 { 1173 HRESULT hr = S_OK; 1174 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1175 DWORD buffer = ~0; 1176 1177 TRACE("%p->(%u, %p, %p)\n", This, dwTimeout, ppSample, pdwUser); 1178 1179 *ppSample = NULL; 1180 *pdwUser = 0; 1181 1182 EnterCriticalSection(&This->csList); 1183 if (!This->bFlushing) 1184 { 1185 LONG oldest = This->oldest_sample; 1186 1187 if (!This->queued_number) 1188 { 1189 /* It could be that nothing is queued right now, but that can be fixed */ 1190 WARN("Called without samples in queue and not flushing!!\n"); 1191 } 1192 LeaveCriticalSection(&This->csList); 1193 1194 /* wait for an object to read, or time out */ 1195 buffer = WaitForMultipleObjectsEx(This->samples+1, This->handle_list + oldest, FALSE, dwTimeout, TRUE); 1196 1197 EnterCriticalSection(&This->csList); 1198 if (buffer <= This->samples) 1199 { 1200 /* Re-scale the buffer back to normal */ 1201 buffer += oldest; 1202 1203 /* Uh oh, we overshot the flusher handle, renormalize it back to 0..Samples-1 */ 1204 if (buffer > This->samples) 1205 buffer -= This->samples + 1; 1206 assert(buffer <= This->samples); 1207 } 1208 1209 if (buffer >= This->samples) 1210 { 1211 if (buffer != This->samples) 1212 { 1213 FIXME("Returned: %u (%08x)\n", buffer, GetLastError()); 1214 hr = VFW_E_TIMEOUT; 1215 } 1216 else 1217 hr = VFW_E_WRONG_STATE; 1218 buffer = ~0; 1219 } 1220 else 1221 --This->queued_number; 1222 } 1223 1224 if (This->bFlushing && buffer == ~0) 1225 { 1226 for (buffer = 0; buffer < This->samples; ++buffer) 1227 { 1228 if (This->sample_list[buffer].pSample) 1229 { 1230 ResetEvent(This->handle_list[buffer]); 1231 break; 1232 } 1233 } 1234 if (buffer == This->samples) 1235 { 1236 assert(!This->queued_number); 1237 hr = VFW_E_TIMEOUT; 1238 } 1239 else 1240 { 1241 --This->queued_number; 1242 hr = S_OK; 1243 } 1244 } 1245 1246 if (SUCCEEDED(hr)) 1247 { 1248 REFERENCE_TIME rtStart, rtStop; 1249 REFERENCE_TIME rtSampleStart, rtSampleStop; 1250 DATAREQUEST *pDataRq = This->sample_list + buffer; 1251 DWORD dwBytes = 0; 1252 1253 /* get any errors */ 1254 if (!This->bFlushing && !GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE)) 1255 hr = HRESULT_FROM_WIN32(GetLastError()); 1256 1257 /* Return the sample no matter what so it can be destroyed */ 1258 *ppSample = pDataRq->pSample; 1259 *pdwUser = pDataRq->dwUserData; 1260 1261 if (This->bFlushing) 1262 hr = VFW_E_WRONG_STATE; 1263 1264 if (FAILED(hr)) 1265 dwBytes = 0; 1266 1267 /* Set the time on the sample */ 1268 IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes); 1269 1270 rtStart = (DWORD64)pDataRq->ovl.u.s.Offset + ((DWORD64)pDataRq->ovl.u.s.OffsetHigh << 32); 1271 rtStart = MEDIATIME_FROM_BYTES(rtStart); 1272 rtStop = rtStart + MEDIATIME_FROM_BYTES(dwBytes); 1273 1274 IMediaSample_GetTime(pDataRq->pSample, &rtSampleStart, &rtSampleStop); 1275 assert(rtStart == rtSampleStart); 1276 assert(rtStop <= rtSampleStop); 1277 1278 IMediaSample_SetTime(pDataRq->pSample, &rtStart, &rtStop); 1279 assert(rtStart == rtSampleStart); 1280 if (hr == S_OK) 1281 assert(rtStop == rtSampleStop); 1282 else 1283 assert(rtStop == rtStart); 1284 1285 This->sample_list[buffer].pSample = NULL; 1286 assert(This->oldest_sample < This->samples); 1287 1288 if (buffer == This->oldest_sample) 1289 { 1290 LONG x; 1291 for (x = This->oldest_sample + 1; x < This->samples; ++x) 1292 if (This->sample_list[x].pSample) 1293 break; 1294 if (x >= This->samples) 1295 for (x = 0; x < This->oldest_sample; ++x) 1296 if (This->sample_list[x].pSample) 1297 break; 1298 if (This->oldest_sample == x) 1299 /* No samples found, reset to 0 */ 1300 x = 0; 1301 This->oldest_sample = x; 1302 } 1303 } 1304 LeaveCriticalSection(&This->csList); 1305 1306 TRACE("-- %x\n", hr); 1307 return hr; 1308 } 1309 1310 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer); 1311 1312 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample) 1313 { 1314 BYTE * pBuffer; 1315 REFERENCE_TIME tStart; 1316 REFERENCE_TIME tStop; 1317 HRESULT hr; 1318 1319 TRACE("(%p)\n", pSample); 1320 1321 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 1322 1323 if (SUCCEEDED(hr)) 1324 hr = IMediaSample_GetPointer(pSample, &pBuffer); 1325 1326 if (SUCCEEDED(hr)) 1327 hr = FileAsyncReader_SyncRead(iface, 1328 BYTES_FROM_MEDIATIME(tStart), 1329 (LONG) BYTES_FROM_MEDIATIME(tStop - tStart), 1330 pBuffer); 1331 1332 TRACE("-- %x\n", hr); 1333 return hr; 1334 } 1335 1336 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer) 1337 { 1338 OVERLAPPED ovl; 1339 HRESULT hr = S_OK; 1340 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1341 1342 TRACE("%p->(%s, %d, %p)\n", This, wine_dbgstr_longlong(llPosition), lLength, pBuffer); 1343 1344 ZeroMemory(&ovl, sizeof(ovl)); 1345 1346 ovl.hEvent = CreateEventW(NULL, 0, 0, NULL); 1347 /* NOTE: llPosition is the actual byte position to start reading from */ 1348 ovl.u.s.Offset = (DWORD) llPosition; 1349 ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8)); 1350 1351 if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl)) 1352 hr = HRESULT_FROM_WIN32(GetLastError()); 1353 1354 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) 1355 hr = S_OK; 1356 1357 if (SUCCEEDED(hr)) 1358 { 1359 DWORD dwBytesRead; 1360 1361 if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE)) 1362 hr = HRESULT_FROM_WIN32(GetLastError()); 1363 } 1364 1365 CloseHandle(ovl.hEvent); 1366 1367 TRACE("-- %x\n", hr); 1368 return hr; 1369 } 1370 1371 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable) 1372 { 1373 DWORD dwSizeLow; 1374 DWORD dwSizeHigh; 1375 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1376 1377 TRACE("%p->(%p, %p)\n", This, pTotal, pAvailable); 1378 1379 if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) && 1380 (GetLastError() != NO_ERROR)) 1381 return HRESULT_FROM_WIN32(GetLastError()); 1382 1383 *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8); 1384 1385 *pAvailable = *pTotal; 1386 1387 return S_OK; 1388 } 1389 1390 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface) 1391 { 1392 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1393 1394 TRACE("%p->()\n", This); 1395 1396 EnterCriticalSection(&This->csList); 1397 This->bFlushing = TRUE; 1398 CancelIo(This->hFile); 1399 SetEvent(This->handle_list[This->samples]); 1400 LeaveCriticalSection(&This->csList); 1401 1402 return S_OK; 1403 } 1404 1405 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface) 1406 { 1407 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1408 int x; 1409 1410 TRACE("%p->()\n", This); 1411 1412 EnterCriticalSection(&This->csList); 1413 ResetEvent(This->handle_list[This->samples]); 1414 This->bFlushing = FALSE; 1415 for (x = 0; x < This->samples; ++x) 1416 assert(!This->sample_list[x].pSample); 1417 1418 LeaveCriticalSection(&This->csList); 1419 1420 return S_OK; 1421 } 1422 1423 static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 1424 { 1425 FileAsyncReader_QueryInterface, 1426 FileAsyncReader_AddRef, 1427 FileAsyncReader_Release, 1428 FileAsyncReader_RequestAllocator, 1429 FileAsyncReader_Request, 1430 FileAsyncReader_WaitForNext, 1431 FileAsyncReader_SyncReadAligned, 1432 FileAsyncReader_SyncRead, 1433 FileAsyncReader_Length, 1434 FileAsyncReader_BeginFlush, 1435 FileAsyncReader_EndFlush, 1436 }; 1437 1438 1439 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) { 1440 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1441 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); 1442 } 1443 1444 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) { 1445 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1446 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); 1447 } 1448 1449 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) { 1450 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1451 return IBaseFilter_Release(&This->filter.IBaseFilter_iface); 1452 } 1453 1454 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) { 1455 return AM_FILTER_MISC_FLAGS_IS_SOURCE; 1456 } 1457 1458 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = { 1459 AMFilterMiscFlags_QueryInterface, 1460 AMFilterMiscFlags_AddRef, 1461 AMFilterMiscFlags_Release, 1462 AMFilterMiscFlags_GetMiscFlags 1463 }; 1464