sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // source\server\mmfdatapath.cpp sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "mmfclientaudiostreamutils.h" sl@0: #include sl@0: #include // KUidMmfCodecAudioSettings sl@0: #include sl@0: sl@0: const TUid KUidCodecAudioConfig = {KUidMmfCodecAudioSettings}; sl@0: sl@0: void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber) sl@0: { sl@0: _LIT(KMMFDataPathPanicCategory, "MMFDataPath"); sl@0: User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber); sl@0: } sl@0: sl@0: //all functions are exported form the DLL and are virtual to allow plugins to define there own CMMFDataPaths sl@0: sl@0: /** sl@0: Allocates and constructs a data path. sl@0: sl@0: Use this function if the codec UID is not already known by CMMFController sl@0: and there is no data path ambiguity - ie only one data path is possible. sl@0: sl@0: Will create codec via fourCC. sl@0: sl@0: @param aEventHandler sl@0: Installs an event handler to provide message passing between clients and sources/sinks. sl@0: sl@0: @return Newly constructed data path object. sl@0: */ sl@0: sl@0: EXPORT_C CMMFDataPath* CMMFDataPath::NewL(MAsyncEventHandler& aEventHandler) sl@0: { sl@0: CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Allocates and constructs a data path according to the specified media ID. sl@0: sl@0: Use this function if the codec UID is not already known by CMMFController sl@0: and there is ambiguity with the data path ie. there is more than one possible data path. sl@0: sl@0: @param aMediaId sl@0: Optional media ID parameter when there are multiple media types. sl@0: @param aEventHandler sl@0: Installs an event handler to provide message passing between clients and sources/sinks. sl@0: sl@0: @return A newly constructed data path object. sl@0: */ sl@0: sl@0: EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) sl@0: { sl@0: CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: /** sl@0: Allocates and constructs a data path according to the specified codec UID. sl@0: sl@0: Use this function if the codec UID is already known by CMMFController sl@0: and there is no data path ambiguity ie. only one data path is possible sl@0: will create codec explicitly using the supplied codec Uid sl@0: sl@0: @param aCodecUid sl@0: Optional mediaID parameter when there are multiple media types sl@0: @param aEventHandler sl@0: Installs an event handler to provide message passing between clients and sources/sinks. sl@0: sl@0: @return A newly constructed data path object. sl@0: */ sl@0: sl@0: EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler) sl@0: { sl@0: CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aCodecUid); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Allocates and constructs a data path according to the specified codec UID. sl@0: sl@0: Use this function if the codec UID is already known by CMMFController sl@0: and there is ambiguity ie. more than one possible data path. sl@0: TMediaId used to select the path. sl@0: sl@0: @param aCodecUid sl@0: The codec UID. sl@0: @param aMediaId sl@0: Optional mediaID parameter when there are multiple media types. sl@0: @param aEventHandler sl@0: Installs an event handler to provide message passing between clients and sources/sinks. sl@0: sl@0: @return A newly constructed data path object. sl@0: */ sl@0: EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler) sl@0: { sl@0: CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aCodecUid); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: /** sl@0: Standard destructor. sl@0: */ sl@0: sl@0: EXPORT_C CMMFDataPath::~CMMFDataPath() sl@0: { sl@0: Cancel(); sl@0: delete iCodec; sl@0: DoCleanupBuffers(); sl@0: sl@0: //log off the source and sink sl@0: if (iDataSource) sl@0: iDataSource->SourceThreadLogoff(); sl@0: if (iDataSink) sl@0: iDataSink->SinkThreadLogoff(); sl@0: sl@0: if (iCompleteCallback) sl@0: { sl@0: iCompleteCallback->Cancel(); sl@0: delete iCompleteCallback; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL(). sl@0: Typically if buffers are created asychronously, the datapath doesn't own the buffer sl@0: so leaves cleanup handling to the owner sources/sinks. sl@0: sl@0: Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive, sl@0: iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::ResetL() sl@0: { sl@0: delete iCodec; sl@0: iCodec = NULL; sl@0: DoCleanupBuffers(); // Delete buffers sl@0: //logoff and dereference source and sink sl@0: if (iDataSource) sl@0: { iDataSource->SourceThreadLogoff(); iDataSource = NULL; } sl@0: if (iDataSink) sl@0: { iDataSink->SinkThreadLogoff(); iDataSink = NULL; } sl@0: sl@0: // Reset states sl@0: iDataPathCreated = EFalse; sl@0: iState = EStopped; sl@0: iSrcBufRef = EFalse; sl@0: iSnkBufRef = EFalse; sl@0: iPauseCalled = EFalse; sl@0: sl@0: delete iCompleteCallback; iCompleteCallback = NULL; sl@0: } sl@0: sl@0: /** sl@0: Delete source and/or sink buffers that are owned by DataPath. sl@0: sl@0: Ownership indicated by iSrcBufRef and iSnkBufRef. sl@0: sl@0: Ownership is assigned during buffer allocation within the datapath PrimeL(). sl@0: */ sl@0: void CMMFDataPath::DoCleanupBuffers() sl@0: { sl@0: // delete source and/or sink buffer that is owned by DataPath sl@0: if ( !iSrcBufRef && iSourceBuffer ) sl@0: { sl@0: delete iSourceBuffer; sl@0: } sl@0: iSourceBuffer = NULL; sl@0: if ( !iSnkBufRef && iSinkBuffer ) sl@0: { sl@0: delete iSinkBuffer; sl@0: } sl@0: iSinkBuffer = NULL; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Obtain source and/or sink buffer using the synchronous API CreateSourceBufferL() and CreateSinkBufferL(). sl@0: */ sl@0: void CMMFDataPath::ObtainSyncBuffersL() sl@0: { sl@0: //Try to create source and sink buffers. If we can't create them synchronously via sl@0: //CreateSourceBufferL and CreateSinkBufferL we will need to obtain them by sl@0: //asynchronous buffer creation when playing starts. sl@0: sl@0: if (iBuffersToUse & ENeedSourceBuffer) sl@0: { sl@0: if (!iSourceBuffer) //we may already have a buffer from a previous initialization sl@0: { sl@0: TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); sl@0: if(err != KErrNone && err != KErrNotSupported) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); sl@0: #endif sl@0: User::Leave(err); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: if (iBuffersToUse & ENeedSinkBuffer) sl@0: { sl@0: if (!iSinkBuffer) //we may already have a buffer from a previous initialization sl@0: { sl@0: TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); sl@0: if(err != KErrNone && err != KErrNotSupported) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); sl@0: #endif sl@0: User::Leave(err); sl@0: } sl@0: } sl@0: } sl@0: sl@0: if (iSourceBuffer && !(iBuffersToUse & ENeedSinkBuffer)) sl@0: {//only need one buffer, use source sl@0: iSinkBuffer =iSourceBuffer; sl@0: iSnkBufRef = ETrue; //the sink buffer is not to be deleted sl@0: } sl@0: else if (iSinkBuffer && !(iBuffersToUse & ENeedSourceBuffer)) sl@0: {//only need one buffer, use sink sl@0: iSourceBuffer =iSinkBuffer; sl@0: iSrcBufRef = ETrue; //the sink buffer is not to be deleted sl@0: } sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::ObtainSyncBuffersL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: Constructs a source. sl@0: sl@0: The default implementation leaves with KErrNotSupported. sl@0: sl@0: @param aInitData sl@0: The initialisation data. sl@0: */ sl@0: sl@0: EXPORT_C void CMMFDataPath::ConstructSourceL( const TDesC8& /*aInitData*/ ) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: /** sl@0: Constructs a sink. sl@0: sl@0: Overridable constuction specific to this datasource. sl@0: sl@0: The default implementation leaves with KErrNotSupported. sl@0: sl@0: @param aInitData sl@0: The initialisation data. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::ConstructSinkL( const TDesC8& /*aInitData*/ ) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: /** sl@0: Takes UID of codec on construction, and if not an NULL codec sets the datapath up for codec instantiation. sl@0: sl@0: @param aCodecUid sl@0: The UID of the codec. sl@0: */ sl@0: sl@0: EXPORT_C void CMMFDataPath::ConstructL(TUid aCodecUid) sl@0: { sl@0: iUseSuppliedCodecUid = EFalse; //initially assume no supplied codec uid sl@0: sl@0: if (aCodecUid != KNullUid) sl@0: {//the data path NewL has specified a specific codec sl@0: //create CMMFCodec here sl@0: iCodec = CMMFCodec::NewL(aCodecUid); sl@0: if (iCodec) sl@0: iUseSuppliedCodecUid = ETrue; sl@0: } sl@0: sl@0: iSrcBufRef = EFalse; sl@0: iSnkBufRef = EFalse; sl@0: iObtainingAsyncSourceBuffer = EFalse; sl@0: iObtainingAsyncSinkBuffer = EFalse; sl@0: iSourceBufferWithSource = EFalse; sl@0: iSinkBufferWithSink = EFalse; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Adds a data source to the datapath and, if the sink already exists, tries to establish a connection sl@0: between the source and sink. sl@0: sl@0: @param aSource sl@0: The data source to add to the data path. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::AddDataSourceL(MDataSource* aSource) sl@0: { sl@0: if (!iDataSink) iDataSource=aSource; //can't create a data path without the MDataSink as well sl@0: else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path sl@0: {//we have a data sink as well so check a data path can be established between source&sink sl@0: CreateDataPathL(aSource, iDataSink); sl@0: iDataSource = aSource; sl@0: } sl@0: else //the CMMFController specified the codec uid so must use existing codec sl@0: {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid sl@0: //can make the appropriate data conversion sl@0: iDataPathCreated = ETrue; sl@0: iDataSource = aSource; sl@0: } sl@0: ClearPlayWindowL() ; sl@0: User::LeaveIfError(iDataSource->SourceThreadLogon(*this)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Adds a data sink to the datapath and, if the source already exists, tries to establish a connection sl@0: between the source and sink. sl@0: sl@0: @param aSink sl@0: The data sink to add to the data path. sl@0: */ sl@0: sl@0: EXPORT_C void CMMFDataPath::AddDataSinkL(MDataSink* aSink) sl@0: { sl@0: if (!iDataSource) iDataSink=aSink; //can't create a data path without the MDataSource as well sl@0: else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path sl@0: {//we have a data source as well so check a media path can be established between source&sink sl@0: CreateDataPathL(iDataSource, aSink); sl@0: iDataSink = aSink; sl@0: } sl@0: else //the CMMFController specified the codec uid so must use existing codec sl@0: {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid sl@0: //can make the appropriate data conversion sl@0: iDataPathCreated = ETrue; sl@0: iDataSink = aSink; sl@0: sl@0: //set 4CCs sl@0: iSourceFourCC = iDataSink->SinkDataTypeCode(iMediaId);//sink because CMMFDataPath is an MDataSink to its MDataSource! sl@0: iSinkFourCC = iDataSource->SourceDataTypeCode(iMediaId);//source because CMMFDataPath is an MDataSource to its MDataSink! sl@0: } sl@0: User::LeaveIfError(iDataSink->SinkThreadLogon(*this)); sl@0: } sl@0: sl@0: sl@0: /* sl@0: * CreateDataPathL sl@0: * sl@0: * internal function to establish a datapath between the source and sink sl@0: * the data supplied by the sink adn expected by the source are checked and sl@0: * a codec is instantiated if necessary sl@0: * sl@0: * @param aSource sl@0: * @param aSink sl@0: */ sl@0: sl@0: void CMMFDataPath::CreateDataPathL(MDataSource* aSource, MDataSink* aSink) sl@0: { //procedure to attempt to match the source to the sink creating a codec if necessary sl@0: // returns ETrue if the datapath could be constructed else false sl@0: //sets iCodec to the appropriate codec.& sets its own iSink/iSource FourCC datatype codes sl@0: iDataPathCreated = EFalse; sl@0: if (aSource && aSink) //have a source and sink sl@0: { //we have a data source & sink but no codec so try and find one - if required sl@0: TFourCC sourceFourCCCode = aSource->SourceDataTypeCode(iMediaId); //get MDataSource data type fourCC code sl@0: TFourCC sinkFourCCCode = aSink->SinkDataTypeCode(iMediaId); //get MDataSink data type fourCC code sl@0: if ((sourceFourCCCode != sinkFourCCCode) && //MDataSource & MDataSink datatypes are not compatible sl@0: (sourceFourCCCode != KMMFFourCCCodeNULL) && (sinkFourCCCode != KMMFFourCCCodeNULL)) sl@0: {//we need a codec to make the conversion between the source and the sink sl@0: CMMFCodec* codec = CMMFCodec::NewL(sourceFourCCCode, sinkFourCCCode); sl@0: sl@0: if (codec) sl@0: { sl@0: delete iCodec; sl@0: iCodec = codec; sl@0: //data path created ie have source/sink and can match their datatypes sl@0: iDataPathCreated = ETrue; sl@0: sl@0: //now we have an source attached we need to configure the codec for sample rate sl@0: //and number of channels sl@0: sl@0: //prepare a package to send to a codec sl@0: TMMFAudioConfig audioSettings; sl@0: sl@0: //test for decoder sl@0: if (aSource->DataSourceType() == KUidMmfFormatDecode) sl@0: { sl@0: audioSettings.iSampleRate = static_cast(aSource)->SampleRate(); sl@0: audioSettings.iChannels = static_cast(aSource)->NumChannels(); sl@0: } sl@0: sl@0: //package up to send to codec sl@0: TPckgBuf configPackage(audioSettings); sl@0: sl@0: //we need to catch User::Leave(KErrNotSupported) as by default most codecs sl@0: //do not support the ConfigureL method. sl@0: TRAPD(err,iCodec->ConfigureL(KUidCodecAudioConfig, configPackage)); sl@0: // need to check other error here sl@0: if (err != KErrNone && err != KErrNotSupported) sl@0: { sl@0: User::Leave(err); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: User::Leave( KErrNotSupported ) ; //couldn't find suitable codec sl@0: } sl@0: } //if (sourceFourCCCode != sinkFourCCCode) sl@0: else sl@0: { //source & sink fourCC datatypes are the same so no codec is required sl@0: __ASSERT_DEBUG(iCodec == NULL, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); sl@0: sl@0: iDataPathCreated = ETrue; sl@0: } sl@0: //can assign FourCC codes for the CMMFDataPath sl@0: iSinkFourCC = sourceFourCCCode; //sink because CMMFDataPath is an MDataSink to its MDataSource! sl@0: iSourceFourCC = sinkFourCCCode; //source because CMMFDataPath is an MDataSource to its MDataSink! sl@0: //If sink & source need its own Prime() done in controller sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Clears the specified buffer. sl@0: sl@0: Pure virtual dummy implementation, not needed by datapath sl@0: comes from MDataSink - CMMFData path is a sink to its MDataSource. sl@0: sl@0: This is only required for an active push MDataSource requesting a buffer empty. sl@0: sl@0: @param aBuffer sl@0: The buffer to empty. sl@0: @param aSupplier sl@0: The MDataSource supplying this buffer. sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving of different media types. sl@0: sl@0: */ sl@0: EXPORT_C void CMMFDataPath::EmptyBufferL(CMMFBuffer* /* aBuffer */, MDataSource* /*aSupplier*/, TMediaId /*aMediaId*/) sl@0: { sl@0: //not implemented sl@0: } sl@0: sl@0: sl@0: sl@0: /* sl@0: * FillSourceBufferL sl@0: * sl@0: * Function to get data from the datapath's iDataSource sl@0: */ sl@0: sl@0: void CMMFDataPath::FillSourceBufferL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSourceBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: sl@0: // clear the no-more-source flag here (as well as in PlayL()) because sl@0: // there may have been a re-position since the last call to BufferFilledL() sl@0: iNoMoreSourceData = EFalse; sl@0: sl@0: if(!iObtainingAsyncSourceBuffer) sl@0: {//this is a normal request for data. sl@0: //If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL sl@0: iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with sl@0: iSourceBuffer->SetStatus(EBeingFilled); sl@0: iSourceBuffer->SetLastBuffer(EFalse); sl@0: } sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP asking for buffer %d - ptr=0x%x (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this); sl@0: #endif sl@0: sl@0: iSourceBufferWithSource = ETrue; sl@0: sl@0: // wait for BufferFilled callback from source. Do this here as some sources cause sl@0: //re-entrancy into data path via BufferFilledL sl@0: ChangeDataPathTransferState(EWaitSource); sl@0: sl@0: iDataSource->FillBufferL(iSourceBuffer, this, iMediaId); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Indicates the data source has filled the specified buffer. sl@0: sl@0: Called by the CMMFDataPath's MDataSource when it has filled the buffer. sl@0: sl@0: @param aBuffer sl@0: A pointer to the filled buffer. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::BufferFilledL(CMMFBuffer* aBuffer) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL src has filled buffer %d (ptr=0x%x) with %d bytes EoF = %d tick-%d (this 0x%x)\n"),aBuffer->FrameNumber(),aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), User::TickCount(),this); sl@0: #endif sl@0: sl@0: //This assertion is commented because of PDEF117405 sl@0: //state only used if we are passing data sl@0: //__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: iSourceBufferWithSource = EFalse; sl@0: sl@0: //Has the datapath stopped running, if so were not interested in any callbacks. sl@0: if(iState == EStopped || iState == EPrimed || (iPauseCalled && iState != ERecording)) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); sl@0: #endif sl@0: return; sl@0: } sl@0: sl@0: #ifdef REPOSITION_SPEEDUP sl@0: // if the source has been re-positioned, then go & get some more source data now sl@0: if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this); sl@0: #endif sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: return; sl@0: } sl@0: #endif //REPOSITION_SPEEDUP sl@0: sl@0: //bufer is NULL, indicating no more source data. sl@0: if (!aBuffer) sl@0: { sl@0: //If we only hold a reference to the source buffer, set that to NULL sl@0: if(iSnkBufRef) sl@0: iSourceBuffer = NULL; sl@0: sl@0: sl@0: iNoMoreSourceData = ETrue; sl@0: sl@0: if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data sl@0: iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data sl@0: { sl@0: ChangeDataPathTransferState(EEndOfData); sl@0: } sl@0: else //sink buffer is with datapath, see if there is anything to send to sink sl@0: ChangeDataPathTransferState(ENeedToMatchSourceToSink); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: return; sl@0: } sl@0: sl@0: sl@0: //We were waiting for a response from the source to get an asynchronous buffer. sl@0: //We now have it, and we proceed to transfer this data to the sink. sl@0: if (iObtainingAsyncSourceBuffer) sl@0: { sl@0: iObtainingAsyncSourceBuffer = EFalse; sl@0: } sl@0: sl@0: sl@0: aBuffer->SetStatus(EFull); sl@0: sl@0: if(iSourceBuffer != aBuffer) sl@0: {//buffer has been changed by the source sl@0: iSourceBuffer = aBuffer; sl@0: if (!(iBuffersToUse & ENeedSinkBuffer)) sl@0: {//we only need one buffer and use source sl@0: iSinkBuffer = iSourceBuffer; sl@0: iSnkBufRef = ETrue; sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: } sl@0: //Is this the last buffer from the source (0 length or LastBuffer flag set) sl@0: //or have reached the end of the play window; we only look at the play window here sl@0: //if we are converting. For conversion we look at the data we have read. This is then passed onto sl@0: //the source sl@0: if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() || sl@0: (((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition ))) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL end of input data tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize()); sl@0: RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer()); sl@0: RDebug::Print(_L("InputPosition()=%d >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64())); sl@0: #endif sl@0: iNoMoreSourceData = ETrue; sl@0: iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window sl@0: } sl@0: sl@0: sl@0: if (!iCodec) sl@0: ChangeDataPathTransferState(ESendDataToSink); sl@0: else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it sl@0: ChangeDataPathTransferState(ENeedToMatchSourceToSink); sl@0: //else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /* sl@0: * FillSinkBufferL sl@0: * sl@0: * Function to take the data from an already full source buffer and by using sl@0: * a codec if necessary fills the sink buffer sl@0: */ sl@0: sl@0: void CMMFDataPath::FillSinkBufferL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: //This state is only used if we are passing data sl@0: __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: //This state is only used if we have a real codec sl@0: __ASSERT_DEBUG(iCodec, Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: sl@0: //The sink buffer is with the sink so we can't fill it. sl@0: //When it has been emptied, this state will be re-entered from BufferEmptiedL sl@0: if(iSinkBufferWithSink) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SINK - DONE (this 0x%x)\n"),this); sl@0: #endif sl@0: ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink sl@0: return; sl@0: } sl@0: sl@0: //The source buffer is with the source so we can't take data from it. sl@0: //When it has been filled, this state will be re-entered from BufferFilledL sl@0: if(iSourceBufferWithSource) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SOURCE - DONE (this 0x%x)\n"),this); sl@0: #endif sl@0: ChangeDataPathTransferState(EWaitSource); // wait for BufferFilled callback from source sl@0: return; sl@0: } sl@0: sl@0: //source buffer is NULL, can't be any more data to send. sl@0: //iNoMoreSourceData is set and the source buffer is empty, can't be any more data to send. sl@0: if(!iSourceBuffer || (iNoMoreSourceData && !iSourceBuffer->BufferSize())) sl@0: { sl@0: if(iSinkBuffer->Status() == EBeingFilled) sl@0: {//if we have data in sink buffer, mark it as last buffer and send sl@0: iSinkBuffer->SetLastBuffer(ETrue); sl@0: ChangeDataPathTransferState(ESendDataToSink); sl@0: } sl@0: else //the sink buffer can't have anything in it sl@0: ChangeDataPathTransferState(EEndOfData); sl@0: } sl@0: sl@0: #ifdef REPOSITION_SPEEDUP sl@0: // if the source has been re-positioned, sl@0: // speed things up by getting some more source data now sl@0: if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); sl@0: #endif sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: return; sl@0: } sl@0: #endif //REPOSITION_SPEEDUP sl@0: sl@0: iSinkBuffer->SetStatus(EBeingFilled); sl@0: iSinkBuffer->SetLastBuffer(EFalse); sl@0: sl@0: //pass buffer to codec for processing sl@0: iCodecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSinkBuffer); sl@0: //the codec tries to fill the sink buffer to its max length sl@0: //TCodecProcessResult returns the status of the codec Process - sl@0: //this can result in result conditions such as: sl@0: //EProcessComplete - the codec processed all the source data into the sink buffer sl@0: //EProcessIncomplete - the codec filled sink buffer before all the source buffer was processed sl@0: //EDstNotFilled - the codec processed the source buffer but the sink buffer was not filled sl@0: //EEndOfData - the codec detected the end data - all source data in processed but sink may not be full sl@0: //EProcessError - the codec process error condition sl@0: sl@0: sl@0: switch (iCodecProcessResult.iStatus) sl@0: { sl@0: case TCodecProcessResult::EProcessComplete: sl@0: //finished procesing source data - all data in sink buffer sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessComplete (this 0x%x)\n"),this); sl@0: #endif sl@0: iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble sl@0: iSinkBuffer->SetStatus(EFull); //sink buffer is full sl@0: if (iNoMoreSourceData) sl@0: iSinkBuffer->SetLastBuffer(ETrue); sl@0: ChangeDataPathTransferState(ESendDataToSink);// the full sink buffer needs to be sent to the sink sl@0: } sl@0: break; sl@0: case TCodecProcessResult::EProcessIncomplete: sl@0: // the sink was filled before all the src was processed sl@0: // therefore still send everything to sink sl@0: //but datapath needs to carry on processing the source buffer before it gets more source data sl@0: //when sink has emptied data path needs to send rest of data sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessIncomplete (this 0x%x)\n"),this); sl@0: #endif sl@0: TUint sourceBufferPosition = iCodecProcessResult.iSrcBytesProcessed + iSourceBuffer->Position(); sl@0: iSourceBuffer->SetPosition(sourceBufferPosition);//update source buffer position sl@0: iSinkBuffer->SetStatus(EFull); //sink & source buffers are both full sl@0: ChangeDataPathTransferState(ESendDataToSink); // the full sink buffer needs to be sent to the sink sl@0: } sl@0: break; sl@0: case TCodecProcessResult::EDstNotFilled: sl@0: // the destination is not full sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EDstNotFilled (this 0x%x)\n"),this); sl@0: #endif sl@0: iSourceBuffer->SetStatus(EAvailable); //source buffer is now available sl@0: TUint sinkBufferPosition = iCodecProcessResult.iDstBytesAdded + iSinkBuffer->Position(); sl@0: iSinkBuffer->SetPosition(sinkBufferPosition);//update sink buffer position (still EBeingFilled) sl@0: // if this was the last source buffer, send what we've got (if anything) sl@0: // to the sink... EmptySinkBuffer() should then enter EEndOfData state sl@0: if (iNoMoreSourceData) sl@0: { sl@0: iSinkBuffer->SetLastBuffer(ETrue); sl@0: ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - sl@0: } sl@0: else sl@0: { sl@0: ChangeDataPathTransferState(ENeedSourceData); //need to get more source data to fill sink buffer sl@0: } sl@0: } sl@0: break; sl@0: case TCodecProcessResult::EEndOfData: sl@0: //no more data - send what we've got to the sink sl@0: //note we can't always rely on this - in many cases the codec will not know when sl@0: //it has reached the end of data. sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EEndOfData (this 0x%x)\n"),this); sl@0: #endif sl@0: iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble sl@0: iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get sl@0: sl@0: //This only occurs where the codec can detect the end of data, but the source can't sl@0: iNoMoreSourceData=ETrue; sl@0: iSinkBuffer->SetLastBuffer(ETrue); sl@0: sl@0: ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - sl@0: //doesn't matter if sink buffer is not full sl@0: } sl@0: break; sl@0: case TCodecProcessResult::EProcessError: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); sl@0: #endif sl@0: User::Leave(KErrCorrupt); //codec process error sl@0: break; sl@0: default: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); sl@0: #endif sl@0: User::Leave(KErrCorrupt); //should never get here sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::FillSinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Tests whether the data path can create a sink buffer. sl@0: sl@0: The default implementation returns false. sl@0: sl@0: @return ETrue if the data path can create a sink buffer. EFalse if the data path cannot create a sink buffer. sl@0: */ sl@0: EXPORT_C TBool CMMFDataPath::CanCreateSinkBuffer() sl@0: { sl@0: return NULL; //CMMFDataPath cannot create buffer sl@0: } sl@0: sl@0: /** sl@0: Creates a sink buffer according to the specifed media ID. sl@0: sl@0: Intended for synchronous usage (buffers supplied by datapath for an MDataSink). sl@0: This method is essentially a dummy implementation of an MDataSink pure virtual. sl@0: sl@0: The default implementation returns NULL. sl@0: sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving of different media types. sl@0: sl@0: @return Returns NULL in this instance as datapath can't create sink buffers sl@0: */ sl@0: EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/) sl@0: {//CMMFDataPath can't create buffers sl@0: return NULL; sl@0: } sl@0: sl@0: /** sl@0: Creates a sink buffer according to the specifed media ID and reference. sl@0: sl@0: Intended for asynchronous usage (buffers supplied by Devsound device). sl@0: This method is essentially a dummy implementation of an MDataSink pure virtual. sl@0: sl@0: The default implementation returns NULL. sl@0: sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving for different media types. sl@0: @param aReference sl@0: A boolean indicating buffer ownership. sl@0: sl@0: @return Returns NULL in this instance as datapath can't create sink buffers. sl@0: */ sl@0: EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) sl@0: {//CMMFDataPath can't create buffers sl@0: return NULL; sl@0: } sl@0: sl@0: /** sl@0: Gets the sink's data type for the specified media ID. sl@0: sl@0: @param aMediaId sl@0: An optional parameter to specifiy the specific stream when datasource contains more than one stream of data sl@0: @return The sink's data type. sl@0: */ sl@0: EXPORT_C TFourCC CMMFDataPath::SinkDataTypeCode(TMediaId /*aMediaId*/) sl@0: { sl@0: return(iSinkFourCC); sl@0: } sl@0: sl@0: /** sl@0: Fills the specified buffer. sl@0: sl@0: Pure virtual dummy implementation, not needed by datapath sl@0: comes from MDataSink - CMMFData path is a source to its MDataSink sl@0: sl@0: Only required for an active pull MDataSink requesting a buffer fill. The default implementation is empty. sl@0: sl@0: @param aBuffer sl@0: The buffer to fill. sl@0: @param aConsumer sl@0: The MDataSink supplying this buffer. sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving of different media types sl@0: */ sl@0: EXPORT_C void CMMFDataPath::FillBufferL(CMMFBuffer* /*aBuffer*/, MDataSink* /*aConsumer*/, TMediaId /*aMediaId*/) sl@0: { sl@0: //not implementated sl@0: } sl@0: sl@0: void CMMFDataPath::SetBuffersAvailable() sl@0: { sl@0: // set source buffer to be available sl@0: if (iSourceBuffer) sl@0: iSourceBuffer->SetStatus(EAvailable); sl@0: // set sink buffer to be available sl@0: if (iSinkBuffer) sl@0: iSinkBuffer->SetStatus(EAvailable); sl@0: } sl@0: sl@0: void CMMFDataPath::ResetRefBuffers() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::ResetRefBuffers iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: sl@0: // Reset the buffer pointers to NULL if they are supplied by DevSound sl@0: // We do this because buffers that are not owned by the datapath may not be valid any more. sl@0: if (iSrcBufRef) sl@0: { sl@0: iSourceBuffer = NULL; sl@0: } sl@0: if (iSnkBufRef) sl@0: { sl@0: iSinkBuffer = NULL; sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: TInt CMMFDataPath::DetermineBuffersToUseL(void) const sl@0: { sl@0: TInt buffs = ENoBuffers; sl@0: if(iCodec) sl@0: {//Using a real Codec, need both sets of buffers sl@0: if(!iDataSink->CanCreateSinkBuffer() || ! iDataSource->CanCreateSourceBuffer()) sl@0: User::Leave(KErrNotSupported); sl@0: sl@0: buffs = CMMFDataPath::ENeedSinkBuffer | CMMFDataPath::ENeedSourceBuffer; sl@0: } sl@0: else //we are using a Null Codec, only need one buffer, but which one? sl@0: {//use buffer from DevSound, if no DevSound (ie, clip to clip), prefer source buffer. sl@0: //If preferring source but it can't create buffers, use sink. sl@0: if ((iDataSink->DataSinkType() == KUidMmfAudioOutput) && (iDataSink->CanCreateSinkBuffer())) sl@0: buffs = ENeedSinkBuffer; sl@0: else if(iDataSource->CanCreateSourceBuffer()) sl@0: buffs = ENeedSourceBuffer; sl@0: else if(iDataSink->CanCreateSinkBuffer()) sl@0: buffs = ENeedSinkBuffer; sl@0: else sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: return buffs; sl@0: } sl@0: sl@0: sl@0: sl@0: /* sl@0: * InitializeSinkL sl@0: * sl@0: * Function to initialize iDataSink before it can start sending data sl@0: * This is a one time prime. This will synchronize DataPath's data driving sl@0: * mechanism with HwDevice implementation. sl@0: * sl@0: * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine sl@0: * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF sl@0: */ sl@0: sl@0: void CMMFDataPath::InitializeSinkL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InitializeSinkL iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: sl@0: //state only used if we are passing data sl@0: __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: iObtainingAsyncSinkBuffer = EFalse; sl@0: sl@0: if (iBuffersToUse & ENeedSinkBuffer) sl@0: { sl@0: //Buffers are initially created in the Prime method. But following a pause, we must re-create sl@0: //any referenced buffers, so try direct creation. sl@0: //NB: this does mean we are trying this twice, Prime and here sl@0: if (!iSinkBuffer) //we may already have a buffer from a previous initialization sl@0: { sl@0: TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); sl@0: if(err != KErrNone && err != KErrNotSupported) sl@0: User::Leave(err); sl@0: } sl@0: sl@0: sl@0: //If buffer has not been supplied via CreateSinkBufferL, sl@0: //must use asynchronous buffer creation sl@0: if (!iSinkBuffer) sl@0: { sl@0: iObtainingAsyncSinkBuffer = ETrue; sl@0: ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink sl@0: iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId); sl@0: } sl@0: else sl@0: { sl@0: //we have a sink buffer from CreateSinkBufferL sl@0: iSinkBuffer->SetStatus(EAvailable); sl@0: sl@0: if (iBuffersToUse & ENeedSourceBuffer) sl@0: {//need a source buffer, go get it sl@0: ChangeDataPathTransferState(EInitializeSource); sl@0: } sl@0: else sl@0: {//only need one buffer, use sink sl@0: iSourceBuffer = iSinkBuffer; sl@0: iSrcBufRef = ETrue; //the src buffer is not to be deleted sl@0: sl@0: ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data sl@0: } sl@0: } sl@0: } sl@0: else sl@0: {//don't need a sink buffer, but we need a source one sl@0: ChangeDataPathTransferState(EInitializeSource); sl@0: } sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InitializeSinkL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: * InitializeSourceL sl@0: * sl@0: * Function to initialize iDataSource before it can start sending data sl@0: * This is a one time prime. This will synchronize DataPath's data driving sl@0: * mechanism with HwDevice implementation. sl@0: * sl@0: * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine sl@0: * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF sl@0: */ sl@0: sl@0: void CMMFDataPath::InitializeSourceL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: sl@0: //state only used if we are passing data sl@0: __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: iObtainingAsyncSourceBuffer = EFalse; sl@0: sl@0: if (iBuffersToUse & ENeedSourceBuffer) sl@0: { sl@0: //Buffers are initially created in the Prime method. But following a pause, we must re-create sl@0: //any referenced buffers, so try direct creation. sl@0: //NB: this does mean we are trying this twice, Prime and here. sl@0: if (!iSourceBuffer) //we may already have a buffer from a previous initialization sl@0: { sl@0: TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); sl@0: if(err != KErrNone && err != KErrNotSupported) sl@0: User::Leave(err); sl@0: } sl@0: sl@0: sl@0: //If buffer has not been supplied via CreateSourceBufferL sl@0: //must use asynchronous buffer creation sl@0: if (!iSourceBuffer) sl@0: { sl@0: iObtainingAsyncSourceBuffer = ETrue; sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: } sl@0: else sl@0: {//we have a source buffer from CreateSourceBufferL sl@0: iSourceBuffer->SetStatus(EAvailable); sl@0: sl@0: if (!(iBuffersToUse & ENeedSinkBuffer)) sl@0: {//only need one buffer, use sink sl@0: iSinkBuffer = iSourceBuffer; sl@0: iSnkBufRef = ETrue; sl@0: } sl@0: sl@0: ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data sl@0: } sl@0: } sl@0: else sl@0: {//don't need a source buffer, use sinks sl@0: if(iSinkBuffer) sl@0: { sl@0: iSourceBuffer = iSinkBuffer; sl@0: iSrcBufRef = iSnkBufRef; sl@0: SetBuffersAvailable(); sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: else sl@0: Panic(EMMFDataPathPanicProgrammingError,__LINE__); sl@0: #endif sl@0: ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data sl@0: sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * EmptySinkBufferL sl@0: * sl@0: * Function to pass a full databuffer to the iDataSink sl@0: */ sl@0: void CMMFDataPath::EmptySinkBufferL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::EmptySinkBufferL pass data to sink tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: if(iSinkBuffer) sl@0: RDebug::Print(_L("iSinkBuffer %d contains %d bytes eof = %d line %d (this 0x%x)\n"),iSinkBuffer->FrameNumber(), iSinkBuffer->BufferSize(),iSinkBuffer->LastBuffer(),__LINE__,this); sl@0: #endif sl@0: sl@0: //Before emptying the sink buffer we need to check it has data to empty - this sl@0: //may not be the case if there is no more data ie iNoMoreSourceData is true. sl@0: //In this case we need to check to see if there is any data left in the sink sl@0: //buffer, ie the sink buffer is either full or being filled. If there is not any sl@0: //data in the sink buffer, ie it is not in the state of EBeingFilled or EFull sl@0: //then there is nothing to empty so the datapath state is set to EEndOfData and sl@0: //we return from the procedure. sl@0: sl@0: //state only used if we are passing data sl@0: __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled)), Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: __ASSERT_DEBUG(iSinkBuffer && sl@0: ((iSinkBuffer->Status()==EBeingFilled) || (iSinkBuffer->Status()==EFull)), sl@0: Panic(EMMFDataPathPanicProgrammingError,__LINE__)); sl@0: sl@0: __ASSERT_DEBUG(iSinkBufferWithSink == EFalse, Panic(EMMFDataPathPanicBadState,__LINE__)); sl@0: sl@0: sl@0: //Due to sinks that may call BuferEmptied directly (ie. re-entrancy onto DataPath) we sl@0: //must work out next state here. If re-entrancy, the next state may validly get overwritten sl@0: // in BuferEmptied. sl@0: if(iObtainingAsyncSinkBuffer) //wait for buffer to be returned in BufferEmptied sl@0: { sl@0: ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink sl@0: } sl@0: sl@0: #ifdef REPOSITION_SPEEDUP sl@0: // if the source has been re-positioned, sl@0: // speed things up by getting some more source data now sl@0: if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::EmptySinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); sl@0: #endif sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: return; sl@0: } sl@0: #endif //REPOSITION_SPEEDUP sl@0: sl@0: //We have sent data to sink, if we are using a real Codec, we can now get more data from source sl@0: //if there is any more to get and the codec has emptied it. sl@0: //NB: No need to check we own the source buffer as we will no be in this state sl@0: //if we have asked for more source data and haven't received it. sl@0: else if (iCodec && !iNoMoreSourceData && (iSourceBuffer->Status() == EAvailable)) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("ASKING for more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource,this); sl@0: #endif sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: } sl@0: else sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("Not asking for any more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d iSourceBuffer->Status=%d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource ,iSourceBuffer->Status(), this); sl@0: #endif sl@0: sl@0: //if this is the last buffer, set this flag so we can deal with KErrUnderflow sl@0: //as a valid termination of playing sl@0: if(iSinkBuffer->LastBuffer()) sl@0: iAllDataSentToSink=ETrue; sl@0: sl@0: ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink sl@0: } sl@0: sl@0: sl@0: if(!iObtainingAsyncSinkBuffer) //normal data transfer sl@0: iSinkBuffer->SetFrameNumber(++iCurrentSinkFrameNumber); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP sending buffer %d ptr=0x%x of %d bytes to sink (this 0x%x)\n"), iSinkBuffer->FrameNumber(),iSinkBuffer,iSinkBuffer->BufferSize(),this); sl@0: #endif sl@0: sl@0: iSinkBufferWithSink = ETrue; sl@0: TRAPD(error, iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId)); sl@0: sl@0: // Check that we haven't exceeded the maximum clip length - if so, go to the EndOfData state sl@0: // so we perform necessary cleanup. sl@0: if (error == KErrEof || error == KErrOverflow || error == KErrUnderflow) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::EmptySinkBufferL DONE %d error = %d tick-%d (this 0x%x)\n"),__LINE__, error, User::TickCount(),this); sl@0: #endif sl@0: iDataPathCompletedErrorCode = error; sl@0: ChangeDataPathTransferState(EEndOfData); sl@0: return; sl@0: } sl@0: User::LeaveIfError(error); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::EmptySinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Indicates the data sink has emptied the buffer. sl@0: sl@0: Called by the CMMFDataPath's MDataSink when it has emptied the buffer sl@0: sl@0: @param aBuffer sl@0: The emptied buffer. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::BufferEmptiedL(CMMFBuffer* aBuffer) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: TInt bufNum = 9999; sl@0: if(aBuffer) sl@0: bufNum = aBuffer->FrameNumber(); sl@0: else sl@0: RDebug::Print(_L("DP::BufferEmptiedL returned NULL (this 0x%x)\n"),this); sl@0: sl@0: RDebug::Print(_L("DP::BufferEmptiedL sink has taken buffer %d (ptr=0x%x) bytes %d eof= %d iNoMoreSourceData = %d tick-%d (this 0x%x)\n"), sl@0: bufNum, aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), iNoMoreSourceData, User::TickCount(),this); sl@0: #endif sl@0: sl@0: iSinkBufferWithSink = EFalse; sl@0: sl@0: //Has the datapath stopped running, if so were not interested in any callbacks. sl@0: if(iState == EStopped || (iState == EPrimed && !iPauseCalled)) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferEmptiedL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); sl@0: #endif sl@0: return; sl@0: } sl@0: sl@0: sl@0: // This will allow MDataSink to send dynamic buffer to DataPath with each BufferEmptiedL request. sl@0: if (iSinkBuffer != aBuffer) //buffer has been updated sl@0: { sl@0: iSinkBuffer = aBuffer; sl@0: if (!(iBuffersToUse & ENeedSourceBuffer)) sl@0: { //can use a single buffer sl@0: iSourceBuffer = iSinkBuffer; sl@0: iSrcBufRef = iSnkBufRef; sl@0: } sl@0: sl@0: } sl@0: sl@0: iSinkBuffer->SetStatus(EAvailable); sl@0: sl@0: if (iObtainingAsyncSinkBuffer) //we are creating an asynchronous sink buffer sl@0: { sl@0: iObtainingAsyncSinkBuffer = EFalse; sl@0: sl@0: //we have a sink buffer, should this also be used by the source sl@0: if (!(iBuffersToUse & ENeedSourceBuffer)) sl@0: {//using a single buffer, so start getting data sl@0: iSourceBuffer = iSinkBuffer; sl@0: iSrcBufRef = iSnkBufRef; sl@0: sl@0: ChangeDataPathTransferState(ENeedSourceData); sl@0: } sl@0: else //obtain a separate source buffer sl@0: ChangeDataPathTransferState(EInitializeSource); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferEmptiedL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); sl@0: #endif sl@0: return; sl@0: } sl@0: sl@0: if(!iCodec) //No Codec in use sl@0: { sl@0: if(iNoMoreSourceData) sl@0: ChangeDataPathTransferState(EEndOfData);//final buffer returned from sink sl@0: else sl@0: ChangeDataPathTransferState(ENeedSourceData);//get more data from source sl@0: } sl@0: else //real codecs sl@0: { sl@0: //There is more source data and src buffer is being filled or we are about to go into sl@0: // ENeedSourceData state to fill it, so wait for source data to arrive. sl@0: //NB:if there was more source data and we are using a real codec and source buffer was empty, sl@0: //more source data would have been requested in EmptySinkBuffer sl@0: if(!iNoMoreSourceData && (iSourceBufferWithSource || iTransferState == ENeedSourceData)) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferEmptiedL - waiting for more source data - DONE tick-%d line %d (this 0x%x)\n"),User::TickCount(),__LINE__,this); sl@0: #endif sl@0: return; sl@0: } sl@0: sl@0: //source has supplied a NULL buffer or it has been emptied; no more data to send. sl@0: if(!iSourceBuffer || (iSourceBuffer->Status() == EAvailable)) sl@0: ChangeDataPathTransferState(EEndOfData); sl@0: else if(iSourceBuffer->Status() == EFull) //there is data in the source buffer, go and get it sl@0: ChangeDataPathTransferState(ENeedToMatchSourceToSink); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: else sl@0: Panic(EMMFDataPathPanicProgrammingError,__LINE__); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::BufferEmptiedL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Tests whether the data path can create a source buffer. sl@0: sl@0: Would expect datapath to always return NULL, so this is a default implementation of a pure virtual from MDataSink. sl@0: sl@0: The default implementation returns EFalse. sl@0: sl@0: @return ETrue if the data path can create a source buffer. EFalse if the data path cannot create a source buffer. sl@0: */ sl@0: EXPORT_C TBool CMMFDataPath::CanCreateSourceBuffer() //from both MDataSource & MDataSink? sl@0: { sl@0: return EFalse; //CMMFDataPath cannot create buffer sl@0: } sl@0: sl@0: /** sl@0: Creates a source buffer. sl@0: sl@0: Intended for synchronous usage (buffers supplied by datapath for a MDataSource) sl@0: This method is essentially a dummy implementation of an MDataSource pure virtual. sl@0: sl@0: The default implementation leaves with KErrNotSupported and returns NULL. sl@0: sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving of different media types. sl@0: @return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers sl@0: */ sl@0: EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/) //CMMFDataPath can't create buffers sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: return NULL; sl@0: } sl@0: sl@0: /** sl@0: Creates a source buffer according to the specifed media ID and reference. sl@0: sl@0: Intended for asynchronous usage (buffers supplied by datapath for a MDataSource) sl@0: This method is essentially a dummy implementation of an MDataSource pure virtual. sl@0: sl@0: The default implementation leaves with KErrNotSupported and returns NULL. sl@0: sl@0: @param aMediaId sl@0: An optional mediaID parameter when there are multiple buffers arriving of different media types. sl@0: @param aReference sl@0: A boolean indicating buffer ownership. ETrue if the MDataSource owns the buffer, EFalse if the caller owns the buffer. sl@0: sl@0: @return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers. sl@0: */ sl@0: EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) //CMMFDataPath can't create buffers sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: return NULL; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Gets the source data type for the specified media ID. sl@0: sl@0: @param aMediaId sl@0: An optional parameter to specifiy specific stream when datasource contains more than one stream of data. sl@0: sl@0: @return The source data type. sl@0: */ sl@0: EXPORT_C TFourCC CMMFDataPath::SourceDataTypeCode(TMediaId /*aMediaId*/) sl@0: { sl@0: return(iSourceFourCC); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Allocates buffers in preparation to play. sl@0: sl@0: Must be called before calling PlayL(). sl@0: sl@0: iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink sl@0: For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL(). sl@0: */ sl@0: sl@0: EXPORT_C void CMMFDataPath::PrimeL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::PrimeL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: //allocate resources ie buffers & prepare to play sl@0: if (iDataPathCreated && (iState == EStopped)) sl@0: //luckily the client utility does this. sl@0: {//can only prime from the stopped state sl@0: sl@0: //This will determine what buffers we need to run the datapath. sl@0: //Can leave KERRNotSupported sl@0: iBuffersToUse = DetermineBuffersToUseL(); sl@0: sl@0: //Try to create source and sink buffers. If we can't create them in the Prime, sl@0: //we will need to obtain them by asynchronous buffer creation when playing starts. sl@0: ObtainSyncBuffersL(); sl@0: sl@0: iDataSource->SourcePrimeL(); //propogate state change to source sl@0: iDataSink->SinkPrimeL(); //propogate state change to sink sl@0: sl@0: sl@0: //If Client has set these, they will be set following the prime sl@0: iPlayWindowStartPosition = 0; sl@0: iPlayWindowEndPosition = Duration(); sl@0: sl@0: iState = EPrimed; sl@0: iPauseCalled = EFalse; sl@0: sl@0: if (iCompleteCallback) sl@0: { sl@0: delete iCompleteCallback; sl@0: iCompleteCallback = NULL; sl@0: } sl@0: TBool waitForSink = (iDataSink->DataSinkType() == KUidMmfAudioOutput)?ETrue:EFalse; sl@0: iCompleteCallback = new (ELeave) CCompleteCallback(*this,waitForSink); sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::PrimeL Done tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Starts an active scheduler 'play' loop. sl@0: sl@0: Can only play from the primed state. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::PlayL() sl@0: { sl@0: #if defined(__PROFILING) sl@0: RDebug::ProfileEnd(1); sl@0: #endif // defined(__PROFILING) sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::PlayL, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); sl@0: RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64())); sl@0: #endif sl@0: sl@0: if ((iDataPathCreated) && (iState == EPrimed)) sl@0: { sl@0: //can only play from the primed state sl@0: sl@0: TBool savedPauseCalled=EFalse; sl@0: if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed sl@0: { sl@0: savedPauseCalled=ETrue; sl@0: iDataSink->SinkPrimeL(); //propagate change down to sink sl@0: iPauseCalled = EFalse; sl@0: } sl@0: sl@0: iCurrentSourceFrameNumber = 0; //reset to beginning sl@0: iCurrentSinkFrameNumber = 0; //reset to beginning sl@0: sl@0: iSourceBufferWithSource = EFalse; sl@0: iSinkBufferWithSink = EFalse; sl@0: sl@0: iNoMoreSourceData = EFalse; sl@0: iAllDataSentToSink=EFalse; sl@0: iDataPathCompletedErrorCode=KErrNone; sl@0: sl@0: SetPositionL( iStartPosition ) ; sl@0: iReferenceAudioSamplesPlayed = 0; sl@0: iReferenceAudioSamplesRecorded = 0; sl@0: sl@0: //complete a request on iStatus to invoke play code sl@0: iDataSource->SourcePlayL(); //propagate state change to source sl@0: if (!(savedPauseCalled && (iTransferState==EWaitSink || iTransferState==EInitializeSink))) sl@0: { sl@0: iDataSink->SinkPlayL(); //propogate state change to sink sl@0: } sl@0: sl@0: if (iDataSink->DataSinkType() == KUidMmfAudioOutput) sl@0: iState = EPlaying; sl@0: else if(iDataSource->DataSourceType() == KUidMmfAudioInput) sl@0: iState = ERecording; sl@0: else sl@0: iState = EConverting; sl@0: sl@0: //need to re-initialize any buffer(s) that we only own references to sl@0: ChangeDataPathTransferState(EInitializeSink); sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::Play - DONE\n")); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Pauses playing. sl@0: sl@0: Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::Pause() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::Pause, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); sl@0: RDebug::Print(_L("DP::Pause current state=%d tick-%d (this 0x%x)\n"),iTransferState, User::TickCount(),this); sl@0: #endif sl@0: sl@0: TRAPD(err, DoPauseL()); sl@0: sl@0: if (err) sl@0: { sl@0: DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::Pause - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: /** sl@0: Stops playing. sl@0: sl@0: Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError sl@0: to the client if an error occurs. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::Stop() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::Stop current state=%d tick-%d (this 0x%x)\n"), iTransferState, User::TickCount(),this); sl@0: #endif sl@0: sl@0: if ((iDataPathCreated) && (iState != EStopped)) sl@0: { sl@0: TRAPD(err, DoStopL()); sl@0: sl@0: if (err) sl@0: DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); sl@0: } sl@0: } sl@0: /** sl@0: Forces and end of data state on the datapath sl@0: */ sl@0: EXPORT_C void CMMFDataPath::EndOfData() sl@0: { sl@0: TRAPD(err, DoEndOfDataL()); sl@0: sl@0: if (err) sl@0: { sl@0: DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: sl@0: */ sl@0: TInt CMMFDataPath::AudioSamplesPlayed() const sl@0: { sl@0: if (iDataSink->DataSinkType() != KUidMmfAudioOutput) sl@0: return 0; sl@0: sl@0: CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); sl@0: sl@0: TInt samples = 0; sl@0: sl@0: if(iState == EPlaying) sl@0: samples = audioOutput->SoundDevice().SamplesPlayed(); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::AudioSamplesPlayed = %d\n"),samples); sl@0: #endif sl@0: sl@0: return samples; sl@0: } sl@0: sl@0: sl@0: /** sl@0: sl@0: */ sl@0: TInt CMMFDataPath::AudioSamplesRecorded() const sl@0: { sl@0: if (iDataSource->DataSourceType() != KUidMmfAudioInput) sl@0: return 0; sl@0: sl@0: CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); sl@0: sl@0: TInt samples = 0; sl@0: sl@0: if(iState == ERecording) sl@0: samples = audioInput->SoundDevice().SamplesRecorded(); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::AudioSamplesRecorded = %d\n"),samples); sl@0: #endif sl@0: sl@0: return samples; sl@0: } sl@0: sl@0: sl@0: /** sl@0: sl@0: */ sl@0: TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioOutputPosition() const sl@0: { sl@0: //This operation can only be carried out on an Audio Output sl@0: __ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); sl@0: sl@0: sl@0: //If we are not playing, simply return where we will play from sl@0: if(iState != EPlaying || iCurrentSinkFrameNumber == 0) sl@0: return iStartPosition; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed); sl@0: #endif sl@0: sl@0: TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed; sl@0: sl@0: CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); sl@0: CMMFDevSound& devSound = audioOutput->SoundDevice(); sl@0: sl@0: TMMFCapabilities devSoundConfig = devSound.Config(); sl@0: sl@0: TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); sl@0: #endif sl@0: sl@0: TReal timePlayedSeconds = 0; sl@0: if(samplesPlayed) sl@0: timePlayedSeconds = samplesPlayed/samplingFreq; sl@0: sl@0: TInt64 timePlayed(I64DOUBLECAST(timePlayedSeconds * 1000000)); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed)); sl@0: #endif sl@0: sl@0: return TTimeIntervalMicroSeconds(timePlayed + iStartPosition.Int64()); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: sl@0: */ sl@0: TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioInputPosition() const sl@0: { sl@0: //This operation can only be carried out on an Audio Input sl@0: __ASSERT_ALWAYS(iDataSource->DataSourceType() == KUidMmfAudioInput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); sl@0: sl@0: sl@0: //If we are not playing, simply return where we will play from sl@0: if(iState != ERecording) sl@0: return iStartPosition; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::CalculateAudioInputPosition from %d\n"),iReferenceAudioSamplesRecorded); sl@0: #endif sl@0: sl@0: TReal samplesRecorded = AudioSamplesRecorded() - iReferenceAudioSamplesRecorded; sl@0: sl@0: CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); sl@0: CMMFDevSound& devSound = audioInput->SoundDevice(); sl@0: sl@0: TMMFCapabilities devSoundConfig = devSound.Config(); sl@0: sl@0: TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); sl@0: #endif sl@0: sl@0: TReal timeRecordedSeconds = 0; sl@0: if(samplesRecorded) sl@0: timeRecordedSeconds = samplesRecorded/samplingFreq; sl@0: sl@0: TInt64 timeRecorded(I64DOUBLECAST(timeRecordedSeconds * 1000000)); sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("timeRecorded %d\n"), I64LOW(timeRecorded)); sl@0: #endif sl@0: return TTimeIntervalMicroSeconds(timeRecorded); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: TTimeIntervalMicroSeconds CMMFDataPath::OutputPosition() const sl@0: { sl@0: TTimeIntervalMicroSeconds interval; sl@0: TTimeIntervalMicroSeconds position; sl@0: sl@0: if (iDataSink->DataSinkType() == KUidMmfAudioOutput) sl@0: { sl@0: position = CalculateAudioOutputPosition(); sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64())); sl@0: #endif sl@0: } sl@0: else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) sl@0: { sl@0: //note Encode format position takes priority if both source & sink are formats? sl@0: // try to get the position directly from the format. If that fails, work it out here sl@0: TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL()); sl@0: if (error)//getting the position from the format didn't work so calculate it here sl@0: { sl@0: interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); sl@0: TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber; sl@0: position = position64; sl@0: } sl@0: sl@0: TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration sl@0: if (position > duration)//this can happen on last buffer sl@0: position = duration; sl@0: sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::OutputPosition from format= %d\n"),I64INT(position.Int64())); sl@0: #endif sl@0: } sl@0: else sl@0: {//can only read output position if sink is a format or an audio output sl@0: return TTimeIntervalMicroSeconds(0); sl@0: } sl@0: sl@0: return position; sl@0: } sl@0: sl@0: TTimeIntervalMicroSeconds CMMFDataPath::InputPosition() const sl@0: { sl@0: TTimeIntervalMicroSeconds interval; sl@0: TTimeIntervalMicroSeconds position; sl@0: sl@0: if (iDataSource->DataSourceType() == KUidMmfAudioInput) sl@0: { sl@0: position = CalculateAudioInputPosition(); sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InputPosition from audio input= %d\n"),I64INT(position.Int64())); sl@0: #endif sl@0: } sl@0: else if (iDataSource->DataSourceType() == KUidMmfFormatDecode) sl@0: {//note Decode format position takes priority if both source & sink are formats? sl@0: // try to get the position directly from the format. If that fails, work it out here sl@0: TRAPD(error, position = ((CMMFFormatDecode*)iDataSource)->PositionL()); sl@0: if (error)//getting the position from the format didn't work so calculate it here sl@0: { sl@0: interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); sl@0: TInt64 position64 = interval.Int64() * iCurrentSourceFrameNumber; sl@0: position = position64; sl@0: } sl@0: sl@0: TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration sl@0: if (position > duration)//this can happen on last buffer sl@0: position = duration; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::InputPosition from format = %d\n"),I64INT(position.Int64())); sl@0: #endif sl@0: } sl@0: else sl@0: {//can only read input position if source is a format or an audio input sl@0: return TTimeIntervalMicroSeconds(0); sl@0: } sl@0: sl@0: return position; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: Gets the data path position. sl@0: sl@0: @return The data path position. sl@0: */ sl@0: EXPORT_C TTimeIntervalMicroSeconds CMMFDataPath::Position() const sl@0: { sl@0: if ((iState == ERecording) || (iState == EConverting)) sl@0: return InputPosition(); sl@0: else if(iState == EPlaying) sl@0: return OutputPosition(); sl@0: else sl@0: { sl@0: return iStartPosition; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Sets the data path position. sl@0: sl@0: @param aPosition sl@0: The data path position. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::SetPositionL(const TTimeIntervalMicroSeconds& aPosition) sl@0: {//need to map to source position to frame position sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::SetPositionL = %d ticks-%d (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this); sl@0: #endif sl@0: sl@0: if (iState == EStopped) sl@0: User::Leave(KErrNotReady); //can only set position if primed sl@0: sl@0: //As this will affect the position, we need to know how many bytes were sl@0: //played when position was updated. Future Position() requests will sl@0: //then use this refernce to determine the current position. sl@0: iReferenceAudioSamplesPlayed = AudioSamplesPlayed(); sl@0: sl@0: // Force the new position to be inside the play window (also within the file duration) sl@0: if ( aPosition < iPlayWindowStartPosition ) sl@0: iStartPosition = iPlayWindowStartPosition; sl@0: else if ( aPosition > iPlayWindowEndPosition ) sl@0: iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played sl@0: else sl@0: iStartPosition = aPosition; sl@0: sl@0: TTimeIntervalMicroSeconds interval; sl@0: sl@0: //can only set the position on an MDataSource that is a format object sl@0: //Note: position defaults to source if both source & sink are clips sl@0: if (iDataSource->DataSourceType() == KUidMmfFormatDecode) sl@0: { sl@0: //position is not beyond the end of file sl@0: interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); sl@0: sl@0: // for some reason this code won't compile without these intermediate steps sl@0: TInt64 position = iStartPosition.Int64(); sl@0: TInt64 interval64 = interval.Int64(); sl@0: if (interval64 == 0) sl@0: User::Leave(KErrDivideByZero); sl@0: TInt64 datapos64 = position/interval64; sl@0: iCurrentSourceFrameNumber = I64LOW(datapos64); sl@0: sl@0: sl@0: // Try to set the position directly on the format sl@0: TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition)); sl@0: //NB: don't worry about error, since we'll reposition anyway when we get the next buffer sl@0: } sl@0: else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) sl@0: { sl@0: //position is not beyond the end of file sl@0: interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); sl@0: sl@0: //convert to TUint - for some reason it won't compile without these intermediate steps sl@0: TInt64 position = iStartPosition.Int64(); sl@0: TInt64 interval64 = interval.Int64(); sl@0: if (interval64 == 0) sl@0: User::Leave(KErrDivideByZero); sl@0: TInt64 datapos64 = position/interval64; sl@0: iCurrentSinkFrameNumber = I64LOW(datapos64); sl@0: sl@0: sl@0: // Try to set the position directly on the format sl@0: TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition)); sl@0: //NB: don't worry about error, since we'll reposition anyway when we get the next buffer sl@0: } sl@0: else sl@0: {//can only set position if source or sink is a format sl@0: //If both source and sink are formats position is relative to the source sl@0: User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips sl@0: } sl@0: sl@0: if(iCodec) //we have a real codec, must reset it sl@0: iCodec->ResetL(); // Need to preserve sync when resuming play sl@0: sl@0: // Once we've sent the last buffer to the sink it's too late to start sl@0: // changing the state since we may get a RunError(KErrUnderflow) at any time. sl@0: // Once this happens, the sound driver may have unloaded etc..and recovery sl@0: // would be complicated. sl@0: if (iAllDataSentToSink) sl@0: return; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d iStartPosition=%d ticks-%d (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: Sets the play window absolutely (i.e. the parameters are relative to the start of the entire clip). sl@0: sl@0: @param aStart sl@0: The offset from the start of the Clip sl@0: @param aEnd sl@0: The offset from the end of the clip (if this is less than aStart, then the two will be inverted). sl@0: */ sl@0: EXPORT_C void CMMFDataPath::SetPlayWindowL( const TTimeIntervalMicroSeconds& aStart, const TTimeIntervalMicroSeconds& aEnd ) sl@0: { sl@0: // Clear the existing Play window sl@0: ClearPlayWindowL() ; sl@0: sl@0: // Check that the parameters are legitimate. 0 <= startpos < endpos <= duration & update member variables sl@0: TTimeIntervalMicroSeconds duration = Duration(); sl@0: sl@0: if ( aStart < TTimeIntervalMicroSeconds(0) ) sl@0: iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0); sl@0: else if ( aStart > duration ) sl@0: iPlayWindowStartPosition = duration; sl@0: else iPlayWindowStartPosition = aStart; sl@0: sl@0: if ( aEnd < TTimeIntervalMicroSeconds(0) ) sl@0: iPlayWindowEndPosition = TTimeIntervalMicroSeconds(0); sl@0: else if ( aEnd > duration ) sl@0: iPlayWindowEndPosition = duration; sl@0: else iPlayWindowEndPosition = aEnd; sl@0: sl@0: // ensure that the current position is inside the new play window sl@0: if ( iPlayWindowEndPosition != TTimeIntervalMicroSeconds(0) ) sl@0: { sl@0: TTimeIntervalMicroSeconds currentPosition = Position() ; sl@0: if ( currentPosition < iPlayWindowStartPosition ) sl@0: SetPositionL( iPlayWindowStartPosition ) ; sl@0: else if ( currentPosition > iPlayWindowEndPosition ) sl@0: SetPositionL( iPlayWindowEndPosition ) ; sl@0: } sl@0: else sl@0: ClearPlayWindowL() ; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::SetPlayWindowL iPlayWindowStartPosition=%d iPlayWindowEndPosition=%d\n"),I64INT(iPlayWindowStartPosition.Int64()),I64INT(iPlayWindowEndPosition.Int64())); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Sets the play window to the full length of clip. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::ClearPlayWindowL() sl@0: { sl@0: iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0) ; sl@0: iPlayWindowEndPosition = Duration(); sl@0: sl@0: sl@0: if(iState == EStopped) sl@0: iStartPosition = iPlayWindowStartPosition; sl@0: } sl@0: sl@0: /** sl@0: Returns the current data path state. sl@0: */ sl@0: EXPORT_C TInt CMMFDataPath::State() sl@0: { sl@0: return iState ; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Uses the AO mechanism to drive state changes between sources and sinks. sl@0: sl@0: RunL() moves and assigns buffers between its attached MDataSource/MDataSinks. sl@0: */ sl@0: void CMMFDataPath::ChangeDataPathTransferState(TTransferState aNewDataPathTransferState) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: switch (aNewDataPathTransferState) sl@0: { sl@0: case EWaitSink: sl@0: RDebug::Print(_L("Next State EWaitSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case EWaitSource: sl@0: RDebug::Print(_L("Next State EWaitSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case EInitializeSink: sl@0: RDebug::Print(_L("Next State EInitializeSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case EInitializeSource: sl@0: RDebug::Print(_L("Next State EInitializeSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case ENeedSourceData: sl@0: RDebug::Print(_L("Next State ENeedSourceData ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case ENeedSinkData: sl@0: RDebug::Print(_L("Next State ENeedSinkData ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case ENeedToMatchSourceToSink: sl@0: RDebug::Print(_L("Next State ENeedToMatchSourceToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case ESendDataToSink: sl@0: RDebug::Print(_L("Next State ESendDataToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: case EEndOfData: sl@0: RDebug::Print(_L("Next State EEndOfData ticks-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: break; sl@0: } sl@0: #endif sl@0: sl@0: sl@0: TRequestStatus* stat = &iStatus; sl@0: //change state sl@0: iTransferState = aNewDataPathTransferState; sl@0: if ((iTransferState != EWaitSink) && (iTransferState != EWaitSource) && sl@0: (iState == EPlaying || iState == ERecording || iState == EConverting || (iState == EPrimed && iPauseCalled))) sl@0: {//can go ahead with transfer sl@0: if (!IsActive()) sl@0: { sl@0: User::RequestComplete(stat, KErrNone); sl@0: SetActive(); sl@0: } sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: else sl@0: RDebug::Print(_L("Datapath is no longer active, not going to new state (this 0x%x)\n"),this); sl@0: #endif sl@0: } sl@0: sl@0: /** sl@0: Runs the clip depending on the current data path and transfer state. sl@0: sl@0: For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::RunL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::RunL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: switch (iState) sl@0: { sl@0: case EStopped: sl@0: break; sl@0: case EPrimed: sl@0: //paused with stored position sl@0: break; sl@0: case EPlaying: sl@0: case ERecording: sl@0: case EConverting: sl@0: switch (iTransferState) sl@0: { sl@0: case EWaitSink: sl@0: case EWaitSource: sl@0: break; sl@0: case EInitializeSink: sl@0: InitializeSinkL(); sl@0: break; sl@0: case EInitializeSource: sl@0: InitializeSourceL(); sl@0: break; sl@0: case ENeedSourceData: sl@0: FillSourceBufferL(); sl@0: break; sl@0: case ENeedSinkData: sl@0: FillSinkBufferL(); sl@0: break; sl@0: case ENeedToMatchSourceToSink: sl@0: FillSinkBufferL(); sl@0: break; sl@0: case ESendDataToSink: sl@0: EmptySinkBufferL(); sl@0: break; sl@0: case EEndOfData: sl@0: EndOfData(); sl@0: break; sl@0: } sl@0: break; sl@0: default: sl@0: break; sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::RunL DONE\n")); sl@0: #endif sl@0: } sl@0: sl@0: /** sl@0: Cancels the clip. sl@0: sl@0: The default implementation is empty. sl@0: */ sl@0: EXPORT_C void CMMFDataPath::DoCancel() sl@0: { sl@0: //don't need to do anything as we don't have any async requests to other objects sl@0: } sl@0: sl@0: /** sl@0: Handles errors coming from attached sources and passes them to the clients. sl@0: sl@0: @param aError sl@0: Standard error code (KErrNone = No Error). sl@0: sl@0: @return The event code returned to the data path. KErrNone if end of file is encountered. sl@0: */ sl@0: EXPORT_C TInt CMMFDataPath::RunError(TInt aError) sl@0: { sl@0: return DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, aError); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Returns the duration of the clip. sl@0: sl@0: @return The length of clip in TTimeIntervalMicroSeconds. sl@0: */ sl@0: TTimeIntervalMicroSeconds CMMFDataPath::Duration() const sl@0: { sl@0: TTimeIntervalMicroSeconds duration(0); sl@0: sl@0: if ( iDataSource && ( iDataSource->DataSourceType() == KUidMmfFormatDecode ) ) sl@0: { sl@0: //this updated version of datapath caches the duration of the sl@0: //source clip for efficiency - since this meathod is const sl@0: //we need to cast away the constness to set iCachedSourceDuration sl@0: CMMFDataPath* thisNonConst = const_cast(this); sl@0: duration = STATIC_CAST(CMMFFormatDecode*, thisNonConst->iDataSource )->Duration( iMediaId ) ; sl@0: thisNonConst->iCachedSourceDuration = duration; sl@0: } sl@0: else if ( iDataSink && ( iDataSink->DataSinkType() == KUidMmfFormatEncode ) ) sl@0: duration = STATIC_CAST(CMMFFormatEncode*, iDataSink )->Duration( iMediaId ) ; sl@0: sl@0: return duration ; sl@0: } sl@0: sl@0: /** sl@0: This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. sl@0: sl@0: Additional Stop() method specific to this datapath. sl@0: */ sl@0: void CMMFDataPath::DoStopL() sl@0: { sl@0: // Note that the datapath needs to be paused first sl@0: // before it can be stopped - this is important for audio play sl@0: // for instance to effect an immediate stop rather than playing out sl@0: // the current buffer. sl@0: sl@0: sl@0: sl@0: // Stop data source and sink sl@0: iDataSource->SourceStopL(); // propagate state change to source sl@0: iDataSink->SinkStopL(); // propagate change down to sink sl@0: sl@0: iSinkBufferWithSink = EFalse; sl@0: iSourceBufferWithSource = EFalse; sl@0: sl@0: //Contains the completion code if the datapath completes as a result of an error sl@0: iDataPathCompletedErrorCode = KErrNone; sl@0: sl@0: ResetRefBuffers(); // buffer references may not be valid any more sl@0: sl@0: SetPositionL(iPlayWindowStartPosition); // reset position sl@0: sl@0: iState = EStopped; // stop succeeded, set state to stopped sl@0: } sl@0: sl@0: /** sl@0: This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. sl@0: sl@0: Additional Pause method specific to this datapath. sl@0: sl@0: The DataPath implements pause by recording the current position and stopping the source and sink. sl@0: When Play is called the DataPath uses the stored position as the starting point and resumes sl@0: playing. The reason for this (rather than using the PauseL() API on sources and sinks) is that sl@0: some implementations do not support a suitable pause, therefore the DataPath is implemented to sl@0: support the lowest common denominator. sl@0: sl@0: Note: sl@0: A suitable pause implementation will retain any buffers in use. There will be no sl@0: need to call PrimeL() prior to PlayL(). sl@0: */ sl@0: void CMMFDataPath::DoPauseL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::DoPauseL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: sl@0: if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting))) sl@0: { sl@0: // try to pause source and sink sl@0: iDataSource->SourcePauseL(); // propagate state change to source sl@0: SetPositionL(Position()); sl@0: iDataSink->SinkStopL(); sl@0: sl@0: iPauseCalled = ETrue; // indicate pause is called sl@0: sl@0: iState = EPrimed; // go back to primed state (we're not playing) sl@0: sl@0: iSinkBufferWithSink = EFalse; sl@0: iSourceBufferWithSource = EFalse; sl@0: sl@0: ResetRefBuffers(); // buffer references may not be valid any more sl@0: sl@0: Cancel(); //Stop the state machine sl@0: } sl@0: else if(iState == ERecording) sl@0: { sl@0: iPauseCalled = ETrue; sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n")); sl@0: #endif sl@0: iDataSource->SourcePauseL(); sl@0: } sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed); sl@0: RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: /** sl@0: This is a virtual function datapath (or derivations off) that can be implemented or may be left blank for default behaviour. sl@0: sl@0: Additional Pause method specific to this datapath. sl@0: */ sl@0: void CMMFDataPath::DoEndOfDataL() sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::DoEndOfDataL tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: SetPositionL(iPlayWindowStartPosition); // reset position sl@0: sl@0: ASSERT(iCompleteCallback); sl@0: iCompleteCallback->SignalDataPathComplete(iDataPathCompletedErrorCode); sl@0: sl@0: ResetRefBuffers(); sl@0: iState = EStopped; sl@0: sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("DP::DoEndOfDataL - Done tick-%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /** sl@0: Passes error handling and general messages up to clients. sl@0: sl@0: @param aEventType sl@0: Category code for the event. Category codes can be used as unique identifers. sl@0: @param aErrorCode sl@0: Standard error code. sl@0: sl@0: @return The event code sent to client. sl@0: */ sl@0: //error handling sl@0: EXPORT_C TInt CMMFDataPath::DoSendEventToClient(TUid aEventType, TInt aErrorCode) sl@0: { sl@0: TMMFEvent event(aEventType, aErrorCode); sl@0: return iEventHandler.SendEventToClient(event); sl@0: } sl@0: sl@0: /** sl@0: Passes error handling and general messages to clients. sl@0: sl@0: @param aEvent sl@0: TMMFEvent supplied by callee (typically DoSendEventToClient) sl@0: sl@0: @return The Event Message sent to the datapath event handler sl@0: */ sl@0: EXPORT_C TInt CMMFDataPath::SendEventToClient(const TMMFEvent& aEvent) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::SendEventToClient CODE = %d ticks=%d (this 0x%x)\n"),aEvent.iErrorCode,User::TickCount(),this); sl@0: RDebug::Print(_L("CMMFDataPath::SendEventToClient iEventType = %d == %d\n"),aEvent.iEventType, KMMFEventCategoryPlaybackComplete); sl@0: #endif sl@0: sl@0: //If we have sent all the data to the sink, it is legal for it to send KErrUnderFlow sl@0: //to us and we can go through a clean shutdown, otherwise we pass the error to the sl@0: //controller and it is responsible for determining what to do sl@0: if(iAllDataSentToSink && sl@0: (aEvent.iEventType == KMMFEventCategoryPlaybackComplete) && sl@0: (aEvent.iErrorCode == KErrUnderflow)) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::SendEventToClient Clean complete\n")); sl@0: #endif sl@0: //sink may not return the final buffer once it has finished with it sl@0: //force ourselves into the EndOfData state sl@0: if(iTransferState != EEndOfData) sl@0: { sl@0: iDataPathCompletedErrorCode = KErrNone; sl@0: TRAP_IGNORE(DoEndOfDataL()); sl@0: } sl@0: sl@0: iCompleteCallback->SignalSinkComplete(KErrNone); sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: sl@0: return iEventHandler.SendEventToClient(aEvent); sl@0: } sl@0: sl@0: CMMFDataPath::CCompleteCallback::CCompleteCallback(CMMFDataPath& aDataPath, TBool aWaitForSink) sl@0: : CActive(EPriorityStandard), sl@0: iDataPath(aDataPath), sl@0: iWaitForSink(aWaitForSink) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: CMMFDataPath::CCompleteCallback::~CCompleteCallback() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: void CMMFDataPath::CCompleteCallback::SignalDataPathComplete(TInt aDataPathError) sl@0: { sl@0: iDataPathComplete = ETrue; sl@0: iDataPathError = aDataPathError; sl@0: if (!IsActive()) sl@0: { sl@0: // Signal ourselves to run with the given completion code sl@0: TRequestStatus* status = &ActiveStatus(); sl@0: User::RequestComplete(status, KErrNone); sl@0: } sl@0: } sl@0: sl@0: void CMMFDataPath::CCompleteCallback::SignalSinkComplete(TInt aSinkError) sl@0: { sl@0: iSinkComplete = ETrue; sl@0: iSinkError = aSinkError; sl@0: if (!IsActive()) sl@0: { sl@0: // Signal ourselves to run with the given completion code sl@0: TRequestStatus* status = &ActiveStatus(); sl@0: User::RequestComplete(status, KErrNone); sl@0: } sl@0: } sl@0: sl@0: sl@0: TRequestStatus& CMMFDataPath::CCompleteCallback::ActiveStatus() sl@0: { sl@0: SetActive(); sl@0: return iStatus; sl@0: } sl@0: sl@0: void CMMFDataPath::CCompleteCallback::DoCancel() sl@0: { sl@0: } sl@0: sl@0: void CMMFDataPath::CCompleteCallback::RunL() sl@0: { sl@0: if (iWaitForSink) sl@0: { sl@0: if (iDataPathComplete && iSinkComplete) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: TRAP_IGNORE(iDataPath.DoStopL()) sl@0: sl@0: iDataPathComplete = EFalse; sl@0: iSinkComplete = EFalse; sl@0: sl@0: // if we have to wait for the sink to complete, always use the sink error sl@0: iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iSinkError); sl@0: } sl@0: } sl@0: else if (iDataPathComplete) sl@0: { sl@0: #ifdef _DP_DEBUG sl@0: RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); sl@0: #endif sl@0: sl@0: TRAP_IGNORE(iDataPath.DoStopL()) sl@0: sl@0: iDataPathComplete = EFalse; sl@0: iSinkComplete = EFalse; sl@0: sl@0: iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iDataPathError); sl@0: } sl@0: } sl@0: sl@0: EXPORT_C TInt CMMFDataPath::SetBlockLength(TUint aBlockLength) sl@0: { sl@0: MMMFDevSoundCustomInterfaceFileBlockLength* fileBlockLengthCI = NULL; sl@0: TInt err = KErrNotSupported; sl@0: if (iCodec) sl@0: { sl@0: err = iCodec->ExtensionInterface(KUidCustomInterfaceDevSoundFileBlockLength.iUid, (TAny*&)fileBlockLengthCI); sl@0: } sl@0: sl@0: if (err == KErrNone) sl@0: { sl@0: fileBlockLengthCI->SetFileBlockLength(aBlockLength); sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: