memallocator.c 26.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Memory Allocator and Media Sample Implementation
 *
 * 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 24 25 26 27 28 29 30 31 32 33
 */

#include <assert.h>
#include <limits.h>
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "vfwmsgs.h"

#include "quartz_private.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(quartz);

34 35 36 37 38 39 40 41 42 43 44
typedef struct StdMediaSample2
{
    IMediaSample2 IMediaSample2_iface;
    LONG ref;
    AM_SAMPLE2_PROPERTIES props;
    IMemAllocator * pParent;
    struct list listentry;
    LONGLONG tMediaStart;
    LONGLONG tMediaEnd;
} StdMediaSample2;

45
typedef struct BaseMemAllocator
46
{
47
    IMemAllocator IMemAllocator_iface;
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

    LONG ref;
    ALLOCATOR_PROPERTIES props;
    HRESULT (* fnAlloc) (IMemAllocator *);
    HRESULT (* fnFree)(IMemAllocator *);
    HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *);
    HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD flags);
    HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *);
    void (* fnDestroyed)(IMemAllocator *);
    HANDLE hSemWaiting;
    BOOL bDecommitQueued;
    BOOL bCommitted;
    LONG lWaiting;
    struct list free_list;
    struct list used_list;
    CRITICAL_SECTION *pCritSect;
} BaseMemAllocator;
65

66 67 68 69 70
static inline BaseMemAllocator *impl_from_IMemAllocator(IMemAllocator *iface)
{
    return CONTAINING_RECORD(iface, BaseMemAllocator, IMemAllocator_iface);
}

71 72
static const IMemAllocatorVtbl BaseMemAllocator_VTable;
static const IMediaSample2Vtbl StdMediaSample2_VTable;
73
static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface);
74

75
#define AM_SAMPLE2_PROP_SIZE_WRITABLE FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer)
76

77
#define INVALID_MEDIA_TIME (((ULONGLONG)0x7fffffff << 32) | 0xffffffff)
78

79 80 81 82 83 84 85 86
static HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *),
                                     HRESULT (* fnFree)(IMemAllocator *),
                                     HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *),
                                     HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD),
                                     HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *),
                                     void (* fnDestroyed)(IMemAllocator *),
                                     CRITICAL_SECTION *pCritSect,
                                     BaseMemAllocator * pMemAlloc)
87
{
88
    assert(fnAlloc && fnFree && fnDestroyed);
89

90
    pMemAlloc->IMemAllocator_iface.lpVtbl = &BaseMemAllocator_VTable;
91

92
    pMemAlloc->ref = 1;
93
    ZeroMemory(&pMemAlloc->props, sizeof(pMemAlloc->props));
94 95 96 97
    list_init(&pMemAlloc->free_list);
    list_init(&pMemAlloc->used_list);
    pMemAlloc->fnAlloc = fnAlloc;
    pMemAlloc->fnFree = fnFree;
98 99 100 101
    pMemAlloc->fnVerify = fnVerify;
    pMemAlloc->fnBufferPrepare = fnBufferPrepare;
    pMemAlloc->fnBufferReleased = fnBufferReleased;
    pMemAlloc->fnDestroyed = fnDestroyed;
102 103 104 105
    pMemAlloc->bDecommitQueued = FALSE;
    pMemAlloc->bCommitted = FALSE;
    pMemAlloc->hSemWaiting = NULL;
    pMemAlloc->lWaiting = 0;
106
    pMemAlloc->pCritSect = pCritSect;
107 108 109 110 111 112

    return S_OK;
}

