rtworkq: Add RtwqJoinWorkQueue()/RtwqUnjoinWorkQueue() stubs.
[wine.git] / dlls / quartz / filesource.c
blob72e52dd01c7ada17255589bca4ed26cbc0608e3f
1 /*
2 * File Source Filter
4 * Copyright 2003 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
24 #include "quartz_private.h"
26 #include "wine/debug.h"
27 #include "uuids.h"
28 #include "vfwmsgs.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "shlwapi.h"
32 #include <assert.h>
34 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
36 static const AM_MEDIA_TYPE default_mt =
38 {0xe436eb83,0x524f,0x11ce,{0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70}}, /* MEDIATYPE_Stream */
39 {0,0,0,{0,0,0,0,0,0,0,0}},
40 TRUE,
41 FALSE,
43 {0,0,0,{0,0,0,0,0,0,0,0}},
44 NULL,
46 NULL
49 struct request
51 IMediaSample *sample;
52 DWORD_PTR cookie;
53 OVERLAPPED ovl;
56 typedef struct AsyncReader
58 struct strmbase_filter filter;
59 IFileSourceFilter IFileSourceFilter_iface;
61 struct strmbase_source source;
62 IAsyncReader IAsyncReader_iface;
64 LPOLESTR pszFileName;
65 AM_MEDIA_TYPE mt;
66 ALLOCATOR_PROPERTIES allocProps;
67 HANDLE file, port, io_thread;
68 CRITICAL_SECTION sample_cs;
69 BOOL flushing;
70 struct request *requests;
71 unsigned int max_requests;
72 CONDITION_VARIABLE sample_cv;
73 } AsyncReader;
75 static const struct strmbase_source_ops source_ops;
77 static inline AsyncReader *impl_from_strmbase_filter(struct strmbase_filter *iface)
79 return CONTAINING_RECORD(iface, AsyncReader, filter);
82 static inline AsyncReader *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
84 return CONTAINING_RECORD(iface, AsyncReader, IFileSourceFilter_iface);
87 static const IFileSourceFilterVtbl FileSource_Vtbl;
88 static const IAsyncReaderVtbl FileAsyncReader_Vtbl;
90 static int byte_from_hex_char(WCHAR c)
92 if ('0' <= c && c <= '9') return c - '0';
93 if ('a' <= c && c <= 'f') return c - 'a' + 10;
94 if ('A' <= c && c <= 'F') return c - 'A' + 10;
95 return -1;
98 static BOOL process_pattern_string(const WCHAR *pattern, HANDLE file)
100 ULONG size, offset, i, ret_size;
101 BYTE *mask, *expect, *actual;
102 int d;
103 BOOL ret = TRUE;
105 /* format: "offset, size, mask, value" */
107 offset = wcstol(pattern, NULL, 10);
109 if (!(pattern = wcschr(pattern, ',')))
110 return FALSE;
111 pattern++;
113 size = wcstol(pattern, NULL, 10);
114 mask = heap_alloc(size);
115 expect = heap_alloc(size);
116 memset(mask, 0xff, size);
118 if (!(pattern = wcschr(pattern, ',')))
120 heap_free(mask);
121 heap_free(expect);
122 return FALSE;
124 pattern++;
125 while (byte_from_hex_char(*pattern) == -1 && (*pattern != ','))
126 pattern++;
128 for (i = 0; (d = byte_from_hex_char(*pattern)) != -1 && (i/2 < size); pattern++, i++)
130 if (i % 2)
131 mask[i / 2] |= d;
132 else
133 mask[i / 2] = d << 4;
136 if (!(pattern = wcschr(pattern, ',')))
138 heap_free(mask);
139 heap_free(expect);
140 return FALSE;
142 pattern++;
143 while (byte_from_hex_char(*pattern) == -1 && (*pattern != ','))
144 pattern++;
146 for (i = 0; (d = byte_from_hex_char(*pattern)) != -1 && (i/2 < size); pattern++, i++)
148 if (i % 2)
149 expect[i / 2] |= d;
150 else
151 expect[i / 2] = d << 4;
154 actual = heap_alloc(size);
155 SetFilePointer(file, offset, NULL, FILE_BEGIN);
156 if (!ReadFile(file, actual, size, &ret_size, NULL) || ret_size != size)
158 heap_free(actual);
159 heap_free(expect);
160 heap_free(mask);
161 return FALSE;
164 for (i = 0; i < size; ++i)
166 if ((actual[i] & mask[i]) != expect[i])
168 ret = FALSE;
169 break;
173 heap_free(actual);
174 heap_free(expect);
175 heap_free(mask);
177 /* If there is a following tuple, then we must match that as well. */
178 if (ret && (pattern = wcschr(pattern, ',')))
179 return process_pattern_string(pattern + 1, file);
181 return ret;
184 BOOL get_media_type(const WCHAR *filename, GUID *majortype, GUID *subtype, GUID *source_clsid)
186 WCHAR extensions_path[278] = {'M','e','d','i','a',' ','T','y','p','e','\\','E','x','t','e','n','s','i','o','n','s','\\',0};
187 static const WCHAR wszExtensions[] = {'E','x','t','e','n','s','i','o','n','s',0};
188 static const WCHAR wszMediaType[] = {'M','e','d','i','a',' ','T','y','p','e',0};
189 DWORD majortype_idx, size;
190 const WCHAR *ext;
191 HKEY parent_key;
192 HANDLE file;
194 if ((ext = wcsrchr(filename, '.')))
196 WCHAR guidstr[39];
197 HKEY key;
199 wcscat(extensions_path, ext);
200 if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, extensions_path, 0, KEY_READ, &key))
202 size = sizeof(guidstr);
203 if (majortype && !RegQueryValueExW(key, L"Media Type", NULL, NULL, (BYTE *)guidstr, &size))
204 CLSIDFromString(guidstr, majortype);
206 size = sizeof(guidstr);
207 if (subtype && !RegQueryValueExW(key, L"Subtype", NULL, NULL, (BYTE *)guidstr, &size))
208 CLSIDFromString(guidstr, subtype);
210 size = sizeof(guidstr);
211 if (source_clsid && !RegQueryValueExW(key, L"Source Filter", NULL, NULL, (BYTE *)guidstr, &size))
212 CLSIDFromString(guidstr, source_clsid);
214 RegCloseKey(key);
215 return FALSE;
219 if ((file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
220 OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
222 WARN("Failed to open file %s, error %u.\n", debugstr_w(filename), GetLastError());
223 return FALSE;
226 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMediaType, 0, KEY_READ, &parent_key))
228 CloseHandle(file);
229 return FALSE;
232 for (majortype_idx = 0; ; ++majortype_idx)
234 WCHAR majortype_str[39];
235 HKEY majortype_key;
236 DWORD subtype_idx;
238 size = ARRAY_SIZE(majortype_str);
239 if (RegEnumKeyExW(parent_key, majortype_idx, majortype_str, &size, NULL, NULL, NULL, NULL))
240 break;
242 if (!wcscmp(majortype_str, wszExtensions))
243 continue;
245 if (RegOpenKeyExW(parent_key, majortype_str, 0, KEY_READ, &majortype_key))
246 continue;
248 for (subtype_idx = 0; ; ++subtype_idx)
250 WCHAR subtype_str[39], *pattern;
251 DWORD value_idx, max_size;
252 HKEY subtype_key;
254 size = ARRAY_SIZE(subtype_str);
255 if (RegEnumKeyExW(majortype_key, subtype_idx, subtype_str, &size, NULL, NULL, NULL, NULL))
256 break;
258 if (RegOpenKeyExW(majortype_key, subtype_str, 0, KEY_READ, &subtype_key))
259 continue;
261 if (RegQueryInfoKeyW(subtype_key, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &max_size, NULL, NULL))
262 continue;
264 pattern = heap_alloc(max_size);
266 for (value_idx = 0; ; ++value_idx)
268 /* The longest name we should encounter is "Source Filter". */
269 WCHAR value_name[14], source_clsid_str[39];
270 DWORD value_len = ARRAY_SIZE(value_name);
272 size = max_size;
273 if (RegEnumValueW(subtype_key, value_idx, value_name, &value_len,
274 NULL, NULL, (BYTE *)pattern, &max_size))
275 break;
277 if (!wcscmp(value_name, L"Source Filter"))
278 continue;
280 if (!process_pattern_string(pattern, file))
281 continue;
283 if (majortype)
284 CLSIDFromString(majortype_str, majortype);
285 if (subtype)
286 CLSIDFromString(subtype_str, subtype);
287 size = sizeof(source_clsid_str);
288 if (source_clsid && !RegQueryValueExW(subtype_key, L"Source Filter",
289 NULL, NULL, (BYTE *)source_clsid_str, &size))
290 CLSIDFromString(source_clsid_str, source_clsid);
292 heap_free(pattern);
293 RegCloseKey(subtype_key);
294 RegCloseKey(majortype_key);
295 RegCloseKey(parent_key);
296 CloseHandle(file);
297 return TRUE;
300 heap_free(pattern);
301 RegCloseKey(subtype_key);
304 RegCloseKey(majortype_key);
307 RegCloseKey(parent_key);
308 CloseHandle(file);
309 return FALSE;
312 static struct strmbase_pin *async_reader_get_pin(struct strmbase_filter *iface, unsigned int index)
314 AsyncReader *filter = impl_from_strmbase_filter(iface);
316 if (!index && filter->pszFileName)
317 return &filter->source.pin;
318 return NULL;
321 static void async_reader_destroy(struct strmbase_filter *iface)
323 AsyncReader *filter = impl_from_strmbase_filter(iface);
325 if (filter->pszFileName)
327 unsigned int i;
329 if (filter->source.pin.peer)
330 IPin_Disconnect(filter->source.pin.peer);
332 IPin_Disconnect(&filter->source.pin.IPin_iface);
334 if (filter->requests)
336 for (i = 0; i < filter->max_requests; ++i)
337 CloseHandle(filter->requests[i].ovl.hEvent);
338 free(filter->requests);
340 CloseHandle(filter->file);
341 filter->sample_cs.DebugInfo->Spare[0] = 0;
342 DeleteCriticalSection(&filter->sample_cs);
343 strmbase_source_cleanup(&filter->source);
345 CoTaskMemFree(filter->pszFileName);
346 FreeMediaType(&filter->mt);
349 PostQueuedCompletionStatus(filter->port, 0, 1, NULL);
350 WaitForSingleObject(filter->io_thread, INFINITE);
351 CloseHandle(filter->io_thread);
352 CloseHandle(filter->port);
354 strmbase_filter_cleanup(&filter->filter);
355 free(filter);
357 InterlockedDecrement(&object_locks);
360 static HRESULT async_reader_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
362 AsyncReader *filter = impl_from_strmbase_filter(iface);
364 if (IsEqualGUID(iid, &IID_IFileSourceFilter))
366 *out = &filter->IFileSourceFilter_iface;
367 IUnknown_AddRef((IUnknown *)*out);
368 return S_OK;
371 return E_NOINTERFACE;
374 static const struct strmbase_filter_ops filter_ops =
376 .filter_get_pin = async_reader_get_pin,
377 .filter_destroy = async_reader_destroy,
378 .filter_query_interface = async_reader_query_interface,
381 static DWORD CALLBACK io_thread(void *arg)
383 AsyncReader *filter = arg;
384 struct request *req;
385 OVERLAPPED *ovl;
386 ULONG_PTR key;
387 DWORD size;
388 BOOL ret;
390 for (;;)
392 ret = GetQueuedCompletionStatus(filter->port, &size, &key, &ovl, INFINITE);
394 if (ret && key)
395 break;
397 EnterCriticalSection(&filter->sample_cs);
399 req = CONTAINING_RECORD(ovl, struct request, ovl);
400 TRACE("Got sample %u.\n", req - filter->requests);
401 assert(req >= filter->requests && req < filter->requests + filter->max_requests);
403 if (ret)
404 WakeConditionVariable(&filter->sample_cv);
405 else
407 ERR("GetQueuedCompletionStatus() returned failure, error %u.\n", GetLastError());
408 req->sample = NULL;
411 LeaveCriticalSection(&filter->sample_cs);
414 return 0;
417 HRESULT async_reader_create(IUnknown *outer, IUnknown **out)
419 AsyncReader *object;
421 if (!(object = calloc(1, sizeof(*object))))
422 return E_OUTOFMEMORY;
424 strmbase_filter_init(&object->filter, outer, &CLSID_AsyncReader, &filter_ops);
426 object->IFileSourceFilter_iface.lpVtbl = &FileSource_Vtbl;
427 object->IAsyncReader_iface.lpVtbl = &FileAsyncReader_Vtbl;
429 InitializeCriticalSection(&object->sample_cs);
430 object->sample_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.sample_cs");
431 InitializeConditionVariable(&object->sample_cv);
432 object->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
433 object->io_thread = CreateThread(NULL, 0, io_thread, object, 0, NULL);
435 TRACE("Created file source %p.\n", object);
436 *out = &object->filter.IUnknown_inner;
437 return S_OK;
440 static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv)
442 AsyncReader *This = impl_from_IFileSourceFilter(iface);
444 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
447 static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface)
449 AsyncReader *This = impl_from_IFileSourceFilter(iface);
451 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
454 static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface)
456 AsyncReader *This = impl_from_IFileSourceFilter(iface);
458 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
461 static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
463 HANDLE hFile;
464 AsyncReader *This = impl_from_IFileSourceFilter(iface);
466 TRACE("%p->(%s, %p)\n", This, debugstr_w(pszFileName), pmt);
467 strmbase_dump_media_type(pmt);
469 if (!pszFileName)
470 return E_POINTER;
472 /* open file */
473 /* FIXME: check the sharing values that native uses */
474 hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
476 if (hFile == INVALID_HANDLE_VALUE)
478 return HRESULT_FROM_WIN32(GetLastError());
481 if (This->pszFileName)
483 free(This->pszFileName);
484 FreeMediaType(&This->mt);
487 if (!(This->pszFileName = wcsdup(pszFileName)))
489 CloseHandle(hFile);
490 return E_OUTOFMEMORY;
493 strmbase_source_init(&This->source, &This->filter, L"Output", &source_ops);
494 BaseFilterImpl_IncrementPinVersion(&This->filter);
496 This->file = hFile;
497 This->flushing = FALSE;
498 This->requests = NULL;
500 if (!pmt)
502 CopyMediaType(&This->mt, &default_mt);
503 if (get_media_type(pszFileName, &This->mt.majortype, &This->mt.subtype, NULL))
505 TRACE("Found major type %s, subtype %s.\n",
506 debugstr_guid(&This->mt.majortype), debugstr_guid(&This->mt.subtype));
509 else
510 CopyMediaType(&This->mt, pmt);
512 return S_OK;
515 static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter *iface, LPOLESTR *ppszFileName, AM_MEDIA_TYPE *mt)
517 AsyncReader *This = impl_from_IFileSourceFilter(iface);
519 TRACE("filter %p, filename %p, mt %p.\n", This, ppszFileName, mt);
521 if (!ppszFileName)
522 return E_POINTER;
524 /* copy file name & media type if available, otherwise clear the outputs */
525 if (This->pszFileName)
527 *ppszFileName = CoTaskMemAlloc((wcslen(This->pszFileName) + 1) * sizeof(WCHAR));
528 wcscpy(*ppszFileName, This->pszFileName);
529 if (mt)
530 CopyMediaType(mt, &This->mt);
532 else
534 *ppszFileName = NULL;
535 if (mt)
536 memset(mt, 0, sizeof(AM_MEDIA_TYPE));
539 return S_OK;
542 static const IFileSourceFilterVtbl FileSource_Vtbl =
544 FileSource_QueryInterface,
545 FileSource_AddRef,
546 FileSource_Release,
547 FileSource_Load,
548 FileSource_GetCurFile
551 static inline AsyncReader *impl_from_strmbase_pin(struct strmbase_pin *iface)
553 return CONTAINING_RECORD(iface, AsyncReader, source.pin);
556 static inline AsyncReader *impl_from_strmbase_source(struct strmbase_source *iface)
558 return CONTAINING_RECORD(iface, AsyncReader, source);
561 static inline AsyncReader *impl_from_IAsyncReader(IAsyncReader *iface)
563 return CONTAINING_RECORD(iface, AsyncReader, IAsyncReader_iface);
566 static HRESULT source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
568 AsyncReader *filter = impl_from_strmbase_pin(iface);
570 if (IsEqualGUID(&mt->majortype, &filter->mt.majortype)
571 && (!IsEqualGUID(&mt->subtype, &GUID_NULL)
572 || IsEqualGUID(&filter->mt.subtype, &GUID_NULL)))
573 return S_OK;
575 return S_FALSE;
578 static HRESULT source_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt)
580 AsyncReader *filter = impl_from_strmbase_pin(iface);
582 if (index > 1)
583 return VFW_S_NO_MORE_ITEMS;
585 if (index == 0)
586 CopyMediaType(mt, &filter->mt);
587 else if (index == 1)
588 CopyMediaType(mt, &default_mt);
589 return S_OK;
592 static HRESULT source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
594 AsyncReader *filter = impl_from_strmbase_pin(iface);
596 if (IsEqualGUID(iid, &IID_IAsyncReader))
597 *out = &filter->IAsyncReader_iface;
598 else
599 return E_NOINTERFACE;
601 IUnknown_AddRef((IUnknown *)*out);
602 return S_OK;
605 /* Function called as a helper to IPin_Connect */
606 /* specific AM_MEDIA_TYPE - it cannot be NULL */
607 /* this differs from standard OutputPin_AttemptConnection only in that it
608 * doesn't need the IMemInputPin interface on the receiving pin */
609 static HRESULT WINAPI FileAsyncReaderPin_AttemptConnection(struct strmbase_source *This,
610 IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
612 HRESULT hr;
614 TRACE("%p->(%p, %p)\n", This, pReceivePin, pmt);
616 if (This->pin.ops->pin_query_accept(&This->pin, pmt) != S_OK)
617 return VFW_E_TYPE_NOT_ACCEPTED;
619 This->pin.peer = pReceivePin;
620 IPin_AddRef(pReceivePin);
621 CopyMediaType(&This->pin.mt, pmt);
623 hr = IPin_ReceiveConnection(pReceivePin, &This->pin.IPin_iface, pmt);
625 if (FAILED(hr))
627 IPin_Release(This->pin.peer);
628 This->pin.peer = NULL;
629 FreeMediaType(&This->pin.mt);
632 TRACE(" -- %x\n", hr);
633 return hr;
636 static HRESULT WINAPI FileAsyncReaderPin_DecideBufferSize(struct strmbase_source *iface,
637 IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
639 AsyncReader *This = impl_from_strmbase_source(iface);
640 ALLOCATOR_PROPERTIES actual;
642 if (ppropInputRequest->cbAlign && ppropInputRequest->cbAlign != This->allocProps.cbAlign)
643 FIXME("Requested Buffer cbAlign mismatch %i,%i\n",This->allocProps.cbAlign, ppropInputRequest->cbAlign);
644 if (ppropInputRequest->cbPrefix)
645 FIXME("Requested Buffer cbPrefix mismatch %i,%i\n",This->allocProps.cbPrefix, ppropInputRequest->cbPrefix);
646 if (ppropInputRequest->cbBuffer)
647 FIXME("Requested Buffer cbBuffer mismatch %i,%i\n",This->allocProps.cbBuffer, ppropInputRequest->cbBuffer);
648 if (ppropInputRequest->cBuffers)
649 FIXME("Requested Buffer cBuffers mismatch %i,%i\n",This->allocProps.cBuffers, ppropInputRequest->cBuffers);
651 return IMemAllocator_SetProperties(pAlloc, &This->allocProps, &actual);
654 static const struct strmbase_source_ops source_ops =
656 .base.pin_query_accept = source_query_accept,
657 .base.pin_get_media_type = source_get_media_type,
658 .base.pin_query_interface = source_query_interface,
659 .pfnAttemptConnection = FileAsyncReaderPin_AttemptConnection,
660 .pfnDecideBufferSize = FileAsyncReaderPin_DecideBufferSize,
661 .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
664 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader *iface, REFIID iid, void **out)
666 AsyncReader *filter = impl_from_IAsyncReader(iface);
667 return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out);
670 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
672 AsyncReader *filter = impl_from_IAsyncReader(iface);
673 return IPin_AddRef(&filter->source.pin.IPin_iface);
676 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
678 AsyncReader *filter = impl_from_IAsyncReader(iface);
679 return IPin_Release(&filter->source.pin.IPin_iface);
682 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader *iface,
683 IMemAllocator *preferred, ALLOCATOR_PROPERTIES *props, IMemAllocator **ret_allocator)
685 AsyncReader *filter = impl_from_IAsyncReader(iface);
686 IMemAllocator *allocator;
687 unsigned int i;
688 HRESULT hr;
690 TRACE("filter %p, preferred %p, props %p, ret_allocator %p.\n", filter, preferred, props, ret_allocator);
692 if (!props->cbAlign)
693 props->cbAlign = 1;
695 *ret_allocator = NULL;
697 if (preferred)
698 IMemAllocator_AddRef(allocator = preferred);
699 else if (FAILED(hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL,
700 CLSCTX_INPROC, &IID_IMemAllocator, (void **)&allocator)))
701 return hr;
703 if (FAILED(hr = IMemAllocator_SetProperties(allocator, props, props)))
705 IMemAllocator_Release(allocator);
706 return hr;
709 if (filter->requests)
711 for (i = 0; i < filter->max_requests; ++i)
712 CloseHandle(filter->requests[i].ovl.hEvent);
713 free(filter->requests);
716 filter->max_requests = props->cBuffers;
717 TRACE("Maximum request count: %u.\n", filter->max_requests);
718 if (!(filter->requests = calloc(filter->max_requests, sizeof(filter->requests[0]))))
720 IMemAllocator_Release(allocator);
721 return E_OUTOFMEMORY;
724 for (i = 0; i < filter->max_requests; ++i)
725 filter->requests[i].ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
726 filter->allocProps = *props;
728 *ret_allocator = allocator;
729 return S_OK;
732 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader *iface, IMediaSample *sample, DWORD_PTR cookie)
734 AsyncReader *filter = impl_from_IAsyncReader(iface);
735 REFERENCE_TIME start, end;
736 struct request *req;
737 unsigned int i;
738 HRESULT hr;
739 BYTE *data;
741 TRACE("filter %p, sample %p, cookie %#lx.\n", filter, sample, cookie);
743 if (!sample)
744 return E_POINTER;
746 if (FAILED(hr = IMediaSample_GetTime(sample, &start, &end)))
747 return hr;
749 if (FAILED(hr = IMediaSample_GetPointer(sample, &data)))
750 return hr;
752 EnterCriticalSection(&filter->sample_cs);
753 if (filter->flushing)
755 LeaveCriticalSection(&filter->sample_cs);
756 return VFW_E_WRONG_STATE;
759 for (i = 0; i < filter->max_requests; ++i)
761 if (!filter->requests[i].sample)
762 break;
764 assert(i < filter->max_requests);
765 req = &filter->requests[i];
767 req->ovl.u.s.Offset = BYTES_FROM_MEDIATIME(start);
768 req->ovl.u.s.OffsetHigh = BYTES_FROM_MEDIATIME(start) >> 32;
769 /* No reference is taken. */
771 if (ReadFile(filter->file, data, BYTES_FROM_MEDIATIME(end - start), NULL, &req->ovl)
772 || GetLastError() == ERROR_IO_PENDING)
774 hr = S_OK;
775 req->sample = sample;
776 req->cookie = cookie;
778 else
779 hr = HRESULT_FROM_WIN32(GetLastError());
781 LeaveCriticalSection(&filter->sample_cs);
782 return hr;
785 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader *iface,
786 DWORD timeout, IMediaSample **sample, DWORD_PTR *cookie)
788 AsyncReader *filter = impl_from_IAsyncReader(iface);
789 unsigned int i;
791 TRACE("filter %p, timeout %u, sample %p, cookie %p.\n", filter, timeout, sample, cookie);
793 *sample = NULL;
794 *cookie = 0;
796 EnterCriticalSection(&filter->sample_cs);
800 if (filter->flushing)
802 LeaveCriticalSection(&filter->sample_cs);
803 return VFW_E_WRONG_STATE;
806 for (i = 0; i < filter->max_requests; ++i)
808 struct request *req = &filter->requests[i];
809 DWORD size;
811 if (req->sample && GetOverlappedResult(filter->file, &req->ovl, &size, FALSE))
813 REFERENCE_TIME start, end;
815 IMediaSample_SetActualDataLength(req->sample, size);
816 start = MEDIATIME_FROM_BYTES(((ULONGLONG)req->ovl.u.s.OffsetHigh << 32) + req->ovl.u.s.Offset);
817 end = start + MEDIATIME_FROM_BYTES(size);
818 IMediaSample_SetTime(req->sample, &start, &end);
820 *sample = req->sample;
821 *cookie = req->cookie;
822 req->sample = NULL;
824 LeaveCriticalSection(&filter->sample_cs);
825 TRACE("Returning sample %u.\n", i);
826 return S_OK;
829 } while (SleepConditionVariableCS(&filter->sample_cv, &filter->sample_cs, timeout));
831 LeaveCriticalSection(&filter->sample_cs);
832 return VFW_E_TIMEOUT;
835 static BOOL sync_read(HANDLE file, LONGLONG offset, LONG length, BYTE *buffer, DWORD *read_len)
837 OVERLAPPED ovl = {0};
838 BOOL ret;
840 ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1);
841 ovl.u.s.Offset = (DWORD)offset;
842 ovl.u.s.OffsetHigh = offset >> 32;
844 *read_len = 0;
846 ret = ReadFile(file, buffer, length, NULL, &ovl);
847 if (ret || GetLastError() == ERROR_IO_PENDING)
848 ret = GetOverlappedResult(file, &ovl, read_len, TRUE);
850 TRACE("Returning %u bytes.\n", *read_len);
852 CloseHandle(ovl.hEvent);
853 return ret;
856 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader *iface, IMediaSample *sample)
858 AsyncReader *filter = impl_from_IAsyncReader(iface);
859 REFERENCE_TIME start_time, end_time;
860 DWORD read_len;
861 BYTE *buffer;
862 LONG length;
863 HRESULT hr;
864 BOOL ret;
866 TRACE("filter %p, sample %p.\n", filter, sample);
868 hr = IMediaSample_GetTime(sample, &start_time, &end_time);
870 if (SUCCEEDED(hr))
871 hr = IMediaSample_GetPointer(sample, &buffer);
873 if (SUCCEEDED(hr))
875 length = BYTES_FROM_MEDIATIME(end_time - start_time);
876 ret = sync_read(filter->file, BYTES_FROM_MEDIATIME(start_time), length, buffer, &read_len);
877 if (ret)
878 hr = (read_len == length) ? S_OK : S_FALSE;
879 else if (GetLastError() == ERROR_HANDLE_EOF)
880 hr = S_OK;
881 else
882 hr = HRESULT_FROM_WIN32(GetLastError());
885 if (SUCCEEDED(hr))
886 IMediaSample_SetActualDataLength(sample, read_len);
888 return hr;
891 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader *iface,
892 LONGLONG offset, LONG length, BYTE *buffer)
894 AsyncReader *filter = impl_from_IAsyncReader(iface);
895 DWORD read_len;
896 HRESULT hr;
897 BOOL ret;
899 TRACE("filter %p, offset %s, length %d, buffer %p.\n",
900 filter, wine_dbgstr_longlong(offset), length, buffer);
902 ret = sync_read(filter->file, offset, length, buffer, &read_len);
903 if (ret)
904 hr = (read_len == length) ? S_OK : S_FALSE;
905 else if (GetLastError() == ERROR_HANDLE_EOF)
906 hr = S_FALSE;
907 else
908 hr = HRESULT_FROM_WIN32(GetLastError());
910 return hr;
913 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader *iface, LONGLONG *total, LONGLONG *available)
915 AsyncReader *filter = impl_from_IAsyncReader(iface);
916 DWORD low, high;
918 TRACE("iface %p, total %p, available %p.\n", iface, total, available);
920 if ((low = GetFileSize(filter->file, &high)) == -1 && GetLastError() != NO_ERROR)
921 return HRESULT_FROM_WIN32(GetLastError());
923 *available = *total = (LONGLONG)low | (LONGLONG)high << (sizeof(DWORD) * 8);
925 return S_OK;
928 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
930 AsyncReader *filter = impl_from_IAsyncReader(iface);
931 unsigned int i;
933 TRACE("iface %p.\n", iface);
935 EnterCriticalSection(&filter->sample_cs);
937 filter->flushing = TRUE;
938 for (i = 0; i < filter->max_requests; ++i)
939 filter->requests[i].sample = NULL;
940 CancelIoEx(filter->file, NULL);
941 WakeAllConditionVariable(&filter->sample_cv);
943 LeaveCriticalSection(&filter->sample_cs);
945 return S_OK;
948 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
950 AsyncReader *filter = impl_from_IAsyncReader(iface);
952 TRACE("iface %p.\n", iface);
954 EnterCriticalSection(&filter->sample_cs);
956 filter->flushing = FALSE;
958 LeaveCriticalSection(&filter->sample_cs);
960 return S_OK;
963 static const IAsyncReaderVtbl FileAsyncReader_Vtbl =
965 FileAsyncReader_QueryInterface,
966 FileAsyncReader_AddRef,
967 FileAsyncReader_Release,
968 FileAsyncReader_RequestAllocator,
969 FileAsyncReader_Request,
970 FileAsyncReader_WaitForNext,
971 FileAsyncReader_SyncReadAligned,
972 FileAsyncReader_SyncRead,
973 FileAsyncReader_Length,
974 FileAsyncReader_BeginFlush,
975 FileAsyncReader_EndFlush,