diff -r 000000000000 -r b8ed18f6c07b mmlibs/mmfw/src/server/BaseClasses/mmfdatapath.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmlibs/mmfw/src/server/BaseClasses/mmfdatapath.cpp Thu Oct 07 22:34:12 2010 +0100 @@ -0,0 +1,2394 @@ +// Copyright (c) 2002-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: +// source\server\mmfdatapath.cpp +// +// + +#include +#include +#include +#include +#include +#include +#include "mmfclientaudiostreamutils.h" +#include +#include // KUidMmfCodecAudioSettings +#include + +const TUid KUidCodecAudioConfig = {KUidMmfCodecAudioSettings}; + +void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber) + { + _LIT(KMMFDataPathPanicCategory, "MMFDataPath"); + User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber); + } + +//all functions are exported form the DLL and are virtual to allow plugins to define there own CMMFDataPaths + +/** +Allocates and constructs a data path. + +Use this function if the codec UID is not already known by CMMFController +and there is no data path ambiguity - ie only one data path is possible. + +Will create codec via fourCC. + +@param aEventHandler + Installs an event handler to provide message passing between clients and sources/sinks. + +@return Newly constructed data path object. +*/ + +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(MAsyncEventHandler& aEventHandler) + { + CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + + +/** +Allocates and constructs a data path according to the specified media ID. + +Use this function if the codec UID is not already known by CMMFController +and there is ambiguity with the data path ie. there is more than one possible data path. + +@param aMediaId + Optional media ID parameter when there are multiple media types. +@param aEventHandler + Installs an event handler to provide message passing between clients and sources/sinks. + +@return A newly constructed data path object. +*/ + +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +/** +Allocates and constructs a data path according to the specified codec UID. + +Use this function if the codec UID is already known by CMMFController +and there is no data path ambiguity ie. only one data path is possible +will create codec explicitly using the supplied codec Uid + +@param aCodecUid + Optional mediaID parameter when there are multiple media types +@param aEventHandler + Installs an event handler to provide message passing between clients and sources/sinks. + +@return A newly constructed data path object. +*/ + +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); + CleanupStack::PushL(self); + self->ConstructL(aCodecUid); + CleanupStack::Pop(); + return self; + } + + +/** +Allocates and constructs a data path according to the specified codec UID. + +Use this function if the codec UID is already known by CMMFController +and there is ambiguity ie. more than one possible data path. +TMediaId used to select the path. + +@param aCodecUid + The codec UID. +@param aMediaId + Optional mediaID parameter when there are multiple media types. +@param aEventHandler + Installs an event handler to provide message passing between clients and sources/sinks. + +@return A newly constructed data path object. +*/ +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); + CleanupStack::PushL(self); + self->ConstructL(aCodecUid); + CleanupStack::Pop(); + return self; + } + +/** +Standard destructor. +*/ + +EXPORT_C CMMFDataPath::~CMMFDataPath() + { + Cancel(); + delete iCodec; + DoCleanupBuffers(); + + //log off the source and sink + if (iDataSource) + iDataSource->SourceThreadLogoff(); + if (iDataSink) + iDataSink->SinkThreadLogoff(); + + if (iCompleteCallback) + { + iCompleteCallback->Cancel(); + delete iCompleteCallback; + } + } + +/** +Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL(). +Typically if buffers are created asychronously, the datapath doesn't own the buffer +so leaves cleanup handling to the owner sources/sinks. + +Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive, +iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped. +*/ +EXPORT_C void CMMFDataPath::ResetL() + { + delete iCodec; + iCodec = NULL; + DoCleanupBuffers(); // Delete buffers + //logoff and dereference source and sink + if (iDataSource) + { iDataSource->SourceThreadLogoff(); iDataSource = NULL; } + if (iDataSink) + { iDataSink->SinkThreadLogoff(); iDataSink = NULL; } + + // Reset states + iDataPathCreated = EFalse; + iState = EStopped; + iSrcBufRef = EFalse; + iSnkBufRef = EFalse; + iPauseCalled = EFalse; + + delete iCompleteCallback; iCompleteCallback = NULL; + } + +/** +Delete source and/or sink buffers that are owned by DataPath. + +Ownership indicated by iSrcBufRef and iSnkBufRef. + +Ownership is assigned during buffer allocation within the datapath PrimeL(). +*/ +void CMMFDataPath::DoCleanupBuffers() + { + // delete source and/or sink buffer that is owned by DataPath + if ( !iSrcBufRef && iSourceBuffer ) + { + delete iSourceBuffer; + } + iSourceBuffer = NULL; + if ( !iSnkBufRef && iSinkBuffer ) + { + delete iSinkBuffer; + } + iSinkBuffer = NULL; + } + + +/** +Obtain source and/or sink buffer using the synchronous API CreateSourceBufferL() and CreateSinkBufferL(). +*/ +void CMMFDataPath::ObtainSyncBuffersL() + { + //Try to create source and sink buffers. If we can't create them synchronously via + //CreateSourceBufferL and CreateSinkBufferL we will need to obtain them by + //asynchronous buffer creation when playing starts. + + if (iBuffersToUse & ENeedSourceBuffer) + { + if (!iSourceBuffer) //we may already have a buffer from a previous initialization + { + TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); + if(err != KErrNone && err != KErrNotSupported) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); +#endif + User::Leave(err); + } + } + } + + + if (iBuffersToUse & ENeedSinkBuffer) + { + if (!iSinkBuffer) //we may already have a buffer from a previous initialization + { + TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); + if(err != KErrNone && err != KErrNotSupported) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); +#endif + User::Leave(err); + } + } + } + + if (iSourceBuffer && !(iBuffersToUse & ENeedSinkBuffer)) + {//only need one buffer, use source + iSinkBuffer =iSourceBuffer; + iSnkBufRef = ETrue; //the sink buffer is not to be deleted + } + else if (iSinkBuffer && !(iBuffersToUse & ENeedSourceBuffer)) + {//only need one buffer, use sink + iSourceBuffer =iSinkBuffer; + iSrcBufRef = ETrue; //the sink buffer is not to be deleted + } + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::ObtainSyncBuffersL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + } + + + + + +/** +Constructs a source. + +The default implementation leaves with KErrNotSupported. + +@param aInitData + The initialisation data. +*/ + +EXPORT_C void CMMFDataPath::ConstructSourceL( const TDesC8& /*aInitData*/ ) + { + User::Leave(KErrNotSupported); + } + +/** +Constructs a sink. + +Overridable constuction specific to this datasource. + +The default implementation leaves with KErrNotSupported. + +@param aInitData + The initialisation data. +*/ +EXPORT_C void CMMFDataPath::ConstructSinkL( const TDesC8& /*aInitData*/ ) + { + User::Leave(KErrNotSupported); + } + +/** +Takes UID of codec on construction, and if not an NULL codec sets the datapath up for codec instantiation. + +@param aCodecUid + The UID of the codec. +*/ + +EXPORT_C void CMMFDataPath::ConstructL(TUid aCodecUid) + { + iUseSuppliedCodecUid = EFalse; //initially assume no supplied codec uid + + if (aCodecUid != KNullUid) + {//the data path NewL has specified a specific codec + //create CMMFCodec here + iCodec = CMMFCodec::NewL(aCodecUid); + if (iCodec) + iUseSuppliedCodecUid = ETrue; + } + + iSrcBufRef = EFalse; + iSnkBufRef = EFalse; + iObtainingAsyncSourceBuffer = EFalse; + iObtainingAsyncSinkBuffer = EFalse; + iSourceBufferWithSource = EFalse; + iSinkBufferWithSink = EFalse; + } + + +/** +Adds a data source to the datapath and, if the sink already exists, tries to establish a connection +between the source and sink. + +@param aSource + The data source to add to the data path. +*/ +EXPORT_C void CMMFDataPath::AddDataSourceL(MDataSource* aSource) + { + if (!iDataSink) iDataSource=aSource; //can't create a data path without the MDataSink as well + else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path + {//we have a data sink as well so check a data path can be established between source&sink + CreateDataPathL(aSource, iDataSink); + iDataSource = aSource; + } + else //the CMMFController specified the codec uid so must use existing codec + {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid + //can make the appropriate data conversion + iDataPathCreated = ETrue; + iDataSource = aSource; + } + ClearPlayWindowL() ; + User::LeaveIfError(iDataSource->SourceThreadLogon(*this)); + } + + +/** +Adds a data sink to the datapath and, if the source already exists, tries to establish a connection +between the source and sink. + +@param aSink + The data sink to add to the data path. +*/ + +EXPORT_C void CMMFDataPath::AddDataSinkL(MDataSink* aSink) + { + if (!iDataSource) iDataSink=aSink; //can't create a data path without the MDataSource as well + else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path + {//we have a data source as well so check a media path can be established between source&sink + CreateDataPathL(iDataSource, aSink); + iDataSink = aSink; + } + else //the CMMFController specified the codec uid so must use existing codec + {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid + //can make the appropriate data conversion + iDataPathCreated = ETrue; + iDataSink = aSink; + + //set 4CCs + iSourceFourCC = iDataSink->SinkDataTypeCode(iMediaId);//sink because CMMFDataPath is an MDataSink to its MDataSource! + iSinkFourCC = iDataSource->SourceDataTypeCode(iMediaId);//source because CMMFDataPath is an MDataSource to its MDataSink! + } + User::LeaveIfError(iDataSink->SinkThreadLogon(*this)); + } + + +/* + * CreateDataPathL + * + * internal function to establish a datapath between the source and sink + * the data supplied by the sink adn expected by the source are checked and + * a codec is instantiated if necessary + * + * @param aSource + * @param aSink + */ + +void CMMFDataPath::CreateDataPathL(MDataSource* aSource, MDataSink* aSink) + { //procedure to attempt to match the source to the sink creating a codec if necessary + // returns ETrue if the datapath could be constructed else false + //sets iCodec to the appropriate codec.& sets its own iSink/iSource FourCC datatype codes + iDataPathCreated = EFalse; + if (aSource && aSink) //have a source and sink + { //we have a data source & sink but no codec so try and find one - if required + TFourCC sourceFourCCCode = aSource->SourceDataTypeCode(iMediaId); //get MDataSource data type fourCC code + TFourCC sinkFourCCCode = aSink->SinkDataTypeCode(iMediaId); //get MDataSink data type fourCC code + if ((sourceFourCCCode != sinkFourCCCode) && //MDataSource & MDataSink datatypes are not compatible + (sourceFourCCCode != KMMFFourCCCodeNULL) && (sinkFourCCCode != KMMFFourCCCodeNULL)) + {//we need a codec to make the conversion between the source and the sink + CMMFCodec* codec = CMMFCodec::NewL(sourceFourCCCode, sinkFourCCCode); + + if (codec) + { + delete iCodec; + iCodec = codec; + //data path created ie have source/sink and can match their datatypes + iDataPathCreated = ETrue; + + //now we have an source attached we need to configure the codec for sample rate + //and number of channels + + //prepare a package to send to a codec + TMMFAudioConfig audioSettings; + + //test for decoder + if (aSource->DataSourceType() == KUidMmfFormatDecode) + { + audioSettings.iSampleRate = static_cast(aSource)->SampleRate(); + audioSettings.iChannels = static_cast(aSource)->NumChannels(); + } + + //package up to send to codec + TPckgBuf configPackage(audioSettings); + + //we need to catch User::Leave(KErrNotSupported) as by default most codecs + //do not support the ConfigureL method. + TRAPD(err,iCodec->ConfigureL(KUidCodecAudioConfig, configPackage)); + // need to check other error here + if (err != KErrNone && err != KErrNotSupported) + { + User::Leave(err); + } + } + else + { + User::Leave( KErrNotSupported ) ; //couldn't find suitable codec + } + } //if (sourceFourCCCode != sinkFourCCCode) + else + { //source & sink fourCC datatypes are the same so no codec is required + __ASSERT_DEBUG(iCodec == NULL, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); + + iDataPathCreated = ETrue; + } + //can assign FourCC codes for the CMMFDataPath + iSinkFourCC = sourceFourCCCode; //sink because CMMFDataPath is an MDataSink to its MDataSource! + iSourceFourCC = sinkFourCCCode; //source because CMMFDataPath is an MDataSource to its MDataSink! + //If sink & source need its own Prime() done in controller + } + } + +/** +Clears the specified buffer. + +Pure virtual dummy implementation, not needed by datapath +comes from MDataSink - CMMFData path is a sink to its MDataSource. + +This is only required for an active push MDataSource requesting a buffer empty. + +@param aBuffer + The buffer to empty. +@param aSupplier + The MDataSource supplying this buffer. +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving of different media types. + +*/ +EXPORT_C void CMMFDataPath::EmptyBufferL(CMMFBuffer* /* aBuffer */, MDataSource* /*aSupplier*/, TMediaId /*aMediaId*/) + { + //not implemented + } + + + +/* + * FillSourceBufferL + * + * Function to get data from the datapath's iDataSource + */ + +void CMMFDataPath::FillSourceBufferL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSourceBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); + + + // clear the no-more-source flag here (as well as in PlayL()) because + // there may have been a re-position since the last call to BufferFilledL() + iNoMoreSourceData = EFalse; + + if(!iObtainingAsyncSourceBuffer) + {//this is a normal request for data. + //If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL + iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with + iSourceBuffer->SetStatus(EBeingFilled); + iSourceBuffer->SetLastBuffer(EFalse); + } + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP asking for buffer %d - ptr=0x%x (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this); +#endif + + iSourceBufferWithSource = ETrue; + + // wait for BufferFilled callback from source. Do this here as some sources cause + //re-entrancy into data path via BufferFilledL + ChangeDataPathTransferState(EWaitSource); + + iDataSource->FillBufferL(iSourceBuffer, this, iMediaId); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + +/** +Indicates the data source has filled the specified buffer. + +Called by the CMMFDataPath's MDataSource when it has filled the buffer. + +@param aBuffer + A pointer to the filled buffer. +*/ +EXPORT_C void CMMFDataPath::BufferFilledL(CMMFBuffer* aBuffer) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL src has filled buffer %d (ptr=0x%x) with %d bytes EoF = %d tick-%d (this 0x%x)\n"),aBuffer->FrameNumber(),aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), User::TickCount(),this); +#endif + + //This assertion is commented because of PDEF117405 + //state only used if we are passing data + //__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); + + __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__)); + + iSourceBufferWithSource = EFalse; + + //Has the datapath stopped running, if so were not interested in any callbacks. + if(iState == EStopped || iState == EPrimed || (iPauseCalled && iState != ERecording)) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); +#endif + return; + } + +#ifdef REPOSITION_SPEEDUP + // if the source has been re-positioned, then go & get some more source data now + if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this); +#endif + ChangeDataPathTransferState(ENeedSourceData); + return; + } +#endif //REPOSITION_SPEEDUP + + //bufer is NULL, indicating no more source data. + if (!aBuffer) + { + //If we only hold a reference to the source buffer, set that to NULL + if(iSnkBufRef) + iSourceBuffer = NULL; + + + iNoMoreSourceData = ETrue; + + if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data + iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data + { + ChangeDataPathTransferState(EEndOfData); + } + else //sink buffer is with datapath, see if there is anything to send to sink + ChangeDataPathTransferState(ENeedToMatchSourceToSink); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + return; + } + + + //We were waiting for a response from the source to get an asynchronous buffer. + //We now have it, and we proceed to transfer this data to the sink. + if (iObtainingAsyncSourceBuffer) + { + iObtainingAsyncSourceBuffer = EFalse; + } + + + aBuffer->SetStatus(EFull); + + if(iSourceBuffer != aBuffer) + {//buffer has been changed by the source + iSourceBuffer = aBuffer; + if (!(iBuffersToUse & ENeedSinkBuffer)) + {//we only need one buffer and use source + iSinkBuffer = iSourceBuffer; + iSnkBufRef = ETrue; + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + } + //Is this the last buffer from the source (0 length or LastBuffer flag set) + //or have reached the end of the play window; we only look at the play window here + //if we are converting. For conversion we look at the data we have read. This is then passed onto + //the source + if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() || + (((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition ))) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL end of input data tick-%d (this 0x%x)\n"),User::TickCount(),this); + RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize()); + RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer()); + RDebug::Print(_L("InputPosition()=%d >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64())); +#endif + iNoMoreSourceData = ETrue; + iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window + } + + + if (!iCodec) + ChangeDataPathTransferState(ESendDataToSink); + else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it + ChangeDataPathTransferState(ENeedToMatchSourceToSink); + //else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + + + +/* + * FillSinkBufferL + * + * Function to take the data from an already full source buffer and by using + * a codec if necessary fills the sink buffer + */ + +void CMMFDataPath::FillSinkBufferL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + //This state is only used if we are passing data + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); + + //This state is only used if we have a real codec + __ASSERT_DEBUG(iCodec, Panic(EMMFDataPathPanicBadState,__LINE__)); + + + //The sink buffer is with the sink so we can't fill it. + //When it has been emptied, this state will be re-entered from BufferEmptiedL + if(iSinkBufferWithSink) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SINK - DONE (this 0x%x)\n"),this); +#endif + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink + return; + } + + //The source buffer is with the source so we can't take data from it. + //When it has been filled, this state will be re-entered from BufferFilledL + if(iSourceBufferWithSource) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SOURCE - DONE (this 0x%x)\n"),this); +#endif + ChangeDataPathTransferState(EWaitSource); // wait for BufferFilled callback from source + return; + } + + //source buffer is NULL, can't be any more data to send. + //iNoMoreSourceData is set and the source buffer is empty, can't be any more data to send. + if(!iSourceBuffer || (iNoMoreSourceData && !iSourceBuffer->BufferSize())) + { + if(iSinkBuffer->Status() == EBeingFilled) + {//if we have data in sink buffer, mark it as last buffer and send + iSinkBuffer->SetLastBuffer(ETrue); + ChangeDataPathTransferState(ESendDataToSink); + } + else //the sink buffer can't have anything in it + ChangeDataPathTransferState(EEndOfData); + } + +#ifdef REPOSITION_SPEEDUP + // if the source has been re-positioned, + // speed things up by getting some more source data now + if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); +#endif + ChangeDataPathTransferState(ENeedSourceData); + return; + } +#endif //REPOSITION_SPEEDUP + + iSinkBuffer->SetStatus(EBeingFilled); + iSinkBuffer->SetLastBuffer(EFalse); + + //pass buffer to codec for processing + iCodecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSinkBuffer); + //the codec tries to fill the sink buffer to its max length + //TCodecProcessResult returns the status of the codec Process - + //this can result in result conditions such as: + //EProcessComplete - the codec processed all the source data into the sink buffer + //EProcessIncomplete - the codec filled sink buffer before all the source buffer was processed + //EDstNotFilled - the codec processed the source buffer but the sink buffer was not filled + //EEndOfData - the codec detected the end data - all source data in processed but sink may not be full + //EProcessError - the codec process error condition + + + switch (iCodecProcessResult.iStatus) + { + case TCodecProcessResult::EProcessComplete: + //finished procesing source data - all data in sink buffer + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessComplete (this 0x%x)\n"),this); +#endif + iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble + iSinkBuffer->SetStatus(EFull); //sink buffer is full + if (iNoMoreSourceData) + iSinkBuffer->SetLastBuffer(ETrue); + ChangeDataPathTransferState(ESendDataToSink);// the full sink buffer needs to be sent to the sink + } + break; + case TCodecProcessResult::EProcessIncomplete: + // the sink was filled before all the src was processed + // therefore still send everything to sink + //but datapath needs to carry on processing the source buffer before it gets more source data + //when sink has emptied data path needs to send rest of data + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessIncomplete (this 0x%x)\n"),this); +#endif + TUint sourceBufferPosition = iCodecProcessResult.iSrcBytesProcessed + iSourceBuffer->Position(); + iSourceBuffer->SetPosition(sourceBufferPosition);//update source buffer position + iSinkBuffer->SetStatus(EFull); //sink & source buffers are both full + ChangeDataPathTransferState(ESendDataToSink); // the full sink buffer needs to be sent to the sink + } + break; + case TCodecProcessResult::EDstNotFilled: + // the destination is not full + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EDstNotFilled (this 0x%x)\n"),this); +#endif + iSourceBuffer->SetStatus(EAvailable); //source buffer is now available + TUint sinkBufferPosition = iCodecProcessResult.iDstBytesAdded + iSinkBuffer->Position(); + iSinkBuffer->SetPosition(sinkBufferPosition);//update sink buffer position (still EBeingFilled) + // if this was the last source buffer, send what we've got (if anything) + // to the sink... EmptySinkBuffer() should then enter EEndOfData state + if (iNoMoreSourceData) + { + iSinkBuffer->SetLastBuffer(ETrue); + ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - + } + else + { + ChangeDataPathTransferState(ENeedSourceData); //need to get more source data to fill sink buffer + } + } + break; + case TCodecProcessResult::EEndOfData: + //no more data - send what we've got to the sink + //note we can't always rely on this - in many cases the codec will not know when + //it has reached the end of data. + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EEndOfData (this 0x%x)\n"),this); +#endif + iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble + iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get + + //This only occurs where the codec can detect the end of data, but the source can't + iNoMoreSourceData=ETrue; + iSinkBuffer->SetLastBuffer(ETrue); + + ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - + //doesn't matter if sink buffer is not full + } + break; + case TCodecProcessResult::EProcessError: +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); +#endif + User::Leave(KErrCorrupt); //codec process error + break; + default: +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); +#endif + User::Leave(KErrCorrupt); //should never get here + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::FillSinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + + +/** +Tests whether the data path can create a sink buffer. + +The default implementation returns false. + +@return ETrue if the data path can create a sink buffer. EFalse if the data path cannot create a sink buffer. +*/ +EXPORT_C TBool CMMFDataPath::CanCreateSinkBuffer() + { + return NULL; //CMMFDataPath cannot create buffer + } + +/** +Creates a sink buffer according to the specifed media ID. + +Intended for synchronous usage (buffers supplied by datapath for an MDataSink). +This method is essentially a dummy implementation of an MDataSink pure virtual. + +The default implementation returns NULL. + +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving of different media types. + +@return Returns NULL in this instance as datapath can't create sink buffers +*/ +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/) + {//CMMFDataPath can't create buffers + return NULL; + } + +/** +Creates a sink buffer according to the specifed media ID and reference. + +Intended for asynchronous usage (buffers supplied by Devsound device). +This method is essentially a dummy implementation of an MDataSink pure virtual. + +The default implementation returns NULL. + +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving for different media types. +@param aReference + A boolean indicating buffer ownership. + +@return Returns NULL in this instance as datapath can't create sink buffers. +*/ +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) + {//CMMFDataPath can't create buffers + return NULL; + } + +/** +Gets the sink's data type for the specified media ID. + +@param aMediaId + An optional parameter to specifiy the specific stream when datasource contains more than one stream of data +@return The sink's data type. +*/ +EXPORT_C TFourCC CMMFDataPath::SinkDataTypeCode(TMediaId /*aMediaId*/) + { + return(iSinkFourCC); + } + +/** +Fills the specified buffer. + +Pure virtual dummy implementation, not needed by datapath +comes from MDataSink - CMMFData path is a source to its MDataSink + +Only required for an active pull MDataSink requesting a buffer fill. The default implementation is empty. + +@param aBuffer + The buffer to fill. +@param aConsumer + The MDataSink supplying this buffer. +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving of different media types +*/ +EXPORT_C void CMMFDataPath::FillBufferL(CMMFBuffer* /*aBuffer*/, MDataSink* /*aConsumer*/, TMediaId /*aMediaId*/) + { + //not implementated + } + +void CMMFDataPath::SetBuffersAvailable() + { + // set source buffer to be available + if (iSourceBuffer) + iSourceBuffer->SetStatus(EAvailable); + // set sink buffer to be available + if (iSinkBuffer) + iSinkBuffer->SetStatus(EAvailable); + } + +void CMMFDataPath::ResetRefBuffers() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::ResetRefBuffers iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + + // Reset the buffer pointers to NULL if they are supplied by DevSound + // We do this because buffers that are not owned by the datapath may not be valid any more. + if (iSrcBufRef) + { + iSourceBuffer = NULL; + } + if (iSnkBufRef) + { + iSinkBuffer = NULL; + } + } + + + + +TInt CMMFDataPath::DetermineBuffersToUseL(void) const + { + TInt buffs = ENoBuffers; + if(iCodec) + {//Using a real Codec, need both sets of buffers + if(!iDataSink->CanCreateSinkBuffer() || ! iDataSource->CanCreateSourceBuffer()) + User::Leave(KErrNotSupported); + + buffs = CMMFDataPath::ENeedSinkBuffer | CMMFDataPath::ENeedSourceBuffer; + } + else //we are using a Null Codec, only need one buffer, but which one? + {//use buffer from DevSound, if no DevSound (ie, clip to clip), prefer source buffer. + //If preferring source but it can't create buffers, use sink. + if ((iDataSink->DataSinkType() == KUidMmfAudioOutput) && (iDataSink->CanCreateSinkBuffer())) + buffs = ENeedSinkBuffer; + else if(iDataSource->CanCreateSourceBuffer()) + buffs = ENeedSourceBuffer; + else if(iDataSink->CanCreateSinkBuffer()) + buffs = ENeedSinkBuffer; + else + User::Leave(KErrNotSupported); + } + return buffs; + } + + + +/* + * InitializeSinkL + * + * Function to initialize iDataSink before it can start sending data + * This is a one time prime. This will synchronize DataPath's data driving + * mechanism with HwDevice implementation. + * + * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine + * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF + */ + +void CMMFDataPath::InitializeSinkL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InitializeSinkL iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + + //state only used if we are passing data + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); + + iObtainingAsyncSinkBuffer = EFalse; + + if (iBuffersToUse & ENeedSinkBuffer) + { + //Buffers are initially created in the Prime method. But following a pause, we must re-create + //any referenced buffers, so try direct creation. + //NB: this does mean we are trying this twice, Prime and here + if (!iSinkBuffer) //we may already have a buffer from a previous initialization + { + TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); + if(err != KErrNone && err != KErrNotSupported) + User::Leave(err); + } + + + //If buffer has not been supplied via CreateSinkBufferL, + //must use asynchronous buffer creation + if (!iSinkBuffer) + { + iObtainingAsyncSinkBuffer = ETrue; + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink + iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId); + } + else + { + //we have a sink buffer from CreateSinkBufferL + iSinkBuffer->SetStatus(EAvailable); + + if (iBuffersToUse & ENeedSourceBuffer) + {//need a source buffer, go get it + ChangeDataPathTransferState(EInitializeSource); + } + else + {//only need one buffer, use sink + iSourceBuffer = iSinkBuffer; + iSrcBufRef = ETrue; //the src buffer is not to be deleted + + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data + } + } + } + else + {//don't need a sink buffer, but we need a source one + ChangeDataPathTransferState(EInitializeSource); + } + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InitializeSinkL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + } + + +/* + * InitializeSourceL + * + * Function to initialize iDataSource before it can start sending data + * This is a one time prime. This will synchronize DataPath's data driving + * mechanism with HwDevice implementation. + * + * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine + * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF + */ + +void CMMFDataPath::InitializeSourceL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + + //state only used if we are passing data + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); + + iObtainingAsyncSourceBuffer = EFalse; + + if (iBuffersToUse & ENeedSourceBuffer) + { + //Buffers are initially created in the Prime method. But following a pause, we must re-create + //any referenced buffers, so try direct creation. + //NB: this does mean we are trying this twice, Prime and here. + if (!iSourceBuffer) //we may already have a buffer from a previous initialization + { + TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); + if(err != KErrNone && err != KErrNotSupported) + User::Leave(err); + } + + + //If buffer has not been supplied via CreateSourceBufferL + //must use asynchronous buffer creation + if (!iSourceBuffer) + { + iObtainingAsyncSourceBuffer = ETrue; + ChangeDataPathTransferState(ENeedSourceData); + } + else + {//we have a source buffer from CreateSourceBufferL + iSourceBuffer->SetStatus(EAvailable); + + if (!(iBuffersToUse & ENeedSinkBuffer)) + {//only need one buffer, use sink + iSinkBuffer = iSourceBuffer; + iSnkBufRef = ETrue; + } + + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data + } + } + else + {//don't need a source buffer, use sinks + if(iSinkBuffer) + { + iSourceBuffer = iSinkBuffer; + iSrcBufRef = iSnkBufRef; + SetBuffersAvailable(); + } +#ifdef _DP_DEBUG + else + Panic(EMMFDataPathPanicProgrammingError,__LINE__); +#endif + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data + + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + } + } + +/* + * EmptySinkBufferL + * + * Function to pass a full databuffer to the iDataSink + */ +void CMMFDataPath::EmptySinkBufferL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::EmptySinkBufferL pass data to sink tick-%d (this 0x%x)\n"),User::TickCount(),this); + if(iSinkBuffer) + RDebug::Print(_L("iSinkBuffer %d contains %d bytes eof = %d line %d (this 0x%x)\n"),iSinkBuffer->FrameNumber(), iSinkBuffer->BufferSize(),iSinkBuffer->LastBuffer(),__LINE__,this); +#endif + + //Before emptying the sink buffer we need to check it has data to empty - this + //may not be the case if there is no more data ie iNoMoreSourceData is true. + //In this case we need to check to see if there is any data left in the sink + //buffer, ie the sink buffer is either full or being filled. If there is not any + //data in the sink buffer, ie it is not in the state of EBeingFilled or EFull + //then there is nothing to empty so the datapath state is set to EEndOfData and + //we return from the procedure. + + //state only used if we are passing data + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled)), Panic(EMMFDataPathPanicBadState,__LINE__)); + __ASSERT_DEBUG(iSinkBuffer && + ((iSinkBuffer->Status()==EBeingFilled) || (iSinkBuffer->Status()==EFull)), + Panic(EMMFDataPathPanicProgrammingError,__LINE__)); + + __ASSERT_DEBUG(iSinkBufferWithSink == EFalse, Panic(EMMFDataPathPanicBadState,__LINE__)); + + + //Due to sinks that may call BuferEmptied directly (ie. re-entrancy onto DataPath) we + //must work out next state here. If re-entrancy, the next state may validly get overwritten + // in BuferEmptied. + if(iObtainingAsyncSinkBuffer) //wait for buffer to be returned in BufferEmptied + { + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink + } + +#ifdef REPOSITION_SPEEDUP + // if the source has been re-positioned, + // speed things up by getting some more source data now + if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::EmptySinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); +#endif + ChangeDataPathTransferState(ENeedSourceData); + return; + } +#endif //REPOSITION_SPEEDUP + + //We have sent data to sink, if we are using a real Codec, we can now get more data from source + //if there is any more to get and the codec has emptied it. + //NB: No need to check we own the source buffer as we will no be in this state + //if we have asked for more source data and haven't received it. + else if (iCodec && !iNoMoreSourceData && (iSourceBuffer->Status() == EAvailable)) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("ASKING for more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource,this); +#endif + ChangeDataPathTransferState(ENeedSourceData); + } + else + { +#ifdef _DP_DEBUG + RDebug::Print(_L("Not asking for any more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d iSourceBuffer->Status=%d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource ,iSourceBuffer->Status(), this); +#endif + + //if this is the last buffer, set this flag so we can deal with KErrUnderflow + //as a valid termination of playing + if(iSinkBuffer->LastBuffer()) + iAllDataSentToSink=ETrue; + + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink + } + + + if(!iObtainingAsyncSinkBuffer) //normal data transfer + iSinkBuffer->SetFrameNumber(++iCurrentSinkFrameNumber); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP sending buffer %d ptr=0x%x of %d bytes to sink (this 0x%x)\n"), iSinkBuffer->FrameNumber(),iSinkBuffer,iSinkBuffer->BufferSize(),this); +#endif + + iSinkBufferWithSink = ETrue; + TRAPD(error, iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId)); + + // Check that we haven't exceeded the maximum clip length - if so, go to the EndOfData state + // so we perform necessary cleanup. + if (error == KErrEof || error == KErrOverflow || error == KErrUnderflow) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::EmptySinkBufferL DONE %d error = %d tick-%d (this 0x%x)\n"),__LINE__, error, User::TickCount(),this); +#endif + iDataPathCompletedErrorCode = error; + ChangeDataPathTransferState(EEndOfData); + return; + } + User::LeaveIfError(error); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::EmptySinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + +/** +Indicates the data sink has emptied the buffer. + +Called by the CMMFDataPath's MDataSink when it has emptied the buffer + +@param aBuffer + The emptied buffer. +*/ +EXPORT_C void CMMFDataPath::BufferEmptiedL(CMMFBuffer* aBuffer) + { +#ifdef _DP_DEBUG + TInt bufNum = 9999; + if(aBuffer) + bufNum = aBuffer->FrameNumber(); + else + RDebug::Print(_L("DP::BufferEmptiedL returned NULL (this 0x%x)\n"),this); + + RDebug::Print(_L("DP::BufferEmptiedL sink has taken buffer %d (ptr=0x%x) bytes %d eof= %d iNoMoreSourceData = %d tick-%d (this 0x%x)\n"), + bufNum, aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), iNoMoreSourceData, User::TickCount(),this); +#endif + + iSinkBufferWithSink = EFalse; + + //Has the datapath stopped running, if so were not interested in any callbacks. + if(iState == EStopped || (iState == EPrimed && !iPauseCalled)) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferEmptiedL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); +#endif + return; + } + + + // This will allow MDataSink to send dynamic buffer to DataPath with each BufferEmptiedL request. + if (iSinkBuffer != aBuffer) //buffer has been updated + { + iSinkBuffer = aBuffer; + if (!(iBuffersToUse & ENeedSourceBuffer)) + { //can use a single buffer + iSourceBuffer = iSinkBuffer; + iSrcBufRef = iSnkBufRef; + } + + } + + iSinkBuffer->SetStatus(EAvailable); + + if (iObtainingAsyncSinkBuffer) //we are creating an asynchronous sink buffer + { + iObtainingAsyncSinkBuffer = EFalse; + + //we have a sink buffer, should this also be used by the source + if (!(iBuffersToUse & ENeedSourceBuffer)) + {//using a single buffer, so start getting data + iSourceBuffer = iSinkBuffer; + iSrcBufRef = iSnkBufRef; + + ChangeDataPathTransferState(ENeedSourceData); + } + else //obtain a separate source buffer + ChangeDataPathTransferState(EInitializeSource); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferEmptiedL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); +#endif + return; + } + + if(!iCodec) //No Codec in use + { + if(iNoMoreSourceData) + ChangeDataPathTransferState(EEndOfData);//final buffer returned from sink + else + ChangeDataPathTransferState(ENeedSourceData);//get more data from source + } + else //real codecs + { + //There is more source data and src buffer is being filled or we are about to go into + // ENeedSourceData state to fill it, so wait for source data to arrive. + //NB:if there was more source data and we are using a real codec and source buffer was empty, + //more source data would have been requested in EmptySinkBuffer + if(!iNoMoreSourceData && (iSourceBufferWithSource || iTransferState == ENeedSourceData)) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferEmptiedL - waiting for more source data - DONE tick-%d line %d (this 0x%x)\n"),User::TickCount(),__LINE__,this); +#endif + return; + } + + //source has supplied a NULL buffer or it has been emptied; no more data to send. + if(!iSourceBuffer || (iSourceBuffer->Status() == EAvailable)) + ChangeDataPathTransferState(EEndOfData); + else if(iSourceBuffer->Status() == EFull) //there is data in the source buffer, go and get it + ChangeDataPathTransferState(ENeedToMatchSourceToSink); + +#ifdef _DP_DEBUG + else + Panic(EMMFDataPathPanicProgrammingError,__LINE__); +#endif + } + + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::BufferEmptiedL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + + +/** +Tests whether the data path can create a source buffer. + +Would expect datapath to always return NULL, so this is a default implementation of a pure virtual from MDataSink. + +The default implementation returns EFalse. + +@return ETrue if the data path can create a source buffer. EFalse if the data path cannot create a source buffer. +*/ +EXPORT_C TBool CMMFDataPath::CanCreateSourceBuffer() //from both MDataSource & MDataSink? + { + return EFalse; //CMMFDataPath cannot create buffer + } + +/** +Creates a source buffer. + +Intended for synchronous usage (buffers supplied by datapath for a MDataSource) +This method is essentially a dummy implementation of an MDataSource pure virtual. + +The default implementation leaves with KErrNotSupported and returns NULL. + +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving of different media types. +@return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers +*/ +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/) //CMMFDataPath can't create buffers + { + User::Leave(KErrNotSupported); + return NULL; + } + +/** +Creates a source buffer according to the specifed media ID and reference. + +Intended for asynchronous usage (buffers supplied by datapath for a MDataSource) +This method is essentially a dummy implementation of an MDataSource pure virtual. + +The default implementation leaves with KErrNotSupported and returns NULL. + +@param aMediaId + An optional mediaID parameter when there are multiple buffers arriving of different media types. +@param aReference + A boolean indicating buffer ownership. ETrue if the MDataSource owns the buffer, EFalse if the caller owns the buffer. + +@return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers. +*/ +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) //CMMFDataPath can't create buffers + { + User::Leave(KErrNotSupported); + return NULL; + } + + +/** +Gets the source data type for the specified media ID. + +@param aMediaId + An optional parameter to specifiy specific stream when datasource contains more than one stream of data. + +@return The source data type. +*/ +EXPORT_C TFourCC CMMFDataPath::SourceDataTypeCode(TMediaId /*aMediaId*/) + { + return(iSourceFourCC); + } + + +/** +Allocates buffers in preparation to play. + +Must be called before calling PlayL(). + +iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink +For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL(). +*/ + +EXPORT_C void CMMFDataPath::PrimeL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::PrimeL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + //allocate resources ie buffers & prepare to play + if (iDataPathCreated && (iState == EStopped)) + //luckily the client utility does this. + {//can only prime from the stopped state + + //This will determine what buffers we need to run the datapath. + //Can leave KERRNotSupported + iBuffersToUse = DetermineBuffersToUseL(); + + //Try to create source and sink buffers. If we can't create them in the Prime, + //we will need to obtain them by asynchronous buffer creation when playing starts. + ObtainSyncBuffersL(); + + iDataSource->SourcePrimeL(); //propogate state change to source + iDataSink->SinkPrimeL(); //propogate state change to sink + + + //If Client has set these, they will be set following the prime + iPlayWindowStartPosition = 0; + iPlayWindowEndPosition = Duration(); + + iState = EPrimed; + iPauseCalled = EFalse; + + if (iCompleteCallback) + { + delete iCompleteCallback; + iCompleteCallback = NULL; + } + TBool waitForSink = (iDataSink->DataSinkType() == KUidMmfAudioOutput)?ETrue:EFalse; + iCompleteCallback = new (ELeave) CCompleteCallback(*this,waitForSink); + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::PrimeL Done tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + +/** +Starts an active scheduler 'play' loop. + +Can only play from the primed state. +*/ +EXPORT_C void CMMFDataPath::PlayL() + { +#if defined(__PROFILING) + RDebug::ProfileEnd(1); +#endif // defined(__PROFILING) + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::PlayL, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); + RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64())); +#endif + + if ((iDataPathCreated) && (iState == EPrimed)) + { + //can only play from the primed state + + TBool savedPauseCalled=EFalse; + if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed + { + savedPauseCalled=ETrue; + iDataSink->SinkPrimeL(); //propagate change down to sink + iPauseCalled = EFalse; + } + + iCurrentSourceFrameNumber = 0; //reset to beginning + iCurrentSinkFrameNumber = 0; //reset to beginning + + iSourceBufferWithSource = EFalse; + iSinkBufferWithSink = EFalse; + + iNoMoreSourceData = EFalse; + iAllDataSentToSink=EFalse; + iDataPathCompletedErrorCode=KErrNone; + + SetPositionL( iStartPosition ) ; + iReferenceAudioSamplesPlayed = 0; + iReferenceAudioSamplesRecorded = 0; + + //complete a request on iStatus to invoke play code + iDataSource->SourcePlayL(); //propagate state change to source + if (!(savedPauseCalled && (iTransferState==EWaitSink || iTransferState==EInitializeSink))) + { + iDataSink->SinkPlayL(); //propogate state change to sink + } + + if (iDataSink->DataSinkType() == KUidMmfAudioOutput) + iState = EPlaying; + else if(iDataSource->DataSourceType() == KUidMmfAudioInput) + iState = ERecording; + else + iState = EConverting; + + //need to re-initialize any buffer(s) that we only own references to + ChangeDataPathTransferState(EInitializeSink); + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::Play - DONE\n")); +#endif + } + + +/** +Pauses playing. + +Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs. +*/ +EXPORT_C void CMMFDataPath::Pause() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::Pause, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); + RDebug::Print(_L("DP::Pause current state=%d tick-%d (this 0x%x)\n"),iTransferState, User::TickCount(),this); +#endif + + TRAPD(err, DoPauseL()); + + if (err) + { + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::Pause - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + +/** +Stops playing. + +Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError +to the client if an error occurs. +*/ +EXPORT_C void CMMFDataPath::Stop() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::Stop current state=%d tick-%d (this 0x%x)\n"), iTransferState, User::TickCount(),this); +#endif + + if ((iDataPathCreated) && (iState != EStopped)) + { + TRAPD(err, DoStopL()); + + if (err) + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); + } + } +/** +Forces and end of data state on the datapath +*/ +EXPORT_C void CMMFDataPath::EndOfData() + { + TRAPD(err, DoEndOfDataL()); + + if (err) + { + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); + } + } + +/** + +*/ +TInt CMMFDataPath::AudioSamplesPlayed() const + { + if (iDataSink->DataSinkType() != KUidMmfAudioOutput) + return 0; + + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); + + TInt samples = 0; + + if(iState == EPlaying) + samples = audioOutput->SoundDevice().SamplesPlayed(); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::AudioSamplesPlayed = %d\n"),samples); +#endif + + return samples; + } + + +/** + +*/ +TInt CMMFDataPath::AudioSamplesRecorded() const + { + if (iDataSource->DataSourceType() != KUidMmfAudioInput) + return 0; + + CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); + + TInt samples = 0; + + if(iState == ERecording) + samples = audioInput->SoundDevice().SamplesRecorded(); + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::AudioSamplesRecorded = %d\n"),samples); +#endif + + return samples; + } + + +/** + +*/ +TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioOutputPosition() const + { + //This operation can only be carried out on an Audio Output + __ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); + + + //If we are not playing, simply return where we will play from + if(iState != EPlaying || iCurrentSinkFrameNumber == 0) + return iStartPosition; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed); +#endif + + TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed; + + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); + CMMFDevSound& devSound = audioOutput->SoundDevice(); + + TMMFCapabilities devSoundConfig = devSound.Config(); + + TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); + +#ifdef _DP_DEBUG + RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); +#endif + + TReal timePlayedSeconds = 0; + if(samplesPlayed) + timePlayedSeconds = samplesPlayed/samplingFreq; + + TInt64 timePlayed(I64DOUBLECAST(timePlayedSeconds * 1000000)); + +#ifdef _DP_DEBUG + RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed)); +#endif + + return TTimeIntervalMicroSeconds(timePlayed + iStartPosition.Int64()); + } + + + +/** + +*/ +TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioInputPosition() const + { + //This operation can only be carried out on an Audio Input + __ASSERT_ALWAYS(iDataSource->DataSourceType() == KUidMmfAudioInput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); + + + //If we are not playing, simply return where we will play from + if(iState != ERecording) + return iStartPosition; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::CalculateAudioInputPosition from %d\n"),iReferenceAudioSamplesRecorded); +#endif + + TReal samplesRecorded = AudioSamplesRecorded() - iReferenceAudioSamplesRecorded; + + CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); + CMMFDevSound& devSound = audioInput->SoundDevice(); + + TMMFCapabilities devSoundConfig = devSound.Config(); + + TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); + +#ifdef _DP_DEBUG + RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); +#endif + + TReal timeRecordedSeconds = 0; + if(samplesRecorded) + timeRecordedSeconds = samplesRecorded/samplingFreq; + + TInt64 timeRecorded(I64DOUBLECAST(timeRecordedSeconds * 1000000)); + +#ifdef _DP_DEBUG + RDebug::Print(_L("timeRecorded %d\n"), I64LOW(timeRecorded)); +#endif + return TTimeIntervalMicroSeconds(timeRecorded); + } + + + + + +TTimeIntervalMicroSeconds CMMFDataPath::OutputPosition() const + { + TTimeIntervalMicroSeconds interval; + TTimeIntervalMicroSeconds position; + + if (iDataSink->DataSinkType() == KUidMmfAudioOutput) + { + position = CalculateAudioOutputPosition(); +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64())); +#endif + } + else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) + { + //note Encode format position takes priority if both source & sink are formats? + // try to get the position directly from the format. If that fails, work it out here + TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL()); + if (error)//getting the position from the format didn't work so calculate it here + { + interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); + TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber; + position = position64; + } + + TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration + if (position > duration)//this can happen on last buffer + position = duration; + + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::OutputPosition from format= %d\n"),I64INT(position.Int64())); +#endif + } + else + {//can only read output position if sink is a format or an audio output + return TTimeIntervalMicroSeconds(0); + } + + return position; + } + +TTimeIntervalMicroSeconds CMMFDataPath::InputPosition() const + { + TTimeIntervalMicroSeconds interval; + TTimeIntervalMicroSeconds position; + + if (iDataSource->DataSourceType() == KUidMmfAudioInput) + { + position = CalculateAudioInputPosition(); +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InputPosition from audio input= %d\n"),I64INT(position.Int64())); +#endif + } + else if (iDataSource->DataSourceType() == KUidMmfFormatDecode) + {//note Decode format position takes priority if both source & sink are formats? + // try to get the position directly from the format. If that fails, work it out here + TRAPD(error, position = ((CMMFFormatDecode*)iDataSource)->PositionL()); + if (error)//getting the position from the format didn't work so calculate it here + { + interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); + TInt64 position64 = interval.Int64() * iCurrentSourceFrameNumber; + position = position64; + } + + TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration + if (position > duration)//this can happen on last buffer + position = duration; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::InputPosition from format = %d\n"),I64INT(position.Int64())); +#endif + } + else + {//can only read input position if source is a format or an audio input + return TTimeIntervalMicroSeconds(0); + } + + return position; + } + + + + +/** +Gets the data path position. + +@return The data path position. +*/ +EXPORT_C TTimeIntervalMicroSeconds CMMFDataPath::Position() const + { + if ((iState == ERecording) || (iState == EConverting)) + return InputPosition(); + else if(iState == EPlaying) + return OutputPosition(); + else + { + return iStartPosition; + } + } + +/** +Sets the data path position. + +@param aPosition + The data path position. +*/ +EXPORT_C void CMMFDataPath::SetPositionL(const TTimeIntervalMicroSeconds& aPosition) + {//need to map to source position to frame position +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::SetPositionL = %d ticks-%d (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this); +#endif + + if (iState == EStopped) + User::Leave(KErrNotReady); //can only set position if primed + + //As this will affect the position, we need to know how many bytes were + //played when position was updated. Future Position() requests will + //then use this refernce to determine the current position. + iReferenceAudioSamplesPlayed = AudioSamplesPlayed(); + + // Force the new position to be inside the play window (also within the file duration) + if ( aPosition < iPlayWindowStartPosition ) + iStartPosition = iPlayWindowStartPosition; + else if ( aPosition > iPlayWindowEndPosition ) + iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played + else + iStartPosition = aPosition; + + TTimeIntervalMicroSeconds interval; + + //can only set the position on an MDataSource that is a format object + //Note: position defaults to source if both source & sink are clips + if (iDataSource->DataSourceType() == KUidMmfFormatDecode) + { + //position is not beyond the end of file + interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); + + // for some reason this code won't compile without these intermediate steps + TInt64 position = iStartPosition.Int64(); + TInt64 interval64 = interval.Int64(); + if (interval64 == 0) + User::Leave(KErrDivideByZero); + TInt64 datapos64 = position/interval64; + iCurrentSourceFrameNumber = I64LOW(datapos64); + + + // Try to set the position directly on the format + TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition)); + //NB: don't worry about error, since we'll reposition anyway when we get the next buffer + } + else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) + { + //position is not beyond the end of file + interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); + + //convert to TUint - for some reason it won't compile without these intermediate steps + TInt64 position = iStartPosition.Int64(); + TInt64 interval64 = interval.Int64(); + if (interval64 == 0) + User::Leave(KErrDivideByZero); + TInt64 datapos64 = position/interval64; + iCurrentSinkFrameNumber = I64LOW(datapos64); + + + // Try to set the position directly on the format + TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition)); + //NB: don't worry about error, since we'll reposition anyway when we get the next buffer + } + else + {//can only set position if source or sink is a format + //If both source and sink are formats position is relative to the source + User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips + } + + if(iCodec) //we have a real codec, must reset it + iCodec->ResetL(); // Need to preserve sync when resuming play + + // Once we've sent the last buffer to the sink it's too late to start + // changing the state since we may get a RunError(KErrUnderflow) at any time. + // Once this happens, the sound driver may have unloaded etc..and recovery + // would be complicated. + if (iAllDataSentToSink) + return; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d iStartPosition=%d ticks-%d (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this); +#endif + } + + + + +/** +Sets the play window absolutely (i.e. the parameters are relative to the start of the entire clip). + +@param aStart + The offset from the start of the Clip +@param aEnd + The offset from the end of the clip (if this is less than aStart, then the two will be inverted). +*/ +EXPORT_C void CMMFDataPath::SetPlayWindowL( const TTimeIntervalMicroSeconds& aStart, const TTimeIntervalMicroSeconds& aEnd ) + { + // Clear the existing Play window + ClearPlayWindowL() ; + + // Check that the parameters are legitimate. 0 <= startpos < endpos <= duration & update member variables + TTimeIntervalMicroSeconds duration = Duration(); + + if ( aStart < TTimeIntervalMicroSeconds(0) ) + iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0); + else if ( aStart > duration ) + iPlayWindowStartPosition = duration; + else iPlayWindowStartPosition = aStart; + + if ( aEnd < TTimeIntervalMicroSeconds(0) ) + iPlayWindowEndPosition = TTimeIntervalMicroSeconds(0); + else if ( aEnd > duration ) + iPlayWindowEndPosition = duration; + else iPlayWindowEndPosition = aEnd; + + // ensure that the current position is inside the new play window + if ( iPlayWindowEndPosition != TTimeIntervalMicroSeconds(0) ) + { + TTimeIntervalMicroSeconds currentPosition = Position() ; + if ( currentPosition < iPlayWindowStartPosition ) + SetPositionL( iPlayWindowStartPosition ) ; + else if ( currentPosition > iPlayWindowEndPosition ) + SetPositionL( iPlayWindowEndPosition ) ; + } + else + ClearPlayWindowL() ; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::SetPlayWindowL iPlayWindowStartPosition=%d iPlayWindowEndPosition=%d\n"),I64INT(iPlayWindowStartPosition.Int64()),I64INT(iPlayWindowEndPosition.Int64())); +#endif + } + + +/** +Sets the play window to the full length of clip. +*/ +EXPORT_C void CMMFDataPath::ClearPlayWindowL() + { + iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0) ; + iPlayWindowEndPosition = Duration(); + + + if(iState == EStopped) + iStartPosition = iPlayWindowStartPosition; + } + +/** +Returns the current data path state. +*/ +EXPORT_C TInt CMMFDataPath::State() + { + return iState ; + } + + +/** +Uses the AO mechanism to drive state changes between sources and sinks. + +RunL() moves and assigns buffers between its attached MDataSource/MDataSinks. +*/ +void CMMFDataPath::ChangeDataPathTransferState(TTransferState aNewDataPathTransferState) + { +#ifdef _DP_DEBUG + switch (aNewDataPathTransferState) + { + case EWaitSink: + RDebug::Print(_L("Next State EWaitSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case EWaitSource: + RDebug::Print(_L("Next State EWaitSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case EInitializeSink: + RDebug::Print(_L("Next State EInitializeSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case EInitializeSource: + RDebug::Print(_L("Next State EInitializeSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case ENeedSourceData: + RDebug::Print(_L("Next State ENeedSourceData ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case ENeedSinkData: + RDebug::Print(_L("Next State ENeedSinkData ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case ENeedToMatchSourceToSink: + RDebug::Print(_L("Next State ENeedToMatchSourceToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case ESendDataToSink: + RDebug::Print(_L("Next State ESendDataToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + case EEndOfData: + RDebug::Print(_L("Next State EEndOfData ticks-%d (this 0x%x)\n"),User::TickCount(),this); + break; + } +#endif + + + TRequestStatus* stat = &iStatus; + //change state + iTransferState = aNewDataPathTransferState; + if ((iTransferState != EWaitSink) && (iTransferState != EWaitSource) && + (iState == EPlaying || iState == ERecording || iState == EConverting || (iState == EPrimed && iPauseCalled))) + {//can go ahead with transfer + if (!IsActive()) + { + User::RequestComplete(stat, KErrNone); + SetActive(); + } + } +#ifdef _DP_DEBUG + else + RDebug::Print(_L("Datapath is no longer active, not going to new state (this 0x%x)\n"),this); +#endif + } + +/** +Runs the clip depending on the current data path and transfer state. + +For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData. +*/ +EXPORT_C void CMMFDataPath::RunL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::RunL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + switch (iState) + { + case EStopped: + break; + case EPrimed: + //paused with stored position + break; + case EPlaying: + case ERecording: + case EConverting: + switch (iTransferState) + { + case EWaitSink: + case EWaitSource: + break; + case EInitializeSink: + InitializeSinkL(); + break; + case EInitializeSource: + InitializeSourceL(); + break; + case ENeedSourceData: + FillSourceBufferL(); + break; + case ENeedSinkData: + FillSinkBufferL(); + break; + case ENeedToMatchSourceToSink: + FillSinkBufferL(); + break; + case ESendDataToSink: + EmptySinkBufferL(); + break; + case EEndOfData: + EndOfData(); + break; + } + break; + default: + break; + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::RunL DONE\n")); +#endif + } + +/** +Cancels the clip. + +The default implementation is empty. +*/ +EXPORT_C void CMMFDataPath::DoCancel() + { + //don't need to do anything as we don't have any async requests to other objects + } + +/** +Handles errors coming from attached sources and passes them to the clients. + +@param aError + Standard error code (KErrNone = No Error). + +@return The event code returned to the data path. KErrNone if end of file is encountered. +*/ +EXPORT_C TInt CMMFDataPath::RunError(TInt aError) + { + return DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, aError); + } + + +/** +Returns the duration of the clip. + +@return The length of clip in TTimeIntervalMicroSeconds. +*/ +TTimeIntervalMicroSeconds CMMFDataPath::Duration() const + { + TTimeIntervalMicroSeconds duration(0); + + if ( iDataSource && ( iDataSource->DataSourceType() == KUidMmfFormatDecode ) ) + { + //this updated version of datapath caches the duration of the + //source clip for efficiency - since this meathod is const + //we need to cast away the constness to set iCachedSourceDuration + CMMFDataPath* thisNonConst = const_cast(this); + duration = STATIC_CAST(CMMFFormatDecode*, thisNonConst->iDataSource )->Duration( iMediaId ) ; + thisNonConst->iCachedSourceDuration = duration; + } + else if ( iDataSink && ( iDataSink->DataSinkType() == KUidMmfFormatEncode ) ) + duration = STATIC_CAST(CMMFFormatEncode*, iDataSink )->Duration( iMediaId ) ; + + return duration ; + } + +/** +This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. + +Additional Stop() method specific to this datapath. +*/ +void CMMFDataPath::DoStopL() + { + // Note that the datapath needs to be paused first + // before it can be stopped - this is important for audio play + // for instance to effect an immediate stop rather than playing out + // the current buffer. + + + + // Stop data source and sink + iDataSource->SourceStopL(); // propagate state change to source + iDataSink->SinkStopL(); // propagate change down to sink + + iSinkBufferWithSink = EFalse; + iSourceBufferWithSource = EFalse; + + //Contains the completion code if the datapath completes as a result of an error + iDataPathCompletedErrorCode = KErrNone; + + ResetRefBuffers(); // buffer references may not be valid any more + + SetPositionL(iPlayWindowStartPosition); // reset position + + iState = EStopped; // stop succeeded, set state to stopped + } + +/** +This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. + +Additional Pause method specific to this datapath. + +The DataPath implements pause by recording the current position and stopping the source and sink. +When Play is called the DataPath uses the stored position as the starting point and resumes +playing. The reason for this (rather than using the PauseL() API on sources and sinks) is that +some implementations do not support a suitable pause, therefore the DataPath is implemented to +support the lowest common denominator. + +Note: +A suitable pause implementation will retain any buffers in use. There will be no +need to call PrimeL() prior to PlayL(). +*/ +void CMMFDataPath::DoPauseL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoPauseL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + + if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting))) + { + // try to pause source and sink + iDataSource->SourcePauseL(); // propagate state change to source + SetPositionL(Position()); + iDataSink->SinkStopL(); + + iPauseCalled = ETrue; // indicate pause is called + + iState = EPrimed; // go back to primed state (we're not playing) + + iSinkBufferWithSink = EFalse; + iSourceBufferWithSource = EFalse; + + ResetRefBuffers(); // buffer references may not be valid any more + + Cancel(); //Stop the state machine + } + else if(iState == ERecording) + { + iPauseCalled = ETrue; +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n")); +#endif + iDataSource->SourcePauseL(); + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed); + RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this); +#endif + } + +/** +This is a virtual function datapath (or derivations off) that can be implemented or may be left blank for default behaviour. + +Additional Pause method specific to this datapath. +*/ +void CMMFDataPath::DoEndOfDataL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoEndOfDataL tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + SetPositionL(iPlayWindowStartPosition); // reset position + + ASSERT(iCompleteCallback); + iCompleteCallback->SignalDataPathComplete(iDataPathCompletedErrorCode); + + ResetRefBuffers(); + iState = EStopped; + +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoEndOfDataL - Done tick-%d (this 0x%x)\n"),User::TickCount(),this); +#endif + } + + +/** +Passes error handling and general messages up to clients. + +@param aEventType + Category code for the event. Category codes can be used as unique identifers. +@param aErrorCode + Standard error code. + +@return The event code sent to client. +*/ +//error handling +EXPORT_C TInt CMMFDataPath::DoSendEventToClient(TUid aEventType, TInt aErrorCode) + { + TMMFEvent event(aEventType, aErrorCode); + return iEventHandler.SendEventToClient(event); + } + +/** +Passes error handling and general messages to clients. + +@param aEvent + TMMFEvent supplied by callee (typically DoSendEventToClient) + +@return The Event Message sent to the datapath event handler +*/ +EXPORT_C TInt CMMFDataPath::SendEventToClient(const TMMFEvent& aEvent) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::SendEventToClient CODE = %d ticks=%d (this 0x%x)\n"),aEvent.iErrorCode,User::TickCount(),this); + RDebug::Print(_L("CMMFDataPath::SendEventToClient iEventType = %d == %d\n"),aEvent.iEventType, KMMFEventCategoryPlaybackComplete); +#endif + + //If we have sent all the data to the sink, it is legal for it to send KErrUnderFlow + //to us and we can go through a clean shutdown, otherwise we pass the error to the + //controller and it is responsible for determining what to do + if(iAllDataSentToSink && + (aEvent.iEventType == KMMFEventCategoryPlaybackComplete) && + (aEvent.iErrorCode == KErrUnderflow)) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::SendEventToClient Clean complete\n")); +#endif + //sink may not return the final buffer once it has finished with it + //force ourselves into the EndOfData state + if(iTransferState != EEndOfData) + { + iDataPathCompletedErrorCode = KErrNone; + TRAP_IGNORE(DoEndOfDataL()); + } + + iCompleteCallback->SignalSinkComplete(KErrNone); + return KErrNone; + } + + + + return iEventHandler.SendEventToClient(aEvent); + } + +CMMFDataPath::CCompleteCallback::CCompleteCallback(CMMFDataPath& aDataPath, TBool aWaitForSink) + : CActive(EPriorityStandard), + iDataPath(aDataPath), + iWaitForSink(aWaitForSink) + { + CActiveScheduler::Add(this); + } + +CMMFDataPath::CCompleteCallback::~CCompleteCallback() + { + Cancel(); + } + +void CMMFDataPath::CCompleteCallback::SignalDataPathComplete(TInt aDataPathError) + { + iDataPathComplete = ETrue; + iDataPathError = aDataPathError; + if (!IsActive()) + { + // Signal ourselves to run with the given completion code + TRequestStatus* status = &ActiveStatus(); + User::RequestComplete(status, KErrNone); + } + } + +void CMMFDataPath::CCompleteCallback::SignalSinkComplete(TInt aSinkError) + { + iSinkComplete = ETrue; + iSinkError = aSinkError; + if (!IsActive()) + { + // Signal ourselves to run with the given completion code + TRequestStatus* status = &ActiveStatus(); + User::RequestComplete(status, KErrNone); + } + } + + +TRequestStatus& CMMFDataPath::CCompleteCallback::ActiveStatus() + { + SetActive(); + return iStatus; + } + +void CMMFDataPath::CCompleteCallback::DoCancel() + { + } + +void CMMFDataPath::CCompleteCallback::RunL() + { + if (iWaitForSink) + { + if (iDataPathComplete && iSinkComplete) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + TRAP_IGNORE(iDataPath.DoStopL()) + + iDataPathComplete = EFalse; + iSinkComplete = EFalse; + + // if we have to wait for the sink to complete, always use the sink error + iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iSinkError); + } + } + else if (iDataPathComplete) + { +#ifdef _DP_DEBUG + RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); +#endif + + TRAP_IGNORE(iDataPath.DoStopL()) + + iDataPathComplete = EFalse; + iSinkComplete = EFalse; + + iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iDataPathError); + } + } + +EXPORT_C TInt CMMFDataPath::SetBlockLength(TUint aBlockLength) + { + MMMFDevSoundCustomInterfaceFileBlockLength* fileBlockLengthCI = NULL; + TInt err = KErrNotSupported; + if (iCodec) + { + err = iCodec->ExtensionInterface(KUidCustomInterfaceDevSoundFileBlockLength.iUid, (TAny*&)fileBlockLengthCI); + } + + if (err == KErrNone) + { + fileBlockLengthCI->SetFileBlockLength(aBlockLength); + } + + return err; + } +