/*
 * 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)
        {
        shared.iMutex.Signal();
        return KErrNoMemory;
        }

    CVoIPDownlinkThread* thread = 0;
    TRAPD(err, thread = CVoIPDownlinkThread::NewL(shared));
    if (err != KErrNone)
        {
        shared.iMutex.Signal();
        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