static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv)
{
113
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
114
    TRACE("(%p)->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv);
115 116 117 118

    *ppv = NULL;

    if (IsEqualIID(riid, &IID_IUnknown))
119
        *ppv = This;
120
    else if (IsEqualIID(riid, &IID_IMemAllocator))
121
        *ppv = This;
122 123 124 125 126 127 128 129 130 131 132 133 134 135

    if (*ppv)
    {
        IUnknown_AddRef((IUnknown *)(*ppv));
        return S_OK;
    }

    FIXME("No interface for %s!\n", qzdebugstr_guid(riid));

    return E_NOINTERFACE;
}

static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface)
{
136
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
137
    ULONG ref = InterlockedIncrement(&This->ref);
138

139
    TRACE("(%p)->() AddRef from %d\n", iface, ref - 1);
140

141
    return ref;
142 143 144 145
}

static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface)
{
146
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
147
    ULONG ref = InterlockedDecrement(&This->ref);
148

149
    TRACE("(%p)->() Release from %d\n", iface, ref + 1);
150

151
    if (!ref)
152 153 154 155
    {
        CloseHandle(This->hSemWaiting);
        if (This->bCommitted)
            This->fnFree(iface);
156 157

        This->fnDestroyed(iface);
158 159
        return 0;
    }
160
    return ref;
161 162 163 164
}

static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual)
{
165
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
166 167
    HRESULT hr;

168
    TRACE("(%p)->(%p, %p)\n", This, pRequest, pActual);
169

170
    EnterCriticalSection(This->pCritSect);
171 172 173 174 175
    {
        if (!list_empty(&This->used_list))
            hr = VFW_E_BUFFERS_OUTSTANDING;
        else if (This->bCommitted)
            hr = VFW_E_ALREADY_COMMITTED;
176 177
        else if (pRequest->cbAlign == 0)
            hr = VFW_E_BADALIGN;
178 179
        else
        {
180 181
            if (This->fnVerify)
                 hr = This->fnVerify(iface, pRequest);
182
            else
183
                 hr = S_OK;
184

185 186
            if (SUCCEEDED(hr))
                 This->props = *pRequest;
187

188
            *pActual = This->props;
189 190
        }
    }
191
    LeaveCriticalSection(This->pCritSect);
192 193 194 195 196 197

    return hr;
}

static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps)
{
198
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
199 200
    HRESULT hr = S_OK;

201
    TRACE("(%p)->(%p)\n", This, pProps);
202

203
    EnterCriticalSection(This->pCritSect);
204
    {
205
         memcpy(pProps, &This->props, sizeof(*pProps));
206
    }
207
    LeaveCriticalSection(This->pCritSect);
208 209 210 211 212 213

    return hr;
}

static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface)
{
214
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
215 216
    HRESULT hr;

217
    TRACE("(%p)->()\n", This);
218

219
    EnterCriticalSection(This->pCritSect);
220
    {
221 222 223
        if (!This->props.cbAlign)
            hr = VFW_E_BADALIGN;
        else if (!This->props.cbBuffer)
224
            hr = VFW_E_SIZENOTSET;
225 226
        else if (!This->props.cBuffers)
            hr = VFW_E_BUFFER_NOTSET;
227
        else if (This->bDecommitQueued && This->bCommitted)
228 229 230 231
        {
            This->bDecommitQueued = FALSE;
            hr = S_OK;
        }
232 233
        else if (This->bCommitted)
            hr = S_OK;
234 235
        else
        {
236
            if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->props.cBuffers, This->props.cBuffers, NULL)))
237
            {
238
                ERR("Couldn't create semaphore (error was %u)\n", GetLastError());
239 240 241 242 243 244 245 246
                hr = HRESULT_FROM_WIN32(GetLastError());
            }
            else
            {
                hr = This->fnAlloc(iface);
                if (SUCCEEDED(hr))
                    This->bCommitted = TRUE;
                else
247
                    ERR("fnAlloc failed with error 0x%x\n", hr);
248 249 250
            }
        }
    }
251
    LeaveCriticalSection(This->pCritSect);
252 253 254 255 256 257

    return hr;
}

