--- /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 <AudioOutput.h>
+#include <MetaDataFieldContainer.h>
+#include <MetaDataUtility.h>
+#include <mmfformatimplementationuids.hrh>
+#include <mmfmeta.h>
+#include <MultimediaDataSourceEvents.h>
+#include <MultimediaDataSourceFactory.h>
+#include <oma2dcf.h>
+
+// KMdaRepeatForever constant is defined in this file
+#include <mda/common/resource.h>
+// 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<CMMFDataBuffer*>(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<CMMFDataBuffer*>(aBuffer)->Data().Ptr(), iState);
+
+ aBuffer->SetLastBuffer(EFalse);
+ aBuffer->SetStatus(EBeingFilled);
+ aBuffer->SetPosition(0);
+ static_cast<CMMFDataBuffer*>(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<CMMFFile*>(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<CMMFFile*>(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<CMMFClip*>(iDataSink)->SinkPrimeL();
+ static_cast<CMMFFile*>(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<CMMFDataBuffer*>(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<CMMFDataBuffer*>(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<TInt>& codecConfigData = const_cast<RArray<TInt>&>(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<TInt>& 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<CMMFDataBuffer*>(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<CMMFDataBuffer*>(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