os/mm/mmlibs/mmfw/src/server/BaseClasses/mmfdatapath2.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // source\server\mmfdatapath2.cpp
    15 // 
    16 //
    17 
    18 #include <e32math.h>
    19 #include <mmf/common/mmffourcc.h>
    20 #include <mmf/common/mmfpaniccodes.h>
    21 #include <mmf/server/mmfaudiooutput.h>
    22 #include <mmf/server/mmfaudioinput.h>
    23 #include "mmfdatapath2.h"
    24 #include "mmfclientaudiostreamutils.h"
    25 #include <mmf/common/mmfaudio.h>
    26 #include <mmf/plugin/mmfcodecimplementationuids.hrh> // KUidMmfCodecAudioSettings
    27 #include <mmf/server/devsoundstandardcustominterfaces.h>
    28 #include <mmf/server/mmffile.h>
    29 #include <mda/client/resource.h>
    30 
    31 static void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber)
    32 	{
    33 	_LIT(KMMFDataPathPanicCategory, "MMFDataPath2");
    34 	User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber);
    35 	}
    36 
    37 /**
    38 Allocates and constructs a data path.
    39 
    40 Use this function if the codec UID is not already known by CMMFController
    41 and there is no data path ambiguity - ie only one data path is possible.
    42 
    43 Will create codec via fourCC.
    44 
    45 @param  aEventHandler
    46         Installs an event handler to provide message passing between clients and sources/sinks.
    47 
    48 @return Newly constructed data path object.
    49 */
    50 
    51 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(MAsyncEventHandler& aEventHandler)
    52 	{
    53 	CMMFDataPath2* self = new(ELeave) CMMFDataPath2(TMediaId(), aEventHandler);
    54 	CleanupStack::PushL(self);
    55 	self->ConstructL();
    56 	CleanupStack::Pop();
    57 	return self;
    58 	}
    59 
    60 
    61 /**
    62 Allocates and constructs a data path according to the specified media ID.
    63 
    64 Use this function if the codec UID is not already known by CMMFController
    65 and there is ambiguity with the data path ie. there is more than one possible data path.
    66 
    67 @param  aMediaId
    68         Optional media ID parameter when there are multiple media types.
    69 @param  aEventHandler
    70         Installs an event handler to provide message passing between clients and sources/sinks.
    71 
    72 @return A newly constructed data path object.
    73 */
    74 
    75 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
    76 	{
    77 	CMMFDataPath2* self = new(ELeave) CMMFDataPath2(aMediaId, aEventHandler);
    78 	CleanupStack::PushL(self);
    79 	self->ConstructL();
    80 	CleanupStack::Pop();
    81 	return self;
    82 	}
    83 
    84 /**
    85 Allocates and constructs a data path according to the specified codec UID.
    86 
    87 Use this function if the codec UID is already known by CMMFController
    88 and there is no data path ambiguity ie. only one data path is possible
    89 will create codec explicitly using the supplied codec Uid
    90 
    91 @param  aCodecUid
    92         Optional mediaID parameter when there are multiple media types
    93 @param  aEventHandler
    94         Installs an event handler to provide message passing between clients and sources/sinks.
    95 
    96 @return A newly constructed data path object.
    97 */
    98 
    99 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler)
   100 	{
   101 	CMMFDataPath2* self = new(ELeave) CMMFDataPath2(TMediaId(), aEventHandler);
   102 	CleanupStack::PushL(self);
   103 	self->ConstructL(aCodecUid);
   104 	CleanupStack::Pop();
   105 	return self;
   106 	}
   107 
   108 
   109 /**
   110 Allocates and constructs a data path according to the specified codec UID.
   111 
   112 Use this function if the codec UID is already known by CMMFController
   113 and there is ambiguity ie. more than one possible data path.
   114 TMediaId used to select the path.
   115 
   116 @param  aCodecUid
   117 		The codec UID.
   118 @param  aMediaId
   119         Optional mediaID parameter when there are multiple media types.
   120 @param  aEventHandler
   121         Installs an event handler to provide message passing between clients and sources/sinks.
   122 
   123 @return A newly constructed data path object.
   124 */
   125 EXPORT_C CMMFDataPath2* CMMFDataPath2::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler)
   126 	{
   127 	CMMFDataPath2* self = new(ELeave) CMMFDataPath2(aMediaId, aEventHandler);
   128 	CleanupStack::PushL(self);
   129 	self->ConstructL(aCodecUid);
   130 	CleanupStack::Pop();
   131 	return self;
   132 	}
   133 
   134 CMMFDataPath2::CMMFDataPath2(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) 
   135 	: CMMFDataPath(aMediaId, aEventHandler), iTimeLeftToPlayComplete(-1)
   136 	{
   137 	}
   138 		
   139 void CMMFDataPath2::ConstructL(TUid aCodecUid)
   140 	{
   141 	CMMFDataPath::ConstructL(aCodecUid);
   142 	iRepeatTrailingSilenceTimer = CPeriodic::NewL(CActive::EPriorityStandard); 
   143 	}
   144 	
   145 /**
   146 Standard destructor.
   147 */
   148 
   149 CMMFDataPath2::~CMMFDataPath2()
   150 	{
   151 	if(iRepeatTrailingSilenceTimer)
   152 		{
   153 		iRepeatTrailingSilenceTimer->Cancel();
   154 		delete iRepeatTrailingSilenceTimer;
   155 		}
   156 	}
   157 
   158 TInt CMMFDataPath2::RepeatTrailingSilenceTimerComplete(TAny* aDataPath)
   159 	{
   160 	CMMFDataPath2* dataPath = static_cast<CMMFDataPath2*>(aDataPath);
   161 	
   162 	TRAPD(err, dataPath->DoRepeatTrailingSilenceTimerCompleteL());
   163 	if (err != KErrNone)
   164 		{
   165 		dataPath->DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err);
   166 		}	
   167 	return KErrNone;
   168 	}
   169 	
   170 TInt CMMFDataPath2::DoRepeatTrailingSilenceTimerCompleteL()
   171 	{
   172 	//cancel this periodic timer
   173 	iRepeatTrailingSilenceTimer->Cancel();
   174 	if(iTimeLeftToPlayComplete.Int64()>0)
   175 		{
   176 		iTimeLeftToPlayComplete=0;
   177 		}
   178 		
   179 	if (iTrailingSilenceLeftToPlay.Int64() > 0)
   180 		{
   181 		PlaySilence();
   182 		}
   183 	else
   184 		{
   185 		SetPositionL(iPlayWindowStartPosition);
   186 		iTimeLeftToPlayComplete=-1;
   187 		FillSourceBufferL();		
   188 		}
   189 	return KErrNone;
   190 	}
   191 	
   192 void CMMFDataPath2::PlaySilence()
   193 	{		
   194 	// iRepeatTrailingSilenceTimer->After() takes a TTimeIntervalMicroSeconds32
   195 	// so for longer periods of silence call it repeatedly with KMaxTInt lengths
   196 	TTimeIntervalMicroSeconds32 silence;
   197 	if(iTimeLeftToPlayComplete.Int64() > 0)
   198 		{
   199 		silence = I64INT(iTimeLeftToPlayComplete.Int64());
   200 		}
   201 	else if (iTrailingSilenceLeftToPlay.Int64() > KMaxTInt)
   202 		{
   203 		silence = KMaxTInt;
   204 		iTrailingSilenceLeftToPlay = iTrailingSilenceLeftToPlay.Int64() - KMaxTInt;
   205 		}
   206 	else
   207 		{
   208 		silence = I64INT(iTrailingSilenceLeftToPlay.Int64());
   209 		iTrailingSilenceLeftToPlay = 0;
   210 		}
   211 	iRepeatTrailingSilenceTimer->Start(silence, silence , TCallBack(RepeatTrailingSilenceTimerComplete, this));
   212 	}
   213 
   214 /* 
   215  *  FillSourceBufferL
   216  * 
   217  *	Function to get data from the datapath's iDataSource
   218  */
   219 
   220 void CMMFDataPath2::DoFillSourceBufferL()
   221 	{
   222 #ifdef _DP_DEBUG
   223 	RDebug::Print(_L("DP::FillSourceBufferL tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   224 #endif
   225 
   226 	__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport)), Panic(EMMFDataPathPanicBadState,__LINE__)); 
   227 
   228 	// clear the no-more-source flag here (as well as in PlayL()) because 
   229 	// there may have been a re-position since the last call to BufferFilledL()
   230 	iNoMoreSourceData = EFalse;
   231 
   232 	if(!iObtainingAsyncSourceBuffer) 
   233 		{//this is a normal request for data. 
   234 		//If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL
   235 		iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with
   236 		iSourceBuffer->SetStatus(EBeingFilled);
   237 		iSourceBuffer->SetLastBuffer(EFalse);
   238 		}
   239 
   240 #ifdef _DP_DEBUG
   241 	RDebug::Print(_L("DP asking for buffer %d  - ptr=0x%x   (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this);	
   242 #endif
   243 
   244 	iSourceBufferWithSource = ETrue;
   245 
   246 	// wait for BufferFilled callback from source. Do this here as some sources cause
   247 	//re-entrancy into data path via BufferFilledL
   248 	ChangeDataPathTransferState(EWaitSource);  
   249 
   250 	iDataSource->FillBufferL(iSourceBuffer, this, iMediaId);
   251 
   252 #ifdef _DP_DEBUG
   253 	RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   254 #endif
   255 	}
   256 
   257 /**
   258 Runs the clip depending on the current data path and transfer state.
   259 
   260 For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData.
   261 */
   262 void CMMFDataPath2::RunL()
   263 	{
   264 #ifdef _DP_DEBUG 
   265 	RDebug::Print(_L("DP::RunL transfer state %d, iPausedCalled %d, tick-%d   (this 0x%x)\n"),iTransferState, iPauseCalled, User::TickCount(),this);
   266 #endif
   267 	
   268 	switch (iState)
   269 		{
   270 	case EStopped:
   271 		break;
   272 	case EPrimed: // In the paused state we still continue to feed buffers to the sink. The sink (DevSound) handles the logic of whether the buffers should be emptied
   273 	    {
   274 	    if (!iPauseCalled || !iIsUsingResumeSupport)
   275 	        break;
   276 	    }	    
   277 	    // fall-through
   278 	case EPlaying:
   279 	case ERecording:
   280 	case EConverting:
   281 		switch (iTransferState)
   282 			{
   283 		case EWaitSink:
   284 		case EWaitSource:
   285 			break;
   286 		case EInitializeSink:
   287 			InitializeSinkL();
   288 			break;
   289 		case EInitializeSource:
   290 			InitializeSourceL();
   291 			break;
   292 		case ENeedSourceData:
   293 			FillSourceBufferL();
   294 			break;
   295 		case ENeedSinkData:
   296 			FillSinkBufferL();
   297 			break;
   298 		case ENeedToMatchSourceToSink:
   299 			FillSinkBufferL();
   300 			break;
   301 		case ESendDataToSink:
   302 			EmptySinkBufferL();
   303 			break;
   304 		case EEndOfData:
   305 			EndOfData();
   306 			break;
   307 			}
   308 		break;
   309 	default:
   310 		break;
   311 		}
   312 #ifdef _DP_DEBUG
   313 	RDebug::Print(_L("DP::RunL DONE\n"));
   314 #endif
   315 	}
   316 	
   317 /* 
   318  *  FillSourceBufferL
   319  * 
   320  *	Function to get data from the datapath's iDataSource
   321  */
   322 
   323 void CMMFDataPath2::FillSourceBufferL()
   324 	{
   325 	__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled && iIsUsingResumeSupport) ), Panic(EMMFDataPathPanicBadState,__LINE__)); 
   326 
   327 	//if the silence timer is active then dont propagate the request
   328 	if(iRepeatTrailingSilenceTimer->IsActive())
   329 		{
   330 		return;
   331 		}
   332 	
   333 	//play the silence period and dont propagate the request
   334 	if(iTrailingSilenceLeftToPlay>0 || iVerifyPlayComplete)
   335 		{
   336 		if(iVerifyPlayComplete)//case when the trailing silence is zero
   337 			{
   338 			if (!*iDisableAutoIntent && iDrmSource)
   339 				{
   340 				CMMFFile* file = static_cast<CMMFFile*>(iDrmSource);
   341 				TInt err = file->ExecuteIntent(ContentAccess::EPlay);
   342 				if (err != KErrNone)
   343 					{
   344 					DoSendEventToClient(KMMFEventCategoryPlaybackComplete, err);
   345 					return;
   346 					}
   347 				}
   348 			
   349 			//Retrieve the current play time and add "duration-currentplaytime" to the silence period
   350 			//This is to ensure that silence timer is not started before the previous play is actually completed by the devsound
   351 			TTimeIntervalMicroSeconds currentTime = CalculateAudioOutputPosition();
   352 			if(currentTime.Int64()>iPlayWindowStartPosition.Int64())
   353 				{
   354 				iTimeLeftToPlayComplete = iPlayWindowEndPosition.Int64()-currentTime.Int64();
   355 				}
   356 			else
   357 				{
   358 				iTimeLeftToPlayComplete = 0;
   359 				}
   360 
   361 			iVerifyPlayComplete = EFalse;
   362 			}
   363 		if(iTrailingSilenceLeftToPlay==0 && iTimeLeftToPlayComplete==0)
   364 			{
   365 			SetPositionL(iPlayWindowStartPosition);
   366 			iTimeLeftToPlayComplete=-1;
   367 			}
   368 		else
   369 			{
   370 			PlaySilence();
   371 			return;
   372 			}
   373 		}
   374 	
   375 	DoFillSourceBufferL();
   376 	}
   377 
   378 
   379 /** 
   380 Indicates the data source has filled the specified buffer.
   381 
   382 Called by the CMMFDataPath2's MDataSource when it has filled the buffer.
   383 
   384 @param aBuffer
   385        A pointer to the filled buffer.
   386 */
   387 void CMMFDataPath2::BufferFilledL(CMMFBuffer* aBuffer)
   388 	{	
   389 #ifdef _DP_DEBUG
   390 	RDebug::Print(_L("DP::BufferFilledL src has filled buffer %d (ptr=0x%x) with %d bytes EoF = %d  tick-%d    (this 0x%x)\n"),aBuffer->FrameNumber(),aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), User::TickCount(),this);
   391 #endif
   392 	
   393 	TBool isInTruePause = (iState == EPrimed  && iPauseCalled && iIsUsingResumeSupport);
   394 	//state only used if we are passing data
   395 	__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || isInTruePause), Panic(EMMFDataPathPanicBadState,__LINE__));
   396 
   397 	__ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__)); 
   398 	
   399 	//if we have been asked to repeat and this is the last buffer, reset last buffer flag and send to the device
   400 	if(aBuffer!= NULL && aBuffer->LastBuffer())
   401 		{
   402 		iNumberOfTimesPlayed++;
   403 		if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever)
   404 			{
   405 			aBuffer->SetLastBuffer(EFalse);
   406 			
   407 			//this will trigger the trailing silence timer next time a buffer is requested.
   408 			iTrailingSilenceLeftToPlay = iTrailingSilence;
   409 			iVerifyPlayComplete = ETrue;
   410 			}
   411 		}		
   412 		
   413 	iSourceBufferWithSource = EFalse;
   414 
   415 	//Has the datapath stopped running, if so were not interested in any callbacks.
   416 	if(iState == EStopped || (iState == EPrimed  && !isInTruePause))
   417 		{
   418 #ifdef _DP_DEBUG
   419 		RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d  iPauseCalled=%d  (this 0x%x)\n"),iState, iPauseCalled,this);
   420 #endif
   421 		return;
   422 		}
   423 
   424 #ifdef REPOSITION_SPEEDUP
   425 	// if the source has been re-positioned, then go & get some more source data now
   426 	if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber)
   427 		{
   428 #ifdef _DP_DEBUG
   429 		RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this);
   430 #endif
   431 		ChangeDataPathTransferState(ENeedSourceData);
   432 		return;
   433 		}
   434 #endif //REPOSITION_SPEEDUP
   435 
   436 	//bufer is NULL, indicating no more source data.
   437 	if (!aBuffer)
   438 		{
   439 		//If we only hold a reference to the source buffer, set that to NULL
   440 		if(iSnkBufRef)
   441 			{
   442 			iSourceBuffer = NULL;
   443 			}
   444 		
   445 		iNoMoreSourceData = ETrue;
   446 
   447 		if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data
   448 		  iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data
   449 			{
   450 			ChangeDataPathTransferState(EEndOfData);
   451 			}
   452 		else //sink buffer is with datapath, see if there is anything to send to sink
   453 			{
   454 			ChangeDataPathTransferState(ENeedToMatchSourceToSink);
   455 			}
   456 		
   457 #ifdef _DP_DEBUG
   458 		RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this);
   459 #endif
   460 		return;
   461 		} 
   462 
   463 	
   464 	//We were waiting for a response from the source to get an asynchronous buffer.
   465 	//We now have it, and we proceed to transfer this data to the sink.
   466 	if	(iObtainingAsyncSourceBuffer)
   467 		{
   468 		iObtainingAsyncSourceBuffer = EFalse;
   469 		}
   470 	
   471 
   472 	aBuffer->SetStatus(EFull);
   473 
   474 	if(iSourceBuffer != aBuffer)
   475 		{//buffer has been changed by the source
   476 		iSourceBuffer = aBuffer;
   477 		if (!(iBuffersToUse & ENeedSinkBuffer))
   478 			{//we only need one buffer and use source
   479 			iSinkBuffer = iSourceBuffer;
   480 			iSnkBufRef = ETrue;
   481 			}
   482 #ifdef _DP_DEBUG
   483 	RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d   iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this);
   484 #endif	
   485 		}
   486 	//Is this the last buffer from the source (0 length or LastBuffer flag set)
   487 	//or have reached the end of the play window; we only look at the play window here
   488 	//if we are converting. For conversion we look at the data we have read. This is then passed onto 
   489 	//the source
   490 	if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() ||
   491 		(((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition ))) 
   492 		{
   493 		//When it resumes in silence , position of the buffer is in end so we need to skip the increament.
   494 		if(!iPauseCalledInsilence) 
   495 		{    
   496 			iNumberOfTimesPlayed++;
   497 		}else
   498 		{
   499 			iPauseCalledInsilence=EFalse;
   500 		}
   501 		if ((iNumberOfTimesPlayed <= iNumberOfTimesToRepeat) || iNumberOfTimesToRepeat == KMdaRepeatForever)
   502 			{
   503 			iSourceBuffer->SetLastBuffer(EFalse);
   504 			//this will trigger the trailing silence timer next time a buffer is requested.
   505 			iTrailingSilenceLeftToPlay = iTrailingSilence;
   506 			iVerifyPlayComplete = ETrue;
   507 			}
   508 		else
   509 			{		
   510 	#ifdef _DP_DEBUG
   511 			RDebug::Print(_L("DP::BufferFilledL end of input data  tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   512 			RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize());
   513 			RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer());
   514 			RDebug::Print(_L("InputPosition()=%d  >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64()));
   515 	#endif
   516 			iNoMoreSourceData = ETrue;
   517 			iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window
   518 			}
   519 		}
   520 
   521 
   522 	if (!iCodec)
   523 		{
   524 		ChangeDataPathTransferState(ESendDataToSink);
   525 		}
   526 	else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it
   527 		{
   528 		ChangeDataPathTransferState(ENeedToMatchSourceToSink);	
   529 		}
   530 	//else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state
   531 
   532 #ifdef _DP_DEBUG
   533 	RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   534 #endif
   535 	}
   536 	
   537 /** 
   538 Sets the data path position.
   539 
   540 @param  aPosition
   541 		The data path position.
   542 */
   543 void CMMFDataPath2::SetPositionL(const TTimeIntervalMicroSeconds& aPosition)
   544 	{//need to map to source position to frame position 
   545 #ifdef _DP_DEBUG
   546 	RDebug::Print(_L("DP::SetPositionL = %d  ticks-%d   (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this);
   547 #endif
   548 
   549 	if (iState == EStopped)
   550 		{
   551 		User::Leave(KErrNotReady); //can only set position if primed
   552 		}
   553 	
   554 	if(iGetTimePlayedSupported)
   555 		{
   556 		TTimeIntervalMicroSeconds timePlayed(0);
   557 		if(iState == EPlaying && iDataSink->DataSinkType() == KUidMmfAudioOutput)
   558 			{
   559 			CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
   560 			CMMFDevSound& devSound = audioOutput->SoundDevice();
   561 			TInt err= devSound.GetTimePlayed(timePlayed);
   562 			if(err == KErrNone)
   563 				{
   564 				iDevSoundRepositionTime = timePlayed.Int64();
   565 				}
   566 			}
   567 		else
   568 			{
   569 			iDevSoundRepositionTime = 0;
   570 			}
   571 		}
   572 	else //roll back to samplesplayed
   573 		{
   574 		//As this will affect the position, we need to know how many bytes were 
   575 		//played when position was updated. Future Position() requests will
   576 		//then use this refernce to determine the current position.
   577 		iReferenceAudioSamplesPlayed = AudioSamplesPlayed();
   578 		}
   579 	
   580 	// Force the new position to be inside the play window (also within the file duration)
   581 	if ( aPosition < iPlayWindowStartPosition )
   582 		{
   583 		iStartPosition = iPlayWindowStartPosition;
   584 		}
   585 	else if ( aPosition > iPlayWindowEndPosition )
   586 		{
   587 		iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played
   588 		}
   589 	else
   590 		{
   591 		iStartPosition = aPosition;
   592 		}
   593 	
   594 	TTimeIntervalMicroSeconds interval;
   595 
   596 	//can only set the position on an MDataSource that is a format object
   597 	//Note: position defaults to source if both source & sink are clips
   598 	if (iDataSource->DataSourceType() == KUidMmfFormatDecode)
   599 		{
   600 		//position is not beyond the end of file
   601 		interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId);
   602 
   603 		// for some reason this code won't compile without these intermediate steps
   604 		TInt64 position = iStartPosition.Int64();
   605 		TInt64 interval64 = interval.Int64();
   606 		if (interval64 == 0)
   607 			User::Leave(KErrDivideByZero); 
   608 		TInt64 datapos64 = position/interval64; 
   609 		iCurrentSourceFrameNumber = I64LOW(datapos64);
   610 
   611 
   612         // Try to set the position directly on the format
   613         TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition));
   614         //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
   615 		}
   616 	else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
   617 		{			
   618 		//position is not beyond the end of file
   619 		interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
   620 
   621 		//convert to TUint - for some reason it won't compile without these intermediate steps
   622 		TInt64 position = iStartPosition.Int64();
   623 		TInt64 interval64 = interval.Int64();
   624 		if (interval64 == 0)
   625 			User::Leave(KErrDivideByZero); 
   626 		TInt64 datapos64 = position/interval64; 
   627 		iCurrentSinkFrameNumber = I64LOW(datapos64);
   628 
   629 
   630         // Try to set the position directly on the format
   631         TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition));
   632         //NB: don't worry about error, since we'll reposition anyway when we get the next buffer
   633 		}
   634 	else
   635 		{//can only set position if source or sink is a format
   636 		//If both source and sink are formats position is relative to the source
   637 		User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips
   638 		}
   639 
   640 	if(iCodec) //we have a real codec, must reset it
   641 		{
   642 		iCodec->ResetL(); // Need to preserve sync when resuming play
   643 		}
   644 		
   645 	// Once we've sent the last buffer to the sink it's too late to start
   646 	// changing the state since we may get a RunError(KErrUnderflow) at any time.
   647 	// Once this happens, the sound driver may have unloaded etc..and recovery
   648 	// would be complicated.
   649 	if (iAllDataSentToSink)
   650 		{
   651 		return;
   652 		}
   653 #ifdef _DP_DEBUG
   654 	RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d  iStartPosition=%d  ticks-%d   (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this);	
   655 #endif
   656 	}
   657 
   658 /** 
   659 Gets the data path position.
   660 
   661 @return The data path position.
   662 */
   663 TTimeIntervalMicroSeconds CMMFDataPath2::Position() const
   664 	{
   665 	if ((iState == ERecording) || (iState == EConverting))
   666 		{
   667 		return InputPosition();
   668 		}
   669 	else if(iState == EPlaying)
   670 		{
   671 		return OutputPosition();
   672 		}
   673 	else
   674 		{
   675 		return iStartPosition;
   676 		}
   677 	}
   678 
   679 TTimeIntervalMicroSeconds CMMFDataPath2::OutputPosition() const
   680 	{
   681 	TTimeIntervalMicroSeconds interval;
   682 	TTimeIntervalMicroSeconds position;
   683 
   684     if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
   685         {
   686 		position = CalculateAudioOutputPosition();
   687 #ifdef _DP_DEBUG
   688 		RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64()));
   689 #endif
   690         }
   691 	else if (iDataSink->DataSinkType() == KUidMmfFormatEncode)
   692 		{
   693 		//note Encode format position takes priority if both source & sink are formats?
   694         // try to get the position directly from the format. If that fails, work it out here
   695         TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL());
   696         if (error)//getting the position from the format didn't work so calculate it here
   697             {
   698 		    interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId);
   699 			TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber;
   700 			position = position64;
   701             }
   702 
   703 		TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath2*,this)->Duration(); //need this to check position doesn't exceed duration
   704 		if (position > duration)//this can happen on last buffer 
   705 			{
   706 			position = duration;
   707 			}
   708 #ifdef _DP_DEBUG
   709 		RDebug::Print(_L("DP::OutputPosition  from format= %d\n"),I64INT(position.Int64()));
   710 #endif
   711 		}
   712 	else
   713 		{//can only read output position if sink is a format or an audio output
   714 		return TTimeIntervalMicroSeconds(0);
   715 		}
   716 
   717 	return position;
   718 	}
   719 			
   720 TTimeIntervalMicroSeconds CMMFDataPath2::CalculateAudioOutputPosition() const
   721     {
   722 	//This operation can only be carried out on an Audio Output
   723 	__ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__));
   724 
   725 
   726 	//If we are not playing, simply return where we will play from
   727 	if(iState != EPlaying || iCurrentSinkFrameNumber == 0 || iStartPosition == iPlayWindowEndPosition )
   728 		{
   729 		return iStartPosition;
   730 		}
   731 #ifdef _DP_DEBUG
   732 	RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed);
   733 #endif
   734 
   735 	CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
   736 	CMMFDevSound& devSound = audioOutput->SoundDevice();
   737 	
   738 	TTimeIntervalMicroSeconds devSoundTimePlayed(0);
   739 	TInt64 timePlayed(0);
   740 	TInt err = KErrNone;
   741 	if(iGetTimePlayedSupported)
   742 		{
   743 		err= devSound.GetTimePlayed(devSoundTimePlayed);
   744 		if(err == KErrNone)
   745 			{
   746 			timePlayed = devSoundTimePlayed.Int64()-iDevSoundRepositionTime.Int64();
   747 			}
   748 		}
   749 	else //Roll back to SamplesPlayed
   750 		{
   751 		TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed;
   752 		TMMFCapabilities devSoundConfig = devSound.Config();
   753 		TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig);
   754 	#ifdef _DP_DEBUG
   755 		RDebug::Print(_L("samplingFreq %d\n"),samplingFreq);
   756 	#endif
   757 		
   758 		TReal timePlayedSeconds = 0;
   759 		if(samplesPlayed)
   760 			{
   761 			timePlayedSeconds = samplesPlayed/samplingFreq;
   762 			}
   763 		timePlayed = I64DOUBLECAST(timePlayedSeconds * 1000000);
   764 
   765 	#ifdef _DP_DEBUG
   766 		RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed));
   767 	#endif
   768 		}
   769 	if(err == KErrNone)
   770 		{
   771 		// When Resume is not supported. Datapath simulates Pause() through DevSound's Stop
   772 		// the time played is lost. So we need to saved the last position
   773 		// On the opposite, when Resume is supported the time played returned by DevSound 
   774 		// reflects the real position, so there is no needed to recalculate at least playwindow is being used
   775 		// Finally, if Resume is supported but is not used the position also need to be saved
   776 		if(!iIsUsingResumeSupport || iPlayWindowStartPosition.Int64() > 0)
   777 			{
   778 			timePlayed = timePlayed + iStartPosition.Int64();	
   779 			}
   780 		
   781 		//During repeats. we need to reset the positin manually to playstart once playend is reached
   782 		//this is because the bytes returned by devsound are not accurate in all the cases
   783 		if(iRepeatTrailingSilenceTimer->IsActive() || iTimeLeftToPlayComplete==0)//loop play
   784 			{
   785 			if(iTimeLeftToPlayComplete==0)
   786 				{
   787 				timePlayed = iPlayWindowStartPosition.Int64();
   788 				}
   789 			}
   790 		else if(timePlayed>=(iPlayWindowEndPosition.Int64()+1))
   791 			{
   792 			timePlayed = iPlayWindowStartPosition.Int64();
   793 			}
   794 		}
   795 	
   796 	return TTimeIntervalMicroSeconds(timePlayed);
   797     }
   798    
   799 /** 
   800 Stops playing.
   801 
   802 Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError 
   803 to the client if an error occurs.
   804 */
   805 void CMMFDataPath2::Stop()
   806 	{ 
   807 #ifdef _DP_DEBUG
   808 	RDebug::Print(_L("DP::Stop current state=%d  tick-%d   (this 0x%x)\n"), iTransferState, User::TickCount(),this);
   809 #endif
   810 	if ((iDataPathCreated)  && (iState != EStopped))
   811 		{ 
   812 		TRAPD(err, DoStopL());
   813 		if (err)
   814 			{
   815 			DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
   816 			}
   817 		}
   818 	}
   819 
   820 void CMMFDataPath2::DoStopL()
   821 	{
   822 	CMMFDataPath::DoStopL();
   823 	iRepeatTrailingSilenceTimer->Cancel();
   824 	iIsUsingResumeSupport = EFalse;
   825 	}
   826 	
   827 /** 
   828 Pauses playing.
   829 
   830 Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs.
   831 */
   832 void CMMFDataPath2::Pause()
   833 	{
   834 #ifdef _DP_DEBUG
   835 	RDebug::Print(_L("DP::Pause, on src buff %d  sink buf %d   (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);			
   836 	RDebug::Print(_L("DP::Pause current state=%d  tick-%d    (this 0x%x)\n"),iTransferState, User::TickCount(),this);
   837 #endif
   838 
   839 	TRAPD(err, DoPauseL());
   840 	
   841 	if (err)
   842 		{
   843 		DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
   844 		}
   845 #ifdef _DP_DEBUG
   846 	RDebug::Print(_L("DP::Pause - DONE tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   847 #endif
   848 	}
   849 
   850 EXPORT_C void CMMFDataPath2::PreEmptionPause()
   851     {
   852     TRAPD(err, DoPreEmptionPauseL());
   853     
   854     if (err)
   855         {
   856         DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err);
   857         }
   858     }
   859 
   860 /** 
   861 Pauses playing.
   862 
   863 Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs.
   864 */
   865 void CMMFDataPath2::DoPreEmptionPauseL()
   866 	{
   867 
   868     if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting) && ( iDataSink->DataSinkType() == KUidMmfAudioOutput )))
   869         {
   870         iDataSource->SourcePauseL();        // propagate state change to source
   871         SetPositionL(Position());
   872         iIsUsingResumeSupport = EFalse;
   873 
   874         iDataSink->SinkStopL();
   875 	    iPauseCalled = ETrue;               // indicate pause is called
   876      
   877 	    iState = EPrimed;                   // go back to primed state (we're not playing)
   878 		
   879 		iSinkBufferWithSink = EFalse;
   880 		iSourceBufferWithSource = EFalse;
   881   
   882 		ResetRefBuffers();                  // buffer references may not be valid any more
   883   
   884 		Cancel(); //Stop the state machine		
   885 	    }
   886 	if(iState == ERecording)
   887 	     {
   888 	     User::Leave(KErrNotSupported);
   889 	     }
   890 	 iRepeatTrailingSilenceTimer->Cancel();
   891 	}
   892 	
   893 void CMMFDataPath2::DoPauseL()
   894 	{
   895 #ifdef _DP_DEBUG
   896 	RDebug::Print(_L("DP::DoPauseL tick-%d   (this 0x%x)\n"),User::TickCount(),this);
   897 #endif
   898 
   899 
   900 	if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting)))
   901 		{
   902 		if (iDataSink->DataSinkType() == KUidMmfAudioOutput && iIsResumeSupported)
   903 			{
   904 			iDataSink->SinkPauseL();
   905 			// When true pause is supported only the datapath's position 
   906 			// should be updated, MDataSource position should be changed
   907 			iStartPosition = Position();
   908 			iIsUsingResumeSupport = ETrue;
   909 			if(iRepeatTrailingSilenceTimer->IsActive())
   910 			{   
   911 				iPauseCalledInsilence=ETrue;
   912 			}   
   913 			// If we wait for the sink to complete play, then we do not proceed with supplying the buffers to the sink
   914 			// In this case we need to reset the buffers so that InitializeSinkL won't attempt bringing in new ones
   915 			if (iTransferState == EWaitSink)
   916 				{
   917 				ResetRefBuffers();
   918 				}
   919 			}
   920 		else
   921 			{
   922 			// If we use resume support, then there is no need to pause source as we would continue to supply buffers to the sink
   923 			// Here we are not using resume support, thus we're pausing the source
   924 			iDataSource->SourcePauseL();		
   925 			SetPositionL(Position());
   926 			iDataSink->SinkStopL();
   927 			ResetRefBuffers();                  // buffer references may not be valid any more
   928   
   929 			Cancel(); //Stop the state machine					
   930 			}
   931 		iPauseCalled = ETrue;				// indicate pause is called
   932 		
   933 		iState = EPrimed;					// go back to primed state (we're not playing)
   934 		}
   935 	else if(iState == ERecording)
   936 		{
   937 		iPauseCalled = ETrue;
   938 #ifdef _DP_DEBUG
   939 		RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n"));
   940 #endif
   941 		iDataSource->SourcePauseL();
   942 		}
   943 	iRepeatTrailingSilenceTimer->Cancel();
   944 #ifdef _DP_DEBUG
   945 	RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed);
   946 	RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d   (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this);
   947 #endif
   948 	}
   949 
   950 /**
   951 Cancels the silence timer.
   952 */
   953 void CMMFDataPath2::DoCancel()
   954 	{
   955 	//cancel repeats
   956 	iRepeatTrailingSilenceTimer->Cancel();
   957 	iNumberOfTimesToRepeat=0;
   958 	}
   959 
   960 /**
   961 Allocates buffers in preparation to play.
   962 
   963 Must be called before calling PlayL().
   964 
   965 iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink
   966 For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL().
   967 */
   968 void CMMFDataPath2::PrimeL()
   969 	{
   970 	CMMFDataPath::PrimeL();
   971 	if(iDataSink->DataSinkType() == KUidMmfAudioOutput)
   972 		{
   973 		CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink);
   974 		CMMFDevSound& devSound = audioOutput->SoundDevice();
   975 		iGetTimePlayedSupported = devSound.IsGetTimePlayedSupported();
   976 		iIsResumeSupported = devSound.IsResumeSupported();
   977 		iIsUsingResumeSupport = EFalse;
   978 		iPauseCalledInsilence = EFalse;
   979 		}
   980 	}
   981 
   982 /**
   983 Starts an active scheduler 'play' loop.
   984 
   985 Can only play from the primed state.
   986 */
   987 void CMMFDataPath2::PlayL()
   988 	{
   989 
   990 #if defined(__PROFILING)
   991 	RDebug::ProfileEnd(1);
   992 #endif  // defined(__PROFILING)
   993 
   994 #ifdef _DP_DEBUG
   995 	RDebug::Print(_L("DP::PlayL, on src buff %d  sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this);		
   996 	RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64()));		
   997 #endif
   998 
   999 	if ((iDataPathCreated) && (iState == EPrimed))
  1000 		{
  1001 		//can only play from the primed state
  1002 
  1003 		if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed
  1004 			{
  1005 			//When pause is called silence,we need to send the buffer while resume so icansendbuffer should enabled
  1006 			if(!iPauseCalledInsilence)
  1007 			{
  1008 				iDataSink->SinkPrimeL(); //propagate change down to sink
  1009 			}
  1010 			iPauseCalled = EFalse;
  1011 			}
  1012 
  1013 		iCurrentSourceFrameNumber = 0; //reset to beginning
  1014 		iCurrentSinkFrameNumber = 0; //reset to beginning
  1015 
  1016 		iSourceBufferWithSource = EFalse;
  1017 		iSinkBufferWithSink = EFalse;
  1018 
  1019 		iNoMoreSourceData = EFalse;
  1020 		iAllDataSentToSink=EFalse;
  1021 		iDataPathCompletedErrorCode=KErrNone;
  1022 
  1023 		if(!iIsResumeSupported || !iIsUsingResumeSupport)
  1024 			{
  1025 			SetPositionL( iStartPosition );
  1026 			}
  1027 		iReferenceAudioSamplesPlayed = 0;
  1028 		iReferenceAudioSamplesRecorded = 0;
  1029 		
  1030 		//complete a request on iStatus to invoke play code
  1031 		iDataSource->SourcePlayL(); //propagate state change to source
  1032 
  1033 		// This need to be done always since CMMFAudioOutput::EmptyBuffer
  1034 		// doesn't start playback anymore
  1035 		iDataSink->SinkPlayL(); //propogate state change to sink
  1036 
  1037 		if (iDataSink->DataSinkType() == KUidMmfAudioOutput)
  1038 			iState = EPlaying;
  1039 		else if(iDataSource->DataSourceType() == KUidMmfAudioInput)
  1040 			iState = ERecording;
  1041 		else
  1042 			iState = EConverting;
  1043 
  1044 		//need to re-initialize any buffer(s) that we only own references to
  1045 		ChangeDataPathTransferState(EInitializeSink);
  1046 		}
  1047 	iDevSoundRepositionTime = 0;
  1048 	if(!iRetainRepeatInfo)
  1049 		{
  1050 		iNumberOfTimesPlayed = 0;
  1051 		iTimeLeftToPlayComplete = -1;
  1052 		iVerifyPlayComplete = EFalse;
  1053 		}
  1054 	iRetainRepeatInfo = EFalse;
  1055 #ifdef _DP_DEBUG
  1056 	RDebug::Print(_L("DP::Play - DONE\n"));		
  1057 #endif
  1058 	}
  1059 
  1060 /**
  1061 Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL().
  1062 Typically if buffers are created asychronously, the datapath doesn't own the buffer
  1063 so leaves cleanup handling to the owner sources/sinks.
  1064 
  1065 Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive, 
  1066 iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped.
  1067 */
  1068 void CMMFDataPath2::ResetL()
  1069 	{
  1070 	CMMFDataPath::ResetL();
  1071 	iDrmSource = NULL;
  1072 	}
  1073 
  1074 /**
  1075 Sets the number of times the audio sample is to be repeated during the
  1076 playback operation.
  1077 
  1078 A period of silence can follow each playing of the sample. The audio
  1079 sample can be repeated indefinitely.
  1080 
  1081 @param   aRepeatNumberOfTimes
  1082          The number of times the audio sample, together with
  1083          the trailing silence, is to be repeated. If this is
  1084          set to KMdaRepeatForever, then the audio
  1085          sample, together with the trailing silence, is
  1086          repeated indefinitely or until Stop() is
  1087          called. If this is set to zero, then the audio sample
  1088          is not repeated.
  1089 @param   aTrailingSilence
  1090          The time interval of the trailing silence in microseconds.
  1091 
  1092 */
  1093 EXPORT_C void CMMFDataPath2::SetRepeats(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence)
  1094 	{
  1095 	iNumberOfTimesToRepeat=aRepeatNumberOfTimes;
  1096 	iTrailingSilence=aTrailingSilence;
  1097 	}
  1098 	
  1099 /**
  1100 Sets the Drm file source and the automatic execute intent flag. This method is used by the controller plugin
  1101 to pass these to the datapath in order to execute the play intent during loop play.
  1102 
  1103 @param  aSource
  1104         Data Source on which the play intent needs to be executed. This is usually the CMMFFile source
  1105 @param  aDisableAutoIntent
  1106 		Boolean variable which states whether the controller plugin or the datapath needs to execute play intent
  1107 		automatically or not.
  1108 */	
  1109 EXPORT_C void CMMFDataPath2::SetDrmProperties(MDataSource* aSource, TBool *aDisableAutoIntent)
  1110 	{
  1111 	iDrmSource = aSource;
  1112 	iDisableAutoIntent = aDisableAutoIntent;
  1113 	}
  1114 
  1115 /**
  1116 This call indicates PlayL not to reset the iNumberOfTimesPlayed property. This method is used by the controller plugin
  1117 during repositioning. PlayL call during seeking should not reset the iNumberOfTimesPlayed property.
  1118 */
  1119 EXPORT_C void CMMFDataPath2::RetainRepeatInfo()
  1120 	{
  1121 	iRetainRepeatInfo = ETrue;
  1122 	}