static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface)
{
258
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
259 260
    HRESULT hr;

261
    TRACE("(%p)->()\n", This);
262

263
    EnterCriticalSection(This->pCritSect);
264 265
    {
        if (!This->bCommitted)
266
            hr = S_OK;
267 268 269 270 271 272 273 274 275 276 277 278
        else
        {
            if (!list_empty(&This->used_list))
            {
                This->bDecommitQueued = TRUE;
                /* notify ALL waiting threads that they cannot be allocated a buffer any more */
                ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL);
                
                hr = S_OK;
            }
            else
            {
279 280
                if (This->lWaiting != 0)
                    ERR("Waiting: %d\n", This->lWaiting);
281 282 283 284 285 286 287

                This->bCommitted = FALSE;
                CloseHandle(This->hSemWaiting);
                This->hSemWaiting = NULL;

                hr = This->fnFree(iface);
                if (FAILED(hr))
288
                    ERR("fnFree failed with error 0x%x\n", hr);
289 290 291
            }
        }
    }
292
    LeaveCriticalSection(This->pCritSect);
293 294 295 296 297 298

    return hr;
}

static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags)
{
299
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
300 301 302 303 304
    HRESULT hr = S_OK;

    /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample. 
     * The allocator might use these values to determine which buffer it retrieves */
    
305
    TRACE("(%p)->(%p, %p, %p, %x)\n", This, pSample, pStartTime, pEndTime, dwFlags);
306 307 308

    *pSample = NULL;

309 310
    EnterCriticalSection(This->pCritSect);
    if (!This->bCommitted || This->bDecommitQueued)
311 312
    {
        WARN("Not committed\n");
313
        hr = VFW_E_NOT_COMMITTED;
314
    }
315 316 317 318 319
    else
        ++This->lWaiting;
    LeaveCriticalSection(This->pCritSect);
    if (FAILED(hr))
        return hr;
320 321 322

    if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0)
    {
323 324 325 326
        EnterCriticalSection(This->pCritSect);
        --This->lWaiting;
        LeaveCriticalSection(This->pCritSect);
        WARN("Timed out\n");
327 328 329
        return VFW_E_TIMEOUT;
    }

330
    EnterCriticalSection(This->pCritSect);
331
    {
332
        --This->lWaiting;
333 334 335 336 337 338
        if (!This->bCommitted)
            hr = VFW_E_NOT_COMMITTED;
        else if (This->bDecommitQueued)
            hr = VFW_E_TIMEOUT;
        else
        {
339
            StdMediaSample2 *ms;
340 341 342 343
            struct list * free = list_head(&This->free_list);
            list_remove(free);
            list_add_head(&This->used_list, free);

344 345 346
            ms = LIST_ENTRY(free, StdMediaSample2, listentry);
            assert(ms->ref == 0);
            *pSample = (IMediaSample *)&ms->IMediaSample2_iface;
347 348 349
            IMediaSample_AddRef(*pSample);
        }
    }
350
    LeaveCriticalSection(This->pCritSect);
351

352 353
    if (hr != S_OK)
        WARN("%08x\n", hr);
354 355 356 357 358
    return hr;
}

