diff -r 000000000000 -r 5d29cba61097 omxilvideocomps/omxilgraphicsink/src/omxilgraphicsinkprocessingfunction.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omxilvideocomps/omxilgraphicsink/src/omxilgraphicsinkprocessingfunction.cpp Fri Oct 08 22:09:17 2010 +0100 @@ -0,0 +1,1349 @@ +/* +* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +/** + @file + @internalComponent +*/ + +#include "log.h" + +#include "omxilgraphicsinkprocessingfunction.h" +#include "omxilgraphicsinktrace.h" +#include "omxilgraphicsinkpanics.h" +#include "omxilgraphicsinkvpb0port.h" +#include +#include +#include "omxilgraphicsinkextensionsindexes.h" +#include +#include + +// Constant numbers +#ifndef __WINSCW__ +const TInt KRefGfxAlignment = RSurfaceManager::EPageAligned; +#else +const TInt KRefGfxAlignment = 2; +#endif +static const TBool KRefGfxContiguous = ETrue; +static const TInt KSurfaceUpdateNumOfMessageSlots = 4; +static const TUint32 KNullTickCount = 0xFFFFFFFF; + + +/** +Create a new processing function object. + +@param aCallbacks The callback manager interface for processing function. + +@return A pointer to the processing function object to be created. +*/ +COmxILGraphicSinkProcessingFunction* +COmxILGraphicSinkProcessingFunction::NewL(MOmxILCallbackNotificationIf& aCallbacks) + { + COmxILGraphicSinkProcessingFunction* self = + new (ELeave)COmxILGraphicSinkProcessingFunction(aCallbacks); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +/** +Second phase construction for the processing function. Loads the device driver for surface manager and initializes the surface attributes. +*/ +void +COmxILGraphicSinkProcessingFunction::ConstructL() + { + iTransitionToPauseWait = new(ELeave) CActiveSchedulerWait(); + User::LeaveIfError(iTransitionToPauseWaitSemaphore.CreateLocal(0)); + + //record the ID of the creator thread for later use + iOwnerThreadId = RThread().Id(); + + iGraphicSurfaceAccess = CGraphicSurfaceAccess::NewL(*this); + iPFHelper = CPFHelper::NewL(*this, *iGraphicSurfaceAccess); + + User::LeaveIfError(iBufferMutex.CreateLocal()); + InitSurfaceAttributes(); + iState = OMX_StateLoaded; + } + +/** +Constructor of the class. + +@param aCallbacks The callback manager interface for processing function. +*/ +COmxILGraphicSinkProcessingFunction::COmxILGraphicSinkProcessingFunction( + MOmxILCallbackNotificationIf& aCallbacks) + : + COmxILProcessingFunction(aCallbacks) + { + HAL::Get(HALData::EFastCounterFrequency,iFastCounterFrequency); + } + +/** +Destructor of the class. +*/ +COmxILGraphicSinkProcessingFunction::~COmxILGraphicSinkProcessingFunction() + { + delete iTransitionToPauseWait; + iTransitionToPauseWaitSemaphore.Close(); + + // Check in case the driver has not been closed. That would happen in + // an scenario where the component is deleted while being in + // OMX_StateExecuting state. + if(iPFHelper && + (iState == OMX_StateInvalid || + iState == OMX_StateExecuting || + iState == OMX_StatePause)) + { + // Ignore error if the following call fails + iPFHelper->StopSync(); + } + + delete iPFHelper; + delete iGraphicSurfaceAccess; + + iSurfaceManager.Close(); + + // Buffer headers are not owned by the processing function + iBuffersToEmpty.Close(); + + iBufferMutex.Close(); + } + +/** +This method provides the state change information within the processing function so that appropriate action can be taken. +This state change information is provided by the FSM on behalf of the IL Client. + +@param aNewState The new state of FSM. + +@return OMX_ErrorNone if successful + OMX_ErrorInsufficientResources if fail to start GraphicSink frame acceptor + OMX_ErrorIncorrectStateTransition if unsupported state + Any other OpenMAX IL wide error code +*/ +OMX_ERRORTYPE +COmxILGraphicSinkProcessingFunction::StateTransitionIndication(TStateIndex aNewState) + { + switch(aNewState) + { + case EStateExecuting: + { + return iPFHelper->ExecuteAsync(); + } + case EStateInvalid: + { + return iPFHelper->StopAsync(); + } + case EStatePause: + { + // must be done immediately + OMX_ERRORTYPE omxErr = iPFHelper->Pause(); + if(omxErr == OMX_ErrorNone) + { + WaitForTransitionToPauseToFinish(); + } + return omxErr; + } + case EStateIdle: + { + iBufferMutex.Wait(); + iBuffersToEmpty.Reset(); + iBufferMutex.Signal(); + iState = OMX_StateIdle; + return OMX_ErrorNone; + } + case EStateLoaded: + case EStateWaitForResources: + { + return iPFHelper->StopAsync(); + } + case ESubStateLoadedToIdle: + { + // Open a channel to the surface manager logical device driver. + TInt err = iSurfaceManager.Open(); + if ( err != KErrNone) + { + return OMX_ErrorHardware; + } + + if (iPFHelper->OpenDevice() != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + /* + if (iGraphicSinkPort->ValidateStride() != OMX_ErrorNone) + { + return OMX_ErrorUnsupportedSetting; + } + return OMX_ErrorNone; + */ + return iGraphicSinkPort->ValidateStride(); + } + case ESubStateIdleToLoaded: + { + return iPFHelper->CloseDevice(); + } + case ESubStateExecutingToIdle: + { + // must be done immediately + return iPFHelper->StopAsync(); + } + case ESubStatePauseToIdle: + { + // Ignore these transitions... + return OMX_ErrorNone; + } + default: + { + return OMX_ErrorIncorrectStateTransition; + } + }; + } + +/** +Flushes all the buffers retained by the processing function and sends it to either IL Client or the Tunelled component, as the case may be. + +@param aPortIndex Port index used to flush buffers from a given port of the component. +@param aDirection This describes the direction of the port. + +@return OMX_ErrorNone if successful; + Any other OpenMAX IL wide error code; +*/ +OMX_ERRORTYPE +COmxILGraphicSinkProcessingFunction::BufferFlushingIndication( + TUint32 aPortIndex, + OMX_DIRTYPE aDirection) + { + iBufferMutex.Wait(); + if ((aPortIndex == OMX_ALL && aDirection == OMX_DirMax) || + (aPortIndex == 0 && aDirection == OMX_DirInput)) + { + // Send BufferDone notifications for each buffer... + if(iBufferOnScreen) + { + iCallbacks.BufferDoneNotification(iBufferOnScreen, 0, OMX_DirInput); + iBufferOnScreen = NULL; + } + const TUint bufferCount = iBuffersToEmpty.Count(); + OMX_BUFFERHEADERTYPE* pBufferHeader = 0; + for (TUint i=0; inTickCount = KNullTickCount; + iCallbacks. + BufferDoneNotification( + pBufferHeader, + pBufferHeader->nInputPortIndex, + OMX_DirInput + ); + } + // Empty buffer lists... + iBuffersToEmpty.Reset(); + + iBufferMutex.Signal(); + return OMX_ErrorNone; + } + else + { + iBufferMutex.Signal(); + return OMX_ErrorBadParameter; + } + } + +/** +Update the structure corresponding to the given index which belongs to the static parameters list. + +@param aParamIndex The index representing the desired structure to be updated. +@param aComponentParameterStructure A pointer to structure which has the desired settings that will be used to update the Processing function. + +@return OMX_ErrorNone if successful; + OMX_ErrorUnsupportedIndex if unsupported index; + OMX_ErrorUnsupportedSetting if pixel format is EUidPixelFormatUnknown; + Any other OpenMAX IL wide error code; +*/ +OMX_ERRORTYPE +COmxILGraphicSinkProcessingFunction::ParamIndication( + OMX_INDEXTYPE aParamIndex, + const TAny* apComponentParameterStructure) + { + DEBUG_PRINTF(_L8("COmxILGraphicSinkProcessingFunction::PortParamIndication")); + + switch(aParamIndex) + { + case OMX_IndexParamPortDefinition: + { + const OMX_PARAM_PORTDEFINITIONTYPE* portDefinition = static_cast(apComponentParameterStructure); + + // + // All the fields in SurfaceAttribute structure should be updated. + // + iGraphicSurfaceSettings.iSurfaceAttributes.iSize.iWidth = portDefinition->format.video.nFrameWidth; + iGraphicSurfaceSettings.iSurfaceAttributes.iSize.iHeight = portDefinition->format.video.nFrameHeight; + + // Need to convert OMX Color Format to TUidPixelFormat. + TUidPixelFormat pixelFormat = ConvertPixelFormat(portDefinition->format.video.eColorFormat); + if(pixelFormat == EUidPixelFormatUnknown) + { + return OMX_ErrorUnsupportedSetting; + } + else + { + iGraphicSurfaceSettings.iSurfaceAttributes.iPixelFormat = pixelFormat; + } + + iGraphicSurfaceSettings.iSurfaceAttributes.iBuffers = portDefinition->nBufferCountActual; + iGraphicSurfaceSettings.iSurfaceAttributes.iStride = portDefinition->format.video.nStride; + break; + } + case OMX_IndexParamVideoPortFormat: + { + const OMX_VIDEO_PARAM_PORTFORMATTYPE* videoPortFormat = static_cast(apComponentParameterStructure); + + // only OMX_COLOR_FORMATTYPE eColorFormat to be used for SurfaceAttributes.iPixelFormat + TUidPixelFormat pixelFormat = ConvertPixelFormat(videoPortFormat->eColorFormat); + if(pixelFormat == EUidPixelFormatUnknown) + { + return OMX_ErrorUnsupportedSetting; + } + else + { + iGraphicSurfaceSettings.iSurfaceAttributes.iPixelFormat = pixelFormat; + } + break; + } + default: + { + return OMX_ErrorUnsupportedIndex; + } + } + return OMX_ErrorNone; + } + +/** +Update the structure corresponding to the given index which belongs to the dynamic configuration list. + +@param aConfigIndex The index representing the desired structure to be updated. +@param aComponentConfigStructure A pointer to structure which has the desired settings that will be used to update the Processing function. + +@return OMX_ErrorNone if successful; + OMX_ErrorUnsupportedIndex if unsupported index; + OMX_ErrorUnsupportedSetting if SurfaceConfiguration returns error; + Any other OpenMAX IL wide error code; +*/ +OMX_ERRORTYPE +COmxILGraphicSinkProcessingFunction::ConfigIndication(OMX_INDEXTYPE aConfigIndex, + const TAny* apComponentConfigStructure) + { + DEBUG_PRINTF(_L8("COmxILGraphicSinkProcessingFunction::ConfigIndication")); + + TInt err = KErrNone; + + switch(aConfigIndex) + { + case OMX_SymbianIndexConfigSharedChunkMetadata: + { + const OMX_SYMBIAN_CONFIG_SHAREDCHUNKMETADATATYPE* + pSharedChunkMetadata + = static_cast< + const OMX_SYMBIAN_CONFIG_SHAREDCHUNKMETADATATYPE*>( + apComponentConfigStructure); + + iGraphicSurfaceAccess->iSharedChunkHandleId = pSharedChunkMetadata->nHandleId; + iGraphicSurfaceAccess->iSharedChunkThreadId = pSharedChunkMetadata->nOwnerThreadId; + + iGraphicSurfaceAccess->iIsLocalChunk = EFalse; + break; + } + case OMX_IndexConfigCommonScale: + { + const OMX_CONFIG_SCALEFACTORTYPE* scaleFactor = static_cast(apComponentConfigStructure); + + err = iGraphicSurfaceSettings.iSurfaceConfig.SetExtent(TRect(TSize(scaleFactor->xWidth, scaleFactor->xHeight))); + if(err != KErrNone) + { + return OMX_ErrorUnsupportedSetting; + } + + break; + } + case OMX_IndexConfigCommonOutputSize: + { + const OMX_FRAMESIZETYPE* frameSize = static_cast(apComponentConfigStructure); + + err = iGraphicSurfaceSettings.iSurfaceConfig.SetExtent(TRect(TSize(frameSize->nWidth, frameSize->nHeight))); + if(err != KErrNone) + { + return OMX_ErrorUnsupportedSetting; + } + + break; + } + case OMX_IndexConfigCommonInputCrop: + case OMX_IndexConfigCommonOutputCrop: + case OMX_IndexConfigCommonExclusionRect: + { + const OMX_CONFIG_RECTTYPE* rec = static_cast(apComponentConfigStructure); + + err = iGraphicSurfaceSettings.iSurfaceConfig.SetExtent(TRect(TPoint(rec->nTop, rec->nLeft), TSize(rec->nWidth, rec->nHeight))); + if(err != KErrNone) + { + return OMX_ErrorUnsupportedSetting; + } + + break; + } + default: + { + return OMX_ErrorUnsupportedIndex; + } + } + + return OMX_ErrorNone; + } + +void COmxILGraphicSinkProcessingFunction::SetSharedChunkBufConfig(TMMSharedChunkBufConfig aSharedChunkBufConfig) + { + iGraphicSurfaceAccess->iSharedChunkBufConfig = aSharedChunkBufConfig; + } + +void COmxILGraphicSinkProcessingFunction::GetSharedChunkMetadata( + OMX_U32& aHandleId, + OMX_U64& aThreadId) const + { + aHandleId = iGraphicSurfaceAccess->iSharedChunkHandleId; + aThreadId = iGraphicSurfaceAccess->iSharedChunkThreadId; + } + +/** +This method is invoked whenever the component is requested to emtpy/display the contents of the buffers passed as function arguments. + +@param apBufferHeader A pointer to buffer header. +@param aDirection provides the direction either input or output. This can be used as a further check whether buffers received are valid or not. + +@return OMX_ErrorNone if successful; + Any other OpenMAX IL wide error code; +*/ +OMX_ERRORTYPE +COmxILGraphicSinkProcessingFunction::BufferIndication( + OMX_BUFFERHEADERTYPE* apBufferHeader, + OMX_DIRTYPE aDirection) + { + if (aDirection != OMX_DirInput) + { + return OMX_ErrorBadParameter; + } + //The nTickCount is just internal here, stored temporarily. So it is count not time period. + apBufferHeader->nTickCount = User::FastCounter(); + iBufferMutex.Wait(); + OMX_ERRORTYPE ret = OMX_ErrorNone; + if (iBuffersToEmpty.Append(apBufferHeader) != KErrNone) + { + apBufferHeader->nTickCount = KNullTickCount; + ret = OMX_ErrorInsufficientResources; + } + else if (iState != OMX_StateExecuting) + { + // If Component not in an executing state delay processing buffer + ret = OMX_ErrorNone; + } + else if (iPFHelper->BufferIndication() != KErrNone) + { + apBufferHeader->nTickCount = KNullTickCount; + ret = OMX_ErrorInsufficientResources; + } + iBufferMutex.Signal(); + return ret; + } + +/** +This method is used to check whether the required buffer is held by the processing function or not. + +@param apBufferHeader A pointer to buffer header being searched. +@param aDirection provides the direction either input or output. This can be used as a further check whether buffers received are valid or not. + +@return OMX_TRUE if find the buffer; + OMX_FALSE if fail to find the buffer; +*/ +OMX_BOOL +COmxILGraphicSinkProcessingFunction::BufferRemovalIndication( + OMX_BUFFERHEADERTYPE* apBufferHeader, + OMX_DIRTYPE /* aDirection */) + { + TBool headerDeletionResult = ETrue; + + TInt headerIndexInArray = KErrNotFound; + iBufferMutex.Wait(); + if (KErrNotFound != + (headerIndexInArray = + iBuffersToEmpty.Find(apBufferHeader))) + { + iBuffersToEmpty.Remove(headerIndexInArray); + } + else if(iBufferOnScreen == apBufferHeader) + { + iBufferOnScreen = NULL; + } + else + { + headerDeletionResult = EFalse; + } + iBufferMutex.Signal(); + return (headerDeletionResult ? OMX_TRUE : OMX_FALSE); + } + +/** +This method creates COmxILMMBuffer class object. Also creates surface and allocates the chunks based on the number of buffers, +maps the surface in the given process. It also allocates the resources like creating the message queue and other necessary C +class objects. This method gets called when the component acts as buffer supplier. + +@param aPortSpecificBuffer gives the starting address of the specific buffer. +@param aPortPrivate gives the private data which is COmxILMMBuffer pointer in this case. + +@leave OMX_ErrorNone if successful; +@leave OMX_ErrorInsufficientResources if function returns KErrNoMemory; +@leave OMX_ErrorBadParameter if function returns errors except KErrNoMemory; +@leave Any other OpenMAX IL wide error code; +*/ +void COmxILGraphicSinkProcessingFunction::CreateBufferL(OMX_U8*& aPortSpecificBuffer, OMX_PTR& aPortPrivate, OMX_U32 aBufferCountActual) + { + iGraphicSurfaceAccess->CreateBufferL(aPortSpecificBuffer, aPortPrivate, aBufferCountActual); + } + +/** +Destroy MM buffer, close surface, and deallocate other resources like message queue and C class objects. This is called when component +acts as buffer supplier. +@param apPortPrivate gives the private data which is COmxILMMBuffer pointer in this case. +*/ +void COmxILGraphicSinkProcessingFunction::DestroyBuffer(OMX_PTR /*apPortPrivate*/) + { + if( iGraphicSurfaceAccess ) + { + iGraphicSurfaceAccess->iBufferIdGenerator--; + // to reset surface id in case client requests different settings. + if(iGraphicSurfaceAccess->iBufferIdGenerator == 0) + { + iGraphicSurfaceAccess->ResetSurfaceId(); + iGraphicSurfaceAccess->CloseChunk(); + } + } + } + +/** +Creates the surface by utilizing the buffers passed via application private data. It then maps the surface in the given process. +It also allocates the resources like creating the message queue and other necessary C class objects. This method gets called when +the component acts as non buffer supplier. + +@param aSizeBytes The size of buffer. +@param apBuffer gives the starting address of the specific buffer. +@param aAppPrivate provides the private data which is COmxILMMBuffer pointer in this case and holds details of buffers already allocated. +@param aBufferCountActual The actual number of buffers. + +@leave OMX_ErrorNone if successful; +@leave OMX_ErrorInsufficientResources if aPortPrivate is null or functions return KErrNoMemory; +@leave OMX_ErrorBadParameter if functions return errors except KErrNoMemory; +@leave Any other OpenMAX IL wide error code; +*/ +void COmxILGraphicSinkProcessingFunction::InitBufferL(OMX_U32 aSizeBytes, OMX_U8* apBuffer, OMX_U32 aBufferCountActual) + { + iGraphicSurfaceAccess->InitBufferL(aSizeBytes, apBuffer, aBufferCountActual); + } + +/** +Deallocate resources like message queue and C class objects. This is called when component acts as non buffer supplier. +*/ +void COmxILGraphicSinkProcessingFunction::DeInitBuffer() + { + // to reset surface id in case client requests different settings. + if(iGraphicSurfaceAccess) + { + iGraphicSurfaceAccess->ResetSurfaceId(); + iGraphicSurfaceAccess->CloseChunk(); + } + } + +/** +Initialise surface attribute structure. +*/ +void COmxILGraphicSinkProcessingFunction::InitSurfaceAttributes() + { + RSurfaceManager::TSurfaceCreationAttributes* attr = &iGraphicSurfaceSettings.iSurfaceAttributes; + + attr->iAlignment = KRefGfxAlignment; + attr->iContiguous = KRefGfxContiguous; + attr->iCacheAttrib = RSurfaceManager::ENotCached; + attr->iMappable = ETrue; + } + +TUidPixelFormat COmxILGraphicSinkProcessingFunction::ConvertPixelFormat(OMX_COLOR_FORMATTYPE aColorFormat) + { + switch(aColorFormat) + { + // OMX "Planar" formats not currently supported by GraphicSink since data comes in more than one buffer. + // "PackedPlanar" formats can be added easily provided the GCE backend supports them. + + case OMX_COLOR_Format16bitRGB565: + return EUidPixelFormatRGB_565; + + case OMX_COLOR_Format32bitARGB8888: + return EUidPixelFormatARGB_8888; + + case OMX_COLOR_FormatYCrYCb: + return EUidPixelFormatYUV_422Reversed; + + case OMX_COLOR_FormatCbYCrY: + return EUidPixelFormatYUV_422Interleaved; + + // Need to map color format to Symbian pixel format. + default: + { + return EUidPixelFormatUnknown; + } + } + } + +void COmxILGraphicSinkProcessingFunction::WaitForTransitionToPauseToFinish() + { + if(RThread().Id() == iOwnerThreadId) + { + //if the owner thread is the same thread as the one created the active objects in this processing function + //then we can wait by using CActiveSchedulerWait + DEBUG_PRINTF(_L8("GraphicSinkProcessingFunction::WaitForTransitionToPauseToFinish - blocking transition to pause with active scheduler wait now")); + iTransitionToPauseWait->Start(); + } + else + { + //if this is a thread different from the creator thread then semaphore is needed to block this thread until the transition + //to paused state completes + DEBUG_PRINTF(_L8("GraphicSinkProcessingFunction::WaitForTransitionToPauseToFinish - blocking thread with semaphore now")); + iTransitionToPauseWaitSemaphore.Wait(); + } + } + +void COmxILGraphicSinkProcessingFunction::TransitionToPauseFinished() + { + if(iTransitionToPauseWait->IsStarted()) + { + DEBUG_PRINTF(_L8("GraphicSinkProcessingFunction::TransitionToPauseFinished - unblocking transition to pause (active scheduler wait) now")); + iTransitionToPauseWait->AsyncStop(); + } + else + { + DEBUG_PRINTF(_L8("GraphicSinkProcessingFunction::TransitionToPauseFinished - unblocking transition to pause (semaphore) now")); + iTransitionToPauseWaitSemaphore.Signal(); + } + } + +COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess* COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::NewL(COmxILGraphicSinkProcessingFunction& aParent) + { + return new (ELeave) CGraphicSurfaceAccess(aParent); + } + +COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::CGraphicSurfaceAccess(COmxILGraphicSinkProcessingFunction& aParent) +: CActive(EPriorityStandard), + iIsLocalChunk(ETrue), + iParent(aParent) + { + CActiveScheduler::Add(this); + iSurfaceId = TSurfaceId::CreateNullId(); + iOffsetArray.Reset(); + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::CloseChunk() + { + if(iChunk.Handle()) + { + iChunk.Close(); + } + } + +COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::~CGraphicSurfaceAccess() + { + Cancel(); + + CloseChunk(); + + if (!iSurfaceId.IsNull()) + { + iParent.SurfaceManager().CloseSurface(iSurfaceId); // ignore the error + } + #ifdef ILCOMPONENTCONFORMANCE + iArrayOffsets.Close(); + #endif + + iOffsetArray.Close(); + iSurfaceUpdateSession.Close(); + + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::RunL() + { + // The buffer is not on the list implies that they have already been flushed/spotted + // via BufferFlushingIndication/BufferRemovalIndication + iParent.iBufferMutex.Wait(); + TInt index = iParent.BuffersToEmpty().Find(iCurrentBuffer); + if (KErrNotFound != index) + { + switch(iStatus.Int()) + { + case KErrNone: + { + // Consumed all data completely and setting nFilledLen to zero. + iCurrentBuffer->nFilledLen = 0; + break; + } + case KErrCancel: + default: + { + // Leave actual value of iCurrentBuffer->nFilledLen + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::RunL() err = %d"), iStatus.Int()); + } + }; + + if(iStatus.Int() != KErrNone) + { + iCurrentBuffer->nTickCount = KNullTickCount; + } + else + { + TUint32 currentTickCount = User::FastCounter(); + + // On some hardware boards, tick count decrements as time increases so + // need to check which way the counter is going + if (currentTickCount >= iCurrentBuffer->nTickCount) + { + iCurrentBuffer->nTickCount = (currentTickCount - iCurrentBuffer->nTickCount) / iParent.GetFastCounterFrequency(); + } + else + { + iCurrentBuffer->nTickCount = (iCurrentBuffer->nTickCount - currentTickCount) / iParent.GetFastCounterFrequency(); + } + } + + + if(iCurrentBuffer->nFlags & OMX_BUFFERFLAG_EOS) + { + iParent.GetCallbacks().EventNotification(OMX_EventBufferFlag, iCurrentBuffer->nInputPortIndex, iCurrentBuffer->nFlags, NULL); + } + + iCurrentBuffer->nFilledLen = 0; + iCurrentBuffer->nFlags = 0; + iCurrentBuffer->nOffset = 0; + iCurrentBuffer->nTimeStamp = 0; + + // now sending back to framework.. + if(iParent.iBufferOnScreen) + { + // Add error handling? + iParent.GetCallbacks().BufferDoneNotification(iParent.iBufferOnScreen, iParent.iBufferOnScreen->nInputPortIndex,OMX_DirInput); + } + iParent.iBufferOnScreen = iCurrentBuffer; + + iParent.BuffersToEmpty().Remove(index); + iCurrentBuffer = NULL; + + // check if any more buffers to be consumed.. + if (ProcessNextBuffer() != KErrNone) + { + iParent.GetCallbacks().ErrorEventNotification(OMX_ErrorInsufficientResources); + } + } + iParent.iBufferMutex.Signal(); + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::CreateBufferL(OMX_U8*& aPortSpecificBuffer, OMX_PTR& aPortPrivate, OMX_U32 aBufferCountActual) + { + if(iSurfaceId.IsNull()) + { + // race condition on nBufferCountActual + if(aBufferCountActual != iParent.GraphicSurfaceSettings().iSurfaceAttributes.iBuffers) + { + iParent.GraphicSurfaceSettings().iSurfaceAttributes.iBuffers = aBufferCountActual; + } + + User::LeaveIfError(iParent.SurfaceManager().CreateSurface(iParent.GraphicSurfaceSettings().iSurfaceAttributesBuf, iSurfaceId)); + + RChunk chunk; + CleanupClosePushL(chunk); + User::LeaveIfError(iParent.SurfaceManager().MapSurface(iSurfaceId, chunk)); + + //We need to change the chunk handle to be shared by the whole process. + RThread thread; + CleanupClosePushL(thread); + iChunk.SetHandle(chunk.Handle()); + User::LeaveIfError(iChunk.Duplicate(thread)); + CleanupStack::PopAndDestroy(2, &chunk); + + // for SetConfig(OMX_SYMBIAN_CONFIG_SHARED_CHUNK_METADATA) + iSharedChunkHandleId = iChunk.Handle(); + iSharedChunkThreadId = RThread().Id().Id(); + + switch(iParent.iGraphicSurfaceSettings.iSurfaceAttributes.iPixelFormat) + { + case EUidPixelFormatYUV_422Reversed: + { + // fill buffer 0 with black + TUint32* data = reinterpret_cast(iChunk.Base()); + TInt numPixelPairs = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iStride * iParent.GraphicSurfaceSettings().iSurfaceAttributes.iSize.iHeight / 4; + for(TInt offset = 0; offset < numPixelPairs; offset++) + { + data[offset] = 0x80108010; + } + } + break; + case EUidPixelFormatYUV_422Interleaved: + { + // fill buffer 0 with black + TUint32* data = reinterpret_cast(iChunk.Base()); + TInt numPixelPairs = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iStride * iParent.GraphicSurfaceSettings().iSurfaceAttributes.iSize.iHeight / 4; + for(TInt offset = 0; offset < numPixelPairs; offset++) + { + data[offset] = 0x10801080; + } + } + break; + case EUidPixelFormatRGB_565: + case EUidPixelFormatARGB_8888: + Mem::FillZ(iChunk.Base(), iParent.GraphicSurfaceSettings().iSurfaceAttributes.iStride * iParent.GraphicSurfaceSettings().iSurfaceAttributes.iSize.iHeight); + break; + default: +#ifdef _DEBUG + // Panic in a debug build. It will make people think about how the error should be handled. + Panic(EUndefinedPixelFormat); +#endif + break; + } + + // Now, GFX needs to make sure that TSurfaceConfiguration has valid surface id + // so that IL Client can use RSurfaceManager::SetBackroundSurface() + User::LeaveIfError(iParent.GraphicSurfaceSettings().iSurfaceConfig.SetSurfaceId(iSurfaceId)); + iParent.iCallbacks.EventNotification(OMX_EventPortSettingsChanged, OMX_NokiaIndexParamGraphicSurfaceConfig, 0, NULL); + + chunk.Close(); + #ifdef ILCOMPONENTCONFORMANCE + iIsBufferSupplier = ETrue; + #endif + } + + ASSERT(iChunk.Handle()); + + + RSurfaceManager::TInfoBuf surfaceInfoBuf; + RSurfaceManager::TSurfaceInfoV01& surfaceInfo (surfaceInfoBuf()); + User::LeaveIfError(iParent.SurfaceManager().SurfaceInfo(iSurfaceId, surfaceInfoBuf)); + for (TInt i = 0 ; i < surfaceInfo.iBuffers ; i++) + { + TInt offset = 0; + User::LeaveIfError(iParent.SurfaceManager().GetBufferOffset(iSurfaceId, i, offset)); + + if(iBufferIdGenerator == 0) + { + iOffsetArray.AppendL(offset); + } + } + + aPortSpecificBuffer = iChunk.Base() + iOffsetArray[iBufferIdGenerator]; + aPortPrivate = NULL; + + iBufferIdGenerator++; + } + + +#ifndef ILCOMPONENTCONFORMANCE +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL(OMX_U32 aSizeBytes, OMX_U8* apBuffer, OMX_U32 aBufferCountActual) +{ + // open chunk at the beginning + if(iChunk.Handle() == NULL) + { + // only support chunk extension otherwise error + if(iSharedChunkThreadId == NULL || iSharedChunkHandleId == NULL) + { + DEBUG_PRINTF(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL handles not valie")); + User::Leave(KErrBadHandle); + } + + // race condition on nBufferCountActual + if(aBufferCountActual != iParent.GraphicSurfaceSettings().iSurfaceAttributes.iBuffers) + { + iParent.GraphicSurfaceSettings().iSurfaceAttributes.iBuffers = aBufferCountActual; + } + + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL iSharedChunkThreadId = %Lu"), iSharedChunkThreadId); + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL iSharedChunkHandleId = %Lu"), iSharedChunkHandleId); + + RThread chunkOwnerThread; + User::LeaveIfError(chunkOwnerThread.Open(TThreadId(iSharedChunkThreadId))); + CleanupClosePushL(chunkOwnerThread); + + iChunk.SetHandle(iSharedChunkHandleId); + User::LeaveIfError(iChunk.Duplicate(chunkOwnerThread)); + CleanupStack::PopAndDestroy(&chunkOwnerThread); + } + + // differ creating surface id with chunk at last call + if(iSurfaceId.IsNull() && (((aBufferCountActual - 1) == iOffsetArray.Count()))) + { + // Buffer size must be > 0! + if( aSizeBytes == 0 ) + { + User::Leave( KErrArgument ); + } + + // Update surface attributes using the buffer size supplied by the client. + // The supplied buffer size is used to create the graphics surface and must + // therefore be large enough to accommodate any meta-data too. + iParent.GraphicSurfaceSettings().iSurfaceAttributes.iOffsetBetweenBuffers = aSizeBytes; + + TInt err = KErrGeneral; + // create surface id with chunk + err = iParent.SurfaceManager().CreateSurface(iParent.GraphicSurfaceSettings().iSurfaceAttributesBuf, iSurfaceId, iChunk); + if(err != KErrNone) + { + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL createsurface err = %d"), err); + User::Leave(err); + } + + // Now, GFX needs to make sure that TSurfaceConfiguration has valid surface id + err = iParent.GraphicSurfaceSettings().iSurfaceConfig.SetSurfaceId(iSurfaceId); + if(err != KErrNone) + { + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL SetSurfaceId err = %d"), err); + User::Leave(err); + } + + RSurfaceManager::TInfoBuf surfaceInfoBuf; + RSurfaceManager::TSurfaceInfoV01& surfaceInfo (surfaceInfoBuf()); + surfaceInfo.iSize = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iSize; + surfaceInfo.iBuffers = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iBuffers; + surfaceInfo.iPixelFormat = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iPixelFormat; + surfaceInfo.iStride = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iStride; + surfaceInfo.iContiguous = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iContiguous; + surfaceInfo.iCacheAttrib = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iCacheAttrib; + surfaceInfo.iMappable = iParent.GraphicSurfaceSettings().iSurfaceAttributes.iMappable; + + err = iParent.SurfaceManager().SurfaceInfo(iSurfaceId, surfaceInfoBuf); + if(err != KErrNone) + { + DEBUG_PRINTF2(_L8("COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL SurfaceInfo err = %d"), err); + User::Leave(err); + } + + // everything is fine and now ready to rock and roll... + iParent.iCallbacks.EventNotification(OMX_EventPortSettingsChanged, OMX_NokiaIndexParamGraphicSurfaceConfig, 0, NULL); + } + + // save offsets + TInt offset = apBuffer - iChunk.Base(); + iOffsetArray.AppendL(offset); + } + +#else + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::InitBufferL(OMX_U32 /*aSizeBytes*/, OMX_U8* /*apBuffer*/, OMX_U32 aBufferCountActual) + { + if(iSurfaceId.IsNull()) + { + User::LeaveIfError(iParent.SurfaceManager().CreateSurface(iParent.GraphicSurfaceSettings().iSurfaceAttributesBuf, iSurfaceId)); + + RChunk chunk; + User::LeaveIfError(iParent.SurfaceManager().MapSurface(iSurfaceId, chunk)); + CleanupClosePushL(chunk); + + //We need to change the chunk handle to be shared by the whole process. + RThread thread; + CleanupClosePushL(thread); + iChunk.SetHandle(chunk.Handle()); + User::LeaveIfError(iChunk.Duplicate(thread)); + CleanupStack::PopAndDestroy(2, &chunk); + + // Now, GFX needs to make sure that TSurfaceConfiguration has valid surface id + // so that IL Client can use RSurfaceManager::SetBackroundSurface() + User::LeaveIfError(iParent.GraphicSurfaceSettings().iSurfaceConfig.SetSurfaceId(iSurfaceId)); + iParent.iCallbacks.EventNotification(OMX_EventPortSettingsChanged, OMX_NokiaIndexParamGraphicSurfaceConfig, 0, NULL); + + iIsBufferSupplier = EFalse; + + for(TInt i = 0 ; i < aBufferCountActual ; i++) + { + TInt offset = 0; + User::LeaveIfError(iParent.SurfaceManager().GetBufferOffset(iSurfaceId, i, offset)); + iArrayOffsets.AppendL(offset); + } + } + + } +#endif //ILCOMPONENTCONFORMANCE + +TInt COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::ProcessNextBuffer() + { + __ASSERT_DEBUG(iParent.iBufferMutex.IsHeld(), User::Invariant()); + + TInt err = KErrNone; + + if ((iParent.BuffersToEmpty().Count()>0) && !IsActive() && iParent.State() == OMX_StateExecuting) + { + iCurrentBuffer = iParent.BuffersToEmpty()[0]; + TInt bufferId = KErrNotFound; + + if (iCurrentBuffer->nFilledLen == 0) + { + // Nothing in the buffer so no need to display it. The buffer might have a flag + // that needs processing though. Self complete to keep the state machine running. + iStatus = KRequestPending; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + return KErrNone; + } + +#ifdef ILCOMPONENTCONFORMANCE + if (iIsBufferSupplier) + { +#endif + TInt offset = iCurrentBuffer->pBuffer - iChunk.Base(); + bufferId = iOffsetArray.Find(offset); +#ifdef ILCOMPONENTCONFORMANCE + } + else + { + // Copy data from IL Conformance Suite in to Chunk address at specific address. + TPtr8 ptr(iChunk.Base(),iCurrentBuffer->nFilledLen ,iCurrentBuffer->nAllocLen); + ptr.Copy(iCurrentBuffer->pBuffer + iCurrentBuffer->nOffset, iCurrentBuffer->nFilledLen); + // isn't nOffset likely going to be 0? better to map buffer pointer to buffer id directly + bufferId = iArrayOffsets.Find(iCurrentBuffer->nOffset); + // nOffset is not ideal for identifying buffer area since it's likely each buffer header + // will have a unique pBuffer and nOffset == 0. We could calculate buffer ID by finding + // the buffer header on the buffer header array in the port, but this isn't how bufferID + // is calculated in the rest of the code. (we could just store the buffer ID on the + // pInputPortPrivate but we have a habit of trying to export GS specific info into COmxILMMBuffer) + __ASSERT_ALWAYS(bufferId == 0, User::Invariant()); + } +#endif + + if(KErrNotFound == bufferId) + { + // An error here means that the buffer will not be displayed and RunL() will not be + // invoked. However the buffer might have a flag that needs processing. Self complete + // to keep the state machine running. + iStatus = KRequestPending; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNotFound); + return KErrNotFound; + } + + // due to COmxMMILBuffer dependency. to be removed + if (iIsLocalChunk) + { + // copy data into local chunk + TPtr8 ptr(iCurrentBuffer->pBuffer,iCurrentBuffer->nFilledLen ,iCurrentBuffer->nAllocLen); + ptr.Copy(iChunk.Base() + offset, iCurrentBuffer->nFilledLen); + } + + iSurfaceUpdateSession.NotifyWhenDisplayed(iStatus, iTimeStamp); + SetActive(); + + err = iSurfaceUpdateSession.SubmitUpdate(KAllScreens, iSurfaceId, bufferId); + if(err) + { + // An error here means that the buffer will not be displayed and RunL() will not be + // invoked. However the buffer might have a flag that needs processing. Self complete + // to keep the state machine running. + iStatus = KRequestPending; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, err); + } + } + + return err; + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::DoCancel() + { + if (iSurfaceUpdateSession.Handle() != KNullHandle) + { + iSurfaceUpdateSession.CancelAllUpdateNotifications(); + } + + iCurrentBuffer = NULL; + } + +TInt COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::OpenDevice() + { + TInt err = iSurfaceUpdateSession.Connect(KSurfaceUpdateNumOfMessageSlots); + + if (err == KErrNotFound || err != KErrNone) + { +#ifdef __WINSCW__ + DEBUG_PRINTF(_L8("Make sure SYMBIAN_GRAPHICS_USE_GCE ON is specified in epoc.ini")); +#else + DEBUG_PRINTF(_L8("Make sure SYMBIAN_GRAPHICS_USE_GCE is defined in ROM build")); +#endif + } + + return err; + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::CloseDevice() + { + iSurfaceUpdateSession.CancelAllUpdateNotifications(); + + if (!iSurfaceId.IsNull()) + { + iParent.SurfaceManager().CloseSurface(iSurfaceId); // ignore the error + } + + iSurfaceUpdateSession.Close(); + // RSurface::Open() happened in context of caller. + iParent.SurfaceManager().Close(); + + iOffsetArray.Reset(); + } + +TInt COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::Execute() + { + iParent.SetState(OMX_StateExecuting); + iFirstFrameDisplayed = EFalse; + TInt r = ProcessNextBuffer(); + return r; + } + +TInt COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::Pause() + { + iParent.SetState(OMX_StatePause); + iFirstFrameDisplayed = EFalse; + Cancel(); + iParent.TransitionToPauseFinished(); + return KErrNone; + } + +TInt COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::Stop() + { + if(iParent.State() == OMX_StateExecuting || iParent.State() == OMX_StatePause) + { + // Cancel and flush the device driver + Cancel(); + iParent.SetState(OMX_StateIdle); + iFirstFrameDisplayed = EFalse; + } + return KErrNone; + } + +void COmxILGraphicSinkProcessingFunction::CGraphicSurfaceAccess::ResetSurfaceId() + { + iSurfaceId = TSurfaceId::CreateNullId(); + } + +COmxILGraphicSinkProcessingFunction::CPFHelper* COmxILGraphicSinkProcessingFunction::CPFHelper::NewL(COmxILGraphicSinkProcessingFunction& aParent, CGraphicSurfaceAccess& aGraphicSurfaceAccess) + { + CPFHelper* self = new (ELeave) CPFHelper(aParent, aGraphicSurfaceAccess); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +COmxILGraphicSinkProcessingFunction::CPFHelper::CPFHelper(COmxILGraphicSinkProcessingFunction& aParent, CGraphicSurfaceAccess& aSurfaceAccess) +: CActive(EPriorityUserInput), + iParent(aParent), + iGraphicSurfaceAccess(aSurfaceAccess) + { + CActiveScheduler::Add(this); + } + +void COmxILGraphicSinkProcessingFunction::CPFHelper::ConstructL() + { + User::LeaveIfError(iMsgQueue.CreateLocal(KMaxMsgQueueEntries)); + SetActive(); + iMsgQueue.NotifyDataAvailable(iStatus); + } + +COmxILGraphicSinkProcessingFunction::CPFHelper::~CPFHelper() + { + Cancel(); + iMsgQueue.Close(); + } + +void COmxILGraphicSinkProcessingFunction::CPFHelper::RunL() + { + iParent.iBufferMutex.Wait(); + if (ProcessQueue() != KErrNone) + { + iParent.GetCallbacks().ErrorEventNotification(OMX_ErrorInsufficientResources); + } + + // setup for next callbacks + SetActive(); + iMsgQueue.NotifyDataAvailable(iStatus); + iParent.iBufferMutex.Signal(); + } + +void COmxILGraphicSinkProcessingFunction::CPFHelper::DoCancel() + { + if (iMsgQueue.Handle()) + { + ProcessQueue(); // Ignore the error? + iMsgQueue.CancelDataAvailable(); + } + } + +TInt COmxILGraphicSinkProcessingFunction::CPFHelper::ProcessQueue() + { + TMessageType msg; + TInt err = iMsgQueue.Receive(msg); + while (err == KErrNone) + { + switch (msg) + { + case EOpenDevice: + { + err = iGraphicSurfaceAccess.OpenDevice(); + break; + } + case ECloseDevice: + { + iGraphicSurfaceAccess.CloseDevice(); + break; + } + + case EExecuteCommand: + { + err = iGraphicSurfaceAccess.Execute(); + break; + } + + case EStopCommand: + { + err = iGraphicSurfaceAccess.Stop(); + break; + } + + case EPauseCommand: + { + err = iGraphicSurfaceAccess.Pause(); + break; + } + + case EBufferIndication: + { + // Add buffer to list waiting to process. + // While we could send zero length buffers straight back, this would cause a + // problem if that buffer has a flag on it that needs processing. So we still + // pass them on and the graphics surface access code will not display them. + if (iParent.State() == OMX_StateExecuting || iParent.State() == OMX_StatePause) + { + err = iGraphicSurfaceAccess.ProcessNextBuffer(); + } + break; + } + default: + { + DEBUG_PRINTF2(_L8("\nMsqQue >> %d"),msg); + break; + } + } + + if (err) + { + break; + } + + err = iMsgQueue.Receive(msg); + } + + if ( err == KErrUnderflow) + { + err = KErrNone; + } + + return err; + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::OpenDevice() + { + TMessageType message; + message = EOpenDevice; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::CloseDevice() + { + TMessageType message; + message = ECloseDevice; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::ExecuteAsync() + { + TMessageType message; + message = EExecuteCommand; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::StopAsync() + { + TMessageType message; + message = EStopCommand; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::StopSync() + { + // Cancel to process the existing queue before handling this command + if (IsActive()) + { + Cancel(); + } + + TInt err = iGraphicSurfaceAccess.Stop(); + + // setup for next callbacks + SetActive(); + iMsgQueue.NotifyDataAvailable(iStatus); + + return ConvertError(err); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::Pause() + { + TMessageType message; + message = EPauseCommand; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::BufferIndication() + { + TMessageType message; + message = EBufferIndication; + return ConvertError(iMsgQueue.Send(message)); + } + +OMX_ERRORTYPE COmxILGraphicSinkProcessingFunction::CPFHelper::ConvertError(TInt aError) + { + if(aError == KErrNone) + { + return OMX_ErrorNone; + } + else if(aError == KErrOverflow) + { + return OMX_ErrorInsufficientResources; + } + + // default + return OMX_ErrorUndefined; + }