filesource.c 26.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * File Source Filter
 *
 * Copyright 2003 Robert Shearman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

24 25 26 27 28 29 30
#include "quartz_private.h"

#include "wine/debug.h"
#include "uuids.h"
#include "vfwmsgs.h"
#include "winbase.h"
#include "winreg.h"
31
#include "shlwapi.h"
32 33 34 35
#include <assert.h>

WINE_DEFAULT_DEBUG_CHANNEL(quartz);

36 37 38 39 40 41 42 43 44 45 46 47 48
static const AM_MEDIA_TYPE default_mt =
{
    {0xe436eb83,0x524f,0x11ce,{0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70}},   /* MEDIATYPE_Stream */
    {0,0,0,{0,0,0,0,0,0,0,0}},
    TRUE,
    FALSE,
    1,
    {0,0,0,{0,0,0,0,0,0,0,0}},
    NULL,
    0,
    NULL
};

49
struct request
50
{
51 52
    IMediaSample *sample;
    DWORD_PTR cookie;
53
    OVERLAPPED ovl;
54
};
55

56
struct async_reader
57
{
58
    struct strmbase_filter filter;
59 60
    IFileSourceFilter IFileSourceFilter_iface;

61
    struct strmbase_source source;
62 63
    IAsyncReader IAsyncReader_iface;

64
    LPOLESTR pszFileName;
65
    AM_MEDIA_TYPE mt;
66
    HANDLE file, port, io_thread;
67
    LARGE_INTEGER file_size;
68
    CRITICAL_SECTION sample_cs;
69 70 71 72
    BOOL flushing;
    struct request *requests;
    unsigned int max_requests;
    CONDITION_VARIABLE sample_cv;
73
};
74

75
static const struct strmbase_source_ops source_ops;
76

77
static inline struct async_reader *impl_from_strmbase_filter(struct strmbase_filter *iface)
78
{
79
    return CONTAINING_RECORD(iface, struct async_reader, filter);
80 81
}

82
static inline struct async_reader *impl_from_IFileSourceFilter(IFileSourceFilter *iface)
83
{
84
    return CONTAINING_RECORD(iface, struct async_reader, IFileSourceFilter_iface);
85 86
}

87 88
static const IFileSourceFilterVtbl FileSource_Vtbl;
static const IAsyncReaderVtbl FileAsyncReader_Vtbl;
89

90
static int byte_from_hex_char(WCHAR c)
91
{
92 93 94 95
    if ('0' <= c && c <= '9') return c - '0';
    if ('a' <= c && c <= 'f') return c - 'a' + 10;
    if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return -1;
96 97
}

98
static BOOL process_pattern_string(const WCHAR *pattern, HANDLE file)
99
{
100
    ULONG size, offset, i, ret_size;
101
    BYTE *mask, *expect, *actual;
102
    int d;
103
    BOOL ret = TRUE;
104

105
    /* format: "offset, size, mask, value" */
106

107
    offset = wcstol(pattern, NULL, 10);
108

109 110 111
    if (!(pattern = wcschr(pattern, ',')))
        return FALSE;
    pattern++;
112

113 114 115 116
    size = wcstol(pattern, NULL, 10);
    mask = heap_alloc(size);
    expect = heap_alloc(size);
    memset(mask, 0xff, size);
117

118
    if (!(pattern = wcschr(pattern, ',')))
119 120 121
    {
        heap_free(mask);
        heap_free(expect);
122
        return FALSE;
123
    }
124
    pattern++;
125
    while (byte_from_hex_char(*pattern) == -1 && (*pattern != ','))
126
        pattern++;
127

128
    for (i = 0; (d = byte_from_hex_char(*pattern)) != -1 && (i/2 < size); pattern++, i++)
129
    {
130
        if (i % 2)
131
            mask[i / 2] |= d;
132
        else
133
            mask[i / 2] = d << 4;
134
    }
135

136 137 138 139 140 141 142
    if (!(pattern = wcschr(pattern, ',')))
    {
        heap_free(mask);
        heap_free(expect);
        return FALSE;
    }
    pattern++;
143
    while (byte_from_hex_char(*pattern) == -1 && (*pattern != ','))
144
        pattern++;
145

146
    for (i = 0; (d = byte_from_hex_char(*pattern)) != -1 && (i/2 < size); pattern++, i++)
147 148
    {
        if (i % 2)
149
            expect[i / 2] |= d;
150
        else
151
            expect[i / 2] = d << 4;
152 153
    }

154
    actual = heap_alloc(size);
155 156
    SetFilePointer(file, offset, NULL, FILE_BEGIN);
    if (!ReadFile(file, actual, size, &ret_size, NULL) || ret_size != size)
157
    {
158 159 160 161 162
        heap_free(actual);
        heap_free(expect);
        heap_free(mask);
        return FALSE;
    }
163

164 165 166
    for (i = 0; i < size; ++i)
    {
        if ((actual[i] & mask[i]) != expect[i])
167
        {
168 169
            ret = FALSE;
            break;
170 171 172
        }
    }

173 174 175
    heap_free(actual);
    heap_free(expect);
    heap_free(mask);
176

177 178
    /* If there is a following tuple, then we must match that as well. */
    if (ret && (pattern = wcschr(pattern, ',')))
179
        return process_pattern_string(pattern + 1, file);
180

181
    return ret;
182 183
}

