diff -r 000000000000 -r bde4ae8d615e os/mm/mmlibs/mmfw/src/server/BaseClasses/mmfdatapath2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/mm/mmlibs/mmfw/src/server/BaseClasses/mmfdatapath2.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,1122 @@ +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// source\server\mmfdatapath2.cpp +// +// + +#include +#include +#include +#include +#include +#include "mmfdatapath2.h" +#include "mmfclientaudiostreamutils.h" +#include +#include // KUidMmfCodecAudioSettings +#include +#include +#include + +static void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber) + { + _LIT(KMMFDataPathPanicCategory, "MMFDataPath2"); + User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber); + } + +/** +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 CMMFDataPath2* CMMFDataPath2::NewL(MAsyncEventHandler& aEventHandler) + { + CMMFDataPath2* self = new(ELeave) CMMFDataPath2(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 CMMFDataPath2* CMMFDataPath2::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath2* self = new(ELeave) CMMFDataPath2(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 CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath2* self = new(ELeave) CMMFDataPath2(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 CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler) + { + CMMFDataPath2* self = new(ELeave) CMMFDataPath2(aMediaId, aEventHandler); + CleanupStack::PushL(self); + self->ConstructL(aCodecUid); + CleanupStack::Pop(); + return self; + } + +CMMFDataPath2::CMMFDataPath2(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) + : CMMFDataPath(aMediaId, aEventHandler), iTimeLeftToPlayComplete(-1) + { + } + +void CMMFDataPath2::ConstructL(TUid aCodecUid) + { + CMMFDataPath::ConstructL(aCodecUid); + iRepeatTrailingSilenceTimer = CPeriodic::NewL(CActive::EPriorityStandard); + } + +/** +Standard destructor. +*/ + +CMMFDataPath2::~CMMFDataPath2() + { + if(iRepeatTrailingSilenceTimer) + { + iRepeatTrailingSilenceTimer->Cancel(); + delete iRepeatTrailingSilenceTimer; + } + } + +TInt CMMFDataPath2::RepeatTrailingSilenceTimerComplete(TAny* aDataPath) + { + CMMFDataPath2* dataPath = static_cast(aDataPath); + + TRAPD(err, dataPath->DoRepeatTrailingSilenceTimerCompleteL()); + if (err != KErrNone) + { + dataPath->DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err); + } + return KErrNone; + } + +TInt CMMFDataPath2::DoRepeatTrailingSilenceTimerCompleteL() + { + //cancel this periodic timer + iRepeatTrailingSilenceTimer->Cancel(); + if(iTimeLeftToPlayComplete.Int64()>0) + { + iTimeLeftToPlayComplete=0; + } + + if (iTrailingSilenceLeftToPlay.Int64() > 0) + { + PlaySilence(); + } + else + { + SetPositionL(iPlayWindowStartPosition); + iTimeLeftToPlayComplete=-1; + FillSourceBufferL(); + } + return KErrNone; + } + +void CMMFDataPath2::PlaySilence() + { + // iRepeatTrailingSilenceTimer->After() takes a TTimeIntervalMicroSeconds32 + // so for longer periods of silence call it repeatedly with KMaxTInt lengths + TTimeIntervalMicroSeconds32 silence; + if(iTimeLeftToPlayComplete.Int64() > 0) + { + silence = I64INT(iTimeLeftToPlayComplete.Int64()); + } + else if (iTrailingSilenceLeftToPlay.Int64() > KMaxTInt) + { + silence = KMaxTInt; + iTrailingSilenceLeftToPlay = iTrailingSilenceLeftToPlay.Int64() - KMaxTInt; + } + else + { + silence = I64INT(iTrailingSilenceLeftToPlay.Int64()); + iTrailingSilenceLeftToPlay = 0; + } + iRepeatTrailingSilenceTimer->Start(silence, silence , TCallBack(RepeatTrailingSilenceTimerComplete, this)); + } + +/* + * FillSourceBufferL + * + * Function to get data from the datapath's iDataSource + */ + +void CMMFDataPath2::DoFillSourceBufferL() + { +#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 || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport)), 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 + } + +/** +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. +*/ +void CMMFDataPath2::RunL() + { +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::RunL transfer state %d, iPausedCalled %d, tick-%d (this 0x%x)\n"),iTransferState, iPauseCalled, User::TickCount(),this); +#endif + + switch (iState) + { + case EStopped: + break; + case EPrimed: // In the paused state we still continue to feed buffers to the sink. The sink (DevSound) handles the logic of whether the buffers should be emptied + { + if (!iPauseCalled || !iIsUsingResumeSupport) + break; + } + // fall-through + 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 + } + +/* + * FillSourceBufferL + * + * Function to get data from the datapath's iDataSource + */ + +void CMMFDataPath2::FillSourceBufferL() + { + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport) ), Panic(EMMFDataPathPanicBadState,__LINE__)); + + //if the silence timer is active then dont propagate the request + if(iRepeatTrailingSilenceTimer->IsActive()) + { + return; + } + + //play the silence period and dont propagate the request + if(iTrailingSilenceLeftToPlay>0 || iVerifyPlayComplete) + { + if(iVerifyPlayComplete)//case when the trailing silence is zero + { + if (!*iDisableAutoIntent && iDrmSource) + { + CMMFFile* file = static_cast(iDrmSource); + TInt err = file->ExecuteIntent(ContentAccess::EPlay); + if (err != KErrNone) + { + DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err); + return; + } + } + + //Retrieve the current play time and add "duration-currentplaytime" to the silence period + //This is to ensure that silence timer is not started before the previous play is actually completed by the devsound + TTimeIntervalMicroSeconds currentTime = CalculateAudioOutputPosition(); + if(currentTime.Int64()>iPlayWindowStartPosition.Int64()) + { + iTimeLeftToPlayComplete = iPlayWindowEndPosition.Int64()-currentTime.Int64(); + } + else + { + iTimeLeftToPlayComplete = 0; + } + + iVerifyPlayComplete = EFalse; + } + if(iTrailingSilenceLeftToPlay==0 && iTimeLeftToPlayComplete==0) + { + SetPositionL(iPlayWindowStartPosition); + iTimeLeftToPlayComplete=-1; + } + else + { + PlaySilence(); + return; + } + } + + DoFillSourceBufferL(); + } + + +/** +Indicates the data source has filled the specified buffer. + +Called by the CMMFDataPath2's MDataSource when it has filled the buffer. + +@param aBuffer + A pointer to the filled buffer. +*/ +void CMMFDataPath2::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 + + TBool isInTruePause = (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport); + //state only used if we are passing data + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || isInTruePause), Panic(EMMFDataPathPanicBadState,__LINE__)); + + __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__)); + + //if we have been asked to repeat and this is the last buffer, reset last buffer flag and send to the device + if(aBuffer!= NULL && aBuffer->LastBuffer()) + { + iNumberOfTimesPlayed++; + if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever) + { + aBuffer->SetLastBuffer(EFalse); + + //this will trigger the trailing silence timer next time a buffer is requested. + iTrailingSilenceLeftToPlay = iTrailingSilence; + iVerifyPlayComplete = ETrue; + } + } + + iSourceBufferWithSource = EFalse; + + //Has the datapath stopped running, if so were not interested in any callbacks. + if(iState == EStopped || (iState == EPrimed && !isInTruePause)) + { +#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 ))) + { + //When it resumes in silence , position of the buffer is in end so we need to skip the increament. + if(!iPauseCalledInsilence) + { + iNumberOfTimesPlayed++; + }else + { + iPauseCalledInsilence=EFalse; + } + if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever) + { + iSourceBuffer->SetLastBuffer(EFalse); + //this will trigger the trailing silence timer next time a buffer is requested. + iTrailingSilenceLeftToPlay = iTrailingSilence; + iVerifyPlayComplete = ETrue; + } + else + { + #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 + } + +/** +Sets the data path position. + +@param aPosition + The data path position. +*/ +void CMMFDataPath2::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 + } + + if(iGetTimePlayedSupported) + { + TTimeIntervalMicroSeconds timePlayed(0); + if(iState == EPlaying && iDataSink->DataSinkType() == KUidMmfAudioOutput) + { + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); + CMMFDevSound& devSound = audioOutput->SoundDevice(); + TInt err= devSound.GetTimePlayed(timePlayed); + if(err == KErrNone) + { + iDevSoundRepositionTime = timePlayed.Int64(); + } + } + else + { + iDevSoundRepositionTime = 0; + } + } + else //roll back to samplesplayed + { + //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 + } + +/** +Gets the data path position. + +@return The data path position. +*/ +TTimeIntervalMicroSeconds CMMFDataPath2::Position() const + { + if ((iState == ERecording) || (iState == EConverting)) + { + return InputPosition(); + } + else if(iState == EPlaying) + { + return OutputPosition(); + } + else + { + return iStartPosition; + } + } + +TTimeIntervalMicroSeconds CMMFDataPath2::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(CMMFDataPath2*,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 CMMFDataPath2::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 || iStartPosition == iPlayWindowEndPosition ) + { + return iStartPosition; + } +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed); +#endif + + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); + CMMFDevSound& devSound = audioOutput->SoundDevice(); + + TTimeIntervalMicroSeconds devSoundTimePlayed(0); + TInt64 timePlayed(0); + TInt err = KErrNone; + if(iGetTimePlayedSupported) + { + err= devSound.GetTimePlayed(devSoundTimePlayed); + if(err == KErrNone) + { + timePlayed = devSoundTimePlayed.Int64()-iDevSoundRepositionTime.Int64(); + } + } + else //Roll back to SamplesPlayed + { + TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed; + 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; + } + timePlayed = I64DOUBLECAST(timePlayedSeconds * 1000000); + + #ifdef _DP_DEBUG + RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed)); + #endif + } + if(err == KErrNone) + { + // When Resume is not supported. Datapath simulates Pause() through DevSound's Stop + // the time played is lost. So we need to saved the last position + // On the opposite, when Resume is supported the time played returned by DevSound + // reflects the real position, so there is no needed to recalculate at least playwindow is being used + // Finally, if Resume is supported but is not used the position also need to be saved + if(!iIsUsingResumeSupport || iPlayWindowStartPosition.Int64() > 0) + { + timePlayed = timePlayed + iStartPosition.Int64(); + } + + //During repeats. we need to reset the positin manually to playstart once playend is reached + //this is because the bytes returned by devsound are not accurate in all the cases + if(iRepeatTrailingSilenceTimer->IsActive() || iTimeLeftToPlayComplete==0)//loop play + { + if(iTimeLeftToPlayComplete==0) + { + timePlayed = iPlayWindowStartPosition.Int64(); + } + } + else if(timePlayed>=(iPlayWindowEndPosition.Int64()+1)) + { + timePlayed = iPlayWindowStartPosition.Int64(); + } + } + + return TTimeIntervalMicroSeconds(timePlayed); + } + +/** +Stops playing. + +Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError +to the client if an error occurs. +*/ +void CMMFDataPath2::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); + } + } + } + +void CMMFDataPath2::DoStopL() + { + CMMFDataPath::DoStopL(); + iRepeatTrailingSilenceTimer->Cancel(); + iIsUsingResumeSupport = EFalse; + } + +/** +Pauses playing. + +Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs. +*/ +void CMMFDataPath2::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 + } + +EXPORT_C void CMMFDataPath2::PreEmptionPause() + { + TRAPD(err, DoPreEmptionPauseL()); + + if (err) + { + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); + } + } + +/** +Pauses playing. + +Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs. +*/ +void CMMFDataPath2::DoPreEmptionPauseL() + { + + if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting) && ( iDataSink->DataSinkType() == KUidMmfAudioOutput ))) + { + iDataSource->SourcePauseL(); // propagate state change to source + SetPositionL(Position()); + iIsUsingResumeSupport = EFalse; + + 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 + } + if(iState == ERecording) + { + User::Leave(KErrNotSupported); + } + iRepeatTrailingSilenceTimer->Cancel(); + } + +void CMMFDataPath2::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))) + { + if (iDataSink->DataSinkType() == KUidMmfAudioOutput && iIsResumeSupported) + { + iDataSink->SinkPauseL(); + // When true pause is supported only the datapath's position + // should be updated, MDataSource position should be changed + iStartPosition = Position(); + iIsUsingResumeSupport = ETrue; + if(iRepeatTrailingSilenceTimer->IsActive()) + { + iPauseCalledInsilence=ETrue; + } + // If we wait for the sink to complete play, then we do not proceed with supplying the buffers to the sink + // In this case we need to reset the buffers so that InitializeSinkL won't attempt bringing in new ones + if (iTransferState == EWaitSink) + { + ResetRefBuffers(); + } + } + else + { + // If we use resume support, then there is no need to pause source as we would continue to supply buffers to the sink + // Here we are not using resume support, thus we're pausing the source + iDataSource->SourcePauseL(); + SetPositionL(Position()); + iDataSink->SinkStopL(); + ResetRefBuffers(); // buffer references may not be valid any more + + Cancel(); //Stop the state machine + } + iPauseCalled = ETrue; // indicate pause is called + + iState = EPrimed; // go back to primed state (we're not playing) + } + else if(iState == ERecording) + { + iPauseCalled = ETrue; +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n")); +#endif + iDataSource->SourcePauseL(); + } + iRepeatTrailingSilenceTimer->Cancel(); +#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 + } + +/** +Cancels the silence timer. +*/ +void CMMFDataPath2::DoCancel() + { + //cancel repeats + iRepeatTrailingSilenceTimer->Cancel(); + iNumberOfTimesToRepeat=0; + } + +/** +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(). +*/ +void CMMFDataPath2::PrimeL() + { + CMMFDataPath::PrimeL(); + if(iDataSink->DataSinkType() == KUidMmfAudioOutput) + { + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); + CMMFDevSound& devSound = audioOutput->SoundDevice(); + iGetTimePlayedSupported = devSound.IsGetTimePlayedSupported(); + iIsResumeSupported = devSound.IsResumeSupported(); + iIsUsingResumeSupport = EFalse; + iPauseCalledInsilence = EFalse; + } + } + +/** +Starts an active scheduler 'play' loop. + +Can only play from the primed state. +*/ +void CMMFDataPath2::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 + + if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed + { + //When pause is called silence,we need to send the buffer while resume so icansendbuffer should enabled + if(!iPauseCalledInsilence) + { + 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; + + if(!iIsResumeSupported || !iIsUsingResumeSupport) + { + SetPositionL( iStartPosition ); + } + iReferenceAudioSamplesPlayed = 0; + iReferenceAudioSamplesRecorded = 0; + + //complete a request on iStatus to invoke play code + iDataSource->SourcePlayL(); //propagate state change to source + + // This need to be done always since CMMFAudioOutput::EmptyBuffer + // doesn't start playback anymore + 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); + } + iDevSoundRepositionTime = 0; + if(!iRetainRepeatInfo) + { + iNumberOfTimesPlayed = 0; + iTimeLeftToPlayComplete = -1; + iVerifyPlayComplete = EFalse; + } + iRetainRepeatInfo = EFalse; +#ifdef _DP_DEBUG + RDebug::Print(_L("DP::Play - DONE\n")); +#endif + } + +/** +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. +*/ +void CMMFDataPath2::ResetL() + { + CMMFDataPath::ResetL(); + iDrmSource = NULL; + } + +/** +Sets the number of times the audio sample is to be repeated during the +playback operation. + +A period of silence can follow each playing of the sample. The audio +sample can be repeated indefinitely. + +@param aRepeatNumberOfTimes + The number of times the audio sample, together with + the trailing silence, is to be repeated. If this is + set to KMdaRepeatForever, then the audio + sample, together with the trailing silence, is + repeated indefinitely or until Stop() is + called. If this is set to zero, then the audio sample + is not repeated. +@param aTrailingSilence + The time interval of the trailing silence in microseconds. + +*/ +EXPORT_C void CMMFDataPath2::SetRepeats(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence) + { + iNumberOfTimesToRepeat=aRepeatNumberOfTimes; + iTrailingSilence=aTrailingSilence; + } + +/** +Sets the Drm file source and the automatic execute intent flag. This method is used by the controller plugin +to pass these to the datapath in order to execute the play intent during loop play. + +@param aSource + Data Source on which the play intent needs to be executed. This is usually the CMMFFile source +@param aDisableAutoIntent + Boolean variable which states whether the controller plugin or the datapath needs to execute play intent + automatically or not. +*/ +EXPORT_C void CMMFDataPath2::SetDrmProperties(MDataSource* aSource, TBool *aDisableAutoIntent) + { + iDrmSource = aSource; + iDisableAutoIntent = aDisableAutoIntent; + } + +/** +This call indicates PlayL not to reset the iNumberOfTimesPlayed property. This method is used by the controller plugin +during repositioning. PlayL call during seeking should not reset the iNumberOfTimesPlayed property. +*/ +EXPORT_C void CMMFDataPath2::RetainRepeatInfo() + { + iRetainRepeatInfo = ETrue; + }