First public contribution.
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // source\server\mmfdatapath2.cpp
19 #include <mmf/common/mmffourcc.h>
20 #include <mmf/common/mmfpaniccodes.h>
21 #include <mmf/server/mmfaudiooutput.h>
22 #include <mmf/server/mmfaudioinput.h>
23 #include "mmfdatapath2.h"
24 #include "mmfclientaudiostreamutils.h"
25 #include <mmf/common/mmfaudio.h>
26 #include <mmf/plugin/mmfcodecimplementationuids.hrh> // KUidMmfCodecAudioSettings
27 #include <mmf/server/devsoundstandardcustominterfaces.h>
28 #include <mmf/server/mmffile.h>
29 #include <mda/client/resource.h>
31 static void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber)
33 _LIT(KMMFDataPathPanicCategory, "MMFDataPath2");
34 User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber);
38 Allocates and constructs a data path.
40 Use this function if the codec UID is not already known by CMMFController
41 and there is no data path ambiguity - ie only one data path is possible.
43 Will create codec via fourCC.
46 Installs an event handler to provide message passing between clients and sources/sinks.
48 @return Newly constructed data path object.
51 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(MAsyncEventHandler& aEventHandler)
53 CMMFDataPath2* self = new(ELeave) CMMFDataPath2(TMediaId(), aEventHandler);
54 CleanupStack::PushL(self);
62 Allocates and constructs a data path according to the specified media ID.
64 Use this function if the codec UID is not already known by CMMFController
65 and there is ambiguity with the data path ie. there is more than one possible data path.
68 Optional media ID parameter when there are multiple media types.
70 Installs an event handler to provide message passing between clients and sources/sinks.
72 @return A newly constructed data path object.
75 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
77 CMMFDataPath2* self = new(ELeave) CMMFDataPath2(aMediaId, aEventHandler);
78 CleanupStack::PushL(self);
85 Allocates and constructs a data path according to the specified codec UID.
87 Use this function if the codec UID is already known by CMMFController
88 and there is no data path ambiguity ie. only one data path is possible
89 will create codec explicitly using the supplied codec Uid
92 Optional mediaID parameter when there are multiple media types
94 Installs an event handler to provide message passing between clients and sources/sinks.
96 @return A newly constructed data path object.
99 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler)
101 CMMFDataPath2* self = new(ELeave) CMMFDataPath2(TMediaId(), aEventHandler);
102 CleanupStack::PushL(self);
103 self->ConstructL(aCodecUid);
110 Allocates and constructs a data path according to the specified codec UID.
112 Use this function if the codec UID is already known by CMMFController
113 and there is ambiguity ie. more than one possible data path.
114 TMediaId used to select the path.
119 Optional mediaID parameter when there are multiple media types.
121 Installs an event handler to provide message passing between clients and sources/sinks.
123 @return A newly constructed data path object.
125 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
127 CMMFDataPath2* self = new(ELeave) CMMFDataPath2(aMediaId, aEventHandler);
128 CleanupStack::PushL(self);
129 self->ConstructL(aCodecUid);
134 CMMFDataPath2::CMMFDataPath2(TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
135 : CMMFDataPath(aMediaId, aEventHandler), iTimeLeftToPlayComplete(-1)
139 void CMMFDataPath2::ConstructL(TUid aCodecUid)
141 CMMFDataPath::ConstructL(aCodecUid);
142 iRepeatTrailingSilenceTimer = CPeriodic::NewL(CActive::EPriorityStandard);
149 CMMFDataPath2::~CMMFDataPath2()
151 if(iRepeatTrailingSilenceTimer)
153 iRepeatTrailingSilenceTimer->Cancel();
154 delete iRepeatTrailingSilenceTimer;
158 TInt CMMFDataPath2::RepeatTrailingSilenceTimerComplete(TAny* aDataPath)
160 CMMFDataPath2* dataPath = static_cast<CMMFDataPath2*>(aDataPath);
162 TRAPD(err, dataPath->DoRepeatTrailingSilenceTimerCompleteL());
165 dataPath->DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err);
170 TInt CMMFDataPath2::DoRepeatTrailingSilenceTimerCompleteL()
172 //cancel this periodic timer
173 iRepeatTrailingSilenceTimer->Cancel();
174 if(iTimeLeftToPlayComplete.Int64()>0)
176 iTimeLeftToPlayComplete=0;
179 if (iTrailingSilenceLeftToPlay.Int64() > 0)
185 SetPositionL(iPlayWindowStartPosition);
186 iTimeLeftToPlayComplete=-1;
192 void CMMFDataPath2::PlaySilence()
194 // iRepeatTrailingSilenceTimer->After() takes a TTimeIntervalMicroSeconds32
195 // so for longer periods of silence call it repeatedly with KMaxTInt lengths
196 TTimeIntervalMicroSeconds32 silence;
197 if(iTimeLeftToPlayComplete.Int64() > 0)
199 silence = I64INT(iTimeLeftToPlayComplete.Int64());
201 else if (iTrailingSilenceLeftToPlay.Int64() > KMaxTInt)
204 iTrailingSilenceLeftToPlay = iTrailingSilenceLeftToPlay.Int64() - KMaxTInt;
208 silence = I64INT(iTrailingSilenceLeftToPlay.Int64());
209 iTrailingSilenceLeftToPlay = 0;
211 iRepeatTrailingSilenceTimer->Start(silence, silence , TCallBack(RepeatTrailingSilenceTimerComplete, this));
217 * Function to get data from the datapath's iDataSource
220 void CMMFDataPath2::DoFillSourceBufferL()
223 RDebug::Print(_L("DP::FillSourceBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this);
226 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport)), Panic(EMMFDataPathPanicBadState,__LINE__));
228 // clear the no-more-source flag here (as well as in PlayL()) because
229 // there may have been a re-position since the last call to BufferFilledL()
230 iNoMoreSourceData = EFalse;
232 if(!iObtainingAsyncSourceBuffer)
233 {//this is a normal request for data.
234 //If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL
235 iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with
236 iSourceBuffer->SetStatus(EBeingFilled);
237 iSourceBuffer->SetLastBuffer(EFalse);
241 RDebug::Print(_L("DP asking for buffer %d - ptr=0x%x (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this);
244 iSourceBufferWithSource = ETrue;
246 // wait for BufferFilled callback from source. Do this here as some sources cause
247 //re-entrancy into data path via BufferFilledL
248 ChangeDataPathTransferState(EWaitSource);
250 iDataSource->FillBufferL(iSourceBuffer, this, iMediaId);
253 RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
258 Runs the clip depending on the current data path and transfer state.
260 For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData.
262 void CMMFDataPath2::RunL()
265 RDebug::Print(_L("DP::RunL transfer state %d, iPausedCalled %d, tick-%d (this 0x%x)\n"),iTransferState, iPauseCalled, User::TickCount(),this);
272 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
274 if (!iPauseCalled || !iIsUsingResumeSupport)
281 switch (iTransferState)
286 case EInitializeSink:
289 case EInitializeSource:
292 case ENeedSourceData:
298 case ENeedToMatchSourceToSink:
301 case ESendDataToSink:
313 RDebug::Print(_L("DP::RunL DONE\n"));
320 * Function to get data from the datapath's iDataSource
323 void CMMFDataPath2::FillSourceBufferL()
325 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport) ), Panic(EMMFDataPathPanicBadState,__LINE__));
327 //if the silence timer is active then dont propagate the request
328 if(iRepeatTrailingSilenceTimer->IsActive())
333 //play the silence period and dont propagate the request
334 if(iTrailingSilenceLeftToPlay>0 || iVerifyPlayComplete)
336 if(iVerifyPlayComplete)//case when the trailing silence is zero
338 if (!*iDisableAutoIntent && iDrmSource)
340 CMMFFile* file = static_cast<CMMFFile*>(iDrmSource);
341 TInt err = file->ExecuteIntent(ContentAccess::EPlay);
344 DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err);
349 //Retrieve the current play time and add "duration-currentplaytime" to the silence period
350 //This is to ensure that silence timer is not started before the previous play is actually completed by the devsound
351 TTimeIntervalMicroSeconds currentTime = CalculateAudioOutputPosition();
352 if(currentTime.Int64()>iPlayWindowStartPosition.Int64())
354 iTimeLeftToPlayComplete = iPlayWindowEndPosition.Int64()-currentTime.Int64();
358 iTimeLeftToPlayComplete = 0;
361 iVerifyPlayComplete = EFalse;
363 if(iTrailingSilenceLeftToPlay==0 && iTimeLeftToPlayComplete==0)
365 SetPositionL(iPlayWindowStartPosition);
366 iTimeLeftToPlayComplete=-1;
375 DoFillSourceBufferL();
380 Indicates the data source has filled the specified buffer.
382 Called by the CMMFDataPath2's MDataSource when it has filled the buffer.
385 A pointer to the filled buffer.
387 void CMMFDataPath2::BufferFilledL(CMMFBuffer* aBuffer)
390 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);
393 TBool isInTruePause = (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport);
394 //state only used if we are passing data
395 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || isInTruePause), Panic(EMMFDataPathPanicBadState,__LINE__));
397 __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__));
399 //if we have been asked to repeat and this is the last buffer, reset last buffer flag and send to the device
400 if(aBuffer!= NULL && aBuffer->LastBuffer())
402 iNumberOfTimesPlayed++;
403 if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever)
405 aBuffer->SetLastBuffer(EFalse);
407 //this will trigger the trailing silence timer next time a buffer is requested.
408 iTrailingSilenceLeftToPlay = iTrailingSilence;
409 iVerifyPlayComplete = ETrue;
413 iSourceBufferWithSource = EFalse;
415 //Has the datapath stopped running, if so were not interested in any callbacks.
416 if(iState == EStopped || (iState == EPrimed && !isInTruePause))
419 RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this);
424 #ifdef REPOSITION_SPEEDUP
425 // if the source has been re-positioned, then go & get some more source data now
426 if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber)
429 RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this);
431 ChangeDataPathTransferState(ENeedSourceData);
434 #endif //REPOSITION_SPEEDUP
436 //bufer is NULL, indicating no more source data.
439 //If we only hold a reference to the source buffer, set that to NULL
442 iSourceBuffer = NULL;
445 iNoMoreSourceData = ETrue;
447 if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data
448 iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data
450 ChangeDataPathTransferState(EEndOfData);
452 else //sink buffer is with datapath, see if there is anything to send to sink
454 ChangeDataPathTransferState(ENeedToMatchSourceToSink);
458 RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this);
464 //We were waiting for a response from the source to get an asynchronous buffer.
465 //We now have it, and we proceed to transfer this data to the sink.
466 if (iObtainingAsyncSourceBuffer)
468 iObtainingAsyncSourceBuffer = EFalse;
472 aBuffer->SetStatus(EFull);
474 if(iSourceBuffer != aBuffer)
475 {//buffer has been changed by the source
476 iSourceBuffer = aBuffer;
477 if (!(iBuffersToUse & ENeedSinkBuffer))
478 {//we only need one buffer and use source
479 iSinkBuffer = iSourceBuffer;
483 RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
486 //Is this the last buffer from the source (0 length or LastBuffer flag set)
487 //or have reached the end of the play window; we only look at the play window here
488 //if we are converting. For conversion we look at the data we have read. This is then passed onto
490 if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() ||
491 (((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition )))
493 //When it resumes in silence , position of the buffer is in end so we need to skip the increament.
494 if(!iPauseCalledInsilence)
496 iNumberOfTimesPlayed++;
499 iPauseCalledInsilence=EFalse;
501 if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever)
503 iSourceBuffer->SetLastBuffer(EFalse);
504 //this will trigger the trailing silence timer next time a buffer is requested.
505 iTrailingSilenceLeftToPlay = iTrailingSilence;
506 iVerifyPlayComplete = ETrue;
511 RDebug::Print(_L("DP::BufferFilledL end of input data tick-%d (this 0x%x)\n"),User::TickCount(),this);
512 RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize());
513 RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer());
514 RDebug::Print(_L("InputPosition()=%d >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64()));
516 iNoMoreSourceData = ETrue;
517 iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window
524 ChangeDataPathTransferState(ESendDataToSink);
526 else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it
528 ChangeDataPathTransferState(ENeedToMatchSourceToSink);
530 //else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state
533 RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
538 Sets the data path position.
541 The data path position.
543 void CMMFDataPath2::SetPositionL(const TTimeIntervalMicroSeconds& aPosition)
544 {//need to map to source position to frame position
546 RDebug::Print(_L("DP::SetPositionL = %d ticks-%d (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this);
549 if (iState == EStopped)
551 User::Leave(KErrNotReady); //can only set position if primed
554 if(iGetTimePlayedSupported)
556 TTimeIntervalMicroSeconds timePlayed(0);
557 if(iState == EPlaying && iDataSink->DataSinkType() == KUidMmfAudioOutput)
559 CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
560 CMMFDevSound& devSound = audioOutput->SoundDevice();
561 TInt err= devSound.GetTimePlayed(timePlayed);
564 iDevSoundRepositionTime = timePlayed.Int64();
569 iDevSoundRepositionTime = 0;
572 else //roll back to samplesplayed
574 //As this will affect the position, we need to know how many bytes were
575 //played when position was updated. Future Position() requests will
576 //then use this refernce to determine the current position.
577 iReferenceAudioSamplesPlayed = AudioSamplesPlayed();
580 // Force the new position to be inside the play window (also within the file duration)
581 if ( aPosition < iPlayWindowStartPosition )
583 iStartPosition = iPlayWindowStartPosition;
585 else if ( aPosition > iPlayWindowEndPosition )
587 iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played
591 iStartPosition = aPosition;
594 TTimeIntervalMicroSeconds interval;
596 //can only set the position on an MDataSource that is a format object
597 //Note: position defaults to source if both source & sink are clips
598 if (iDataSource->DataSourceType() == KUidMmfFormatDecode)
600 //position is not beyond the end of file
601 interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId);
603 // for some reason this code won't compile without these intermediate steps
604 TInt64 position = iStartPosition.Int64();
605 TInt64 interval64 = interval.Int64();
607 User::Leave(KErrDivideByZero);
608 TInt64 datapos64 = position/interval64;
609 iCurrentSourceFrameNumber = I64LOW(datapos64);
612 // Try to set the position directly on the format
613 TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition));
614 //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
616 else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
618 //position is not beyond the end of file
619 interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
621 //convert to TUint - for some reason it won't compile without these intermediate steps
622 TInt64 position = iStartPosition.Int64();
623 TInt64 interval64 = interval.Int64();
625 User::Leave(KErrDivideByZero);
626 TInt64 datapos64 = position/interval64;
627 iCurrentSinkFrameNumber = I64LOW(datapos64);
630 // Try to set the position directly on the format
631 TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition));
632 //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
635 {//can only set position if source or sink is a format
636 //If both source and sink are formats position is relative to the source
637 User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips
640 if(iCodec) //we have a real codec, must reset it
642 iCodec->ResetL(); // Need to preserve sync when resuming play
645 // Once we've sent the last buffer to the sink it's too late to start
646 // changing the state since we may get a RunError(KErrUnderflow) at any time.
647 // Once this happens, the sound driver may have unloaded etc..and recovery
648 // would be complicated.
649 if (iAllDataSentToSink)
654 RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d iStartPosition=%d ticks-%d (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this);
659 Gets the data path position.
661 @return The data path position.
663 TTimeIntervalMicroSeconds CMMFDataPath2::Position() const
665 if ((iState == ERecording) || (iState == EConverting))
667 return InputPosition();
669 else if(iState == EPlaying)
671 return OutputPosition();
675 return iStartPosition;
679 TTimeIntervalMicroSeconds CMMFDataPath2::OutputPosition() const
681 TTimeIntervalMicroSeconds interval;
682 TTimeIntervalMicroSeconds position;
684 if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
686 position = CalculateAudioOutputPosition();
688 RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64()));
691 else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
693 //note Encode format position takes priority if both source & sink are formats?
694 // try to get the position directly from the format. If that fails, work it out here
695 TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL());
696 if (error)//getting the position from the format didn't work so calculate it here
698 interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
699 TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber;
700 position = position64;
703 TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath2*,this)->Duration(); //need this to check position doesn't exceed duration
704 if (position > duration)//this can happen on last buffer
709 RDebug::Print(_L("DP::OutputPosition from format= %d\n"),I64INT(position.Int64()));
713 {//can only read output position if sink is a format or an audio output
714 return TTimeIntervalMicroSeconds(0);
720 TTimeIntervalMicroSeconds CMMFDataPath2::CalculateAudioOutputPosition() const
722 //This operation can only be carried out on an Audio Output
723 __ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__));
726 //If we are not playing, simply return where we will play from
727 if(iState != EPlaying || iCurrentSinkFrameNumber == 0 || iStartPosition == iPlayWindowEndPosition )
729 return iStartPosition;
732 RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed);
735 CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
736 CMMFDevSound& devSound = audioOutput->SoundDevice();
738 TTimeIntervalMicroSeconds devSoundTimePlayed(0);
739 TInt64 timePlayed(0);
741 if(iGetTimePlayedSupported)
743 err= devSound.GetTimePlayed(devSoundTimePlayed);
746 timePlayed = devSoundTimePlayed.Int64()-iDevSoundRepositionTime.Int64();
749 else //Roll back to SamplesPlayed
751 TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed;
752 TMMFCapabilities devSoundConfig = devSound.Config();
753 TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig);
755 RDebug::Print(_L("samplingFreq %d\n"),samplingFreq);
758 TReal timePlayedSeconds = 0;
761 timePlayedSeconds = samplesPlayed/samplingFreq;
763 timePlayed = I64DOUBLECAST(timePlayedSeconds * 1000000);
766 RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed));
771 // When Resume is not supported. Datapath simulates Pause() through DevSound's Stop
772 // the time played is lost. So we need to saved the last position
773 // On the opposite, when Resume is supported the time played returned by DevSound
774 // reflects the real position, so there is no needed to recalculate at least playwindow is being used
775 // Finally, if Resume is supported but is not used the position also need to be saved
776 if(!iIsUsingResumeSupport || iPlayWindowStartPosition.Int64() > 0)
778 timePlayed = timePlayed + iStartPosition.Int64();
781 //During repeats. we need to reset the positin manually to playstart once playend is reached
782 //this is because the bytes returned by devsound are not accurate in all the cases
783 if(iRepeatTrailingSilenceTimer->IsActive() || iTimeLeftToPlayComplete==0)//loop play
785 if(iTimeLeftToPlayComplete==0)
787 timePlayed = iPlayWindowStartPosition.Int64();
790 else if(timePlayed>=(iPlayWindowEndPosition.Int64()+1))
792 timePlayed = iPlayWindowStartPosition.Int64();
796 return TTimeIntervalMicroSeconds(timePlayed);
802 Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError
803 to the client if an error occurs.
805 void CMMFDataPath2::Stop()
808 RDebug::Print(_L("DP::Stop current state=%d tick-%d (this 0x%x)\n"), iTransferState, User::TickCount(),this);
810 if ((iDataPathCreated) && (iState != EStopped))
812 TRAPD(err, DoStopL());
815 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
820 void CMMFDataPath2::DoStopL()
822 CMMFDataPath::DoStopL();
823 iRepeatTrailingSilenceTimer->Cancel();
824 iIsUsingResumeSupport = EFalse;
830 Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs.
832 void CMMFDataPath2::Pause()
835 RDebug::Print(_L("DP::Pause, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);
836 RDebug::Print(_L("DP::Pause current state=%d tick-%d (this 0x%x)\n"),iTransferState, User::TickCount(),this);
839 TRAPD(err, DoPauseL());
843 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
846 RDebug::Print(_L("DP::Pause - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
850 EXPORT_C void CMMFDataPath2::PreEmptionPause()
852 TRAPD(err, DoPreEmptionPauseL());
856 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
863 Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs.
865 void CMMFDataPath2::DoPreEmptionPauseL()
868 if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting) && ( iDataSink->DataSinkType() == KUidMmfAudioOutput )))
870 iDataSource->SourcePauseL(); // propagate state change to source
871 SetPositionL(Position());
872 iIsUsingResumeSupport = EFalse;
874 iDataSink->SinkStopL();
875 iPauseCalled = ETrue; // indicate pause is called
877 iState = EPrimed; // go back to primed state (we're not playing)
879 iSinkBufferWithSink = EFalse;
880 iSourceBufferWithSource = EFalse;
882 ResetRefBuffers(); // buffer references may not be valid any more
884 Cancel(); //Stop the state machine
886 if(iState == ERecording)
888 User::Leave(KErrNotSupported);
890 iRepeatTrailingSilenceTimer->Cancel();
893 void CMMFDataPath2::DoPauseL()
896 RDebug::Print(_L("DP::DoPauseL tick-%d (this 0x%x)\n"),User::TickCount(),this);
900 if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting)))
902 if (iDataSink->DataSinkType() == KUidMmfAudioOutput && iIsResumeSupported)
904 iDataSink->SinkPauseL();
905 // When true pause is supported only the datapath's position
906 // should be updated, MDataSource position should be changed
907 iStartPosition = Position();
908 iIsUsingResumeSupport = ETrue;
909 if(iRepeatTrailingSilenceTimer->IsActive())
911 iPauseCalledInsilence=ETrue;
913 // If we wait for the sink to complete play, then we do not proceed with supplying the buffers to the sink
914 // In this case we need to reset the buffers so that InitializeSinkL won't attempt bringing in new ones
915 if (iTransferState == EWaitSink)
922 // If we use resume support, then there is no need to pause source as we would continue to supply buffers to the sink
923 // Here we are not using resume support, thus we're pausing the source
924 iDataSource->SourcePauseL();
925 SetPositionL(Position());
926 iDataSink->SinkStopL();
927 ResetRefBuffers(); // buffer references may not be valid any more
929 Cancel(); //Stop the state machine
931 iPauseCalled = ETrue; // indicate pause is called
933 iState = EPrimed; // go back to primed state (we're not playing)
935 else if(iState == ERecording)
937 iPauseCalled = ETrue;
939 RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n"));
941 iDataSource->SourcePauseL();
943 iRepeatTrailingSilenceTimer->Cancel();
945 RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed);
946 RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this);
951 Cancels the silence timer.
953 void CMMFDataPath2::DoCancel()
956 iRepeatTrailingSilenceTimer->Cancel();
957 iNumberOfTimesToRepeat=0;
961 Allocates buffers in preparation to play.
963 Must be called before calling PlayL().
965 iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink
966 For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL().
968 void CMMFDataPath2::PrimeL()
970 CMMFDataPath::PrimeL();
971 if(iDataSink->DataSinkType() == KUidMmfAudioOutput)
973 CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
974 CMMFDevSound& devSound = audioOutput->SoundDevice();
975 iGetTimePlayedSupported = devSound.IsGetTimePlayedSupported();
976 iIsResumeSupported = devSound.IsResumeSupported();
977 iIsUsingResumeSupport = EFalse;
978 iPauseCalledInsilence = EFalse;
983 Starts an active scheduler 'play' loop.
985 Can only play from the primed state.
987 void CMMFDataPath2::PlayL()
990 #if defined(__PROFILING)
991 RDebug::ProfileEnd(1);
992 #endif // defined(__PROFILING)
995 RDebug::Print(_L("DP::PlayL, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);
996 RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64()));
999 if ((iDataPathCreated) && (iState == EPrimed))
1001 //can only play from the primed state
1003 if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed
1005 //When pause is called silence,we need to send the buffer while resume so icansendbuffer should enabled
1006 if(!iPauseCalledInsilence)
1008 iDataSink->SinkPrimeL(); //propagate change down to sink
1010 iPauseCalled = EFalse;
1013 iCurrentSourceFrameNumber = 0; //reset to beginning
1014 iCurrentSinkFrameNumber = 0; //reset to beginning
1016 iSourceBufferWithSource = EFalse;
1017 iSinkBufferWithSink = EFalse;
1019 iNoMoreSourceData = EFalse;
1020 iAllDataSentToSink=EFalse;
1021 iDataPathCompletedErrorCode=KErrNone;
1023 if(!iIsResumeSupported || !iIsUsingResumeSupport)
1025 SetPositionL( iStartPosition );
1027 iReferenceAudioSamplesPlayed = 0;
1028 iReferenceAudioSamplesRecorded = 0;
1030 //complete a request on iStatus to invoke play code
1031 iDataSource->SourcePlayL(); //propagate state change to source
1033 // This need to be done always since CMMFAudioOutput::EmptyBuffer
1034 // doesn't start playback anymore
1035 iDataSink->SinkPlayL(); //propogate state change to sink
1037 if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
1039 else if(iDataSource->DataSourceType() == KUidMmfAudioInput)
1040 iState = ERecording;
1042 iState = EConverting;
1044 //need to re-initialize any buffer(s) that we only own references to
1045 ChangeDataPathTransferState(EInitializeSink);
1047 iDevSoundRepositionTime = 0;
1048 if(!iRetainRepeatInfo)
1050 iNumberOfTimesPlayed = 0;
1051 iTimeLeftToPlayComplete = -1;
1052 iVerifyPlayComplete = EFalse;
1054 iRetainRepeatInfo = EFalse;
1056 RDebug::Print(_L("DP::Play - DONE\n"));
1061 Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL().
1062 Typically if buffers are created asychronously, the datapath doesn't own the buffer
1063 so leaves cleanup handling to the owner sources/sinks.
1065 Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive,
1066 iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped.
1068 void CMMFDataPath2::ResetL()
1070 CMMFDataPath::ResetL();
1075 Sets the number of times the audio sample is to be repeated during the
1078 A period of silence can follow each playing of the sample. The audio
1079 sample can be repeated indefinitely.
1081 @param aRepeatNumberOfTimes
1082 The number of times the audio sample, together with
1083 the trailing silence, is to be repeated. If this is
1084 set to KMdaRepeatForever, then the audio
1085 sample, together with the trailing silence, is
1086 repeated indefinitely or until Stop() is
1087 called. If this is set to zero, then the audio sample
1089 @param aTrailingSilence
1090 The time interval of the trailing silence in microseconds.
1093 EXPORT_C void CMMFDataPath2::SetRepeats(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence)
1095 iNumberOfTimesToRepeat=aRepeatNumberOfTimes;
1096 iTrailingSilence=aTrailingSilence;
1100 Sets the Drm file source and the automatic execute intent flag. This method is used by the controller plugin
1101 to pass these to the datapath in order to execute the play intent during loop play.
1104 Data Source on which the play intent needs to be executed. This is usually the CMMFFile source
1105 @param aDisableAutoIntent
1106 Boolean variable which states whether the controller plugin or the datapath needs to execute play intent
1107 automatically or not.
1109 EXPORT_C void CMMFDataPath2::SetDrmProperties(MDataSource* aSource, TBool *aDisableAutoIntent)
1111 iDrmSource = aSource;
1112 iDisableAutoIntent = aDisableAutoIntent;
1116 This call indicates PlayL not to reset the iNumberOfTimesPlayed property. This method is used by the controller plugin
1117 during repositioning. PlayL call during seeking should not reset the iNumberOfTimesPlayed property.
1119 EXPORT_C void CMMFDataPath2::RetainRepeatInfo()
1121 iRetainRepeatInfo = ETrue;