os/mm/devsound/sounddevbt/src/swcodecwrapper/mmfBtSwCodecPlayDataPath.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2005-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\MmfBtSwCodecPlayDatapath.cpp
    15 // 
    16 //
    17 
    18 #include "mmfBtSwCodecPlayDataPath.h"
    19 #include "mmfbtswcodecwrapper.h"
    20 #include "mmfbtswcodecwrappercustominterfacesuids.hrh"
    21 #include <mmfpaniccodes.h>
    22 #include "mmfBtSwCodecUtility.h"
    23 
    24 #include "MMFBtRoutingSoundDevice.h"
    25 #include "A2dpBTHeadsetAudioIfClientServer.h" // for TRange (will be deprecated)
    26 
    27 CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL(	CRoutingSoundPlayDevice* aSoundDevice,
    28 														TUid aDeviceUid)
    29 	{
    30 	CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath(aSoundDevice,
    31 																		aDeviceUid);
    32 	CleanupStack::PushL(self);
    33 	self->ConstructL();
    34 	CleanupStack::Pop();
    35 	return self;
    36 	}
    37 
    38 
    39 void CMMFSwCodecPlayDataPath::ConstructL()
    40 	{
    41 	iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput);
    42 	iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
    43 	iUtility = CMMFSwCodecUtility::NewL();
    44 	}
    45 
    46 
    47 CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath()
    48 	{
    49 	delete iAudioPlayer;
    50 	delete iSoundDeviceErrorReceiver;
    51 	delete iUtility;
    52 
    53 	TRequestStatus status;
    54 	iSoundDevice->CloseDevice(iDeviceUid, status);
    55 	//TODO there should be a timeout for the line below
    56 	User::WaitForRequest(status);
    57 
    58 	if (iCodec)
    59 		{
    60 		delete iSourceBuffer;
    61 		if (!iCodec->IsNullCodec()) 
    62 			{
    63 			delete iSoundDeviceBuffer;
    64 			}
    65 		}
    66 
    67 #ifdef __USE_MMF_TRANSFERBUFFERS__
    68 	delete iTransferWindow;
    69 
    70 	if(iTransferBuffer)
    71 		{
    72 		iTransferBuffer->Close();
    73 		delete iTransferBuffer;
    74 		}
    75 #endif
    76 
    77 #ifdef __USE_MMF_PTRBUFFERS__
    78 	delete iPtrBufferMemoryBlock;
    79 #endif
    80 	}
    81 
    82 
    83 TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
    84 	{
    85 	TInt error;
    86 	if (iHwDeviceObserver)
    87 		{
    88 		error =  KErrAlreadyExists;
    89 		}
    90 	else
    91 		{
    92 		iHwDeviceObserver = &aObserver;
    93 		error  = KErrNone;
    94 		}
    95 	return error;
    96 	}
    97 
    98 
    99 TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec)
   100 	{
   101 	if (iCodec)
   102 		{
   103 		return KErrNotSupported; //doesn't support multiple codecs		
   104 		}
   105 
   106 	TInt err = KErrNone;
   107 	
   108 	iCodec = &aCodec;
   109 
   110 	// Allocate data buffer
   111 	iSourceBufferSize = iCodec->SourceBufferSize();
   112 	iSoundDevBufferSize = iCodec->SinkBufferSize();
   113 
   114 	if ((!iSourceBufferSize) || (!iSoundDevBufferSize))
   115 		{
   116 		err = KErrArgument; //codec plugin has not specified buffer size		
   117 		}
   118 
   119 	if (err == KErrNone)
   120 		{
   121 #ifdef __USE_MMF_TRANSFERBUFFERS__
   122 		TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer)));
   123 #endif
   124 #ifdef __USE_MMF_PTRBUFFERS__
   125 		TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize));
   126 #else
   127 		TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize));
   128 #endif
   129 		}
   130 	
   131 	if (err == KErrNone)
   132 		{
   133 		if (iCodec->IsNullCodec())
   134 			{//use source buffer for sound device buffer	
   135 			iSoundDeviceBuffer = NULL;
   136 			}
   137 		else
   138 			{//codec needs separate source and sound device buffers
   139 			TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize));
   140 			}
   141 		}
   142 	return err;
   143 	}
   144 
   145 
   146 TInt CMMFSwCodecPlayDataPath::Start()
   147 	{
   148 	TInt startError = KErrNone;
   149 
   150 	if (!iCodec || (!iSoundDevice->Handle()))
   151 		{
   152 		//check that a codec has been added and the sound device is open
   153 		startError = KErrNotReady;
   154 		}
   155 
   156 	if (iState == EPaused)
   157 		{//we are paused so need to resume play
   158 		if (!startError)
   159 			{
   160 #ifdef _SCW_DEBUG
   161 			RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume"));
   162 #endif
   163 			iAudioPlayer->ResumePlaying();
   164 			iState = EPlaying;
   165 			}
   166 		}
   167 	else if (!startError)
   168 		{
   169 #ifdef _SCW_DEBUG
   170 		RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal"));
   171 #endif
   172 		if (!startError)
   173 			{
   174 			iNoMoreSourceData = EFalse;
   175 			iSourceBuffer->SetLastBuffer(EFalse);
   176 			iState = EPlaying;
   177 			iSoundDeviceErrorReceiver->Start();
   178 			TRAP(startError, FillSourceBufferL()); //get initial buffer of audio data
   179 			if (startError == KErrNone)
   180 				{
   181 				// Start the player objects
   182 				iAudioPlayer->Start();
   183 				}
   184 			else
   185 				{//failed to start up correctly go back to stopped state
   186 				iState = EStopped;
   187 				iSoundDeviceErrorReceiver->Stop();
   188 				}
   189 			}
   190    		}
   191 	return startError;
   192 	}
   193 
   194 
   195 // *** Main Play Loop ***
   196 
   197 void CMMFSwCodecPlayDataPath::FillSourceBufferL()
   198 	{//asks observer to fill the source buffer          
   199     // Ask immediately for data from the observer
   200 #ifdef __CYCLE_MMF_DATABUFFERS__
   201 	// Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
   202 	// If the creation fails, we carry on regardless as the original buffer will not have been 
   203 	// destroyed. Must do this as alloc fail tests will not run.
   204 	if(iSourceBuffer)
   205 		{
   206 		iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
   207 		}
   208 #endif // __CYCLE_MMF_DATABUFFERS__	
   209 	User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer));
   210 	
   211 	}
   212 
   213 
   214 void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer)
   215 	{//call back from observer to indicate buffer has been filled
   216 	if (iState == EStopped)
   217 		User::Leave(KErrNotReady);//ok if paused?
   218 
   219 	iSourceBuffer = &aBuffer;
   220 	iSourceBuffer->SetStatus(EFull);
   221 #ifdef _SCW_DEBUG
   222 	RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL"));
   223 #endif
   224 
   225 	//need to check that the buffer size is not 0 - if so assume we've reached the end of the data
   226 	if (!iSourceBuffer->BufferSize())
   227 		{//no buffer  - could be end of source or could be that the source has no data??
   228 		iNoMoreSourceData = ETrue;
   229 #ifdef _SCW_DEBUG
   230 		RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData"));
   231 #endif
   232 		}
   233 	//even if the buffer size is 0 we still 
   234 	//need to perform the following to get the sound device callback
   235 	FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device	
   236 
   237     // attenuate the amplitude of the samples if volume ramping has been changed
   238 	// and is non-zero
   239 	if (iCustomInterface)
   240 		{
   241 		TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp();
   242 		if (volumeRamp != iVolumeRamp)
   243 			{
   244 			iVolumeRamp = volumeRamp;
   245 			if (iVolumeRamp.Int64() != 0)
   246 				{
   247 				iUtility->ConfigAudioRamper(
   248 					iVolumeRamp.Int64(), 
   249 					iSampleRate, 
   250 					iChannels);
   251 				iRampAudioSample = ETrue;
   252 				}
   253 			else
   254 				{
   255 				iRampAudioSample = EFalse;
   256 				}
   257 			}
   258 			if (iRampAudioSample)
   259 				{
   260 				iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer);
   261 				}
   262 		}
   263 
   264 	iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
   265 
   266 	if (iSourceBuffer->LastBuffer())//check last buffer flag
   267 		{
   268 		iNoMoreSourceData = ETrue;
   269 #ifdef _SCW_DEBUG
   270 		RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData"));
   271 #endif
   272 		}
   273 	}
   274 
   275 
   276 void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL()
   277 	{//use CMMFSwCodec to fill the sound device buffer
   278 	
   279 	CMMFSwCodec::TCodecProcessResult codecProcessResult;
   280 
   281 	if (iCodec->IsNullCodec())
   282 		{//no codec so data can be sent direct to sink
   283 		iSoundDeviceBuffer = iSourceBuffer;
   284 		iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full
   285 		}	
   286 	else 
   287 		{	
   288 		//pass buffer to codec for processing
   289 		codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer);
   290 		if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev
   291 			iSoundDeviceBuffer->SetLastBuffer(ETrue);
   292 		if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded))
   293 			{//the codec has added data but not set the buffer length
   294 			iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded);
   295 			}
   296 		//only supports EProcessComplete
   297 		switch (codecProcessResult.iCodecProcessStatus)
   298 			{
   299 		case CMMFSwCodec::TCodecProcessResult::EProcessComplete:
   300 		//finished procesing source data - all data in sink buffer
   301 			{
   302 			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
   303 			}
   304 		break;
   305 		case CMMFSwCodec::TCodecProcessResult::EDstNotFilled:
   306 		//could be the last buffer in which case dst might not get filled
   307 			{
   308 			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
   309 			}
   310 		break;
   311 		case CMMFSwCodec::TCodecProcessResult::EEndOfData:
   312 			//no more data - send what we've got to the sink
   313 			//note we can't always rely on this  - in many cases the codec will not know when
   314 			//it has reached the end of data.
   315 			{
   316 			iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
   317 			iNoMoreSourceData = ETrue;
   318 			//doesn't matter if sink buffer is not full
   319 			}
   320 		break;
   321 		default:
   322 			Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec
   323 			}
   324 		}
   325 	}
   326 
   327 
   328 void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
   329 	{//call back from CDataPathPlayer when the sound device buffer has been emptied
   330 	if (&aBuffer != iSoundDeviceBuffer) 
   331 		Panic(EMMFSwCodecWrapperBadBuffer);
   332 	if (!iNoMoreSourceData) 
   333 		FillSourceBufferL();
   334 	}
   335 
   336 //*** End of Main Play Loop ***
   337 
   338 
   339 void CMMFSwCodecPlayDataPath::Stop()
   340 	{
   341 	iAudioPlayer->Cancel();
   342 	iSoundDeviceErrorReceiver->Cancel();
   343 	TRequestStatus status;
   344 	iSoundDevice->CloseDevice(iDeviceUid, status);
   345 	User::WaitForRequest(status);
   346 
   347 #ifdef __CYCLE_MMF_DATABUFFERS__
   348 	// Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
   349 	// If the creation fails, we carry on regardless as the original buffer will not have been 
   350 	// destroyed. Must do this as alloc fail tests will not run.
   351 	if(iSourceBuffer)
   352 		{
   353 		iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
   354 		}
   355 #endif // __CYCLE_MMF_DATABUFFERS__	
   356 
   357 	iState = EStopped;
   358 	}
   359 
   360 
   361 void CMMFSwCodecPlayDataPath::Pause()
   362 	{
   363 	//since a pause can happen anyway in the datatransfer -need to set to a known 
   364 	//state so that when play is resumed the behaviour is predictable
   365 	if (iSoundDevice->Handle())
   366 		{
   367 		iSoundDevice->PauseBuffer(); // ignores return value?
   368 		iState = EPaused;
   369 #ifdef _SCW_DEBUG
   370 		RDebug::Print(_L("Pause"));
   371 #endif
   372 		}
   373 	else
   374 		{//an error must have occured 
   375 		iState = EStopped;
   376 		}
   377 	}
   378 
   379 
   380 CRoutingSoundPlayDevice* CMMFSwCodecPlayDataPath::Device()
   381 	{
   382 	return iSoundDevice;
   383 	}
   384 
   385 void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError)
   386 	{
   387 	//this sends a request to the hw device observer usually Devsound
   388 	//to update the bytes played
   389 	//it is done here so that the sound driver can be closed prior to
   390 	//updating the plicy and sending the error back
   391 	TUid uidUpdateBytesPlayed;
   392 	uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed;
   393 	TPtrC8 dummy(0,0);
   394 	iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy);
   395 
   396 	//this closes RMdaDevSound.
   397 	Stop(); 
   398 
   399 	//inform devsound so it can update policy
   400 	iHwDeviceObserver->Stopped(); 
   401 
   402 	// Inform the observer of the exception condition
   403 	// We inform the hw device observer after the policy has been
   404 	// updated incase the observer relied on the error to assume
   405 	// the policy has been updated
   406 	iHwDeviceObserver->Error(aError);
   407 	}
   408 
   409 
   410 void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface)
   411 	{
   412 	iCustomInterface = &aCustomInterface;
   413 	}
   414 	
   415 void CMMFSwCodecPlayDataPath::SetConfigForAudioRamp(TUint aSampleRate, TUint aChannels)
   416 	{
   417 	iSampleRate = aSampleRate;
   418 	iChannels = aChannels;
   419 	}
   420 
   421 /************************************************************************
   422  *				CDataPathPlayer											*
   423  ************************************************************************/
   424 
   425 CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
   426 : CActive(aPriority), iParent(aParent)
   427 	{
   428 	CActiveScheduler::Add(this);
   429 	}
   430 
   431 
   432 CDataPathPlayer::~CDataPathPlayer()
   433 	{
   434 	Cancel();
   435 	}
   436 
   437 
   438 void CDataPathPlayer::Start()
   439 	{
   440 	// No implementation
   441 	}
   442 
   443 
   444 void CDataPathPlayer::ResumePlaying()
   445 	{
   446 	if (iParent.Device()->Handle())
   447 		{
   448 		//should be ok to call this even if we are active
   449 		iParent.Device()->ResumePlaying();
   450 		iResumePlaying = ETrue;
   451 		}
   452 #ifdef _SCW_DEBUG
   453 	RDebug::Print(_L("Playing Resumed"));
   454 #endif
   455 	}
   456 
   457 
   458 void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
   459 	{
   460 	iDataFromSource = &aData;
   461 	if (!IsActive())
   462 		{
   463 #ifdef _SCW_DEBUG
   464 		RDebug::Print(_L("CDataPathPlayer::PlayData"));
   465 #endif
   466 		iParent.Device()->PlayData(aData.Data(), iStatus);
   467 		SetActive();
   468 		}
   469 	}
   470 
   471 
   472 void CDataPathPlayer::Stop()
   473 	{
   474 	if (!IsActive())
   475 		{
   476 		iParent.Device()->FlushBuffer();
   477 		}
   478 	Cancel();
   479 	iParent.SoundDeviceException(KErrCancel);
   480 	}
   481 
   482 
   483 void CDataPathPlayer::RunL()
   484 	{
   485 #ifdef _SCW_DEBUG
   486 	RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int());
   487 #endif
   488 	if (!iStatus.Int())
   489 		{
   490 		iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
   491 		iResumePlaying = EFalse;
   492 		}
   493 	//if we don't have a sound driver handle then we have stopped
   494 	//but the client still thinks we are recording so swallow error
   495 	else if (iStatus.Int()!= KErrBadHandle)
   496 		{ 	
   497 		iParent.SoundDeviceException(iStatus.Int());
   498 		}
   499 	}
   500 
   501 
   502 TInt CDataPathPlayer::RunError(TInt aError)
   503 	{
   504 	Error(aError);
   505 	return KErrNone;
   506 	}
   507 
   508 
   509 void CDataPathPlayer::DoCancel()
   510 	{
   511 	if (iParent.Device()->Handle())
   512 		{
   513 		iParent.Device()->CancelPlayData();
   514 		iParent.Device()->FlushBuffer();
   515 		}
   516 	}
   517 
   518 
   519 void CDataPathPlayer::Error(TInt aError)
   520 	{ 
   521 	iParent.SoundDeviceException(aError);
   522 	}
   523 
   524 
   525 /************************************************************************
   526  *				CSoundDevPlayErrorReceiver								*
   527  ************************************************************************/
   528 
   529 CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
   530 : CActive(aPriority), iParent(aParent)
   531 	{
   532 	CActiveScheduler::Add(this);
   533 	}
   534 
   535 CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver()
   536 	{
   537 	Cancel();
   538 	}
   539 
   540 void CSoundDevPlayErrorReceiver::Start()
   541 	{
   542 	iParent.Device()->NotifyError(iStatus);
   543 	SetActive();
   544 	}
   545 
   546 void CSoundDevPlayErrorReceiver::Stop()
   547 	{
   548 	Cancel();
   549 	}
   550 
   551 void CSoundDevPlayErrorReceiver::RunL()
   552 	{
   553 	// An error has been returned
   554 #ifdef _SCW_DEBUG
   555 	RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), iStatus.Int());
   556 #endif
   557 	iParent.SoundDeviceException(iStatus.Int());
   558 	}
   559 
   560 void CSoundDevPlayErrorReceiver::DoCancel()
   561 	{
   562 	iParent.Device()->CancelNotifyError();
   563 	}
   564 
   565