static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample)
{
359
    BaseMemAllocator *This = impl_from_IMemAllocator(iface);
360
    StdMediaSample2 * pStdSample = unsafe_impl_from_IMediaSample(pSample);
361
    HRESULT hr = S_OK;
362

363
    TRACE("(%p)->(%p)\n", This, pSample);
364 365 366 367 368

    /* FIXME: make sure that sample is currently on the used list */

    /* FIXME: we should probably check the ref count on the sample before freeing
     * it to make sure that it is not still in use */
369
    EnterCriticalSection(This->pCritSect);
370 371 372 373 374 375 376 377 378 379 380 381 382
    {
        if (!This->bCommitted)
            ERR("Releasing a buffer when the allocator is not committed?!?\n");

		/* remove from used_list */
        list_remove(&pStdSample->listentry);

        list_add_head(&This->free_list, &pStdSample->listentry);

        if (list_empty(&This->used_list) && This->bDecommitQueued && This->bCommitted)
        {
            HRESULT hrfree;

383 384
            if (This->lWaiting != 0)
                ERR("Waiting: %d\n", This->lWaiting);
385 386 387 388 389 390 391 392

            This->bCommitted = FALSE;
            This->bDecommitQueued = FALSE;

            CloseHandle(This->hSemWaiting);
            This->hSemWaiting = NULL;
            
            if (FAILED(hrfree = This->fnFree(iface)))
393
                ERR("fnFree failed with error 0x%x\n", hrfree);
394 395
        }
    }
396
    LeaveCriticalSection(This->pCritSect);
397 398

    /* notify a waiting thread that there is now a free buffer */
399
    if (This->hSemWaiting && !ReleaseSemaphore(This->hSemWaiting, 1, NULL))
400
    {
401
        ERR("ReleaseSemaphore failed with error %u\n", GetLastError());
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    return hr;
}

static const IMemAllocatorVtbl BaseMemAllocator_VTable = 
{
    BaseMemAllocator_QueryInterface,
    BaseMemAllocator_AddRef,
    BaseMemAllocator_Release,
    BaseMemAllocator_SetProperties,
    BaseMemAllocator_GetProperties,
    BaseMemAllocator_Commit,
    BaseMemAllocator_Decommit,
    BaseMemAllocator_GetBuffer,
    BaseMemAllocator_ReleaseBuffer
};

421
static HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample)
422 423 424 425 426 427
{
    assert(pbBuffer && pParent && (cbBuffer > 0));

    if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2))))
        return E_OUTOFMEMORY;

428
    (*ppSample)->IMediaSample2_iface.lpVtbl = &StdMediaSample2_VTable;
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    (*ppSample)->ref = 0;
    ZeroMemory(&(*ppSample)->props, sizeof((*ppSample)->props));

    /* NOTE: no need to AddRef as the parent is guaranteed to be around
     * at least as long as us and we don't want to create circular
     * dependencies on the ref count */
    (*ppSample)->pParent = pParent;
    (*ppSample)->props.cbData = sizeof(AM_SAMPLE2_PROPERTIES);
    (*ppSample)->props.cbBuffer = (*ppSample)->props.lActual = cbBuffer;
    (*ppSample)->props.pbBuffer = pbBuffer;
    (*ppSample)->tMediaStart = INVALID_MEDIA_TIME;
    (*ppSample)->tMediaEnd = 0;

    return S_OK;
}

445
static void StdMediaSample2_Delete(StdMediaSample2 * This)
446 447 448 449 450
{
    /* NOTE: does not remove itself from the list it belongs to */
    CoTaskMemFree(This);
}

451 452 453 454 455 456
static inline StdMediaSample2 *impl_from_IMediaSample2(IMediaSample2 * iface)
{
    return CONTAINING_RECORD(iface, StdMediaSample2, IMediaSample2_iface);
}

static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, void ** ppv)
457 458 459 460 461
{
    TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);

    *ppv = NULL;

462 463
    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMediaSample) ||
            IsEqualIID(riid, &IID_IMediaSample2))
464
    {
465 466
        *ppv = iface;
        IMediaSample2_AddRef(iface);
467 468 469 470 471 472 473 474 475
        return S_OK;
    }

    FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
    return E_NOINTERFACE;
}

static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface)
{
476
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
477
    ULONG ref = InterlockedIncrement(&This->ref);
478

479
    TRACE("(%p)->(): new ref = %d\n", This, ref);
480

481
    return ref;
482 483 484 485
}

static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface)
{
486
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
487
    ULONG ref = InterlockedDecrement(&This->ref);
488

489
    TRACE("(%p)->(): new ref = %d\n", This, ref);
490

491
    if (!ref)
492
    {
493 494 495 496
        if (This->pParent)
            IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface);
        else
            StdMediaSample2_Delete(This);
497
    }
498
    return ref;
499 500 501 502
}

