diff -r 000000000000 -r 71ca22bcf22a mmfenh/advancedaudiocontroller/audiocontrollerpluginsvariant/AdvancedAudioController/Src/AdvancedAudioPlayController.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmfenh/advancedaudiocontroller/audiocontrollerpluginsvariant/AdvancedAudioController/Src/AdvancedAudioPlayController.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,2793 @@ +/* +* Copyright (c) 2006 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: This file contains the base class from which specific audio +* play controllers are derived. This class encapsulates common +* behavior for all audio play controllers. +* +*/ + + +// INCLUDE FILES +#include "AdvancedAudioPlayController.h" +#include "AdvancedAudioResource.h" +#include "DebugMacros.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// KMdaRepeatForever constant is defined in this file +#include +// CONSTANTS +const TInt KOneThousandMilliSecond = 1000; // 1 sec + +// ============================= LOCAL FUNCTIONS =============================== + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::CAdvancedAudioPlayController +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +// +EXPORT_C CAdvancedAudioPlayController::CAdvancedAudioPlayController() + : iState(EStopped), + iAudioOutput(NULL), + iAudioResource(NULL), + iAudioUtility(NULL), + iDisableAutoIntent (EFalse), + iDecoderExists(EFalse), + iRepeatCount(-1), + iCurrentRepeatCount(0), + iLoopPlayEnabled(EFalse), + iRepeatForever(EFalse), + iTrailingSilenceMs(0), + iSavedTimePositionInMicroSecs(0) //, + { + iEventsEnabled = EFalse; + + RThread().SetPriority(EPriorityRealTime); + } + +EXPORT_C void CAdvancedAudioPlayController::ConstructL() + { + DP1(_L("CAdvancedAudioPlayController::ConstructL this[%x]"), this); + CAdvancedAudioController::ConstructL(); + + // Construct custom command parsers + CMMFAudioPlayDeviceCustomCommandParser* audPlayDevParser = + CMMFAudioPlayDeviceCustomCommandParser::NewL(*this); + CleanupStack::PushL(audPlayDevParser); + AddCustomCommandParserL(*audPlayDevParser); + CleanupStack::Pop(audPlayDevParser); + + CMMFAudioPlayControllerCustomCommandParser* audPlayConParser = + CMMFAudioPlayControllerCustomCommandParser::NewL(*this); + CleanupStack::PushL(audPlayConParser); + AddCustomCommandParserL(*audPlayConParser); + CleanupStack::Pop(audPlayConParser); + + CMMFDRMCustomCommandParser* drmParser = CMMFDRMCustomCommandParser::NewL(*this); + CleanupStack::PushL(drmParser); + AddCustomCommandParserL(*drmParser); + CleanupStack::Pop(drmParser); + + CStreamControlCustomCommandParser* scCCParser = CStreamControlCustomCommandParser::NewL(*this); + CleanupStack::PushL(scCCParser); + AddCustomCommandParserL(*scCCParser); + CleanupStack::Pop(scCCParser); + + // new custom command parser to implement the MapcSetRepeats custom command from the client + CMMFAudioPlayControllerSetRepeatsCustomCommandParser* audPlayConSetRepeatsParser = + CMMFAudioPlayControllerSetRepeatsCustomCommandParser::NewL(*this); + CleanupStack::PushL(audPlayConSetRepeatsParser); + AddCustomCommandParserL(*audPlayConSetRepeatsParser); + CleanupStack::Pop(audPlayConSetRepeatsParser); + } + +// Destructor +EXPORT_C CAdvancedAudioPlayController::~CAdvancedAudioPlayController() + { + DP1(_L("CAdvancedAudioPlayController::~CAdvancedAudioPlayController start this[%x]"), this); + DP4(_L("CAdvancedAudioPlayController::~CAdvancedAudioPlayController iState[%d] iWait[%d] iBlockSetPos[%d] iSharedBuffers.Count[%d]"), + iState, iWait, iBlockSetPos, iSharedBuffers.Count()); + RThread().SetPriority(EPriorityNormal); + + if (iMetaDataEntries.Count()) + { + iMetaDataEntries.ResetAndDestroy(); + iMetaDataEntries.Close(); + } + + if (iTrailingSilenceTimer) + { + delete iTrailingSilenceTimer; + iTrailingSilenceTimer = NULL; + } + + delete iDataSourceAdapter; + delete iWait; + delete iBlockSetPos; + iSharedBuffers.ResetAndDestroy(); + iSharedBuffers.Close(); + DP0(_L("CAdvancedAudioPlayController::~CAdvancedAudioPlayController end")); + } + + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::DoReadHeaderL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::DoReadHeaderL(CMMFDataBuffer* /*aBuffer*/) + { + // do nothing + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::DoSetPositionL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::DoSetPositionL(const TTimeIntervalMicroSeconds& aTimePos) + { + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL start -----------")); + TInt err = KErrNone; + TInt foundPosition; + TInt foundTimeMs; + TBool seekFwd = EFalse; + TUint timeMs = aTimePos.Int64()/1000; + DP2(_L("CAdvancedAudioPlayController::DoSetPositionL timeMs[%u] pre[%d]"), timeMs, (iPreSeekTime.Int64()/1000)); + // Fix for the error ou1cimx1#151598 - ESLM-7T5GJH + // unregisters any old position entries from the FrameTable before setting any new play seek positions + iAudioUtility->SeekToTimeMs(KMaxTUint); + + if (aTimePos < 0) + { + timeMs = 0; + } + + // get current position in time + // if we need to play to seek forward (not in table), the time will be advanced immediately + // if we pause before we get there, pause will set pos to that time - which is what we want. + // but we need to remember that we are not there yet so we can still seek forward. So we use pre-seek time. + if (!iPlaySeeking) + { + iPreSeekTime = PositionL(); + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL save pre-seek time[%d]"), iPreSeekTime.Int64()/1000); + } + if (aTimePos > iPreSeekTime) + { + seekFwd = ETrue; + } + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL seekFwd[%d]"), seekFwd); + + TUint const seekTime = timeMs; + TUint position = 0; + TBool posFoundInTable = EFalse; + + // look in current buffers first + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL not time seekable")); + err = SetPositionInSharedBuffers(timeMs, foundPosition, foundTimeMs); + if (!iSourceIsTimeSeekable && !iSourceIsPosSeekable) + { + if (err == KErrNone) + { // found exact position + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL found exact position")); + iCurrentPosition = foundPosition; // absolute position into file or content + iTimePositionInMicroSecs = foundTimeMs; + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL iTimePositionInMicroSecs[%d]"),foundTimeMs); + iAudioUtility->SetSourceReference(iTimePositionInMicroSecs/1000, iCurrentPosition-iHeaderOffset-iSyncOffset); + RefillPreceedingBuffersL(); + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL end -----------")); + return; + } + // err means it just found something close + ASSERT(err != KErrGeneral); + if (err == KErrGeneral) + { // KErrGeneral means it did not find anything and we have to stop. + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL abort -------")); + TMMFEvent event(KMMFEventCategoryPlaybackComplete,KErrGeneral); + SendEvent(event); + return; + } + // source is not seekable so use buffered data + // use closest time and position found above + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL using closest position")); + iCurrentPosition = foundPosition; + iTimePositionInMicroSecs = foundTimeMs; + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL iTimePositionInMicroSecs[%d]"),foundTimeMs); + iAudioUtility->SetSourceReference(iTimePositionInMicroSecs/1000, iCurrentPosition-iHeaderOffset-iSyncOffset); + RefillPreceedingBuffersL(); + } + if (iSourceIsTimeSeekable) + { // time seekable source won't have accurate table positions + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL is time seekable")); + iPreSeekTime = 0; + iDataSourceAdapter->SourceStopL(); // clear the buffers in the source before seeking and priming it + iDataSourceAdapter->SourcePrimeL(); + err = iDataSourceAdapter->SeekToTime(seekTime, timeMs); + iAudioUtility->ResetTable(); + iDataSourceAdapter->SourcePlayL(); + iTimePositionInMicroSecs = timeMs; + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL iTimePositionInMicroSecs[%d]"),timeMs); + iSourceReadPosition = 0; // we don't know the position - source doesn't give us that + // iCurrentPosition = iSourceReadPosition; + iCurrentPosition = KMaxTUint; + iAudioUtility->SetSourceReference(iTimePositionInMicroSecs/1000, 0); + TRAP(err, RefillSharedBuffersL()); + } + else if (iSourceIsPosSeekable) + { + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL is position seekable")); + TInt err = iAudioUtility->FindFramePosFromTime(timeMs, position); + if (err == KErrNone) + { + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL pos found in table")); + posFoundInTable = ETrue; + } + TBool tryForAccuracy = EFalse; + if (posFoundInTable) + { // seek in source is taken care of below + // The position was found in the table, and assuming it was found in the low res table + // lets try to seek fwd to the exact position. + // The reason for this is that the record utility does a stop play instead of a pause play + // The inaccuracy in the resume position is then visible, using the low res table. + tryForAccuracy = ETrue; + } + else + { // then we must be going forward or just starting + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL pos not found in table")); + err = iAudioUtility->LastFramePos(position); + if (err) // use KErrDoesNotExist or something + { // do this a little better, maybe return something different from LastFramePos + position = 0; + timeMs = 0; + } + else + { + err = iAudioUtility->FindFrameTimeFromPos(timeMs, position); + } + } + // seek in source + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL seek in the source")); + iSourceReadPosition = position+iHeaderOffset+iSyncOffset; + iDataSourceAdapter->SourceStopL(); // clear the buffers in the source before seeking and priming it + iDataSourceAdapter->SourcePrimeL(); + iDataSourceAdapter->SeekToPosition(iSourceReadPosition); + iAudioUtility->ResetTable(); + iDataSourceAdapter->SourcePlayL(); + iCurrentPosition = iSourceReadPosition; + iTimePositionInMicroSecs = timeMs; + iTimePositionInMicroSecs *=1000; // bh 21Mar08 // need this time to correlate to the position found + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL iTimePositionInMicroSecs[%d]"),timeMs); + iAudioUtility->SetSourceReference(iTimePositionInMicroSecs/1000, iCurrentPosition-iHeaderOffset-iSyncOffset); + TRAP(err, RefillSharedBuffersL()); + DP1(_L("CAdvancedAudioPlayController::DoSetPositionL RefillSharedBuffersL[%d]"),err); + if (seekFwd && (!posFoundInTable || tryForAccuracy)) + { + // seeking forward past entries available in table + // will scan buffers for frame lengths without rendering + // which will add table entries and notify when pos is reached + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL do non-render seek fwd")); + SeekToTimeMsL(seekTime); // this will set iTimePositionInMicroSecs to the seek fwd time + } + else + { + iPreSeekTime = 0; + } + } + else + { // continue from closest position and render once the time is reached + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL src not seekable continue")); + if (seekFwd) + { + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL src not seekable do non-render seek fwd")); + SeekToTimeMsL(seekTime); + } + } + DP0(_L("CAdvancedAudioPlayController::DoSetPositionL end -----------")); + } + +void CAdvancedAudioPlayController::SeekToTimeMsL(TUint aTimeMs) + { // used when seeking forward without rendering + DP1(_L("CAdvancedAudioPlayController::SeekToTimeMs[%d]"),aTimeMs); + + iAudioUtility->SeekToTimeMs(aTimeMs); + iPlaySeeking = ETrue; + // ok to set the time forward because during this seek, devsound won't be getting + // any data - so it's samples played would stay 0 and not keep incrementing if position is called + iTimePositionInMicroSecs = aTimeMs; + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::SeekToTimeMs iTimePositionInMicroSecs[%d]"),iTimePositionInMicroSecs); + if (iState == EPaused) + { + PlayForPauseSeekL(); + } + else if (iState == EInitialized) + { + PlayForInitPositionL(); + } + } + +void CAdvancedAudioPlayController::GoToInitPositionL() + { + DP1(_L("CAdvancedAudioPlayController::GoToInitPositionL[%d]"),iInitPosition.Int64()); + if (iInitPosition >= 0) + { // -1 is inactive, >=0 will seek + TTimeIntervalMicroSeconds position = iInitPosition; + DP0(_L("CAdvancedAudioPlayController::GoToInitPositionL clearing iInitPosition and seeking")); + iInitPosition = -1; + DoSetPositionL(position); + } + // Register for the Playwindow end position, if PlayWindow is created + if (iPlayWindowEndPosition > 0) + { + iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64()/1000); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::RefillSharedBuffersL +// ----------------------------------------------------------------------------- +// +void CAdvancedAudioPlayController::RefillSharedBuffersL() + { + DP0(_L("CAdvancedAudioPlayController::RefillSharedBuffersL")); + TInt i; + ClearSharedBuffersL(); + for (i=0; i < iSharedBufferMaxNum; i++) + { + FillSharedBufferL(iSharedBuffers[i]); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::ReinitSharedBuffersL +// ----------------------------------------------------------------------------- +// +void CAdvancedAudioPlayController::ClearSharedBuffersL() + { + DP0(_L("CAdvancedAudioPlayController::ClearSharedBuffersL")); + for (TInt i=0; i < iSharedBufferMaxNum; i++) + { + static_cast(iSharedBuffers[i])->Data().SetLength(0); + iSharedBuffers[i]->SetLastBuffer(EFalse); + iSharedBuffers[i]->SetStatus(EAvailable); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::InitSharedBuffersL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::InitSharedBuffersL() + { + DP0(_L("CAdvancedAudioPlayController::InitSharedBuffersL")); + TInt i; + ResetSharedBuffersL(iSharedBufferMaxNum, iSharedBufferMaxSize); + for (i=0; i < iSharedBufferMaxNum; i++) + { + FillSharedBufferL(iSharedBuffers[i]); + } + } + +EXPORT_C void CAdvancedAudioPlayController::RefillPreceedingBuffersL() + { + DP0(_L("CAdvancedAudioPlayController::RefillPreceedingBuffersL")); + TInt curIndex = iSharedBufferIndex; + TInt curPos = iSharedBuffers[curIndex]->FrameNumber(); + TInt maxNum = iSharedBufferMaxNum; + // buffers are filled in increasing order + TInt index = iSharedBufferIndex; + TInt pos; + TInt stat; + for (TInt i=0; i< maxNum-1; i++) + { + index++; + if (index >= maxNum) + { + index = 0; + } + stat = iSharedBuffers[index]->Status(); + pos = iSharedBuffers[index]->FrameNumber(); + DP4(_L("CAdvancedAudioPlayController::RefillPreceedingBuffersL index[%d] stat[%d] pos[%d] bufpos[%d]"), + index, stat, pos, iSharedBuffers[index]->Position()); + if ((pos < curPos) && (stat == EFull)) + { + DP1(_L("CAdvancedAudioPlayController::RefillPreceedingBuffersL index[%d]"),index); + FillSharedBufferL(iSharedBuffers[index]); + } + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SourceSinkStopL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::SourceSinkStopL() + { + iDataSourceAdapter->SourceStopL(); + + if (iDataSink->DataSinkType() == KUidMmfFileSink) + { + iDataSink->SinkStopL(); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::FillSharedBufferL +// Read the next block of data from source into the given buffer. +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::FillSharedBufferL( + CMMFBuffer* aBuffer) + { + DP2(_L("CAdvancedAudioPlayController::FillSharedBufferL ptr[%x], iState[%d]"), + static_cast(aBuffer)->Data().Ptr(), iState); + + aBuffer->SetLastBuffer(EFalse); + aBuffer->SetStatus(EBeingFilled); + aBuffer->SetPosition(0); + static_cast(aBuffer)->Data().SetLength(0); + + TMediaId mediaId(KUidMediaTypeAudio); + iDataSourceAdapter->FillBufferL(aBuffer, this, mediaId); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::FindBufferFromPos +// ----------------------------------------------------------------------------- +// aPos is relative to source position in that it includes any header offsets +// Will return KErrNotFound if the position is not found in the buffers. +// Will look only in full buffers. +EXPORT_C TInt CAdvancedAudioPlayController::FindBufferFromPos(TUint aPos) + { + DP1(_L("CAdvancedAudioPlayController::FindBufferFromPos, aPos[%d]"), aPos); + + TInt retVal = KErrNotFound; + + for (TInt i = 0; i < iSharedBufferMaxNum; i++) + { + DP5(_L("CAdvancedAudioPlayController::FindBufferFromPos iSharedBuffers[%d], ptr=%x, FrameNumber[%d], Status[%d], LastBuffer[%d]"), i, iSharedBuffers[i]->Data().Ptr(), iSharedBuffers[i]->FrameNumber(), iSharedBuffers[i]->Status(), iSharedBuffers[i]->LastBuffer()); + TUint framenumber = iSharedBuffers[i]->FrameNumber(); + TUint buffersize = iSharedBuffers[i]->BufferSize(); + DP6(_L("CAdvancedAudioPlayController::FindBufferFromPos iSharedBuffers[%d], ptr=%x, FrameNumber[%d], Status[%d], LastBuffer[%d], BufferSize[%d]"), i, iSharedBuffers[i]->Data().Ptr(), framenumber, iSharedBuffers[i]->Status(), iSharedBuffers[i]->LastBuffer(), buffersize); + if ((aPos >= framenumber) && (aPos < (framenumber+buffersize)) && +// if ((aPos >= framenumber) && (aPos < (framenumber+iSharedBufferMaxSize)) && + (iSharedBuffers[i]->Status() == EFull)) + { + iSharedBuffers[i]->SetPosition(aPos-framenumber); + DP1(_L("CAdvancedAudioPlayController::FindBufferFromPos setting position to [%d]"), iSharedBuffers[i]->Position()); + retVal = i; + break; + } + } + DP1(_L("CAdvancedAudioPlayController::FindBufferFromPos retVal[%d]"), retVal); + return retVal; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SetPositionInSharedBuffers +// ----------------------------------------------------------------------------- +// +/* +This function will look in current buffers that aren't being filled and set the +buffer and position in it to the corresponding current time. +An error will be returned if the exact position was not located in a full buffer. +A closest available will be calculated. +The buffers past the set will be sent to be filled. +If a buffer is full, it can be used. +If it is being filled, it can not be used here. +If a buffer is other than full or being filled, it should be sent to be filled +in the correct sequence after the buffer position is determined. +*/ +EXPORT_C TInt CAdvancedAudioPlayController::SetPositionInSharedBuffers(TUint aTimeMs, TInt& aFoundPosition, TInt& aFoundTimeMs) + { + TInt err = KErrNone; + TUint position; + TUint timeMs; + + DP1(_L("CAdvancedAudioPlayController::SetPositionInSharedBuffersL -- start this[%x]"), this); + + for (TInt i = 0; i < iSharedBufferMaxNum; i++) + { + DP6(_L("CAdvancedAudioPlayController::SetPositionInSharedBuffersL iSharedBuffers[%d] ptr[%x] FrameNumber[%d] Status[%d] LastBuffer[%d] bufpos[%d]"), + i, iSharedBuffers[i]->Data().Ptr(), iSharedBuffers[i]->FrameNumber(), iSharedBuffers[i]->Status(), iSharedBuffers[i]->LastBuffer(), iSharedBuffers[i]->Position()); + + if (iSharedBuffers[i]->Status() != EBeingFilled) + { // reset the positions on the buffers that the source does not have + iSharedBuffers[i]->SetPosition(0); + } + + if (iSharedBuffers[i]->Status() == EBeingEmptied) + { + iSharedBuffers[i]->SetStatus(EFull); + } + } + timeMs = aTimeMs; + // get the closest position from the table for the given time + err = iAudioUtility->FindFramePosFromTime(timeMs, position); // err indicates if position found, or just what was available + // see if the buffer with this position exists + iSharedBufferIndex = FindBufferFromPos(position + iHeaderOffset + iSyncOffset); + TUint framenumber = 0; + + if (iSharedBufferIndex >= 0) + { + } + else + { + DP0(_L("CAdvancedAudioPlayController::SetPositionInSharedBuffersL, buffer not found")); + err = KErrNotFound; + // find a frame in current buffers + TInt minindex = 0; + TInt tempframenum = -1; + + for (TInt j = 0; j < iSharedBufferMaxNum; j++) + { + if (iSharedBuffers[j]->Status() == EFull) + { + framenumber = iSharedBuffers[j]->FrameNumber(); + } + + if (((framenumber < tempframenum) || (tempframenum == -1)) && + (iSharedBuffers[j]->Status() == EFull)) + { + tempframenum = framenumber; + minindex = j; + } + } + if (tempframenum == -1) + { + return KErrNotFound; + } + iSharedBufferIndex = minindex; + framenumber = iSharedBuffers[iSharedBufferIndex]->FrameNumber(); + // find a frame start + if (framenumber < (iHeaderOffset+iSyncOffset)) + { // special case for buffer that still has header info + position = 0; + timeMs = 0; + } + else + { + position = framenumber-iHeaderOffset-iSyncOffset; + TInt err1 = iAudioUtility->FindFrameTimeFromPos(timeMs, position); +//#ifdef _DEBUG + if (err1 != KErrNone) + { // we have to find a location in the table for the buffers we have + // amr may scan whole file and buffers won't contain start data + // but it would only happen for seekable source + DP0(_L("CAdvancedAudioPlayController::SetPositionInSharedBuffersL, position in buffers not found -- aborting")); + return KErrGeneral; +// ASSERT(err1 == KErrNone); + } +//#endif + } + } + + aFoundPosition = position + iHeaderOffset + iSyncOffset; + aFoundTimeMs = timeMs; + framenumber = iSharedBuffers[iSharedBufferIndex]->FrameNumber(); + iSharedBuffers[iSharedBufferIndex]->SetPosition(aFoundPosition-framenumber); + // after this send buffers to be filled that need to be filled to maintain correct sequence. + DP2(_L("CAdvancedAudioPlayController::SetPositionInSharedBuffersL, iSharedBufferIndex[%d] pos[%d]-- end"), + iSharedBufferIndex, aFoundPosition-framenumber); + return err; + } + + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::UpdateDuration +// +// Possible parameters are: +// -1 = just update duration +// 0 = triggers duration changed immediately +// a number = will trigger duration changed event if duration has changed this amount given +// ----------------------------------------------------------------------------- +// +EXPORT_C TInt CAdvancedAudioPlayController::UpdateDuration(TInt aLimitInMilliSecond) + { // updates iDuration + DP2(_L("CAdvancedAudioPlayController::UpdateDuration, iEventsEnabled[%d], aLimitInMilliSecond[%d]"), iEventsEnabled, aLimitInMilliSecond); + + if (iDurationFrozen) + { + return KErrNone; + } + + if (aLimitInMilliSecond < -1) + { + return KErrArgument; + } + + if (!iAudioUtility) + { + return KErrNone; + } + + TInt64 duration = iAudioUtility->Duration(); // calculates new duration + if ((aLimitInMilliSecond == -1) || !iEventsEnabled) + { + iDuration = duration; + } + else if ((iDuration != duration) && (Abs(duration-iDuration) >= (aLimitInMilliSecond*1000))) + { + iDuration = duration; + SendEventToClient(TMMFEvent(KStreamControlEventDurationChanged, KErrNone)); + } + DP1(_L("CAdvancedAudioPlayController::UpdateDuration, iDuration[%d]"), iDuration); + return KErrNone; + } + +EXPORT_C TInt CAdvancedAudioPlayController::UpdateBitRate() + { + DP0(_L("CAdvancedAudioPlayController::UpdateBitRate")); + if (iBitRateFrozen) + { + DP0(_L("CAdvancedAudioPlayController::UpdateBitRate frozen")); + return KErrNone; + } + if (iAudioUtility) + { + iBitRate = iAudioUtility->BitRate(); + DP1(_L("CAdvancedAudioPlayController::UpdateBitRate iBitRate[%d]"), iBitRate); + } + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::HandleAutoPauseEvent +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::HandleAutoPauseEvent() + { + DP0(_L("CAdvancedAudioPlayController::HandleAutoPauseEvent")); + iState = EAutoPaused; + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedAutoPaused, KErrNone)); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::HandlePreemptionEvent +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::HandlePreemptionEvent(TInt aError) + { + DP1(_L("CAdvancedAudioPlayController::HandlePreemptionEvent this[%x]"), this); + TInt err; + + // Stop the CActiveSchedulerWait for SetPosition if it is started during Playforseek + if(iBlockSetPos) + { + if (iBlockSetPos->IsStarted()) + { + iBlockSetPos->AsyncStop(); + } + } + + iRequestState = EPaused; + TRAP(err, DoPauseL(ETrue)); // this is a preemption pause + // In case of pre-emption we should only Pause ... but not Stop. + SendEventToClient(TMMFEvent(KMMFEventCategoryPlaybackComplete, aError)); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::HandleGeneralEvent +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::HandleGeneralEvent(const TMMFEvent& aEvent) + { + DP1(_L("CAdvancedAudioPlayController::HandleGeneralEvent this[%x]"), this); + TRAP_IGNORE(DoStopL(aEvent.iErrorCode)); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SetPlaybackWindowBoundariesL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::SetPlaybackWindowBoundariesL(const TTimeIntervalMicroSeconds& aStart, const TTimeIntervalMicroSeconds& aEnd) + { + DP2(_L("CAdvancedAudioPlayController::SetPlaybackWindowBoundariesL start[%d] end[%d]"), aStart.Int64(), aEnd.Int64()); + + // if client calls SetPlayWindow with the same values then it can be ignored + // as the controller may be in the middle of processing. + if (iPlayWindowStartPosition == aStart && iPlayWindowEndPosition == aEnd) + { + return; + } + + if ( iPlayWindowStartPosition < TTimeIntervalMicroSeconds(0) ) + { + iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0); + } + else + { + iPlayWindowStartPosition = aStart; + } + + if ( iPlayWindowEndPosition < TTimeIntervalMicroSeconds(0) ) + { + iPlayWindowEndPosition = TTimeIntervalMicroSeconds(0); + } + else + { + iPlayWindowEndPosition = aEnd; + } + + // We should not set the iInitPosition here because, if ClearPlayWindow is called when iState == EPlaying || EPaused + // then iInitPosition will be reset to zero, which should not be the case because the default iInitPosition should be -1. + // We just have to clear the play window end postion and continue to play from the current playback position + // iInitPosition = iPlayWindowStartPosition; + + DP2(_L("CAdvancedAudioPlayController::SetPlaybackWindowBoundariesL iPlayWindowStartPosition[%d] iPlayWindowEndPosition[%d]"), iPlayWindowStartPosition.Int64()/1000, iPlayWindowEndPosition.Int64()/1000); + // Registers / Unregisters the PlayWindowEndPosition depending on the aEnd value, whenever the play window is set / cleared + // For SYMBIAN ClearPlayWindow(): If the ClearPlayWindow() is called during playing, + // it deletes the playwindow that has been set + iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64()/1000); + if (aStart == iSavedSetPosition) + { + return; + } + switch (iState) + { + case EStopped: // no need to do anything with start position since the start position is saved + // iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64()/1000); + // break; + case EInitializing: // if already primed or priming, we need to get to correct start position + case EInitialized: + // DoSetPositionL(iInitPosition); + // iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64()/1000); + SetPositionL(iPlayWindowStartPosition); + break; + case EPlaying: + case EPaused: + case EAutoPaused: + // If the Playwindow start position is greater than the current position + // then we seek to the new position and start playing from there. + if ( iPlayWindowEndPosition != TTimeIntervalMicroSeconds(0) ) + { + TTimeIntervalMicroSeconds currentPosition = PositionL(); + if ( currentPosition < iPlayWindowStartPosition ) + { + SetPositionL(iPlayWindowStartPosition); + } + } + break; + default: + Panic(EBadState); + break; + } + + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::AddDataSourceL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::AddDataSourceL( + MDataSource& aSource) + { + DP1(_L("CAdvancedAudioPlayController::AddDataSourceL this[%x]"), this); + iSourceUnreadable = EFalse; + iEnablePrimedStateChangedEvent = EFalse; + // source type is intended to not be needed by controllers + // but metadata functions still use it + iSourceType = aSource.DataSourceType(); + iBitRateFrozen = EFalse; + iDurationFrozen = EFalse; + if (iWait) + { + iWait->AsyncStop(); + } + if (iDataSourceAdapter) + { + User::Leave(KErrAlreadyExists); + } + if (iSharedBufferMaxNum <= 2) + { + iSharedBufferMaxNum = 3; + } + + + // set iReadHeader here in case prime is not called before set position is used. + // reset before adding data source + DP0(_L("CAdvancedAudioPlayController::AddDataSourceL reseting iSharedBufferCnt iReadHeader iInitPosition position vars")); + iSharedBufferCnt = 0; + iReadHeader = ETrue; + iInitPosition = -1; + ResetPositionVariables(); + + DoAddDataSourceL(); + + if (!iDataSourceAdapter) + { + iDataSourceAdapter = CDataSourceAdapter::NewL(); + } + iDataSourceAdapter->SetDataSourceL(&aSource, this, this); + + iDataSource = &aSource; // remove this eventually when all the references are removed + + iSourceIsTimeSeekable = iDataSourceAdapter->IsTimeSeekable(); + iSourceIsPosSeekable = iDataSourceAdapter->IsPositonSeekable(); + + if (iDataSourceAdapter->IsProtectedL()) + { + if (iDataSink && iDataSink->DataSinkType() == KUidMmfFileSink) + { + // Conversion is not allowed for DRM protected files + User::Leave(KErrNotSupported); + } + } + + iDataSourceAdapter->SetSourcePrioritySettings(iPrioritySettings); + + if (iAudioUtility) + { // 3gp would not have utility built yet + iAudioUtility->SetObserver(*this); + } + + // we need to block this until duration is calculated if using mmfplayutility + iBlockDuration = EFalse; + + if ((!iEventsEnabled) && (!iDataSourceAdapter->OnlyHeaderPresent())) + { + // recorder inserts just the header into the file before recording + // we don't want to prime in this case + DP0(_L("CAdvancedAudioPlayController::AddDataSourceL() Prime to get duration")); + iBlockDuration = ETrue; + DoInitializeL(); // to get data from the source to calculate bitrate and duration + } + if ((!iEventsEnabled) && (iDataSourceAdapter->OnlyHeaderPresent())) + { + // this is a file being recorded. + // the recorder might have to open this file again - and if we keep it open + // with shared read access, he won't be able to open it for writing. + // So we will close the file here. + iDataSourceAdapter->SourceStopL(); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::DurationL +// +// ----------------------------------------------------------------------------- +// +EXPORT_C TTimeIntervalMicroSeconds CAdvancedAudioPlayController::DurationL() const + { + DP2(_L("CAdvancedAudioController::DurationL this[%x] iDuration[%d]"), this, iDuration); + // we need to block this until duration is calculated if using mmfplayutility + if (iBlockDuration) + { + iBlockDuration = EFalse; + iWait = new (ELeave) CActiveSchedulerWait(); + DP0(_L("CAdvancedAudioController::DurationL() blocking for duration")); + iWait->Start(); + DP0(_L("CAdvancedAudioController::DurationL() continuing")); + delete iWait; + iWait = NULL; + } + return TTimeIntervalMicroSeconds(iDuration); + } + + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::AddDataSinkL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::AddDataSinkL( + MDataSink& aSink) + { + DP1(_L("CAdvancedAudioPlayController::AddDataSinkL this[%x]"), this); + +// TThreadStackInfo info; +// RThread().StackInfo(info); +// DP1(_L("### Stack Base Address: [0x%x]"), info.iBase); +// DP1(_L("### Stack End Address: [0x%x]"), info.iLimit); + + if (iDataSink) + { + User::Leave(KErrAlreadyExists); + } + + if (aSink.DataSinkType() == KUidMmfAudioOutput) + { + iDataSink = &aSink; + } + else if (aSink.DataSinkType() == KUidMmfFileSink) + { // only use of source type here other than metadata functions + if (iDataSource && iSourceType == KUidMmfFileSource) + { + CMMFFile* file = static_cast(iDataSource); + file->SourcePrimeL(); + + if (file->IsProtectedL()) + { + // Conversion is not allowed for DRM protected files + User::Leave(KErrNotSupported); + } + } + + iDataSink = &aSink; + iDataSink->SinkPrimeL(); + iDataSink->SinkThreadLogon(*this); + iDriveNumber = iAudioUtility->GetDriveNumber(static_cast(iDataSink)->FileDrive()); + } + else + { + User::Leave(KErrNotSupported); + } + DoAddDataSinkL(); // iAudioOutput will be created here + // if the source has already been added but the output has not been configured because the sink was + // not ready, then we will configure the output here. + if (iSinkInitDataReady) + { + DoInitializeSinkL(); // the decoder will be created here + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::RemoveDataSourceL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::RemoveDataSourceL( + MDataSource& aDataSource) + { + DP0(_L("CAdvancedAudioPlayController::RemoveDataSourceL")); + + if (!iDataSource) + { + User::Leave(KErrNotReady); + } + + if (iDataSource != &aDataSource) + { + User::Leave(KErrArgument); + } + + if ((iState != EStopped) && (iState != EInitialized)) + { + User::Leave(KErrNotReady); + } + + iDataSource->SourceStopL(); // should always stop source before logoff + iDataSource->SourceThreadLogoff(); + + MapcDeletePlaybackWindowL(); + + if (iIsDRMProtected) + { + delete iDataSource; + } + iDataSource = NULL; + delete iDataSourceAdapter; + iDataSourceAdapter = NULL; + iSinkInitDataReady = EFalse; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::RemoveDataSinkL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::RemoveDataSinkL( + MDataSink& aDataSink) + { + DP0(_L("CAdvancedAudioPlayController::RemoveDataSinkL")); + + if (!iDataSink) + { + User::Leave(KErrNotReady); + } + + if (iDataSink != &aDataSink) + { + User::Leave(KErrArgument); + } + + if ((iState != EStopped) && (iState != EInitialized)) + { + User::Leave(KErrNotReady); + } + + iDataSink->SinkStopL(); // should always stop source before logoff + iDataSink->SinkThreadLogoff(); + + // dereference Decoder from Utility before deleting AudioOutput (which took ownership of decoder) + if (iAudioUtility) + { + iAudioUtility->DeReferenceDecoder(); + } + + delete iAudioOutput; + iAudioOutput = NULL; + iDataSink = NULL; + iDecoderExists = EFalse; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::ResetL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::ResetL() + { + DP0(_L("CAdvancedAudioPlayController::ResetL")); + + RemoveDataSourceL(*iDataSource); + RemoveDataSinkL(*iDataSink); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::PrimeL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::PrimeL() + { // this is for user call only - this changes the request state + DP2(_L("CAdvancedAudioPlayController::PrimeL this[%x] iState[%d]"), this, iState); + + switch (iState) + { + case EStopped: + iRequestState = EInitialized; + iEnablePrimedStateChangedEvent = ETrue; + DoInitializeL(); + break; + case EInitializing: // we may already be initializing since we may doinitialize to get header info for mmf client-events disabled) + iRequestState = EInitialized; + iEnablePrimedStateChangedEvent = ETrue; + break; + case EInitialized: + iRequestState = EInitialized; + iEnablePrimedStateChangedEvent = ETrue; + if (AllBuffersFilled()) + { + iEnablePrimedStateChangedEvent = EFalse; + DP0(_L("CAdvancedAudioPlayController::PrimeL state changed primed")); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPrimed, KErrNone)); // both source and sink are ready. + } + break; + case EPlaying: + case EPaused: + case EAutoPaused: + break; + default: + Panic(EBadState); + break; + } + } + +void CAdvancedAudioPlayController::DoInitializeL() + { // this is the DoPrimeL + DP1(_L("CAdvancedAudioPlayController::DoInitializeL this[%x]"), this); + + if (iSourceUnreadable) + { + DP0(_L("CAdvancedAudioPlayController::PrimeL KErrCorrupt")); + User::Leave(KErrCorrupt); + } + + if (iDataSourceAdapter) + { + iState = EInitializing; + if (iAudioUtility) + { // 3gp won't have a utility yet + TInt err = iAudioUtility->ResetTable(); + } + iDataSourceAdapter->SourcePrimeL(); // get the source ready to process data + iDataSourceAdapter->SourcePlayL(); // get the source ready to send data + InitSharedBuffersL(); + // The position variables are reset when primed. + // We don't want to reset them during stop because time needs to be maintained + // in case position is called after stopping. + ResetPositionVariables(); + } + } + + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::PlayL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::PlayL() + { // this is for user call only - this changes the request state + DP5(_L("CAdvancedAudioPlayController::PlayL this[%x] iCurrentPosition[%d] iSourceReadPosition[%d] iState[%d] iRequestState[%d]"), + this, iCurrentPosition, iSourceReadPosition, iState, iRequestState); + if (iSourceUnreadable) + { + SendEventToClient(TMMFEvent(KMMFEventCategoryPlaybackComplete, KErrCorrupt)); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedStopped, KErrCorrupt)); + return; + } + if (iRequestState == EPlaying) + { + return; + } + if (iPlayingForDuration || iPlayingForPauseSeek || iPlayingForInitPos) + { + // PlayForDuration is not used. + // Internally we use DoPlayL(). + // Just save the request and return, we don't want to call doplay again here. + // User has overridden reason for play - we don't want to transition back to old state now. + // iPlayingForDuration, iPlayingForPauseSeek, and iPlayingForInitPos will + // be reset when the current seek is complete. + // Already playing internally so send state change + iRequestState = EPlaying; + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPlaying, KErrNone)); + return; + } + switch (iState) + { + case EStopped: + // don't initialize playback with DoInitialize() here + // because latent play calls would get through after stops - seek to eof and play + // also, latent play calls would get through after pausing just after eof reached and controller stops. + DP1(_L("CAdvancedAudioPlayController::PlayL iState=EStopped and iRequestState[%d]"), iRequestState); + if ((iRequestState == EInitialized) || (iRequestState == EPaused)) + { // primed state requested, but will stop if eof + SendEventToClient(TMMFEvent(KMMFEventCategoryPlaybackComplete, KErrNone)); + } + break; + case EInitializing: // priming + iRequestState = EPlaying; + break; + case EInitialized: // primed + iRequestState = EPlaying; + if (iPlayWindowEndPosition > 0) + { + DP1(_L("CAdvancedAudioPlayController::PlayL iAudioUtility->SetPlayWindowEndTimeMs(%d)"), I64LOW(iPlayWindowEndPosition.Int64()/1000)); + iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64() / 1000); + } + if (iAudioOutput->IsDSStopped()) + { // during loop play the output is not stopped unless we did a seek - seek stops the output and we need to call DoPlay + DP0(_L("CAdvancedAudioPlayController::PlayL Calling DoPlay iState=EInitialized and iRequestState=EPlaying")); + DoPlayL(); + } + else + { + DP1(_L("CAdvancedAudioPlayController::PlayL Calling DoResume iState=EInitialized iCurrentPosition[%d]"), iCurrentPosition); + if (IsLoopPlayEnabled()) + { + DoResume(iCurrentPosition); // sends state change event if successful + } + else + { + DoPlayL(); + } + } + break; + case EPlaying: + break; + case EPaused: + iRequestState = EPlaying; + if (iPlayWindowEndPosition > 0) + { + DP1(_L("CAdvancedAudioPlayController::PlayL iAudioUtility->SetPlayWindowEndTimeMs(%d)"), I64LOW(iPlayWindowEndPosition.Int64()/1000)); + iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64() / 1000); + } + if (iAudioOutput->IsDSStopped()) + { // during loop play the output is not stopped unless we did a seek - seek stops the output and we need to call DoPlay + DP0(_L("CAdvancedAudioPlayController::PlayL Calling DoPlay iState=EPaused")); + DoPlayL(); + } + else + { + DP1(_L("CAdvancedAudioPlayController::PlayL Calling DoResume iState=EPaused iCurrentPosition[%d]"), iCurrentPosition); + if (IsLoopPlayEnabled()) + { + DoResume(iCurrentPosition); + } + else + { + DoPlayL(); + } + } + break; + case EAutoPaused: + break; + default: + Panic(EBadState); + break; + } + +// SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPlaying, KErrNone)); + } + + +void CAdvancedAudioPlayController::DoPlayL() + { + DP2(_L("CAdvancedAudioPlayController::DoPlayL reqstate[%d] pfps[%d]"),iRequestState,iPlayingForPauseSeek); + if ((iRequestState == EPlaying) || iPlayingForInitPos || iPlayingForDuration || iPlayingForPauseSeek) + { + if (AllBuffersFilled()) // state entry condition + { // DoSetPositionL will look in shared buffers first and not seek if found + // we may consider to use the current time here and resolve current position here instead of other places in the code + + if (iCurrentPosition == KMaxTUint) + { // in case of a time seekable source, we use KMaxTUint in the seek since we don't know source position + DP0(_L("CAdvancedAudioPlayController::DoPlayL Resetting iCurrentPosition to zero")); + iCurrentPosition = 0; + } + + iSharedBufferIndex = FindBufferFromPos(iCurrentPosition); + + // if there was seek past end of file, the buffers may not have any data + // they may all be empty last buffers + if ((iSharedBufferIndex < 0) and AllBuffersEmpty()) + { // position in buffer not found + DoStopL(KErrNone); + return; + } + if (iSharedBufferIndex < 0) + { + DoStopL(KErrGeneral); + return; + } + + if (iDataSink->DataSinkType() == KUidMmfFileSink) + { + static_cast(iDataSink)->SinkPrimeL(); + static_cast(iDataSink)->FileL().SetSize(0); // Reset output file + } + + // if client has called Play() again, then we need to set the repeats so that the loop play can go on. + // this must be done only when all the repeats are done and when the play back has stopped after the repeats + // if client issues a play after the loop play, we must retain the repeat count and start the loop play again. + // if play was called during loop play it must be ignored and loop play should continue + if ((!iLoopPlayEnabled) && ((iRepeatCount > 0) || (iRepeatCount == KMdaRepeatForever))) + { + if ((iTrailingSilenceTimer) && (iTrailingSilenceTimer->IsActive())) + iTrailingSilenceTimer->Cancel(); + DoSetRepeats(iRepeatCount, TTimeIntervalMicroSeconds(iTrailingSilenceMs.Int())); + } + TInt errExIntent = KErrNone; + if (!iDisableAutoIntent && (iRequestState == EPlaying)) + { + if (iIntentStopped) + { + errExIntent = iDataSourceAdapter->EvaluateIntent(ContentAccess::EPlay); + if (errExIntent == KErrNone) + { + errExIntent = iDataSourceAdapter->ExecuteIntent(ContentAccess::EPlay); + DP2(_L("CAdvancedAudioPlayController::DoPlayL() iIntentStopped[%d] errExIntent[%d]"), iIntentStopped, errExIntent ); + if (errExIntent == KErrNone) + iIntentStopped = EFalse; + } + else // if ((errExIntent == KErrKErrNoRights) || any other error) + { + DoStopL(errExIntent); + return; + } + } + else + { + errExIntent = iDataSourceAdapter->EvaluateIntent((iState == EPaused) ? ContentAccess::EContinue : ContentAccess::EPlay); + if (errExIntent == KErrNone) + { + errExIntent = iDataSourceAdapter->ExecuteIntent((iState == EPaused) ? ContentAccess::EContinue : ContentAccess::EPlay); + DP2(_L("CAdvancedAudioPlayController::DoPlayL() iIntentStopped[%d] errExIntent[%d]"), iIntentStopped, errExIntent ); + } + else // if ((errExIntent == KErrKErrNoRights) || any other error) + { + DoStopL(errExIntent); + return; + } + } + } + if ((errExIntent != KErrNone) && (errExIntent != KErrNotSupported)) + { + DP1(_L("CAdvancedAudioPlayController::DoPlayL() errExIntent[%d]"), errExIntent); + // const TInt KErrCANoRights = -17452; from caferr.h + // don't leave because, stopping the source above will delete this active object.. + // the active scheduler will try to call runerror on the active object, but it's gone. + //User::Leave(errExIntent); + return; + } + iState = EPlaying; // stay in initialized state so bufferfilled will continue to call doplay + iResumePosition = -1; + iAudioOutput->PlayL(&iSharedBuffers, iSharedBufferIndex); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPlaying, KErrNone)); + } + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::PauseL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::PauseL() + { // this is for user call only - this changes the request state + DP2(_L("CAdvancedAudioPlayController::PauseL this[%x] iState[%d]"), this, iState); + if (iRequestState == EPaused) + { + return; + } + iRequestState = EPaused; + + switch (iState) + { + case EStopped: + case EInitializing: + case EInitialized: + break; + case EAutoPaused: + case EPlaying: // pause even if we are playing for seeking so state change will occur + DoPauseL(); // the pause will set position correctly + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPaused, KErrNone)); + break; + case EPaused: + break; + default: + Panic(EBadState); + break; + } +// SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPaused, KErrNone)); + } + +void CAdvancedAudioPlayController::DoPauseL(TBool aPreemption) + { + DP5(_L("CAdvancedAudioPlayController::DoPauseL pfsp[%d] pfip[%d] pfps[%d] pfd[%d] rqststate[%d]"), + iPausingForSetPos, iPlayingForInitPos, iPlayingForPauseSeek, iPlayingForDuration, iRequestState); +// if ((iPausingForSetPos || iPlayingForInitPos || iPlayingForPauseSeek || iPlayingForDuration) && +// (iRequestState != EPaused)) // seek position reached and returning to pause state automatically + // both user pause and internal pause come here. + // Internal pause may be from preemption or seek position reached. + /* + * Pre-emption case: Pause during a pre-emption and not seeking + */ + if ((iPlayingForInitPos || iPlayingForPauseSeek || iPausingForSetPos) && aPreemption) + {// we got preempted during a seek + // we're already seeking to a position. When we get there we'll come here again, but handle it below + DP0(_L("CAdvancedAudioPlayController::DoPauseL got a preemption during seek")); + return; + } + + /* + * Internal Pause case: Pausing during an internal seeking and not pre-emption case. + */ + if ((iPlayingForInitPos || iPlayingForPauseSeek || iPausingForSetPos) && !aPreemption) + { // already seeking to position, so we don't want to set position again + DP0(_L("CAdvancedAudioPlayController::DoPauseL not setting pos because we are already seeking")); + if (IsLoopPlayEnabled()) + { + DP0(_L("CAdvancedAudioPlayController::DoPauseL AudioOutput->StopL(EFalse)")); + // If in LoopPlay mode, DO NOT STOP the Devsound from AudioOutput + iAudioOutput->StopL(EFalse); + } + else + { + // If in normal Play mode, Devsound can be stopped during Pause + // StopL method takes the default value as ETrue, so we don't need to pass any value. + + // if we are seeking, we need to flush the devsound buffers regardless of loop play + DP0(_L("CAdvancedAudioPlayController::DoPauseL AudioOutput->StopL()")); + iAudioOutput->StopL(); + } + if (iPlayingForInitPos) + { + iState = EInitialized; + } + else + { + iState = EPaused; + } + iPlayingForPauseSeek = EFalse; + iPlayingForInitPos = EFalse; + // iPausingForSetPos is cleared when seekposition reached + // iPlayingForDuration is cleared in stop which is where seekposreached sends it + TInt foundPosition; + TInt foundTimeMs; + TUint time = iTimePositionInMicroSecs/1000; // time was set at the seek so it would be correct after seek. + DP1(_L("CAdvancedAudioPlayController::DoPauseL iTimePositionInMicroSecs[%u]"),iTimePositionInMicroSecs); + // this set position is short version because we expect that we have the buffer available after the seek. + // since we are not setting position as below, we can get the buffer ready for play. + TInt err = SetPositionInSharedBuffers(time, foundPosition, foundTimeMs); + iCurrentPosition = foundPosition; + iTimePositionInMicroSecs = foundTimeMs; // just to get exact to position + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::DoPauseL iCurrentPosition[%d]"),iCurrentPosition); + DP1(_L("CAdvancedAudioPlayController::DoPauseL iTimePositionInMicroSecs[%u]"),foundTimeMs); + // setpositioninsharedbuffers can reposition, so we need to update the ref + iAudioUtility->SetSourceReference(foundTimeMs, iCurrentPosition-iHeaderOffset-iSyncOffset); + + RefillPreceedingBuffersL(); + + return; + } + + /* + * User Pause case: User Pause when TruePause is not supported by DevSound adaptation (not seeking and not pre-emption) + */ + // if user interrupts pause, don't mess with the time since it was already set at the seek + // and the position will end up getting set to that time below. + if (iPlayingForInitPos || iPlayingForPauseSeek) + { + DP2(_L("CAdvancedAudioPlayController::DoPauseL UserPause pfip[%d] pfps[%d]"),iPlayingForInitPos, iPlayingForPauseSeek); + iPlayingForPauseSeek = EFalse; + iPlayingForInitPos = EFalse; + } + else + { + DP1(_L("CAdvancedAudioPlayController::DoPauseL UserPause before calc iTimePositionInMicroSecs [%d]"), iTimePositionInMicroSecs); + // during loop play devsound returns the incremented value of the position + // it must be adjusted to the duration of the audio clip so that playback can resume from the position where it was paused + if (IsLoopPlayEnabled()) + { + iTimePositionInMicroSecs += (iAudioOutput->CalculateAudioOutputPositionL() - iSavedTimePositionInMicroSecs); + } + else + { + iTimePositionInMicroSecs += iAudioOutput->CalculateAudioOutputPositionL(); + } + DP1(_L("CAdvancedAudioPlayController::DoPauseL UserPause after calc iTimePositionInMicroSecs [%d]"), iTimePositionInMicroSecs); + } + + // in case of client pause, stop the trailing silence timer during loop play and stay in paused state. + // controller goes to play state when client calls play + if ((iTrailingSilenceTimer) && (iTrailingSilenceTimer->IsActive())) + { + DP0(_L("CAdvancedAudioPlayController::DoPauseL UserPause Cancelling the TrailingSilenceTimer for User Pause ")); + iTrailingSilenceTimer->Cancel(); + } + + TInt errExIntent = KErrNone; + if (!iDisableAutoIntent) + { + errExIntent = iDataSourceAdapter->EvaluateIntent(ContentAccess::EPause); + if (errExIntent == KErrNone) + { + errExIntent = iDataSourceAdapter->ExecuteIntent(ContentAccess::EPause); + } + } + + if (IsLoopPlayEnabled()) + { + DP0(_L("CAdvancedAudioPlayController::DoPauseL UserPause AudioOutput->StopL(EFalse)")); + // if in LoopPlay mode, DO NOT STOP the Devsound during a pause operation since we resume the playback from the paused position + iAudioOutput->StopL(EFalse); + } + else + { + // If in normal Play mode, Devsound can be stopped during Pause + // StopL method takes the default value as ETrue, so we don't need to pass any value. + DP0(_L("CAdvancedAudioPlayController::DoPauseL UserPause AudioOutput->StopL()")); + iAudioOutput->StopL(); + } + iState = EPaused; + DoSetPositionL(TTimeIntervalMicroSeconds(iTimePositionInMicroSecs)); // if we were play seeking, this will reseek to desired position + + if ((errExIntent != KErrNone) && (errExIntent != KErrNotSupported)) + { + // User::Leave(errExIntent); + return; + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::StopL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::StopL() + { // this is for user call only - this changes the request state + DP2(_L("CAdvancedAudioPlayController::StopL this[%x] iState[%d]"), this, iState); + if (iRequestState == EStopped) + { + return; + } + iRequestState = EStopped; + switch (iState) + { + case EStopped: + break; + case EInitializing: + case EInitialized: + case EPlaying: + case EPaused: + case EAutoPaused: + TRAP_IGNORE(DoStopL(KErrNone)); // ignore if any leave to ensure internal state change on the next statement + break; + default: + Panic(EBadState); + break; + } + // user event + + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedStopped, KErrNone)); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::DoStopL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::DoStopL(TInt aError) + { + DP3(_L("CAdvancedAudioPlayController::DoStopL this[%x] iState[%d], aError[%d]"), this, iState, aError); + DP0(_L("CAdvancedAudioPlayController::DoStopL reseting iInitPosition iReadHeader")); + CleanupForStop(); + // If the playwindow is still active (iPlayWindowEndPosition > 0) + // then set the current playback position to play window start position + // If there is a client Stop() call or End of playback event then we need to check for the + // playwindow start position. + if (iPlayWindowEndPosition > 0) + { + iInitPosition = iPlayWindowStartPosition; + } + else + { + iInitPosition = -1; + } + if (iTrailingSilenceTimer) + { + delete iTrailingSilenceTimer; + iTrailingSilenceTimer = NULL; + } + iReadHeader = ETrue; + iEnablePrimedStateChangedEvent = EFalse; + iSharedBufferCnt = 0; + if (iAudioOutput) + { + // during loop play devsound returns the incremented value of the position + // it must be adjusted to the duration of the audio clip so that playback can resume from the position where it was paused + if (IsLoopPlayEnabled()) + { + DP1(_L("CAdvancedAudioPlayController::DoStopL() Number of times repeated = %d"), iCurrentRepeatCount); + iTimePositionInMicroSecs += (iAudioOutput->CalculateAudioOutputPositionL() - iSavedTimePositionInMicroSecs); + } + else + { + iTimePositionInMicroSecs += iAudioOutput->CalculateAudioOutputPositionL(); + } + // clear the repeat flag + ClearRepeatFlag(); + + // In order to support adaptation implementation of loop play, we cannot stop + // the output at the end of file. +// if (aError == KErrUnderflow) +// { +// TRAP_IGNORE(iAudioOutput->StopL(EFalse)); +// } +// else +// { + TRAP_IGNORE(iAudioOutput->StopL()); +// } + TRAP_IGNORE(SourceSinkStopL()); + } + + DP0(_L("CAdvancedAudioPlayController::DoStopL state changed to stop")); + iState = EStopped; + + if (!iPlayingForInitPos) // internal play for seeking before user calls play + { + // we don't want an event if seeking past eof before play is called - we will get a playcomplete and stop + // no event for a user stop which will have no error + if (aError == KErrUnderflow) + { + // normal end of playback + SendEventToClient(TMMFEvent(KMMFEventCategoryPlaybackComplete, KErrNone)); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedStopped, KErrEof)); + } + else if (aError != KErrNone) + { + // processing had an error + if (iSourceUnreadable) + { + // if source was not even readable, we never made it to user play + // so we don't want to send playback complete + } + else + { + SendEventToClient(TMMFEvent(KMMFEventCategoryPlaybackComplete, aError)); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedStopped, aError)); + } + } + } + } + +TInt CAdvancedAudioPlayController::CleanupForStop() + { + // This is used in cases that need to initial back to a stop like state without a state change + DP1(_L("CAdvancedAudioPlayController::CleanupForStop iState[%d]"), iState); + TInt status = KErrNone; + + iPlaySeeking = EFalse; + + if (iWait) + { + iWait->AsyncStop(); + } + + if (iBlockSetPos) + { + if (iBlockSetPos->IsStarted()) + { + iBlockSetPos->AsyncStop(); + } + } + + iPlayingForDuration = EFalse; + iBlockDuration = EFalse; + iPlayingForPauseSeek = EFalse; + iPlayingForInitPos = EFalse; + iPausingForSetPos = EFalse; + return status; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::ResetPositionVariables() +// ----------------------------------------------------------------------------- +// +// These variables are always updated together, they are reset together here. +void CAdvancedAudioPlayController::ResetPositionVariables() + { + iSourceReadPosition = 0; + iCurrentPosition = 0; + iTimePositionInMicroSecs = 0; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::PositionL() +// ----------------------------------------------------------------------------- +// +EXPORT_C TTimeIntervalMicroSeconds CAdvancedAudioPlayController::PositionL() const + { + DP2(_L("CAdvancedAudioPlayController::PositionL, this[%x] iState[%d]"), this, iState); + + TTimeIntervalMicroSeconds positionMicroSeconds(0); + + if (iState == EPlaying) + { + DP1 (_L("CAdvancedAudioPlayController::PositionL iTimePositionInMicroSecs [%d] msec"), iTimePositionInMicroSecs); + // adjust the position here since devsound returns the incremented postion value during loopplay + if (IsLoopPlayEnabled()) + { + positionMicroSeconds = TTimeIntervalMicroSeconds( iAudioOutput->CalculateAudioOutputPositionL() - iSavedTimePositionInMicroSecs); + } + else + { + positionMicroSeconds = TTimeIntervalMicroSeconds(iTimePositionInMicroSecs + iAudioOutput->CalculateAudioOutputPositionL()); + } + } + else + { + positionMicroSeconds = TTimeIntervalMicroSeconds(iTimePositionInMicroSecs); + } + + DP1 (_L("CAdvancedAudioPlayController::PositionL returns[%d] msec"), I64LOW(positionMicroSeconds.Int64()/1000)); + return positionMicroSeconds; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SetPositionL +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::SetPositionL( + const TTimeIntervalMicroSeconds& aTimePos) + { // this is for user call only + DP3(_L("CAdvancedAudioPlayController::SetPositionL this[%x] [%d]msec iState[%d]"), this, I64LOW(aTimePos.Int64()/1000), iState); + if (iSourceUnreadable) + { + DP0(_L("CAdvancedAudioPlayController::SetPositionL KErrCorrupt")); + User::Leave(KErrCorrupt); + } + + // if play window is set then validate the position with the play window boundaries + // play window boundaries must be checked when seeking during playback in a playwindow + TTimeIntervalMicroSeconds position = aTimePos; + // if play window is set then the playwindowendposition is > 0 + if (iPlayWindowEndPosition > 0) + { + if (aTimePos < iPlayWindowStartPosition) + { + position = iPlayWindowStartPosition; + } + else if (aTimePos > iPlayWindowEndPosition) + { + position = iPlayWindowEndPosition; + } + } + + switch (iState) + { + case EStopped: + case EInitializing: + if (position != 0) + { // if we are priming, we will already be ready to play from 0. + DP2(_L("CAdvancedAudioPlayController::SetPositionL, saving pos iReadHeader[%d] iState[%d]"),iReadHeader,iState); + iInitPosition = position; + } + DP0(_L("CAdvancedAudioPlayController::SetPositionL, can ignore")); + break; + case EInitialized: + case EPaused: + iSavedSetPosition = position; + DoSetPositionL(position); + if (iPlaySeeking) + { + DP0(_L("CAdvancedAudioController::SetPositionL() blocking")); + iBlockSetPos = new (ELeave) CActiveSchedulerWait(); + iBlockSetPos->Start(); + DP0(_L("CAdvancedAudioController::SetPositionL() continuing")); + delete iBlockSetPos; + iBlockSetPos = NULL; + } + break; + case EAutoPaused: + case EPlaying: + DP0(_L("CAdvancedAudioPlayController::SetPositionL, PauseForSetPosL so we can set position")); + PauseForSetPosL(); // will return to play state when position reached + iSavedSetPosition = position; + DP0(_L("CAdvancedAudioPlayController::SetPositionL, After PauseForSetPosL, now set the desired position")); + DoSetPositionL(position); + if (iPlaySeeking) + { + DP0(_L("CAdvancedAudioController::SetPositionL() blocking")); + iBlockSetPos = new (ELeave) CActiveSchedulerWait(); + iBlockSetPos->Start(); + DP0(_L("CAdvancedAudioController::SetPositionL() continuing")); + delete iBlockSetPos; + iBlockSetPos = NULL; + } + else + { + DP0(_L("CAdvancedAudioPlayController::SetPositionL Calling SeekPositionReached ")); + // comes here if seek position found in source, or starting another loop during loop play + // during loop play we seek back to the beginning of the clip or play window start position. + SeekPositionReached(KMaxTUint); + } + break; + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SetPrioritySettings +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::SetPrioritySettings( + const TMMFPrioritySettings& aPrioritySettings) + { + DP0(_L("CAdvancedAudioPlayController::SetPrioritySettings")); + + CAdvancedAudioController::SetPrioritySettings(aPrioritySettings); + + if(iDataSink) + { + TRAPD(err, iAudioOutput->SetPrioritySettingsL(aPrioritySettings)); + err = err; + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SendEventToClient +// ----------------------------------------------------------------------------- +// +EXPORT_C TInt CAdvancedAudioPlayController::SendEventToClient(const TMMFEvent& aEvent) + { + DP4(_L("CAdvancedAudioPlayController::SendEventToClient, this[%x] iEventsEnabled[%d], iEventType[%d], ErrorCode[%d]"), this, iEventsEnabled, aEvent.iEventType, aEvent.iErrorCode); + + // The following logic is to take care of only forward the appropiate event depends on "iEventsEanbled" flag + // KMMFEventCategoryPlaybackComplete event is to support the Existing Symbian MMF API behavior + // while KStreamControlEventStateChangedXXX for Streaming/PDL events, and ONLY ONE of these events should be forwarded to the client + // Current implementation is instead of deciding what event when a SendEventToClient issued, both events would be sent, and the decision + // on which one to be forwarded is being implemented here + // *NOTE*: KStreamControlEventStateChangedStopped does NOT equal to KMMFEventCategoryPlaybackComplete, + // while both would happends during error situation, KStreamControlEventStateChangedStopped also happens when StopL is called + // but KMMFEventCategoryPlaybackComplete would not + if( (aEvent.iErrorCode == KErrSourceAdapter) || ( aEvent.iErrorCode == KErrCorrupt ) ) + {//Stop the controller and send error since error occured in source adapter + iSourceUnreadable = ETrue; + DoStopL(KErrDied); + } + else + { + if (iEventsEnabled) + { // request for new events + if ((aEvent.iEventType == KStreamControlEventStateChangedStopped) || + (aEvent.iEventType == KStreamControlEventStateChangedPrimed) || + (aEvent.iEventType == KStreamControlEventStateChangedPlaying) || + (aEvent.iEventType == KStreamControlEventStateChangedPaused) || + (aEvent.iEventType == KStreamControlEventStateChangedAutoPaused)) + { + return CAdvancedAudioController::SendEventToClient(aEvent); + } + else + { + // do not send any other events for advanced clients. + } + } + else + { + if ((aEvent.iEventType == KStreamControlEventStateChangedStopped) || + (aEvent.iEventType == KStreamControlEventStateChangedPrimed) || + (aEvent.iEventType == KStreamControlEventStateChangedPlaying) || + (aEvent.iEventType == KStreamControlEventStateChangedPaused) || + (aEvent.iEventType == KStreamControlEventStateChangedAutoPaused)) + { + // ignore event and do nothing except convert autopause to old event + } + else + { + return CAdvancedAudioController::SendEventToClient(aEvent); + } + } + } + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::BufferFilledL +// Called after the data buffer is filled. +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::BufferFilledL(CMMFBuffer* aBuffer) + { + aBuffer->SetPosition(0); + CMMFDataBuffer* buffer = static_cast(aBuffer); + + DP6(_L("CAdvancedAudioPlayController::BufferFilledL[%x], readPos[%d], Length[%d], iState[%d], LB[%d], d0[%x]"), + buffer->Data().Ptr(), iSourceReadPosition, buffer->Data().Length(), iState, aBuffer->LastBuffer(), + buffer->Data().Ptr()[0]); + + if ((buffer->Data().Length() > 0) || buffer->LastBuffer()) + { + aBuffer->SetStatus(EFull); + + // iSourceReadPosition is used to mark the buffer with the source position + // This is stored in aBuffer->Frame(). aBuffer->Frame() will indicate + // the source position for the starting byte position for that buffer + aBuffer->SetFrameNumber(iSourceReadPosition); + iSourceReadPosition += buffer->Data().Length(); + + DP1(_L("CAdvancedAudioPlayController::BufferFilledL next readPos[%d]"), iSourceReadPosition); + } + else + { + DP0(_L("CAdvancedAudioPlayController::BufferFilledL, unexpected 0 length buffer")); + User::Leave(KErrAbort); + } + + if (iState != EStopped) + { + DoBufferFilledL(aBuffer); + } + + TBool doState = ETrue; + while (doState) + { + DP5(_L("CAdvancedAudioPlayController::BufferFilledL, state check state[%d] reqstate[%d] pfps[%d] pfd[%d] pfip[%d]"), + iState, iRequestState, iPlayingForPauseSeek,iPlayingForDuration,iPlayingForInitPos); + doState = EFalse; + switch (iState) + { + case EStopped: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EStopped")); + break; + case EInitializing: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EInitializing")); + if (!iReadHeader) + { + // do not transition to initialized if there was a problem + if (iSourceUnreadable) + { + return; + } + iState = EInitialized; + // when playwindow is active for a non-seekable source during loop play + // we must seek to the playwindow start position and then start the playback + if (iPlayWindowStartPosition > 0) // do we need additional checks as loop play / non-seekable source ?? + iInitPosition = iPlayWindowStartPosition; + GoToInitPositionL(); // see if we need to seek to somewhere + doState = ETrue; + } + break; + case EInitialized: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EInitialized")); + if ((iRequestState == EInitialized) && AllBuffersFilled() && iEnablePrimedStateChangedEvent) + { // we have read the header, user has called prime but needed to wait for priming, all the input buffers are filled + // the controller itself may need to prime to get duration, but we just want to send the state change in response to user prime + iEnablePrimedStateChangedEvent = EFalse; + DP0(_L("CAdvancedAudioPlayController::BufferFilledL state changed primed")); + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPrimed, KErrNone)); // both source and sink are ready. + } + if ((iRequestState == EPlaying) || iPlayingForDuration || iPlayingForPauseSeek || iPlayingForInitPos) + { + if (iAudioOutput->IsDSStopped()) + { + DP0(_L("CAdvancedAudioPlayController::BufferFilledL calling DoPlayL Devsound is stopped iState=EInitialized")); + DoPlayL(); + } + else + { + // This is needed for non-seekable sources as DoRepeat() calls DoInitialize in this case + // this resets the source to read from 0 and sets the iState to EInitializing. + // BufferFilled will not read the header again, change state to EInitialized and seek to the iInitPosition. + // The next BufferFilled will come here, where we will continue playback from byte position 0 + // that is now in the buffers. + DP0(_L("CAdvancedAudioPlayController::BufferFilledL Resuming the playback iState=EInitialized")); + // we need to be able to seek fwd to playwindow start pos + if (IsLoopPlayEnabled()) + { + DoResume(iCurrentPosition); + } + else + { + DoPlayL(); + } + + } + } + break; + case EPlaying: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EPlaying")); + break; + case EPaused: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EPaused")); + // iPlayingForPauseSeek would be set if we are in pause mode and we need to seek. + // the controller will not go to play state until all buffers are filled. + // so we need to call DoPlay until all buffers are filled and transition to play state. + // if PauseForSetPos, we will resume play in seekpositionreached. + // if (((iRequestState == EPlaying) || iPlayingForPauseSeek) && (!iPausingForSetPos) ) Suk + if ((iRequestState == EPlaying) || iPlayingForPauseSeek) + { + if (iAudioOutput->IsDSStopped()) + { // during loop play the output is not stopped unless we did a seek - seek stops the output and we need to call DoPlay + DP0(_L("CAdvancedAudioPlayController::BufferFilledL Calling DoPlay Devsound is stopped iState=EPaused")); + DoPlayL(); + } + else + { + DP1(_L("CAdvancedAudioPlayController::BufferFilledL Calling DoResume iState=EPaused iCurrentPosition[%d]"), iCurrentPosition); + if (IsLoopPlayEnabled()) + { + DoResume(iCurrentPosition); + } + else + { + DP0(_L("CAdvancedAudioPlayController::BufferFilledL Calling DoPlay iState=EPaused")); + DoPlayL(); + } + } + } + break; + case EAutoPaused: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EAutoPaused")); + DoResume(); + break; + default: + DP0(_L("CAdvancedAudioPlayController::BufferFilledL EBadState")); + Panic(EBadState); + break; + } + } + } + +void CAdvancedAudioPlayController::DoResume(TInt aResumePosition) // defaults to -1 + { + DP3(_L("CAdvancedAudioPlayController::DoResume iState[%d] iRequestState[%d] aResumePosition[%d]"), iState, iRequestState, aResumePosition); + TInt bufferIndex = -1; + if (aResumePosition >= 0) + { + iResumePosition = aResumePosition; + DP2(_L("CAdvancedAudioPlayController::DoResume saving resume position [%d] iState[%d]"), iResumePosition, iState); + } + + if (iCurrentPosition == KMaxTUint) + { + DP0(_L("CAdvancedAudioPlayController::DoResume Resetting iCurrentPosition to zero")); + iCurrentPosition = 0; + } + + if (AllBuffersFilled()) // state entry condition + { + if (iResumePosition >= 0) + { + bufferIndex = FindBufferFromPos(iResumePosition); + DP1(_L("CAdvancedAudioPlayController::DoResume bufferIndex [%d]"), bufferIndex); + iResumePosition = -1; + DP0(_L("CAdvancedAudioPlayController::DoResume Resetting the ResumePosition")); + // if there was seek past end of file, the buffers may not have any data + // they may all be empty last buffers + if ((bufferIndex < 0) && (AllBuffersEmpty())) + { // position in buffer buffer not found + DP0(_L("CAdvancedAudioPlayController::DoResume Position in the buffer not found ")); + DoStopL(KErrNone); + return; + } + if (bufferIndex < 0) + { + DP0(_L("CAdvancedAudioPlayController::DoResume bufferindex < 0")); + DoStopL(KErrGeneral); + return; + } + } + + TInt errExIntent = KErrNone; + if (!iDisableAutoIntent && (iRequestState == EPlaying)) + { + if (iIntentStopped) + { + errExIntent = iDataSourceAdapter->EvaluateIntent(ContentAccess::EPlay); + if (errExIntent == KErrNone) + { + errExIntent = iDataSourceAdapter->ExecuteIntent(ContentAccess::EPlay); + DP2(_L("CAdvancedAudioPlayController::DoResume() iIntentStopped[%d] errExIntent[%d]"), iIntentStopped, errExIntent ); + if (errExIntent == KErrNone) + iIntentStopped = EFalse; + } + else // if ((errExIntent == KErrKErrNoRights) || any other error) + { + DoStopL(errExIntent); + return; + } + } + else + { + errExIntent = iDataSourceAdapter->EvaluateIntent((iState == EPaused) ? ContentAccess::EContinue : ContentAccess::EPlay); + if (errExIntent == KErrNone) + { + errExIntent = iDataSourceAdapter->ExecuteIntent((iState == EPaused) ? ContentAccess::EContinue : ContentAccess::EPlay); + DP2(_L("CAdvancedAudioPlayController::DoResume() iIntentStopped[%d] errExIntent[%d]"), iIntentStopped, errExIntent ); + } + else // if ((errExIntent == KErrKErrNoRights) || any other error) + { + DoStopL(errExIntent); + return; + } + } + } + if ((errExIntent != KErrNone) && (errExIntent != KErrNotSupported)) + { + DP1(_L("CAdvancedAudioPlayController::DoResume() errExIntent[%d]"), errExIntent); + return; + } + + DP1(_L("CAdvancedAudioPlayController::DoResume iAudioOutput->Resume with bufferIndex [%d]"), bufferIndex); + iState = EPlaying; + // iRequestState should not be changed since it represents user request. + iAudioOutput->Resume(bufferIndex); + + if ((!iRepeatForever) && (iCurrentRepeatCount == iRepeatCount)) + { // this is the last play of the repeats so tell the AudioOutput to set the lastbuffer to True + DP1(_L("CAdvancedAudioPlayController::DoResume iCurrentRepeatCount[%d]"), iCurrentRepeatCount); + iAudioOutput->UnSetLastBuffer(EFalse); + } + SendEventToClient(TMMFEvent(KStreamControlEventStateChangedPlaying, KErrNone)); + } + } + + +TBool CAdvancedAudioPlayController::AllBuffersFilled() + { + DP0(_L("CAdvancedAudioPlayController::AllBuffersFilled")); + TBool allFilled = ETrue; + for (TInt i = 0; i < iSharedBufferMaxNum; i++) + { + if (iSharedBuffers[i]->LastBuffer() && iSharedBuffers[i]->Status() == EFull) + { // if one buffer is filled and marked last buffer + allFilled = ETrue; + break; + } + else if (iSharedBuffers[i]->Status() == EBeingFilled) + { + allFilled = EFalse; + } + } + DP1(_L("CAdvancedAudioPlayController::AllBuffersFilled [%d]"),allFilled); + return allFilled; + } + +TBool CAdvancedAudioPlayController::AllBuffersEmpty() + { + DP0(_L("CAdvancedAudioPlayController::AllBuffersEmpty")); + TBool allEmpty = ETrue; + for (TInt i = 0; i < iSharedBufferMaxNum; i++) + { + DP3(_L("CAdvancedAudioPlayController::AllBuffersEmpty indx[%d] len[%d] stat[%d]"), i, iSharedBuffers[i]->BufferSize(), iSharedBuffers[i]->Status()); + if ((iSharedBuffers[i]->BufferSize() != 0) && + (iSharedBuffers[i]->Status() == EFull)) + { + allEmpty = EFalse; + break; + } + } + DP1(_L("CAdvancedAudioPlayController::AllBuffersEmpty [%d]"), allEmpty); + return allEmpty; + } + + +void CAdvancedAudioPlayController::DoBufferFilledL(CMMFBuffer* aBuffer) + { + DP0(_L("CAdvancedAudioPlayController::DoBufferFilledL")); + + CMMFDataBuffer* buffer = static_cast(aBuffer); + if (iReadHeader) + { + TRAPD(err, DoReadHeaderL(buffer)); + DP2(_L("CAdvancedAudioPlayController::DoBufferFilledL header is read iHeaderOffset[%d] iSyncOffset[%d]"), iHeaderOffset, iSyncOffset); + DP1(_L("CAdvancedAudioPlayController::DoBufferFilledL DoReadHeaderL err[%d]"), err); + + if (((err == KErrNotReady) || (err == KErrNotFound) || (err == KErrCompletion && iSourceIsPosSeekable)) && !aBuffer->LastBuffer()) + { + // this shared buffer does not contain any part of the first frame + DP0(_L("CAdvancedAudioPlayController::DoBufferFilledL iInitPosition = 0 to seek back to start when done")); + iInitPosition = 0; // if we are going to throw away these buffers, we need to indicate we need to seek back to the start + RefillBuffer(aBuffer); + } + else + { + if ( err == KErrCompletion ) + { + if(((iSharedBufferCnt+1) < iSharedBufferMaxNum) && !aBuffer->LastBuffer()) + { + iSharedBufferCnt++; + return; + } + else + { + err = KErrNone; + } + } + + iReadHeader = EFalse; + if (err != KErrNone) + { + if (((err == KErrNotReady) || (err == KErrNotFound)) && (aBuffer->LastBuffer())) + { + // we went through the whole file without finding the header + // so the source is corrupted + DP0(_L("CAdvancedAudioPlayController::DoBufferFilledL KErrCorrupt")); + err = KErrCorrupt; + iSourceUnreadable = ETrue; + } + TRAP_IGNORE(DoStopL(err)); + } + else + { + if (iAudioUtility) + { // 3gp won't have a utility until it reads the header info + TInt sourceSize = iDataSourceAdapter->SourceSize(); + if (sourceSize < 0) + { + sourceSize = 0; + } + TRAP_IGNORE(iAudioUtility->SetClipSizeL(sourceSize)); + } + + iCurrentPosition = iHeaderOffset+iSyncOffset; + DP3(_L("CAdvancedAudioPlayController::DoBufferFilledL header read curpos[%d] hdroff[%d] syncoff[%d]"), + iCurrentPosition, iHeaderOffset, iSyncOffset); + iTimePositionInMicroSecs = 0; + if (iAudioUtility) + { + iAudioUtility->SetSourceReference(0,0); + } + + UpdateDuration(); + UpdateBitRate(); + // we need to unblock any blocked duration call and also disable it from blocking + // since we now have duration and bitrate. + iBlockDuration = EFalse; + if (iWait) + { + iWait->AsyncStop(); // unblock Duration() now that duration is calculated + } + + // Sink may not be provided yet. Client might add source first. + // Or a client trying to get duration may not have provided a sink. + // Without a sink, we'll just return here, but know that we have read enough + // source data to configure the sink once it is added. + // But if the sink is available here, we'll initialize below. + iSinkInitDataReady = ETrue; + // Primed state change event would be sent when both source and sink are ready. + if (!iAudioOutput) + { +// iBlockDuration = EFalse; +// if (iWait) +// { +// iWait->AsyncStop(); // unblock Duration() now that duration is calculated +// } + return; + } + DoInitializeSinkL(); // the decoder will be created here + } + } + } + } + +EXPORT_C void CAdvancedAudioPlayController::DoInitializeSinkL() + { + DP0(_L("CAdvancedAudioPlayController::DoInitializeSinkL")); + iSinkInitDataReady = EFalse; + + //both source and sink have been added + if(iAudioOutput && iDataSourceAdapter) + { + iAudioOutput->SetDataSourceAdapter(iDataSourceAdapter); + } + + if (!iDecoderExists) + { + // move this common code to here from doadddatasink's + CAdvancedAudioDecoder* decoder = BuildDecoderL(); + iDecoderExists = ETrue; + // AudioOutput takes ownership of decoder object + iAudioOutput->SetDecoder(decoder); + iAudioUtility->SetDecoder(*decoder); + iAudioUtility->SetObserver(*this); + decoder->SetDecoderUtilityObserver(*iAudioUtility); + + // Leave if HWCodec and Conversion tried + if(decoder->IsHwAccelerated() && iDataSink->DataSinkType() == KUidMmfFileSink) + { + User::Leave(KErrNotSupported); + } + } + + + + // Read the default codec configuration parameters from resource file + RArray& codecConfigData = const_cast&>(iAudioResource->CodecConfigParametersL()); + // Override default values with values found from header, if available + GetCodecConfigData(codecConfigData); + iAudioOutput->ConfigureL(iSampleRate, iSinkNumChannels, iDataType, codecConfigData); + DP0(_L("CAdvancedAudioPlayController::DoInitializeSinkL, output configured")); + iAudioOutput->PrimeL(); + DP0(_L("CAdvancedAudioPlayController::DoInitializeSinkL, output primed")); + + // we would use this code when we have a NULL sink +/* if (iDuration > 0) + { + DP0(_L("CAdvancedAudioPlayController::BufferFilledL, unblocking duration")); + iBlockDuration = EFalse; + } + if (iWait) + { + if (iDuration > 0) + { + DP0(_L("CAdvancedAudioPlayController::DoBufferFilledL() unblocking")); + iWait->AsyncStop(); // unblock DoAddDataSource now that duration is calculated + } + else + { + PlayForDurationL(); // this should only be used if we are given a NULL sink + } // because policy will get involved with an audio output sink + } // We can skip using this even for NULL sink if we just continue to +*/ + } // stay in read header mode and calulate bitrate on more buffers + +void CAdvancedAudioPlayController::PlayForDurationL() + { + DP0(_L("CAdvancedAudioPlayController::PlayForDurationL()")); + iPlayingForDuration = ETrue; + DoSetPositionL(1000000); // 1 sec + DoPlayL(); + } + +void CAdvancedAudioPlayController::PlayForInitPositionL() + { + DP0(_L("CAdvancedAudioPlayController::PlayForInitPositionL()")); + iPlayingForInitPos = ETrue; + DoPlayL(); + } + +void CAdvancedAudioPlayController::PlayForPauseSeekL() + { + DP1(_L("CAdvancedAudioPlayController::PlayForPauseSeekL() pfsp[%d]"), iPausingForSetPos); + if (!iPausingForSetPos) + { // used to return the state to pause + iPlayingForPauseSeek = ETrue; + } + DoPlayL(); + } + +void CAdvancedAudioPlayController::PauseForSetPosL() + { + DP0(_L("CAdvancedAudioPlayController::PauseForSetPosL()")); + iPausingForSetPos = ETrue; // so that pause won't set position too. + DoPauseL(); // stops the output for reposition - it also repositions at pause point using dosetposition + } + +TInt CAdvancedAudioPlayController::DoSetRepeats(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence) + { + DP2(_L("CAdvancedAudioPlayController::SetRepeats RepeatCount [%d] TrailingSilencePeriod [%d]"), aRepeatNumberOfTimes, I64LOW(aTrailingSilence.Int64()/1000)); + if ((aRepeatNumberOfTimes != KMdaRepeatForever) && (aRepeatNumberOfTimes <= 0)) + { + return KErrArgument; + } + if (aTrailingSilence < 0) + { + return KErrArgument; + } + + if (aRepeatNumberOfTimes == KMdaRepeatForever) // -2 + { + iRepeatForever = ETrue; + } + else + { + iRepeatForever = EFalse; + } + iRepeatCount = aRepeatNumberOfTimes; + iLoopPlayEnabled = ETrue; + if (aTrailingSilence.Int64() > KMaxTInt) + { + iTrailingSilenceMs = TTimeIntervalMicroSeconds32(KMaxTInt); + } + else + { + iTrailingSilenceMs = TTimeIntervalMicroSeconds32(aTrailingSilence.Int64()); + } + if (iAudioOutput) + { + iAudioOutput->IsLoopPlayEnabled(iLoopPlayEnabled); + iAudioOutput->UnSetLastBuffer(ETrue); + } + if (iTrailingSilenceTimer) + { + delete iTrailingSilenceTimer; + iTrailingSilenceTimer = NULL; + } + iTrailingSilenceTimer = CTrailingSilenceTimer::NewL(*this); + // if MapcSetRepeats is called twice or more from the client side, + // then the loop play re-starts and the latest repeat count is considered for + // loop play operation. Reset the counter and the samplesplayed value + iCurrentRepeatCount = 0; + iSavedTimePositionInMicroSecs = 0; + return KErrNone; + } + +void CAdvancedAudioPlayController::ClearRepeatFlag() + { + DP0(_L("CAdvancedAudioPlayController::ClearRepeatFlag")); + iLoopPlayEnabled = EFalse; + if (iAudioOutput) + { + iAudioOutput->IsLoopPlayEnabled(iLoopPlayEnabled); + iAudioOutput->UnSetLastBuffer(EFalse); + } + } + +EXPORT_C void CAdvancedAudioPlayController::LastBufferSent() + { // this is the callback we will get during loop play, instead of playcomplete/dostop + DP2(_L("CAdvancedAudioPlayController::LastBufferSent Begin iRequestedState [%d] iState [%d]"), iRequestState, iState); + if (!iDisableAutoIntent) + { + TInt errExIntent = iDataSourceAdapter->ExecuteIntent(ContentAccess::EStop); + DP1(_L("CAdvancedAudioPlayController::LastBufferSent Calling ExecuteIntent Stop on DataSource [%d]"), errExIntent); + if (errExIntent == KErrNone) + { + iIntentStopped = ETrue; + } + } + if ((iRequestState == EPlaying) && (iState == EPlaying) && (IsLoopPlayEnabled())) // evaluate use of iState + { + // start timer and let it's RunL call repeat + if (iTrailingSilenceTimer) + { + DP1(_L("CAdvancedAudioPlayController::LastBufferSent Waiting for the iTrailingSilenceTimer for [%d] ms"), I64LOW(iTrailingSilenceMs.Int()/1000)); + iTrailingSilenceTimer->After(iTrailingSilenceMs); + } + // save the current position, this will be used to calculate the position in loop play when + // client calls for PositionL() or Pause() operations + // TODO: need to check this position when loop play is going on in a play window + // iSavedTimePositionInMicroSecs = iAudioOutput->CalculateAudioOutputPositionL(); + + /* if ((!iRepeatForever) && (iCurrentRepeatCount < iRepeatCount)) + { + iCurrentRepeatCount++; + } + DP1(_L("CAdvancedAudioPlayController::LastBufferSent() Number of times played till now = %d"), iCurrentRepeatCount); + */ + } + //else if ((iState == EPaused) && + /* + * This is needed for TruePause in a LoopPlay. + * When User Pauses after LastBufferSent and cancels the TrailingSilenceTimer, UserPlay should automatically kickback the DoRepeats + */ + /*#ifdef __TRUEPAUSE_SUPPORT__ + if (IsLoopPlayEnabled()) + { // repeat enabled and last buffer sent but userwe're paused + DP0(_L("CAdvancedAudioPlayController::LastBufferSent iNeedToResumeForRepeat ETrue")); + iNeedToResumeForRepeat = ETrue; // always set in case user pauses - only to be used when resuming from DoPlayL + } + #endif + */ + DP0(_L("CAdvancedAudioPlayController::LastBufferSent End")); + } + +void CAdvancedAudioPlayController::DoRepeat() + { + DP0(_L("CAdvancedAudioPlayController::DoRepeat Begin")); + // save the current position, this will be used to calculate the position in loop play when + // client calls for PositionL() or Pause() operations + // TODO: need to check this position when loop play is going on in a play window + iSavedTimePositionInMicroSecs = iAudioOutput->CalculateAudioOutputPositionL(); + DP1(_L("CAdvancedAudioPlayController::DoRepeat iSavedTimePositionInMicroSecs[%u]"), iSavedTimePositionInMicroSecs); + + if ((!iRepeatForever) && (iCurrentRepeatCount < iRepeatCount)) + { + iCurrentRepeatCount++; + } + DP1(_L("CAdvancedAudioPlayController::DoRepeat Number of times played till now = %d"), iCurrentRepeatCount); + + if (iSourceIsTimeSeekable || iSourceIsPosSeekable) + { // For seekable source + // if there is a playwindow set then use that, otherwise go back to 0 time + // if ((iPlayWindowStartPosition != 0) || (iPlayWindowEndPosition != 0)) + // SetPlayWindow(iPlayWindowStartPosition, iPlayWindowEndPosition); + if (iPlayWindowStartPosition > 0) + { + DP1(_L("CAdvancedAudioPlayController::DoRepeat SetPositionL[%d]ms"), I64LOW(iPlayWindowStartPosition.Int64()/1000)); + SetPositionL(iPlayWindowStartPosition); + } + else + { + DP0(_L("CAdvancedAudioPlayController::DoRepeat SetPositionL(0)")); + SetPositionL(0); + } + // Register for PlayWindow end position as the FrameTable has set its playwindowendpostime to zero + if (iPlayWindowEndPosition > 0) + { + DP1(_L("CAdvancedAudioPlayController::DoRepeat iAudioUtility->SetPlayWindowEndTimeMs(%d)"), I64LOW(iPlayWindowEndPosition.Int64()/1000)); + iAudioUtility->SetPlayWindowEndTimeMs(iPlayWindowEndPosition.Int64() / 1000); + } + } + else + { // For non-seekable source + // Stop and start playback + DP0(_L("CAdvancedAudioPlayController::DoRepeat Non-Seekable source.")); + iAudioOutput->StopL(EFalse); + iDataSourceAdapter->SourceStopL(); // clear the buffers in the source before seeking and priming it + DoInitializeL(); + // set the read header flag to true so that the header is read before the playback is resumed + // and the current position is adjusted to be placed after the header. + iReadHeader = ETrue; + } + DP0(_L("CAdvancedAudioPlayController::DoRepeat End") ); + } + +EXPORT_C void CAdvancedAudioPlayController::TrailingSilenceTimerComplete() + { + DP0(_L("CAdvancedAudioPlayController::TrailingSilenceTimerComplete ")); + DoRepeat(); + } + +EXPORT_C TInt CAdvancedAudioPlayController::GetCodecConfigData(RArray& aCodecConfigData) + { + TInt stat = KErrNone; + iAudioUtility->SetCodecConfigData(aCodecConfigData); + iSinkNumChannels = iAudioUtility->ChannelsOut(); + return stat; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::Refillbuffer +// +// Called after the data buffer is filled. Update the number of bytes read +// and the current read position for the next read operation. +// ----------------------------------------------------------------------------- +// +EXPORT_C TInt CAdvancedAudioPlayController::RefillBuffer(CMMFBuffer* refillBuffer) + { + DP1(_L("CAdvancedAudioPlayController::RefillBuffer: [%x]"), + static_cast(refillBuffer)->Data().Ptr() ); + + // prevent further request - is this really neccessary + TBool lastBufferSet = EFalse; + + for (TInt i = 0; i < iSharedBuffers.Count(); i++) + { + if (iSharedBuffers[i]->LastBuffer() && + (iSharedBuffers[i]->Status() != EBeingFilled)) // need to make sure it is not still held by the source + { + lastBufferSet = ETrue; + break; + } + } + + if (lastBufferSet) + { + // invalidate this buffer + DP1(_L("CAdvancedAudioPlayController::RefillBuffer: marking[%x] EUnavailable and clearing its LB"), + static_cast(refillBuffer)->Data().Ptr() ); + refillBuffer->SetLastBuffer(EFalse); + refillBuffer->SetStatus(EUnAvailable); + + return KErrCancel; + } + + TRAPD(err, FillSharedBufferL(refillBuffer)); // Trap if DRM read fails + + if (err != KErrNone) + { + DP1(_L("Error filling shared buffer!!! %d"),err); + + iState = EPlaying; // Set here to get StopL to shutdown completely + TRAP_IGNORE(DoStopL(err)); // If stop leaves, then just return err + } + + return err; + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::PlaybackComplete +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::PlaybackComplete() + { // comes here for KErrUnderflow - no error + DP3(_L("CAdvancedAudioPlayController::PlaybackComplete() reqstate[%d] state[%d] playseek[%d]"), iRequestState, iState, iPlaySeeking); + // Keep in mind that seek past end of file would end up here instead of SeekPositionReached() + if (iPlaySeeking) + { + // Seeking past end of file. + // Here we should be in play state and iPlaySeeking (playing for seeking purpose) + // Take care of this with the SeekPositionReached method, but don't adjust time. + SeekPositionReached(KMaxTUint); + } + else + { // normal play to end of file + DP0(_L("CAdvancedAudioPlayController::PlaybackComplete() stopping")); + TRAP_IGNORE(DoStopL(KErrUnderflow)); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::SendEvent +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::SendEvent( + const TMMFEvent& aEvent) + { + DP2(_L("CAdvancedAudioPlayController::SendEvent[%d] this[%x]"), aEvent.iErrorCode, this); + + if (aEvent.iErrorCode == KErrBufferNotReady) + { + HandleAutoPauseEvent(); + } + // MMFDevSound throws the following error codes incase of any preemption events + // (DevSound instance has been thrown-off or initial request has been rejected) + else if ((aEvent.iErrorCode == KErrAccessDenied) || (aEvent.iErrorCode == KErrInUse) || (aEvent.iErrorCode == KErrDied) ) + { + // this might be a DevSound Preemption + // KErrDied is too generic because it is being returned in many situations + HandlePreemptionEvent(aEvent.iErrorCode); + } + else + { + HandleGeneralEvent(aEvent); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::BitRateChanged +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::BitRateChanged() + { + DP0(_L("CAdvancedAudioPlayController::BitRateChanged")); + iDataSourceAdapter->Event(KMultimediaDataSourceEventBitRateChanged); + UpdateDuration(KOneThousandMilliSecond); + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::BufferFilled +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::BufferFilled(CMMFBuffer* aBuffer) + { + DP0(_L("CAdvancedAudioPlayController::BufferFilled")); + TRAPD(err, BufferFilledL(aBuffer)); + if (err != KErrNone) + { + DP1(_L("CAdvancedAudioPlayController::BufferFilled, BufferFilledL err[%d]"), err); + TRAP_IGNORE(DoStopL(err)); + } + } + +// ----------------------------------------------------------------------------- +// CAdvancedAudioPlayController::Event +// ----------------------------------------------------------------------------- +// +EXPORT_C void CAdvancedAudioPlayController::Event(TUid aEvent) + { + if (iEventsEnabled) + { + if (aEvent == KMultimediaDataSourceObserverEventSourceSizeChanged) + { + TInt sourceSize = iDataSourceAdapter->SourceSize(); + if (sourceSize < 0) + { + sourceSize = 0; + } + if (iAudioUtility) + { + TRAP_IGNORE(iAudioUtility->SetClipSizeL(sourceSize)); + } + UpdateDuration(0); + } + if (aEvent == KMultimediaDataSourceEventRandomSeekingSupportChanged) + { + iSourceIsTimeSeekable = iDataSourceAdapter->IsTimeSeekable(); + iSourceIsPosSeekable = iDataSourceAdapter->IsPositonSeekable(); + } + } + } + +EXPORT_C void CAdvancedAudioPlayController::SeekPositionReached(TUint aTimeMs) + {// this can only be called from play state - we have reached this seek time + // update the time played and transition back to correct state + DP1(_L("CAdvancedAudioPlayController::SeekPositionReached[%d]"), aTimeMs); + if (aTimeMs < KMaxTUint) + { // no update if seek past end of file - this comes from playbackcomplete() + iTimePositionInMicroSecs = aTimeMs; // this is set before the seek - may not need to do this here + iTimePositionInMicroSecs *=1000; + DP1(_L("CAdvancedAudioPlayController::SeekPositionReached iTimePositionInMicroSecs[%d]"),aTimeMs); + } + + iPlaySeeking = EFalse; + if (iPlayingForDuration) + { + UpdateDuration(); + if (iRequestState == EPlaying) + { // since we are already playing for the seek and the requested is to play, we can just keep playing + // rendering will be enabled when position reached so we don't have to call play here - we're already playing + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForDuration - not stopping")); + iPlayingForDuration = EFalse; + } + else + { // normally this would need to go back to stop state. + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForDuration - stopping")); + TRAP_IGNORE(DoStopL(KErrNone)); + } + } + else if (iPlayingForInitPos) + { + if (iRequestState == EPlaying) + { // since we are already playing for the seek and the requested is to play, we can just keep playing + // rendering will be enabled when position reached so we don't have to call play here - we're already playing + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForInitPos - not pausing")); + iPlayingForInitPos = EFalse; + } + else + { // normally this would need to go back to pause state. + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForInitPos - pausing")); + TRAP_IGNORE(DoPauseL()); + } + } + else if (iPausingForSetPos) + { // seek was initiated during play but paused in order to seek and are now returning to play + // rendering will be enabled when position reached so we don't have to call play here - we're already playing + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPausingForSetPos - already playing - rendering should be enabled")); + iPausingForSetPos = EFalse; + if (iState == EPaused) + { + if (iAudioOutput->IsDSStopped()) + { // during loop play the output is not stopped unless we did a seek - seek stops the output and we need to call DoPlay + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached Calling DoPlay iState=EPaused")); + DoPlayL(); + } + else + { + DP1(_L("CAdvancedAudioPlayController::SeekPositionReached Calling DoResume iState=EPaused iCurrentPosition[%d]"), iCurrentPosition); + if (IsLoopPlayEnabled()) + { + DoResume(iCurrentPosition); + } + else + { + DoPlayL(); + } + } + } + } + else if (iPlayingForPauseSeek) + { // seek was initiated during a pause + if (iRequestState == EPlaying) + { // since we are already playing for the seek and the requested is to play, we can just keep playing + // rendering will be enabled when position reached so we don't have to call play here - we're already playing + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForPauseSeek - not pausing")); + iPlayingForPauseSeek = EFalse; + } + else + { // normally this would need to go back to pause state. + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached iPlayingForPauseSeek - pausing")); + TRAP_IGNORE(DoPauseL()); + } + } + if (iBlockSetPos) + { + DP0(_L("CAdvancedAudioPlayController::SeekPositionReached() unblocking")); + if (iBlockSetPos->IsStarted()) + { + iBlockSetPos->AsyncStop(); + } + } + } + +EXPORT_C void CAdvancedAudioPlayController::PlayWindowEndPositionReached() + { + } + +TBool CAdvancedAudioPlayController::IsLoopPlayEnabled() const + { + if ((iRepeatForever) || (iCurrentRepeatCount <= iRepeatCount)) + { + return ETrue; + } + else + { + return EFalse; + } + } + +// -------------------------------------------------------------------------------------------------------------- +// CTrailingSilenceTimer class definitions +// -------------------------------------------------------------------------------------------------------------- +CTrailingSilenceTimer* CTrailingSilenceTimer::NewL(MTrailingSilenceObserver &aobserver) + { + DP0(_L("CTrailingSilenceTimer::NewL Begin")); + CTrailingSilenceTimer* self=new(ELeave) CTrailingSilenceTimer(aobserver); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + DP0(_L("CTrailingSilenceTimer::NewL End")); + return self; + } + +CTrailingSilenceTimer::CTrailingSilenceTimer(MTrailingSilenceObserver &aobserver) + : + CTimer(CActive::EPriorityStandard), + iObserver(&aobserver) + { + } + +void CTrailingSilenceTimer::ConstructL() + { + DP0(_L("CTrailingSilenceTimer::ConstructL Begin")); + // Base class second-phase construction. + CTimer::ConstructL(); + // Add to the ActiveScheduler + CActiveScheduler::Add(this); + DP0(_L("CTrailingSilenceTimer::ConstructL End")); + } + +CTrailingSilenceTimer::~CTrailingSilenceTimer() + { + DP0(_L("CTrailingSilenceTimer::~CTrailingSilenceTimer Begin")); + Cancel(); + DP0(_L("CTrailingSilenceTimer::~CTrailingSilenceTimer End")); + } + +void CTrailingSilenceTimer::DoCancel() + { + DP0(_L("CTrailingSilenceTimer::DoCancel Begin")); + // Call the default DoCancel method + CTimer::DoCancel(); + DP0(_L("CTrailingSilenceTimer::DoCancel End")); + } + +void CTrailingSilenceTimer::RunL() + { + DP0(_L("CTrailingSilenceTimer::RunL")); + iObserver->TrailingSilenceTimerComplete(); + } + +void CTrailingSilenceTimer::RunError() + { + DP0(_L("CTrailingSilenceTimer::RunError")); + // Handle error here tbd + } +// -------------------------------------------------------------------------------------------------------------- + +// End of file