mmlibs/mmfw/src/server/BaseClasses/mmfdatapath.cpp
changeset 0 b8ed18f6c07b
child 5 b220a9341636
--- /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;
+	}
+