/* * Implementation of CLSID_MemoryAllocator. * * hidenori@a2.ctktv.ne.jp */ #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 "debugtools.h" 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 ); }