184
BOOL get_media_type(const WCHAR *filename, GUID *majortype, GUID *subtype, GUID *source_clsid)
185
{
186
    WCHAR extensions_path[278] = L"Media Type\\Extensions\\";
187
    DWORD majortype_idx, size;
188
    const WCHAR *ext;
189
    HKEY parent_key;
190
    HANDLE file;
191

192
    if ((ext = wcsrchr(filename, '.')))
193 194 195 196 197 198 199 200
    {
        WCHAR guidstr[39];
        HKEY key;

        wcscat(extensions_path, ext);
        if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, extensions_path, 0, KEY_READ, &key))
        {
            size = sizeof(guidstr);
201
            if (majortype && !RegQueryValueExW(key, L"Media Type", NULL, NULL, (BYTE *)guidstr, &size))
202
                CLSIDFromString(guidstr, majortype);
203 204

            size = sizeof(guidstr);
205
            if (subtype && !RegQueryValueExW(key, L"Subtype", NULL, NULL, (BYTE *)guidstr, &size))
206
                CLSIDFromString(guidstr, subtype);
207 208

            size = sizeof(guidstr);
209
            if (source_clsid && !RegQueryValueExW(key, L"Source Filter", NULL, NULL, (BYTE *)guidstr, &size))
210
                CLSIDFromString(guidstr, source_clsid);
211 212

            RegCloseKey(key);
213
            return FALSE;
214 215 216
        }
    }

