1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/devsound/sounddevbt/src/swcodecwrapper/mmfBtSwCodecPlayDataPath.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,565 @@
1.4 +// Copyright (c) 2005-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\MmfBtSwCodecPlayDatapath.cpp
1.18 +//
1.19 +//
1.20 +
1.21 +#include "mmfBtSwCodecPlayDataPath.h"
1.22 +#include "mmfbtswcodecwrapper.h"
1.23 +#include "mmfbtswcodecwrappercustominterfacesuids.hrh"
1.24 +#include <mmfpaniccodes.h>
1.25 +#include "mmfBtSwCodecUtility.h"
1.26 +
1.27 +#include "MMFBtRoutingSoundDevice.h"
1.28 +#include "A2dpBTHeadsetAudioIfClientServer.h" // for TRange (will be deprecated)
1.29 +
1.30 +CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL( CRoutingSoundPlayDevice* aSoundDevice,
1.31 + TUid aDeviceUid)
1.32 + {
1.33 + CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath(aSoundDevice,
1.34 + aDeviceUid);
1.35 + CleanupStack::PushL(self);
1.36 + self->ConstructL();
1.37 + CleanupStack::Pop();
1.38 + return self;
1.39 + }
1.40 +
1.41 +
1.42 +void CMMFSwCodecPlayDataPath::ConstructL()
1.43 + {
1.44 + iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput);
1.45 + iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
1.46 + iUtility = CMMFSwCodecUtility::NewL();
1.47 + }
1.48 +
1.49 +
1.50 +CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath()
1.51 + {
1.52 + delete iAudioPlayer;
1.53 + delete iSoundDeviceErrorReceiver;
1.54 + delete iUtility;
1.55 +
1.56 + TRequestStatus status;
1.57 + iSoundDevice->CloseDevice(iDeviceUid, status);
1.58 + //TODO there should be a timeout for the line below
1.59 + User::WaitForRequest(status);
1.60 +
1.61 + if (iCodec)
1.62 + {
1.63 + delete iSourceBuffer;
1.64 + if (!iCodec->IsNullCodec())
1.65 + {
1.66 + delete iSoundDeviceBuffer;
1.67 + }
1.68 + }
1.69 +
1.70 +#ifdef __USE_MMF_TRANSFERBUFFERS__
1.71 + delete iTransferWindow;
1.72 +
1.73 + if(iTransferBuffer)
1.74 + {
1.75 + iTransferBuffer->Close();
1.76 + delete iTransferBuffer;
1.77 + }
1.78 +#endif
1.79 +
1.80 +#ifdef __USE_MMF_PTRBUFFERS__
1.81 + delete iPtrBufferMemoryBlock;
1.82 +#endif
1.83 + }
1.84 +
1.85 +
1.86 +TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
1.87 + {
1.88 + TInt error;
1.89 + if (iHwDeviceObserver)
1.90 + {
1.91 + error = KErrAlreadyExists;
1.92 + }
1.93 + else
1.94 + {
1.95 + iHwDeviceObserver = &aObserver;
1.96 + error = KErrNone;
1.97 + }
1.98 + return error;
1.99 + }
1.100 +
1.101 +
1.102 +TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec)
1.103 + {
1.104 + if (iCodec)
1.105 + {
1.106 + return KErrNotSupported; //doesn't support multiple codecs
1.107 + }
1.108 +
1.109 + TInt err = KErrNone;
1.110 +
1.111 + iCodec = &aCodec;
1.112 +
1.113 + // Allocate data buffer
1.114 + iSourceBufferSize = iCodec->SourceBufferSize();
1.115 + iSoundDevBufferSize = iCodec->SinkBufferSize();
1.116 +
1.117 + if ((!iSourceBufferSize) || (!iSoundDevBufferSize))
1.118 + {
1.119 + err = KErrArgument; //codec plugin has not specified buffer size
1.120 + }
1.121 +
1.122 + if (err == KErrNone)
1.123 + {
1.124 +#ifdef __USE_MMF_TRANSFERBUFFERS__
1.125 + TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer)));
1.126 +#endif
1.127 +#ifdef __USE_MMF_PTRBUFFERS__
1.128 + TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize));
1.129 +#else
1.130 + TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize));
1.131 +#endif
1.132 + }
1.133 +
1.134 + if (err == KErrNone)
1.135 + {
1.136 + if (iCodec->IsNullCodec())
1.137 + {//use source buffer for sound device buffer
1.138 + iSoundDeviceBuffer = NULL;
1.139 + }
1.140 + else
1.141 + {//codec needs separate source and sound device buffers
1.142 + TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize));
1.143 + }
1.144 + }
1.145 + return err;
1.146 + }
1.147 +
1.148 +
1.149 +TInt CMMFSwCodecPlayDataPath::Start()
1.150 + {
1.151 + TInt startError = KErrNone;
1.152 +
1.153 + if (!iCodec || (!iSoundDevice->Handle()))
1.154 + {
1.155 + //check that a codec has been added and the sound device is open
1.156 + startError = KErrNotReady;
1.157 + }
1.158 +
1.159 + if (iState == EPaused)
1.160 + {//we are paused so need to resume play
1.161 + if (!startError)
1.162 + {
1.163 +#ifdef _SCW_DEBUG
1.164 + RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume"));
1.165 +#endif
1.166 + iAudioPlayer->ResumePlaying();
1.167 + iState = EPlaying;
1.168 + }
1.169 + }
1.170 + else if (!startError)
1.171 + {
1.172 +#ifdef _SCW_DEBUG
1.173 + RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal"));
1.174 +#endif
1.175 + if (!startError)
1.176 + {
1.177 + iNoMoreSourceData = EFalse;
1.178 + iSourceBuffer->SetLastBuffer(EFalse);
1.179 + iState = EPlaying;
1.180 + iSoundDeviceErrorReceiver->Start();
1.181 + TRAP(startError, FillSourceBufferL()); //get initial buffer of audio data
1.182 + if (startError == KErrNone)
1.183 + {
1.184 + // Start the player objects
1.185 + iAudioPlayer->Start();
1.186 + }
1.187 + else
1.188 + {//failed to start up correctly go back to stopped state
1.189 + iState = EStopped;
1.190 + iSoundDeviceErrorReceiver->Stop();
1.191 + }
1.192 + }
1.193 + }
1.194 + return startError;
1.195 + }
1.196 +
1.197 +
1.198 +// *** Main Play Loop ***
1.199 +
1.200 +void CMMFSwCodecPlayDataPath::FillSourceBufferL()
1.201 + {//asks observer to fill the source buffer
1.202 + // Ask immediately for data from the observer
1.203 +#ifdef __CYCLE_MMF_DATABUFFERS__
1.204 + // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
1.205 + // If the creation fails, we carry on regardless as the original buffer will not have been
1.206 + // destroyed. Must do this as alloc fail tests will not run.
1.207 + if(iSourceBuffer)
1.208 + {
1.209 + iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
1.210 + }
1.211 +#endif // __CYCLE_MMF_DATABUFFERS__
1.212 + User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer));
1.213 +
1.214 + }
1.215 +
1.216 +
1.217 +void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer)
1.218 + {//call back from observer to indicate buffer has been filled
1.219 + if (iState == EStopped)
1.220 + User::Leave(KErrNotReady);//ok if paused?
1.221 +
1.222 + iSourceBuffer = &aBuffer;
1.223 + iSourceBuffer->SetStatus(EFull);
1.224 +#ifdef _SCW_DEBUG
1.225 + RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL"));
1.226 +#endif
1.227 +
1.228 + //need to check that the buffer size is not 0 - if so assume we've reached the end of the data
1.229 + if (!iSourceBuffer->BufferSize())
1.230 + {//no buffer - could be end of source or could be that the source has no data??
1.231 + iNoMoreSourceData = ETrue;
1.232 +#ifdef _SCW_DEBUG
1.233 + RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData"));
1.234 +#endif
1.235 + }
1.236 + //even if the buffer size is 0 we still
1.237 + //need to perform the following to get the sound device callback
1.238 + FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device
1.239 +
1.240 + // attenuate the amplitude of the samples if volume ramping has been changed
1.241 + // and is non-zero
1.242 + if (iCustomInterface)
1.243 + {
1.244 + TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp();
1.245 + if (volumeRamp != iVolumeRamp)
1.246 + {
1.247 + iVolumeRamp = volumeRamp;
1.248 + if (iVolumeRamp.Int64() != 0)
1.249 + {
1.250 + iUtility->ConfigAudioRamper(
1.251 + iVolumeRamp.Int64(),
1.252 + iSampleRate,
1.253 + iChannels);
1.254 + iRampAudioSample = ETrue;
1.255 + }
1.256 + else
1.257 + {
1.258 + iRampAudioSample = EFalse;
1.259 + }
1.260 + }
1.261 + if (iRampAudioSample)
1.262 + {
1.263 + iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer);
1.264 + }
1.265 + }
1.266 +
1.267 + iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
1.268 +
1.269 + if (iSourceBuffer->LastBuffer())//check last buffer flag
1.270 + {
1.271 + iNoMoreSourceData = ETrue;
1.272 +#ifdef _SCW_DEBUG
1.273 + RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData"));
1.274 +#endif
1.275 + }
1.276 + }
1.277 +
1.278 +
1.279 +void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL()
1.280 + {//use CMMFSwCodec to fill the sound device buffer
1.281 +
1.282 + CMMFSwCodec::TCodecProcessResult codecProcessResult;
1.283 +
1.284 + if (iCodec->IsNullCodec())
1.285 + {//no codec so data can be sent direct to sink
1.286 + iSoundDeviceBuffer = iSourceBuffer;
1.287 + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
1.288 + }
1.289 + else
1.290 + {
1.291 + //pass buffer to codec for processing
1.292 + codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer);
1.293 + if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev
1.294 + iSoundDeviceBuffer->SetLastBuffer(ETrue);
1.295 + if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded))
1.296 + {//the codec has added data but not set the buffer length
1.297 + iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded);
1.298 + }
1.299 + //only supports EProcessComplete
1.300 + switch (codecProcessResult.iCodecProcessStatus)
1.301 + {
1.302 + case CMMFSwCodec::TCodecProcessResult::EProcessComplete:
1.303 + //finished procesing source data - all data in sink buffer
1.304 + {
1.305 + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
1.306 + }
1.307 + break;
1.308 + case CMMFSwCodec::TCodecProcessResult::EDstNotFilled:
1.309 + //could be the last buffer in which case dst might not get filled
1.310 + {
1.311 + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
1.312 + }
1.313 + break;
1.314 + case CMMFSwCodec::TCodecProcessResult::EEndOfData:
1.315 + //no more data - send what we've got to the sink
1.316 + //note we can't always rely on this - in many cases the codec will not know when
1.317 + //it has reached the end of data.
1.318 + {
1.319 + iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
1.320 + iNoMoreSourceData = ETrue;
1.321 + //doesn't matter if sink buffer is not full
1.322 + }
1.323 + break;
1.324 + default:
1.325 + Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec
1.326 + }
1.327 + }
1.328 + }
1.329 +
1.330 +
1.331 +void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
1.332 + {//call back from CDataPathPlayer when the sound device buffer has been emptied
1.333 + if (&aBuffer != iSoundDeviceBuffer)
1.334 + Panic(EMMFSwCodecWrapperBadBuffer);
1.335 + if (!iNoMoreSourceData)
1.336 + FillSourceBufferL();
1.337 + }
1.338 +
1.339 +//*** End of Main Play Loop ***
1.340 +
1.341 +
1.342 +void CMMFSwCodecPlayDataPath::Stop()
1.343 + {
1.344 + iAudioPlayer->Cancel();
1.345 + iSoundDeviceErrorReceiver->Cancel();
1.346 + TRequestStatus status;
1.347 + iSoundDevice->CloseDevice(iDeviceUid, status);
1.348 + User::WaitForRequest(status);
1.349 +
1.350 +#ifdef __CYCLE_MMF_DATABUFFERS__
1.351 + // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
1.352 + // If the creation fails, we carry on regardless as the original buffer will not have been
1.353 + // destroyed. Must do this as alloc fail tests will not run.
1.354 + if(iSourceBuffer)
1.355 + {
1.356 + iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
1.357 + }
1.358 +#endif // __CYCLE_MMF_DATABUFFERS__
1.359 +
1.360 + iState = EStopped;
1.361 + }
1.362 +
1.363 +
1.364 +void CMMFSwCodecPlayDataPath::Pause()
1.365 + {
1.366 + //since a pause can happen anyway in the datatransfer -need to set to a known
1.367 + //state so that when play is resumed the behaviour is predictable
1.368 + if (iSoundDevice->Handle())
1.369 + {
1.370 + iSoundDevice->PauseBuffer(); // ignores return value?
1.371 + iState = EPaused;
1.372 +#ifdef _SCW_DEBUG
1.373 + RDebug::Print(_L("Pause"));
1.374 +#endif
1.375 + }
1.376 + else
1.377 + {//an error must have occured
1.378 + iState = EStopped;
1.379 + }
1.380 + }
1.381 +
1.382 +
1.383 +CRoutingSoundPlayDevice* CMMFSwCodecPlayDataPath::Device()
1.384 + {
1.385 + return iSoundDevice;
1.386 + }
1.387 +
1.388 +void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError)
1.389 + {
1.390 + //this sends a request to the hw device observer usually Devsound
1.391 + //to update the bytes played
1.392 + //it is done here so that the sound driver can be closed prior to
1.393 + //updating the plicy and sending the error back
1.394 + TUid uidUpdateBytesPlayed;
1.395 + uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed;
1.396 + TPtrC8 dummy(0,0);
1.397 + iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy);
1.398 +
1.399 + //this closes RMdaDevSound.
1.400 + Stop();
1.401 +
1.402 + //inform devsound so it can update policy
1.403 + iHwDeviceObserver->Stopped();
1.404 +
1.405 + // Inform the observer of the exception condition
1.406 + // We inform the hw device observer after the policy has been
1.407 + // updated incase the observer relied on the error to assume
1.408 + // the policy has been updated
1.409 + iHwDeviceObserver->Error(aError);
1.410 + }
1.411 +
1.412 +
1.413 +void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface)
1.414 + {
1.415 + iCustomInterface = &aCustomInterface;
1.416 + }
1.417 +
1.418 +void CMMFSwCodecPlayDataPath::SetConfigForAudioRamp(TUint aSampleRate, TUint aChannels)
1.419 + {
1.420 + iSampleRate = aSampleRate;
1.421 + iChannels = aChannels;
1.422 + }
1.423 +
1.424 +/************************************************************************
1.425 + * CDataPathPlayer *
1.426 + ************************************************************************/
1.427 +
1.428 +CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
1.429 +: CActive(aPriority), iParent(aParent)
1.430 + {
1.431 + CActiveScheduler::Add(this);
1.432 + }
1.433 +
1.434 +
1.435 +CDataPathPlayer::~CDataPathPlayer()
1.436 + {
1.437 + Cancel();
1.438 + }
1.439 +
1.440 +
1.441 +void CDataPathPlayer::Start()
1.442 + {
1.443 + // No implementation
1.444 + }
1.445 +
1.446 +
1.447 +void CDataPathPlayer::ResumePlaying()
1.448 + {
1.449 + if (iParent.Device()->Handle())
1.450 + {
1.451 + //should be ok to call this even if we are active
1.452 + iParent.Device()->ResumePlaying();
1.453 + iResumePlaying = ETrue;
1.454 + }
1.455 +#ifdef _SCW_DEBUG
1.456 + RDebug::Print(_L("Playing Resumed"));
1.457 +#endif
1.458 + }
1.459 +
1.460 +
1.461 +void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
1.462 + {
1.463 + iDataFromSource = &aData;
1.464 + if (!IsActive())
1.465 + {
1.466 +#ifdef _SCW_DEBUG
1.467 + RDebug::Print(_L("CDataPathPlayer::PlayData"));
1.468 +#endif
1.469 + iParent.Device()->PlayData(aData.Data(), iStatus);
1.470 + SetActive();
1.471 + }
1.472 + }
1.473 +
1.474 +
1.475 +void CDataPathPlayer::Stop()
1.476 + {
1.477 + if (!IsActive())
1.478 + {
1.479 + iParent.Device()->FlushBuffer();
1.480 + }
1.481 + Cancel();
1.482 + iParent.SoundDeviceException(KErrCancel);
1.483 + }
1.484 +
1.485 +
1.486 +void CDataPathPlayer::RunL()
1.487 + {
1.488 +#ifdef _SCW_DEBUG
1.489 + RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int());
1.490 +#endif
1.491 + if (!iStatus.Int())
1.492 + {
1.493 + iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
1.494 + iResumePlaying = EFalse;
1.495 + }
1.496 + //if we don't have a sound driver handle then we have stopped
1.497 + //but the client still thinks we are recording so swallow error
1.498 + else if (iStatus.Int()!= KErrBadHandle)
1.499 + {
1.500 + iParent.SoundDeviceException(iStatus.Int());
1.501 + }
1.502 + }
1.503 +
1.504 +
1.505 +TInt CDataPathPlayer::RunError(TInt aError)
1.506 + {
1.507 + Error(aError);
1.508 + return KErrNone;
1.509 + }
1.510 +
1.511 +
1.512 +void CDataPathPlayer::DoCancel()
1.513 + {
1.514 + if (iParent.Device()->Handle())
1.515 + {
1.516 + iParent.Device()->CancelPlayData();
1.517 + iParent.Device()->FlushBuffer();
1.518 + }
1.519 + }
1.520 +
1.521 +
1.522 +void CDataPathPlayer::Error(TInt aError)
1.523 + {
1.524 + iParent.SoundDeviceException(aError);
1.525 + }
1.526 +
1.527 +
1.528 +/************************************************************************
1.529 + * CSoundDevPlayErrorReceiver *
1.530 + ************************************************************************/
1.531 +
1.532 +CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
1.533 +: CActive(aPriority), iParent(aParent)
1.534 + {
1.535 + CActiveScheduler::Add(this);
1.536 + }
1.537 +
1.538 +CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver()
1.539 + {
1.540 + Cancel();
1.541 + }
1.542 +
1.543 +void CSoundDevPlayErrorReceiver::Start()
1.544 + {
1.545 + iParent.Device()->NotifyError(iStatus);
1.546 + SetActive();
1.547 + }
1.548 +
1.549 +void CSoundDevPlayErrorReceiver::Stop()
1.550 + {
1.551 + Cancel();
1.552 + }
1.553 +
1.554 +void CSoundDevPlayErrorReceiver::RunL()
1.555 + {
1.556 + // An error has been returned
1.557 +#ifdef _SCW_DEBUG
1.558 + RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), iStatus.Int());
1.559 +#endif
1.560 + iParent.SoundDeviceException(iStatus.Int());
1.561 + }
1.562 +
1.563 +void CSoundDevPlayErrorReceiver::DoCancel()
1.564 + {
1.565 + iParent.Device()->CancelNotifyError();
1.566 + }
1.567 +
1.568 +