static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer)
{
503
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
504

505
    TRACE("(%p)->(%p)\n", iface, ppBuffer);
506 507 508

    *ppBuffer = This->props.pbBuffer;

509 510 511 512 513 514
    if (!*ppBuffer)
    {
        ERR("Requested an unlocked surface and trying to lock regardless\n");
        return E_FAIL;
    }

515 516 517
    return S_OK;
}

518
static LONG WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface)
519
{
520
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
521 522 523 524 525 526 527 528

    TRACE("StdMediaSample2_GetSize()\n");

    return This->props.cbBuffer;
}

static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
{
529
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
530 531
    HRESULT hr;

532
    TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550

    if (!(This->props.dwSampleFlags & AM_SAMPLE_TIMEVALID))
        hr = VFW_E_SAMPLE_TIME_NOT_SET;
    else if (!(This->props.dwSampleFlags & AM_SAMPLE_STOPVALID))
    {
        *pStart = This->props.tStart;
        *pEnd = This->props.tStart + 1;
        
        hr = VFW_S_NO_STOP_TIME;
    }
    else
    {
        *pStart = This->props.tStart;
        *pEnd = This->props.tStop;

        hr = S_OK;
    }

551
    return hr;
552 553 554 555
}

static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
{
556
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
557

558
    TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

    if (pStart)
    {
        This->props.tStart = *pStart;
        This->props.dwSampleFlags |= AM_SAMPLE_TIMEVALID;
    }
    else
        This->props.dwSampleFlags &= ~AM_SAMPLE_TIMEVALID;

    if (pEnd)
    {
        This->props.tStop = *pEnd;
        This->props.dwSampleFlags |= AM_SAMPLE_STOPVALID;
    }
    else
        This->props.dwSampleFlags &= ~AM_SAMPLE_STOPVALID;

    return S_OK;
}

static HRESULT WINAPI StdMediaSample2_IsSyncPoint(IMediaSample2 * iface)
{
581
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
582

583
    TRACE("(%p)->()\n", iface);
584 585 586 587 588 589

    return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE;
}

static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint)
{
590
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
591

592
    TRACE("(%p)->(%s)\n", iface, bIsSyncPoint ? "TRUE" : "FALSE");
593

594 595 596 597
    if (bIsSyncPoint)
        This->props.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
    else
        This->props.dwSampleFlags &= ~AM_SAMPLE_SPLICEPOINT;
598 599 600 601 602 603

    return S_OK;
}

static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface)
{
604
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
605

606
    TRACE("(%p)->()\n", iface);
607 608 609 610 611 612

    return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE;
}

static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll)
{
613
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
614

615
    TRACE("(%p)->(%s)\n", iface, bIsPreroll ? "TRUE" : "FALSE");
616

617 618
    if (bIsPreroll)
        This->props.dwSampleFlags |= AM_SAMPLE_PREROLL;
619 620
    else
        This->props.dwSampleFlags &= ~AM_SAMPLE_PREROLL;
621 622 623 624 625 626

    return S_OK;
}

static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface)
{
627
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
628

629
    TRACE("(%p)->()\n", iface);
630 631 632 633 634 635

    return This->props.lActual;
}

static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len)
{
636
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
637

638
    TRACE("(%p)->(%d)\n", iface, len);
639 640

    if ((len > This->props.cbBuffer) || (len < 0))
641 642
    {
        WARN("Tried to set length to %d, while max is %d\n", len, This->props.cbBuffer);
643
        return VFW_E_BUFFER_OVERFLOW;
644
    }
645 646 647 648 649 650 651 652 653
    else
    {
        This->props.lActual = len;
        return S_OK;
    }
}

static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType)
{
654
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
655

656
    TRACE("(%p)->(%p)\n", iface, ppMediaType);
657

658 659 660 661
    if (!This->props.pMediaType) {
        /* Make sure we return a NULL pointer (required by native Quartz dll) */
        if (ppMediaType)
            *ppMediaType = NULL;
662
        return S_FALSE;
663
    }
664 665 666 667 668 669 670 671 672

    if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
        return E_OUTOFMEMORY;

    return CopyMediaType(*ppMediaType, This->props.pMediaType);
}