217 218 219 220
    if ((file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
            OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
    {
        WARN("Failed to open file %s, error %u.\n", debugstr_w(filename), GetLastError());
221
        return FALSE;
222
    }
223

224
    if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Media Type", 0, KEY_READ, &parent_key))
225 226
    {
        CloseHandle(file);
227
        return FALSE;
228
    }
229

230
    for (majortype_idx = 0; ; ++majortype_idx)
231
    {
232 233 234
        WCHAR majortype_str[39];
        HKEY majortype_key;
        DWORD subtype_idx;
235

236 237 238 239
        size = ARRAY_SIZE(majortype_str);
        if (RegEnumKeyExW(parent_key, majortype_idx, majortype_str, &size, NULL, NULL, NULL, NULL))
            break;

240
        if (!wcscmp(majortype_str, L"Extensions"))
241 242 243 244 245 246
            continue;

        if (RegOpenKeyExW(parent_key, majortype_str, 0, KEY_READ, &majortype_key))
            continue;

        for (subtype_idx = 0; ; ++subtype_idx)
247
        {
248 249 250
            WCHAR subtype_str[39], *pattern;
            DWORD value_idx, max_size;
            HKEY subtype_key;
251

252 253
            size = ARRAY_SIZE(subtype_str);
            if (RegEnumKeyExW(majortype_key, subtype_idx, subtype_str, &size, NULL, NULL, NULL, NULL))
254
                break;
255 256 257 258 259 260 261 262 263 264

            if (RegOpenKeyExW(majortype_key, subtype_str, 0, KEY_READ, &subtype_key))
                continue;

            if (RegQueryInfoKeyW(subtype_key, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &max_size, NULL, NULL))
                continue;

            pattern = heap_alloc(max_size);

            for (value_idx = 0; ; ++value_idx)
265
            {
266 267 268 269 270 271 272 273 274
                /* The longest name we should encounter is "Source Filter". */
                WCHAR value_name[14], source_clsid_str[39];
                DWORD value_len = ARRAY_SIZE(value_name);

                size = max_size;
                if (RegEnumValueW(subtype_key, value_idx, value_name, &value_len,
                        NULL, NULL, (BYTE *)pattern, &max_size))
                    break;

275
                if (!wcscmp(value_name, L"Source Filter"))
276 277
                    continue;

278
                if (!process_pattern_string(pattern, file))
279 280 281 282 283 284 285
                    continue;

                if (majortype)
                    CLSIDFromString(majortype_str, majortype);
                if (subtype)
                    CLSIDFromString(subtype_str, subtype);
                size = sizeof(source_clsid_str);
286
                if (source_clsid && !RegQueryValueExW(subtype_key, L"Source Filter",
287 288 289 290 291 292 293
                        NULL, NULL, (BYTE *)source_clsid_str, &size))
                    CLSIDFromString(source_clsid_str, source_clsid);

                heap_free(pattern);
                RegCloseKey(subtype_key);
                RegCloseKey(majortype_key);
                RegCloseKey(parent_key);
294
                CloseHandle(file);
295
                return TRUE;
296
            }
297 298 299

            heap_free(pattern);
            RegCloseKey(subtype_key);
300
        }
301 302

        RegCloseKey(majortype_key);
303 304
    }

305
    RegCloseKey(parent_key);
306
    CloseHandle(file);
307
    return FALSE;
308 309
}

310
static struct strmbase_pin *async_reader_get_pin(struct strmbase_filter *iface, unsigned int index)
311
{
312
    struct async_reader *filter = impl_from_strmbase_filter(iface);
313

314
    if (!index && filter->pszFileName)
315
        return &filter->source.pin;
316
    return NULL;
317 318
}

319
static void async_reader_destroy(struct strmbase_filter *iface)
320
{
321
    struct async_reader *filter = impl_from_strmbase_filter(iface);
322

323
    if (filter->pszFileName)
324
    {
325 326
        unsigned int i;

327 328
        if (filter->source.pin.peer)
            IPin_Disconnect(filter->source.pin.peer);
329 330

        IPin_Disconnect(&filter->source.pin.IPin_iface);
331

332
        if (filter->requests)
333
        {
334 335
            for (i = 0; i < filter->max_requests; ++i)
                CloseHandle(filter->requests[i].ovl.hEvent);
336
            free(filter->requests);
337
        }
338 339 340 341
        CloseHandle(filter->file);
        filter->sample_cs.DebugInfo->Spare[0] = 0;
        DeleteCriticalSection(&filter->sample_cs);
        strmbase_source_cleanup(&filter->source);
342

343
        free(filter->pszFileName);
344
        FreeMediaType(&filter->mt);
345
    }
346 347 348 349 350 351

    PostQueuedCompletionStatus(filter->port, 0, 1, NULL);
    WaitForSingleObject(filter->io_thread, INFINITE);
    CloseHandle(filter->io_thread);
    CloseHandle(filter->port);

352
    strmbase_filter_cleanup(&filter->filter);
353
    free(filter);
354 355

    InterlockedDecrement(&object_locks);
356 357
}

358
static HRESULT async_reader_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
359
{
360
    struct async_reader *filter = impl_from_strmbase_filter(iface);
361 362 363 364 365 366 367 368 369 370 371

    if (IsEqualGUID(iid, &IID_IFileSourceFilter))
    {
        *out = &filter->IFileSourceFilter_iface;
        IUnknown_AddRef((IUnknown *)*out);
        return S_OK;
    }

    return E_NOINTERFACE;
}

372
static const struct strmbase_filter_ops filter_ops =
373
{
374
    .filter_get_pin = async_reader_get_pin,
375
    .filter_destroy = async_reader_destroy,
376
    .filter_query_interface = async_reader_query_interface,
377 378
};

379 380
static DWORD CALLBACK io_thread(void *arg)
{
381
    struct async_reader *filter = arg;
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
    struct request *req;
    OVERLAPPED *ovl;
    ULONG_PTR key;
    DWORD size;
    BOOL ret;

    for (;;)
    {
        ret = GetQueuedCompletionStatus(filter->port, &size, &key, &ovl, INFINITE);

        if (ret && key)
            break;

        EnterCriticalSection(&filter->sample_cs);

        req = CONTAINING_RECORD(ovl, struct request, ovl);
        TRACE("Got sample %u.\n", req - filter->requests);
        assert(req >= filter->requests && req < filter->requests + filter->max_requests);

        if (ret)
            WakeConditionVariable(&filter->sample_cv);
        else
        {
            ERR("GetQueuedCompletionStatus() returned failure, error %u.\n", GetLastError());
            req->sample = NULL;
        }

        LeaveCriticalSection(&filter->sample_cs);
    }

    return 0;
}

415
HRESULT async_reader_create(IUnknown *outer, IUnknown **out)
416
{
417
    struct async_reader *object;
418

419
    if (!(object = calloc(1, sizeof(*object))))
420 421
        return E_OUTOFMEMORY;

422
    strmbase_filter_init(&object->filter, outer, &CLSID_AsyncReader, &filter_ops);
423

424 425
    object->IFileSourceFilter_iface.lpVtbl = &FileSource_Vtbl;
    object->IAsyncReader_iface.lpVtbl = &FileAsyncReader_Vtbl;
426

427 428 429 430 431
    InitializeCriticalSection(&object->sample_cs);
    object->sample_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.sample_cs");
    InitializeConditionVariable(&object->sample_cv);
    object->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    object->io_thread = CreateThread(NULL, 0, io_thread, object, 0, NULL);
432

433 434
    TRACE("Created file source %p.\n", object);
    *out = &object->filter.IUnknown_inner;
435 436 437 438 439
    return S_OK;
}

static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv)
{
440 441
    struct async_reader *filter = impl_from_IFileSourceFilter(iface);
    return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, riid, ppv);
