/*
* Copyright (c) 2008 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:  Comfort noise generator of MCC jitterbuffer
*
*/



// INCLUDE FILES
#include <mmcccodecinformation.h> //codec FourCC declarations
#include <voipjitterbufferintfc.h>
#include "debugtracemacros.h"
#include "InternalDef.h"
#include "JitterBufferObserver.h"
#include "CngGenerator.h"


// -----------------------------------------------------------------------------
// CVoIPCNGenerator::NewL
// Static constructor.
// -----------------------------------------------------------------------------
//
CVoIPCNGenerator* CVoIPCNGenerator::NewL(MJitterBufferObserver* aObserver,
                                         const TFourCC& aCodec)
    {
    CVoIPCNGenerator* self = new (ELeave) CVoIPCNGenerator(aObserver, aCodec);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::CVoIPCNGenerator
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CVoIPCNGenerator::CVoIPCNGenerator(MJitterBufferObserver* aObserver,
                                   const TFourCC& aCodec)
    {
    iObserver = aObserver;
    iCodec = aCodec;
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::ConstructL()
    {
    switch (iCodec.FourCC())
        {
        case KMccFourCCIdG711:
        case KMccFourCCIdG729:
        case KMccFourCCIdILBC:
        case KMccFourCCIdAMRNB:
            break;
        default:
            User::Leave(KErrNotSupported);
        }
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::~CVoIPCNGenerator
// Destructor
// -----------------------------------------------------------------------------
//
CVoIPCNGenerator::~CVoIPCNGenerator()
    {
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::CreateSidBufferL
// Creates codec-specific SID Buffer
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::GenerateSidPacket(TDes8& aPayload, TInt aRequestSize)
    {
//    TRACE_PRN_FN_ENT;

    if (aRequestSize > aPayload.MaxLength())
        {
        TRACE_PRN_N(_L("CVoIPCNGenerator::GenerateSidPacket->Adjust Size"));
        aRequestSize = aPayload.MaxLength();
        }

    iGeneratedCNFrames++;
    TRACE_PRN_N1(_L("JB-CNG-> Generated CN frames [%d]"), iGeneratedCNFrames);

    if (iCodec.FourCC() == KMccFourCCIdAMRNB)
        {
        GenerateAmrNoDataPacket(aPayload);

        if (iGeneratedCNFrames == KAmrNbSidUpdateInterval)
            {
            // We haven't received SID_UPDATE for the 8-th frame;
            // this means packet loss
            iDtxPeriodStarted = EFalse;
            }
        }
    else
        {
        GenerateVoIPNoDataPacket(aPayload, aRequestSize);
        }

//    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::GenerateAmrNoDataPacket
// Generates AMR SID packet
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::GenerateAmrNoDataPacket(TDes8& aPayload) const
    {
//    TRACE_PRN_FN_ENT;

    if (aPayload.MaxLength() >= KNoDataLength)
        {
        aPayload.Copy(&KAmrNoDataFrame, KNoDataLength);
        }

//    TRACE_PRN_FN_EXT;
    };

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::GenerateVoIPNoDataPacket
// Generates SID packet for codecs other than AMR
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::GenerateVoIPNoDataPacket(TDes8& aPayload,
                                                TInt aRequestSize) const
    {
//    TRACE_PRN_FN_ENT;

    aPayload.FillZ(aRequestSize);

    if (!iDtxPeriodStarted)
        {
        ConcealErrorForNextFrame();
        }

//    TRACE_PRN_FN_EXT;
    };

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::ConcealErrorForNextFrame
// Requests error concealment for the next frame
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::ConcealErrorForNextFrame() const
    {
    iObserver->EventJB(MJitterBufferObserver::EConcealErrorForNextBuffer);
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::DtxPeriodStatus
// Returns DTX status
// -----------------------------------------------------------------------------
//
TBool CVoIPCNGenerator::DtxPeriodStatus() const
    {
    return iDtxPeriodStarted;
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::DoDtxDecision()
// Determine DTX period based on codec used and data in the buffer
// -----------------------------------------------------------------------------
//
void CVoIPCNGenerator::DoDtxDecision(const TDes8& aData)
    {
//    TRACE_PRN_FN_ENT;

    if (aData.Length() > KMinDataLenForDtx)
        {
        if (IsSidBuffer(aData))
            {
            // DTX period started or updated
            iDtxPeriodStarted = ETrue;
            iGeneratedCNFrames = 0;
            TRACE_PRN_N(_L("JB-CNG-> DTX_START"));
            }
        else
            {
            // DTX period has ended
            iDtxPeriodStarted = EFalse;
            TRACE_PRN_N(_L("JB-CNG-> DTX_END"));
            }
        }

//    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPCNGenerator::IsSidBuffer
// Is the given buffer a SID UPDATE buffer
// -----------------------------------------------------------------------------
//
TBool CVoIPCNGenerator::IsSidBuffer(const TDes8& aData)
    {
//    TRACE_PRN_FN_ENT;
    TBool status = ETrue;

    switch (iCodec.FourCC())
        {
        case KMccFourCCIdAMRNB:
            {
            // Get AMR mode by bit-masking first byte and shifting
            const TUint8 mode((aData[0] & KAmrModeMask) >> KModeShiftBits); 
            if (mode == KAmrSidMode)
                {
                status = ETrue;
                TRACE_PRN_N(_L("JB-CNG-> SID AMRNB FRAME"));
                }
            else
                {
                status = EFalse;
                TRACE_PRN_N(_L("JB-CNG-> NON-SID AMRNB FRAME"));
                }
            break;
            }
        case KMccFourCCIdG711:
        case KMccFourCCIdILBC:
        case KMccFourCCIdG729:
            {
            // Check the frame type
            if (aData[0] == KVoIPCNFrame)
                {
                status = ETrue;
                TRACE_PRN_N(_L("JB-CNG-> SID FRAME"));
                }
            else
                {
                status = EFalse;
                TRACE_PRN_N(_L("JB-CNG-> NON-SID FRAME"));
                }
            break;
            }
        default:
            {
            status = EFalse;
            TRACE_PRN_N(_L("JB-CNG-> SID-UNKNOWN"));
            }
        }

//    TRACE_PRN_FN_EXT;
    return status;
    }

//  End of File
