devsound/devsoundrefplugin/src/swcodecwrapper/mmfSwCodecPlayDataPath.cpp
changeset 0 79dd3e2336a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devsound/devsoundrefplugin/src/swcodecwrapper/mmfSwCodecPlayDataPath.cpp	Fri Oct 08 19:40:43 2010 +0100
@@ -0,0 +1,752 @@
+// Copyright (c) 2003-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\mmfswcodecplaydatapath.cpp
+// 
+//
+
+#include "mmfSwCodecPlayDataPath.h"
+#include <mmf/server/mmfswcodecwrapper.h>
+#include <mmf/server/mmfswcodecwrappercustominterfacesuids.hrh>
+#include <mmf/common/mmfpaniccodes.h>
+#include "mmfSwCodecUtility.h"
+
+const TInt KBytesPerSample = 2;
+const TInt KMaxBytesInSec = 192000; //considering maximum samplerate 96KHz
+CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL()
+	{
+	CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+void CMMFSwCodecPlayDataPath::ConstructL()
+	{
+	iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput);
+	iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
+	iUtility = CMMFSwCodecUtility::NewL();
+	iVbrFlag = EFalse;
+	}
+
+
+CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath()
+	{
+	delete iAudioPlayer;
+	delete iSoundDeviceErrorReceiver;
+	delete iUtility;
+
+	iSoundDevice.Close();
+
+	if (iCodec)
+		{
+		delete iSourceBuffer;
+		if (!iCodec->IsNullCodec()) 
+			{
+			delete iSoundDeviceBuffer;
+			}
+		}
+
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+	delete iTransferWindow;
+
+	if(iTransferBuffer)
+		{
+		iTransferBuffer->Close();
+		delete iTransferBuffer;
+		}
+#endif
+
+#ifdef __USE_MMF_PTRBUFFERS__
+	delete iPtrBufferMemoryBlock;
+#endif
+	}
+
+
+TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
+	{
+	TInt error;
+	if (iHwDeviceObserver)
+		{
+		error =  KErrAlreadyExists;
+		}
+	else
+		{
+		iHwDeviceObserver = &aObserver;
+		error  = KErrNone;
+		}
+	return error;
+	}
+
+
+TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec)
+	{
+	if (iCodec)
+		return KErrNotSupported; //doesn't support multiple codecs
+
+	TInt err = KErrNone;
+	
+	iCodec = &aCodec;
+
+	// Allocate data buffer
+	iSourceBufferSize = iCodec->SourceBufferSize();
+	iSoundDevBufferSize = iCodec->SinkBufferSize();
+
+	if ((!iSourceBufferSize)||(!iSoundDevBufferSize))
+		err = KErrArgument; //codec plugin has not specified buffer size
+
+	if (err == KErrNone)
+		{
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+		TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer)));
+#endif
+#ifdef __USE_MMF_PTRBUFFERS__
+		TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize));
+#else
+		TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize));
+#endif
+		}
+	
+	if (err == KErrNone)
+		{
+		if (iCodec->IsNullCodec())
+			{//use source buffer for sound device buffer	
+			iSoundDeviceBuffer = NULL;
+			}
+		else
+			{//codec needs separate source and sound device buffers
+			TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize));
+			}
+		}
+	return err;
+	}
+
+
+TInt CMMFSwCodecPlayDataPath::Start()
+	{
+	TInt startError = KErrNone;
+
+	if (!iCodec) 
+		{//check that a codec has been added
+		startError = KErrNotReady;
+		}
+	if ((!iSoundDevice.Handle())&&(!startError))
+    	{//check that the sound drivers can be opened
+   		startError = iSoundDevice.Open();
+		}
+
+	if (iState == EPaused)
+		{//we are paused so need to resume play
+		if (!startError)
+			{
+#ifdef _SCW_DEBUG
+			RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume"));
+#endif
+			iAudioPlayer->ResumePlaying();
+			iState = EPlaying;
+			}
+		}
+	else if (!startError)
+		{
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal"));
+#endif
+		// get sample rate and channels from RMdaDevSound
+		RMdaDevSound::TCurrentSoundFormatBuf format;
+		iSoundDevice.GetPlayFormat(format);
+		iSampleRate = format().iRate;
+		iChannels = format().iChannels;
+		
+		iNoMoreSourceData = EFalse;
+		iNoMoreSoundDeviceData = EFalse;
+		iSourceBuffer->SetLastBuffer(EFalse);
+		iBytesPlayed = 0;
+		iState = EPlaying;
+		iSoundDeviceErrorReceiver->Start();
+		TRAP(startError,FillSourceBufferL()); //get initial buffer of audio data
+		if (startError == KErrNone)
+			{
+			// Start the player objects
+			iAudioPlayer->Start();
+			}
+		else
+			{//failed to start up correctly go back to stopped state
+			iState = EStopped;
+			iSoundDeviceErrorReceiver->Stop();
+			}
+   		}
+	return startError;
+	}
+
+
+// *** Main Play Loop ***
+
+void CMMFSwCodecPlayDataPath::FillSourceBufferL()
+	{//asks observer to fill the source buffer          
+    // Ask immediately for data from the observer
+#ifdef __CYCLE_MMF_DATABUFFERS__
+	// Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
+	// If the creation fails, we carry on regardless as the original buffer will not have been 
+	// destroyed. Must do this as alloc fail tests will not run.
+	if(iSourceBuffer)
+		{
+		iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
+		}
+#endif // __CYCLE_MMF_DATABUFFERS__	
+	User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer));
+	
+	}
+
+
+void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer)
+	{//call back from observer to indicate buffer has been filled
+	if (iState == EStopped)
+		User::Leave(KErrNotReady);//ok if paused?
+
+	iSourceBuffer = &aBuffer;
+	iSourceBuffer->SetStatus(EFull);
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL"));
+#endif
+
+	//need to check that the buffer size is not 0 - if so assume we've reached the end of the data
+	if (!iSourceBuffer->BufferSize())
+		{//no buffer  - could be end of source or could be that the source has no data??
+		iNoMoreSourceData = ETrue;
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData"));
+#endif
+		}
+	//even if the buffer size is 0 we still 
+	//need to perform the following to get the sound device callback
+	FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device	
+
+    /* iVbrFlag is added to datapath to avail the alternative dataflow wherein datapath makes sure that 
+    destinationbuffer is filled to its maximum length before sending it to the sound driver. 
+    Sending the buffer directly to the device causes underflow incase of Vorbis codecs.*/
+    if (iVbrFlag)
+    	{
+    	/*There are two cases we need to deal here
+      	1. When the output of the codec is 0 for header data.
+           in that case, repeat till actual decoding of ogg packets and pages.
+        2. When destination buffer is not filled even to its half length, get next source buffer
+           and decode it. This is to avoid underflows when ever we receive little pcm for a 
+           a given source buffer.
+           */
+	    if (iSoundDeviceBuffer->Data().Length() < iSoundDeviceBuffer->Data().MaxLength()/2 && !(iSoundDeviceBuffer->LastBuffer()))
+			{
+	    	iSourceBuffer->SetStatus(EAvailable); //source buffer is now available
+	    	iSoundDeviceBuffer->SetPosition(iSoundDeviceBuffer->Data().Length());//this indicates the available space in the buffer to the codec
+	    	FillSourceBufferL();	
+	    	return;
+	    	}
+		else //data is sufficient to avoid underflows
+			{
+			iSoundDeviceBuffer->SetPosition(0);
+			if(iSoundDeviceBuffer->Data().Length()==0 && iSoundDeviceBuffer->LastBuffer())
+				{
+				iNoMoreSoundDeviceData = ETrue;
+				}
+			}	
+    	}
+
+    // attenuate the amplitude of the samples if volume ramping has been changed
+	// and is non-zero
+	if (iCustomInterface)
+		{
+		TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp();
+		if (volumeRamp != iVolumeRamp)
+			{
+			iVolumeRamp = volumeRamp;
+			if (iVolumeRamp.Int64() != 0)
+				{
+				iUtility->ConfigAudioRamper(
+					iVolumeRamp.Int64(), 
+					iSampleRate, 
+					iChannels);
+				iRampAudioSample = ETrue;
+				}
+			else
+				{
+				iRampAudioSample = EFalse;
+				}
+
+			}
+			if (iRampAudioSample)
+				iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer);
+		}
+
+	iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
+
+	if (iSourceBuffer->LastBuffer())//check last buffer flag
+		{
+		iNoMoreSourceData = ETrue;
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData"));
+#endif
+		}
+	}
+
+
+void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL()
+	{//use CMMFSwCodec to fill the sound device buffer
+	
+	CMMFSwCodec::TCodecProcessResult codecProcessResult;
+
+	if (iCodec->IsNullCodec())
+		{//no codec so data can be sent direct to sink
+		iSoundDeviceBuffer = iSourceBuffer;
+		iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full
+		}	
+	else 
+		{	
+		//pass buffer to codec for processing
+		codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer);
+		
+		if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev
+			iSoundDeviceBuffer->SetLastBuffer(ETrue);
+		if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded))
+			{//the codec has added data but not set the buffer length
+			iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded);
+			}
+		//only supports EProcessComplete
+		switch (codecProcessResult.iCodecProcessStatus)
+			{
+		case CMMFSwCodec::TCodecProcessResult::EProcessComplete:
+		//finished procesing source data - all data in sink buffer
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+#ifdef SYMBIAN_VARIABLE_BITRATE_CODEC
+		case CMMFSwCodec::TCodecProcessResult::EProcessIncomplete:
+		//finished procesing source data - all data in sink buffer
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+#endif
+		case CMMFSwCodec::TCodecProcessResult::EDstNotFilled:
+		//could be the last buffer in which case dst might not get filled
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+		case CMMFSwCodec::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.
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
+			iNoMoreSourceData = ETrue;
+			//doesn't matter if sink buffer is not full
+			}
+		break;
+		default:
+			Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec
+			}
+		}
+	}
+
+
+void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
+	{//call back from CDataPathPlayer when the sound device buffer has been emptied
+	if (&aBuffer != iSoundDeviceBuffer) 
+		Panic(EMMFSwCodecWrapperBadBuffer);
+	if(iVbrFlag && (iSourceBuffer->Status() == EUnAvailable || iNoMoreSourceData))
+		{//No more source data. Play rest of the decoded data.Inform codec not to consider the source buffer
+		if(iSourceBuffer->Status()!=EUnAvailable)
+			{
+			iSourceBuffer->SetStatus(EUnAvailable);
+			}
+		FillSoundDeviceBufferL();
+		if(iSoundDeviceBuffer->BufferSize() > 0)
+			{
+			// attenuate the amplitude of the samples if volume ramping has been changed
+			// and is non-zero
+			if (iCustomInterface)
+				{
+				TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp();
+				if (volumeRamp != iVolumeRamp)
+					{
+					iVolumeRamp = volumeRamp;
+					if (iVolumeRamp.Int64() != 0)
+						{
+						iUtility->ConfigAudioRamper(iVolumeRamp.Int64(), iSampleRate, iChannels);
+						iRampAudioSample = ETrue;
+						}
+					else
+						{
+						iRampAudioSample = EFalse;
+						}
+
+					}
+				if (iRampAudioSample)
+					{
+					iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer);
+					}
+					
+				}
+			iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
+			return;
+			}
+		else
+			{
+			if(iNoMoreSourceData)
+				{
+				iNoMoreSoundDeviceData = ETrue;
+				}
+			iSourceBuffer->SetStatus(EAvailable);
+			}
+		}
+	if (!iNoMoreSourceData) 
+		FillSourceBufferL();
+	}
+
+//*** End of Main Play Loop ***
+
+
+void CMMFSwCodecPlayDataPath::Stop()
+	{
+	iAudioPlayer->Cancel();
+	iSoundDeviceErrorReceiver->Cancel();
+    iSoundDevice.Close();
+
+#ifdef __CYCLE_MMF_DATABUFFERS__
+	// Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
+	// If the creation fails, we carry on regardless as the original buffer will not have been 
+	// destroyed. Must do this as alloc fail tests will not run.
+	if(iSourceBuffer)
+		{
+		iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
+		}
+#endif // __CYCLE_MMF_DATABUFFERS__	
+
+	iState = EStopped;
+	}
+
+
+void CMMFSwCodecPlayDataPath::Pause()
+	{
+	//since a pause can happen anyway in the datatransfer -need to set to a known 
+	//state so that when play is resumed the behaviour is predictable
+	if (iSoundDevice.Handle())
+		{
+		iSoundDevice.PausePlayBuffer(); //needs new LDD
+		iState = EPaused;
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("Pause"));
+#endif
+		}
+	else
+		{//an error must have occured 
+		iState = EStopped;
+		}
+	}
+	
+	
+TInt CMMFSwCodecPlayDataPath::EmptyBuffers()
+	{
+	TInt error = KErrNone;
+	if (iSoundDevice.Handle() == 0)
+		{
+		error = KErrNotReady;
+		}
+	else
+		{ 
+		iAudioPlayer->Cancel();
+		iSoundDevice.CancelPlayData();
+		iSoundDeviceErrorReceiver->Stop();
+		iState = EStopped;
+		}
+	return error;
+	}	
+
+
+RMdaDevSound& CMMFSwCodecPlayDataPath::Device()
+	{
+	return iSoundDevice;
+	}
+
+
+void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError)
+	{
+	if(iIgnoreUnderflow)
+		{
+		if(!iVbrFlag && aError==KErrUnderflow && !iNoMoreSourceData)
+			{
+			//ignore underflow
+			return;
+			}
+		//for VBR codec data,no more source does not mean that no more sounddevice data
+		//so ignore underflows till the last buffer is played from the codec
+		else if(iVbrFlag && aError==KErrUnderflow && !iNoMoreSoundDeviceData)
+			{
+			//ignore underflow
+			return;
+			}	
+		}
+	
+	//this sends a request to the hw device observer usually Devsound
+	//to update the bytes played
+	//it is done here so that the sound driver can be closed prior to
+	//updating the plicy and sending the error back
+	TUid uidUpdateBytesPlayed;
+	uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed;
+	TPtrC8 dummy(0,0);
+	iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy);
+
+	//this closes RMdaDevSound.
+	Stop(); 
+
+	//inform devsound so it can update policy
+	iHwDeviceObserver->Stopped(); 
+
+	// Inform the observer of the exception condition
+	// We inform the hw device observer after the policy has been
+	// updated incase the observer relied on the error to assume
+	// the policy has been updated
+	iHwDeviceObserver->Error(aError);
+	}
+
+
+void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface)
+	{
+	iCustomInterface = &aCustomInterface;
+	}
+	
+/**
+Retrieves a custom interface to the device.
+The reference CMMFSwCodecWrapper supports two  custom interfaces,
+MEmptyBuffersCustomInterface and MSetVbrFlagCustomInterface
+
+@param	aInterface
+		Interface UID, defined with the custom interface.
+		aInterface = KMmfUidEmptyBuffersCustomInterface for MEmptyBuffersCustomInterface,
+					 KSetVbrFlagCustomInterfaceTypeUid for MSetVbrFlagCustomInterface
+		
+@return A pointer to the interface implementation, or NULL if the device can not
+		implement the interface requested. The return value must be cast to the
+		correct type by the user.
+*/
+TAny* CMMFSwCodecPlayDataPath::CustomInterface(TUid aInterface)
+	{
+	TAny* ret = NULL;
+	if (aInterface.iUid == KMmfUidEmptyBuffersCustomInterface)
+		{
+		MEmptyBuffersCustomInterface* result = static_cast<MEmptyBuffersCustomInterface*> (this);
+		ret = static_cast<TAny*>(result);
+		}
+	else if(aInterface.iUid == KSetVbrFlagCustomInterfaceTypeUid)
+		{
+		SetVbrFlag();
+		}
+	if (aInterface == KTimePlayedCustomInterfaceTypeUid)
+		{
+		MTimePlayedCustomInterface* result = static_cast<MTimePlayedCustomInterface*> (this);
+		ret = static_cast<TAny*>(result);
+		}
+	if (aInterface == KIgnoreUnderflowCustomInterfaceTypeUid)
+		{
+		MIgnoreUnderflowEventsCustomInterface* result = static_cast<MIgnoreUnderflowEventsCustomInterface*> (this);
+		ret = static_cast<TAny*>(result);
+		}
+	return ret;
+	}
+
+/**
+Used to set iVbrFlag on the datapath.
+
+This method is used to set the iVbrFlag in datapath. This flag is added to datapath to avail the 
+alternative dataflow wherein datapath makes sure that destinationbuffer is filled to its maximum length
+before sending it to the sound driver. Sending the buffer directly to the device causes underflow incase of VBR codecs.
+*/		
+void CMMFSwCodecPlayDataPath::SetVbrFlag()
+	{
+	iVbrFlag = ETrue;
+	}
+
+TInt CMMFSwCodecPlayDataPath::GetTimePlayed(TTimeIntervalMicroSeconds& aTime)
+	{
+	if(iSoundDevice.Handle())
+		{
+		TInt bytes = iSoundDevice.BytesPlayed();
+		//Work around for overflow of bytes played from driver.
+		//This code will be removed when Base provides the TimePlayed() API which returns the play time
+		//Assuming GetTimePlayed() gets called in an interval not more than 3 secs, reset driver's bytes played when it is near KMaxInt
+		if(bytes > (KMaxTInt - 3*KMaxBytesInSec))
+			{
+			iBytesPlayed = iBytesPlayed+bytes;
+			iSoundDevice.ResetBytesPlayed();
+			bytes = 0;
+			}
+		TInt64 samplesPlayed = (iBytesPlayed+bytes)/(KBytesPerSample*iChannels);
+		aTime = (samplesPlayed*1000000)/iSampleRate;
+		}
+	else
+		{
+		aTime = 0;
+		}
+		
+	return KErrNone;
+	}
+
+void CMMFSwCodecPlayDataPath::IgnoreUnderflowEvents()
+	{
+	iIgnoreUnderflow = ETrue;
+	}
+/************************************************************************
+ *				CDataPathPlayer											*
+ ************************************************************************/
+
+CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+CDataPathPlayer::~CDataPathPlayer()
+	{
+	Cancel();
+	}
+
+
+void CDataPathPlayer::Start()
+	{
+	// No implementation
+	}
+
+
+void CDataPathPlayer::ResumePlaying()
+	{
+	if (iParent.Device().Handle())
+		{
+		//should be ok to call this even if we are active
+		iParent.Device().ResumePlaying(); 
+		iResumePlaying = ETrue;
+		}
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("Playing Resumed"));
+#endif
+	}
+
+
+void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
+	{
+	iDataFromSource = &aData;
+	if (!IsActive())
+		{
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CDataPathPlayer::PlayData"));
+#endif
+		iParent.Device().PlayData(iStatus,(STATIC_CAST(const CMMFDataBuffer*, iDataFromSource))->Data());
+		SetActive();
+		}
+	}
+
+
+void CDataPathPlayer::Stop()
+	{
+	if (!IsActive())	
+		iParent.Device().FlushPlayBuffer(); // Otherwise won't be flushed
+	Cancel();
+	iParent.SoundDeviceException(KErrCancel);
+	}
+
+
+void CDataPathPlayer::RunL()
+	{
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int());
+#endif
+	if (iStatus.Int()!=KErrNone)
+		{ 	
+		iParent.SoundDeviceException(iStatus.Int());
+		}
+	else
+		{
+		iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
+		iResumePlaying = EFalse;
+		}
+	}
+
+
+TInt CDataPathPlayer::RunError(TInt aError)
+	{
+	Error(aError);
+	return KErrNone;
+	}
+
+
+void CDataPathPlayer::DoCancel()
+	{
+	if (iParent.Device().Handle())
+		{
+		iParent.Device().CancelPlayData();
+		iParent.Device().FlushPlayBuffer();
+		}
+	}
+
+
+void CDataPathPlayer::Error(TInt aError)
+	{ 
+	iParent.SoundDeviceException(aError);
+	}
+
+
+/************************************************************************
+ *				CSoundDevPlayErrorReceiver											*
+ ************************************************************************/
+
+CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver()
+	{
+	Cancel();
+	}
+
+void CSoundDevPlayErrorReceiver::Start()
+	{
+	iParent.Device().NotifyPlayError(iStatus);
+	SetActive();
+	}
+
+void CSoundDevPlayErrorReceiver::Stop()
+	{
+	Cancel();
+	}
+
+void CSoundDevPlayErrorReceiver::RunL()
+	{
+	TInt reason = iStatus.Int();
+	Start();
+	// An error has been returned
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), reason);
+#endif
+	iParent.SoundDeviceException(reason);
+	}
+
+void CSoundDevPlayErrorReceiver::DoCancel()
+	{
+	iParent.Device().CancelNotifyPlayError();
+	}
+
+