diff -r 000000000000 -r 71ca22bcf22a mmserv/voipaudioservices/VoIPServer/src/VoIPDownlinkThread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmserv/voipaudioservices/VoIPServer/src/VoIPDownlinkThread.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,1280 @@ +/* + * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). + * All rights reserved. + * This component and the accompanying materials are made available + * under the terms of "Eclipse Public License v1.0" + * which accompanies this distribution, and is available + * at the URL "http://www.eclipse.org/legal/epl-v10.html". + * + * Initial Contributors: + * Nokia Corporation - initial contribution. + * + * Contributors: + * + * Description: VoIP Audio Services + * Implements downlink thread object. + * + */ + +#include +#include +#include +#include +#include +#include +#include "debugtracemacros.h" +#include "VoIPSharedData.h" +#include "VoIPServerThread.h" + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::CVoIPDownlinkThread +// Standard Constructor +// ----------------------------------------------------------------------------- +// +CVoIPDownlinkThread::CVoIPDownlinkThread(TSharedData& aData) : + iShared(aData) + { + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::~CVoIPDownlinkThread +// Standard Constructor +// ----------------------------------------------------------------------------- +// +CVoIPDownlinkThread::~CVoIPDownlinkThread() + { + TRACE_PRN_FN_ENT; + + Stop(); + + delete iJitterBuffer; + delete iAddJBuffer; + delete iGetJBuffer; + + delete iAudioOutput; + delete iErrConcealmentIntfc; + delete iG711DecoderIntfc; + delete iG729DecoderIntfc; + delete iIlbcDecoderIntfc; + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::NewL +// Symbian two-phase constructor +// ----------------------------------------------------------------------------- +// +CVoIPDownlinkThread* CVoIPDownlinkThread::NewL(TSharedData& aData) + { + CVoIPDownlinkThread* self = new (ELeave) CVoIPDownlinkThread(aData); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::ConstructL +// Part two of Symbian two phase construction +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::ConstructL() + { + TRACE_PRN_FN_ENT; + + InitThreadL(); + iCodecID = iShared.iCodecSettings.iFourCC; + InitMsgQueuesL(KDnLinkQueue, KDnLinkThreadComQueue); + + iShared.iMutex.Wait(); + TRAPD(err, InitDevSoundL(EMMFStatePlaying, iShared.iPriority, + iShared.iPreference)); + iShared.iMutex.Signal(); + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + iMaxBufLen = DetermineMaxBufferLen(iShared.iCodecSettings.iG711FrameRate); + + // Client must set these before querying! + iG711CodecMode = CVoIPFormatIntfc::EG711ALaw; + iILBCCodecMode = CVoIPFormatIntfc::EiLBC20mSecFrame; + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::ThreadFunction +// Thread Startup function +// ----------------------------------------------------------------------------- +// +TInt CVoIPDownlinkThread::ThreadFunction(TAny* aData) + { + // get a pointer to the shared data object + TSharedData& shared = *((TSharedData*) aData); + + // we can set the sync flag here + shared.iMutex.Wait(); + + // create a cleanup stack + CTrapCleanup* cleanupStack = CTrapCleanup::New(); + + if (!cleanupStack) + { + return KErrNoMemory; + } + + CVoIPDownlinkThread* thread = 0; + TRAPD(err, thread = CVoIPDownlinkThread::NewL(shared)); + if (err != KErrNone) + { + return err; + } + + shared.iMutex.Signal(); + + // if we're still here, active scheduler has been constructed + // start wait loop which runs until it's time to end the thread + CActiveScheduler::Start(); + + // Termination cleanup + delete thread; + delete cleanupStack; + + TRACE_PRN_N(_L("VoIP Downlink Thread CLOSED")); + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::Start +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::Start() + { + TRACE_PRN_FN_ENT; + + TInt err = KErrNotReady; + + if (iStatus == EReady) + { + TRAP(err, iDevSound->PlayInitL()); + TRACE_PRN_IF_ERR(err); + +#ifdef _DEBUG + iSamplesPlayedCount = 0; +#endif + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + iStatus = EReady; + } + else + { + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + iJitterBuffer->Play(); + } + } + } + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::Stop +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::Stop() + { + TRACE_PRN_FN_ENT; + + if (iStatus == EStreaming) + { + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + iJitterBuffer->Stop(); + } + + iDevSound->Stop(); + iStatus = EReady; + } + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::InitializeComplete +// A callback from the DevSound indicating completion of the initialization. +// It will send config data to the D/S and configure the encoder via CI. +// If everything goes well, the state of the thread is set EReady. +// The initialization completion message is sent back to the main thread. +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::InitializeComplete(TInt aError) + { + TRACE_PRN_FN_ENT; + + TInt err = aError; + + if (iDevSound && err == KErrNone) + { + TMMFCapabilities conf; + conf = iDevSound->Config(); + conf.iRate = EMMFSampleRate8000Hz; + conf.iChannels = EMMFMono; + TRAP(err, iDevSound->SetConfigL(conf)); + if (err == KErrNone) + { + // We are ready to stream even in case of later CI setting failure + iStatus = EReady; + TInt vol = iDevSound->MaxVolume(); + iShared.iMutex.Wait(); + iShared.iMaxVolume = vol; + iShared.iMutex.Signal(); + } + + // Init Custom Interface API to the decoder + TRAPD(err0, SetCodecCiL()); + if (err0 != KErrNone) + { + // DEBUG only + // Can ignore error - the decoder is not fully configured but + // can still run in the default mode. + TRACE_PRN_IF_ERR(err0); + } + } + + // Notify the main thread + SendCmd(ECmdDownlinkInitComplete, err); + + TRACE_PRN_IF_ERR(err); + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::BufferToBeFilled +// A reference to the buffer delivered from the DevSound is stored locally +// for later use. It will be filled with the data passed from the client +// when it calls BufferFilled. +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::BufferToBeFilled(CMMFBuffer* aBuffer) + { + //TRACE_PRN_FN_ENT; + + // Store pointer to the received buffer + iDevSoundBufPtr = static_cast (aBuffer); + iBufLen = iDevSoundBufPtr->RequestSize(); + TRACE_PRN_N1(_L("VoIP->DNL->BTBF:LEN[%d]"), iBufLen); + +#ifndef __WINSCW__ + // The first AMR buffer returns 1 for no data frame. + if (iCodecID == KMccFourCCIdAMRNB) + { + iBufLen = iMaxBufLen; + } +#else //__WINSCW__ + // Don't care about full 4k data buffer in WINS + iBufLen = iMaxBufLen; +#endif //__WINSCW__ + + // Create or adjust the chunk + TInt err = DoChunk(KChunkDNL, iBufLen, iMaxBufLen); + + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + // Note: Do not send ECmdFillBuffer to the client except if error + if (err != KErrNone) + { + Stop(); + iMsgBuffer.iStatus = err; + iMsgBuffer.iRequest = ECmdFillBuffer; + iMsgQueue.Send(iMsgBuffer); + } + else + { + // If JB below threshold, it will generate and return SID frames. + TRAP(err, iGetJBuffer->SetRequestSizeL(iBufLen)); // for CNG + + // Use current buf to play (if data available) + err = iJitterBuffer->FillBuffer(iGetJBuffer); + iStatus = EStreaming; + } + } + else // non-JB case + { + if (err != KErrNone) + { + Stop(); + iMsgBuffer.iStatus = err; + } + else + { + // Notify client there is buffer ready to be filled + iMsgBuffer.iStatus = iChunk.Handle(); + iMsgBuffer.iInt = iBufLen; + iStatus = EStreaming; + } + + iMsgBuffer.iRequest = ECmdFillBuffer; + iMsgQueue.Send(iMsgBuffer); + } + + TRACE_PRN_IF_ERR(err); + //TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::BufferFilled +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::BufferFilled() + { + // TODO: Check D/S status to protect from call to PlayData prior to + // PlayInitL, the case when D/S is stopped either by the user or due to + // an error. + + TUint seqNum; + TBool badFrame = EFalse; + + iShared.iMutex.Wait(); + iBufLen = iShared.iBufferSize; //length of data in the buffer + seqNum = iShared.iSequenceNum; + iShared.iMutex.Signal(); + TRACE_PRN_N1(_L("VoIP->DNL->BF: LEN[%d]"), iBufLen); + + if (iBufLen > iMaxBufLen) + { + iBufLen = iMaxBufLen; + badFrame = ETrue; + TRACE_PRN_N(_L("VoIP->DNL: BAD FRAME")); + } + + // Copy data over from chunk + TPtr8 dataPtr(iChunk.Base(), iBufLen, iMaxBufLen); + + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + if (badFrame) + { + dataPtr.FillZ(); // JB will throw it away + } + + // Send data to JB. When ready to play JB will call back via EventJB(). + iAddJBuffer->Data() = dataPtr; + iAddJBuffer->SetFrameNumber(seqNum); + iJitterBuffer->EmptyBuffer(iAddJBuffer); + } + else + { + if (iStatus == EStreaming) + { + if (badFrame) + { + ConcealErrorForNextBuffer(); + } + + // Fill D/S buffer and send it for playback + iDevSoundBufPtr->Data() = dataPtr; + iDevSound->PlayData(); + + //TRACE_PRN_N1(_L("SAMPLES-PLAYED [%d]"), iSamplesPlayedCount++); + } + } + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::PlayError +// From MDevSoundObserver +// Record error is send to client over comm channel. +// The state of recorder is rolled back to EReady. +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::PlayError(TInt aError) + { + TRACE_PRN_IF_ERR(aError); + +#ifdef _DEBUG + iSamplesPlayedCount = 0; +#endif + iStatus = EReady; + SendCmd(ECmdDnLinkError, aError); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetCodecCiL +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetCodecCiL() + { + TRACE_PRN_FN_ENT; + + if (iCodecID == KMccFourCCIdG711 || + iCodecID == KMccFourCCIdG729 || + iCodecID == KMccFourCCIdILBC || + iCodecID == KMccFourCCIdAMRNB) + { + if (!iErrConcealmentIntfc) + { + iErrConcealmentIntfc = CErrorConcealmentIntfc::NewL(*iDevSound); + } + } + + switch (iCodecID) + { + case KMccFourCCIdG711: + { + if (!iG711DecoderIntfc) + { + iG711DecoderIntfc = CG711DecoderIntfc::NewL(*iDevSound); + } + break; + } + case KMccFourCCIdG729: + { + if (!iG729DecoderIntfc) + { + iG729DecoderIntfc = CG729DecoderIntfc::NewL(*iDevSound); + } + break; + } + case KMccFourCCIdILBC: + { + if (!iIlbcDecoderIntfc) + { + iIlbcDecoderIntfc = CIlbcDecoderIntfc::NewL(*iDevSound); + } + break; + } + case KMccFourCCIdAMRNB: + case KMMFFourCCCodePCM16: + default: + { + break; + } + } + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetVolume +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetVolume() + { + iShared.iMutex.Wait(); + TInt vol = iShared.iInt; + iShared.iMutex.Signal(); + iDevSound->SetVolume(vol); + TRACE_PRN_N1(_L("VoIP->DNL: SetVolume [%d]"), vol); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetVolume +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetVolume() + { + TInt vol = iDevSound->Volume(); + iShared.iMutex.Wait(); + iShared.iInt = vol; + iShared.iMutex.Signal(); + SendCmd(ECmdGetVolumeComplete); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetAudioDeviceL +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetAudioDeviceL() + { + TRACE_PRN_FN_ENT; + + iShared.iMutex.Wait(); + TUint device = iShared.iAudioDevice; + iShared.iMutex.Signal(); + + if (!iAudioOutput) + { + iAudioOutput = CAudioOutput::NewL(*iDevSound); + } + + if (iAudioOutput) + { + // ENoPreference=0, EAll=1, ENoOutput=2, EPrivate=3, EPublic=4 + CAudioOutput::TAudioOutputPreference outputDev; + + if (CVoIPAudioDownlinkStream::TVoIPOutputDevice(device) == + CVoIPAudioDownlinkStream::EHandset) + { + outputDev = CAudioOutput::EPrivate; + } + else if (CVoIPAudioDownlinkStream::TVoIPOutputDevice(device) == + CVoIPAudioDownlinkStream::ELoudSpeaker) + { + outputDev = CAudioOutput::EPublic; + } + else // Use default device routing + { + outputDev = CAudioOutput::ENoPreference; + } //make sure doesn't break loudspeaker audio + + iAudioOutput->SetAudioOutputL(outputDev); + } + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetAudioDeviceL +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetAudioDeviceL() + { + TRACE_PRN_FN_ENT; + + if (!iAudioOutput) + { + iAudioOutput = CAudioOutput::NewL(*iDevSound); + } + + if (iAudioOutput) + { + CAudioOutput::TAudioOutputPreference outputDev = + iAudioOutput->AudioOutput(); + + CVoIPAudioDownlinkStream::TVoIPOutputDevice device; + + switch (outputDev) + { + case (CAudioOutput::ENoPreference): + case (CAudioOutput::EAll): + case (CAudioOutput::ENoOutput): + case (CAudioOutput::EPrivate): + default: + device = CVoIPAudioDownlinkStream::EHandset; + break; + case (CAudioOutput::EPublic): + device = CVoIPAudioDownlinkStream::ELoudSpeaker; + break; + } + + iShared.iMutex.Wait(); + iShared.iAudioDevice = TUint(device); + iShared.iMutex.Signal(); + SendCmd(ECmdGetAudioDeviceComplete); + } + + TRACE_PRN_FN_EXT; + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetIlbcCodecMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetIlbcCodecMode() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + iShared.iMutex.Wait(); + iILBCCodecMode = iShared.iCodecSettings.iILBCCodecMode; + iShared.iMutex.Signal(); + + if (iIlbcDecoderIntfc && iCodecID == KMccFourCCIdILBC) + { + if (iILBCCodecMode == CVoIPFormatIntfc::EiLBC20mSecFrame) + { + err = iIlbcDecoderIntfc->SetDecoderMode( + CIlbcDecoderIntfc::E20msFrame); + TRACE_PRN_N(_L("VoIP->DNL iLBC Mode Set: [20ms]")); + } + else if (iILBCCodecMode == CVoIPFormatIntfc::EiLBC30mSecFrame) + { + err = iIlbcDecoderIntfc->SetDecoderMode( + CIlbcDecoderIntfc::E30msFrame); + TRACE_PRN_N(_L("VoIP->DNL iLBC Mode Set: [30ms]")); + } + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetIlbcCodecMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetIlbcCodecMode() + { + // not available through CIs -> return local value + iShared.iMutex.Wait(); + iShared.iCodecSettings.iILBCCodecMode = iILBCCodecMode; + iShared.iMutex.Signal(); + SendCmd(ECmdGetIlbcCodecModeComplete); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetG711CodecMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetG711CodecMode() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + iShared.iMutex.Wait(); + iG711CodecMode = iShared.iCodecSettings.iG711CodecMode; + iShared.iMutex.Signal(); + + if (iG711DecoderIntfc && iCodecID == KMccFourCCIdG711) + { + if (iG711CodecMode == CVoIPFormatIntfc::EG711ALaw) + { + err = iG711DecoderIntfc->SetDecoderMode( + CG711DecoderIntfc::EDecALaw); + TRACE_PRN_N(_L("VoIP->DNL G711 Mode Set: [ALaw]")); + } + else if (iG711CodecMode == CVoIPFormatIntfc::EG711uLaw) + { + err = iG711DecoderIntfc->SetDecoderMode( + CG711DecoderIntfc::EDecULaw); + TRACE_PRN_N(_L("VoIP->DNL G711 Mode Set: [uLaw]")); + } + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetG711CodecMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetG711CodecMode() + { + // not available through CIs -> return local value + iShared.iMutex.Wait(); + iShared.iCodecSettings.iG711CodecMode = iG711CodecMode; + iShared.iMutex.Signal(); + SendCmd(ECmdGetG711CodecModeComplete); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::FrameModeRqrdForEC +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::FrameModeRqrdForEC() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + TBool modeReq = EFalse; + + if (iErrConcealmentIntfc) + { + err = iErrConcealmentIntfc->FrameModeRqrdForEC(modeReq); + } + + iShared.iMutex.Wait(); + iShared.iCodecSettings.iFrameModeReqForEC = modeReq; + iShared.iMutex.Signal(); + } + + SendCmd(ECmdGetFrameModeReqForECComplete, err); + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetFrameMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetFrameMode() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + iShared.iMutex.Wait(); + iFrameMode = iShared.iCodecSettings.iFrameMode; + iShared.iMutex.Signal(); + + if (iErrConcealmentIntfc) + { + err = iErrConcealmentIntfc->SetFrameMode(iFrameMode); + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetFrameMode +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetFrameMode() + { + TInt err = KErrNotSupported; + + if (iErrConcealmentIntfc) + { + // not available through CIs -> return local value + iShared.iMutex.Wait(); + iShared.iCodecSettings.iFrameMode = iFrameMode; + iShared.iMutex.Signal(); + err = KErrNone; + } + + SendCmd(ECmdGetFrameModeComplete, err); + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::ConcealErrorForNextBuffer +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::ConcealErrorForNextBuffer() + { + TInt err = KErrNotSupported; + + if (iErrConcealmentIntfc) + { + err = iErrConcealmentIntfc->ConcealErrorForNextBuffer(); + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetCng +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetCng() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + iShared.iMutex.Wait(); + TBool cng = iShared.iCodecSettings.iCng; + iShared.iMutex.Signal(); + + if (iCodecID == KMccFourCCIdG711 && iG711DecoderIntfc) + { + err = iG711DecoderIntfc->SetCng(cng); + } + else + { + if (iCodecID == KMccFourCCIdILBC && iIlbcDecoderIntfc) + { + err = iIlbcDecoderIntfc->SetCng(cng); + } + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetCng +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetCng() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + TBool cng = EFalse; + + if (iCodecID == KMccFourCCIdG711 && iG711DecoderIntfc) + { + err = iG711DecoderIntfc->GetCng(cng); + } + else + { + if (iCodecID == KMccFourCCIdILBC && iIlbcDecoderIntfc) + { + err = iIlbcDecoderIntfc->GetCng(cng); + } + } + + iShared.iMutex.Wait(); + iShared.iCodecSettings.iCng = cng; + iShared.iMutex.Signal(); + } + + SendCmd(ECmdGetCngComplete, err); + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SetPlc +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SetPlc() + { + TInt err = KErrNotSupported; + + if (iStatus == EReady) + { + if (iCodecID == KMccFourCCIdG711 && iG711DecoderIntfc) + { + iShared.iMutex.Wait(); + iPlc = iShared.iCodecSettings.iPlc; + iShared.iMutex.Signal(); + err = iG711DecoderIntfc->SetPlc(iPlc); + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::GetPlc +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::GetPlc() + { + TInt err = KErrNotSupported; + + if (iCodecID == KMccFourCCIdG711 && iG711DecoderIntfc) + { + // not available through CIs -> return local value + iShared.iMutex.Wait(); + iShared.iCodecSettings.iPlc = iPlc; + iShared.iMutex.Signal(); + err = KErrNone; + } + + SendCmd(ECmdGetPlcComplete, err); + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::BadLsfNextBuffer +// +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::BadLsfNextBuffer() + { + TInt err = KErrNotSupported; + + if (iStatus == EStreaming) + { + if (iCodecID == KMccFourCCIdG729 && iG729DecoderIntfc) + { + err = iG729DecoderIntfc->BadLsfNextBuffer(); + } + } + + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::ConfigureJitterBufferL +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::ConfigureJitterBufferL() + { + TInt err = KErrNone; + + if (iCodecID != KMMFFourCCCodePCM16) + { + if (iJitterBuffer) + { + delete iJitterBuffer; + iJitterBuffer = NULL; + } + iJitterBuffer = CVoIPJitterBuffer::NewL(this); + + if (iAddJBuffer) + { + delete iAddJBuffer; + iAddJBuffer = NULL; + } + iAddJBuffer = CMMFDataBuffer::NewL(iMaxBufLen); + + if (iGetJBuffer) + { + delete iGetJBuffer; + iGetJBuffer = NULL; + } + iGetJBuffer = CMMFDataBuffer::NewL(iMaxBufLen); + + if (iJitterBuffer && iAddJBuffer && iGetJBuffer) + { + TRACE_PRN_N(_L("VoIP::ConfigureJitterBufferL [OK]")); + + TVoIPJBConfig conf; + iShared.iMutex.Wait(); + conf.iSampleInterval = iShared.iJBConfig.iSampleInterval; + conf.iJitterLatency = iShared.iJBConfig.iJitterLatency; + conf.iJBBufferLength = iShared.iJBConfig.iJBBufferLength; + conf.iJBThreshold = iShared.iJBConfig.iJBThreshold; + conf.iJBInactivityTimeOut + = iShared.iJBConfig.iJBInactivityTimeOut; + // TODO: add tone playback params + //conf.iJBPlayToneTimeout = ?; + //conf.iJBPlayToneDuration = ?; + //conf.iJBPlayToneFrequency = ?; + iShared.iMutex.Signal(); + + iJitterBuffer->SetupL(iCodecID, conf); + iBufLen = iMaxBufLen; + err = DoChunk(KChunkDNL, iBufLen, iMaxBufLen); + } + else + { + err = KErrGeneral; + } + } + + if (err != KErrNone) + { + Stop(); + iMsgBuffer.iStatus = err; + } + else + { + // Notify client there is buffer ready to be filled + iMsgBuffer.iStatus = iChunk.Handle(); + iMsgBuffer.iInt = iBufLen; + } + + // This is the only time we will send ECmdFillBuffer in JB mode to + // indicate to the client we are ready to receive packets of iBufLen size. + iMsgBuffer.iRequest = ECmdFillBuffer; + iMsgQueue.Send(iMsgBuffer); + + TRACE_PRN_IF_ERR(err); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::ResetJitterBuffer +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::ResetJitterBuffer() + { + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + iShared.iMutex.Wait(); + TBool playTone = iShared.iBool; + iShared.iMutex.Signal(); + iJitterBuffer->ResetBuffer(playTone); + } + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::JBDelayDown +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::JBDelayDown() + { + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + iJitterBuffer->DelayDown(); + } + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::JBDelayUp +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::JBDelayUp() + { + if (iJitterBuffer && iCodecID != KMMFFourCCCodePCM16) + { + iJitterBuffer->DelayUp(); + } + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::SendCmd +// Completes active object's request and sets shared data command value to +// one of the user requested commands. +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::SendCmd(TUserCommand aCmd, TInt aError) + { + iShared.iMutex.Wait(); + + iShared.iCmd = aCmd; + TRequestStatus* status = iShared.iMnThreadStatus; + + if (status) + { + if (status->Int() == KRequestPending) + { + RThread t; + TInt err = t.Open(iShared.iMainThreadID); + if (err == KErrNone) + { + t.RequestComplete(status, aError); + } + } + } + + iShared.iMutex.Signal(); + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::Event +// From MQueueHandlerObserverSrv +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::Event(TInt aEventType, TInt /*aError*/) + { + switch (aEventType) + { + case ECmdStartDownlink: + { + Start(); + break; + } + case ECmdStopDownlink: + { + if (iStatus == EStreaming) + { + Stop(); + } + break; + } + case ECmdBufferFilled: + { + BufferFilled(); + break; + } + case ECmdGetVolume: + { + GetVolume(); + break; + } + case ECmdSetVolume: + { + SetVolume(); + break; + } + case ECmdSetAudioDevice: + { + TRAPD(err, SetAudioDeviceL()); + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + break; + } + case ECmdGetAudioDevice: + { + TRAPD(err, GetAudioDeviceL()); + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + break; + } + case ECmdSetIlbcCodecMode: + { + SetIlbcCodecMode(); + break; + } + case ECmdGetIlbcCodecMode: + { + GetIlbcCodecMode(); + break; + } + case ECmdSetG711CodecMode: + { + SetG711CodecMode(); + break; + } + case ECmdGetG711CodecMode: + { + GetG711CodecMode(); + break; + } + case ECmdSetFrameMode: + { + SetFrameMode(); + break; + } + case ECmdGetFrameMode: + { + GetFrameMode(); + break; + } + case ECmdFrameModeRqrdForEC: + { + FrameModeRqrdForEC(); + break; + } + case ECmdConcealErrForNextBuf: + { + ConcealErrorForNextBuffer(); + break; + } + case ECmdSetCng: + { + SetCng(); + break; + } + case ECmdGetCng: + { + GetCng(); + break; + } + case ECmdSetPlc: + { + SetPlc(); + break; + } + case ECmdGetPlc: + { + GetPlc(); + break; + } + case ECmdBadLsfNextBuffer: + { + BadLsfNextBuffer(); + break; + } + case ECmdVoIPConfigJB: + { + TRAPD(err, ConfigureJitterBufferL()); + if (err != KErrNone) + { + SendCmd(ECmdDnLinkError, err); + } + break; + } + case ECmdVoIPResetJB: + { + ResetJitterBuffer(); + break; + } + case ECmdVoIPJBDelayDown: + { + JBDelayDown(); + break; + } + case ECmdVoIPJBDelayUp: + { + JBDelayUp(); + break; + } + case ECmdTerminateThread: + default: + { + if (iStatus == EStreaming) + { + Stop(); + } + // if unknown exception is raised exit thread + CActiveScheduler::Stop(); + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CVoIPDownlinkThread::Event +// From MJitterBufferObserver +// ----------------------------------------------------------------------------- +// +void CVoIPDownlinkThread::EventJB(TInt aEventType, TInt aError) + { + switch (aEventType) + { + case MJitterBufferObserver::EBufferReadyToPlay: + { + if (iStatus == EStreaming) + { + iDevSoundBufPtr->Data() = iGetJBuffer->Data(); + iDevSound->PlayData(); + TRACE_PRN_N1(_L("SAMPLES-PLAYED [%d]"), iSamplesPlayedCount++); + } + break; + } + case MJitterBufferObserver::EBufferConsumed: + { + // Notify client (?) + break; + } + case MJitterBufferObserver::EConcealErrorForNextBuffer: + { + ConcealErrorForNextBuffer(); + break; + } + case MJitterBufferObserver::EGeneralError: +// case MJitterBufferObserver::EBufferUnderflow: +// case MJitterBufferObserver::EBufferOverflow: + default: + { + SendCmd(ECmdDnLinkJBError, aError); + break; + } + } + } + +// End of file