| /* |
| * 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); |
| |
| typedef struct BaseMemAllocator |
| { |
| const IMemAllocatorVtbl * lpVtbl; |
| |
| 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; |
| |
| static const IMemAllocatorVtbl BaseMemAllocator_VTable; |
| static const IMediaSample2Vtbl StdMediaSample2_VTable; |
| |
| #define AM_SAMPLE2_PROP_SIZE_WRITABLE FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer) |
| |
| #define INVALID_MEDIA_TIME (((ULONGLONG)0x7fffffff << 32) | 0xffffffff) |
| |
| 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) |
| { |
| 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 = This; |
| else if (IsEqualIID(riid, &IID_IMemAllocator)) |
| *ppv = 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 |
| }; |
| |
| static 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; |
| } |
| |
| static 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 = This; |
| else if (IsEqualIID(riid, &IID_IMediaSample)) |
| *ppv = This; |
| else if (IsEqualIID(riid, &IID_IMediaSample2)) |
| *ppv = 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 = pMemAlloc; |
| else |
| CoTaskMemFree(pMemAlloc); |
| |
| return hr; |
| } |