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