| /* |
| * Implementation of CLSID_MemoryAllocator. |
| * |
| * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp> |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "config.h" |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "winerror.h" |
| #include "strmif.h" |
| #include "uuids.h" |
| #include "vfwmsgs.h" |
| |
| #include "wine/debug.h" |
| WINE_DEFAULT_DEBUG_CHANNEL(quartz); |
| |
| #include "quartz_private.h" |
| #include "memalloc.h" |
| |
| |
| /*************************************************************************** |
| * |
| * new/delete for CLSID_MemoryAllocator. |
| * |
| */ |
| |
| /* can I use offsetof safely? - FIXME? */ |
| static QUARTZ_IFEntry IFEntries[] = |
| { |
| { &IID_IMemAllocator, offsetof(CMemoryAllocator,memalloc)-offsetof(CMemoryAllocator,unk) }, |
| }; |
| |
| static void QUARTZ_DestroyMemoryAllocator(IUnknown* punk) |
| { |
| CMemoryAllocator_THIS(punk,unk); |
| |
| CMemoryAllocator_UninitIMemAllocator( This ); |
| } |
| |
| HRESULT QUARTZ_CreateMemoryAllocator(IUnknown* punkOuter,void** ppobj) |
| { |
| CMemoryAllocator* pma; |
| HRESULT hr; |
| |
| TRACE("(%p,%p)\n",punkOuter,ppobj); |
| |
| pma = (CMemoryAllocator*)QUARTZ_AllocObj( sizeof(CMemoryAllocator) ); |
| if ( pma == NULL ) |
| return E_OUTOFMEMORY; |
| |
| QUARTZ_IUnkInit( &pma->unk, punkOuter ); |
| hr = CMemoryAllocator_InitIMemAllocator( pma ); |
| if ( FAILED(hr) ) |
| { |
| QUARTZ_FreeObj( pma ); |
| return hr; |
| } |
| |
| pma->unk.pEntries = IFEntries; |
| pma->unk.dwEntries = sizeof(IFEntries)/sizeof(IFEntries[0]); |
| pma->unk.pOnFinalRelease = QUARTZ_DestroyMemoryAllocator; |
| |
| *ppobj = (void*)(&pma->unk); |
| |
| return S_OK; |
| } |
| |
| |
| /*************************************************************************** |
| * |
| * CMemoryAllocator::IMemAllocator |
| * |
| */ |
| |
| static HRESULT |
| IMemAllocator_LockUnusedBuffer(CMemoryAllocator* This,IMediaSample** ppSample) |
| { |
| HRESULT hr = E_FAIL; |
| LONG i; |
| |
| TRACE("(%p) try to enter critical section\n",This); |
| EnterCriticalSection( &This->csMem ); |
| TRACE("(%p) enter critical section\n",This); |
| |
| if ( This->pData == NULL || This->ppSamples == NULL || |
| This->prop.cBuffers <= 0 ) |
| { |
| hr = VFW_E_NOT_COMMITTED; |
| goto end; |
| } |
| |
| |
| for ( i = 0; i < This->prop.cBuffers; i++ ) |
| { |
| if ( This->ppSamples[i] == NULL ) |
| { |
| hr = VFW_E_NOT_COMMITTED; |
| goto end; |
| } |
| if ( This->ppSamples[i]->ref == 0 ) |
| { |
| *ppSample = (IMediaSample*)(This->ppSamples[i]); |
| IMediaSample_AddRef( *ppSample ); |
| hr = NOERROR; |
| goto end; |
| } |
| } |
| |
| hr = VFW_E_TIMEOUT; |
| end: |
| LeaveCriticalSection( &This->csMem ); |
| TRACE("(%p) leave critical section\n",This); |
| |
| return hr; |
| } |
| |
| /* TRUE = all samples are released */ |
| static BOOL |
| IMemAllocator_ReleaseUnusedBuffer(CMemoryAllocator* This) |
| { |
| LONG i; |
| BOOL bRet = TRUE; |
| |
| TRACE("(%p) try to enter critical section\n",This); |
| EnterCriticalSection( &This->csMem ); |
| TRACE("(%p) enter critical section\n",This); |
| |
| if ( This->pData == NULL || This->ppSamples == NULL || |
| This->prop.cBuffers <= 0 ) |
| goto end; |
| |
| for ( i = 0; i < This->prop.cBuffers; i++ ) |
| { |
| if ( This->ppSamples[i]->ref == 0 ) |
| { |
| QUARTZ_DestroyMemMediaSample( This->ppSamples[i] ); |
| This->ppSamples[i] = NULL; |
| } |
| else |
| { |
| bRet = FALSE; |
| } |
| } |
| |
| if ( bRet ) |
| { |
| QUARTZ_FreeMem(This->ppSamples); |
| This->ppSamples = NULL; |
| QUARTZ_FreeMem(This->pData); |
| This->pData = NULL; |
| } |
| |
| end: |
| LeaveCriticalSection( &This->csMem ); |
| TRACE("(%p) leave critical section\n",This); |
| |
| return bRet; |
| } |
| |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnQueryInterface(IMemAllocator* iface,REFIID riid,void** ppobj) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE("(%p)->()\n",This); |
| |
| return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj); |
| } |
| |
| static ULONG WINAPI |
| IMemAllocator_fnAddRef(IMemAllocator* iface) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE("(%p)->()\n",This); |
| |
| return IUnknown_AddRef(This->unk.punkControl); |
| } |
| |
| static ULONG WINAPI |
| IMemAllocator_fnRelease(IMemAllocator* iface) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE("(%p)->()\n",This); |
| |
| return IUnknown_Release(This->unk.punkControl); |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnSetProperties(IMemAllocator* iface,ALLOCATOR_PROPERTIES* pPropReq,ALLOCATOR_PROPERTIES* pPropActual) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| long padding; |
| HRESULT hr; |
| |
| TRACE( "(%p)->(%p,%p)\n", This, pPropReq, pPropActual ); |
| |
| if ( pPropReq == NULL || pPropActual == NULL ) |
| return E_POINTER; |
| if ( pPropReq->cBuffers <= 0 || |
| pPropReq->cbBuffer <= 0 || |
| pPropReq->cbAlign < 0 || |
| pPropReq->cbPrefix < 0 ) |
| { |
| TRACE("pPropReq is invalid\n"); |
| return E_INVALIDARG; |
| } |
| |
| if ( pPropReq->cbAlign == 0 || |
| ( pPropReq->cbAlign & (pPropReq->cbAlign-1) ) != 0 ) |
| { |
| WARN("cbAlign is invalid - %ld\n",pPropReq->cbAlign); |
| return VFW_E_BADALIGN; |
| } |
| |
| hr = NOERROR; |
| |
| EnterCriticalSection( &This->csMem ); |
| |
| if ( This->pData != NULL || This->ppSamples != NULL ) |
| { |
| /* if commited, properties must not be changed. */ |
| TRACE("already commited\n"); |
| hr = E_UNEXPECTED; |
| goto end; |
| } |
| |
| This->prop.cBuffers = pPropReq->cBuffers; |
| This->prop.cbBuffer = pPropReq->cbBuffer; |
| This->prop.cbAlign = pPropReq->cbAlign; |
| This->prop.cbPrefix = pPropReq->cbPrefix; |
| |
| if ( This->prop.cbAlign == 0 ) |
| This->prop.cbAlign = 1; |
| padding = This->prop.cbAlign - |
| ( (This->prop.cbBuffer+This->prop.cbPrefix) % This->prop.cbAlign ); |
| |
| This->prop.cbBuffer += padding; |
| |
| memcpy( pPropActual, &This->prop, sizeof(ALLOCATOR_PROPERTIES) ); |
| |
| end: |
| LeaveCriticalSection( &This->csMem ); |
| |
| TRACE("returned successfully.\n"); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnGetProperties(IMemAllocator* iface,ALLOCATOR_PROPERTIES* pProp) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE( "(%p)->(%p)\n", This, pProp ); |
| |
| if ( pProp == NULL ) |
| return E_POINTER; |
| |
| EnterCriticalSection( &This->csMem ); |
| |
| memcpy( pProp, &This->prop, sizeof(ALLOCATOR_PROPERTIES) ); |
| |
| LeaveCriticalSection( &This->csMem ); |
| |
| return NOERROR; |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnCommit(IMemAllocator* iface) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| HRESULT hr; |
| LONG lBufSize; |
| LONG i; |
| BYTE* pCur; |
| |
| TRACE( "(%p)->()\n", This ); |
| |
| EnterCriticalSection( &This->csMem ); |
| |
| hr = NOERROR; |
| /* FIXME - handle in Decommitting */ |
| if ( This->pData != NULL || This->ppSamples != NULL || |
| This->prop.cBuffers <= 0 ) |
| goto end; |
| |
| lBufSize = This->prop.cBuffers * |
| (This->prop.cbBuffer + This->prop.cbPrefix) + |
| This->prop.cbAlign; |
| if ( lBufSize <= 0 ) |
| lBufSize = 1; |
| |
| This->pData = (BYTE*)QUARTZ_AllocMem( lBufSize ); |
| if ( This->pData == NULL ) |
| { |
| hr = E_OUTOFMEMORY; |
| goto end; |
| } |
| |
| This->ppSamples = (CMemMediaSample**)QUARTZ_AllocMem( |
| sizeof(CMemMediaSample*) * This->prop.cBuffers ); |
| if ( This->ppSamples == NULL ) |
| { |
| hr = E_OUTOFMEMORY; |
| goto end; |
| } |
| |
| for ( i = 0; i < This->prop.cBuffers; i++ ) |
| This->ppSamples[i] = NULL; |
| |
| pCur = This->pData + This->prop.cbAlign - ((This->pData-(BYTE*)NULL) & (This->prop.cbAlign-1)); |
| |
| for ( i = 0; i < This->prop.cBuffers; i++ ) |
| { |
| hr = QUARTZ_CreateMemMediaSample( |
| pCur, (This->prop.cbBuffer + This->prop.cbPrefix), |
| iface, &This->ppSamples[i] ); |
| if ( FAILED(hr) ) |
| goto end; |
| pCur += (This->prop.cbBuffer + This->prop.cbPrefix); |
| } |
| |
| hr = NOERROR; |
| end: |
| if ( FAILED(hr) ) |
| IMemAllocator_Decommit(iface); |
| |
| LeaveCriticalSection( &This->csMem ); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnDecommit(IMemAllocator* iface) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE( "(%p)->()\n", This ); |
| |
| while ( 1 ) |
| { |
| ResetEvent( This->hEventSample ); |
| |
| /* to avoid deadlock, don't hold critical section while blocking */ |
| if ( IMemAllocator_ReleaseUnusedBuffer(This) ) |
| break; |
| |
| WaitForSingleObject( This->hEventSample, INFINITE ); |
| } |
| |
| return NOERROR; |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnGetBuffer(IMemAllocator* iface,IMediaSample** ppSample,REFERENCE_TIME* prtStart,REFERENCE_TIME* prtEnd,DWORD dwFlags) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| HRESULT hr; |
| |
| TRACE( "(%p)->(%p,%p,%p,%lu)\n", This, ppSample, prtStart, prtEnd, dwFlags ); |
| |
| if ( ppSample == NULL ) |
| return E_POINTER; |
| |
| while ( 1 ) |
| { |
| ResetEvent( This->hEventSample ); |
| |
| /* to avoid deadlock, don't hold critical section while blocking */ |
| hr = IMemAllocator_LockUnusedBuffer(This,ppSample); |
| if ( ( hr != VFW_E_TIMEOUT ) || ( dwFlags & AM_GBF_NOWAIT ) ) |
| goto end; |
| |
| WaitForSingleObject( This->hEventSample, INFINITE ); |
| } |
| |
| end: |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI |
| IMemAllocator_fnReleaseBuffer(IMemAllocator* iface,IMediaSample* pSample) |
| { |
| CMemoryAllocator_THIS(iface,memalloc); |
| |
| TRACE( "(%p)->(%p)\n", This, pSample ); |
| SetEvent( This->hEventSample ); |
| |
| return NOERROR; |
| } |
| |
| |
| |
| static ICOM_VTABLE(IMemAllocator) imemalloc = |
| { |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE |
| /* IUnknown fields */ |
| IMemAllocator_fnQueryInterface, |
| IMemAllocator_fnAddRef, |
| IMemAllocator_fnRelease, |
| /* IMemAllocator fields */ |
| IMemAllocator_fnSetProperties, |
| IMemAllocator_fnGetProperties, |
| IMemAllocator_fnCommit, |
| IMemAllocator_fnDecommit, |
| IMemAllocator_fnGetBuffer, |
| IMemAllocator_fnReleaseBuffer, |
| }; |
| |
| |
| HRESULT CMemoryAllocator_InitIMemAllocator( CMemoryAllocator* pma ) |
| { |
| TRACE("(%p)\n",pma); |
| |
| ICOM_VTBL(&pma->memalloc) = &imemalloc; |
| |
| ZeroMemory( &pma->prop, sizeof(pma->prop) ); |
| pma->hEventSample = (HANDLE)NULL; |
| pma->pData = NULL; |
| pma->ppSamples = NULL; |
| |
| pma->hEventSample = CreateEventA( NULL, TRUE, FALSE, NULL ); |
| if ( pma->hEventSample == (HANDLE)NULL ) |
| return E_OUTOFMEMORY; |
| |
| InitializeCriticalSection( &pma->csMem ); |
| |
| return NOERROR; |
| } |
| |
| void CMemoryAllocator_UninitIMemAllocator( CMemoryAllocator* pma ) |
| { |
| TRACE("(%p)\n",pma); |
| |
| IMemAllocator_Decommit( (IMemAllocator*)(&pma->memalloc) ); |
| |
| DeleteCriticalSection( &pma->csMem ); |
| |
| if ( pma->hEventSample != (HANDLE)NULL ) |
| CloseHandle( pma->hEventSample ); |
| } |
| |