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_CheckMediaType(BasePin *pin, const AM_MEDIA_TYPE *pmt) 787 { 788 AM_MEDIA_TYPE *pmt_filter = impl_from_IBaseFilter(pin->pinInfo.pFilter)->pmt; 789 790 FIXME("(%p, %p)\n", pin, pmt); 791 792 if (IsEqualGUID(&pmt->majortype, &pmt_filter->majortype) && 793 IsEqualGUID(&pmt->subtype, &pmt_filter->subtype) && 794 IsEqualGUID(&pmt->formattype, &FORMAT_None)) 795 return S_OK; 796 797 return S_FALSE; 798 } 799 800 static HRESULT WINAPI FileAsyncReaderPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt) 801 { 802 FileAsyncReader *This = impl_from_BasePin(iface); 803 if (iPosition < 0) 804 return E_INVALIDARG; 805 if (iPosition > 0) 806 return VFW_S_NO_MORE_ITEMS; 807 CopyMediaType(pmt, impl_from_IBaseFilter(This->pin.pin.pinInfo.pFilter)->pmt); 808 return S_OK; 809 } 810 811 /* overridden pin functions */ 812 813 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) 814 { 815 FileAsyncReader *This = impl_from_IPin(iface); 816 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv); 817 818 *ppv = NULL; 819 820 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) 821 *ppv = &This->pin.pin.IPin_iface; 822 else if (IsEqualIID(riid, &IID_IAsyncReader)) 823 *ppv = &This->IAsyncReader_iface; 824 825 if (*ppv) 826 { 827 IUnknown_AddRef((IUnknown *)*ppv); 828 return S_OK; 829 } 830 831 if (!IsEqualIID(riid, &IID_IMediaSeeking)) 832 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 833 834 return E_NOINTERFACE; 835 } 836 837 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface) 838 { 839 FileAsyncReader *This = impl_from_IPin(iface); 840 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount); 841 int x; 842 843 TRACE("(%p)->() Release from %d\n", This, refCount + 1); 844 845 if (!refCount) 846 { 847 CoTaskMemFree(This->sample_list); 848 if (This->handle_list) 849 { 850 for (x = 0; x <= This->samples; ++x) 851 CloseHandle(This->handle_list[x]); 852 CoTaskMemFree(This->handle_list); 853 } 854 CloseHandle(This->hFile); 855 This->csList.DebugInfo->Spare[0] = 0; 856 DeleteCriticalSection(&This->csList); 857 BaseOutputPin_Destroy(&This->pin); 858 return 0; 859 } 860 return refCount; 861 } 862 863 static const IPinVtbl FileAsyncReaderPin_Vtbl = 864 { 865 FileAsyncReaderPin_QueryInterface, 866 BasePinImpl_AddRef, 867 FileAsyncReaderPin_Release, 868 BaseOutputPinImpl_Connect, 869 BaseOutputPinImpl_ReceiveConnection, 870 BasePinImpl_Disconnect, 871 BasePinImpl_ConnectedTo, 872 BasePinImpl_ConnectionMediaType, 873 BasePinImpl_QueryPinInfo, 874 BasePinImpl_QueryDirection, 875 BasePinImpl_QueryId, 876 BasePinImpl_QueryAccept, 877 BasePinImpl_EnumMediaTypes, 878 BasePinImpl_QueryInternalConnections, 879 BaseOutputPinImpl_EndOfStream, 880 BaseOutputPinImpl_BeginFlush, 881 BaseOutputPinImpl_EndFlush, 882 BasePinImpl_NewSegment 883 }; 884 885 /* Function called as a helper to IPin_Connect */ 886 /* specific AM_MEDIA_TYPE - it cannot be NULL */ 887 /* this differs from standard OutputPin_AttemptConnection only in that it 888 * doesn't need the IMemInputPin interface on the receiving pin */ 889 static HRESULT WINAPI FileAsyncReaderPin_AttemptConnection(BasePin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 890 { 891 BaseOutputPin *This = impl_BaseOutputPin_from_BasePin(iface); 892 HRESULT hr; 893 894 TRACE("%p->(%p, %p)\n", This, pReceivePin, pmt); 895 dump_AM_MEDIA_TYPE(pmt); 896 897 /* FIXME: call queryacceptproc */ 898 899 This->pin.pConnectedTo = pReceivePin; 900 IPin_AddRef(pReceivePin); 901 CopyMediaType(&This->pin.mtCurrent, pmt); 902 903 hr = IPin_ReceiveConnection(pReceivePin, &iface->IPin_iface, pmt); 904 905 if (FAILED(hr)) 906 { 907 IPin_Release(This->pin.pConnectedTo); 908 This->pin.pConnectedTo = NULL; 909 FreeMediaType(&This->pin.mtCurrent); 910 } 911 912 TRACE(" -- %x\n", hr); 913 return hr; 914 } 915 916 static HRESULT WINAPI FileAsyncReaderPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) 917 { 918 FileAsyncReader *This = impl_from_BaseOutputPin(iface); 919 ALLOCATOR_PROPERTIES actual; 920 921 if (ppropInputRequest->cbAlign && ppropInputRequest->cbAlign != This->allocProps.cbAlign) 922 FIXME("Requested Buffer cbAlign mismatch %i,%i\n",This->allocProps.cbAlign, ppropInputRequest->cbAlign); 923 if (ppropInputRequest->cbPrefix) 924 FIXME("Requested Buffer cbPrefix mismatch %i,%i\n",This->allocProps.cbPrefix, ppropInputRequest->cbPrefix); 925 if (ppropInputRequest->cbBuffer) 926 FIXME("Requested Buffer cbBuffer mismatch %i,%i\n",This->allocProps.cbBuffer, ppropInputRequest->cbBuffer); 927 if (ppropInputRequest->cBuffers) 928 FIXME("Requested Buffer cBuffers mismatch %i,%i\n",This->allocProps.cBuffers, ppropInputRequest->cBuffers); 929 930 return IMemAllocator_SetProperties(pAlloc, &This->allocProps, &actual); 931 } 932 933 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = { 934 { 935 FileAsyncReaderPin_CheckMediaType, 936 FileAsyncReaderPin_AttemptConnection, 937 BasePinImpl_GetMediaTypeVersion, 938 FileAsyncReaderPin_GetMediaType 939 }, 940 FileAsyncReaderPin_DecideBufferSize, 941 BaseOutputPinImpl_DecideAllocator, 942 BaseOutputPinImpl_BreakConnect 943 }; 944 945 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin) 946 { 947 PIN_INFO piOutput; 948 HRESULT hr; 949 950 *ppPin = NULL; 951 piOutput.dir = PINDIR_OUTPUT; 952 piOutput.pFilter = pBaseFilter; 953 strcpyW(piOutput.achName, wszOutputPinName); 954 hr = BaseOutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, &output_BaseOutputFuncTable, pCritSec, ppPin); 955 956 if (SUCCEEDED(hr)) 957 { 958 FileAsyncReader *pPinImpl = (FileAsyncReader *)*ppPin; 959 pPinImpl->IAsyncReader_iface.lpVtbl = &FileAsyncReader_Vtbl; 960 pPinImpl->hFile = hFile; 961 pPinImpl->bFlushing = FALSE; 962 pPinImpl->sample_list = NULL; 963 pPinImpl->handle_list = NULL; 964 pPinImpl->queued_number = 0; 965 InitializeCriticalSection(&pPinImpl->csList); 966 pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList"); 967 } 968 return hr; 969 } 970 971 /* IAsyncReader */ 972 973 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv) 974 { 975 FileAsyncReader *This = impl_from_IAsyncReader(iface); 976 977 return IPin_QueryInterface(&This->pin.pin.IPin_iface, riid, ppv); 978 } 979 980 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface) 981 { 982 FileAsyncReader *This = impl_from_IAsyncReader(iface); 983 984 return IPin_AddRef(&This->pin.pin.IPin_iface); 985 } 986 987 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface) 988 { 989 FileAsyncReader *This = impl_from_IAsyncReader(iface); 990 991 return IPin_Release(&This->pin.pin.IPin_iface); 992 } 993 994 #define DEF_ALIGNMENT 1 995 996 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual) 997 { 998 FileAsyncReader *This = impl_from_IAsyncReader(iface); 999 1000 HRESULT hr = S_OK; 1001 1002 TRACE("%p->(%p, %p, %p)\n", This, pPreferred, pProps, ppActual); 1003 1004 if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0) 1005 pProps->cbAlign = DEF_ALIGNMENT; 1006 1007 if (pPreferred) 1008 { 1009 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps); 1010 /* FIXME: check we are still aligned */ 1011 if (SUCCEEDED(hr)) 1012 { 1013 IMemAllocator_AddRef(pPreferred); 1014 *ppActual = pPreferred; 1015 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr); 1016 goto done; 1017 } 1018 } 1019 1020 pPreferred = NULL; 1021 1022 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred); 1023 1024 if (SUCCEEDED(hr)) 1025 { 1026 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps); 1027 /* FIXME: check we are still aligned */ 1028 if (SUCCEEDED(hr)) 1029 { 1030 *ppActual = pPreferred; 1031 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr); 1032 } 1033 } 1034 1035 done: 1036 if (SUCCEEDED(hr)) 1037 { 1038 CoTaskMemFree(This->sample_list); 1039 if (This->handle_list) 1040 { 1041 int x; 1042 for (x = 0; x <= This->samples; ++x) 1043 CloseHandle(This->handle_list[x]); 1044 CoTaskMemFree(This->handle_list); 1045 } 1046 1047 This->samples = pProps->cBuffers; 1048 This->oldest_sample = 0; 1049 TRACE("Samples: %u\n", This->samples); 1050 This->sample_list = CoTaskMemAlloc(sizeof(This->sample_list[0]) * pProps->cBuffers); 1051 This->handle_list = CoTaskMemAlloc(sizeof(HANDLE) * pProps->cBuffers * 2); 1052 1053 if (This->sample_list && This->handle_list) 1054 { 1055 int x; 1056 ZeroMemory(This->sample_list, sizeof(This->sample_list[0]) * pProps->cBuffers); 1057 for (x = 0; x < This->samples; ++x) 1058 { 1059 This->sample_list[x].ovl.hEvent = This->handle_list[x] = CreateEventW(NULL, 0, 0, NULL); 1060 if (x + 1 < This->samples) 1061 This->handle_list[This->samples + 1 + x] = This->handle_list[x]; 1062 } 1063 This->handle_list[This->samples] = CreateEventW(NULL, 1, 0, NULL); 1064 This->allocProps = *pProps; 1065 } 1066 else 1067 { 1068 hr = E_OUTOFMEMORY; 1069 CoTaskMemFree(This->sample_list); 1070 CoTaskMemFree(This->handle_list); 1071 This->samples = 0; 1072 This->sample_list = NULL; 1073 This->handle_list = NULL; 1074 } 1075 } 1076 1077 if (FAILED(hr)) 1078 { 1079 *ppActual = NULL; 1080 if (pPreferred) 1081 IMemAllocator_Release(pPreferred); 1082 } 1083 1084 TRACE("-- %x\n", hr); 1085 return hr; 1086 } 1087 1088 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples. 1089 * however, this would be quite complicated to do and may be a bit error prone */ 1090 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser) 1091 { 1092 HRESULT hr = S_OK; 1093 REFERENCE_TIME Start; 1094 REFERENCE_TIME Stop; 1095 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1096 LPBYTE pBuffer = NULL; 1097 1098 TRACE("%p->(%p, %lx)\n", This, pSample, dwUser); 1099 1100 if (!pSample) 1101 return E_POINTER; 1102 1103 /* get start and stop positions in bytes */ 1104 if (SUCCEEDED(hr)) 1105 hr = IMediaSample_GetTime(pSample, &Start, &Stop); 1106 1107 if (SUCCEEDED(hr)) 1108 hr = IMediaSample_GetPointer(pSample, &pBuffer); 1109 1110 EnterCriticalSection(&This->csList); 1111 if (This->bFlushing) 1112 { 1113 LeaveCriticalSection(&This->csList); 1114 return VFW_E_WRONG_STATE; 1115 } 1116 1117 if (SUCCEEDED(hr)) 1118 { 1119 DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start); 1120 DATAREQUEST *pDataRq; 1121 int x; 1122 1123 /* Try to insert above the waiting sample if possible */ 1124 for (x = This->oldest_sample; x < This->samples; ++x) 1125 { 1126 if (!This->sample_list[x].pSample) 1127 break; 1128 } 1129 1130 if (x >= This->samples) 1131 for (x = 0; x < This->oldest_sample; ++x) 1132 { 1133 if (!This->sample_list[x].pSample) 1134 break; 1135 } 1136 1137 /* There must be a sample we have found */ 1138 assert(x < This->samples); 1139 ++This->queued_number; 1140 1141 pDataRq = This->sample_list + x; 1142 1143 pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start); 1144 pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8)); 1145 pDataRq->dwUserData = dwUser; 1146 1147 /* we violate traditional COM rules here by maintaining 1148 * a reference to the sample, but not calling AddRef, but 1149 * that's what MSDN says to do */ 1150 pDataRq->pSample = pSample; 1151 1152 /* this is definitely not how it is implemented on Win9x 1153 * as they do not support async reads on files, but it is 1154 * sooo much easier to use this than messing around with threads! 1155 */ 1156 if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl)) 1157 hr = HRESULT_FROM_WIN32(GetLastError()); 1158 1159 /* ERROR_IO_PENDING is not actually an error since this is what we want! */ 1160 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) 1161 hr = S_OK; 1162 } 1163 1164 LeaveCriticalSection(&This->csList); 1165 1166 TRACE("-- %x\n", hr); 1167 return hr; 1168 } 1169 1170 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser) 1171 { 1172 HRESULT hr = S_OK; 1173 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1174 DWORD buffer = ~0; 1175 1176 TRACE("%p->(%u, %p, %p)\n", This, dwTimeout, ppSample, pdwUser); 1177 1178 *ppSample = NULL; 1179 *pdwUser = 0; 1180 1181 EnterCriticalSection(&This->csList); 1182 if (!This->bFlushing) 1183 { 1184 LONG oldest = This->oldest_sample; 1185 1186 if (!This->queued_number) 1187 { 1188 /* It could be that nothing is queued right now, but that can be fixed */ 1189 WARN("Called without samples in queue and not flushing!!\n"); 1190 } 1191 LeaveCriticalSection(&This->csList); 1192 1193 /* wait for an object to read, or time out */ 1194 buffer = WaitForMultipleObjectsEx(This->samples+1, This->handle_list + oldest, FALSE, dwTimeout, TRUE); 1195 1196 EnterCriticalSection(&This->csList); 1197 if (buffer <= This->samples) 1198 { 1199 /* Re-scale the buffer back to normal */ 1200 buffer += oldest; 1201 1202 /* Uh oh, we overshot the flusher handle, renormalize it back to 0..Samples-1 */ 1203 if (buffer > This->samples) 1204 buffer -= This->samples + 1; 1205 assert(buffer <= This->samples); 1206 } 1207 1208 if (buffer >= This->samples) 1209 { 1210 if (buffer != This->samples) 1211 { 1212 FIXME("Returned: %u (%08x)\n", buffer, GetLastError()); 1213 hr = VFW_E_TIMEOUT; 1214 } 1215 else 1216 hr = VFW_E_WRONG_STATE; 1217 buffer = ~0; 1218 } 1219 else 1220 --This->queued_number; 1221 } 1222 1223 if (This->bFlushing && buffer == ~0) 1224 { 1225 for (buffer = 0; buffer < This->samples; ++buffer) 1226 { 1227 if (This->sample_list[buffer].pSample) 1228 { 1229 ResetEvent(This->handle_list[buffer]); 1230 break; 1231 } 1232 } 1233 if (buffer == This->samples) 1234 { 1235 assert(!This->queued_number); 1236 hr = VFW_E_TIMEOUT; 1237 } 1238 else 1239 { 1240 --This->queued_number; 1241 hr = S_OK; 1242 } 1243 } 1244 1245 if (SUCCEEDED(hr)) 1246 { 1247 REFERENCE_TIME rtStart, rtStop; 1248 REFERENCE_TIME rtSampleStart, rtSampleStop; 1249 DATAREQUEST *pDataRq = This->sample_list + buffer; 1250 DWORD dwBytes = 0; 1251 1252 /* get any errors */ 1253 if (!This->bFlushing && !GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE)) 1254 hr = HRESULT_FROM_WIN32(GetLastError()); 1255 1256 /* Return the sample no matter what so it can be destroyed */ 1257 *ppSample = pDataRq->pSample; 1258 *pdwUser = pDataRq->dwUserData; 1259 1260 if (This->bFlushing) 1261 hr = VFW_E_WRONG_STATE; 1262 1263 if (FAILED(hr)) 1264 dwBytes = 0; 1265 1266 /* Set the time on the sample */ 1267 IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes); 1268 1269 rtStart = (DWORD64)pDataRq->ovl.u.s.Offset + ((DWORD64)pDataRq->ovl.u.s.OffsetHigh << 32); 1270 rtStart = MEDIATIME_FROM_BYTES(rtStart); 1271 rtStop = rtStart + MEDIATIME_FROM_BYTES(dwBytes); 1272 1273 IMediaSample_GetTime(pDataRq->pSample, &rtSampleStart, &rtSampleStop); 1274 assert(rtStart == rtSampleStart); 1275 assert(rtStop <= rtSampleStop); 1276 1277 IMediaSample_SetTime(pDataRq->pSample, &rtStart, &rtStop); 1278 assert(rtStart == rtSampleStart); 1279 if (hr == S_OK) 1280 assert(rtStop == rtSampleStop); 1281 else 1282 assert(rtStop == rtStart); 1283 1284 This->sample_list[buffer].pSample = NULL; 1285 assert(This->oldest_sample < This->samples); 1286 1287 if (buffer == This->oldest_sample) 1288 { 1289 LONG x; 1290 for (x = This->oldest_sample + 1; x < This->samples; ++x) 1291 if (This->sample_list[x].pSample) 1292 break; 1293 if (x >= This->samples) 1294 for (x = 0; x < This->oldest_sample; ++x) 1295 if (This->sample_list[x].pSample) 1296 break; 1297 if (This->oldest_sample == x) 1298 /* No samples found, reset to 0 */ 1299 x = 0; 1300 This->oldest_sample = x; 1301 } 1302 } 1303 LeaveCriticalSection(&This->csList); 1304 1305 TRACE("-- %x\n", hr); 1306 return hr; 1307 } 1308 1309 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer); 1310 1311 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample) 1312 { 1313 BYTE * pBuffer; 1314 REFERENCE_TIME tStart; 1315 REFERENCE_TIME tStop; 1316 HRESULT hr; 1317 1318 TRACE("(%p)\n", pSample); 1319 1320 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 1321 1322 if (SUCCEEDED(hr)) 1323 hr = IMediaSample_GetPointer(pSample, &pBuffer); 1324 1325 if (SUCCEEDED(hr)) 1326 hr = FileAsyncReader_SyncRead(iface, 1327 BYTES_FROM_MEDIATIME(tStart), 1328 (LONG) BYTES_FROM_MEDIATIME(tStop - tStart), 1329 pBuffer); 1330 1331 TRACE("-- %x\n", hr); 1332 return hr; 1333 } 1334 1335 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer) 1336 { 1337 OVERLAPPED ovl; 1338 HRESULT hr = S_OK; 1339 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1340 1341 TRACE("%p->(%s, %d, %p)\n", This, wine_dbgstr_longlong(llPosition), lLength, pBuffer); 1342 1343 ZeroMemory(&ovl, sizeof(ovl)); 1344 1345 ovl.hEvent = CreateEventW(NULL, 0, 0, NULL); 1346 /* NOTE: llPosition is the actual byte position to start reading from */ 1347 ovl.u.s.Offset = (DWORD) llPosition; 1348 ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8)); 1349 1350 if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl)) 1351 hr = HRESULT_FROM_WIN32(GetLastError()); 1352 1353 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) 1354 hr = S_OK; 1355 1356 if (SUCCEEDED(hr)) 1357 { 1358 DWORD dwBytesRead; 1359 1360 if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE)) 1361 hr = HRESULT_FROM_WIN32(GetLastError()); 1362 } 1363 1364 CloseHandle(ovl.hEvent); 1365 1366 TRACE("-- %x\n", hr); 1367 return hr; 1368 } 1369 1370 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable) 1371 { 1372 DWORD dwSizeLow; 1373 DWORD dwSizeHigh; 1374 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1375 1376 TRACE("%p->(%p, %p)\n", This, pTotal, pAvailable); 1377 1378 if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) && 1379 (GetLastError() != NO_ERROR)) 1380 return HRESULT_FROM_WIN32(GetLastError()); 1381 1382 *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8); 1383 1384 *pAvailable = *pTotal; 1385 1386 return S_OK; 1387 } 1388 1389 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface) 1390 { 1391 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1392 1393 TRACE("%p->()\n", This); 1394 1395 EnterCriticalSection(&This->csList); 1396 This->bFlushing = TRUE; 1397 CancelIo(This->hFile); 1398 SetEvent(This->handle_list[This->samples]); 1399 LeaveCriticalSection(&This->csList); 1400 1401 return S_OK; 1402 } 1403 1404 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface) 1405 { 1406 FileAsyncReader *This = impl_from_IAsyncReader(iface); 1407 int x; 1408 1409 TRACE("%p->()\n", This); 1410 1411 EnterCriticalSection(&This->csList); 1412 ResetEvent(This->handle_list[This->samples]); 1413 This->bFlushing = FALSE; 1414 for (x = 0; x < This->samples; ++x) 1415 assert(!This->sample_list[x].pSample); 1416 1417 LeaveCriticalSection(&This->csList); 1418 1419 return S_OK; 1420 } 1421 1422 static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 1423 { 1424 FileAsyncReader_QueryInterface, 1425 FileAsyncReader_AddRef, 1426 FileAsyncReader_Release, 1427 FileAsyncReader_RequestAllocator, 1428 FileAsyncReader_Request, 1429 FileAsyncReader_WaitForNext, 1430 FileAsyncReader_SyncReadAligned, 1431 FileAsyncReader_SyncRead, 1432 FileAsyncReader_Length, 1433 FileAsyncReader_BeginFlush, 1434 FileAsyncReader_EndFlush, 1435 }; 1436 1437 1438 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) { 1439 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1440 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv); 1441 } 1442 1443 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) { 1444 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1445 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface); 1446 } 1447 1448 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) { 1449 AsyncReader *This = impl_from_IAMFilterMiscFlags(iface); 1450 return IBaseFilter_Release(&This->filter.IBaseFilter_iface); 1451 } 1452 1453 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) { 1454 return AM_FILTER_MISC_FLAGS_IS_SOURCE; 1455 } 1456 1457 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = { 1458 AMFilterMiscFlags_QueryInterface, 1459 AMFilterMiscFlags_AddRef, 1460 AMFilterMiscFlags_Release, 1461 AMFilterMiscFlags_GetMiscFlags 1462 }; 1463