|  | /* | 
|  | * 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 | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #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); | 
|  |  | 
|  | void dump_AM_SAMPLE2_PROPERTIES(const AM_SAMPLE2_PROPERTIES * pProps) | 
|  | { | 
|  | if (!pProps) | 
|  | { | 
|  | TRACE("AM_SAMPLE2_PROPERTIES: (null)\n"); | 
|  | return; | 
|  | } | 
|  | TRACE("\tcbData: %d\n", pProps->cbData); | 
|  | TRACE("\tdwTypeSpecificFlags: 0x%8x\n", pProps->dwTypeSpecificFlags); | 
|  | TRACE("\tdwSampleFlags: 0x%8x\n", pProps->dwSampleFlags); | 
|  | TRACE("\tlActual: %d\n", pProps->lActual); | 
|  | TRACE("\ttStart: %x%08x%s\n", (LONG)(pProps->tStart >> 32), (LONG)pProps->tStart, pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ? "" : " (not valid)"); | 
|  | TRACE("\ttStop: %x%08x%s\n", (LONG)(pProps->tStop >> 32), (LONG)pProps->tStop, pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ? "" : " (not valid)"); | 
|  | TRACE("\tdwStreamId: 0x%x\n", pProps->dwStreamId); | 
|  | TRACE("\tpMediaType: %p\n", pProps->pMediaType); | 
|  | TRACE("\tpbBuffer: %p\n", pProps->pbBuffer); | 
|  | TRACE("\tcbBuffer: %d\n", pProps->cbBuffer); | 
|  | } | 
|  |  | 
|  | static const IMemAllocatorVtbl BaseMemAllocator_VTable; | 
|  | static const IMediaSample2Vtbl StdMediaSample2_VTable; | 
|  |  | 
|  | #define AM_SAMPLE2_PROP_SIZE_WRITABLE (unsigned int)(&((AM_SAMPLE2_PROPERTIES *)0)->pbBuffer) | 
|  |  | 
|  | #define INVALID_MEDIA_TIME (((ULONGLONG)0x7fffffff << 32) | 0xffffffff) | 
|  |  | 
|  | 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) | 
|  | { | 
|  | assert(fnAlloc && fnFree && fnDestroyed); | 
|  |  | 
|  | pMemAlloc->lpVtbl = &BaseMemAllocator_VTable; | 
|  |  | 
|  | pMemAlloc->ref = 1; | 
|  | ZeroMemory(&pMemAlloc->props, sizeof(pMemAlloc->props)); | 
|  | list_init(&pMemAlloc->free_list); | 
|  | list_init(&pMemAlloc->used_list); | 
|  | pMemAlloc->fnAlloc = fnAlloc; | 
|  | pMemAlloc->fnFree = fnFree; | 
|  | pMemAlloc->fnVerify = fnVerify; | 
|  | pMemAlloc->fnBufferPrepare = fnBufferPrepare; | 
|  | pMemAlloc->fnBufferReleased = fnBufferReleased; | 
|  | pMemAlloc->fnDestroyed = fnDestroyed; | 
|  | pMemAlloc->bDecommitQueued = FALSE; | 
|  | pMemAlloc->bCommitted = FALSE; | 
|  | pMemAlloc->hSemWaiting = NULL; | 
|  | pMemAlloc->lWaiting = 0; | 
|  | pMemAlloc->pCritSect = pCritSect; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | TRACE("(%p)->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv); | 
|  |  | 
|  | *ppv = NULL; | 
|  |  | 
|  | if (IsEqualIID(riid, &IID_IUnknown)) | 
|  | *ppv = (LPVOID)This; | 
|  | else if (IsEqualIID(riid, &IID_IMemAllocator)) | 
|  | *ppv = (LPVOID)This; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p)->() AddRef from %d\n", iface, ref - 1); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p)->() Release from %d\n", iface, ref + 1); | 
|  |  | 
|  | if (!ref) | 
|  | { | 
|  | CloseHandle(This->hSemWaiting); | 
|  | if (This->bCommitted) | 
|  | This->fnFree(iface); | 
|  |  | 
|  | This->fnDestroyed(iface); | 
|  | return 0; | 
|  | } | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->(%p, %p)\n", This, pRequest, pActual); | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | if (!list_empty(&This->used_list)) | 
|  | hr = VFW_E_BUFFERS_OUTSTANDING; | 
|  | else if (This->bCommitted) | 
|  | hr = VFW_E_ALREADY_COMMITTED; | 
|  | else if (pRequest->cbAlign == 0) | 
|  | hr = VFW_E_BADALIGN; | 
|  | else | 
|  | { | 
|  | if (This->fnVerify) | 
|  | hr = This->fnVerify(iface, pRequest); | 
|  | else | 
|  | hr = S_OK; | 
|  |  | 
|  | if (SUCCEEDED(hr)) | 
|  | This->props = *pRequest; | 
|  |  | 
|  | *pActual = This->props; | 
|  | } | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | HRESULT hr = S_OK; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", This, pProps); | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | memcpy(pProps, &This->props, sizeof(*pProps)); | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->()\n", This); | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | if (!This->props.cbAlign) | 
|  | hr = VFW_E_BADALIGN; | 
|  | else if (!This->props.cbBuffer) | 
|  | hr = VFW_E_SIZENOTSET; | 
|  | else if (!This->props.cBuffers) | 
|  | hr = VFW_E_BUFFER_NOTSET; | 
|  | else if (This->bDecommitQueued && This->bCommitted) | 
|  | { | 
|  | This->bDecommitQueued = FALSE; | 
|  | hr = S_OK; | 
|  | } | 
|  | else if (This->bCommitted) | 
|  | hr = S_OK; | 
|  | else | 
|  | { | 
|  | if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->props.cBuffers, This->props.cBuffers, NULL))) | 
|  | { | 
|  | ERR("Couldn't create semaphore (error was %u)\n", GetLastError()); | 
|  | hr = HRESULT_FROM_WIN32(GetLastError()); | 
|  | } | 
|  | else | 
|  | { | 
|  | hr = This->fnAlloc(iface); | 
|  | if (SUCCEEDED(hr)) | 
|  | This->bCommitted = TRUE; | 
|  | else | 
|  | ERR("fnAlloc failed with error 0x%x\n", hr); | 
|  | } | 
|  | } | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p)->()\n", This); | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | if (!This->bCommitted) | 
|  | hr = S_OK; | 
|  | 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 | 
|  | { | 
|  | if (This->lWaiting != 0) | 
|  | ERR("Waiting: %d\n", This->lWaiting); | 
|  |  | 
|  | This->bCommitted = FALSE; | 
|  | CloseHandle(This->hSemWaiting); | 
|  | This->hSemWaiting = NULL; | 
|  |  | 
|  | hr = This->fnFree(iface); | 
|  | if (FAILED(hr)) | 
|  | ERR("fnFree failed with error 0x%x\n", hr); | 
|  | } | 
|  | } | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | 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 */ | 
|  |  | 
|  | TRACE("(%p)->(%p, %p, %p, %x)\n", This, pSample, pStartTime, pEndTime, dwFlags); | 
|  |  | 
|  | *pSample = NULL; | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | if (!This->bCommitted || This->bDecommitQueued) | 
|  | { | 
|  | WARN("Not committed\n"); | 
|  | hr = VFW_E_NOT_COMMITTED; | 
|  | } | 
|  | else | 
|  | ++This->lWaiting; | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0) | 
|  | { | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | --This->lWaiting; | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  | WARN("Timed out\n"); | 
|  | return VFW_E_TIMEOUT; | 
|  | } | 
|  |  | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | --This->lWaiting; | 
|  | if (!This->bCommitted) | 
|  | hr = VFW_E_NOT_COMMITTED; | 
|  | else if (This->bDecommitQueued) | 
|  | hr = VFW_E_TIMEOUT; | 
|  | else | 
|  | { | 
|  | struct list * free = list_head(&This->free_list); | 
|  | list_remove(free); | 
|  | list_add_head(&This->used_list, free); | 
|  |  | 
|  | *pSample = (IMediaSample *)LIST_ENTRY(free, StdMediaSample2, listentry); | 
|  |  | 
|  | assert(((StdMediaSample2 *)*pSample)->ref == 0); | 
|  |  | 
|  | IMediaSample_AddRef(*pSample); | 
|  | } | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | if (hr != S_OK) | 
|  | WARN("%08x\n", hr); | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample) | 
|  | { | 
|  | BaseMemAllocator *This = (BaseMemAllocator *)iface; | 
|  | StdMediaSample2 * pStdSample = (StdMediaSample2 *)pSample; | 
|  | HRESULT hr = S_OK; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", This, pSample); | 
|  |  | 
|  | /* 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 */ | 
|  | EnterCriticalSection(This->pCritSect); | 
|  | { | 
|  | 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; | 
|  |  | 
|  | if (This->lWaiting != 0) | 
|  | ERR("Waiting: %d\n", This->lWaiting); | 
|  |  | 
|  | This->bCommitted = FALSE; | 
|  | This->bDecommitQueued = FALSE; | 
|  |  | 
|  | CloseHandle(This->hSemWaiting); | 
|  | This->hSemWaiting = NULL; | 
|  |  | 
|  | if (FAILED(hrfree = This->fnFree(iface))) | 
|  | ERR("fnFree failed with error 0x%x\n", hrfree); | 
|  | } | 
|  | } | 
|  | LeaveCriticalSection(This->pCritSect); | 
|  |  | 
|  | /* notify a waiting thread that there is now a free buffer */ | 
|  | if (This->hSemWaiting && !ReleaseSemaphore(This->hSemWaiting, 1, NULL)) | 
|  | { | 
|  | ERR("ReleaseSemaphore failed with error %u\n", GetLastError()); | 
|  | 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 | 
|  | }; | 
|  |  | 
|  | HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample) | 
|  | { | 
|  | assert(pbBuffer && pParent && (cbBuffer > 0)); | 
|  |  | 
|  | if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2)))) | 
|  | return E_OUTOFMEMORY; | 
|  |  | 
|  | (*ppSample)->lpvtbl = &StdMediaSample2_VTable; | 
|  | (*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; | 
|  | } | 
|  |  | 
|  | void StdMediaSample2_Delete(StdMediaSample2 * This) | 
|  | { | 
|  | /* NOTE: does not remove itself from the list it belongs to */ | 
|  | CoTaskMemFree(This); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, LPVOID * ppv) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  | TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv); | 
|  |  | 
|  | *ppv = NULL; | 
|  |  | 
|  | if (IsEqualIID(riid, &IID_IUnknown)) | 
|  | *ppv = (LPVOID)This; | 
|  | else if (IsEqualIID(riid, &IID_IMediaSample)) | 
|  | *ppv = (LPVOID)This; | 
|  | else if (IsEqualIID(riid, &IID_IMediaSample2)) | 
|  | *ppv = (LPVOID)This; | 
|  |  | 
|  | if (*ppv) | 
|  | { | 
|  | IUnknown_AddRef((IUnknown *)(*ppv)); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); | 
|  |  | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p)->() AddRef from %d\n", iface, ref - 1); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p)->() Release from %d\n", iface, ref + 1); | 
|  |  | 
|  | if (!ref) | 
|  | { | 
|  | if (This->pParent) | 
|  | IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface); | 
|  | else | 
|  | StdMediaSample2_Delete(This); | 
|  | return 0; | 
|  | } | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", iface, ppBuffer); | 
|  |  | 
|  | *ppBuffer = This->props.pbBuffer; | 
|  |  | 
|  | if (!*ppBuffer) | 
|  | { | 
|  | ERR("Requested an unlocked surface and trying to lock regardless\n"); | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static long WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("StdMediaSample2_GetSize()\n"); | 
|  |  | 
|  | return This->props.cbBuffer; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd) | 
|  | { | 
|  | HRESULT hr; | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->()\n", iface); | 
|  |  | 
|  | return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%s)\n", iface, bIsSyncPoint ? "TRUE" : "FALSE"); | 
|  |  | 
|  | if (bIsSyncPoint) | 
|  | This->props.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT; | 
|  | else | 
|  | This->props.dwSampleFlags &= ~AM_SAMPLE_SPLICEPOINT; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->()\n", iface); | 
|  |  | 
|  | return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%s)\n", iface, bIsPreroll ? "TRUE" : "FALSE"); | 
|  |  | 
|  | if (bIsPreroll) | 
|  | This->props.dwSampleFlags |= AM_SAMPLE_PREROLL; | 
|  | else | 
|  | This->props.dwSampleFlags &= ~AM_SAMPLE_PREROLL; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->()\n", iface); | 
|  |  | 
|  | return This->props.lActual; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%d)\n", iface, len); | 
|  |  | 
|  | if ((len > This->props.cbBuffer) || (len < 0)) | 
|  | { | 
|  | WARN("Tried to set length to %d, while max is %d\n", len, This->props.cbBuffer); | 
|  | return VFW_E_BUFFER_OVERFLOW; | 
|  | } | 
|  | else | 
|  | { | 
|  | This->props.lActual = len; | 
|  | return S_OK; | 
|  | } | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", iface, ppMediaType); | 
|  |  | 
|  | if (!This->props.pMediaType) { | 
|  | /* Make sure we return a NULL pointer (required by native Quartz dll) */ | 
|  | if (ppMediaType) | 
|  | *ppMediaType = NULL; | 
|  | return S_FALSE; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", iface, pMediaType); | 
|  |  | 
|  | if (This->props.pMediaType) | 
|  | FreeMediaType(This->props.pMediaType); | 
|  | else if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)))) | 
|  | return E_OUTOFMEMORY; | 
|  |  | 
|  | return CopyMediaType(This->props.pMediaType, pMediaType); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->()\n", iface); | 
|  |  | 
|  | return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%s)\n", iface, bIsDiscontinuity ? "TRUE" : "FALSE"); | 
|  |  | 
|  | if (bIsDiscontinuity) | 
|  | This->props.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY; | 
|  | else | 
|  | This->props.dwSampleFlags &= ~AM_SAMPLE_DATADISCONTINUITY; | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); | 
|  |  | 
|  | if (This->tMediaStart == INVALID_MEDIA_TIME) | 
|  | return VFW_E_MEDIA_TIME_NOT_SET; | 
|  |  | 
|  | *pStart = This->tMediaStart; | 
|  | *pEnd = This->tMediaEnd; | 
|  |  | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties); | 
|  |  | 
|  | memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props))); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties) | 
|  | { | 
|  | StdMediaSample2 *This = (StdMediaSample2 *)iface; | 
|  |  | 
|  | TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties); | 
|  |  | 
|  | /* 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 | 
|  | }; | 
|  |  | 
|  | typedef struct StdMemAllocator | 
|  | { | 
|  | BaseMemAllocator base; | 
|  | CRITICAL_SECTION csState; | 
|  | LPVOID pMemory; | 
|  | } StdMemAllocator; | 
|  |  | 
|  | static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface) | 
|  | { | 
|  | StdMemAllocator *This = (StdMemAllocator *)iface; | 
|  | StdMediaSample2 * pSample = NULL; | 
|  | SYSTEM_INFO si; | 
|  | long i; | 
|  |  | 
|  | assert(list_empty(&This->base.free_list)); | 
|  |  | 
|  | /* check alignment */ | 
|  | GetSystemInfo(&si); | 
|  |  | 
|  | /* we do not allow a courser alignment than the OS page size */ | 
|  | if ((si.dwPageSize % This->base.props.cbAlign) != 0) | 
|  | 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 */ | 
|  | This->pMemory = VirtualAlloc(NULL, (This->base.props.cbBuffer + This->base.props.cbPrefix) * This->base.props.cBuffers, MEM_COMMIT, PAGE_READWRITE); | 
|  |  | 
|  | for (i = This->base.props.cBuffers - 1; i >= 0; i--) | 
|  | { | 
|  | /* pbBuffer does not start at the base address, it starts at base + cbPrefix */ | 
|  | BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.props.cbBuffer + This->base.props.cbPrefix) + This->base.props.cbPrefix; | 
|  |  | 
|  | StdMediaSample2_Construct(pbBuffer, This->base.props.cbBuffer, iface, &pSample); | 
|  |  | 
|  | list_add_head(&This->base.free_list, &pSample->listentry); | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT StdMemAllocator_Free(IMemAllocator * iface) | 
|  | { | 
|  | StdMemAllocator *This = (StdMemAllocator *)iface; | 
|  | struct list * cursor; | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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)) | 
|  | { | 
|  | ERR("Couldn't free memory. Error: %u\n", GetLastError()); | 
|  | return HRESULT_FROM_WIN32(GetLastError()); | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static void StdMemAllocator_Destroy(IMemAllocator *iface) | 
|  | { | 
|  | StdMemAllocator *This = (StdMemAllocator *)iface; | 
|  |  | 
|  | This->csState.DebugInfo->Spare[0] = 0; | 
|  | DeleteCriticalSection(&This->csState); | 
|  |  | 
|  | CoTaskMemFree(This); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | InitializeCriticalSection(&pMemAlloc->csState); | 
|  | pMemAlloc->csState.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StdMemAllocator.csState"); | 
|  |  | 
|  | pMemAlloc->pMemory = NULL; | 
|  |  | 
|  | if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, NULL, NULL, NULL, StdMemAllocator_Destroy, &pMemAlloc->csState, &pMemAlloc->base))) | 
|  | *ppv = (LPVOID)pMemAlloc; | 
|  | else | 
|  | CoTaskMemFree(pMemAlloc); | 
|  |  | 
|  | return hr; | 
|  | } |