--- /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 <e32math.h>
+#include <mmf/common/mmffourcc.h>
+#include <mmf/common/mmfpaniccodes.h>
+#include <mmf/server/mmfaudiooutput.h>
+#include <mmf/server/mmfaudioinput.h>
+#include <mmf/server/mmfdatapath.h>
+#include "mmfclientaudiostreamutils.h"
+#include <mmf/common/mmfaudio.h>
+#include <mmf/plugin/mmfcodecimplementationuids.hrh> // KUidMmfCodecAudioSettings
+#include <mmf/server/devsoundstandardcustominterfaces.h>
+
+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<CMMFFormatDecode*>(aSource)->SampleRate();
+ audioSettings.iChannels = static_cast<CMMFFormatDecode*>(aSource)->NumChannels();
+ }
+
+ //package up to send to codec
+ TPckgBuf<TMMFAudioConfig> 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<CMMFDataPath*>(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;
+ }
+