442 443 444 445
}

static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface)
{
446 447
    struct async_reader *filter = impl_from_IFileSourceFilter(iface);
    return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface);
448 449 450 451
}

static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface)
{
452 453
    struct async_reader *filter = impl_from_IFileSourceFilter(iface);
    return IBaseFilter_Release(&filter->filter.IBaseFilter_iface);
454 455 456 457
}

static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
{
458
    struct async_reader *This = impl_from_IFileSourceFilter(iface);
459 460
    HANDLE hFile;

461
    TRACE("%p->(%s, %p)\n", This, debugstr_w(pszFileName), pmt);
462
    strmbase_dump_media_type(pmt);
463

464 465 466
    if (!pszFileName)
        return E_POINTER;

467 468 469 470 471 472 473 474 475
    /* open file */
    /* FIXME: check the sharing values that native uses */
    hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

476 477 478
    if (!GetFileSizeEx(hFile, &This->file_size))
    {
        WARN("Could not get file size.\n");
479
        CloseHandle(hFile);
480 481 482
        return HRESULT_FROM_WIN32(GetLastError());
    }

483 484 485 486 487 488
    if (This->pszFileName)
    {
        free(This->pszFileName);
        FreeMediaType(&This->mt);
    }

489 490 491 492 493 494 495
    if (!(This->pszFileName = wcsdup(pszFileName)))
    {
        CloseHandle(hFile);
        return E_OUTOFMEMORY;
    }

    strmbase_source_init(&This->source, &This->filter, L"Output", &source_ops);
496
    BaseFilterImpl_IncrementPinVersion(&This->filter);
497

498 499
    This->file = hFile;
    This->flushing = FALSE;
500
    This->requests = NULL;
501

502
    if (!pmt)
503
    {
504 505
        CopyMediaType(&This->mt, &default_mt);
        if (get_media_type(pszFileName, &This->mt.majortype, &This->mt.subtype, NULL))
506 507
        {
            TRACE("Found major type %s, subtype %s.\n",
508
                    debugstr_guid(&This->mt.majortype), debugstr_guid(&This->mt.subtype));
509
        }
510
    }
511
    else
512
        CopyMediaType(&This->mt, pmt);
513

514
    return S_OK;
515 516
}

