mmserv/voipaudioservices/JitterBufferUtility/src/JitterBufferImpl.cpp
author hgs
Tue, 21 Sep 2010 11:38:43 -0500
changeset 53 eabc8c503852
parent 0 71ca22bcf22a
permissions -rw-r--r--
201037

/*
 * 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:  Implementation of Mcc Jitterbuffer
 *
 */

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

// -----------------------------------------------------------------------------
// TJitterBufferElement::CompareSeqNum
// Compare SequenceNumber
// -----------------------------------------------------------------------------
//
TInt TJitterBufferElement::CompareSeqNum(const TJitterBufferElement& aElem1,
        const TJitterBufferElement& aElem2)
    {
    if (aElem1.iSequenceNumber > aElem2.iSequenceNumber)
        {
        return (-1);
        }
    else if (aElem1.iSequenceNumber < aElem2.iSequenceNumber)
        {
        return (1);
        }
    else
        {
        return (0);
        }
    }

// -----------------------------------------------------------------------------
// TJitterBufferElement::CompareStamp
// Compare Stamp
// -----------------------------------------------------------------------------
//
TInt TJitterBufferElement::CompareStamp(const TJitterBufferElement& aElem1,
        const TJitterBufferElement& aElem2)
    {
    if (aElem1.iTimeStamp > aElem2.iTimeStamp)
        {
        return (-1);
        }
    else if (aElem1.iTimeStamp < aElem2.iTimeStamp)
        {
        return (1);
        }
    else
        {
        return (0);
        }
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::CVoIPJitterBufferImpl
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CVoIPJitterBufferImpl::CVoIPJitterBufferImpl(MJitterBufferObserver* aObserver) :
    iBufStampSorter(TLinearOrder<TJitterBufferElement> (
            TJitterBufferElement::CompareStamp)),
    iBufSequenceSorter(TLinearOrder<TJitterBufferElement> (
            TJitterBufferElement::CompareSeqNum))
    {
    iObserver = aObserver;
    iTonePlayTime.UniversalTime();
    iSampleRate = KDefaultSampleRateInkHz;
    iPlay = EFalse;
    iLastPlayedSeqNum = -1;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::NewL
// Static constructor.
// -----------------------------------------------------------------------------
//
CVoIPJitterBufferImpl* CVoIPJitterBufferImpl::NewL(
        MJitterBufferObserver* aObserver)
    {
    CVoIPJitterBufferImpl* self = new (ELeave) CVoIPJitterBufferImpl(aObserver);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::ConstructL()
    {
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::~CVoIPJitterBufferImpl
// Destructor deallocate memory.
// -----------------------------------------------------------------------------
//
CVoIPJitterBufferImpl::~CVoIPJitterBufferImpl()
    {
    TRACE_PRN_FN_ENT;

    delete iCNGenerator;

    // Deallocate payload memory of jitter buffer elements.
    TInt count = iBuffer.Count();

    for (TInt i = 0; i < count; i++)
        {
        if (iBuffer[i].iDataFrame)
            {
            delete iBuffer[i].iDataFrame;
            }
        }

    iBuffer.Close();

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::SetupL
// Setup Jitter buffer
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::SetupL(const TFourCC aCodec,
        const TVoIPJBConfig& aJBConfig)
    {
    TRACE_PRN_FN_ENT;

    __ASSERT_ALWAYS(aJBConfig.iJitterLatency, User::Leave(KErrArgument));
    __ASSERT_ALWAYS(aJBConfig.iJBBufferLength, User::Leave(KErrArgument));
    __ASSERT_ALWAYS(aJBConfig.iSampleInterval, User::Leave(KErrArgument));

    // Save the original HW frame time because we may need it in case of
    // dynamic G.711 adjustment.
    const TUint8 origHwtime = iJBConfig.iSampleInterval;
    iJBConfig = aJBConfig;

    if (iJBConfig.iJBInactivityTimeOut)
        {
        if ((iJBConfig.iJBPlayToneFrequency > 0) &&
                (iJBConfig.iJBPlayToneDuration > 0))
            {
            iPlayToneInterval = iJBConfig.iJBPlayToneTimeout;
            iPlay = ETrue;
            }
        }

    TInt bufLenMultiplier = 1;

    if (aCodec == KMMFFourCCCodeAMR)
        {
        iFrameSize = KAMRNBFrameSize;
        iSampleInterval = KAMRNBFrameTime;
        }
    else if (aCodec == KMccFourCCIdG711)
        {
        // G.711 is configured dynamically. Take voip headerlength also into
        // account. G.711 hwframetime is in milliseconds, so need to multiply.
        iFrameSize = (iJBConfig.iSampleInterval * KDefaultSampleRateInkHz) +
                KVoIPHeaderLength;
        iSampleInterval = 0;

        // In case of G.711 codec dynamic configuration, we may need to double
        // the jitter buffer length if HW frame time is changed from 20ms to
        // 10ms.
        if (origHwtime)
            {
            bufLenMultiplier = origHwtime / iJBConfig.iSampleInterval;

            if (!bufLenMultiplier)
                {
                bufLenMultiplier = 1;
                }
            }
        }
    else if (aCodec == KMccFourCCIdILBC)
        {
        iFrameSize = KILBCFrameSize;
        iSampleInterval = 0;
        }
    else if (aCodec == KMccFourCCIdG729)
        {
        iFrameSize = KG729FrameSize;
        iSampleInterval = 0;
        // Multiply G.729 also by two...
        bufLenMultiplier = 2;
        }
    else
        {
        TRACE_PRN_N(_L("JB-> Codec Not Supported"));
        User::Leave(KErrNotSupported);
        }

    // Delete and reset old buffer
    const TInt elems = iBuffer.Count();

    for (TInt i = 0; i < elems; i++)
        {
        if ((iBuffer[i].iDataFrame))
            {
            delete iBuffer[i].iDataFrame;
            iBuffer[i].iDataFrame = NULL;
            }
        }

    iBuffer.Reset();

    // Calculate needed elements
    iBufferLength = iJBConfig.iJBBufferLength * bufLenMultiplier;

    if (iJBConfig.iJBThreshold >= iBufferLength)
        {
        // adjust threshold size (no need to leave here)
        iJBConfig.iJBThreshold = iBufferLength / 2;
        }

    // If the difference between buffer length and threshold is less than 10
    // increase buffer length, so the differences is 10. This helps handle
    // buffer overflow more easily.
    CheckThresholdBufferLength(iBufferLength, iJBConfig.iJBThreshold);
    iCurrentPlayThreshold = iJBConfig.iJBThreshold;
    iOriginalPlayThreshold = iJBConfig.iJBThreshold;

    if (iCNGenerator)
        {
        delete iCNGenerator;
        iCNGenerator = NULL;
        }

    iCNGenerator = CVoIPCNGenerator::NewL(iObserver, aCodec);

    // Create the elements in the Buffer
    for (TInt k = 0; k < iBufferLength; k++)
        {
        CMMFDataBuffer* buf = CMMFDataBuffer::NewL(iFrameSize);
        CleanupStack::PushL(buf);
        TJitterBufferElement newElement;
        newElement.iDataFrame = buf;
        newElement.iSequenceNumber = -1;
        newElement.iTimeStamp = -1;
        iBuffer.AppendL(newElement);
        CleanupStack::Pop(buf);
        }

    // Reset statistics
    iFramesLost = 0;
    iFramesReceived = 0;
    iNumOfLateFrames = 0;
    iFramesRemoved = 0;
    iFramesPlayed = 0;
    iPacketsInBuffer = 0;

    // Calculate the sequence number increment
    iSeqNumIncrement = iSampleRate * iJBConfig.iSampleInterval;

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::ResetBufferL
// Reset Jitter Buffer
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::ResetBuffer(TBool aPlayTone)
    {
    TRACE_PRN_FN_ENT;

    for (TInt i = 0; i < iBufferLength; i++)
        {
        iBuffer[i].iSequenceNumber = -1;
        iBuffer[i].iTimeStamp = -1;
        }

    iLastPlayedSeqNum = -1;
    iCurrentPlayThreshold = iOriginalPlayThreshold;
    iPacketsInBuffer = 0;
    iPlay = aPlayTone;

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::DelayUpL
// Adds a buffer element into the jitter buffer
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::DelayUpL()
    {
    TRACE_PRN_FN_ENT;

    CMMFDataBuffer* buf = CMMFDataBuffer::NewL(iFrameSize);
    CleanupStack::PushL(buf);
    TJitterBufferElement newElement;
    newElement.iDataFrame = buf;
    newElement.iSequenceNumber = -1;
    newElement.iTimeStamp = -1;
    iBuffer.AppendL(newElement);
    CleanupStack::Pop(buf);

    iBufferLength++;

    // Insert one NO_DATA frame into the audio stream, so the jitterbuffer has
    // the possibility to grow at least one frame from the current size. If
    // the current playout threshold is zero, then there is ongoing playback or
    // a DTX period. This means that we should buffer at least one frame before
    // starting playback.
    if (iCurrentPlayThreshold == 0)
        {
        TRACE_PRN_N(_L("JB-> Adjusting Playback Threshold"));
        iCurrentPlayThreshold = iPacketsInBuffer + 1;
        }

    TRACE_PRN_N1(_L("JB-DelayUpL-> BUF_LEN [%d]"), iBufferLength);
    TRACE_PRN_N1(_L("JB-DelayUpL-> CUR_FRAMES [%d]"), iPacketsInBuffer);
    TRACE_PRN_N1(_L("JB-DelayUpL-> PLAY_TH [%d]"), iCurrentPlayThreshold);

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::DelayDownL
// Removes an buffer element from the jitter buffer
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::DelayDownL()
    {
    TRACE_PRN_FN_ENT;

    // We need to remove one frame from the jitterbuffer. If currently we are
    // in a DTX period, we're in luck as we do not affect voice quality at all.

    // DURING DTX:
    // During DTX periods the jitterbuffer can be empty, so we cannot remove
    // anything from nothing. Then we basically can remove one element from the
    // buffer so it is shorter.

    // DURING SPEECH:
    // We'll need to remove one audio frame and mark the one preceding the
    // removed frame as bad. Then we'll continue as usual.
    TRACE_PRN_N1(_L("JB-DelayDownL-> BUF_LEN [%d]"), iBufferLength);
    TRACE_PRN_N1(_L("JB-DelayDownL-> CUR_FRAMES [%d]"), iPacketsInBuffer);

    if (IsEmpty())
        {
        // Cannot do anything for empty buffer
        if (KLowBufferLimit < iBufferLength)
            {
            TRACE_PRN_N(_L("JB-DelayDownL-> Empty buffer"));

            delete iBuffer[iBufferLength - 1].iDataFrame;
            iBuffer.Remove(iBufferLength - 1);
            iBufferLength--;
            }
        else if (iCurrentPlayThreshold > 0)
            {
            TRACE_PRN_N(_L("JB-DelayDownL-> Play threshold modification"));

            iCurrentPlayThreshold--;
            }
        else
            {
            // Cannot do anything currently
            TRACE_PRN_N(_L("JB-DelayDownL-> Buffer empty - Do nothing"));
            return;
            }
        }
    else if (IsFull())
        {
        // If there is sufficiently data in the buffer, then remove one
        // frame and mark the one preceding it bad
        if (KLowBufferLimit < iPacketsInBuffer)
            {
            TRACE_PRN_N(_L("JB-DelayDownL-> Buffer full - Removing"));

            // Remove the 2nd oldest frame and mark the oldest as bad
            delete iBuffer[1].iDataFrame;
            iBuffer.Remove(1);

            // MARK THE FIRST AS BAD FRAME!!!

            iBufferLength--;
            iPacketsInBuffer--;
            iFramesRemoved++;
            }
        else
            {
            // Cant do removing, see if playthreshold can be adjusted
            if (iCurrentPlayThreshold > 0)
                {
                TRACE_PRN_N(_L("JB-DelayDownL-> Buffer full - Do nothing"));

                iCurrentPlayThreshold--;
                }
            }
        }
    else
        {
        if (KLowBufferLimit < iBufferLength)
            {
            TRACE_PRN_N(_L("JB-DelayDownL-> Adjusting frame length"));

            // Adjust the length of the buffer
            delete iBuffer[iBufferLength - 1].iDataFrame;
            iBuffer.Remove(iBufferLength - 1);
            iBufferLength--;
            }
        else
            {
            TRACE_PRN_N(_L("JB-DelayDownL-> Buffer too small"));
            }
        }

    TRACE_PRN_FN_EXT;
    }

#ifdef __FEATURE_NOT_SUPPORTED__
// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::CalculateDelay
// Calculates the current jitter buffer playback
// -----------------------------------------------------------------------------
//
TTimeIntervalMicroSeconds32 CVoIPJitterBufferImpl::CalculateDelay() const
    {
    TTimeIntervalMicroSeconds32 delay = iJBConfig.iJitterLatency *
            iPacketsInBuffer;

    //TRACE_PRN_N1(_L("JB-CalculateDelay-> Delay [%d]"), delay);

    return delay;
    }
#endif //__FEATURE_NOT_SUPPORTED__

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::AddDataFrame
// Adds audio frame into the jitter buffer
// -----------------------------------------------------------------------------
//
TInt CVoIPJitterBufferImpl::AddDataFrame(CMMFBuffer* aDataBuffer)
    {
    if (aDataBuffer == NULL)
        {
        return KErrNotReady;
        }

    iCurrentSeqNum = aDataBuffer->FrameNumber();
    iFramesReceived++;

    // TODO: && TBD: Playout threshold update & re-buffering when starting
    // a new talk-burst.
    //
    // 1. If sender does not use inbaund DTX signalling.
    //  1.1 We may start re-buffering and sender goes into DTX mode again
    //      => We may not reach the playout threshold if the talkspurt is short
    //      => Also some random babble noise may activate the sender temporarily
    //      => The babble noise frame never gets played or some of these frames
    //         never get played.
    //
    // 2. If sender does use inband DTX signalling
    //  2.1 We can see the start of the DTX period which is cheked in
    //      GetDataFrameL()
    //  2.2 During DTX period we may detect the end of the DTX period &
    //      updates during the DTX period.
    //
    // 3. Packet loss may affect in the DTX detection.
    //  3.1 DTX start packet may be lost
    //  3.2 DTX UPDATE packet may be lost (if used)
    //

    TUint index = FindLargestSeqNum();

    if (iLastPlayedSeqNum < KMaxSeqNumber - iSeqNumIncrement)
        {
        iIsWrappedAround = IsSeqNumWrappedAround(iLargestSeqNum,
                iCurrentSeqNum);
        }
    else
        {
        // If the last played frame was larger than the current largest
        // in JB we are most likely running in the wrap-around mode.
        iIsWrappedAround = IsSeqNumWrappedAround(iLastPlayedSeqNum,
                iLargestSeqNum);
        }

    if (iIsWrappedAround)
        {
        iLastPlayedSeqNum = -1;
        }

    TRACE_PRN_N1(_L("JB-> [ADD] LARGEST IN  [%d]"), TInt32(iLargestSeqNum));
    TRACE_PRN_N1(_L("JB-> [ADD] THIS FRAME  [%d]"), TInt32(iCurrentSeqNum));
    TRACE_PRN_N1(_L("JB-> [ADD] LAST PLAYED [%d]"), TInt32(iLastPlayedSeqNum));
    TRACE_PRN_N1(_L("JB-> [ADD] PACKETS [%d]"), iPacketsInBuffer);

    iDataBuffer = static_cast<CMMFDataBuffer*> (aDataBuffer);

    if (iCurrentSeqNum > iLastPlayedSeqNum)
        {
        if (IsFull())
            {
            TRACE_PRN_N(_L("JB-> [ADD] BUFFER OVERFLOW"));

            // Remove the oldest frame from the buffer
            iLastPlayedSeqNum = iBuffer[iPacketsInBuffer - 1].iSequenceNumber;
            iBuffer[iPacketsInBuffer - 1].iSequenceNumber = -1;
            iBuffer[iPacketsInBuffer - 1].iTimeStamp = -1;
            iPacketsInBuffer--;

            InsertBufferElement(iDataBuffer->Data(), index);
            iDataBuffer = NULL;
            iFramesRemoved++;
            iOverflowCounter++;

            TRACE_PRN_N1(_L("JB-> [ADD] OverflowCount[%d]"), iOverflowCounter);

            // Report overflow only after KOverflowAlarmLimit
            if (iObserver && iOverflowCounter > KOverflowAlarmLimit)
                {
                iObserver->EventJB(MJitterBufferObserver::EBufferOverflow);
                ResetBuffer(ETrue);
                iOverflowCounter = 0;
                }
            }
        else if (IsEmpty())
            {
            TTimeIntervalSeconds interval;
            TTime currentTime;
            currentTime.UniversalTime();
            currentTime.SecondsFrom(iTonePlayTime, interval);

            if (iPlay && interval.Int() >= iPlayToneInterval)
                {
                TRACE_PRN_N(_L("JB-> [ADD] BUFFER EMPTY - Tone to be played"));
                iTonePlayTime.UniversalTime();
                iPlay = EFalse;
                }
            else
                {
                TRACE_PRN_N(_L("JB-> [ADD] BUFFER EMPTY - Tone NOT played"));
                TRACE_PRN_N1(_L("JB-> [ADD] Interval Tm [%d]"), interval.Int());
                }

            // No point to run at 0 threshold; restore the original
            // settings and rebuffer.
            iCurrentPlayThreshold = iOriginalPlayThreshold;

            const TInt64 nextGetSeqNum(iLastPlayedSeqNum + iSeqNumIncrement);
            const TInt64 frameSeq(iDataBuffer->FrameNumber());

            if (frameSeq > nextGetSeqNum)
                {
                // We are empty and there are frames missing between last
                // received frame and frame sequence number. This might be
                // because of DTX period, so we need to adjust accordingly.
                iLastPlayedSeqNum = frameSeq - iSeqNumIncrement;
                InsertBufferElement(iDataBuffer->Data(), index);

                TRACE_PRN_N(_L("JB-> [ADD] MISSING & EMPTY"));
                }
            else
                {
                InsertBufferElement(iDataBuffer->Data(), index);
                }
            }
        else
            {
            TRACE_PRN_N(_L("JB-> [ADD] NORMAL"));
            iOverflowCounter = 0;
            InsertBufferElement(iDataBuffer->Data(), index);
            }
        }
    else
        {
        iNumOfLateFrames++;
        aDataBuffer->SetStatus(EAvailable);
        TRACE_PRN_N(_L("JB-> [ADD] TOO LATE"));
        }

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::GetDataFrame
// Get DataFrame
// -----------------------------------------------------------------------------
//
TInt CVoIPJitterBufferImpl::GetDataFrame(CMMFBuffer* aBuffer)
    {
    if (aBuffer == NULL)
        {
        return KErrNotReady;
        }

    iDataBuffer = static_cast<CMMFDataBuffer*> (aBuffer);
    TDes8& playBuffer(iDataBuffer->Data());
    iFramesPlayed++;

    if (IsEmpty())
        {
        // Buffer is empty, so we need to generate CN frames. During DTX period
        // this is where we'll end up. If we are empty because of DTX period,
        // the CN generator will do the error concealment.
        TRACE_PRN_N(_L("JB-> [GET] BUFFER EMPTY"));

        iCNGenerator->GenerateSidPacket(playBuffer,
                iDataBuffer->RequestSize());

        // Used for talkburst; when talk-burst is over, reset buffer
        // and sequence number
        if (iJBConfig.iJBInactivityTimeOut > 0 && iInactivityTime >= 0)
            {
            // Currently it is only used for AMR codec.
            iInactivityTime += iSampleInterval;

            if (iInactivityTime >= iJBConfig.iJBInactivityTimeOut)
                {
                TRACE_PRN_N1(_L("JB-> [GET] Inactivity Time Detected [%d]"),
                        iInactivityTime);
                iInactivityTime = -1;
                ResetBuffer();
                }
            }
        }
    else
        {
        // Check if threshold is met. If not, continue buffering.
        if (iPacketsInBuffer < iCurrentPlayThreshold)
            {
            // Give comfort noise when we are buffering before playback starts
            TRACE_PRN_N(_L("JB-> [GET] THRESHOLD NOT REACHED"));
            iCNGenerator->GenerateSidPacket(playBuffer,
                    iDataBuffer->RequestSize());
            }
        else
            {
            // Reset threshold, so we can  play current talk-spurt till the
            // end without affecting speech quality. This means that we will
            // play the buffer until it is empty. After that, it is either
            // DTX period or packet loss.
            iCurrentPlayThreshold = 0;

            // The actual sequence number which is in the buffer
            //const TInt64 frameToPlay(
            //iBuffer[iPacketsInBuffer - 1].iSequenceNumber);

            TRACE_PRN_N1(_L("JB-> [GET] FRAME TO PLAY [%d]"),
                    TInt32(iBuffer[iPacketsInBuffer - 1].iSequenceNumber));
            TRACE_PRN_N1(_L("JB-> [GET] LAST PLAYED   [%d]"),
                    TInt32(iLastPlayedSeqNum));
            TRACE_PRN_N1(_L("JB-> [GET] PACKETS [%d]"), iPacketsInBuffer);

            // Get next in order frame to play
            TJitterBufferElement& jbElement = iBuffer[iPacketsInBuffer - 1];

            if (playBuffer.MaxLength() >=
                    jbElement.iDataFrame->Data().Length())
                {
                playBuffer.Copy(jbElement.iDataFrame->Data());
                }
            else
                {
                TRACE_PRN_N1(_L("JB-> [GET] ERROR: BUFFER TOO SMALL [%d]"),
                        playBuffer.MaxLength());

                iCNGenerator->GenerateSidPacket(playBuffer,
                        iDataBuffer->RequestSize());
                }

            iLastPlayedSeqNum = jbElement.iSequenceNumber;
            jbElement.iSequenceNumber = -1;
            jbElement.iTimeStamp = -1;
            iPacketsInBuffer--;

            // Reset inactivity timeout timer
            iInactivityTime = 0;

#ifdef __FEATURE_NOT_SUPPORTED__
            // Try to lead new buffer to DTX period
            iCNGenerator->DoDtxDecision(playBuffer);
#endif //__FEATURE_NOT_SUPPORTED__
            }
        }

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::FindLargestSeqNum
// Find Largest SequenceNumber
// -----------------------------------------------------------------------------
//
TUint CVoIPJitterBufferImpl::FindLargestSeqNum()
    {
    TUint pos = 0;
    const TInt len = BufferLength();
    iLargestSeqNum = 0;

    for (TUint i = 0; i < len; i++)
        {
        // 99% of the time element 0 will hold the largest sequence num,
        // so, it won't have to search through the entire array.
        if (iBuffer[i].iSequenceNumber > iLargestSeqNum)
            {
            iLargestSeqNum = iBuffer[i].iSequenceNumber;
            pos = i;
            }
        }

    return pos;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::InsertBufferElement
// Insert Buffer Element
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::InsertBufferElement(const TDesC8& aBuffer,
        TInt64 aLargestSeqNum)
    {
    /* if (aBuffer.Length() == 0 || aBuffer.Length() > iFrameSize)
        {
        TRACE_PRN_N(_L("JB->INVALID DATA, IGNORING"));
        return;
        }*/

    const TInt len(BufferLength());

    if (iIsWrappedAround)
        {
        iBuffer[len - 1].iDataFrame->Data().Copy(aBuffer);
        iBuffer[len - 1].iSequenceNumber = iCurrentSeqNum;

        for (TInt i = 0; i < len; i++)
            {
            if (iBuffer[i].iTimeStamp > aLargestSeqNum)
                {
                aLargestSeqNum = iBuffer[i].iTimeStamp;
                }
            }

        iBuffer[len - 1].iTimeStamp = aLargestSeqNum + 1;
        iBuffer.Sort(iBufStampSorter);
        iLastPlayedSeqNum = -1;
        }
    else
        {
        iBuffer[len - 1].iDataFrame->Data().Copy(aBuffer);
        iBuffer[len - 1].iSequenceNumber = iCurrentSeqNum;
        iBuffer[len - 1].iTimeStamp = iCurrentSeqNum - BufferLength();
        iBuffer.Sort(iBufSequenceSorter);
        }

    iPacketsInBuffer++;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::BufferLength()
// Return buffer length.
// -----------------------------------------------------------------------------
//
TInt CVoIPJitterBufferImpl::BufferLength() const
    {
    return iBufferLength;
    }

#ifdef __FEATURE_NOT_SUPPORTED__
// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::PacketCount()
// Return number of packets that are currently in the buffer.
// -----------------------------------------------------------------------------
//
TInt CVoIPJitterBufferImpl::PacketCount() const
    {
    return iPacketsInBuffer;
    }
#endif //__FEATURE_NOT_SUPPORTED__

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::IsFull()
// Return:    True if full
//            False if not full
// -----------------------------------------------------------------------------
//
TBool CVoIPJitterBufferImpl::IsFull() const
    {
    return (iPacketsInBuffer == iBufferLength);
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::IsEmpty()
// Return:    True if empty
//            False if not empty
// -----------------------------------------------------------------------------
//
TBool CVoIPJitterBufferImpl::IsEmpty() const
    {
    return (iPacketsInBuffer == 0);
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::IsSeqNumWrappedAround
// Checks if sequence number is wrapped around between aSeqNum1 and aSeqNum2.
// The sequence number wrap-around condition cannot be reliably detected by
// checking for exact numbers (e.g. aSeqNum1==KMaxSeqNumber and aSeqNum2==0).
// This is because of network packet loss, where there might be gaps between
// frame sequence numbers.
// -----------------------------------------------------------------------------
//
TBool CVoIPJitterBufferImpl::IsSeqNumWrappedAround(TInt64 aSeqNum1,
        TInt64 aSeqNum2) const
    {
    TBool status = EFalse;

    if (aSeqNum1 >= (KMaxSeqNumber - iSeqNumIncrement) &&
            aSeqNum2 <= (iBufferLength * iSeqNumIncrement))
        {
        TRACE_PRN_N(_L("JB-> SEQUENCE WRAPPED-AROUND"));
        status = ETrue;
        }

    return status;
    }

#ifdef __FEATURE_NOT_SUPPORTED__
// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::GenerateStatistics
// Generates the statistics from the jitterbuffer
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::GenerateStatistics(/*TJBStats& aStats*/) const
    {
    aStats.iFramesReceived = iFramesReceived;
    aStats.iBufferLength = iBufferLength;
    aStats.iFramesInBuffer = iPacketsInBuffer;
    aStats.iFrameLoss = iFramesLost;
    aStats.iLateFrames = iNumOfLateFrames;
    aStats.iFramesRemoved = iFramesRemoved;
    aStats.iFramesPlayed = iFramesPlayed;
    }
#endif //__FEATURE_NOT_SUPPORTED__

// -----------------------------------------------------------------------------
// CVoIPJitterBufferImpl::CheckThresholdBufferLength
// -----------------------------------------------------------------------------
//
void CVoIPJitterBufferImpl::CheckThresholdBufferLength(TInt& aBufferLength,
        TInt aTreshhold) const
    {
    const TInt numTen = 10;

    if ((aBufferLength - aTreshhold) < numTen)
        {
        aBufferLength = aTreshhold + numTen;
        }
    }

//  End of File