1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmlibs/mmfw/src/server/BaseClasses/mmfdatapath.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,2394 @@
1.4 +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// source\server\mmfdatapath.cpp
1.18 +//
1.19 +//
1.20 +
1.21 +#include <e32math.h>
1.22 +#include <mmf/common/mmffourcc.h>
1.23 +#include <mmf/common/mmfpaniccodes.h>
1.24 +#include <mmf/server/mmfaudiooutput.h>
1.25 +#include <mmf/server/mmfaudioinput.h>
1.26 +#include <mmf/server/mmfdatapath.h>
1.27 +#include "mmfclientaudiostreamutils.h"
1.28 +#include <mmf/common/mmfaudio.h>
1.29 +#include <mmf/plugin/mmfcodecimplementationuids.hrh> // KUidMmfCodecAudioSettings
1.30 +#include <mmf/server/devsoundstandardcustominterfaces.h>
1.31 +
1.32 +const TUid KUidCodecAudioConfig = {KUidMmfCodecAudioSettings};
1.33 +
1.34 +void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber)
1.35 + {
1.36 + _LIT(KMMFDataPathPanicCategory, "MMFDataPath");
1.37 + User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber);
1.38 + }
1.39 +
1.40 +//all functions are exported form the DLL and are virtual to allow plugins to define there own CMMFDataPaths
1.41 +
1.42 +/**
1.43 +Allocates and constructs a data path.
1.44 +
1.45 +Use this function if the codec UID is not already known by CMMFController
1.46 +and there is no data path ambiguity - ie only one data path is possible.
1.47 +
1.48 +Will create codec via fourCC.
1.49 +
1.50 +@param aEventHandler
1.51 + Installs an event handler to provide message passing between clients and sources/sinks.
1.52 +
1.53 +@return Newly constructed data path object.
1.54 +*/
1.55 +
1.56 +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(MAsyncEventHandler& aEventHandler)
1.57 + {
1.58 + CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler);
1.59 + CleanupStack::PushL(self);
1.60 + self->ConstructL();
1.61 + CleanupStack::Pop();
1.62 + return self;
1.63 + }
1.64 +
1.65 +
1.66 +/**
1.67 +Allocates and constructs a data path according to the specified media ID.
1.68 +
1.69 +Use this function if the codec UID is not already known by CMMFController
1.70 +and there is ambiguity with the data path ie. there is more than one possible data path.
1.71 +
1.72 +@param aMediaId
1.73 + Optional media ID parameter when there are multiple media types.
1.74 +@param aEventHandler
1.75 + Installs an event handler to provide message passing between clients and sources/sinks.
1.76 +
1.77 +@return A newly constructed data path object.
1.78 +*/
1.79 +
1.80 +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
1.81 + {
1.82 + CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler);
1.83 + CleanupStack::PushL(self);
1.84 + self->ConstructL();
1.85 + CleanupStack::Pop();
1.86 + return self;
1.87 + }
1.88 +
1.89 +/**
1.90 +Allocates and constructs a data path according to the specified codec UID.
1.91 +
1.92 +Use this function if the codec UID is already known by CMMFController
1.93 +and there is no data path ambiguity ie. only one data path is possible
1.94 +will create codec explicitly using the supplied codec Uid
1.95 +
1.96 +@param aCodecUid
1.97 + Optional mediaID parameter when there are multiple media types
1.98 +@param aEventHandler
1.99 + Installs an event handler to provide message passing between clients and sources/sinks.
1.100 +
1.101 +@return A newly constructed data path object.
1.102 +*/
1.103 +
1.104 +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler)
1.105 + {
1.106 + CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler);
1.107 + CleanupStack::PushL(self);
1.108 + self->ConstructL(aCodecUid);
1.109 + CleanupStack::Pop();
1.110 + return self;
1.111 + }
1.112 +
1.113 +
1.114 +/**
1.115 +Allocates and constructs a data path according to the specified codec UID.
1.116 +
1.117 +Use this function if the codec UID is already known by CMMFController
1.118 +and there is ambiguity ie. more than one possible data path.
1.119 +TMediaId used to select the path.
1.120 +
1.121 +@param aCodecUid
1.122 + The codec UID.
1.123 +@param aMediaId
1.124 + Optional mediaID parameter when there are multiple media types.
1.125 +@param aEventHandler
1.126 + Installs an event handler to provide message passing between clients and sources/sinks.
1.127 +
1.128 +@return A newly constructed data path object.
1.129 +*/
1.130 +EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
1.131 + {
1.132 + CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler);
1.133 + CleanupStack::PushL(self);
1.134 + self->ConstructL(aCodecUid);
1.135 + CleanupStack::Pop();
1.136 + return self;
1.137 + }
1.138 +
1.139 +/**
1.140 +Standard destructor.
1.141 +*/
1.142 +
1.143 +EXPORT_C CMMFDataPath::~CMMFDataPath()
1.144 + {
1.145 + Cancel();
1.146 + delete iCodec;
1.147 + DoCleanupBuffers();
1.148 +
1.149 + //log off the source and sink
1.150 + if (iDataSource)
1.151 + iDataSource->SourceThreadLogoff();
1.152 + if (iDataSink)
1.153 + iDataSink->SinkThreadLogoff();
1.154 +
1.155 + if (iCompleteCallback)
1.156 + {
1.157 + iCompleteCallback->Cancel();
1.158 + delete iCompleteCallback;
1.159 + }
1.160 + }
1.161 +
1.162 +/**
1.163 +Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL().
1.164 +Typically if buffers are created asychronously, the datapath doesn't own the buffer
1.165 +so leaves cleanup handling to the owner sources/sinks.
1.166 +
1.167 +Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive,
1.168 +iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped.
1.169 +*/
1.170 +EXPORT_C void CMMFDataPath::ResetL()
1.171 + {
1.172 + delete iCodec;
1.173 + iCodec = NULL;
1.174 + DoCleanupBuffers(); // Delete buffers
1.175 + //logoff and dereference source and sink
1.176 + if (iDataSource)
1.177 + { iDataSource->SourceThreadLogoff(); iDataSource = NULL; }
1.178 + if (iDataSink)
1.179 + { iDataSink->SinkThreadLogoff(); iDataSink = NULL; }
1.180 +
1.181 + // Reset states
1.182 + iDataPathCreated = EFalse;
1.183 + iState = EStopped;
1.184 + iSrcBufRef = EFalse;
1.185 + iSnkBufRef = EFalse;
1.186 + iPauseCalled = EFalse;
1.187 +
1.188 + delete iCompleteCallback; iCompleteCallback = NULL;
1.189 + }
1.190 +
1.191 +/**
1.192 +Delete source and/or sink buffers that are owned by DataPath.
1.193 +
1.194 +Ownership indicated by iSrcBufRef and iSnkBufRef.
1.195 +
1.196 +Ownership is assigned during buffer allocation within the datapath PrimeL().
1.197 +*/
1.198 +void CMMFDataPath::DoCleanupBuffers()
1.199 + {
1.200 + // delete source and/or sink buffer that is owned by DataPath
1.201 + if ( !iSrcBufRef && iSourceBuffer )
1.202 + {
1.203 + delete iSourceBuffer;
1.204 + }
1.205 + iSourceBuffer = NULL;
1.206 + if ( !iSnkBufRef && iSinkBuffer )
1.207 + {
1.208 + delete iSinkBuffer;
1.209 + }
1.210 + iSinkBuffer = NULL;
1.211 + }
1.212 +
1.213 +
1.214 +/**
1.215 +Obtain source and/or sink buffer using the synchronous API CreateSourceBufferL() and CreateSinkBufferL().
1.216 +*/
1.217 +void CMMFDataPath::ObtainSyncBuffersL()
1.218 + {
1.219 + //Try to create source and sink buffers. If we can't create them synchronously via
1.220 + //CreateSourceBufferL and CreateSinkBufferL we will need to obtain them by
1.221 + //asynchronous buffer creation when playing starts.
1.222 +
1.223 + if (iBuffersToUse & ENeedSourceBuffer)
1.224 + {
1.225 + if (!iSourceBuffer) //we may already have a buffer from a previous initialization
1.226 + {
1.227 + TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef));
1.228 + if(err != KErrNone && err != KErrNotSupported)
1.229 + {
1.230 +#ifdef _DP_DEBUG
1.231 + RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this);
1.232 +#endif
1.233 + User::Leave(err);
1.234 + }
1.235 + }
1.236 + }
1.237 +
1.238 +
1.239 + if (iBuffersToUse & ENeedSinkBuffer)
1.240 + {
1.241 + if (!iSinkBuffer) //we may already have a buffer from a previous initialization
1.242 + {
1.243 + TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef));
1.244 + if(err != KErrNone && err != KErrNotSupported)
1.245 + {
1.246 +#ifdef _DP_DEBUG
1.247 + RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this);
1.248 +#endif
1.249 + User::Leave(err);
1.250 + }
1.251 + }
1.252 + }
1.253 +
1.254 + if (iSourceBuffer && !(iBuffersToUse & ENeedSinkBuffer))
1.255 + {//only need one buffer, use source
1.256 + iSinkBuffer =iSourceBuffer;
1.257 + iSnkBufRef = ETrue; //the sink buffer is not to be deleted
1.258 + }
1.259 + else if (iSinkBuffer && !(iBuffersToUse & ENeedSourceBuffer))
1.260 + {//only need one buffer, use sink
1.261 + iSourceBuffer =iSinkBuffer;
1.262 + iSrcBufRef = ETrue; //the sink buffer is not to be deleted
1.263 + }
1.264 +
1.265 +#ifdef _DP_DEBUG
1.266 + 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);
1.267 +#endif
1.268 + }
1.269 +
1.270 +
1.271 +
1.272 +
1.273 +
1.274 +/**
1.275 +Constructs a source.
1.276 +
1.277 +The default implementation leaves with KErrNotSupported.
1.278 +
1.279 +@param aInitData
1.280 + The initialisation data.
1.281 +*/
1.282 +
1.283 +EXPORT_C void CMMFDataPath::ConstructSourceL( const TDesC8& /*aInitData*/ )
1.284 + {
1.285 + User::Leave(KErrNotSupported);
1.286 + }
1.287 +
1.288 +/**
1.289 +Constructs a sink.
1.290 +
1.291 +Overridable constuction specific to this datasource.
1.292 +
1.293 +The default implementation leaves with KErrNotSupported.
1.294 +
1.295 +@param aInitData
1.296 + The initialisation data.
1.297 +*/
1.298 +EXPORT_C void CMMFDataPath::ConstructSinkL( const TDesC8& /*aInitData*/ )
1.299 + {
1.300 + User::Leave(KErrNotSupported);
1.301 + }
1.302 +
1.303 +/**
1.304 +Takes UID of codec on construction, and if not an NULL codec sets the datapath up for codec instantiation.
1.305 +
1.306 +@param aCodecUid
1.307 + The UID of the codec.
1.308 +*/
1.309 +
1.310 +EXPORT_C void CMMFDataPath::ConstructL(TUid aCodecUid)
1.311 + {
1.312 + iUseSuppliedCodecUid = EFalse; //initially assume no supplied codec uid
1.313 +
1.314 + if (aCodecUid != KNullUid)
1.315 + {//the data path NewL has specified a specific codec
1.316 + //create CMMFCodec here
1.317 + iCodec = CMMFCodec::NewL(aCodecUid);
1.318 + if (iCodec)
1.319 + iUseSuppliedCodecUid = ETrue;
1.320 + }
1.321 +
1.322 + iSrcBufRef = EFalse;
1.323 + iSnkBufRef = EFalse;
1.324 + iObtainingAsyncSourceBuffer = EFalse;
1.325 + iObtainingAsyncSinkBuffer = EFalse;
1.326 + iSourceBufferWithSource = EFalse;
1.327 + iSinkBufferWithSink = EFalse;
1.328 + }
1.329 +
1.330 +
1.331 +/**
1.332 +Adds a data source to the datapath and, if the sink already exists, tries to establish a connection
1.333 +between the source and sink.
1.334 +
1.335 +@param aSource
1.336 + The data source to add to the data path.
1.337 +*/
1.338 +EXPORT_C void CMMFDataPath::AddDataSourceL(MDataSource* aSource)
1.339 + {
1.340 + if (!iDataSink) iDataSource=aSource; //can't create a data path without the MDataSink as well
1.341 + else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path
1.342 + {//we have a data sink as well so check a data path can be established between source&sink
1.343 + CreateDataPathL(aSource, iDataSink);
1.344 + iDataSource = aSource;
1.345 + }
1.346 + else //the CMMFController specified the codec uid so must use existing codec
1.347 + {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid
1.348 + //can make the appropriate data conversion
1.349 + iDataPathCreated = ETrue;
1.350 + iDataSource = aSource;
1.351 + }
1.352 + ClearPlayWindowL() ;
1.353 + User::LeaveIfError(iDataSource->SourceThreadLogon(*this));
1.354 + }
1.355 +
1.356 +
1.357 +/**
1.358 +Adds a data sink to the datapath and, if the source already exists, tries to establish a connection
1.359 +between the source and sink.
1.360 +
1.361 +@param aSink
1.362 + The data sink to add to the data path.
1.363 +*/
1.364 +
1.365 +EXPORT_C void CMMFDataPath::AddDataSinkL(MDataSink* aSink)
1.366 + {
1.367 + if (!iDataSource) iDataSink=aSink; //can't create a data path without the MDataSource as well
1.368 + else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path
1.369 + {//we have a data source as well so check a media path can be established between source&sink
1.370 + CreateDataPathL(iDataSource, aSink);
1.371 + iDataSink = aSink;
1.372 + }
1.373 + else //the CMMFController specified the codec uid so must use existing codec
1.374 + {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid
1.375 + //can make the appropriate data conversion
1.376 + iDataPathCreated = ETrue;
1.377 + iDataSink = aSink;
1.378 +
1.379 + //set 4CCs
1.380 + iSourceFourCC = iDataSink->SinkDataTypeCode(iMediaId);//sink because CMMFDataPath is an MDataSink to its MDataSource!
1.381 + iSinkFourCC = iDataSource->SourceDataTypeCode(iMediaId);//source because CMMFDataPath is an MDataSource to its MDataSink!
1.382 + }
1.383 + User::LeaveIfError(iDataSink->SinkThreadLogon(*this));
1.384 + }
1.385 +
1.386 +
1.387 +/*
1.388 + * CreateDataPathL
1.389 + *
1.390 + * internal function to establish a datapath between the source and sink
1.391 + * the data supplied by the sink adn expected by the source are checked and
1.392 + * a codec is instantiated if necessary
1.393 + *
1.394 + * @param aSource
1.395 + * @param aSink
1.396 + */
1.397 +
1.398 +void CMMFDataPath::CreateDataPathL(MDataSource* aSource, MDataSink* aSink)
1.399 + { //procedure to attempt to match the source to the sink creating a codec if necessary
1.400 + // returns ETrue if the datapath could be constructed else false
1.401 + //sets iCodec to the appropriate codec.& sets its own iSink/iSource FourCC datatype codes
1.402 + iDataPathCreated = EFalse;
1.403 + if (aSource && aSink) //have a source and sink
1.404 + { //we have a data source & sink but no codec so try and find one - if required
1.405 + TFourCC sourceFourCCCode = aSource->SourceDataTypeCode(iMediaId); //get MDataSource data type fourCC code
1.406 + TFourCC sinkFourCCCode = aSink->SinkDataTypeCode(iMediaId); //get MDataSink data type fourCC code
1.407 + if ((sourceFourCCCode != sinkFourCCCode) && //MDataSource & MDataSink datatypes are not compatible
1.408 + (sourceFourCCCode != KMMFFourCCCodeNULL) && (sinkFourCCCode != KMMFFourCCCodeNULL))
1.409 + {//we need a codec to make the conversion between the source and the sink
1.410 + CMMFCodec* codec = CMMFCodec::NewL(sourceFourCCCode, sinkFourCCCode);
1.411 +
1.412 + if (codec)
1.413 + {
1.414 + delete iCodec;
1.415 + iCodec = codec;
1.416 + //data path created ie have source/sink and can match their datatypes
1.417 + iDataPathCreated = ETrue;
1.418 +
1.419 + //now we have an source attached we need to configure the codec for sample rate
1.420 + //and number of channels
1.421 +
1.422 + //prepare a package to send to a codec
1.423 + TMMFAudioConfig audioSettings;
1.424 +
1.425 + //test for decoder
1.426 + if (aSource->DataSourceType() == KUidMmfFormatDecode)
1.427 + {
1.428 + audioSettings.iSampleRate = static_cast<CMMFFormatDecode*>(aSource)->SampleRate();
1.429 + audioSettings.iChannels = static_cast<CMMFFormatDecode*>(aSource)->NumChannels();
1.430 + }
1.431 +
1.432 + //package up to send to codec
1.433 + TPckgBuf<TMMFAudioConfig> configPackage(audioSettings);
1.434 +
1.435 + //we need to catch User::Leave(KErrNotSupported) as by default most codecs
1.436 + //do not support the ConfigureL method.
1.437 + TRAPD(err,iCodec->ConfigureL(KUidCodecAudioConfig, configPackage));
1.438 + // need to check other error here
1.439 + if (err != KErrNone && err != KErrNotSupported)
1.440 + {
1.441 + User::Leave(err);
1.442 + }
1.443 + }
1.444 + else
1.445 + {
1.446 + User::Leave( KErrNotSupported ) ; //couldn't find suitable codec
1.447 + }
1.448 + } //if (sourceFourCCCode != sinkFourCCCode)
1.449 + else
1.450 + { //source & sink fourCC datatypes are the same so no codec is required
1.451 + __ASSERT_DEBUG(iCodec == NULL, Panic(EMMFDataPathPanicProgrammingError,__LINE__));
1.452 +
1.453 + iDataPathCreated = ETrue;
1.454 + }
1.455 + //can assign FourCC codes for the CMMFDataPath
1.456 + iSinkFourCC = sourceFourCCCode; //sink because CMMFDataPath is an MDataSink to its MDataSource!
1.457 + iSourceFourCC = sinkFourCCCode; //source because CMMFDataPath is an MDataSource to its MDataSink!
1.458 + //If sink & source need its own Prime() done in controller
1.459 + }
1.460 + }
1.461 +
1.462 +/**
1.463 +Clears the specified buffer.
1.464 +
1.465 +Pure virtual dummy implementation, not needed by datapath
1.466 +comes from MDataSink - CMMFData path is a sink to its MDataSource.
1.467 +
1.468 +This is only required for an active push MDataSource requesting a buffer empty.
1.469 +
1.470 +@param aBuffer
1.471 + The buffer to empty.
1.472 +@param aSupplier
1.473 + The MDataSource supplying this buffer.
1.474 +@param aMediaId
1.475 + An optional mediaID parameter when there are multiple buffers arriving of different media types.
1.476 +
1.477 +*/
1.478 +EXPORT_C void CMMFDataPath::EmptyBufferL(CMMFBuffer* /* aBuffer */, MDataSource* /*aSupplier*/, TMediaId /*aMediaId*/)
1.479 + {
1.480 + //not implemented
1.481 + }
1.482 +
1.483 +
1.484 +
1.485 +/*
1.486 + * FillSourceBufferL
1.487 + *
1.488 + * Function to get data from the datapath's iDataSource
1.489 + */
1.490 +
1.491 +void CMMFDataPath::FillSourceBufferL()
1.492 + {
1.493 +#ifdef _DP_DEBUG
1.494 + RDebug::Print(_L("DP::FillSourceBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.495 +#endif
1.496 +
1.497 + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__));
1.498 +
1.499 +
1.500 + // clear the no-more-source flag here (as well as in PlayL()) because
1.501 + // there may have been a re-position since the last call to BufferFilledL()
1.502 + iNoMoreSourceData = EFalse;
1.503 +
1.504 + if(!iObtainingAsyncSourceBuffer)
1.505 + {//this is a normal request for data.
1.506 + //If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL
1.507 + iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with
1.508 + iSourceBuffer->SetStatus(EBeingFilled);
1.509 + iSourceBuffer->SetLastBuffer(EFalse);
1.510 + }
1.511 +
1.512 +#ifdef _DP_DEBUG
1.513 + RDebug::Print(_L("DP asking for buffer %d - ptr=0x%x (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this);
1.514 +#endif
1.515 +
1.516 + iSourceBufferWithSource = ETrue;
1.517 +
1.518 + // wait for BufferFilled callback from source. Do this here as some sources cause
1.519 + //re-entrancy into data path via BufferFilledL
1.520 + ChangeDataPathTransferState(EWaitSource);
1.521 +
1.522 + iDataSource->FillBufferL(iSourceBuffer, this, iMediaId);
1.523 +
1.524 +#ifdef _DP_DEBUG
1.525 + RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.526 +#endif
1.527 + }
1.528 +
1.529 +
1.530 +/**
1.531 +Indicates the data source has filled the specified buffer.
1.532 +
1.533 +Called by the CMMFDataPath's MDataSource when it has filled the buffer.
1.534 +
1.535 +@param aBuffer
1.536 + A pointer to the filled buffer.
1.537 +*/
1.538 +EXPORT_C void CMMFDataPath::BufferFilledL(CMMFBuffer* aBuffer)
1.539 + {
1.540 +#ifdef _DP_DEBUG
1.541 + 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);
1.542 +#endif
1.543 +
1.544 + //This assertion is commented because of PDEF117405
1.545 + //state only used if we are passing data
1.546 + //__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__));
1.547 +
1.548 + __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__));
1.549 +
1.550 + iSourceBufferWithSource = EFalse;
1.551 +
1.552 + //Has the datapath stopped running, if so were not interested in any callbacks.
1.553 + if(iState == EStopped || iState == EPrimed || (iPauseCalled && iState != ERecording))
1.554 + {
1.555 +#ifdef _DP_DEBUG
1.556 + RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this);
1.557 +#endif
1.558 + return;
1.559 + }
1.560 +
1.561 +#ifdef REPOSITION_SPEEDUP
1.562 + // if the source has been re-positioned, then go & get some more source data now
1.563 + if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber)
1.564 + {
1.565 +#ifdef _DP_DEBUG
1.566 + RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this);
1.567 +#endif
1.568 + ChangeDataPathTransferState(ENeedSourceData);
1.569 + return;
1.570 + }
1.571 +#endif //REPOSITION_SPEEDUP
1.572 +
1.573 + //bufer is NULL, indicating no more source data.
1.574 + if (!aBuffer)
1.575 + {
1.576 + //If we only hold a reference to the source buffer, set that to NULL
1.577 + if(iSnkBufRef)
1.578 + iSourceBuffer = NULL;
1.579 +
1.580 +
1.581 + iNoMoreSourceData = ETrue;
1.582 +
1.583 + if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data
1.584 + iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data
1.585 + {
1.586 + ChangeDataPathTransferState(EEndOfData);
1.587 + }
1.588 + else //sink buffer is with datapath, see if there is anything to send to sink
1.589 + ChangeDataPathTransferState(ENeedToMatchSourceToSink);
1.590 +
1.591 +#ifdef _DP_DEBUG
1.592 + RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.593 +#endif
1.594 + return;
1.595 + }
1.596 +
1.597 +
1.598 + //We were waiting for a response from the source to get an asynchronous buffer.
1.599 + //We now have it, and we proceed to transfer this data to the sink.
1.600 + if (iObtainingAsyncSourceBuffer)
1.601 + {
1.602 + iObtainingAsyncSourceBuffer = EFalse;
1.603 + }
1.604 +
1.605 +
1.606 + aBuffer->SetStatus(EFull);
1.607 +
1.608 + if(iSourceBuffer != aBuffer)
1.609 + {//buffer has been changed by the source
1.610 + iSourceBuffer = aBuffer;
1.611 + if (!(iBuffersToUse & ENeedSinkBuffer))
1.612 + {//we only need one buffer and use source
1.613 + iSinkBuffer = iSourceBuffer;
1.614 + iSnkBufRef = ETrue;
1.615 + }
1.616 +#ifdef _DP_DEBUG
1.617 + RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
1.618 +#endif
1.619 + }
1.620 + //Is this the last buffer from the source (0 length or LastBuffer flag set)
1.621 + //or have reached the end of the play window; we only look at the play window here
1.622 + //if we are converting. For conversion we look at the data we have read. This is then passed onto
1.623 + //the source
1.624 + if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() ||
1.625 + (((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition )))
1.626 + {
1.627 +#ifdef _DP_DEBUG
1.628 + RDebug::Print(_L("DP::BufferFilledL end of input data tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.629 + RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize());
1.630 + RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer());
1.631 + RDebug::Print(_L("InputPosition()=%d >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64()));
1.632 +#endif
1.633 + iNoMoreSourceData = ETrue;
1.634 + iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window
1.635 + }
1.636 +
1.637 +
1.638 + if (!iCodec)
1.639 + ChangeDataPathTransferState(ESendDataToSink);
1.640 + else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it
1.641 + ChangeDataPathTransferState(ENeedToMatchSourceToSink);
1.642 + //else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state
1.643 +
1.644 +#ifdef _DP_DEBUG
1.645 + RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.646 +#endif
1.647 + }
1.648 +
1.649 +
1.650 +
1.651 +
1.652 +/*
1.653 + * FillSinkBufferL
1.654 + *
1.655 + * Function to take the data from an already full source buffer and by using
1.656 + * a codec if necessary fills the sink buffer
1.657 + */
1.658 +
1.659 +void CMMFDataPath::FillSinkBufferL()
1.660 + {
1.661 +#ifdef _DP_DEBUG
1.662 + RDebug::Print(_L("DP::FillSinkBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.663 +#endif
1.664 +
1.665 + //This state is only used if we are passing data
1.666 + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__));
1.667 +
1.668 + //This state is only used if we have a real codec
1.669 + __ASSERT_DEBUG(iCodec, Panic(EMMFDataPathPanicBadState,__LINE__));
1.670 +
1.671 +
1.672 + //The sink buffer is with the sink so we can't fill it.
1.673 + //When it has been emptied, this state will be re-entered from BufferEmptiedL
1.674 + if(iSinkBufferWithSink)
1.675 + {
1.676 +#ifdef _DP_DEBUG
1.677 + RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SINK - DONE (this 0x%x)\n"),this);
1.678 +#endif
1.679 + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink
1.680 + return;
1.681 + }
1.682 +
1.683 + //The source buffer is with the source so we can't take data from it.
1.684 + //When it has been filled, this state will be re-entered from BufferFilledL
1.685 + if(iSourceBufferWithSource)
1.686 + {
1.687 +#ifdef _DP_DEBUG
1.688 + RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SOURCE - DONE (this 0x%x)\n"),this);
1.689 +#endif
1.690 + ChangeDataPathTransferState(EWaitSource); // wait for BufferFilled callback from source
1.691 + return;
1.692 + }
1.693 +
1.694 + //source buffer is NULL, can't be any more data to send.
1.695 + //iNoMoreSourceData is set and the source buffer is empty, can't be any more data to send.
1.696 + if(!iSourceBuffer || (iNoMoreSourceData && !iSourceBuffer->BufferSize()))
1.697 + {
1.698 + if(iSinkBuffer->Status() == EBeingFilled)
1.699 + {//if we have data in sink buffer, mark it as last buffer and send
1.700 + iSinkBuffer->SetLastBuffer(ETrue);
1.701 + ChangeDataPathTransferState(ESendDataToSink);
1.702 + }
1.703 + else //the sink buffer can't have anything in it
1.704 + ChangeDataPathTransferState(EEndOfData);
1.705 + }
1.706 +
1.707 +#ifdef REPOSITION_SPEEDUP
1.708 + // if the source has been re-positioned,
1.709 + // speed things up by getting some more source data now
1.710 + if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber)
1.711 + {
1.712 +#ifdef _DP_DEBUG
1.713 + RDebug::Print(_L("DP::FillSinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this);
1.714 +#endif
1.715 + ChangeDataPathTransferState(ENeedSourceData);
1.716 + return;
1.717 + }
1.718 +#endif //REPOSITION_SPEEDUP
1.719 +
1.720 + iSinkBuffer->SetStatus(EBeingFilled);
1.721 + iSinkBuffer->SetLastBuffer(EFalse);
1.722 +
1.723 + //pass buffer to codec for processing
1.724 + iCodecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSinkBuffer);
1.725 + //the codec tries to fill the sink buffer to its max length
1.726 + //TCodecProcessResult returns the status of the codec Process -
1.727 + //this can result in result conditions such as:
1.728 + //EProcessComplete - the codec processed all the source data into the sink buffer
1.729 + //EProcessIncomplete - the codec filled sink buffer before all the source buffer was processed
1.730 + //EDstNotFilled - the codec processed the source buffer but the sink buffer was not filled
1.731 + //EEndOfData - the codec detected the end data - all source data in processed but sink may not be full
1.732 + //EProcessError - the codec process error condition
1.733 +
1.734 +
1.735 + switch (iCodecProcessResult.iStatus)
1.736 + {
1.737 + case TCodecProcessResult::EProcessComplete:
1.738 + //finished procesing source data - all data in sink buffer
1.739 + {
1.740 +#ifdef _DP_DEBUG
1.741 + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessComplete (this 0x%x)\n"),this);
1.742 +#endif
1.743 + iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble
1.744 + iSinkBuffer->SetStatus(EFull); //sink buffer is full
1.745 + if (iNoMoreSourceData)
1.746 + iSinkBuffer->SetLastBuffer(ETrue);
1.747 + ChangeDataPathTransferState(ESendDataToSink);// the full sink buffer needs to be sent to the sink
1.748 + }
1.749 + break;
1.750 + case TCodecProcessResult::EProcessIncomplete:
1.751 + // the sink was filled before all the src was processed
1.752 + // therefore still send everything to sink
1.753 + //but datapath needs to carry on processing the source buffer before it gets more source data
1.754 + //when sink has emptied data path needs to send rest of data
1.755 + {
1.756 +#ifdef _DP_DEBUG
1.757 + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessIncomplete (this 0x%x)\n"),this);
1.758 +#endif
1.759 + TUint sourceBufferPosition = iCodecProcessResult.iSrcBytesProcessed + iSourceBuffer->Position();
1.760 + iSourceBuffer->SetPosition(sourceBufferPosition);//update source buffer position
1.761 + iSinkBuffer->SetStatus(EFull); //sink & source buffers are both full
1.762 + ChangeDataPathTransferState(ESendDataToSink); // the full sink buffer needs to be sent to the sink
1.763 + }
1.764 + break;
1.765 + case TCodecProcessResult::EDstNotFilled:
1.766 + // the destination is not full
1.767 + {
1.768 +#ifdef _DP_DEBUG
1.769 + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EDstNotFilled (this 0x%x)\n"),this);
1.770 +#endif
1.771 + iSourceBuffer->SetStatus(EAvailable); //source buffer is now available
1.772 + TUint sinkBufferPosition = iCodecProcessResult.iDstBytesAdded + iSinkBuffer->Position();
1.773 + iSinkBuffer->SetPosition(sinkBufferPosition);//update sink buffer position (still EBeingFilled)
1.774 + // if this was the last source buffer, send what we've got (if anything)
1.775 + // to the sink... EmptySinkBuffer() should then enter EEndOfData state
1.776 + if (iNoMoreSourceData)
1.777 + {
1.778 + iSinkBuffer->SetLastBuffer(ETrue);
1.779 + ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink -
1.780 + }
1.781 + else
1.782 + {
1.783 + ChangeDataPathTransferState(ENeedSourceData); //need to get more source data to fill sink buffer
1.784 + }
1.785 + }
1.786 + break;
1.787 + case TCodecProcessResult::EEndOfData:
1.788 + //no more data - send what we've got to the sink
1.789 + //note we can't always rely on this - in many cases the codec will not know when
1.790 + //it has reached the end of data.
1.791 + {
1.792 +#ifdef _DP_DEBUG
1.793 + RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EEndOfData (this 0x%x)\n"),this);
1.794 +#endif
1.795 + iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble
1.796 + iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
1.797 +
1.798 + //This only occurs where the codec can detect the end of data, but the source can't
1.799 + iNoMoreSourceData=ETrue;
1.800 + iSinkBuffer->SetLastBuffer(ETrue);
1.801 +
1.802 + ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink -
1.803 + //doesn't matter if sink buffer is not full
1.804 + }
1.805 + break;
1.806 + case TCodecProcessResult::EProcessError:
1.807 +#ifdef _DP_DEBUG
1.808 + RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this);
1.809 +#endif
1.810 + User::Leave(KErrCorrupt); //codec process error
1.811 + break;
1.812 + default:
1.813 +#ifdef _DP_DEBUG
1.814 + RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this);
1.815 +#endif
1.816 + User::Leave(KErrCorrupt); //should never get here
1.817 + }
1.818 +#ifdef _DP_DEBUG
1.819 + RDebug::Print(_L("DP::FillSinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.820 +#endif
1.821 + }
1.822 +
1.823 +
1.824 +
1.825 +/**
1.826 +Tests whether the data path can create a sink buffer.
1.827 +
1.828 +The default implementation returns false.
1.829 +
1.830 +@return ETrue if the data path can create a sink buffer. EFalse if the data path cannot create a sink buffer.
1.831 +*/
1.832 +EXPORT_C TBool CMMFDataPath::CanCreateSinkBuffer()
1.833 + {
1.834 + return NULL; //CMMFDataPath cannot create buffer
1.835 + }
1.836 +
1.837 +/**
1.838 +Creates a sink buffer according to the specifed media ID.
1.839 +
1.840 +Intended for synchronous usage (buffers supplied by datapath for an MDataSink).
1.841 +This method is essentially a dummy implementation of an MDataSink pure virtual.
1.842 +
1.843 +The default implementation returns NULL.
1.844 +
1.845 +@param aMediaId
1.846 + An optional mediaID parameter when there are multiple buffers arriving of different media types.
1.847 +
1.848 +@return Returns NULL in this instance as datapath can't create sink buffers
1.849 +*/
1.850 +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/)
1.851 + {//CMMFDataPath can't create buffers
1.852 + return NULL;
1.853 + }
1.854 +
1.855 +/**
1.856 +Creates a sink buffer according to the specifed media ID and reference.
1.857 +
1.858 +Intended for asynchronous usage (buffers supplied by Devsound device).
1.859 +This method is essentially a dummy implementation of an MDataSink pure virtual.
1.860 +
1.861 +The default implementation returns NULL.
1.862 +
1.863 +@param aMediaId
1.864 + An optional mediaID parameter when there are multiple buffers arriving for different media types.
1.865 +@param aReference
1.866 + A boolean indicating buffer ownership.
1.867 +
1.868 +@return Returns NULL in this instance as datapath can't create sink buffers.
1.869 +*/
1.870 +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/)
1.871 + {//CMMFDataPath can't create buffers
1.872 + return NULL;
1.873 + }
1.874 +
1.875 +/**
1.876 +Gets the sink's data type for the specified media ID.
1.877 +
1.878 +@param aMediaId
1.879 + An optional parameter to specifiy the specific stream when datasource contains more than one stream of data
1.880 +@return The sink's data type.
1.881 +*/
1.882 +EXPORT_C TFourCC CMMFDataPath::SinkDataTypeCode(TMediaId /*aMediaId*/)
1.883 + {
1.884 + return(iSinkFourCC);
1.885 + }
1.886 +
1.887 +/**
1.888 +Fills the specified buffer.
1.889 +
1.890 +Pure virtual dummy implementation, not needed by datapath
1.891 +comes from MDataSink - CMMFData path is a source to its MDataSink
1.892 +
1.893 +Only required for an active pull MDataSink requesting a buffer fill. The default implementation is empty.
1.894 +
1.895 +@param aBuffer
1.896 + The buffer to fill.
1.897 +@param aConsumer
1.898 + The MDataSink supplying this buffer.
1.899 +@param aMediaId
1.900 + An optional mediaID parameter when there are multiple buffers arriving of different media types
1.901 +*/
1.902 +EXPORT_C void CMMFDataPath::FillBufferL(CMMFBuffer* /*aBuffer*/, MDataSink* /*aConsumer*/, TMediaId /*aMediaId*/)
1.903 + {
1.904 + //not implementated
1.905 + }
1.906 +
1.907 +void CMMFDataPath::SetBuffersAvailable()
1.908 + {
1.909 + // set source buffer to be available
1.910 + if (iSourceBuffer)
1.911 + iSourceBuffer->SetStatus(EAvailable);
1.912 + // set sink buffer to be available
1.913 + if (iSinkBuffer)
1.914 + iSinkBuffer->SetStatus(EAvailable);
1.915 + }
1.916 +
1.917 +void CMMFDataPath::ResetRefBuffers()
1.918 + {
1.919 +#ifdef _DP_DEBUG
1.920 + RDebug::Print(_L("DP::ResetRefBuffers iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
1.921 +#endif
1.922 +
1.923 + // Reset the buffer pointers to NULL if they are supplied by DevSound
1.924 + // We do this because buffers that are not owned by the datapath may not be valid any more.
1.925 + if (iSrcBufRef)
1.926 + {
1.927 + iSourceBuffer = NULL;
1.928 + }
1.929 + if (iSnkBufRef)
1.930 + {
1.931 + iSinkBuffer = NULL;
1.932 + }
1.933 + }
1.934 +
1.935 +
1.936 +
1.937 +
1.938 +TInt CMMFDataPath::DetermineBuffersToUseL(void) const
1.939 + {
1.940 + TInt buffs = ENoBuffers;
1.941 + if(iCodec)
1.942 + {//Using a real Codec, need both sets of buffers
1.943 + if(!iDataSink->CanCreateSinkBuffer() || ! iDataSource->CanCreateSourceBuffer())
1.944 + User::Leave(KErrNotSupported);
1.945 +
1.946 + buffs = CMMFDataPath::ENeedSinkBuffer | CMMFDataPath::ENeedSourceBuffer;
1.947 + }
1.948 + else //we are using a Null Codec, only need one buffer, but which one?
1.949 + {//use buffer from DevSound, if no DevSound (ie, clip to clip), prefer source buffer.
1.950 + //If preferring source but it can't create buffers, use sink.
1.951 + if ((iDataSink->DataSinkType() == KUidMmfAudioOutput) && (iDataSink->CanCreateSinkBuffer()))
1.952 + buffs = ENeedSinkBuffer;
1.953 + else if(iDataSource->CanCreateSourceBuffer())
1.954 + buffs = ENeedSourceBuffer;
1.955 + else if(iDataSink->CanCreateSinkBuffer())
1.956 + buffs = ENeedSinkBuffer;
1.957 + else
1.958 + User::Leave(KErrNotSupported);
1.959 + }
1.960 + return buffs;
1.961 + }
1.962 +
1.963 +
1.964 +
1.965 +/*
1.966 + * InitializeSinkL
1.967 + *
1.968 + * Function to initialize iDataSink before it can start sending data
1.969 + * This is a one time prime. This will synchronize DataPath's data driving
1.970 + * mechanism with HwDevice implementation.
1.971 + *
1.972 + * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine
1.973 + * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF
1.974 + */
1.975 +
1.976 +void CMMFDataPath::InitializeSinkL()
1.977 + {
1.978 +#ifdef _DP_DEBUG
1.979 + RDebug::Print(_L("DP::InitializeSinkL iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
1.980 +#endif
1.981 +
1.982 + //state only used if we are passing data
1.983 + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__));
1.984 +
1.985 + iObtainingAsyncSinkBuffer = EFalse;
1.986 +
1.987 + if (iBuffersToUse & ENeedSinkBuffer)
1.988 + {
1.989 + //Buffers are initially created in the Prime method. But following a pause, we must re-create
1.990 + //any referenced buffers, so try direct creation.
1.991 + //NB: this does mean we are trying this twice, Prime and here
1.992 + if (!iSinkBuffer) //we may already have a buffer from a previous initialization
1.993 + {
1.994 + TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef));
1.995 + if(err != KErrNone && err != KErrNotSupported)
1.996 + User::Leave(err);
1.997 + }
1.998 +
1.999 +
1.1000 + //If buffer has not been supplied via CreateSinkBufferL,
1.1001 + //must use asynchronous buffer creation
1.1002 + if (!iSinkBuffer)
1.1003 + {
1.1004 + iObtainingAsyncSinkBuffer = ETrue;
1.1005 + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink
1.1006 + iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId);
1.1007 + }
1.1008 + else
1.1009 + {
1.1010 + //we have a sink buffer from CreateSinkBufferL
1.1011 + iSinkBuffer->SetStatus(EAvailable);
1.1012 +
1.1013 + if (iBuffersToUse & ENeedSourceBuffer)
1.1014 + {//need a source buffer, go get it
1.1015 + ChangeDataPathTransferState(EInitializeSource);
1.1016 + }
1.1017 + else
1.1018 + {//only need one buffer, use sink
1.1019 + iSourceBuffer = iSinkBuffer;
1.1020 + iSrcBufRef = ETrue; //the src buffer is not to be deleted
1.1021 +
1.1022 + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data
1.1023 + }
1.1024 + }
1.1025 + }
1.1026 + else
1.1027 + {//don't need a sink buffer, but we need a source one
1.1028 + ChangeDataPathTransferState(EInitializeSource);
1.1029 + }
1.1030 +
1.1031 +#ifdef _DP_DEBUG
1.1032 + 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);
1.1033 +#endif
1.1034 + }
1.1035 +
1.1036 +
1.1037 +/*
1.1038 + * InitializeSourceL
1.1039 + *
1.1040 + * Function to initialize iDataSource before it can start sending data
1.1041 + * This is a one time prime. This will synchronize DataPath's data driving
1.1042 + * mechanism with HwDevice implementation.
1.1043 + *
1.1044 + * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine
1.1045 + * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF
1.1046 + */
1.1047 +
1.1048 +void CMMFDataPath::InitializeSourceL()
1.1049 + {
1.1050 +#ifdef _DP_DEBUG
1.1051 + RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
1.1052 +#endif
1.1053 +
1.1054 + //state only used if we are passing data
1.1055 + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__));
1.1056 +
1.1057 + iObtainingAsyncSourceBuffer = EFalse;
1.1058 +
1.1059 + if (iBuffersToUse & ENeedSourceBuffer)
1.1060 + {
1.1061 + //Buffers are initially created in the Prime method. But following a pause, we must re-create
1.1062 + //any referenced buffers, so try direct creation.
1.1063 + //NB: this does mean we are trying this twice, Prime and here.
1.1064 + if (!iSourceBuffer) //we may already have a buffer from a previous initialization
1.1065 + {
1.1066 + TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef));
1.1067 + if(err != KErrNone && err != KErrNotSupported)
1.1068 + User::Leave(err);
1.1069 + }
1.1070 +
1.1071 +
1.1072 + //If buffer has not been supplied via CreateSourceBufferL
1.1073 + //must use asynchronous buffer creation
1.1074 + if (!iSourceBuffer)
1.1075 + {
1.1076 + iObtainingAsyncSourceBuffer = ETrue;
1.1077 + ChangeDataPathTransferState(ENeedSourceData);
1.1078 + }
1.1079 + else
1.1080 + {//we have a source buffer from CreateSourceBufferL
1.1081 + iSourceBuffer->SetStatus(EAvailable);
1.1082 +
1.1083 + if (!(iBuffersToUse & ENeedSinkBuffer))
1.1084 + {//only need one buffer, use sink
1.1085 + iSinkBuffer = iSourceBuffer;
1.1086 + iSnkBufRef = ETrue;
1.1087 + }
1.1088 +
1.1089 + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data
1.1090 + }
1.1091 + }
1.1092 + else
1.1093 + {//don't need a source buffer, use sinks
1.1094 + if(iSinkBuffer)
1.1095 + {
1.1096 + iSourceBuffer = iSinkBuffer;
1.1097 + iSrcBufRef = iSnkBufRef;
1.1098 + SetBuffersAvailable();
1.1099 + }
1.1100 +#ifdef _DP_DEBUG
1.1101 + else
1.1102 + Panic(EMMFDataPathPanicProgrammingError,__LINE__);
1.1103 +#endif
1.1104 + ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data
1.1105 +
1.1106 +
1.1107 +#ifdef _DP_DEBUG
1.1108 + RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
1.1109 +#endif
1.1110 + }
1.1111 + }
1.1112 +
1.1113 +/*
1.1114 + * EmptySinkBufferL
1.1115 + *
1.1116 + * Function to pass a full databuffer to the iDataSink
1.1117 + */
1.1118 +void CMMFDataPath::EmptySinkBufferL()
1.1119 + {
1.1120 +#ifdef _DP_DEBUG
1.1121 + RDebug::Print(_L("DP::EmptySinkBufferL pass data to sink tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1122 + if(iSinkBuffer)
1.1123 + RDebug::Print(_L("iSinkBuffer %d contains %d bytes eof = %d line %d (this 0x%x)\n"),iSinkBuffer->FrameNumber(), iSinkBuffer->BufferSize(),iSinkBuffer->LastBuffer(),__LINE__,this);
1.1124 +#endif
1.1125 +
1.1126 + //Before emptying the sink buffer we need to check it has data to empty - this
1.1127 + //may not be the case if there is no more data ie iNoMoreSourceData is true.
1.1128 + //In this case we need to check to see if there is any data left in the sink
1.1129 + //buffer, ie the sink buffer is either full or being filled. If there is not any
1.1130 + //data in the sink buffer, ie it is not in the state of EBeingFilled or EFull
1.1131 + //then there is nothing to empty so the datapath state is set to EEndOfData and
1.1132 + //we return from the procedure.
1.1133 +
1.1134 + //state only used if we are passing data
1.1135 + __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled)), Panic(EMMFDataPathPanicBadState,__LINE__));
1.1136 + __ASSERT_DEBUG(iSinkBuffer &&
1.1137 + ((iSinkBuffer->Status()==EBeingFilled) || (iSinkBuffer->Status()==EFull)),
1.1138 + Panic(EMMFDataPathPanicProgrammingError,__LINE__));
1.1139 +
1.1140 + __ASSERT_DEBUG(iSinkBufferWithSink == EFalse, Panic(EMMFDataPathPanicBadState,__LINE__));
1.1141 +
1.1142 +
1.1143 + //Due to sinks that may call BuferEmptied directly (ie. re-entrancy onto DataPath) we
1.1144 + //must work out next state here. If re-entrancy, the next state may validly get overwritten
1.1145 + // in BuferEmptied.
1.1146 + if(iObtainingAsyncSinkBuffer) //wait for buffer to be returned in BufferEmptied
1.1147 + {
1.1148 + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink
1.1149 + }
1.1150 +
1.1151 +#ifdef REPOSITION_SPEEDUP
1.1152 + // if the source has been re-positioned,
1.1153 + // speed things up by getting some more source data now
1.1154 + if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber)
1.1155 + {
1.1156 +#ifdef _DP_DEBUG
1.1157 + RDebug::Print(_L("DP::EmptySinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this);
1.1158 +#endif
1.1159 + ChangeDataPathTransferState(ENeedSourceData);
1.1160 + return;
1.1161 + }
1.1162 +#endif //REPOSITION_SPEEDUP
1.1163 +
1.1164 + //We have sent data to sink, if we are using a real Codec, we can now get more data from source
1.1165 + //if there is any more to get and the codec has emptied it.
1.1166 + //NB: No need to check we own the source buffer as we will no be in this state
1.1167 + //if we have asked for more source data and haven't received it.
1.1168 + else if (iCodec && !iNoMoreSourceData && (iSourceBuffer->Status() == EAvailable))
1.1169 + {
1.1170 +#ifdef _DP_DEBUG
1.1171 + RDebug::Print(_L("ASKING for more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource,this);
1.1172 +#endif
1.1173 + ChangeDataPathTransferState(ENeedSourceData);
1.1174 + }
1.1175 + else
1.1176 + {
1.1177 +#ifdef _DP_DEBUG
1.1178 + 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);
1.1179 +#endif
1.1180 +
1.1181 + //if this is the last buffer, set this flag so we can deal with KErrUnderflow
1.1182 + //as a valid termination of playing
1.1183 + if(iSinkBuffer->LastBuffer())
1.1184 + iAllDataSentToSink=ETrue;
1.1185 +
1.1186 + ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink
1.1187 + }
1.1188 +
1.1189 +
1.1190 + if(!iObtainingAsyncSinkBuffer) //normal data transfer
1.1191 + iSinkBuffer->SetFrameNumber(++iCurrentSinkFrameNumber);
1.1192 +
1.1193 +#ifdef _DP_DEBUG
1.1194 + 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);
1.1195 +#endif
1.1196 +
1.1197 + iSinkBufferWithSink = ETrue;
1.1198 + TRAPD(error, iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId));
1.1199 +
1.1200 + // Check that we haven't exceeded the maximum clip length - if so, go to the EndOfData state
1.1201 + // so we perform necessary cleanup.
1.1202 + if (error == KErrEof || error == KErrOverflow || error == KErrUnderflow)
1.1203 + {
1.1204 +#ifdef _DP_DEBUG
1.1205 + RDebug::Print(_L("DP::EmptySinkBufferL DONE %d error = %d tick-%d (this 0x%x)\n"),__LINE__, error, User::TickCount(),this);
1.1206 +#endif
1.1207 + iDataPathCompletedErrorCode = error;
1.1208 + ChangeDataPathTransferState(EEndOfData);
1.1209 + return;
1.1210 + }
1.1211 + User::LeaveIfError(error);
1.1212 +
1.1213 +#ifdef _DP_DEBUG
1.1214 + RDebug::Print(_L("DP::EmptySinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1215 +#endif
1.1216 + }
1.1217 +
1.1218 +
1.1219 +/**
1.1220 +Indicates the data sink has emptied the buffer.
1.1221 +
1.1222 +Called by the CMMFDataPath's MDataSink when it has emptied the buffer
1.1223 +
1.1224 +@param aBuffer
1.1225 + The emptied buffer.
1.1226 +*/
1.1227 +EXPORT_C void CMMFDataPath::BufferEmptiedL(CMMFBuffer* aBuffer)
1.1228 + {
1.1229 +#ifdef _DP_DEBUG
1.1230 + TInt bufNum = 9999;
1.1231 + if(aBuffer)
1.1232 + bufNum = aBuffer->FrameNumber();
1.1233 + else
1.1234 + RDebug::Print(_L("DP::BufferEmptiedL returned NULL (this 0x%x)\n"),this);
1.1235 +
1.1236 + 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"),
1.1237 + bufNum, aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), iNoMoreSourceData, User::TickCount(),this);
1.1238 +#endif
1.1239 +
1.1240 + iSinkBufferWithSink = EFalse;
1.1241 +
1.1242 + //Has the datapath stopped running, if so were not interested in any callbacks.
1.1243 + if(iState == EStopped || (iState == EPrimed && !iPauseCalled))
1.1244 + {
1.1245 +#ifdef _DP_DEBUG
1.1246 + RDebug::Print(_L("DP::BufferEmptiedL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this);
1.1247 +#endif
1.1248 + return;
1.1249 + }
1.1250 +
1.1251 +
1.1252 + // This will allow MDataSink to send dynamic buffer to DataPath with each BufferEmptiedL request.
1.1253 + if (iSinkBuffer != aBuffer) //buffer has been updated
1.1254 + {
1.1255 + iSinkBuffer = aBuffer;
1.1256 + if (!(iBuffersToUse & ENeedSourceBuffer))
1.1257 + { //can use a single buffer
1.1258 + iSourceBuffer = iSinkBuffer;
1.1259 + iSrcBufRef = iSnkBufRef;
1.1260 + }
1.1261 +
1.1262 + }
1.1263 +
1.1264 + iSinkBuffer->SetStatus(EAvailable);
1.1265 +
1.1266 + if (iObtainingAsyncSinkBuffer) //we are creating an asynchronous sink buffer
1.1267 + {
1.1268 + iObtainingAsyncSinkBuffer = EFalse;
1.1269 +
1.1270 + //we have a sink buffer, should this also be used by the source
1.1271 + if (!(iBuffersToUse & ENeedSourceBuffer))
1.1272 + {//using a single buffer, so start getting data
1.1273 + iSourceBuffer = iSinkBuffer;
1.1274 + iSrcBufRef = iSnkBufRef;
1.1275 +
1.1276 + ChangeDataPathTransferState(ENeedSourceData);
1.1277 + }
1.1278 + else //obtain a separate source buffer
1.1279 + ChangeDataPathTransferState(EInitializeSource);
1.1280 +
1.1281 +#ifdef _DP_DEBUG
1.1282 + 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);
1.1283 +#endif
1.1284 + return;
1.1285 + }
1.1286 +
1.1287 + if(!iCodec) //No Codec in use
1.1288 + {
1.1289 + if(iNoMoreSourceData)
1.1290 + ChangeDataPathTransferState(EEndOfData);//final buffer returned from sink
1.1291 + else
1.1292 + ChangeDataPathTransferState(ENeedSourceData);//get more data from source
1.1293 + }
1.1294 + else //real codecs
1.1295 + {
1.1296 + //There is more source data and src buffer is being filled or we are about to go into
1.1297 + // ENeedSourceData state to fill it, so wait for source data to arrive.
1.1298 + //NB:if there was more source data and we are using a real codec and source buffer was empty,
1.1299 + //more source data would have been requested in EmptySinkBuffer
1.1300 + if(!iNoMoreSourceData && (iSourceBufferWithSource || iTransferState == ENeedSourceData))
1.1301 + {
1.1302 +#ifdef _DP_DEBUG
1.1303 + RDebug::Print(_L("DP::BufferEmptiedL - waiting for more source data - DONE tick-%d line %d (this 0x%x)\n"),User::TickCount(),__LINE__,this);
1.1304 +#endif
1.1305 + return;
1.1306 + }
1.1307 +
1.1308 + //source has supplied a NULL buffer or it has been emptied; no more data to send.
1.1309 + if(!iSourceBuffer || (iSourceBuffer->Status() == EAvailable))
1.1310 + ChangeDataPathTransferState(EEndOfData);
1.1311 + else if(iSourceBuffer->Status() == EFull) //there is data in the source buffer, go and get it
1.1312 + ChangeDataPathTransferState(ENeedToMatchSourceToSink);
1.1313 +
1.1314 +#ifdef _DP_DEBUG
1.1315 + else
1.1316 + Panic(EMMFDataPathPanicProgrammingError,__LINE__);
1.1317 +#endif
1.1318 + }
1.1319 +
1.1320 +
1.1321 +#ifdef _DP_DEBUG
1.1322 + RDebug::Print(_L("DP::BufferEmptiedL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1323 +#endif
1.1324 + }
1.1325 +
1.1326 +
1.1327 +
1.1328 +/**
1.1329 +Tests whether the data path can create a source buffer.
1.1330 +
1.1331 +Would expect datapath to always return NULL, so this is a default implementation of a pure virtual from MDataSink.
1.1332 +
1.1333 +The default implementation returns EFalse.
1.1334 +
1.1335 +@return ETrue if the data path can create a source buffer. EFalse if the data path cannot create a source buffer.
1.1336 +*/
1.1337 +EXPORT_C TBool CMMFDataPath::CanCreateSourceBuffer() //from both MDataSource & MDataSink?
1.1338 + {
1.1339 + return EFalse; //CMMFDataPath cannot create buffer
1.1340 + }
1.1341 +
1.1342 +/**
1.1343 +Creates a source buffer.
1.1344 +
1.1345 +Intended for synchronous usage (buffers supplied by datapath for a MDataSource)
1.1346 +This method is essentially a dummy implementation of an MDataSource pure virtual.
1.1347 +
1.1348 +The default implementation leaves with KErrNotSupported and returns NULL.
1.1349 +
1.1350 +@param aMediaId
1.1351 + An optional mediaID parameter when there are multiple buffers arriving of different media types.
1.1352 +@return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers
1.1353 +*/
1.1354 +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/) //CMMFDataPath can't create buffers
1.1355 + {
1.1356 + User::Leave(KErrNotSupported);
1.1357 + return NULL;
1.1358 + }
1.1359 +
1.1360 +/**
1.1361 +Creates a source buffer according to the specifed media ID and reference.
1.1362 +
1.1363 +Intended for asynchronous usage (buffers supplied by datapath for a MDataSource)
1.1364 +This method is essentially a dummy implementation of an MDataSource pure virtual.
1.1365 +
1.1366 +The default implementation leaves with KErrNotSupported and returns NULL.
1.1367 +
1.1368 +@param aMediaId
1.1369 + An optional mediaID parameter when there are multiple buffers arriving of different media types.
1.1370 +@param aReference
1.1371 + A boolean indicating buffer ownership. ETrue if the MDataSource owns the buffer, EFalse if the caller owns the buffer.
1.1372 +
1.1373 +@return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers.
1.1374 +*/
1.1375 +EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) //CMMFDataPath can't create buffers
1.1376 + {
1.1377 + User::Leave(KErrNotSupported);
1.1378 + return NULL;
1.1379 + }
1.1380 +
1.1381 +
1.1382 +/**
1.1383 +Gets the source data type for the specified media ID.
1.1384 +
1.1385 +@param aMediaId
1.1386 + An optional parameter to specifiy specific stream when datasource contains more than one stream of data.
1.1387 +
1.1388 +@return The source data type.
1.1389 +*/
1.1390 +EXPORT_C TFourCC CMMFDataPath::SourceDataTypeCode(TMediaId /*aMediaId*/)
1.1391 + {
1.1392 + return(iSourceFourCC);
1.1393 + }
1.1394 +
1.1395 +
1.1396 +/**
1.1397 +Allocates buffers in preparation to play.
1.1398 +
1.1399 +Must be called before calling PlayL().
1.1400 +
1.1401 +iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink
1.1402 +For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL().
1.1403 +*/
1.1404 +
1.1405 +EXPORT_C void CMMFDataPath::PrimeL()
1.1406 + {
1.1407 +#ifdef _DP_DEBUG
1.1408 + RDebug::Print(_L("DP::PrimeL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1409 +#endif
1.1410 +
1.1411 + //allocate resources ie buffers & prepare to play
1.1412 + if (iDataPathCreated && (iState == EStopped))
1.1413 + //luckily the client utility does this.
1.1414 + {//can only prime from the stopped state
1.1415 +
1.1416 + //This will determine what buffers we need to run the datapath.
1.1417 + //Can leave KERRNotSupported
1.1418 + iBuffersToUse = DetermineBuffersToUseL();
1.1419 +
1.1420 + //Try to create source and sink buffers. If we can't create them in the Prime,
1.1421 + //we will need to obtain them by asynchronous buffer creation when playing starts.
1.1422 + ObtainSyncBuffersL();
1.1423 +
1.1424 + iDataSource->SourcePrimeL(); //propogate state change to source
1.1425 + iDataSink->SinkPrimeL(); //propogate state change to sink
1.1426 +
1.1427 +
1.1428 + //If Client has set these, they will be set following the prime
1.1429 + iPlayWindowStartPosition = 0;
1.1430 + iPlayWindowEndPosition = Duration();
1.1431 +
1.1432 + iState = EPrimed;
1.1433 + iPauseCalled = EFalse;
1.1434 +
1.1435 + if (iCompleteCallback)
1.1436 + {
1.1437 + delete iCompleteCallback;
1.1438 + iCompleteCallback = NULL;
1.1439 + }
1.1440 + TBool waitForSink = (iDataSink->DataSinkType() == KUidMmfAudioOutput)?ETrue:EFalse;
1.1441 + iCompleteCallback = new (ELeave) CCompleteCallback(*this,waitForSink);
1.1442 + }
1.1443 +#ifdef _DP_DEBUG
1.1444 + RDebug::Print(_L("DP::PrimeL Done tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1445 +#endif
1.1446 + }
1.1447 +
1.1448 +
1.1449 +/**
1.1450 +Starts an active scheduler 'play' loop.
1.1451 +
1.1452 +Can only play from the primed state.
1.1453 +*/
1.1454 +EXPORT_C void CMMFDataPath::PlayL()
1.1455 + {
1.1456 +#if defined(__PROFILING)
1.1457 + RDebug::ProfileEnd(1);
1.1458 +#endif // defined(__PROFILING)
1.1459 +
1.1460 +#ifdef _DP_DEBUG
1.1461 + RDebug::Print(_L("DP::PlayL, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);
1.1462 + RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64()));
1.1463 +#endif
1.1464 +
1.1465 + if ((iDataPathCreated) && (iState == EPrimed))
1.1466 + {
1.1467 + //can only play from the primed state
1.1468 +
1.1469 + TBool savedPauseCalled=EFalse;
1.1470 + if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed
1.1471 + {
1.1472 + savedPauseCalled=ETrue;
1.1473 + iDataSink->SinkPrimeL(); //propagate change down to sink
1.1474 + iPauseCalled = EFalse;
1.1475 + }
1.1476 +
1.1477 + iCurrentSourceFrameNumber = 0; //reset to beginning
1.1478 + iCurrentSinkFrameNumber = 0; //reset to beginning
1.1479 +
1.1480 + iSourceBufferWithSource = EFalse;
1.1481 + iSinkBufferWithSink = EFalse;
1.1482 +
1.1483 + iNoMoreSourceData = EFalse;
1.1484 + iAllDataSentToSink=EFalse;
1.1485 + iDataPathCompletedErrorCode=KErrNone;
1.1486 +
1.1487 + SetPositionL( iStartPosition ) ;
1.1488 + iReferenceAudioSamplesPlayed = 0;
1.1489 + iReferenceAudioSamplesRecorded = 0;
1.1490 +
1.1491 + //complete a request on iStatus to invoke play code
1.1492 + iDataSource->SourcePlayL(); //propagate state change to source
1.1493 + if (!(savedPauseCalled && (iTransferState==EWaitSink || iTransferState==EInitializeSink)))
1.1494 + {
1.1495 + iDataSink->SinkPlayL(); //propogate state change to sink
1.1496 + }
1.1497 +
1.1498 + if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
1.1499 + iState = EPlaying;
1.1500 + else if(iDataSource->DataSourceType() == KUidMmfAudioInput)
1.1501 + iState = ERecording;
1.1502 + else
1.1503 + iState = EConverting;
1.1504 +
1.1505 + //need to re-initialize any buffer(s) that we only own references to
1.1506 + ChangeDataPathTransferState(EInitializeSink);
1.1507 + }
1.1508 +#ifdef _DP_DEBUG
1.1509 + RDebug::Print(_L("DP::Play - DONE\n"));
1.1510 +#endif
1.1511 + }
1.1512 +
1.1513 +
1.1514 +/**
1.1515 +Pauses playing.
1.1516 +
1.1517 +Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs.
1.1518 +*/
1.1519 +EXPORT_C void CMMFDataPath::Pause()
1.1520 + {
1.1521 +#ifdef _DP_DEBUG
1.1522 + RDebug::Print(_L("DP::Pause, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);
1.1523 + RDebug::Print(_L("DP::Pause current state=%d tick-%d (this 0x%x)\n"),iTransferState, User::TickCount(),this);
1.1524 +#endif
1.1525 +
1.1526 + TRAPD(err, DoPauseL());
1.1527 +
1.1528 + if (err)
1.1529 + {
1.1530 + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
1.1531 + }
1.1532 +#ifdef _DP_DEBUG
1.1533 + RDebug::Print(_L("DP::Pause - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.1534 +#endif
1.1535 + }
1.1536 +
1.1537 +/**
1.1538 +Stops playing.
1.1539 +
1.1540 +Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError
1.1541 +to the client if an error occurs.
1.1542 +*/
1.1543 +EXPORT_C void CMMFDataPath::Stop()
1.1544 + {
1.1545 +#ifdef _DP_DEBUG
1.1546 + RDebug::Print(_L("DP::Stop current state=%d tick-%d (this 0x%x)\n"), iTransferState, User::TickCount(),this);
1.1547 +#endif
1.1548 +
1.1549 + if ((iDataPathCreated) && (iState != EStopped))
1.1550 + {
1.1551 + TRAPD(err, DoStopL());
1.1552 +
1.1553 + if (err)
1.1554 + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
1.1555 + }
1.1556 + }
1.1557 +/**
1.1558 +Forces and end of data state on the datapath
1.1559 +*/
1.1560 +EXPORT_C void CMMFDataPath::EndOfData()
1.1561 + {
1.1562 + TRAPD(err, DoEndOfDataL());
1.1563 +
1.1564 + if (err)
1.1565 + {
1.1566 + DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
1.1567 + }
1.1568 + }
1.1569 +
1.1570 +/**
1.1571 +
1.1572 +*/
1.1573 +TInt CMMFDataPath::AudioSamplesPlayed() const
1.1574 + {
1.1575 + if (iDataSink->DataSinkType() != KUidMmfAudioOutput)
1.1576 + return 0;
1.1577 +
1.1578 + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
1.1579 +
1.1580 + TInt samples = 0;
1.1581 +
1.1582 + if(iState == EPlaying)
1.1583 + samples = audioOutput->SoundDevice().SamplesPlayed();
1.1584 +
1.1585 +#ifdef _DP_DEBUG
1.1586 + RDebug::Print(_L("DP::AudioSamplesPlayed = %d\n"),samples);
1.1587 +#endif
1.1588 +
1.1589 + return samples;
1.1590 + }
1.1591 +
1.1592 +
1.1593 +/**
1.1594 +
1.1595 +*/
1.1596 +TInt CMMFDataPath::AudioSamplesRecorded() const
1.1597 + {
1.1598 + if (iDataSource->DataSourceType() != KUidMmfAudioInput)
1.1599 + return 0;
1.1600 +
1.1601 + CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource);
1.1602 +
1.1603 + TInt samples = 0;
1.1604 +
1.1605 + if(iState == ERecording)
1.1606 + samples = audioInput->SoundDevice().SamplesRecorded();
1.1607 +
1.1608 +#ifdef _DP_DEBUG
1.1609 + RDebug::Print(_L("DP::AudioSamplesRecorded = %d\n"),samples);
1.1610 +#endif
1.1611 +
1.1612 + return samples;
1.1613 + }
1.1614 +
1.1615 +
1.1616 +/**
1.1617 +
1.1618 +*/
1.1619 +TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioOutputPosition() const
1.1620 + {
1.1621 + //This operation can only be carried out on an Audio Output
1.1622 + __ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__));
1.1623 +
1.1624 +
1.1625 + //If we are not playing, simply return where we will play from
1.1626 + if(iState != EPlaying || iCurrentSinkFrameNumber == 0)
1.1627 + return iStartPosition;
1.1628 +
1.1629 +#ifdef _DP_DEBUG
1.1630 + RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed);
1.1631 +#endif
1.1632 +
1.1633 + TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed;
1.1634 +
1.1635 + CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
1.1636 + CMMFDevSound& devSound = audioOutput->SoundDevice();
1.1637 +
1.1638 + TMMFCapabilities devSoundConfig = devSound.Config();
1.1639 +
1.1640 + TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig);
1.1641 +
1.1642 +#ifdef _DP_DEBUG
1.1643 + RDebug::Print(_L("samplingFreq %d\n"),samplingFreq);
1.1644 +#endif
1.1645 +
1.1646 + TReal timePlayedSeconds = 0;
1.1647 + if(samplesPlayed)
1.1648 + timePlayedSeconds = samplesPlayed/samplingFreq;
1.1649 +
1.1650 + TInt64 timePlayed(I64DOUBLECAST(timePlayedSeconds * 1000000));
1.1651 +
1.1652 +#ifdef _DP_DEBUG
1.1653 + RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed));
1.1654 +#endif
1.1655 +
1.1656 + return TTimeIntervalMicroSeconds(timePlayed + iStartPosition.Int64());
1.1657 + }
1.1658 +
1.1659 +
1.1660 +
1.1661 +/**
1.1662 +
1.1663 +*/
1.1664 +TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioInputPosition() const
1.1665 + {
1.1666 + //This operation can only be carried out on an Audio Input
1.1667 + __ASSERT_ALWAYS(iDataSource->DataSourceType() == KUidMmfAudioInput, Panic(EMMFDataPathPanicProgrammingError,__LINE__));
1.1668 +
1.1669 +
1.1670 + //If we are not playing, simply return where we will play from
1.1671 + if(iState != ERecording)
1.1672 + return iStartPosition;
1.1673 +
1.1674 +#ifdef _DP_DEBUG
1.1675 + RDebug::Print(_L("DP::CalculateAudioInputPosition from %d\n"),iReferenceAudioSamplesRecorded);
1.1676 +#endif
1.1677 +
1.1678 + TReal samplesRecorded = AudioSamplesRecorded() - iReferenceAudioSamplesRecorded;
1.1679 +
1.1680 + CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource);
1.1681 + CMMFDevSound& devSound = audioInput->SoundDevice();
1.1682 +
1.1683 + TMMFCapabilities devSoundConfig = devSound.Config();
1.1684 +
1.1685 + TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig);
1.1686 +
1.1687 +#ifdef _DP_DEBUG
1.1688 + RDebug::Print(_L("samplingFreq %d\n"),samplingFreq);
1.1689 +#endif
1.1690 +
1.1691 + TReal timeRecordedSeconds = 0;
1.1692 + if(samplesRecorded)
1.1693 + timeRecordedSeconds = samplesRecorded/samplingFreq;
1.1694 +
1.1695 + TInt64 timeRecorded(I64DOUBLECAST(timeRecordedSeconds * 1000000));
1.1696 +
1.1697 +#ifdef _DP_DEBUG
1.1698 + RDebug::Print(_L("timeRecorded %d\n"), I64LOW(timeRecorded));
1.1699 +#endif
1.1700 + return TTimeIntervalMicroSeconds(timeRecorded);
1.1701 + }
1.1702 +
1.1703 +
1.1704 +
1.1705 +
1.1706 +
1.1707 +TTimeIntervalMicroSeconds CMMFDataPath::OutputPosition() const
1.1708 + {
1.1709 + TTimeIntervalMicroSeconds interval;
1.1710 + TTimeIntervalMicroSeconds position;
1.1711 +
1.1712 + if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
1.1713 + {
1.1714 + position = CalculateAudioOutputPosition();
1.1715 +#ifdef _DP_DEBUG
1.1716 + RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64()));
1.1717 +#endif
1.1718 + }
1.1719 + else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
1.1720 + {
1.1721 + //note Encode format position takes priority if both source & sink are formats?
1.1722 + // try to get the position directly from the format. If that fails, work it out here
1.1723 + TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL());
1.1724 + if (error)//getting the position from the format didn't work so calculate it here
1.1725 + {
1.1726 + interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
1.1727 + TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber;
1.1728 + position = position64;
1.1729 + }
1.1730 +
1.1731 + TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration
1.1732 + if (position > duration)//this can happen on last buffer
1.1733 + position = duration;
1.1734 +
1.1735 +
1.1736 +#ifdef _DP_DEBUG
1.1737 + RDebug::Print(_L("DP::OutputPosition from format= %d\n"),I64INT(position.Int64()));
1.1738 +#endif
1.1739 + }
1.1740 + else
1.1741 + {//can only read output position if sink is a format or an audio output
1.1742 + return TTimeIntervalMicroSeconds(0);
1.1743 + }
1.1744 +
1.1745 + return position;
1.1746 + }
1.1747 +
1.1748 +TTimeIntervalMicroSeconds CMMFDataPath::InputPosition() const
1.1749 + {
1.1750 + TTimeIntervalMicroSeconds interval;
1.1751 + TTimeIntervalMicroSeconds position;
1.1752 +
1.1753 + if (iDataSource->DataSourceType() == KUidMmfAudioInput)
1.1754 + {
1.1755 + position = CalculateAudioInputPosition();
1.1756 +#ifdef _DP_DEBUG
1.1757 + RDebug::Print(_L("DP::InputPosition from audio input= %d\n"),I64INT(position.Int64()));
1.1758 +#endif
1.1759 + }
1.1760 + else if (iDataSource->DataSourceType() == KUidMmfFormatDecode)
1.1761 + {//note Decode format position takes priority if both source & sink are formats?
1.1762 + // try to get the position directly from the format. If that fails, work it out here
1.1763 + TRAPD(error, position = ((CMMFFormatDecode*)iDataSource)->PositionL());
1.1764 + if (error)//getting the position from the format didn't work so calculate it here
1.1765 + {
1.1766 + interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId);
1.1767 + TInt64 position64 = interval.Int64() * iCurrentSourceFrameNumber;
1.1768 + position = position64;
1.1769 + }
1.1770 +
1.1771 + TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration
1.1772 + if (position > duration)//this can happen on last buffer
1.1773 + position = duration;
1.1774 +
1.1775 +#ifdef _DP_DEBUG
1.1776 + RDebug::Print(_L("DP::InputPosition from format = %d\n"),I64INT(position.Int64()));
1.1777 +#endif
1.1778 + }
1.1779 + else
1.1780 + {//can only read input position if source is a format or an audio input
1.1781 + return TTimeIntervalMicroSeconds(0);
1.1782 + }
1.1783 +
1.1784 + return position;
1.1785 + }
1.1786 +
1.1787 +
1.1788 +
1.1789 +
1.1790 +/**
1.1791 +Gets the data path position.
1.1792 +
1.1793 +@return The data path position.
1.1794 +*/
1.1795 +EXPORT_C TTimeIntervalMicroSeconds CMMFDataPath::Position() const
1.1796 + {
1.1797 + if ((iState == ERecording) || (iState == EConverting))
1.1798 + return InputPosition();
1.1799 + else if(iState == EPlaying)
1.1800 + return OutputPosition();
1.1801 + else
1.1802 + {
1.1803 + return iStartPosition;
1.1804 + }
1.1805 + }
1.1806 +
1.1807 +/**
1.1808 +Sets the data path position.
1.1809 +
1.1810 +@param aPosition
1.1811 + The data path position.
1.1812 +*/
1.1813 +EXPORT_C void CMMFDataPath::SetPositionL(const TTimeIntervalMicroSeconds& aPosition)
1.1814 + {//need to map to source position to frame position
1.1815 +#ifdef _DP_DEBUG
1.1816 + RDebug::Print(_L("DP::SetPositionL = %d ticks-%d (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this);
1.1817 +#endif
1.1818 +
1.1819 + if (iState == EStopped)
1.1820 + User::Leave(KErrNotReady); //can only set position if primed
1.1821 +
1.1822 + //As this will affect the position, we need to know how many bytes were
1.1823 + //played when position was updated. Future Position() requests will
1.1824 + //then use this refernce to determine the current position.
1.1825 + iReferenceAudioSamplesPlayed = AudioSamplesPlayed();
1.1826 +
1.1827 + // Force the new position to be inside the play window (also within the file duration)
1.1828 + if ( aPosition < iPlayWindowStartPosition )
1.1829 + iStartPosition = iPlayWindowStartPosition;
1.1830 + else if ( aPosition > iPlayWindowEndPosition )
1.1831 + iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played
1.1832 + else
1.1833 + iStartPosition = aPosition;
1.1834 +
1.1835 + TTimeIntervalMicroSeconds interval;
1.1836 +
1.1837 + //can only set the position on an MDataSource that is a format object
1.1838 + //Note: position defaults to source if both source & sink are clips
1.1839 + if (iDataSource->DataSourceType() == KUidMmfFormatDecode)
1.1840 + {
1.1841 + //position is not beyond the end of file
1.1842 + interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId);
1.1843 +
1.1844 + // for some reason this code won't compile without these intermediate steps
1.1845 + TInt64 position = iStartPosition.Int64();
1.1846 + TInt64 interval64 = interval.Int64();
1.1847 + if (interval64 == 0)
1.1848 + User::Leave(KErrDivideByZero);
1.1849 + TInt64 datapos64 = position/interval64;
1.1850 + iCurrentSourceFrameNumber = I64LOW(datapos64);
1.1851 +
1.1852 +
1.1853 + // Try to set the position directly on the format
1.1854 + TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition));
1.1855 + //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
1.1856 + }
1.1857 + else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
1.1858 + {
1.1859 + //position is not beyond the end of file
1.1860 + interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
1.1861 +
1.1862 + //convert to TUint - for some reason it won't compile without these intermediate steps
1.1863 + TInt64 position = iStartPosition.Int64();
1.1864 + TInt64 interval64 = interval.Int64();
1.1865 + if (interval64 == 0)
1.1866 + User::Leave(KErrDivideByZero);
1.1867 + TInt64 datapos64 = position/interval64;
1.1868 + iCurrentSinkFrameNumber = I64LOW(datapos64);
1.1869 +
1.1870 +
1.1871 + // Try to set the position directly on the format
1.1872 + TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition));
1.1873 + //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
1.1874 + }
1.1875 + else
1.1876 + {//can only set position if source or sink is a format
1.1877 + //If both source and sink are formats position is relative to the source
1.1878 + User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips
1.1879 + }
1.1880 +
1.1881 + if(iCodec) //we have a real codec, must reset it
1.1882 + iCodec->ResetL(); // Need to preserve sync when resuming play
1.1883 +
1.1884 + // Once we've sent the last buffer to the sink it's too late to start
1.1885 + // changing the state since we may get a RunError(KErrUnderflow) at any time.
1.1886 + // Once this happens, the sound driver may have unloaded etc..and recovery
1.1887 + // would be complicated.
1.1888 + if (iAllDataSentToSink)
1.1889 + return;
1.1890 +
1.1891 +#ifdef _DP_DEBUG
1.1892 + RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d iStartPosition=%d ticks-%d (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this);
1.1893 +#endif
1.1894 + }
1.1895 +
1.1896 +
1.1897 +
1.1898 +
1.1899 +/**
1.1900 +Sets the play window absolutely (i.e. the parameters are relative to the start of the entire clip).
1.1901 +
1.1902 +@param aStart
1.1903 + The offset from the start of the Clip
1.1904 +@param aEnd
1.1905 + The offset from the end of the clip (if this is less than aStart, then the two will be inverted).
1.1906 +*/
1.1907 +EXPORT_C void CMMFDataPath::SetPlayWindowL( const TTimeIntervalMicroSeconds& aStart, const TTimeIntervalMicroSeconds& aEnd )
1.1908 + {
1.1909 + // Clear the existing Play window
1.1910 + ClearPlayWindowL() ;
1.1911 +
1.1912 + // Check that the parameters are legitimate. 0 <= startpos < endpos <= duration & update member variables
1.1913 + TTimeIntervalMicroSeconds duration = Duration();
1.1914 +
1.1915 + if ( aStart < TTimeIntervalMicroSeconds(0) )
1.1916 + iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0);
1.1917 + else if ( aStart > duration )
1.1918 + iPlayWindowStartPosition = duration;
1.1919 + else iPlayWindowStartPosition = aStart;
1.1920 +
1.1921 + if ( aEnd < TTimeIntervalMicroSeconds(0) )
1.1922 + iPlayWindowEndPosition = TTimeIntervalMicroSeconds(0);
1.1923 + else if ( aEnd > duration )
1.1924 + iPlayWindowEndPosition = duration;
1.1925 + else iPlayWindowEndPosition = aEnd;
1.1926 +
1.1927 + // ensure that the current position is inside the new play window
1.1928 + if ( iPlayWindowEndPosition != TTimeIntervalMicroSeconds(0) )
1.1929 + {
1.1930 + TTimeIntervalMicroSeconds currentPosition = Position() ;
1.1931 + if ( currentPosition < iPlayWindowStartPosition )
1.1932 + SetPositionL( iPlayWindowStartPosition ) ;
1.1933 + else if ( currentPosition > iPlayWindowEndPosition )
1.1934 + SetPositionL( iPlayWindowEndPosition ) ;
1.1935 + }
1.1936 + else
1.1937 + ClearPlayWindowL() ;
1.1938 +
1.1939 +#ifdef _DP_DEBUG
1.1940 + RDebug::Print(_L("DP::SetPlayWindowL iPlayWindowStartPosition=%d iPlayWindowEndPosition=%d\n"),I64INT(iPlayWindowStartPosition.Int64()),I64INT(iPlayWindowEndPosition.Int64()));
1.1941 +#endif
1.1942 + }
1.1943 +
1.1944 +
1.1945 +/**
1.1946 +Sets the play window to the full length of clip.
1.1947 +*/
1.1948 +EXPORT_C void CMMFDataPath::ClearPlayWindowL()
1.1949 + {
1.1950 + iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0) ;
1.1951 + iPlayWindowEndPosition = Duration();
1.1952 +
1.1953 +
1.1954 + if(iState == EStopped)
1.1955 + iStartPosition = iPlayWindowStartPosition;
1.1956 + }
1.1957 +
1.1958 +/**
1.1959 +Returns the current data path state.
1.1960 +*/
1.1961 +EXPORT_C TInt CMMFDataPath::State()
1.1962 + {
1.1963 + return iState ;
1.1964 + }
1.1965 +
1.1966 +
1.1967 +/**
1.1968 +Uses the AO mechanism to drive state changes between sources and sinks.
1.1969 +
1.1970 +RunL() moves and assigns buffers between its attached MDataSource/MDataSinks.
1.1971 +*/
1.1972 +void CMMFDataPath::ChangeDataPathTransferState(TTransferState aNewDataPathTransferState)
1.1973 + {
1.1974 +#ifdef _DP_DEBUG
1.1975 + switch (aNewDataPathTransferState)
1.1976 + {
1.1977 + case EWaitSink:
1.1978 + RDebug::Print(_L("Next State EWaitSink ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1979 + break;
1.1980 + case EWaitSource:
1.1981 + RDebug::Print(_L("Next State EWaitSource ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1982 + break;
1.1983 + case EInitializeSink:
1.1984 + RDebug::Print(_L("Next State EInitializeSink ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1985 + break;
1.1986 + case EInitializeSource:
1.1987 + RDebug::Print(_L("Next State EInitializeSource ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1988 + break;
1.1989 + case ENeedSourceData:
1.1990 + RDebug::Print(_L("Next State ENeedSourceData ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1991 + break;
1.1992 + case ENeedSinkData:
1.1993 + RDebug::Print(_L("Next State ENeedSinkData ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1994 + break;
1.1995 + case ENeedToMatchSourceToSink:
1.1996 + RDebug::Print(_L("Next State ENeedToMatchSourceToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.1997 + break;
1.1998 + case ESendDataToSink:
1.1999 + RDebug::Print(_L("Next State ESendDataToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.2000 + break;
1.2001 + case EEndOfData:
1.2002 + RDebug::Print(_L("Next State EEndOfData ticks-%d (this 0x%x)\n"),User::TickCount(),this);
1.2003 + break;
1.2004 + }
1.2005 +#endif
1.2006 +
1.2007 +
1.2008 + TRequestStatus* stat = &iStatus;
1.2009 + //change state
1.2010 + iTransferState = aNewDataPathTransferState;
1.2011 + if ((iTransferState != EWaitSink) && (iTransferState != EWaitSource) &&
1.2012 + (iState == EPlaying || iState == ERecording || iState == EConverting || (iState == EPrimed && iPauseCalled)))
1.2013 + {//can go ahead with transfer
1.2014 + if (!IsActive())
1.2015 + {
1.2016 + User::RequestComplete(stat, KErrNone);
1.2017 + SetActive();
1.2018 + }
1.2019 + }
1.2020 +#ifdef _DP_DEBUG
1.2021 + else
1.2022 + RDebug::Print(_L("Datapath is no longer active, not going to new state (this 0x%x)\n"),this);
1.2023 +#endif
1.2024 + }
1.2025 +
1.2026 +/**
1.2027 +Runs the clip depending on the current data path and transfer state.
1.2028 +
1.2029 +For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData.
1.2030 +*/
1.2031 +EXPORT_C void CMMFDataPath::RunL()
1.2032 + {
1.2033 +#ifdef _DP_DEBUG
1.2034 + RDebug::Print(_L("DP::RunL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.2035 +#endif
1.2036 +
1.2037 + switch (iState)
1.2038 + {
1.2039 + case EStopped:
1.2040 + break;
1.2041 + case EPrimed:
1.2042 + //paused with stored position
1.2043 + break;
1.2044 + case EPlaying:
1.2045 + case ERecording:
1.2046 + case EConverting:
1.2047 + switch (iTransferState)
1.2048 + {
1.2049 + case EWaitSink:
1.2050 + case EWaitSource:
1.2051 + break;
1.2052 + case EInitializeSink:
1.2053 + InitializeSinkL();
1.2054 + break;
1.2055 + case EInitializeSource:
1.2056 + InitializeSourceL();
1.2057 + break;
1.2058 + case ENeedSourceData:
1.2059 + FillSourceBufferL();
1.2060 + break;
1.2061 + case ENeedSinkData:
1.2062 + FillSinkBufferL();
1.2063 + break;
1.2064 + case ENeedToMatchSourceToSink:
1.2065 + FillSinkBufferL();
1.2066 + break;
1.2067 + case ESendDataToSink:
1.2068 + EmptySinkBufferL();
1.2069 + break;
1.2070 + case EEndOfData:
1.2071 + EndOfData();
1.2072 + break;
1.2073 + }
1.2074 + break;
1.2075 + default:
1.2076 + break;
1.2077 + }
1.2078 +#ifdef _DP_DEBUG
1.2079 + RDebug::Print(_L("DP::RunL DONE\n"));
1.2080 +#endif
1.2081 + }
1.2082 +
1.2083 +/**
1.2084 +Cancels the clip.
1.2085 +
1.2086 +The default implementation is empty.
1.2087 +*/
1.2088 +EXPORT_C void CMMFDataPath::DoCancel()
1.2089 + {
1.2090 + //don't need to do anything as we don't have any async requests to other objects
1.2091 + }
1.2092 +
1.2093 +/**
1.2094 +Handles errors coming from attached sources and passes them to the clients.
1.2095 +
1.2096 +@param aError
1.2097 + Standard error code (KErrNone = No Error).
1.2098 +
1.2099 +@return The event code returned to the data path. KErrNone if end of file is encountered.
1.2100 +*/
1.2101 +EXPORT_C TInt CMMFDataPath::RunError(TInt aError)
1.2102 + {
1.2103 + return DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, aError);
1.2104 + }
1.2105 +
1.2106 +
1.2107 +/**
1.2108 +Returns the duration of the clip.
1.2109 +
1.2110 +@return The length of clip in TTimeIntervalMicroSeconds.
1.2111 +*/
1.2112 +TTimeIntervalMicroSeconds CMMFDataPath::Duration() const
1.2113 + {
1.2114 + TTimeIntervalMicroSeconds duration(0);
1.2115 +
1.2116 + if ( iDataSource && ( iDataSource->DataSourceType() == KUidMmfFormatDecode ) )
1.2117 + {
1.2118 + //this updated version of datapath caches the duration of the
1.2119 + //source clip for efficiency - since this meathod is const
1.2120 + //we need to cast away the constness to set iCachedSourceDuration
1.2121 + CMMFDataPath* thisNonConst = const_cast<CMMFDataPath*>(this);
1.2122 + duration = STATIC_CAST(CMMFFormatDecode*, thisNonConst->iDataSource )->Duration( iMediaId ) ;
1.2123 + thisNonConst->iCachedSourceDuration = duration;
1.2124 + }
1.2125 + else if ( iDataSink && ( iDataSink->DataSinkType() == KUidMmfFormatEncode ) )
1.2126 + duration = STATIC_CAST(CMMFFormatEncode*, iDataSink )->Duration( iMediaId ) ;
1.2127 +
1.2128 + return duration ;
1.2129 + }
1.2130 +
1.2131 +/**
1.2132 +This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour.
1.2133 +
1.2134 +Additional Stop() method specific to this datapath.
1.2135 +*/
1.2136 +void CMMFDataPath::DoStopL()
1.2137 + {
1.2138 + // Note that the datapath needs to be paused first
1.2139 + // before it can be stopped - this is important for audio play
1.2140 + // for instance to effect an immediate stop rather than playing out
1.2141 + // the current buffer.
1.2142 +
1.2143 +
1.2144 +
1.2145 + // Stop data source and sink
1.2146 + iDataSource->SourceStopL(); // propagate state change to source
1.2147 + iDataSink->SinkStopL(); // propagate change down to sink
1.2148 +
1.2149 + iSinkBufferWithSink = EFalse;
1.2150 + iSourceBufferWithSource = EFalse;
1.2151 +
1.2152 + //Contains the completion code if the datapath completes as a result of an error
1.2153 + iDataPathCompletedErrorCode = KErrNone;
1.2154 +
1.2155 + ResetRefBuffers(); // buffer references may not be valid any more
1.2156 +
1.2157 + SetPositionL(iPlayWindowStartPosition); // reset position
1.2158 +
1.2159 + iState = EStopped; // stop succeeded, set state to stopped
1.2160 + }
1.2161 +
1.2162 +/**
1.2163 +This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour.
1.2164 +
1.2165 +Additional Pause method specific to this datapath.
1.2166 +
1.2167 +The DataPath implements pause by recording the current position and stopping the source and sink.
1.2168 +When Play is called the DataPath uses the stored position as the starting point and resumes
1.2169 +playing. The reason for this (rather than using the PauseL() API on sources and sinks) is that
1.2170 +some implementations do not support a suitable pause, therefore the DataPath is implemented to
1.2171 +support the lowest common denominator.
1.2172 +
1.2173 +Note:
1.2174 +A suitable pause implementation will retain any buffers in use. There will be no
1.2175 +need to call PrimeL() prior to PlayL().
1.2176 +*/
1.2177 +void CMMFDataPath::DoPauseL()
1.2178 + {
1.2179 +#ifdef _DP_DEBUG
1.2180 + RDebug::Print(_L("DP::DoPauseL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.2181 +#endif
1.2182 +
1.2183 +
1.2184 + if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting)))
1.2185 + {
1.2186 + // try to pause source and sink
1.2187 + iDataSource->SourcePauseL(); // propagate state change to source
1.2188 + SetPositionL(Position());
1.2189 + iDataSink->SinkStopL();
1.2190 +
1.2191 + iPauseCalled = ETrue; // indicate pause is called
1.2192 +
1.2193 + iState = EPrimed; // go back to primed state (we're not playing)
1.2194 +
1.2195 + iSinkBufferWithSink = EFalse;
1.2196 + iSourceBufferWithSource = EFalse;
1.2197 +
1.2198 + ResetRefBuffers(); // buffer references may not be valid any more
1.2199 +
1.2200 + Cancel(); //Stop the state machine
1.2201 + }
1.2202 + else if(iState == ERecording)
1.2203 + {
1.2204 + iPauseCalled = ETrue;
1.2205 +#ifdef _DP_DEBUG
1.2206 + RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n"));
1.2207 +#endif
1.2208 + iDataSource->SourcePauseL();
1.2209 + }
1.2210 +#ifdef _DP_DEBUG
1.2211 + RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed);
1.2212 + RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this);
1.2213 +#endif
1.2214 + }
1.2215 +
1.2216 +/**
1.2217 +This is a virtual function datapath (or derivations off) that can be implemented or may be left blank for default behaviour.
1.2218 +
1.2219 +Additional Pause method specific to this datapath.
1.2220 +*/
1.2221 +void CMMFDataPath::DoEndOfDataL()
1.2222 + {
1.2223 +#ifdef _DP_DEBUG
1.2224 + RDebug::Print(_L("DP::DoEndOfDataL tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.2225 +#endif
1.2226 + SetPositionL(iPlayWindowStartPosition); // reset position
1.2227 +
1.2228 + ASSERT(iCompleteCallback);
1.2229 + iCompleteCallback->SignalDataPathComplete(iDataPathCompletedErrorCode);
1.2230 +
1.2231 + ResetRefBuffers();
1.2232 + iState = EStopped;
1.2233 +
1.2234 +#ifdef _DP_DEBUG
1.2235 + RDebug::Print(_L("DP::DoEndOfDataL - Done tick-%d (this 0x%x)\n"),User::TickCount(),this);
1.2236 +#endif
1.2237 + }
1.2238 +
1.2239 +
1.2240 +/**
1.2241 +Passes error handling and general messages up to clients.
1.2242 +
1.2243 +@param aEventType
1.2244 + Category code for the event. Category codes can be used as unique identifers.
1.2245 +@param aErrorCode
1.2246 + Standard error code.
1.2247 +
1.2248 +@return The event code sent to client.
1.2249 +*/
1.2250 +//error handling
1.2251 +EXPORT_C TInt CMMFDataPath::DoSendEventToClient(TUid aEventType, TInt aErrorCode)
1.2252 + {
1.2253 + TMMFEvent event(aEventType, aErrorCode);
1.2254 + return iEventHandler.SendEventToClient(event);
1.2255 + }
1.2256 +
1.2257 +/**
1.2258 +Passes error handling and general messages to clients.
1.2259 +
1.2260 +@param aEvent
1.2261 + TMMFEvent supplied by callee (typically DoSendEventToClient)
1.2262 +
1.2263 +@return The Event Message sent to the datapath event handler
1.2264 +*/
1.2265 +EXPORT_C TInt CMMFDataPath::SendEventToClient(const TMMFEvent& aEvent)
1.2266 + {
1.2267 +#ifdef _DP_DEBUG
1.2268 + RDebug::Print(_L("CMMFDataPath::SendEventToClient CODE = %d ticks=%d (this 0x%x)\n"),aEvent.iErrorCode,User::TickCount(),this);
1.2269 + RDebug::Print(_L("CMMFDataPath::SendEventToClient iEventType = %d == %d\n"),aEvent.iEventType, KMMFEventCategoryPlaybackComplete);
1.2270 +#endif
1.2271 +
1.2272 + //If we have sent all the data to the sink, it is legal for it to send KErrUnderFlow
1.2273 + //to us and we can go through a clean shutdown, otherwise we pass the error to the
1.2274 + //controller and it is responsible for determining what to do
1.2275 + if(iAllDataSentToSink &&
1.2276 + (aEvent.iEventType == KMMFEventCategoryPlaybackComplete) &&
1.2277 + (aEvent.iErrorCode == KErrUnderflow))
1.2278 + {
1.2279 +#ifdef _DP_DEBUG
1.2280 + RDebug::Print(_L("CMMFDataPath::SendEventToClient Clean complete\n"));
1.2281 +#endif
1.2282 + //sink may not return the final buffer once it has finished with it
1.2283 + //force ourselves into the EndOfData state
1.2284 + if(iTransferState != EEndOfData)
1.2285 + {
1.2286 + iDataPathCompletedErrorCode = KErrNone;
1.2287 + TRAP_IGNORE(DoEndOfDataL());
1.2288 + }
1.2289 +
1.2290 + iCompleteCallback->SignalSinkComplete(KErrNone);
1.2291 + return KErrNone;
1.2292 + }
1.2293 +
1.2294 +
1.2295 +
1.2296 + return iEventHandler.SendEventToClient(aEvent);
1.2297 + }
1.2298 +
1.2299 +CMMFDataPath::CCompleteCallback::CCompleteCallback(CMMFDataPath& aDataPath, TBool aWaitForSink)
1.2300 + : CActive(EPriorityStandard),
1.2301 + iDataPath(aDataPath),
1.2302 + iWaitForSink(aWaitForSink)
1.2303 + {
1.2304 + CActiveScheduler::Add(this);
1.2305 + }
1.2306 +
1.2307 +CMMFDataPath::CCompleteCallback::~CCompleteCallback()
1.2308 + {
1.2309 + Cancel();
1.2310 + }
1.2311 +
1.2312 +void CMMFDataPath::CCompleteCallback::SignalDataPathComplete(TInt aDataPathError)
1.2313 + {
1.2314 + iDataPathComplete = ETrue;
1.2315 + iDataPathError = aDataPathError;
1.2316 + if (!IsActive())
1.2317 + {
1.2318 + // Signal ourselves to run with the given completion code
1.2319 + TRequestStatus* status = &ActiveStatus();
1.2320 + User::RequestComplete(status, KErrNone);
1.2321 + }
1.2322 + }
1.2323 +
1.2324 +void CMMFDataPath::CCompleteCallback::SignalSinkComplete(TInt aSinkError)
1.2325 + {
1.2326 + iSinkComplete = ETrue;
1.2327 + iSinkError = aSinkError;
1.2328 + if (!IsActive())
1.2329 + {
1.2330 + // Signal ourselves to run with the given completion code
1.2331 + TRequestStatus* status = &ActiveStatus();
1.2332 + User::RequestComplete(status, KErrNone);
1.2333 + }
1.2334 + }
1.2335 +
1.2336 +
1.2337 +TRequestStatus& CMMFDataPath::CCompleteCallback::ActiveStatus()
1.2338 + {
1.2339 + SetActive();
1.2340 + return iStatus;
1.2341 + }
1.2342 +
1.2343 +void CMMFDataPath::CCompleteCallback::DoCancel()
1.2344 + {
1.2345 + }
1.2346 +
1.2347 +void CMMFDataPath::CCompleteCallback::RunL()
1.2348 + {
1.2349 + if (iWaitForSink)
1.2350 + {
1.2351 + if (iDataPathComplete && iSinkComplete)
1.2352 + {
1.2353 +#ifdef _DP_DEBUG
1.2354 + RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this);
1.2355 +#endif
1.2356 +
1.2357 + TRAP_IGNORE(iDataPath.DoStopL())
1.2358 +
1.2359 + iDataPathComplete = EFalse;
1.2360 + iSinkComplete = EFalse;
1.2361 +
1.2362 + // if we have to wait for the sink to complete, always use the sink error
1.2363 + iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iSinkError);
1.2364 + }
1.2365 + }
1.2366 + else if (iDataPathComplete)
1.2367 + {
1.2368 +#ifdef _DP_DEBUG
1.2369 + RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this);
1.2370 +#endif
1.2371 +
1.2372 + TRAP_IGNORE(iDataPath.DoStopL())
1.2373 +
1.2374 + iDataPathComplete = EFalse;
1.2375 + iSinkComplete = EFalse;
1.2376 +
1.2377 + iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iDataPathError);
1.2378 + }
1.2379 + }
1.2380 +
1.2381 +EXPORT_C TInt CMMFDataPath::SetBlockLength(TUint aBlockLength)
1.2382 + {
1.2383 + MMMFDevSoundCustomInterfaceFileBlockLength* fileBlockLengthCI = NULL;
1.2384 + TInt err = KErrNotSupported;
1.2385 + if (iCodec)
1.2386 + {
1.2387 + err = iCodec->ExtensionInterface(KUidCustomInterfaceDevSoundFileBlockLength.iUid, (TAny*&)fileBlockLengthCI);
1.2388 + }
1.2389 +
1.2390 + if (err == KErrNone)
1.2391 + {
1.2392 + fileBlockLengthCI->SetFileBlockLength(aBlockLength);
1.2393 + }
1.2394 +
1.2395 + return err;
1.2396 + }
1.2397 +