517
static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter *iface, LPOLESTR *ppszFileName, AM_MEDIA_TYPE *mt)
518
{
519
    struct async_reader *This = impl_from_IFileSourceFilter(iface);
520 521

    TRACE("filter %p, filename %p, mt %p.\n", This, ppszFileName, mt);
522

523 524 525
    if (!ppszFileName)
        return E_POINTER;

526 527 528
    /* copy file name & media type if available, otherwise clear the outputs */
    if (This->pszFileName)
    {
529 530
        *ppszFileName = CoTaskMemAlloc((wcslen(This->pszFileName) + 1) * sizeof(WCHAR));
        wcscpy(*ppszFileName, This->pszFileName);
531 532
        if (mt)
            CopyMediaType(mt, &This->mt);
533 534 535
    }
    else
    {
536 537 538
        *ppszFileName = NULL;
        if (mt)
            memset(mt, 0, sizeof(AM_MEDIA_TYPE));
539 540 541 542 543 544 545 546 547 548 549 550 551 552
    }

    return S_OK;
}

static const IFileSourceFilterVtbl FileSource_Vtbl = 
{
    FileSource_QueryInterface,
    FileSource_AddRef,
    FileSource_Release,
    FileSource_Load,
    FileSource_GetCurFile
};

553
static inline struct async_reader *impl_from_strmbase_pin(struct strmbase_pin *iface)
554
{
555
    return CONTAINING_RECORD(iface, struct async_reader, source.pin);
556 557
}

558
static inline struct async_reader *impl_from_IAsyncReader(IAsyncReader *iface)
559
{
560
    return CONTAINING_RECORD(iface, struct async_reader, IAsyncReader_iface);
561 562
}

563
static HRESULT source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
564
{
565
    struct async_reader *filter = impl_from_strmbase_pin(iface);
566

567
    if (IsEqualGUID(&mt->majortype, &filter->mt.majortype)
568 569
            && (!IsEqualGUID(&mt->subtype, &GUID_NULL)
            || IsEqualGUID(&filter->mt.subtype, &GUID_NULL)))
570
        return S_OK;
571

572 573 574
    return S_FALSE;
}

575
static HRESULT source_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt)
576
{
577
    struct async_reader *filter = impl_from_strmbase_pin(iface);
578

579
    if (index > 1)
580
        return VFW_S_NO_MORE_ITEMS;
581 582

    if (index == 0)
583
        CopyMediaType(mt, &filter->mt);
584 585
    else if (index == 1)
        CopyMediaType(mt, &default_mt);
586 587 588
    return S_OK;
}

589
static HRESULT source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
590
{
591
    struct async_reader *filter = impl_from_strmbase_pin(iface);
592

593
    if (IsEqualGUID(iid, &IID_IAsyncReader))
594 595 596
        *out = &filter->IAsyncReader_iface;
    else
        return E_NOINTERFACE;
597

598 599
    IUnknown_AddRef((IUnknown *)*out);
    return S_OK;
600 601
}

602 603
/* Function called as a helper to IPin_Connect */
/* specific AM_MEDIA_TYPE - it cannot be NULL */
604
/* this differs from standard OutputPin_AttemptConnection only in that it
605
 * doesn't need the IMemInputPin interface on the receiving pin */