static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType)
{
673
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
674

675
    TRACE("(%p)->(%p)\n", iface, pMediaType);
676 677

    if (This->props.pMediaType)
678
    {
679
        FreeMediaType(This->props.pMediaType);
680 681 682 683 684
        This->props.pMediaType = NULL;
    }
    if (!pMediaType)
        return S_FALSE;
    if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
685 686 687 688 689 690 691
        return E_OUTOFMEMORY;

    return CopyMediaType(This->props.pMediaType, pMediaType);
}

static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface)
{
692
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
693

694
    TRACE("(%p)->()\n", iface);
695 696 697 698 699 700

    return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE;
}

static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity)
{
701
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
702

703
    TRACE("(%p)->(%s)\n", iface, bIsDiscontinuity ? "TRUE" : "FALSE");
704

705 706 707 708
    if (bIsDiscontinuity)
        This->props.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
    else
        This->props.dwSampleFlags &= ~AM_SAMPLE_DATADISCONTINUITY;
709 710 711 712 713 714

    return S_OK;
}

static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
{
715
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
716

717
    TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
718 719 720 721 722 723 724

    if (This->tMediaStart == INVALID_MEDIA_TIME)
        return VFW_E_MEDIA_TIME_NOT_SET;

    *pStart = This->tMediaStart;
    *pEnd = This->tMediaEnd;

725
    return S_OK;
726 727 728 729
}

static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
{
730
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
731

732
    TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

    if (pStart)
        This->tMediaStart = *pStart;
    else
        This->tMediaStart = INVALID_MEDIA_TIME;

    if (pEnd)
        This->tMediaEnd = *pEnd;
    else
        This->tMediaEnd = 0;

    return S_OK;
}

static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties)
{
749
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
750

751
    TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
752 753 754 755 756 757 758 759

    memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props)));

    return S_OK;
}

static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties)
{
760
    StdMediaSample2 *This = impl_from_IMediaSample2(iface);
761

762
    TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794

    /* NOTE: pbBuffer and cbBuffer are read-only */
    memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE));

    return S_OK;
}

static const IMediaSample2Vtbl StdMediaSample2_VTable = 
{
    StdMediaSample2_QueryInterface,
    StdMediaSample2_AddRef,
    StdMediaSample2_Release,
    StdMediaSample2_GetPointer,
    StdMediaSample2_GetSize,
    StdMediaSample2_GetTime,
    StdMediaSample2_SetTime,
    StdMediaSample2_IsSyncPoint,
    StdMediaSample2_SetSyncPoint,
    StdMediaSample2_IsPreroll,
    StdMediaSample2_SetPreroll,
    StdMediaSample2_GetActualDataLength,
    StdMediaSample2_SetActualDataLength,
    StdMediaSample2_GetMediaType,
    StdMediaSample2_SetMediaType,
    StdMediaSample2_IsDiscontinuity,
    StdMediaSample2_SetDiscontinuity,
    StdMediaSample2_GetMediaTime,
    StdMediaSample2_SetMediaTime,
    StdMediaSample2_GetProperties,
    StdMediaSample2_SetProperties
};

795 796 797 798 799 800 801 802 803 804
static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface)
{
    IMediaSample2 *iface2 = (IMediaSample2 *)iface;

    if (!iface)
        return NULL;
    assert(iface2->lpVtbl == &StdMediaSample2_VTable);
    return impl_from_IMediaSample2(iface2);
}

805 806 807
typedef struct StdMemAllocator
{
    BaseMemAllocator base;
808
    CRITICAL_SECTION csState;
809 810 811
    LPVOID pMemory;
} StdMemAllocator;

