strmbase: Implement BaseFilter in strmbase.
[wine/multimedia.git] / dlls / quartz / filesource.c
blob5562983c4832231475760203a269c6ed2ff5618e
1 /*
2 * File Source Filter
4 * Copyright 2003 Robert Shearman
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.
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.
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
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
24 #include "quartz_private.h"
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>
36 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
38 static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
40 typedef struct AsyncReader
42 BaseFilter filter;
43 const IFileSourceFilterVtbl * lpVtblFSF;
45 DWORD lastpinchange;
47 IPin * pOutputPin;
48 LPOLESTR pszFileName;
49 AM_MEDIA_TYPE * pmt;
50 } AsyncReader;
52 static const IBaseFilterVtbl AsyncReader_Vtbl;
53 static const IFileSourceFilterVtbl FileSource_Vtbl;
54 static const IAsyncReaderVtbl FileAsyncReader_Vtbl;
56 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
58 static inline AsyncReader *impl_from_IFileSourceFilter( IFileSourceFilter *iface )
60 return (AsyncReader *)((char*)iface - FIELD_OFFSET(AsyncReader, lpVtblFSF));
63 static WCHAR const mediatype_name[11] = {
64 'M', 'e', 'd', 'i', 'a', ' ', 'T', 'y', 'p', 'e', 0 };
65 static WCHAR const subtype_name[8] = {
66 'S', 'u', 'b', 't', 'y', 'p', 'e', 0 };
68 static HRESULT process_extensions(HKEY hkeyExtensions, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
70 WCHAR *extension;
71 LONG l;
72 HKEY hsub;
73 WCHAR keying[39];
74 DWORD size;
76 if (!pszFileName)
77 return E_POINTER;
79 /* Get the part of the name that matters */
80 extension = PathFindExtensionW(pszFileName);
81 if (*extension != '.')
82 return E_FAIL;
84 l = RegOpenKeyExW(hkeyExtensions, extension, 0, KEY_READ, &hsub);
85 if (l)
86 return E_FAIL;
88 size = sizeof(keying);
89 l = RegQueryValueExW(hsub, mediatype_name, NULL, NULL, (LPBYTE)keying, &size);
90 if (!l)
91 CLSIDFromString(keying, majorType);
93 size = sizeof(keying);
94 if (!l)
95 l = RegQueryValueExW(hsub, subtype_name, NULL, NULL, (LPBYTE)keying, &size);
97 if (!l)
98 CLSIDFromString(keying, minorType);
100 RegCloseKey(hsub);
102 if (!l)
103 return S_OK;
104 return E_FAIL;
107 static unsigned char byte_from_hex_char(WCHAR wHex)
109 switch (tolowerW(wHex))
111 case '0':
112 case '1':
113 case '2':
114 case '3':
115 case '4':
116 case '5':
117 case '6':
118 case '7':
119 case '8':
120 case '9':
121 return (wHex - '0') & 0xf;
122 case 'a':
123 case 'b':
124 case 'c':
125 case 'd':
126 case 'e':
127 case 'f':
128 return (wHex - 'a' + 10) & 0xf;
129 default:
130 return 0;
134 static HRESULT process_pattern_string(LPCWSTR wszPatternString, IAsyncReader * pReader)
136 ULONG ulOffset;
137 ULONG ulBytes;
138 BYTE * pbMask;
139 BYTE * pbValue;
140 BYTE * pbFile;
141 HRESULT hr = S_OK;
142 ULONG strpos;
144 TRACE("\t\tPattern string: %s\n", debugstr_w(wszPatternString));
146 /* format: "offset, bytestocompare, mask, value" */
148 ulOffset = strtolW(wszPatternString, NULL, 10);
150 if (!(wszPatternString = strchrW(wszPatternString, ',')))
151 return E_INVALIDARG;
153 wszPatternString++; /* skip ',' */
155 ulBytes = strtolW(wszPatternString, NULL, 10);
157 pbMask = HeapAlloc(GetProcessHeap(), 0, ulBytes);
158 pbValue = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBytes);
159 pbFile = HeapAlloc(GetProcessHeap(), 0, ulBytes);
161 /* default mask is match everything */
162 memset(pbMask, 0xFF, ulBytes);
164 if (!(wszPatternString = strchrW(wszPatternString, ',')))
165 hr = E_INVALIDARG;
167 if (hr == S_OK)
169 wszPatternString++; /* skip ',' */
170 while (!isxdigitW(*wszPatternString) && (*wszPatternString != ',')) wszPatternString++;
172 for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
174 if ((strpos % 2) == 1) /* odd numbered position */
175 pbMask[strpos / 2] |= byte_from_hex_char(*wszPatternString);
176 else
177 pbMask[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
180 if (!(wszPatternString = strchrW(wszPatternString, ',')))
181 hr = E_INVALIDARG;
183 wszPatternString++; /* skip ',' */
186 if (hr == S_OK)
188 for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++)
191 for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
193 if ((strpos % 2) == 1) /* odd numbered position */
194 pbValue[strpos / 2] |= byte_from_hex_char(*wszPatternString);
195 else
196 pbValue[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
200 if (hr == S_OK)
201 hr = IAsyncReader_SyncRead(pReader, ulOffset, ulBytes, pbFile);
203 if (hr == S_OK)
205 ULONG i;
206 for (i = 0; i < ulBytes; i++)
207 if ((pbFile[i] & pbMask[i]) != pbValue[i])
209 hr = S_FALSE;
210 break;
214 HeapFree(GetProcessHeap(), 0, pbMask);
215 HeapFree(GetProcessHeap(), 0, pbValue);
216 HeapFree(GetProcessHeap(), 0, pbFile);
218 /* if we encountered no errors with this string, and there is a following tuple, then we
219 * have to match that as well to succeed */
220 if ((hr == S_OK) && (wszPatternString = strchrW(wszPatternString, ',')))
221 return process_pattern_string(wszPatternString + 1, pReader);
222 else
223 return hr;
226 static HRESULT GetClassMediaFile(IAsyncReader * pReader, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
228 HKEY hkeyMediaType = NULL;
229 LONG lRet;
230 HRESULT hr = S_OK;
231 BOOL bFound = FALSE;
232 static const WCHAR wszMediaType[] = {'M','e','d','i','a',' ','T','y','p','e',0};
234 TRACE("(%p, %s, %p, %p)\n", pReader, debugstr_w(pszFileName), majorType, minorType);
236 *majorType = GUID_NULL;
237 *minorType = GUID_NULL;
239 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMediaType, 0, KEY_READ, &hkeyMediaType);
240 hr = HRESULT_FROM_WIN32(lRet);
242 if (SUCCEEDED(hr))
244 DWORD indexMajor;
246 for (indexMajor = 0; !bFound; indexMajor++)
248 HKEY hkeyMajor;
249 WCHAR wszMajorKeyName[CHARS_IN_GUID];
250 DWORD dwKeyNameLength = sizeof(wszMajorKeyName) / sizeof(wszMajorKeyName[0]);
251 static const WCHAR wszExtensions[] = {'E','x','t','e','n','s','i','o','n','s',0};
253 if (RegEnumKeyExW(hkeyMediaType, indexMajor, wszMajorKeyName, &dwKeyNameLength, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
254 break;
255 if (RegOpenKeyExW(hkeyMediaType, wszMajorKeyName, 0, KEY_READ, &hkeyMajor) != ERROR_SUCCESS)
256 break;
257 TRACE("%s\n", debugstr_w(wszMajorKeyName));
258 if (!strcmpW(wszExtensions, wszMajorKeyName))
260 if (process_extensions(hkeyMajor, pszFileName, majorType, minorType) == S_OK)
261 bFound = TRUE;
263 else
265 DWORD indexMinor;
267 for (indexMinor = 0; !bFound; indexMinor++)
269 HKEY hkeyMinor;
270 WCHAR wszMinorKeyName[CHARS_IN_GUID];
271 DWORD dwMinorKeyNameLen = sizeof(wszMinorKeyName) / sizeof(wszMinorKeyName[0]);
272 DWORD maxValueLen;
273 DWORD indexValue;
275 if (RegEnumKeyExW(hkeyMajor, indexMinor, wszMinorKeyName, &dwMinorKeyNameLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
276 break;
278 if (RegOpenKeyExW(hkeyMajor, wszMinorKeyName, 0, KEY_READ, &hkeyMinor) != ERROR_SUCCESS)
279 break;
281 TRACE("\t%s\n", debugstr_w(wszMinorKeyName));
283 if (RegQueryInfoKeyW(hkeyMinor, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &maxValueLen, NULL, NULL) != ERROR_SUCCESS)
284 break;
286 for (indexValue = 0; !bFound; indexValue++)
288 DWORD dwType;
289 WCHAR wszValueName[14]; /* longest name we should encounter will be "Source Filter" */
290 LPWSTR wszPatternString = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
291 DWORD dwValueNameLen = sizeof(wszValueName) / sizeof(wszValueName[0]); /* remember this is in chars */
292 DWORD dwDataLen = maxValueLen; /* remember this is in bytes */
293 static const WCHAR wszSourceFilter[] = {'S','o','u','r','c','e',' ','F','i','l','t','e','r',0};
294 LONG temp;
296 if ((temp = RegEnumValueW(hkeyMinor, indexValue, wszValueName, &dwValueNameLen, NULL, &dwType, (LPBYTE)wszPatternString, &dwDataLen)) != ERROR_SUCCESS)
298 HeapFree(GetProcessHeap(), 0, wszPatternString);
299 break;
302 /* if it is not the source filter value */
303 if (strcmpW(wszValueName, wszSourceFilter))
305 if (process_pattern_string(wszPatternString, pReader) == S_OK)
307 if (SUCCEEDED(CLSIDFromString(wszMajorKeyName, majorType)) &&
308 SUCCEEDED(CLSIDFromString(wszMinorKeyName, minorType)))
309 bFound = TRUE;
312 HeapFree(GetProcessHeap(), 0, wszPatternString);
314 CloseHandle(hkeyMinor);
317 CloseHandle(hkeyMajor);
320 CloseHandle(hkeyMediaType);
322 if (SUCCEEDED(hr) && !bFound)
324 ERR("Media class not found\n");
325 hr = E_FAIL;
327 else if (bFound)
328 TRACE("Found file's class: major = %s, subtype = %s\n", qzdebugstr_guid(majorType), qzdebugstr_guid(minorType));
330 return hr;
333 HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv)
335 AsyncReader *pAsyncRead;
337 if( pUnkOuter )
338 return CLASS_E_NOAGGREGATION;
340 pAsyncRead = CoTaskMemAlloc(sizeof(AsyncReader));
342 if (!pAsyncRead)
343 return E_OUTOFMEMORY;
345 BaseFilter_Init(&pAsyncRead->filter, &AsyncReader_Vtbl, &CLSID_AsyncReader, (DWORD_PTR)(__FILE__ ": AsyncReader.csFilter"));
347 pAsyncRead->lpVtblFSF = &FileSource_Vtbl;
348 pAsyncRead->pOutputPin = NULL;
349 pAsyncRead->lastpinchange = GetTickCount();
351 pAsyncRead->pszFileName = NULL;
352 pAsyncRead->pmt = NULL;
354 *ppv = pAsyncRead;
356 TRACE("-- created at %p\n", pAsyncRead);
358 return S_OK;
361 /** IUnknown methods **/
363 static HRESULT WINAPI AsyncReader_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
365 AsyncReader *This = (AsyncReader *)iface;
367 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
369 *ppv = NULL;
371 if (IsEqualIID(riid, &IID_IUnknown))
372 *ppv = This;
373 else if (IsEqualIID(riid, &IID_IPersist))
374 *ppv = This;
375 else if (IsEqualIID(riid, &IID_IMediaFilter))
376 *ppv = This;
377 else if (IsEqualIID(riid, &IID_IBaseFilter))
378 *ppv = This;
379 else if (IsEqualIID(riid, &IID_IFileSourceFilter))
380 *ppv = &This->lpVtblFSF;
382 if (*ppv)
384 IUnknown_AddRef((IUnknown *)(*ppv));
385 return S_OK;
388 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking) &&
389 !IsEqualIID(riid, &IID_IVideoWindow) && !IsEqualIID(riid, &IID_IBasicAudio))
390 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
392 return E_NOINTERFACE;
395 static ULONG WINAPI AsyncReader_Release(IBaseFilter * iface)
397 AsyncReader *This = (AsyncReader *)iface;
398 ULONG refCount = BaseFilterImpl_Release(iface);
400 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
402 if (!refCount)
404 if (This->pOutputPin)
406 IPin *pConnectedTo;
407 if(SUCCEEDED(IPin_ConnectedTo(This->pOutputPin, &pConnectedTo)))
409 IPin_Disconnect(pConnectedTo);
410 IPin_Release(pConnectedTo);
412 IPin_Disconnect(This->pOutputPin);
413 IPin_Release(This->pOutputPin);
415 CoTaskMemFree(This->pszFileName);
416 if (This->pmt)
417 FreeMediaType(This->pmt);
418 CoTaskMemFree(This);
419 return 0;
421 else
422 return refCount;
425 /** IMediaFilter methods **/
427 static HRESULT WINAPI AsyncReader_Stop(IBaseFilter * iface)
429 AsyncReader *This = (AsyncReader *)iface;
431 TRACE("()\n");
433 This->filter.state = State_Stopped;
435 return S_OK;
438 static HRESULT WINAPI AsyncReader_Pause(IBaseFilter * iface)
440 AsyncReader *This = (AsyncReader *)iface;
442 TRACE("()\n");
444 This->filter.state = State_Paused;
446 return S_OK;
449 static HRESULT WINAPI AsyncReader_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
451 AsyncReader *This = (AsyncReader *)iface;
453 TRACE("(%x%08x)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
455 This->filter.state = State_Running;
457 return S_OK;
460 /** IBaseFilter methods **/
462 static IPin* WINAPI AsyncReader_GetPin(IBaseFilter *iface, int pos)
464 AsyncReader *This = (AsyncReader *)iface;
466 if (pos >= 1 || !This->pOutputPin)
467 return NULL;
469 IPin_AddRef(This->pOutputPin);
470 return This->pOutputPin;
473 static LONG WINAPI AsyncReader_GetPinCount(IBaseFilter *iface)
475 AsyncReader *This = (AsyncReader *)iface;
477 if (!This->pOutputPin)
478 return 0;
479 else
480 return 1;
483 static LONG WINAPI AsyncReader_GetPinVersion(IBaseFilter *iface)
485 AsyncReader *This = (AsyncReader *)iface;
487 /* Our pins are almost static, not changing so setting static tick count is ok */
488 return This->lastpinchange;
491 static HRESULT WINAPI AsyncReader_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
493 AsyncReader *This = (AsyncReader *)iface;
495 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
497 return EnumPins_Construct(iface, AsyncReader_GetPin, AsyncReader_GetPinCount, AsyncReader_GetPinVersion, ppEnum);
500 static HRESULT WINAPI AsyncReader_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
502 FIXME("(%s, %p)\n", debugstr_w(Id), ppPin);
504 return E_NOTIMPL;
507 static const IBaseFilterVtbl AsyncReader_Vtbl =
509 AsyncReader_QueryInterface,
510 BaseFilterImpl_AddRef,
511 AsyncReader_Release,
512 BaseFilterImpl_GetClassID,
513 AsyncReader_Stop,
514 AsyncReader_Pause,
515 AsyncReader_Run,
516 BaseFilterImpl_GetState,
517 BaseFilterImpl_SetSyncSource,
518 BaseFilterImpl_GetSyncSource,
519 AsyncReader_EnumPins,
520 AsyncReader_FindPin,
521 BaseFilterImpl_QueryFilterInfo,
522 BaseFilterImpl_JoinFilterGraph,
523 BaseFilterImpl_QueryVendorInfo
526 static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv)
528 AsyncReader *This = impl_from_IFileSourceFilter(iface);
530 return IBaseFilter_QueryInterface((IFileSourceFilter*)&This->filter.lpVtbl, riid, ppv);
533 static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface)
535 AsyncReader *This = impl_from_IFileSourceFilter(iface);
537 return IBaseFilter_AddRef((IFileSourceFilter*)&This->filter.lpVtbl);
540 static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface)
542 AsyncReader *This = impl_from_IFileSourceFilter(iface);
544 return IBaseFilter_Release((IFileSourceFilter*)&This->filter.lpVtbl);
547 static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
549 HRESULT hr;
550 HANDLE hFile;
551 IAsyncReader * pReader = NULL;
552 AsyncReader *This = impl_from_IFileSourceFilter(iface);
554 TRACE("(%s, %p)\n", debugstr_w(pszFileName), pmt);
556 /* open file */
557 /* FIXME: check the sharing values that native uses */
558 hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
560 if (hFile == INVALID_HANDLE_VALUE)
562 return HRESULT_FROM_WIN32(GetLastError());
565 /* create pin */
566 hr = FileAsyncReader_Construct(hFile, (IBaseFilter *)&This->filter.lpVtbl, &This->filter.csFilter, &This->pOutputPin);
567 This->lastpinchange = GetTickCount();
569 if (SUCCEEDED(hr))
570 hr = IPin_QueryInterface(This->pOutputPin, &IID_IAsyncReader, (LPVOID *)&pReader);
572 /* store file name & media type */
573 if (SUCCEEDED(hr))
575 CoTaskMemFree(This->pszFileName);
576 if (This->pmt)
577 FreeMediaType(This->pmt);
579 This->pszFileName = CoTaskMemAlloc((strlenW(pszFileName) + 1) * sizeof(WCHAR));
580 strcpyW(This->pszFileName, pszFileName);
582 This->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
583 if (!pmt)
585 This->pmt->bFixedSizeSamples = TRUE;
586 This->pmt->bTemporalCompression = FALSE;
587 This->pmt->cbFormat = 0;
588 This->pmt->pbFormat = NULL;
589 This->pmt->pUnk = NULL;
590 This->pmt->lSampleSize = 0;
591 This->pmt->formattype = FORMAT_None;
592 hr = GetClassMediaFile(pReader, pszFileName, &This->pmt->majortype, &This->pmt->subtype);
593 if (FAILED(hr))
595 CoTaskMemFree(This->pmt);
596 This->pmt = NULL;
599 else
600 CopyMediaType(This->pmt, pmt);
603 if (pReader)
604 IAsyncReader_Release(pReader);
606 if (FAILED(hr))
608 if (This->pOutputPin)
610 IPin_Release(This->pOutputPin);
611 This->pOutputPin = NULL;
614 CoTaskMemFree(This->pszFileName);
615 if (This->pmt)
616 FreeMediaType(This->pmt);
617 This->pszFileName = NULL;
618 This->pmt = NULL;
620 CloseHandle(hFile);
623 /* FIXME: check return codes */
624 return hr;
627 static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter * iface, LPOLESTR * ppszFileName, AM_MEDIA_TYPE * pmt)
629 AsyncReader *This = impl_from_IFileSourceFilter(iface);
631 TRACE("(%p, %p)\n", ppszFileName, pmt);
633 if (!ppszFileName)
634 return E_POINTER;
636 /* copy file name & media type if available, otherwise clear the outputs */
637 if (This->pszFileName)
639 *ppszFileName = CoTaskMemAlloc((strlenW(This->pszFileName) + 1) * sizeof(WCHAR));
640 strcpyW(*ppszFileName, This->pszFileName);
642 else
643 *ppszFileName = NULL;
645 if (pmt)
647 if (This->pmt)
648 CopyMediaType(pmt, This->pmt);
649 else
650 ZeroMemory(pmt, sizeof(*pmt));
653 return S_OK;
656 static const IFileSourceFilterVtbl FileSource_Vtbl =
658 FileSource_QueryInterface,
659 FileSource_AddRef,
660 FileSource_Release,
661 FileSource_Load,
662 FileSource_GetCurFile
666 /* the dwUserData passed back to user */
667 typedef struct DATAREQUEST
669 IMediaSample * pSample; /* sample passed to us by user */
670 DWORD_PTR dwUserData; /* user data passed to us */
671 OVERLAPPED ovl; /* our overlapped structure */
672 } DATAREQUEST;
674 typedef struct FileAsyncReader
676 BaseOutputPin pin;
677 const struct IAsyncReaderVtbl * lpVtblAR;
679 HANDLE hFile;
680 BOOL bFlushing;
681 /* Why would you need more? Every sample has its own handle */
682 LONG queued_number;
683 LONG samples;
684 LONG oldest_sample;
685 CRITICAL_SECTION csList; /* critical section to prevent concurrency issues */
686 DATAREQUEST *sample_list;
688 /* Have a handle for every sample, and then one more as flushing handle */
689 HANDLE *handle_list;
690 } FileAsyncReader;
692 static inline FileAsyncReader *impl_from_IAsyncReader( IAsyncReader *iface )
694 return (FileAsyncReader *)((char*)iface - FIELD_OFFSET(FileAsyncReader, lpVtblAR));
697 static HRESULT WINAPI FileAsyncReaderPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt)
699 FileAsyncReader *This = (FileAsyncReader *)iface;
700 AM_MEDIA_TYPE *pmt_filter = ((AsyncReader *)This->pin.pin.pinInfo.pFilter)->pmt;
702 FIXME("(%p, %p)\n", iface, pmt);
704 if (IsEqualGUID(&pmt->majortype, &pmt_filter->majortype) &&
705 IsEqualGUID(&pmt->subtype, &pmt_filter->subtype) &&
706 IsEqualGUID(&pmt->formattype, &FORMAT_None))
707 return S_OK;
709 return S_FALSE;
712 static HRESULT WINAPI FileAsyncReaderPin_GetMediaType(IPin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
714 FileAsyncReader *This = (FileAsyncReader *)iface;
715 if (iPosition < 0)
716 return E_INVALIDARG;
717 if (iPosition > 0)
718 return VFW_S_NO_MORE_ITEMS;
719 CopyMediaType(pmt, ((AsyncReader *)This->pin.pin.pinInfo.pFilter)->pmt);
720 return S_OK;
723 /* overridden pin functions */
725 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
727 FileAsyncReader *This = (FileAsyncReader *)iface;
728 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
730 *ppv = NULL;
732 if (IsEqualIID(riid, &IID_IUnknown))
733 *ppv = This;
734 else if (IsEqualIID(riid, &IID_IPin))
735 *ppv = This;
736 else if (IsEqualIID(riid, &IID_IAsyncReader))
737 *ppv = &This->lpVtblAR;
739 if (*ppv)
741 IUnknown_AddRef((IUnknown *)(*ppv));
742 return S_OK;
745 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking))
746 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
748 return E_NOINTERFACE;
751 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface)
753 FileAsyncReader *This = (FileAsyncReader *)iface;
754 ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
755 int x;
757 TRACE("(%p)->() Release from %d\n", This, refCount + 1);
759 if (!refCount)
761 CoTaskMemFree(This->sample_list);
762 if (This->handle_list)
764 for (x = 0; x <= This->samples; ++x)
765 CloseHandle(This->handle_list[x]);
766 CoTaskMemFree(This->handle_list);
768 CloseHandle(This->hFile);
769 This->csList.DebugInfo->Spare[0] = 0;
770 DeleteCriticalSection(&This->csList);
771 CoTaskMemFree(This);
772 return 0;
774 return refCount;
777 static HRESULT WINAPI FileAsyncReaderPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
779 TRACE("(%p)\n", ppEnum);
781 return EnumMediaTypes_Construct(iface, FileAsyncReaderPin_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
784 static const IPinVtbl FileAsyncReaderPin_Vtbl =
786 FileAsyncReaderPin_QueryInterface,
787 BasePinImpl_AddRef,
788 FileAsyncReaderPin_Release,
789 BaseOutputPinImpl_Connect,
790 BaseOutputPinImpl_ReceiveConnection,
791 BasePinImpl_Disconnect,
792 BasePinImpl_ConnectedTo,
793 BasePinImpl_ConnectionMediaType,
794 BasePinImpl_QueryPinInfo,
795 BasePinImpl_QueryDirection,
796 BasePinImpl_QueryId,
797 FileAsyncReaderPin_QueryAccept,
798 FileAsyncReaderPin_EnumMediaTypes,
799 BasePinImpl_QueryInternalConnections,
800 BaseOutputPinImpl_EndOfStream,
801 BaseOutputPinImpl_BeginFlush,
802 BaseOutputPinImpl_EndFlush,
803 BaseOutputPinImpl_NewSegment
806 /* Function called as a helper to IPin_Connect */
807 /* specific AM_MEDIA_TYPE - it cannot be NULL */
808 /* this differs from standard OutputPin_AttemptConnection only in that it
809 * doesn't need the IMemInputPin interface on the receiving pin */
810 static HRESULT WINAPI FileAsyncReaderPin_AttemptConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
812 BaseOutputPin *This = (BaseOutputPin *)iface;
813 HRESULT hr;
815 TRACE("(%p, %p)\n", pReceivePin, pmt);
816 dump_AM_MEDIA_TYPE(pmt);
818 /* FIXME: call queryacceptproc */
820 This->pin.pConnectedTo = pReceivePin;
821 IPin_AddRef(pReceivePin);
822 CopyMediaType(&This->pin.mtCurrent, pmt);
824 hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
826 if (FAILED(hr))
828 IPin_Release(This->pin.pConnectedTo);
829 This->pin.pConnectedTo = NULL;
830 FreeMediaType(&This->pin.mtCurrent);
833 TRACE(" -- %x\n", hr);
834 return hr;
837 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
839 PIN_INFO piOutput;
840 HRESULT hr;
842 *ppPin = NULL;
843 piOutput.dir = PINDIR_OUTPUT;
844 piOutput.pFilter = pBaseFilter;
845 strcpyW(piOutput.achName, wszOutputPinName);
846 hr = BaseOutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, NULL, FileAsyncReaderPin_AttemptConnection, pCritSec, ppPin);
848 if (SUCCEEDED(hr))
850 FileAsyncReader *pPinImpl = (FileAsyncReader *)*ppPin;
851 pPinImpl->lpVtblAR = &FileAsyncReader_Vtbl;
852 pPinImpl->hFile = hFile;
853 pPinImpl->bFlushing = FALSE;
854 pPinImpl->sample_list = NULL;
855 pPinImpl->handle_list = NULL;
856 pPinImpl->queued_number = 0;
857 InitializeCriticalSection(&pPinImpl->csList);
858 pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList");
860 return hr;
863 /* IAsyncReader */
865 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv)
867 FileAsyncReader *This = impl_from_IAsyncReader(iface);
869 return IPin_QueryInterface((IPin *)This, riid, ppv);
872 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
874 FileAsyncReader *This = impl_from_IAsyncReader(iface);
876 return IPin_AddRef((IPin *)This);
879 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
881 FileAsyncReader *This = impl_from_IAsyncReader(iface);
883 return IPin_Release((IPin *)This);
886 #define DEF_ALIGNMENT 1
888 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual)
890 FileAsyncReader *This = impl_from_IAsyncReader(iface);
892 HRESULT hr = S_OK;
894 TRACE("(%p, %p, %p)\n", pPreferred, pProps, ppActual);
896 if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0)
897 pProps->cbAlign = DEF_ALIGNMENT;
899 if (pPreferred)
901 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
902 /* FIXME: check we are still aligned */
903 if (SUCCEEDED(hr))
905 IMemAllocator_AddRef(pPreferred);
906 *ppActual = pPreferred;
907 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
908 goto done;
912 pPreferred = NULL;
914 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred);
916 if (SUCCEEDED(hr))
918 hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
919 /* FIXME: check we are still aligned */
920 if (SUCCEEDED(hr))
922 *ppActual = pPreferred;
923 TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
927 done:
928 if (SUCCEEDED(hr))
930 CoTaskMemFree(This->sample_list);
931 if (This->handle_list)
933 int x;
934 for (x = 0; x <= This->samples; ++x)
935 CloseHandle(This->handle_list[x]);
936 CoTaskMemFree(This->handle_list);
939 This->samples = pProps->cBuffers;
940 This->oldest_sample = 0;
941 TRACE("Samples: %u\n", This->samples);
942 This->sample_list = CoTaskMemAlloc(sizeof(This->sample_list[0]) * pProps->cBuffers);
943 This->handle_list = CoTaskMemAlloc(sizeof(HANDLE) * pProps->cBuffers * 2);
945 if (This->sample_list && This->handle_list)
947 int x;
948 ZeroMemory(This->sample_list, sizeof(This->sample_list[0]) * pProps->cBuffers);
949 for (x = 0; x < This->samples; ++x)
951 This->sample_list[x].ovl.hEvent = This->handle_list[x] = CreateEventW(NULL, 0, 0, NULL);
952 if (x + 1 < This->samples)
953 This->handle_list[This->samples + 1 + x] = This->handle_list[x];
955 This->handle_list[This->samples] = CreateEventW(NULL, 1, 0, NULL);
956 This->pin.allocProps = *pProps;
958 else
960 hr = E_OUTOFMEMORY;
961 CoTaskMemFree(This->sample_list);
962 CoTaskMemFree(This->handle_list);
963 This->samples = 0;
964 This->sample_list = NULL;
965 This->handle_list = NULL;
969 if (FAILED(hr))
971 *ppActual = NULL;
972 if (pPreferred)
973 IMemAllocator_Release(pPreferred);
976 TRACE("-- %x\n", hr);
977 return hr;
980 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples.
981 * however, this would be quite complicated to do and may be a bit error prone */
982 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser)
984 HRESULT hr = S_OK;
985 REFERENCE_TIME Start;
986 REFERENCE_TIME Stop;
987 FileAsyncReader *This = impl_from_IAsyncReader(iface);
988 LPBYTE pBuffer = NULL;
990 TRACE("(%p, %lx)\n", pSample, dwUser);
992 if (!pSample)
993 return E_POINTER;
995 /* get start and stop positions in bytes */
996 if (SUCCEEDED(hr))
997 hr = IMediaSample_GetTime(pSample, &Start, &Stop);
999 if (SUCCEEDED(hr))
1000 hr = IMediaSample_GetPointer(pSample, &pBuffer);
1002 EnterCriticalSection(&This->csList);
1003 if (This->bFlushing)
1005 LeaveCriticalSection(&This->csList);
1006 return VFW_E_WRONG_STATE;
1009 if (SUCCEEDED(hr))
1011 DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start);
1012 DATAREQUEST *pDataRq;
1013 int x;
1015 /* Try to insert above the waiting sample if possible */
1016 for (x = This->oldest_sample; x < This->samples; ++x)
1018 if (!This->sample_list[x].pSample)
1019 break;
1022 if (x >= This->samples)
1023 for (x = 0; x < This->oldest_sample; ++x)
1025 if (!This->sample_list[x].pSample)
1026 break;
1029 /* There must be a sample we have found */
1030 assert(x < This->samples);
1031 ++This->queued_number;
1033 pDataRq = This->sample_list + x;
1035 pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start);
1036 pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8));
1037 pDataRq->dwUserData = dwUser;
1039 /* we violate traditional COM rules here by maintaining
1040 * a reference to the sample, but not calling AddRef, but
1041 * that's what MSDN says to do */
1042 pDataRq->pSample = pSample;
1044 /* this is definitely not how it is implemented on Win9x
1045 * as they do not support async reads on files, but it is
1046 * sooo much easier to use this than messing around with threads!
1048 if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl))
1049 hr = HRESULT_FROM_WIN32(GetLastError());
1051 /* ERROR_IO_PENDING is not actually an error since this is what we want! */
1052 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1053 hr = S_OK;
1056 LeaveCriticalSection(&This->csList);
1058 TRACE("-- %x\n", hr);
1059 return hr;
1062 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser)
1064 HRESULT hr = S_OK;
1065 FileAsyncReader *This = impl_from_IAsyncReader(iface);
1066 DWORD buffer = ~0;
1068 TRACE("(%u, %p, %p)\n", dwTimeout, ppSample, pdwUser);
1070 *ppSample = NULL;
1071 *pdwUser = 0;
1073 EnterCriticalSection(&This->csList);
1074 if (!This->bFlushing)
1076 LONG oldest = This->oldest_sample;
1078 if (!This->queued_number)
1080 /* It could be that nothing is queued right now, but that can be fixed */
1081 WARN("Called without samples in queue and not flushing!!\n");
1083 LeaveCriticalSection(&This->csList);
1085 /* wait for an object to read, or time out */
1086 buffer = WaitForMultipleObjectsEx(This->samples+1, This->handle_list + oldest, FALSE, dwTimeout, TRUE);
1088 EnterCriticalSection(&This->csList);
1089 if (buffer <= This->samples)
1091 /* Re-scale the buffer back to normal */
1092 buffer += oldest;
1094 /* Uh oh, we overshot the flusher handle, renormalize it back to 0..Samples-1 */
1095 if (buffer > This->samples)
1096 buffer -= This->samples + 1;
1097 assert(buffer <= This->samples);
1100 if (buffer >= This->samples)
1102 if (buffer != This->samples)
1104 FIXME("Returned: %u (%08x)\n", buffer, GetLastError());
1105 hr = VFW_E_TIMEOUT;
1107 else
1108 hr = VFW_E_WRONG_STATE;
1109 buffer = ~0;
1111 else
1112 --This->queued_number;
1115 if (This->bFlushing && buffer == ~0)
1117 for (buffer = 0; buffer < This->samples; ++buffer)
1119 if (This->sample_list[buffer].pSample)
1121 ResetEvent(This->handle_list[buffer]);
1122 break;
1125 if (buffer == This->samples)
1127 assert(!This->queued_number);
1128 hr = VFW_E_TIMEOUT;
1130 else
1132 --This->queued_number;
1133 hr = S_OK;
1137 if (SUCCEEDED(hr))
1139 REFERENCE_TIME rtStart, rtStop;
1140 REFERENCE_TIME rtSampleStart, rtSampleStop;
1141 DATAREQUEST *pDataRq = This->sample_list + buffer;
1142 DWORD dwBytes = 0;
1144 /* get any errors */
1145 if (!This->bFlushing && !GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE))
1146 hr = HRESULT_FROM_WIN32(GetLastError());
1148 /* Return the sample no matter what so it can be destroyed */
1149 *ppSample = pDataRq->pSample;
1150 *pdwUser = pDataRq->dwUserData;
1152 if (This->bFlushing)
1153 hr = VFW_E_WRONG_STATE;
1155 if (FAILED(hr))
1156 dwBytes = 0;
1158 /* Set the time on the sample */
1159 IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes);
1161 rtStart = (DWORD64)pDataRq->ovl.u.s.Offset + ((DWORD64)pDataRq->ovl.u.s.OffsetHigh << 32);
1162 rtStart = MEDIATIME_FROM_BYTES(rtStart);
1163 rtStop = rtStart + MEDIATIME_FROM_BYTES(dwBytes);
1165 IMediaSample_GetTime(pDataRq->pSample, &rtSampleStart, &rtSampleStop);
1166 assert(rtStart == rtSampleStart);
1167 assert(rtStop <= rtSampleStop);
1169 IMediaSample_SetTime(pDataRq->pSample, &rtStart, &rtStop);
1170 assert(rtStart == rtSampleStart);
1171 if (hr == S_OK)
1172 assert(rtStop == rtSampleStop);
1173 else
1174 assert(rtStop == rtStart);
1176 This->sample_list[buffer].pSample = NULL;
1177 assert(This->oldest_sample < This->samples);
1179 if (buffer == This->oldest_sample)
1181 LONG x;
1182 for (x = This->oldest_sample + 1; x < This->samples; ++x)
1183 if (This->sample_list[x].pSample)
1184 break;
1185 if (x >= This->samples)
1186 for (x = 0; x < This->oldest_sample; ++x)
1187 if (This->sample_list[x].pSample)
1188 break;
1189 if (This->oldest_sample == x)
1190 /* No samples found, reset to 0 */
1191 x = 0;
1192 This->oldest_sample = x;
1195 LeaveCriticalSection(&This->csList);
1197 TRACE("-- %x\n", hr);
1198 return hr;
1201 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer);
1203 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample)
1205 BYTE * pBuffer;
1206 REFERENCE_TIME tStart;
1207 REFERENCE_TIME tStop;
1208 HRESULT hr;
1210 TRACE("(%p)\n", pSample);
1212 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
1214 if (SUCCEEDED(hr))
1215 hr = IMediaSample_GetPointer(pSample, &pBuffer);
1217 if (SUCCEEDED(hr))
1218 hr = FileAsyncReader_SyncRead(iface,
1219 BYTES_FROM_MEDIATIME(tStart),
1220 (LONG) BYTES_FROM_MEDIATIME(tStop - tStart),
1221 pBuffer);
1223 TRACE("-- %x\n", hr);
1224 return hr;
1227 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer)
1229 OVERLAPPED ovl;
1230 HRESULT hr = S_OK;
1231 FileAsyncReader *This = impl_from_IAsyncReader(iface);
1233 TRACE("(%x%08x, %d, %p)\n", (ULONG)(llPosition >> 32), (ULONG)llPosition, lLength, pBuffer);
1235 ZeroMemory(&ovl, sizeof(ovl));
1237 ovl.hEvent = CreateEventW(NULL, 0, 0, NULL);
1238 /* NOTE: llPosition is the actual byte position to start reading from */
1239 ovl.u.s.Offset = (DWORD) llPosition;
1240 ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8));
1242 if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl))
1243 hr = HRESULT_FROM_WIN32(GetLastError());
1245 if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1246 hr = S_OK;
1248 if (SUCCEEDED(hr))
1250 DWORD dwBytesRead;
1252 if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE))
1253 hr = HRESULT_FROM_WIN32(GetLastError());
1256 CloseHandle(ovl.hEvent);
1258 TRACE("-- %x\n", hr);
1259 return hr;
1262 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable)
1264 DWORD dwSizeLow;
1265 DWORD dwSizeHigh;
1266 FileAsyncReader *This = impl_from_IAsyncReader(iface);
1268 TRACE("(%p, %p)\n", pTotal, pAvailable);
1270 if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) &&
1271 (GetLastError() != NO_ERROR))
1272 return HRESULT_FROM_WIN32(GetLastError());
1274 *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8);
1276 *pAvailable = *pTotal;
1278 return S_OK;
1281 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
1283 FileAsyncReader *This = impl_from_IAsyncReader(iface);
1285 TRACE("()\n");
1287 EnterCriticalSection(&This->csList);
1288 This->bFlushing = TRUE;
1289 CancelIo(This->hFile);
1290 SetEvent(This->handle_list[This->samples]);
1291 LeaveCriticalSection(&This->csList);
1293 return S_OK;
1296 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
1298 FileAsyncReader *This = impl_from_IAsyncReader(iface);
1299 int x;
1301 TRACE("()\n");
1303 EnterCriticalSection(&This->csList);
1304 ResetEvent(This->handle_list[This->samples]);
1305 This->bFlushing = FALSE;
1306 for (x = 0; x < This->samples; ++x)
1307 assert(!This->sample_list[x].pSample);
1309 LeaveCriticalSection(&This->csList);
1311 return S_OK;
1314 static const IAsyncReaderVtbl FileAsyncReader_Vtbl =
1316 FileAsyncReader_QueryInterface,
1317 FileAsyncReader_AddRef,
1318 FileAsyncReader_Release,
1319 FileAsyncReader_RequestAllocator,
1320 FileAsyncReader_Request,
1321 FileAsyncReader_WaitForNext,
1322 FileAsyncReader_SyncReadAligned,
1323 FileAsyncReader_SyncRead,
1324 FileAsyncReader_Length,
1325 FileAsyncReader_BeginFlush,
1326 FileAsyncReader_EndFlush,