mmserv/voipaudioservices/JitterBufferUtility/src/JitterBufferImpl.cpp
changeset 0 71ca22bcf22a
child 53 eabc8c503852
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/voipaudioservices/JitterBufferUtility/src/JitterBufferImpl.cpp	Tue Feb 02 01:08:46 2010 +0200
@@ -0,0 +1,876 @@
+/*
+* 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;
+    }
+
+// -----------------------------------------------------------------------------
+// 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;
+    }
+
+// -----------------------------------------------------------------------------
+// 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;
+
+            // Try to lead new buffer to DTX period
+            iCNGenerator->DoDtxDecision(playBuffer);
+            }
+        }
+    
+    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)
+    {
+//    TRACE_PRN_FN_ENT;
+
+/*    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++;
+
+//    TRACE_PRN_FN_EXT;
+    }
+
+// -----------------------------------------------------------------------------
+// CVoIPJitterBufferImpl::BufferLength()
+// Return buffer length.
+// -----------------------------------------------------------------------------
+//
+TInt CVoIPJitterBufferImpl::BufferLength() const
+    {
+    return iBufferLength;
+    }
+
+// -----------------------------------------------------------------------------
+// CVoIPJitterBufferImpl::PacketCount()
+// Return number of packets that are currently in the buffer.
+// -----------------------------------------------------------------------------
+//
+TInt CVoIPJitterBufferImpl::PacketCount() const
+    {
+    return iPacketsInBuffer;
+    }
+
+// -----------------------------------------------------------------------------
+// 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;
+    }
+
+// -----------------------------------------------------------------------------
+// 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;*/
+    }
+
+// -----------------------------------------------------------------------------
+// CVoIPJitterBufferImpl::CheckThresholdBufferLength
+// -----------------------------------------------------------------------------
+//
+void CVoIPJitterBufferImpl::CheckThresholdBufferLength(TInt& aBufferLength,
+                                                       TInt aTreshhold) const
+    {
+    const TInt numTen = 10;
+
+    if ((aBufferLength - aTreshhold) < numTen)
+        {
+        aBufferLength = aTreshhold + numTen;
+        }
+    }
+
+
+//  End of File
+