606
static HRESULT WINAPI FileAsyncReaderPin_AttemptConnection(struct strmbase_source *This,
607
        IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
608 609 610
{
    HRESULT hr;

611
    TRACE("%p->(%p, %p)\n", This, pReceivePin, pmt);
612

613 614
    if (This->pin.ops->pin_query_accept(&This->pin, pmt) != S_OK)
        return VFW_E_TYPE_NOT_ACCEPTED;
615

616
    This->pin.peer = pReceivePin;
617
    IPin_AddRef(pReceivePin);
618
    CopyMediaType(&This->pin.mt, pmt);
619

620
    hr = IPin_ReceiveConnection(pReceivePin, &This->pin.IPin_iface, pmt);
621 622 623

    if (FAILED(hr))
    {
624 625
        IPin_Release(This->pin.peer);
        This->pin.peer = NULL;
626
        FreeMediaType(&This->pin.mt);
627 628
    }

629
    TRACE(" -- %x\n", hr);
630 631 632
    return hr;
}

633 634
static const struct strmbase_source_ops source_ops =
{
635
    .base.pin_query_accept = source_query_accept,
636
    .base.pin_get_media_type = source_get_media_type,
637
    .base.pin_query_interface = source_query_interface,
638
    .pfnAttemptConnection = FileAsyncReaderPin_AttemptConnection,
639
};
640

641
static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader *iface, REFIID iid, void **out)
642
{
643
    struct async_reader *filter = impl_from_IAsyncReader(iface);
644
    return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out);
645 646 647 648
}

static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
{
649
    struct async_reader *filter = impl_from_IAsyncReader(iface);
650
    return IPin_AddRef(&filter->source.pin.IPin_iface);
651 652 653 654
}

static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
{
655
    struct async_reader *filter = impl_from_IAsyncReader(iface);
656
    return IPin_Release(&filter->source.pin.IPin_iface);
657 658
}

659 660
static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader *iface,
        IMemAllocator *preferred, ALLOCATOR_PROPERTIES *props, IMemAllocator **ret_allocator)
661
{
662
    struct async_reader *filter = impl_from_IAsyncReader(iface);
663 664 665
    IMemAllocator *allocator;
    unsigned int i;
    HRESULT hr;
666

667
    TRACE("filter %p, preferred %p, props %p, ret_allocator %p.\n", filter, preferred, props, ret_allocator);
668

669 670
    if (!props->cbAlign)
        props->cbAlign = 1;
671

672
    *ret_allocator = NULL;
673

674 675 676 677 678
    if (preferred)
        IMemAllocator_AddRef(allocator = preferred);
    else if (FAILED(hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL,
            CLSCTX_INPROC, &IID_IMemAllocator, (void **)&allocator)))
        return hr;
679

680
    if (FAILED(hr = IMemAllocator_SetProperties(allocator, props, props)))
681
    {
682 683
        IMemAllocator_Release(allocator);
        return hr;
684 685
    }

686
    if (filter->requests)
687
    {
688 689 690
        for (i = 0; i < filter->max_requests; ++i)
            CloseHandle(filter->requests[i].ovl.hEvent);
        free(filter->requests);
691 692
    }

693 694 695
    filter->max_requests = props->cBuffers;
    TRACE("Maximum request count: %u.\n", filter->max_requests);
    if (!(filter->requests = calloc(filter->max_requests, sizeof(filter->requests[0]))))
696
    {
697 698
        IMemAllocator_Release(allocator);
        return E_OUTOFMEMORY;
699 700
    }

701 702 703 704 705
    for (i = 0; i < filter->max_requests; ++i)
        filter->requests[i].ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);

    *ret_allocator = allocator;
    return S_OK;
706 707
}

708
static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader *iface, IMediaSample *sample, DWORD_PTR cookie)
709
{
710
    struct async_reader *filter = impl_from_IAsyncReader(iface);
711 712 713 714 715
    REFERENCE_TIME start, end;
    struct request *req;
    unsigned int i;
    HRESULT hr;
    BYTE *data;
716

717
    TRACE("filter %p, sample %p, cookie %#lx.\n", filter, sample, cookie);
718

719
    if (!sample)
720
        return E_POINTER;
721

722 723
    if (FAILED(hr = IMediaSample_GetTime(sample, &start, &end)))
        return hr;
724

725 726 727
    if (BYTES_FROM_MEDIATIME(start) >= filter->file_size.QuadPart)
        return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);

