--- /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 <AudioOutput.h>
+#include <IlbcDecoderIntfc.h>
+#include <G711DecoderIntfc.h>
+#include <G729DecoderIntfc.h>
+#include <ErrorConcealmentIntfc.h>
+#include <voipdownlinkstream.h>
+#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<CMMFDataBuffer*> (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