quartz: Implement IBasicVideo2 for the filtergraph.
[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     wszPatternString++; /* skip ',' */
172
173     if (hr == S_OK)
174     {
175         for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++)
176             ;
177
178         for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
179         {
180             if ((strpos % 2) == 1) /* odd numbered position */
181                 pbMask[strpos / 2] |= byte_from_hex_char(*wszPatternString);
182             else
183                 pbMask[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
184         }
185
186         if (!(wszPatternString = strchrW(wszPatternString, ',')))
187             hr = E_INVALIDARG;
188     
189         wszPatternString++; /* skip ',' */
190     }
191
192     if (hr == S_OK)
193     {
194         for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++)
195             ;
196
197         for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
198         {
199             if ((strpos % 2) == 1) /* odd numbered position */
200                 pbValue[strpos / 2] |= byte_from_hex_char(*wszPatternString);
201             else
202                 pbValue[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
203         }
204     }
205
206     if (hr == S_OK)
207         hr = IAsyncReader_SyncRead(pReader, ulOffset, ulBytes, pbFile);
208
209     if (hr == S_OK)
210     {
211         ULONG i;
212         for (i = 0; i < ulBytes; i++)
213             if ((pbFile[i] & pbMask[i]) != pbValue[i])
214             {
215                 hr = S_FALSE;
216                 break;
217             }
218     }
219
220     HeapFree(GetProcessHeap(), 0, pbMask);
221     HeapFree(GetProcessHeap(), 0, pbValue);
222     HeapFree(GetProcessHeap(), 0, pbFile);
223
224     /* if we encountered no errors with this string, and there is a following tuple, then we
225      * have to match that as well to succeed */
226     if ((hr == S_OK) && (wszPatternString = strchrW(wszPatternString, ',')))
227         return process_pattern_string(wszPatternString + 1, pReader);
228     else
229         return hr;
230 }
231
232 static HRESULT GetClassMediaFile(IAsyncReader * pReader, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
233 {
234     HKEY hkeyMediaType = NULL;
235     LONG lRet;
236     HRESULT hr = S_OK;
237     BOOL bFound = FALSE;
238     static const WCHAR wszMediaType[] = {'M','e','d','i','a',' ','T','y','p','e',0};
239
240     TRACE("(%p, %s, %p, %p)\n", pReader, debugstr_w(pszFileName), majorType, minorType);
241
242     *majorType = GUID_NULL;
243     *minorType = GUID_NULL;
244
245     lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMediaType, 0, KEY_READ, &hkeyMediaType);
246     hr = HRESULT_FROM_WIN32(lRet);
247
248     if (SUCCEEDED(hr))
249     {
250         DWORD indexMajor;
251
252         for (indexMajor = 0; !bFound; indexMajor++)
253         {
254             HKEY hkeyMajor;
255             WCHAR wszMajorKeyName[CHARS_IN_GUID];
256             DWORD dwKeyNameLength = sizeof(wszMajorKeyName) / sizeof(wszMajorKeyName[0]);
257             static const WCHAR wszExtensions[] = {'E','x','t','e','n','s','i','o','n','s',0};
258
259             if (RegEnumKeyExW(hkeyMediaType, indexMajor, wszMajorKeyName, &dwKeyNameLength, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
260                 break;
261             if (RegOpenKeyExW(hkeyMediaType, wszMajorKeyName, 0, KEY_READ, &hkeyMajor) != ERROR_SUCCESS)
262                 break;
263             TRACE("%s\n", debugstr_w(wszMajorKeyName));
264             if (!strcmpW(wszExtensions, wszMajorKeyName))
265             {
266                 if (process_extensions(hkeyMajor, pszFileName, majorType, minorType) == S_OK)
267                     bFound = TRUE;
268             }
269             else
270             {
271                 DWORD indexMinor;
272
273                 for (indexMinor = 0; !bFound; indexMinor++)
274                 {
275                     HKEY hkeyMinor;
276                     WCHAR wszMinorKeyName[CHARS_IN_GUID];
277                     DWORD dwMinorKeyNameLen = sizeof(wszMinorKeyName) / sizeof(wszMinorKeyName[0]);
278                     DWORD maxValueLen;
279                     DWORD indexValue;
280
281                     if (RegEnumKeyExW(hkeyMajor, indexMinor, wszMinorKeyName, &dwMinorKeyNameLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
282                         break;
283
284                     if (RegOpenKeyExW(hkeyMajor, wszMinorKeyName, 0, KEY_READ, &hkeyMinor) != ERROR_SUCCESS)
285                         break;
286
287                     TRACE("\t%s\n", debugstr_w(wszMinorKeyName));
288         
289                     if (RegQueryInfoKeyW(hkeyMinor, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &maxValueLen, NULL, NULL) != ERROR_SUCCESS)
290                         break;
291
292                     for (indexValue = 0; !bFound; indexValue++)
293                     {
294                         DWORD dwType;
295                         WCHAR wszValueName[14]; /* longest name we should encounter will be "Source Filter" */
296                         LPWSTR wszPatternString = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
297                         DWORD dwValueNameLen = sizeof(wszValueName) / sizeof(wszValueName[0]); /* remember this is in chars */
298                         DWORD dwDataLen = maxValueLen; /* remember this is in bytes */
299                         static const WCHAR wszSourceFilter[] = {'S','o','u','r','c','e',' ','F','i','l','t','e','r',0};
300                         LONG temp;
301
302                         if ((temp = RegEnumValueW(hkeyMinor, indexValue, wszValueName, &dwValueNameLen, NULL, &dwType, (LPBYTE)wszPatternString, &dwDataLen)) != ERROR_SUCCESS)
303                         {
304                             HeapFree(GetProcessHeap(), 0, wszPatternString);
305                             break;
306                         }
307
308                         /* if it is not the source filter value */
309                         if (strcmpW(wszValueName, wszSourceFilter))
310                         {
311                             if (process_pattern_string(wszPatternString, pReader) == S_OK)
312                             {
313                                 if (SUCCEEDED(CLSIDFromString(wszMajorKeyName, majorType)) &&
314                                     SUCCEEDED(CLSIDFromString(wszMinorKeyName, minorType)))
315                                     bFound = TRUE;
316                             }
317                         }
318                         HeapFree(GetProcessHeap(), 0, wszPatternString);
319                     }
320                     CloseHandle(hkeyMinor);
321                 }
322             }
323             CloseHandle(hkeyMajor);
324         }
325     }
326     CloseHandle(hkeyMediaType);
327
328     if (SUCCEEDED(hr) && !bFound)
329     {
330         ERR("Media class not found\n");
331         hr = E_FAIL;
332     }
333     else if (bFound)
334         TRACE("Found file's class: major = %s, subtype = %s\n", qzdebugstr_guid(majorType), qzdebugstr_guid(minorType));
335
336     return hr;
337 }
338
339 HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv)
340 {
341     AsyncReader *pAsyncRead;
342     
343     if( pUnkOuter )
344         return CLASS_E_NOAGGREGATION;
345     
346     pAsyncRead = CoTaskMemAlloc(sizeof(AsyncReader));
347
348     if (!pAsyncRead)
349         return E_OUTOFMEMORY;
350
351     pAsyncRead->lpVtbl = &AsyncReader_Vtbl;
352     pAsyncRead->lpVtblFSF = &FileSource_Vtbl;
353     pAsyncRead->refCount = 1;
354     pAsyncRead->filterInfo.achName[0] = '\0';
355     pAsyncRead->filterInfo.pGraph = NULL;
356     pAsyncRead->pOutputPin = NULL;
357     pAsyncRead->lastpinchange = GetTickCount();
358
359     InitializeCriticalSection(&pAsyncRead->csFilter);
360     pAsyncRead->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AsyncReader.csFilter");
361
362     pAsyncRead->pszFileName = NULL;
363     pAsyncRead->pmt = NULL;
364
365     *ppv = (LPVOID)pAsyncRead;
366
367     TRACE("-- created at %p\n", pAsyncRead);
368
369     return S_OK;
370 }
371
372 /** IUnknown methods **/
373
374 static HRESULT WINAPI AsyncReader_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
375 {
376     AsyncReader *This = (AsyncReader *)iface;
377
378     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
379
380     *ppv = NULL;
381
382     if (IsEqualIID(riid, &IID_IUnknown))
383         *ppv = (LPVOID)This;
384     else if (IsEqualIID(riid, &IID_IPersist))
385         *ppv = (LPVOID)This;
386     else if (IsEqualIID(riid, &IID_IMediaFilter))
387         *ppv = (LPVOID)This;
388     else if (IsEqualIID(riid, &IID_IBaseFilter))
389         *ppv = (LPVOID)This;
390     else if (IsEqualIID(riid, &IID_IFileSourceFilter))
391         *ppv = (LPVOID)(&This->lpVtblFSF);
392
393     if (*ppv)
394     {
395         IUnknown_AddRef((IUnknown *)(*ppv));
396         return S_OK;
397     }
398
399     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking) && !IsEqualIID(riid, &IID_IVideoWindow))
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 = (IPin *)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     CRITICAL_SECTION csList; /* critical section to prevent concurrency issues */
771     DATAREQUEST *sample_list;
772
773     /* Have a handle for every sample, and then one more as flushing handle */
774     HANDLE *handle_list;
775 } FileAsyncReader;
776
777 static inline FileAsyncReader *impl_from_IAsyncReader( IAsyncReader *iface )
778 {
779     return (FileAsyncReader *)((char*)iface - FIELD_OFFSET(FileAsyncReader, lpVtblAR));
780 }
781
782 static HRESULT AcceptProcAFR(LPVOID iface, const AM_MEDIA_TYPE *pmt)
783 {
784     AsyncReader *This = (AsyncReader *)iface;
785     
786     FIXME("(%p, %p)\n", iface, pmt);
787
788     if (IsEqualGUID(&pmt->majortype, &This->pmt->majortype) &&
789         IsEqualGUID(&pmt->subtype, &This->pmt->subtype) &&
790         IsEqualGUID(&pmt->formattype, &FORMAT_None))
791         return S_OK;
792     
793     return S_FALSE;
794 }
795
796 /* overridden pin functions */
797
798 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
799 {
800     FileAsyncReader *This = (FileAsyncReader *)iface;
801     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
802
803     *ppv = NULL;
804
805     if (IsEqualIID(riid, &IID_IUnknown))
806         *ppv = (LPVOID)This;
807     else if (IsEqualIID(riid, &IID_IPin))
808         *ppv = (LPVOID)This;
809     else if (IsEqualIID(riid, &IID_IAsyncReader))
810         *ppv = (LPVOID)&This->lpVtblAR;
811
812     if (*ppv)
813     {
814         IUnknown_AddRef((IUnknown *)(*ppv));
815         return S_OK;
816     }
817
818     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking))
819         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
820
821     return E_NOINTERFACE;
822 }
823
824 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface)
825 {
826     FileAsyncReader *This = (FileAsyncReader *)iface;
827     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
828     int x;
829
830     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
831
832     if (!refCount)
833     {
834         CoTaskMemFree(This->sample_list);
835         if (This->handle_list)
836         {
837             for (x = 0; x <= This->samples; ++x)
838                 CloseHandle(This->handle_list[x]);
839             CoTaskMemFree(This->handle_list);
840         }
841         CloseHandle(This->hFile);
842         This->csList.DebugInfo->Spare[0] = 0;
843         DeleteCriticalSection(&This->csList);
844         CoTaskMemFree(This);
845         return 0;
846     }
847     return refCount;
848 }
849
850 static HRESULT WINAPI FileAsyncReaderPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
851 {
852     ENUMMEDIADETAILS emd;
853     FileAsyncReader *This = (FileAsyncReader *)iface;
854
855     TRACE("(%p)\n", ppEnum);
856
857     emd.cMediaTypes = 1;
858     emd.pMediaTypes = ((AsyncReader *)This->pin.pin.pinInfo.pFilter)->pmt;
859
860     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
861 }
862
863 static const IPinVtbl FileAsyncReaderPin_Vtbl = 
864 {
865     FileAsyncReaderPin_QueryInterface,
866     IPinImpl_AddRef,
867     FileAsyncReaderPin_Release,
868     OutputPin_Connect,
869     OutputPin_ReceiveConnection,
870     IPinImpl_Disconnect,
871     IPinImpl_ConnectedTo,
872     IPinImpl_ConnectionMediaType,
873     IPinImpl_QueryPinInfo,
874     IPinImpl_QueryDirection,
875     IPinImpl_QueryId,
876     IPinImpl_QueryAccept,
877     FileAsyncReaderPin_EnumMediaTypes,
878     IPinImpl_QueryInternalConnections,
879     OutputPin_EndOfStream,
880     OutputPin_BeginFlush,
881     OutputPin_EndFlush,
882     OutputPin_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_ConnectSpecific only in that it
888  * doesn't need the IMemInputPin interface on the receiving pin */
889 static HRESULT FileAsyncReaderPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
890 {
891     OutputPin *This = (OutputPin *)iface;
892     HRESULT hr;
893
894     TRACE("(%p, %p)\n", 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, 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 FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
917 {
918     PIN_INFO piOutput;
919     HRESULT hr;
920
921     *ppPin = NULL;
922     piOutput.dir = PINDIR_OUTPUT;
923     piOutput.pFilter = pBaseFilter;
924     strcpyW(piOutput.achName, wszOutputPinName);
925     hr = OutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, NULL, pBaseFilter, AcceptProcAFR, pCritSec, ppPin);
926
927     if (SUCCEEDED(hr))
928     {
929         FileAsyncReader *pPinImpl =  (FileAsyncReader *)*ppPin;
930         pPinImpl->lpVtblAR = &FileAsyncReader_Vtbl;
931         pPinImpl->hFile = hFile;
932         pPinImpl->bFlushing = FALSE;
933         pPinImpl->sample_list = NULL;
934         pPinImpl->handle_list = NULL;
935         pPinImpl->queued_number = 0;
936         pPinImpl->pin.pConnectSpecific = FileAsyncReaderPin_ConnectSpecific;
937         InitializeCriticalSection(&pPinImpl->csList);
938         pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList");
939     }
940     return hr;
941 }
942
943 /* IAsyncReader */
944
945 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv)
946 {
947     FileAsyncReader *This = impl_from_IAsyncReader(iface);
948
949     return IPin_QueryInterface((IPin *)This, riid, ppv);
950 }
951
952 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
953 {
954     FileAsyncReader *This = impl_from_IAsyncReader(iface);
955
956     return IPin_AddRef((IPin *)This);
957 }
958
959 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
960 {
961     FileAsyncReader *This = impl_from_IAsyncReader(iface);
962
963     return IPin_Release((IPin *)This);
964 }
965
966 #define DEF_ALIGNMENT 1
967
968 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual)
969 {
970     FileAsyncReader *This = impl_from_IAsyncReader(iface);
971
972     HRESULT hr = S_OK;
973
974     TRACE("(%p, %p, %p)\n", pPreferred, pProps, ppActual);
975
976     if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0)
977         pProps->cbAlign = DEF_ALIGNMENT;
978
979     if (pPreferred)
980     {
981         hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
982         /* FIXME: check we are still aligned */
983         if (SUCCEEDED(hr))
984         {
985             IMemAllocator_AddRef(pPreferred);
986             *ppActual = pPreferred;
987             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
988             goto done;
989         }
990     }
991
992     pPreferred = NULL;
993
994     hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred);
995
996     if (SUCCEEDED(hr))
997     {
998         hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
999         /* FIXME: check we are still aligned */
1000         if (SUCCEEDED(hr))
1001         {
1002             *ppActual = pPreferred;
1003             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
1004         }
1005     }
1006
1007 done:
1008     if (SUCCEEDED(hr))
1009     {
1010         CoTaskMemFree(This->sample_list);
1011         if (This->handle_list)
1012         {
1013             int x;
1014             for (x = 0; x <= This->samples; ++x)
1015                 CloseHandle(This->handle_list[x]);
1016             CoTaskMemFree(This->handle_list);
1017         }
1018
1019         This->samples = pProps->cBuffers;
1020         TRACE("Samples: %u\n", This->samples);
1021         This->sample_list = CoTaskMemAlloc(sizeof(This->sample_list[0]) * pProps->cBuffers);
1022         This->handle_list = CoTaskMemAlloc(sizeof(HANDLE) * (pProps->cBuffers + 1));
1023
1024         if (This->sample_list && This->handle_list)
1025         {
1026             int x;
1027             ZeroMemory(This->sample_list, sizeof(This->sample_list[0]) * pProps->cBuffers);
1028             for (x = 0; x < This->samples; ++x)
1029             {
1030                 This->sample_list[x].ovl.hEvent = This->handle_list[x] = CreateEventW(NULL, 0, 0, NULL);
1031             }
1032             This->handle_list[This->samples] = CreateEventW(NULL, 1, 0, NULL);
1033             This->pin.allocProps = *pProps;
1034         }
1035         else
1036         {
1037             hr = E_OUTOFMEMORY;
1038             CoTaskMemFree(This->sample_list);
1039             CoTaskMemFree(This->handle_list);
1040             This->samples = 0;
1041             This->sample_list = NULL;
1042             This->handle_list = NULL;
1043         }
1044     }
1045
1046     if (FAILED(hr))
1047     {
1048         *ppActual = NULL;
1049         if (pPreferred)
1050             IMemAllocator_Release(pPreferred);
1051     }
1052
1053     TRACE("-- %x\n", hr);
1054     return hr;
1055 }
1056
1057 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples.
1058  * however, this would be quite complicated to do and may be a bit error prone */
1059 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser)
1060 {
1061     HRESULT hr = S_OK;
1062     REFERENCE_TIME Start;
1063     REFERENCE_TIME Stop;
1064     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1065     LPBYTE pBuffer = NULL;
1066     DWORD wait;
1067
1068     TRACE("(%p, %lx)\n", pSample, dwUser);
1069
1070     if (!pSample)
1071         return E_POINTER;
1072
1073     /* get start and stop positions in bytes */
1074     if (SUCCEEDED(hr))
1075         hr = IMediaSample_GetTime(pSample, &Start, &Stop);
1076
1077     if (SUCCEEDED(hr))
1078         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1079
1080     EnterCriticalSection(&This->csList);
1081     if (This->bFlushing)
1082     {
1083         LeaveCriticalSection(&This->csList);
1084         return VFW_E_WRONG_STATE;
1085     }
1086
1087     wait = WaitForMultipleObjectsEx(This->samples, This->handle_list, FALSE, 0, FALSE);
1088     if (wait < This->samples - 1)
1089         SetEvent(This->handle_list[wait]);
1090     else
1091         wait = This->samples;
1092
1093     if (SUCCEEDED(hr))
1094     {
1095         DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start);
1096         DATAREQUEST *pDataRq;
1097         int x;
1098
1099         /* Try to insert above the waiting sample if possible */
1100         for (x = wait + 1; x < This->samples; ++x)
1101         {
1102             if (!This->sample_list[x].pSample)
1103                 break;
1104         }
1105
1106         if (x >= This->samples)
1107             for (x = 0; x < This->samples; ++x)
1108             {
1109                 if (!This->sample_list[x].pSample)
1110                     break;
1111             }
1112
1113         assert(x < This->samples);
1114         InterlockedIncrement(&This->queued_number);
1115
1116         pDataRq = This->sample_list + x;
1117
1118         pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start);
1119         pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8));
1120         pDataRq->dwUserData = dwUser;
1121
1122         /* we violate traditional COM rules here by maintaining
1123          * a reference to the sample, but not calling AddRef, but
1124          * that's what MSDN says to do */
1125         pDataRq->pSample = pSample;
1126
1127         /* this is definitely not how it is implemented on Win9x
1128          * as they do not support async reads on files, but it is
1129          * sooo much easier to use this than messing around with threads!
1130          */
1131         if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl))
1132             hr = HRESULT_FROM_WIN32(GetLastError());
1133
1134         /* ERROR_IO_PENDING is not actually an error since this is what we want! */
1135         if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1136             hr = S_OK;
1137     }
1138
1139     LeaveCriticalSection(&This->csList);
1140
1141     TRACE("-- %x\n", hr);
1142     return hr;
1143 }
1144
1145 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser)
1146 {
1147     HRESULT hr = S_OK;
1148     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1149     DWORD buffer = ~0;
1150
1151     TRACE("(%u, %p, %p)\n", dwTimeout, ppSample, pdwUser);
1152
1153     *ppSample = NULL;
1154     *pdwUser = 0;
1155
1156     if (!This->bFlushing)
1157     {
1158         if (!This->queued_number)
1159         {
1160             /* It could be that nothing is queued right now, but that can be fixed */
1161             WARN("Called without samples in queue and not flushing!!\n");
1162         }
1163
1164         /* wait for an object to read, or time out */
1165         buffer = WaitForMultipleObjectsEx(This->samples+1, This->handle_list, FALSE, dwTimeout, TRUE);
1166
1167         if (buffer >= This->samples)
1168         {
1169             if (buffer != This->samples)
1170                 FIXME("Returned: %u (%08x)\n", buffer, GetLastError());
1171             hr = VFW_E_TIMEOUT;
1172             buffer = ~0;
1173         }
1174         else if (buffer < This->samples)
1175             InterlockedDecrement(&This->queued_number);
1176     }
1177
1178     EnterCriticalSection(&This->csList);
1179     if (This->bFlushing && buffer == ~0)
1180     {
1181         for (buffer = 0; buffer < This->samples; ++buffer)
1182         {
1183             if (This->sample_list[buffer].pSample)
1184             {
1185                 ResetEvent(This->handle_list[buffer]);
1186                 break;
1187             }
1188         }
1189         if (buffer == This->samples)
1190         {
1191             assert(!This->queued_number);
1192             hr = VFW_E_TIMEOUT;
1193         }
1194         else
1195         {
1196             InterlockedDecrement(&This->queued_number);
1197             hr = S_OK;
1198         }
1199     }
1200
1201     if (SUCCEEDED(hr))
1202     {
1203         REFERENCE_TIME rtStart, rtStop;
1204         REFERENCE_TIME rtSampleStart, rtSampleStop;
1205         DATAREQUEST *pDataRq = This->sample_list + buffer;
1206         DWORD dwBytes = 0;
1207
1208         /* get any errors */
1209         if (!This->bFlushing && !GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE))
1210             hr = HRESULT_FROM_WIN32(GetLastError());
1211
1212         /* Return the sample no matter what so it can be destroyed */
1213         *ppSample = pDataRq->pSample;
1214         *pdwUser = pDataRq->dwUserData;
1215
1216         if (This->bFlushing)
1217             hr = VFW_E_WRONG_STATE;
1218
1219         if (FAILED(hr))
1220             dwBytes = 0;
1221
1222         /* Set the time on the sample */
1223         IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes);
1224
1225         rtStart = (DWORD64)pDataRq->ovl.u.s.Offset + ((DWORD64)pDataRq->ovl.u.s.OffsetHigh << 32);
1226         rtStart = MEDIATIME_FROM_BYTES(rtStart);
1227         rtStop = rtStart + MEDIATIME_FROM_BYTES(dwBytes);
1228
1229         IMediaSample_GetTime(pDataRq->pSample, &rtSampleStart, &rtSampleStop);
1230         assert(rtStart == rtSampleStart);
1231         assert(rtStop <= rtSampleStop);
1232
1233         IMediaSample_SetTime(pDataRq->pSample, &rtStart, &rtStop);
1234         assert(rtStart == rtSampleStart);
1235         if (hr == S_OK)
1236             assert(rtStop == rtSampleStop);
1237         else
1238             assert(rtStop == rtStart);
1239
1240         This->sample_list[buffer].pSample = NULL;
1241     }
1242     LeaveCriticalSection(&This->csList);
1243
1244     TRACE("-- %x\n", hr);
1245     return hr;
1246 }
1247
1248 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer);
1249
1250 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample)
1251 {
1252     BYTE * pBuffer;
1253     REFERENCE_TIME tStart;
1254     REFERENCE_TIME tStop;
1255     HRESULT hr;
1256
1257     TRACE("(%p)\n", pSample);
1258
1259     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
1260
1261     if (SUCCEEDED(hr))
1262         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1263
1264     if (SUCCEEDED(hr))
1265         hr = FileAsyncReader_SyncRead(iface, 
1266             BYTES_FROM_MEDIATIME(tStart),
1267             (LONG) BYTES_FROM_MEDIATIME(tStop - tStart),
1268             pBuffer);
1269
1270     TRACE("-- %x\n", hr);
1271     return hr;
1272 }
1273
1274 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer)
1275 {
1276     OVERLAPPED ovl;
1277     HRESULT hr = S_OK;
1278     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1279
1280     TRACE("(%x%08x, %d, %p)\n", (ULONG)(llPosition >> 32), (ULONG)llPosition, lLength, pBuffer);
1281
1282     ZeroMemory(&ovl, sizeof(ovl));
1283
1284     ovl.hEvent = CreateEventW(NULL, 0, 0, NULL);
1285     /* NOTE: llPosition is the actual byte position to start reading from */
1286     ovl.u.s.Offset = (DWORD) llPosition;
1287     ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8));
1288
1289     if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl))
1290         hr = HRESULT_FROM_WIN32(GetLastError());
1291
1292     if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1293         hr = S_OK;
1294
1295     if (SUCCEEDED(hr))
1296     {
1297         DWORD dwBytesRead;
1298
1299         if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE))
1300             hr = HRESULT_FROM_WIN32(GetLastError());
1301     }
1302
1303     CloseHandle(ovl.hEvent);
1304
1305     TRACE("-- %x\n", hr);
1306     return hr;
1307 }
1308
1309 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable)
1310 {
1311     DWORD dwSizeLow;
1312     DWORD dwSizeHigh;
1313     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1314
1315     TRACE("(%p, %p)\n", pTotal, pAvailable);
1316
1317     if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) &&
1318         (GetLastError() != NO_ERROR))
1319         return HRESULT_FROM_WIN32(GetLastError());
1320
1321     *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8);
1322
1323     *pAvailable = *pTotal;
1324
1325     return S_OK;
1326 }
1327
1328 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
1329 {
1330     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1331
1332     TRACE("()\n");
1333
1334     EnterCriticalSection(&This->csList);
1335     This->bFlushing = TRUE;
1336     CancelIo(This->hFile);
1337     SetEvent(This->handle_list[This->samples]);
1338     LeaveCriticalSection(&This->csList);
1339
1340     return S_OK;
1341 }
1342
1343 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
1344 {
1345     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1346     int x;
1347
1348     TRACE("()\n");
1349
1350     EnterCriticalSection(&This->csList);
1351     ResetEvent(This->handle_list[This->samples]);
1352     This->bFlushing = FALSE;
1353     for (x = 0; x < This->samples; ++x)
1354         assert(!This->sample_list[x].pSample);
1355
1356     LeaveCriticalSection(&This->csList);
1357
1358     return S_OK;
1359 }
1360
1361 static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 
1362 {
1363     FileAsyncReader_QueryInterface,
1364     FileAsyncReader_AddRef,
1365     FileAsyncReader_Release,
1366     FileAsyncReader_RequestAllocator,
1367     FileAsyncReader_Request,
1368     FileAsyncReader_WaitForNext,
1369     FileAsyncReader_SyncReadAligned,
1370     FileAsyncReader_SyncRead,
1371     FileAsyncReader_Length,
1372     FileAsyncReader_BeginFlush,
1373     FileAsyncReader_EndFlush,
1374 };