728 729
    if (FAILED(hr = IMediaSample_GetPointer(sample, &data)))
        return hr;
730

731 732
    EnterCriticalSection(&filter->sample_cs);
    if (filter->flushing)
733
    {
734
        LeaveCriticalSection(&filter->sample_cs);
735 736 737
        return VFW_E_WRONG_STATE;
    }

738
    for (i = 0; i < filter->max_requests; ++i)
739
    {
740 741 742 743 744
        if (!filter->requests[i].sample)
            break;
    }
    assert(i < filter->max_requests);
    req = &filter->requests[i];
745

746 747 748
    req->ovl.u.s.Offset = BYTES_FROM_MEDIATIME(start);
    req->ovl.u.s.OffsetHigh = BYTES_FROM_MEDIATIME(start) >> 32;
    /* No reference is taken. */
749

750 751 752 753 754 755
    if (ReadFile(filter->file, data, BYTES_FROM_MEDIATIME(end - start), NULL, &req->ovl)
            || GetLastError() == ERROR_IO_PENDING)
    {
        hr = S_OK;
        req->sample = sample;
        req->cookie = cookie;
756
    }
757 758
    else
        hr = HRESULT_FROM_WIN32(GetLastError());
759

760
    LeaveCriticalSection(&filter->sample_cs);
761 762 763
    return hr;
}

764 765
static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader *iface,
        DWORD timeout, IMediaSample **sample, DWORD_PTR *cookie)
766
{
767
    struct async_reader *filter = impl_from_IAsyncReader(iface);
768
    unsigned int i;
769

770
    TRACE("filter %p, timeout %u, sample %p, cookie %p.\n", filter, timeout, sample, cookie);
771

772 773
    *sample = NULL;
    *cookie = 0;
774

775
    EnterCriticalSection(&filter->sample_cs);
776

777 778 779
    do
    {
        if (filter->flushing)
780
        {
781 782
            LeaveCriticalSection(&filter->sample_cs);
            return VFW_E_WRONG_STATE;
783
        }
784

785
        for (i = 0; i < filter->max_requests; ++i)
786
        {
787 788
            struct request *req = &filter->requests[i];
            DWORD size;
789

790
            if (req->sample && GetOverlappedResult(filter->file, &req->ovl, &size, FALSE))
791
            {
792
                REFERENCE_TIME start, end;
793

794 795 796 797
                IMediaSample_SetActualDataLength(req->sample, size);
                start = MEDIATIME_FROM_BYTES(((ULONGLONG)req->ovl.u.s.OffsetHigh << 32) + req->ovl.u.s.Offset);
                end = start + MEDIATIME_FROM_BYTES(size);
                IMediaSample_SetTime(req->sample, &start, &end);
798

799 800 801
                *sample = req->sample;
                *cookie = req->cookie;
                req->sample = NULL;
802

803 804 805 806
                LeaveCriticalSection(&filter->sample_cs);
                TRACE("Returning sample %u.\n", i);
                return S_OK;
            }
807
        }
808
    } while (SleepConditionVariableCS(&filter->sample_cv, &filter->sample_cs, timeout));
809

810 811
    LeaveCriticalSection(&filter->sample_cs);
    return VFW_E_TIMEOUT;
812 813
}

814 815 816 817
static BOOL sync_read(HANDLE file, LONGLONG offset, LONG length, BYTE *buffer, DWORD *read_len)
{
    OVERLAPPED ovl = {0};
    BOOL ret;
818

819
    ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1);
820 821 822
    ovl.u.s.Offset = (DWORD)offset;
    ovl.u.s.OffsetHigh = offset >> 32;

823 824
    *read_len = 0;

825 826 827 828 829 830 831 832 833 834 835
    ret = ReadFile(file, buffer, length, NULL, &ovl);
    if (ret || GetLastError() == ERROR_IO_PENDING)
        ret = GetOverlappedResult(file, &ovl, read_len, TRUE);

    TRACE("Returning %u bytes.\n", *read_len);

    CloseHandle(ovl.hEvent);
    return ret;
}