812 813 814 815 816
static inline StdMemAllocator *StdMemAllocator_from_IMemAllocator(IMemAllocator * iface)
{
    return CONTAINING_RECORD(iface, StdMemAllocator, base.IMemAllocator_iface);
}

817 818
static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface)
{
819
    StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
820 821
    StdMediaSample2 * pSample = NULL;
    SYSTEM_INFO si;
822
    LONG i;
823 824 825 826 827 828 829

    assert(list_empty(&This->base.free_list));

    /* check alignment */
    GetSystemInfo(&si);

    /* we do not allow a courser alignment than the OS page size */
830
    if ((si.dwPageSize % This->base.props.cbAlign) != 0)
831 832 833 834 835 836
        return VFW_E_BADALIGN;

    /* FIXME: each sample has to have its buffer start on the right alignment.
     * We don't do this at the moment */

    /* allocate memory */
837
    This->pMemory = VirtualAlloc(NULL, (This->base.props.cbBuffer + This->base.props.cbPrefix) * This->base.props.cBuffers, MEM_COMMIT, PAGE_READWRITE);
838

839 840 841
    if (!This->pMemory)
        return E_OUTOFMEMORY;

842
    for (i = This->base.props.cBuffers - 1; i >= 0; i--)
843 844
    {
        /* pbBuffer does not start at the base address, it starts at base + cbPrefix */
845
        BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.props.cbBuffer + This->base.props.cbPrefix) + This->base.props.cbPrefix;
846
        
847
        StdMediaSample2_Construct(pbBuffer, This->base.props.cbBuffer, iface, &pSample);
848 849 850 851 852 853 854 855 856

        list_add_head(&This->base.free_list, &pSample->listentry);
    }

    return S_OK;
}

static HRESULT StdMemAllocator_Free(IMemAllocator * iface)
{
857
    StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
858 859
    struct list * cursor;

860 861 862 863 864 865 866 867 868 869 870
    if (!list_empty(&This->base.used_list))
    {
        WARN("Freeing allocator with outstanding samples!\n");
        while ((cursor = list_head(&This->base.used_list)) != NULL)
        {
            StdMediaSample2 *pSample;
            list_remove(cursor);
            pSample = LIST_ENTRY(cursor, StdMediaSample2, listentry);
            pSample->pParent = NULL;
        }
    }
871 872 873 874 875 876 877 878 879 880

    while ((cursor = list_head(&This->base.free_list)) != NULL)
    {
        list_remove(cursor);
        StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry));
    }
    
    /* free memory */
    if (!VirtualFree(This->pMemory, 0, MEM_RELEASE))
    {
881
        ERR("Couldn't free memory. Error: %u\n", GetLastError());
882 883 884 885 886 887
        return HRESULT_FROM_WIN32(GetLastError());
    }

    return S_OK;
}

888 889
static void StdMemAllocator_Destroy(IMemAllocator *iface)
{
890
    StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
891 892 893 894 895 896 897

    This->csState.DebugInfo->Spare[0] = 0;
    DeleteCriticalSection(&This->csState);

    CoTaskMemFree(This);
}

898 899 900 901 902 903 904 905 906 907 908 909 910
HRESULT StdMemAllocator_create(LPUNKNOWN lpUnkOuter, LPVOID * ppv)
{
    StdMemAllocator * pMemAlloc;
    HRESULT hr;

    *ppv = NULL;
    
    if (lpUnkOuter)
        return CLASS_E_NOAGGREGATION;

    if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc))))
        return E_OUTOFMEMORY;

911 912 913
    InitializeCriticalSection(&pMemAlloc->csState);
    pMemAlloc->csState.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StdMemAllocator.csState");

914 915
    pMemAlloc->pMemory = NULL;

916
    if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, NULL, NULL, NULL, StdMemAllocator_Destroy, &pMemAlloc->csState, &pMemAlloc->base)))
917
        *ppv = pMemAlloc;
918 919 920 921 922
    else
        CoTaskMemFree(pMemAlloc);

    return hr;
}