mmserv/voipaudioservices/VoIPServer/src/VoIPDownlinkThread.cpp
changeset 0 71ca22bcf22a
child 13 f5c5c82a163e
--- /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