static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader *iface, IMediaSample *sample)
836
{
837
    struct async_reader *filter = impl_from_IAsyncReader(iface);
838 839 840 841
    REFERENCE_TIME start_time, end_time;
    DWORD read_len;
    BYTE *buffer;
    LONG length;
842
    HRESULT hr;
843
    BOOL ret;
844

845
    TRACE("filter %p, sample %p.\n", filter, sample);
846

847
    hr = IMediaSample_GetTime(sample, &start_time, &end_time);
848 849

    if (SUCCEEDED(hr))
850
        hr = IMediaSample_GetPointer(sample, &buffer);
851 852

    if (SUCCEEDED(hr))
853 854
    {
        length = BYTES_FROM_MEDIATIME(end_time - start_time);
855
        ret = sync_read(filter->file, BYTES_FROM_MEDIATIME(start_time), length, buffer, &read_len);
856 857 858 859 860 861 862
        if (ret)
            hr = (read_len == length) ? S_OK : S_FALSE;
        else if (GetLastError() == ERROR_HANDLE_EOF)
            hr = S_OK;
        else
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
863

864 865 866
    if (SUCCEEDED(hr))
        IMediaSample_SetActualDataLength(sample, read_len);

867 868 869
    return hr;
}

870 871
static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader *iface,
        LONGLONG offset, LONG length, BYTE *buffer)
872
{
873
    struct async_reader *filter = impl_from_IAsyncReader(iface);
874 875 876
    DWORD read_len;
    HRESULT hr;
    BOOL ret;
877

878 879
    TRACE("filter %p, offset %s, length %d, buffer %p.\n",
            filter, wine_dbgstr_longlong(offset), length, buffer);
880

881
    ret = sync_read(filter->file, offset, length, buffer, &read_len);
882 883
    if (ret)
        hr = (read_len == length) ? S_OK : S_FALSE;
884 885 886 887
    else if (GetLastError() == ERROR_HANDLE_EOF)
        hr = S_FALSE;
    else
        hr = HRESULT_FROM_WIN32(GetLastError());
888 889 890 891

    return hr;
}

892
static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader *iface, LONGLONG *total, LONGLONG *available)
893
{
894
    struct async_reader *filter = impl_from_IAsyncReader(iface);
895

896
    TRACE("iface %p, total %p, available %p.\n", iface, total, available);
897

898
    *available = *total = filter->file_size.QuadPart;
899 900 901 902 903 904

    return S_OK;
}

static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
{
905
    struct async_reader *filter = impl_from_IAsyncReader(iface);
906
    unsigned int i;
907

908
    TRACE("iface %p.\n", iface);
909

910 911 912
    EnterCriticalSection(&filter->sample_cs);

    filter->flushing = TRUE;
913 914
    for (i = 0; i < filter->max_requests; ++i)
        filter->requests[i].sample = NULL;
915
    CancelIoEx(filter->file, NULL);
916
    WakeAllConditionVariable(&filter->sample_cv);
917 918

    LeaveCriticalSection(&filter->sample_cs);
919 920 921 922 923 924

    return S_OK;
}

static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
{
925
    struct async_reader *filter = impl_from_IAsyncReader(iface);
926

927 928 929
    TRACE("iface %p.\n", iface);

    EnterCriticalSection(&filter->sample_cs);
930

931
    filter->flushing = FALSE;
932

933
    LeaveCriticalSection(&filter->sample_cs);
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

    return S_OK;
}

static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 
{
    FileAsyncReader_QueryInterface,
    FileAsyncReader_AddRef,
    FileAsyncReader_Release,
    FileAsyncReader_RequestAllocator,
    FileAsyncReader_Request,
    FileAsyncReader_WaitForNext,
    FileAsyncReader_SyncReadAligned,
    FileAsyncReader_SyncRead,
    FileAsyncReader_Length,
    FileAsyncReader_BeginFlush,
    FileAsyncReader_EndFlush,
};