/*
* 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 <hal.h>
#include <graphics/suerror.h>
#include "omxilgraphicsinkextensionsindexes.h"
#include <openmax/il/shai/OMX_Symbian_ComponentExt.h>
#include <openmax/il/common/omxilcallbacknotificationif.h>
// 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; i<bufferCount; i++)
{
pBufferHeader = iBuffersToEmpty[i];
pBufferHeader->nTickCount = 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<const OMX_PARAM_PORTDEFINITIONTYPE*>(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<const OMX_VIDEO_PARAM_PORTFORMATTYPE*>(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<const OMX_CONFIG_SCALEFACTORTYPE*>(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<const OMX_FRAMESIZETYPE*>(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<const OMX_CONFIG_RECTTYPE*>(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<TUint32*>(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<TUint32*>(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;
}