omxilcomp/omxilgeneric/filesink/src/omxilfilesinkprocessingfunction.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 02 Sep 2010 20:13:57 +0300
changeset 0 58be5850fb6c
permissions -rw-r--r--
Revision: 2010wk32 Kit: 201035

// 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
 * @internalTechnology
 */

#include <uri8.h>
#include <openmax/il/common/omxilcallbacknotificationif.h>
#include "omxilfilesinkprocessingfunction.h"

const TInt KMaxMsgQueueEntries = 25;

COmxILFileSinkProcessingFunction* COmxILFileSinkProcessingFunction::NewL(MOmxILCallbackNotificationIf& aCallbacks)
	{
	COmxILFileSinkProcessingFunction* self = new (ELeave) COmxILFileSinkProcessingFunction(aCallbacks);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

COmxILFileSinkProcessingFunction::COmxILFileSinkProcessingFunction(MOmxILCallbackNotificationIf& aCallbacks)
: COmxILProcessingFunction(aCallbacks)
	{
	}

void COmxILFileSinkProcessingFunction::ConstructL()
	{
	iState = OMX_StateLoaded;
	ipFileSinkAccess = CFileSinkAccess::NewL(*this);
	ipPFHelper = CPFHelper::NewL(*this, *ipFileSinkAccess);
	}

COmxILFileSinkProcessingFunction::~COmxILFileSinkProcessingFunction()
	{
	if(ipPFHelper &&
	   (iState == OMX_StateInvalid  ||
	    iState == OMX_StateExecuting ||
	    iState == OMX_StatePause))
		{
		ipPFHelper->StopSync();
		}

	delete ipPFHelper;
	delete ipFileSinkAccess;
	delete ipUri;
	delete ipFileName;

	// Buffer headers are not owned by the processing function
    iBuffersToEmpty.Close();
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::StateTransitionIndication(TStateIndex aNewState)
	{
	OMX_ERRORTYPE err = OMX_ErrorNone;
	switch(aNewState)
		{
		case EStateExecuting:
			{
			if (ipPFHelper->ExecuteAsync() != KErrNone)
				{
				err = OMX_ErrorInsufficientResources;
				}
			}
			break;

		case EStateInvalid:
			{
            if (ipPFHelper && ipPFHelper->StopAsync() != KErrNone)
                {
                err = OMX_ErrorInsufficientResources;
                }
			}
			break;

		case EStatePause:
			{
			ipPFHelper->PauseAsync();
			}
			break;

		case EStateIdle:
			{
			ipPFHelper->IdleAsync();
			}
			break;

		case EStateLoaded:
		case EStateWaitForResources:
			{
			if (ipPFHelper && ipPFHelper->StopAsync() != KErrNone)
				{
				err = OMX_ErrorInsufficientResources;
				}
			}
			break;

		case ESubStateExecutingToIdle:
			{
			ipPFHelper->StopAsync();
			}
			break;

        case ESubStateLoadedToIdle:
        case ESubStateIdleToLoaded:
		case ESubStatePauseToIdle:
			break;

		default:
			{
			err = OMX_ErrorIncorrectStateTransition;
			}
		};
	return err;
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::BufferFlushingIndication(TUint32 aPortIndex, OMX_DIRTYPE aDirection)
	{
	OMX_ERRORTYPE err = OMX_ErrorNone;
    if ((aPortIndex == OMX_ALL && aDirection == OMX_DirMax) ||
        (aPortIndex == 0 && aDirection == OMX_DirInput))
        {
        // Send BufferDone notifications for each bufer...
        for (TUint i=0, bufferCount=iBuffersToEmpty.Count(); i<bufferCount; ++i)
            {
            OMX_BUFFERHEADERTYPE* pBufferHeader = iBuffersToEmpty[i];
            iCallbacks.BufferDoneNotification(pBufferHeader,
                                              pBufferHeader->nInputPortIndex,
                                              OMX_DirInput);
            }
        // Empty buffer lists...
        iBuffersToEmpty.Reset();
        }
    else
        {
        err = OMX_ErrorBadParameter;
        }
    return err;
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::ParamIndication(OMX_INDEXTYPE aParamIndex, const TAny* apComponentParameterStructure)
	{
	OMX_ERRORTYPE err = OMX_ErrorNone;
	switch(aParamIndex)
		{
		case OMX_IndexParamPortDefinition:
			{
			//const OMX_PARAM_PORTDEFINITIONTYPE* portDefinition = static_cast<const OMX_PARAM_PORTDEFINITIONTYPE*>(apComponentParameterStructure);
			//nothing to do
			//
			//the number of buffers may change depending on capture mode (single shot vs burst mode)
			//in that case, we need to do something for PF...
			break;
			}

		case OMX_IndexParamContentURI:
			{
			const OMX_PARAM_CONTENTURITYPE* contentUriType = reinterpret_cast<const OMX_PARAM_CONTENTURITYPE*>(apComponentParameterStructure);
			err = SetFileName(contentUriType);
			break;
			}
		default:
			{
			err = OMX_ErrorUnsupportedIndex;
			}
		}
	return err;
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::ConfigIndication(OMX_INDEXTYPE /*aConfigIndex*/, const TAny* /*apComponentConfigStructure*/)
	{
	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::BufferIndication(OMX_BUFFERHEADERTYPE* apBufferHeader, OMX_DIRTYPE aDirection)
	{
	if (aDirection == OMX_DirInput)
    	{
    	if (ipPFHelper->BufferIndication(apBufferHeader) != KErrNone)
    		{
    		return OMX_ErrorInsufficientResources;
    		}
		}
    else
    	{
    	return OMX_ErrorBadParameter;
    	}

    return OMX_ErrorNone;
	}

OMX_BOOL COmxILFileSinkProcessingFunction::BufferRemovalIndication(OMX_BUFFERHEADERTYPE* apBufferHeader, OMX_DIRTYPE /*aDirection*/)
	{
	OMX_BOOL headerDeletionResult = OMX_TRUE;

	TInt headerIndexInArray = KErrNotFound;
	if (KErrNotFound != (headerIndexInArray = iBuffersToEmpty.Find(apBufferHeader)))
		{
		iBuffersToEmpty.Remove(headerIndexInArray);
		}
	else
		{
		headerDeletionResult = OMX_FALSE;
		}

    return headerDeletionResult;
	}

MOmxILCallbackNotificationIf& COmxILFileSinkProcessingFunction::GetCallbacks()
	{
	return iCallbacks;
	}

OMX_ERRORTYPE COmxILFileSinkProcessingFunction::SetFileName(const OMX_PARAM_CONTENTURITYPE* aContentUriType)
	{
	ASSERT(aContentUriType);
	delete ipFileName;
	ipFileName = NULL;
	delete ipUri;
	ipUri = NULL;

	//TInt dataLength = aContentUriType->nSize - sizeof(OMX_PARAM_CONTENTURITYPE) + 4;
	TInt sizeOfUri = aContentUriType->nSize - _FOFF(OMX_PARAM_CONTENTURITYPE, contentURI); //Actual size of URI
	if (sizeOfUri <= 0)
		{
		return OMX_ErrorBadParameter;
		}

	// Don't include the zero character at the end.
	//TPtrC8 uriDes(reinterpret_cast<const TUint8*>(&aContentUriType->contentURI), dataLength - 1);
	TPtrC8 uriDes(aContentUriType->contentURI,sizeOfUri);

	TInt err = KErrNone;
	do
		{
		TUriParser8 parser;
		err = parser.Parse(uriDes);
		if (err != KErrNone)
			{
			break;
			}

		TRAP(err, ipFileName = parser.GetFileNameL());
		if (err != KErrNone)
			{
			break;
			}

		// Remove Null charcter '\0' if any.
		TPtr filePtr(ipFileName->Des());
		TInt index = filePtr.LocateReverse('\0');
		if (index != KErrNotFound && index == filePtr.Length()-1)
		    {
		    filePtr.Delete(index,1);
		    }

		uriDes.Set(reinterpret_cast<const TUint8 *>(aContentUriType), aContentUriType->nSize );
		ipUri = uriDes.Alloc();
		if (!ipUri)
			{
			err = KErrNoMemory;
			break;
			}

		return OMX_ErrorNone;
		}
	while (EFalse);

	// Something failed.
	__ASSERT_DEBUG( (err == KErrNone), User::Panic(_L("UriParsing"), err) );
	delete ipFileName;
	ipFileName = NULL;
	delete ipUri;
	ipUri = NULL;
	return (err == KErrNoMemory ? OMX_ErrorInsufficientResources : OMX_ErrorBadParameter);
	}


const HBufC* COmxILFileSinkProcessingFunction::FileName() const
	{
	return ipFileName;
	}

const HBufC8* COmxILFileSinkProcessingFunction::Uri() const
	{
	return ipUri;
	}

COmxILFileSinkProcessingFunction::CFileSinkAccess* COmxILFileSinkProcessingFunction::CFileSinkAccess::NewL(COmxILFileSinkProcessingFunction& aParent)
	{
	CFileSinkAccess* self = new (ELeave) CFileSinkAccess(aParent);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

COmxILFileSinkProcessingFunction::CFileSinkAccess::CFileSinkAccess(COmxILFileSinkProcessingFunction& aParent)
	: CActive(EPriorityStandard),
	iParent(aParent),
	iBufferOffset(0),
	iWriteBuffer(0,0)
	{
	CActiveScheduler::Add(this);
	}

void COmxILFileSinkProcessingFunction::CFileSinkAccess::ConstructL()
	{
	User::LeaveIfError(iFs.Connect());
	}

COmxILFileSinkProcessingFunction::CFileSinkAccess::~CFileSinkAccess()
	{
	Cancel();

	iFileHandle.Close();
	iFs.Close();
	}

void COmxILFileSinkProcessingFunction::CFileSinkAccess::RunL()
	{

	// The buffer is not on the list implies that they have already been flushed/spotted
	// via BufferFlushingIndication/BufferRemovalIndication
	TInt index = iParent.iBuffersToEmpty.Find(iCurrentBuffer);
	if (KErrNotFound != index)
		{
		switch(iStatus.Int())
			{
			case KErrNone:
				{
				// Consumed all data completely
				if(OMX_BUFFERFLAG_EOS & iCurrentBuffer->nFlags)
					{
					iFileHandle.Close();
					iParent.GetCallbacks().EventNotification(OMX_EventBufferFlag, iCurrentBuffer->nInputPortIndex, OMX_BUFFERFLAG_EOS, NULL);
					}
				iCurrentBuffer->nFilledLen = 0;
				iCurrentBuffer->nOffset = 0;
				iCurrentBuffer->nFlags = 0;
				iCurrentBuffer->nTimeStamp = 0;
				break;
				}
			default:
				{
				// Leave actual value of iCurrentBuffer->nFilledLen
				}
			};

		iParent.GetCallbacks().BufferDoneNotification(iCurrentBuffer,iCurrentBuffer->nInputPortIndex,OMX_DirInput);
		iParent.iBuffersToEmpty.Remove(index);
		iCurrentBuffer = NULL;
		if(iFileHandle.SubSessionHandle() != 0)
			{
			ProcessNextBuffer();
			}
		}
	}

TInt COmxILFileSinkProcessingFunction::CFileSinkAccess::ProcessNextBuffer()
	{
	if ((iParent.iBuffersToEmpty.Count() > 0) && !IsActive() && iParent.iState == OMX_StateExecuting)
		{
		iCurrentBuffer = iParent.iBuffersToEmpty[0];
		iWriteBuffer.Set(iCurrentBuffer->pBuffer, iCurrentBuffer->nFilledLen, iCurrentBuffer->nAllocLen);

		// If the buffer is empty, we should not invoke RFile::Write, but self-complete instead
    	if (iCurrentBuffer->nFilledLen == 0 || iFileHandle.SubSessionHandle() == 0)
    		{
     		SetActive();
			TRequestStatus* status(&iStatus);
			User::RequestComplete(status, KErrNone);
		    }
		else
			{
			iFileHandle.Write(iWriteBuffer, iCurrentBuffer->nFilledLen, iStatus);
			SetActive();
			}
		}
	return KErrNone;
	}

void COmxILFileSinkProcessingFunction::CFileSinkAccess::DoCancel()
	{
	if (iFileHandle.SubSessionHandle() != 0)
	    {
	    iFileHandle.Close();
	    }
	iCurrentBuffer = NULL;
	}

TInt COmxILFileSinkProcessingFunction::CFileSinkAccess::Execute()
	{
	iParent.iState = OMX_StateExecuting;
	return ProcessNextBuffer();
	}

void COmxILFileSinkProcessingFunction::CFileSinkAccess::Pause()
	{
	iParent.iState = OMX_StatePause;
	Cancel();
	}

void COmxILFileSinkProcessingFunction::CFileSinkAccess::Idle()
    {
    iParent.iBuffersToEmpty.Reset();
    iParent.iState = OMX_StateIdle;
    }

void COmxILFileSinkProcessingFunction::CFileSinkAccess::Stop()
	{
	if(iParent.iState == OMX_StateExecuting || iParent.iState == OMX_StatePause)
		{
		Cancel();
		iParent.iState = OMX_StateIdle;
		}
	}

COmxILFileSinkProcessingFunction::CPFHelper* COmxILFileSinkProcessingFunction::CPFHelper::NewL(COmxILFileSinkProcessingFunction& aParent, CFileSinkAccess& aFileSinkAccess)
	{
	CPFHelper* self = new (ELeave) CPFHelper(aParent, aFileSinkAccess);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

COmxILFileSinkProcessingFunction::CPFHelper::CPFHelper(COmxILFileSinkProcessingFunction& aParent, CFileSinkAccess& aFileSinkAccess)
: CActive(EPriorityStandard),
  iParent(aParent),
  iFileSinkAccess(aFileSinkAccess)
	{
	CActiveScheduler::Add(this);
	}

void COmxILFileSinkProcessingFunction::CPFHelper::ConstructL()
	{
	User::LeaveIfError(iMsgQueue.CreateLocal(KMaxMsgQueueEntries));
	SetActive();
	iMsgQueue.NotifyDataAvailable(iStatus);
	}

COmxILFileSinkProcessingFunction::CPFHelper::~CPFHelper()
	{
	Cancel();
	iMsgQueue.Close();
	}

void COmxILFileSinkProcessingFunction::CPFHelper::RunL()
	{
	TInt err = ProcessQueue();
	if (err != KErrNone)
		{
		iParent.GetCallbacks().ErrorEventNotification( ConvertSymbianErrorType(err));
		}

	// setup for next callbacks
	SetActive();
	iMsgQueue.NotifyDataAvailable(iStatus);
	}

void COmxILFileSinkProcessingFunction::CPFHelper::DoCancel()
	{
	if (iMsgQueue.Handle())
		{
		ProcessQueue();
		iMsgQueue.CancelDataAvailable();
		}
	}

TInt COmxILFileSinkProcessingFunction::CPFHelper::ProcessQueue()
	{
	TProcMessage msg;
	TInt err = KErrNone;

	while (iMsgQueue.Receive(msg) == KErrNone)
		{
		switch (msg.iType)
			{
			case EExecuteCommand:
				{
                const HBufC* fileName = iParent.FileName();
	            if (fileName)
                    {
                    TUint fileMode = EFileWrite | EFileShareExclusive;
                    if(iFileSinkAccess.iFileHandle.SubSessionHandle() == 0)
                        {
                        err = iFileSinkAccess.iFileHandle.Replace(iFileSinkAccess.iFs, *fileName, fileMode);
                        }
                    }
                if ( err == KErrNone)
                    {
                    err = iFileSinkAccess.Execute();
                    }
				break;
				}

			case EStopCommand:
				{
				iFileSinkAccess.Stop();
				break;
				}

			case EPauseCommand:
				{
				iFileSinkAccess.Pause();
				break;
				}

            case EIdleCommand:
                {
                iFileSinkAccess.Idle();
                break;
                }

			case EBufferIndication:
				{
				OMX_BUFFERHEADERTYPE* bufferHeader = reinterpret_cast<OMX_BUFFERHEADERTYPE*>(msg.iPtr);
				if ( bufferHeader && (iParent.iState == OMX_StateExecuting || iParent.iState == OMX_StatePause
				        || iParent.iState == OMX_StateIdle) )
					{
					err = iParent.iBuffersToEmpty.Append(bufferHeader);
					if(err == KErrNone)
						{
						if(iParent.iState != OMX_StateIdle)
						    {
						    err = iFileSinkAccess.ProcessNextBuffer();
						    }
						}
					else
					    {
					    // to prevent potential buffer leakage if the Append operation fails
					    iParent.GetCallbacks().BufferDoneNotification(bufferHeader, bufferHeader->nInputPortIndex,OMX_DirInput);
					    }
					}
				break;
				}
			default:
				{
				break;
				}
			}

		if (err)
			{
			break;
			}
		}
	return err;
	}

TInt COmxILFileSinkProcessingFunction::CPFHelper::ExecuteAsync()
	{
	TProcMessage message;
	message.iType = EExecuteCommand;
	return iMsgQueue.Send(message);
	}

TInt COmxILFileSinkProcessingFunction::CPFHelper::StopAsync()
	{
	TProcMessage message;
	message.iType = EStopCommand;
	return iMsgQueue.Send(message);
	}

TInt COmxILFileSinkProcessingFunction::CPFHelper::PauseAsync()
    {
    TProcMessage message;
    message.iType = EPauseCommand;
    return iMsgQueue.Send(message);
    }

TInt COmxILFileSinkProcessingFunction::CPFHelper::BufferIndication(OMX_BUFFERHEADERTYPE* apBufferHeader)
	{
	TProcMessage message;
	message.iType = EBufferIndication;
	message.iPtr = apBufferHeader;
	return iMsgQueue.Send(message);
	}

void COmxILFileSinkProcessingFunction::CPFHelper::StopSync()
    {
    // Cancel to process the existing queue before handling this command
    Cancel();
    iFileSinkAccess.Stop();

    // setup for next callbacks
    SetActive();
    iMsgQueue.NotifyDataAvailable(iStatus);
    }

TInt COmxILFileSinkProcessingFunction::CPFHelper::IdleAsync()
    {
    TProcMessage message;
    message.iType = EIdleCommand;
    return iMsgQueue.Send(message);
    }
/**
 Converts a Symbian error code to an OpenMAX error code.
 @param     aError The Symbian error code.
 @return    The OpenMAX error code.
 */
OMX_ERRORTYPE COmxILFileSinkProcessingFunction::CPFHelper::ConvertSymbianErrorType(TInt aError)
    {
    // In the current implementation this function is only used for the return code in the
    // callback methods. Currently the only expected errors KErrNone and KErrOverflow.

    OMX_ERRORTYPE err = OMX_ErrorNone;
    switch (aError)
        {
    case KErrNone:
        err = OMX_ErrorNone;
        break;
    case KErrOverflow:
    case KErrNoMemory:
        err = OMX_ErrorInsufficientResources;
        break;
    case KErrNotSupported:
        err = OMX_ErrorNotImplemented;
        break;
    case KErrNotReady:
        err = OMX_ErrorNotReady;
        break;
    case KErrGeneral:
    default:
        err = OMX_ErrorUndefined;
        }
    return err;
    }