multimediacommscontroller/mmccjitterbuffer/src/mccjitterbufferimpl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:59:15 +0300
branchRCL_3
changeset 59 b0e4b01681c5
parent 42 817c922b90eb
permissions -rw-r--r--
Revision: 201039 Kit: 201041

/*
* Copyright (c) 2004-2007 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 "mmcccodecinformation.h"
#include "mccjitterbufferimpl.h"
#include "mcccngenerator.h"
#include "mccinternalevents.h"
#include "mccinternaldef.h"
#include "mccjitterbufferlogs.h"
#include "mccjitterbufferobserver.h"

// LOCAL CONSTANTS
const TInt KDefaultSampleRateInkHz = 8;
const TInt KWbSampleRateInkHz = 16;
const TInt KLowBufferLimit = 2;
const TInt KOverflowAlarmLimit = 20;
const TInt KSequentialLateFramesLimit = 20;

// Codec frame sizes
const TInt KAMRNBFrameSize = 32;
const TInt KAMRWBFrameSize = 64;
const TInt KILBCFrameSize = 52;
const TInt KG729FrameSize = 24; // 10 octets + 2 for header

// Maximum possible sequence number of RTP packet with standard RTP header.
const TInt KMaxSeqNumber = 65535;        // 16-bit value

// Codec frame times
const TInt KAMRNBFrameTime = 20;

// ============================= LOCAL FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// 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.iStamp > aElem2.iStamp )
        {
        return (-1);
        }
    else if ( aElem1.iStamp < aElem2.iStamp )
        {
        return (1);
        }
    else
        {
        return (0);
        }
    }

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::CMccJitterBufferImpl
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMccJitterBufferImpl::CMccJitterBufferImpl( MJitterBufferObserver* aObserver): 
		iPacketsInBuffer( 0 ), 
        iLastGetSeqNum( -1 ), 
        iBufStampSorter( 
            TLinearOrder<TJitterBufferElement>( TJitterBufferElement::CompareStamp ) 
            ),
        iBufSequenceSorter( 
            TLinearOrder<TJitterBufferElement>( TJitterBufferElement::CompareSeqNum ) 
            ),
        iSeqNumIncrement( 0 ),
        iPlay( EFalse ),
        iObserver( aObserver ),
        iOverflowCounter( 0 ),
        iPlayToneInterval( 0 ),
        iNotifyUser( ETrue ),
        iLatestNotifiedEvent( KMccEventNone ),
        iSampleRate( KDefaultSampleRateInkHz )
    {
    iTonePlayTime.UniversalTime();
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::NewL
// Static constructor.
// -----------------------------------------------------------------------------
//
CMccJitterBufferImpl* CMccJitterBufferImpl::NewL( MJitterBufferObserver* aObserver )
    {
    __JITTER_BUFFER( "CMccJitterBufferImpl::NewL 64bit version" )

    CMccJitterBufferImpl* self = new( ELeave ) CMccJitterBufferImpl( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::ConstructL()
    {
    SetBufferState( EIdle );
    SendStreamEventToClient( KMccStreamIdle, ETrue );
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::~CMccJitterBufferImpl
// Destructor deallocate memory.
// -----------------------------------------------------------------------------
//
CMccJitterBufferImpl::~CMccJitterBufferImpl()
    {
    __JITTER_BUFFER( "CMccJitterBufferImpl::~CMccJitterBufferImpl entry" )

    delete iCnGenerator;    
        
    // Deallocate payload memory of jitter buffer elements.
    const TInt count = iBuffer.Count();
    for ( TInt i = 0; i < count; i++ )
        {
        delete iBuffer[i].iDataFrame;
        } 

    iBuffer.Close();
    iObserver = NULL;
    
    __JITTER_BUFFER( "CMccJitterBufferImpl::~CMccJitterBufferImpl exit" )
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::SetupL
// Setup Jitterbuffer
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::SetupL( 
    TInt aPlayoutThreshold, 
    const TMccCodecInfo& aCInfo, 
    CMMFDevSound& aDevSound, 
    MAsyncEventHandler* aEventHandler,
    TUint32 aEndpointId )
    {
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufBufferLength:",
                          aCInfo.iJitterBufBufferLength )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aPlayoutThreshold:",
                          aPlayoutThreshold )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufInactivityTimeOut:",
                          aCInfo.iJitterBufInactivityTimeOut )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneTimeout:",
                          aCInfo.iJitterBufPlayToneTimeout )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneFrequency:",
                          aCInfo.iJitterBufPlayToneFrequency )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneDuration:",
                          aCInfo.iJitterBufPlayToneDuration )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iHwFrameTime:",
                          aCInfo.iHwFrameTime )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iPtime:",
                          aCInfo.iPtime )
    
    // Changed if-statement to asserts. Added also check agains 0 hwframetime
    __ASSERT_ALWAYS( aCInfo.iPtime, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( aCInfo.iJitterBufBufferLength, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( aCInfo.iHwFrameTime, User::Leave( KErrArgument ) );
    
    // Save the original HW frame time because we may need it in case of
    // dynamic G.711 adjustment.
    const TUint8 origHwtime = iCInfo.iHwFrameTime;
    iCInfo = aCInfo;
    iEventHandler = aEventHandler;
    iEndpointId = aEndpointId;
    
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL origHwtime:", origHwtime )
    
    if( iCInfo.iJitterBufInactivityTimeOut )
        {
        if( ( iCInfo.iJitterBufPlayToneFrequency > 0 ) && 
            ( iCInfo.iJitterBufPlayToneDuration > 0 ) )
            {
            iPlayToneInterval = iCInfo.iJitterBufPlayToneTimeout;            
            iPlay = ETrue;
            }
        }
        
    TInt bufLenMultiplier = 1;
    if ( iCInfo.iFourCC == KMMFFourCCCodeAMR )
        {
        iFrameSize = KAMRNBFrameSize;
        iFrameTime = KAMRNBFrameTime;
        }
    else if ( iCInfo.iFourCC == KMMFFourCCCodeAWB )
        {   
        iFrameSize = KAMRWBFrameSize;
        iFrameTime = KAMRNBFrameTime;
        iSampleRate = KWbSampleRateInkHz;
        }
    else if( iCInfo.iFourCC == KMccFourCCIdG711 )
        {
        // G.711 is configured dynamically. Take voip headerlength also in to
        // account. G.711 hwframetime is in milliseconds, so need to multiply.
        iFrameSize = ( iCInfo.iHwFrameTime * KDefaultSampleRateInkHz )
            + KVoIPHeaderLength;
        iFrameTime = 0;
        
        // In case of G.711 codec dynamic configuration, we may need to double
        // the jitterbuffer length if HW frame time is changed from 20ms to
        // 10ms. 
        if ( origHwtime )
            {
            bufLenMultiplier = origHwtime / iCInfo.iHwFrameTime;
            if ( !bufLenMultiplier )
                {
                bufLenMultiplier = 1;
                }
            }
        }
    else if( iCInfo.iFourCC == KMccFourCCIdILBC )
        {
        iFrameSize = KILBCFrameSize;
        iFrameTime = 0;
        }
    else if( iCInfo.iFourCC == KMccFourCCIdG729 )
        {
        iFrameSize = KG729FrameSize;
        iFrameTime = 0;
        // Multiply G.729 also by two...
        bufLenMultiplier = 2;
        }
    else
        {
        __JITTER_BUFFER( "CMccJitterBufferImpl::SetupL KErrNotSupported" )
        
        User::Leave( KErrNotSupported );
        }
            
    // Delete old buffer & reset it
    const TInt elems = iBuffer.Count();
    for( TInt i = 0; i < elems; i++ )
        {
        delete iBuffer[i].iDataFrame;
        iBuffer[i].iDataFrame = NULL;
        }
    
    iBuffer.Reset();
    
    // Calculate needed elements
    TInt bufLen = iCInfo.iJitterBufBufferLength * bufLenMultiplier;
    
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL G.711 bufLenMultiplier ",
        bufLenMultiplier )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL iBufferLength: ",
        bufLen )
    
    __ASSERT_ALWAYS( aPlayoutThreshold < bufLen, User::Leave( KErrTooBig ) );
    
    // if differences between bufferlength and treshold set by client
    // is less than 10, increase bufferlength so the differences is 10
    // this is to help buffer goes to overflow easily.
    // Also possible G.711/729 multiplier needs to be taken care of.
    CheckThresholdBufferLength( bufLen, aPlayoutThreshold );
    iCurrentPlayThreshold = aPlayoutThreshold * bufLenMultiplier;
    iOriginalPlayThreshold = aPlayoutThreshold * bufLenMultiplier;
    
    if( iCnGenerator )
        {
        delete iCnGenerator;
        iCnGenerator = NULL;
        }

    iCnGenerator = CMccCnGenerator::NewL( iCInfo.iFourCC, aDevSound );
    
    // Create the elements in the Buffer
    for( TInt k = 0; k < bufLen; k++ )
        {
        CMMFDataBuffer* buf = CMMFDataBuffer::NewL( iFrameSize );
        CleanupStack::PushL( buf );
        TJitterBufferElement newElement;
        newElement.iDataFrame = buf;
        newElement.iSequenceNumber = -1;
        newElement.iStamp = -1;
        iBuffer.AppendL( newElement );
        CleanupStack::Pop( buf );
        }
    
    // Zero the statistic members
    iFramesLost = 0;
    iFramesReceived = 0;
    iNumOfLateFrames = 0;
    iFramesRemoved = 0;
    iFramesPlayed = 0;
    iPacketsInBuffer = 0;
    
    // Calculate the sequence number increment
    iSeqNumIncrement = iSampleRate * iCInfo.iHwFrameTime;
    } 
        
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::ResetBufferL
// Reset Jitterbuffer
// -----------------------------------------------------------------------------
//

void CMccJitterBufferImpl::ResetBuffer( TBool aPlayTone, TBool aNotifyUser )
    {
    const TInt bufLen = BufferLength();
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::ResetBuffer bufLen: ", bufLen )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::ResetBuffer pktsIn: ", iPacketsInBuffer )
    
    for( TInt i = 0; i < bufLen; i++ )
        {
        iBuffer[i].iSequenceNumber = -1;
        iBuffer[i].iStamp = -1;
        }
    
    iLastGetSeqNum = -1;
    iCurrentPlayThreshold = iOriginalPlayThreshold;
    iPacketsInBuffer = 0; 
    iPlay = aPlayTone; 
    
    if ( BufferState() != EIdle && 
         aNotifyUser && 
         iLatestNotifiedEvent != KMccStreamIdle )
        {
        SendStreamEventToClient( KMccStreamIdle, ETrue );
        }
    
    SetBufferState( EIdle );
    iDropNextFrame = EFalse;
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::CurrentCodec
// Return current codec.
// -----------------------------------------------------------------------------
//
TFourCC CMccJitterBufferImpl::CurrentCodec() const
    {
    return iCInfo.iFourCC;
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::DelayUpL
// Adds a buffer element into the jitter buffer
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::DelayUpL()
    {
    CMMFDataBuffer* buf = CMMFDataBuffer::NewL( iFrameSize );
    CleanupStack::PushL( buf );
    TJitterBufferElement newElement;
    newElement.iDataFrame = buf;
    newElement.iSequenceNumber = -1;
    newElement.iStamp = -1;
    iBuffer.AppendL( newElement );
    CleanupStack::Pop( buf );
    
    // Insert one NO_DATA frame into the audio stream, so the jitterbuffer has
    // the possibility to grow at least one frame from the current status. 
    // If the current playout threshold is zero, then there is playout ongoing or
    // a DTX period. This means that we should buffer at least one frame before
    // starting playout.
    if( 0 == iCurrentPlayThreshold )
        {
        __JITTER_BUFFER( "CMccJitterBufferImpl::DelayUpL Adjusting Playout Threshold" )
        
        iCurrentPlayThreshold = iPacketsInBuffer + 1;
        }
    
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL BUF_LEN:", BufferLength() )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL CUR_FRAMES:", iPacketsInBuffer )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL PLAY_TH:", iCurrentPlayThreshold )
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::DelayDownL
// Removes an buffer element from the jitter buffer
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::DelayDownL()
    {
    // 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.
    const TInt bufLen = BufferLength();
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayDownL BUF_LEN:", bufLen )
    __JITTER_BUFFER_INT1("CMccJitterBufferImpl::DelayDownL CUR_FRAMES:", iPacketsInBuffer )
    
    if( this->IsEmpty() )
        {
        // Cannot do anything for empty buffer
        if( KLowBufferLimit < bufLen )
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Empty buffer" )
            
            delete iBuffer[bufLen - 1].iDataFrame;
            iBuffer.Remove( bufLen - 1 );
            }
        else if( 0 < iCurrentPlayThreshold )
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Playthreshold modification" )
                        
            iCurrentPlayThreshold--;
            }
        else
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer empty, cant do anything" )
            // Cannot do anything currently
            return;
            }
        }
    else if( this->IsFull() )
        {
        // If there is sufficiently data in the buffer, then remove one
        // frame and mark the one preceding it bad
        if( KLowBufferLimit < iPacketsInBuffer )
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer full, doing removal" )
            
            // Remove the 2nd oldest frame and mark the oldest as bad
            delete iBuffer[1].iDataFrame;
            iBuffer.Remove( 1 );
            
            // MARK THE FIRST AS BAD FRAME!!!
            iPacketsInBuffer--;
            iFramesRemoved++;
            }
        else
            {
            // Cant do removing, see if playthreshold can be adjusted
            if( 0 < iCurrentPlayThreshold )
                {
                __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer full, cant do anything" )
                
                iCurrentPlayThreshold--;
                }
            }
        }
    else
        {
        if( KLowBufferLimit < bufLen )
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Some frames, adjusting length" )
            
            // Adjust the length of the buffer
            delete iBuffer[bufLen - 1].iDataFrame;
            iBuffer.Remove( bufLen - 1 );
            }
        else
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Some frames, too small buffer" )
            }
        }
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::CalculateDelay
// Calculates the current delay that jitterbuffer has in the playback
// -----------------------------------------------------------------------------
//
TTimeIntervalMicroSeconds32 CMccJitterBufferImpl::CalculateDelay() const
    {
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::CalculateDelay:", iCInfo.iPtime * iPacketsInBuffer )

    return iCInfo.iPtime * iPacketsInBuffer;
    }        
        
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::AddDataFrameL
// Adds a audio frame into the jitterbuffer passed in the aDataBuffer
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::AddDataFrameL( CMMFBuffer* aDataBuffer )
    {
    User::LeaveIfNull( aDataBuffer );
    const TInt64 seqNum = aDataBuffer->FrameNumber();
    iFramesReceived++;
    
    const TInt num( FindLargestSeqNum() );
    
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL FRAM_SEQNUM:", seqNum )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL LAST_SEQNUM:", iLastGetSeqNum )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL PACKETS:", iPacketsInBuffer )
    
    const CMMFDataBuffer* dataBuffer( static_cast<CMMFDataBuffer*>( aDataBuffer ) );
    
    if( seqNum > iLastGetSeqNum )
        {
        iNumOfSequentialLateFrames = 0;
        if( IsFull() )
            {
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL OFLOW dropnext: ",
                iDropNextFrame );
            
            iFramesRemoved++;
			iOverflowCounter++;
			if ( iObserver && iOverflowCounter > KOverflowAlarmLimit )
                {
	            __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL OFLOW reported" )
	            
                iOverflowCounter = 0;
                iObserver->JitterBufferError( MJitterBufferObserver::EBufferOverflow );
                ResetBuffer( ETrue, EFalse );
                
                // Do not inform resource unavailability several times. Once playback
                // is ok again, resource unavailability can be informed if it occurs.
                SendStreamEventToClient( KMccResourceNotAvailable, ETrue );
                iNotifyUser = EFalse;
                }
            
            // Check that we actually have packets to remove from the buffer.
            if ( !iDropNextFrame && iPacketsInBuffer )
                {
                // Remove the newest frame in the buffer
                (void) iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber;
                iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber = -1;
                iBuffer[ iPacketsInBuffer - 1 ].iStamp = -1;
                iPacketsInBuffer--;
                
                iDropNextFrame = ETrue;
                }
            else
                {
                iDropNextFrame = EFalse;
                }
            
            // If we dropped this frame, then return from here.
            if ( !iDropNextFrame )
                {
                return;
                }
            }
        else if( IsEmpty() )
            {
            // Buffering
            HandleNotifications( ETrue );
            
    		TTimeIntervalSeconds interval;
    		TTime currentTime;
    		currentTime.UniversalTime();
            currentTime.SecondsFrom( iTonePlayTime, interval );
            
            if( iPlay && interval.Int() >= iPlayToneInterval )
                {    
                __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL tone to be played" )                
    			iTonePlayTime.UniversalTime();
                iPlay = EFalse;
                }
            
            const TInt64 nextGetSeqNum( iLastGetSeqNum + iSeqNumIncrement );
            if( seqNum > nextGetSeqNum )
                {
                // We are empty and there is frames missing between last get frame
                // and frame sequence number. This might be because of DTX period
                // so we need to adjust to the situation
                iLastGetSeqNum = seqNum - iSeqNumIncrement;
                
                __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL MISSING & EMPTY" )
                __JITTER_BUFFER_INT2( "CMccJitterBufferImpl::AddDataFrameL ADJ_LAST:", iLastGetSeqNum,
                                      " FRM_SEQ:", seqNum )
                }
            }
        else
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL NORMAL" )
            
			iOverflowCounter = 0;
            }
        
        // Reset inactivity timing. NB. all branches need to reset inactivity
        // and insert the new element.
        iInactivityTime = 0;
        InsertBufferElement( dataBuffer->Data(), num, seqNum );
        }
    else
        {
        iNumOfLateFrames++;
        if(++iNumOfSequentialLateFrames > KSequentialLateFramesLimit )
             iLastGetSeqNum = -1;
        
        __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL TOO LATE" )
        }
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::GetDataFrameL
// Get DataFrame
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::GetDataFrameL( CMMFBuffer* aBuffer )
    {
    User::LeaveIfNull( aBuffer );
    CMMFDataBuffer& dataBuffer = static_cast<CMMFDataBuffer&>( *aBuffer );
    TDes8& playBuffer( dataBuffer.Data() );
    iFramesPlayed++;
    
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL PACKETS:", iPacketsInBuffer )
    
    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.
        iCnGenerator->GenerateSidPacket( playBuffer, dataBuffer.RequestSize() );
        iCurrentPlayThreshold = iOriginalPlayThreshold;
        
        // Used for talkburst, if talkburst over, reset buffer and sequence numbers
        if( iCInfo.iJitterBufInactivityTimeOut > 0 && iInactivityTime >= 0 )
            {
            iInactivityTime = iInactivityTime + iFrameTime;
            if( iInactivityTime >= (TInt) iCInfo.iJitterBufInactivityTimeOut )
                {
                __JITTER_BUFFER_INT2("CMccJitterBufferImpl::Inactivity timeout:",
                                     iCInfo.iJitterBufInactivityTimeOut,
                                     " time:", iInactivityTime )
                
                iInactivityTime = -1;
                ResetBuffer();
                }
            }
        }
    else 
        {
        // This is where we buffer some data before starting the playout so we can
        // take some jitter by nature without adaptation.
        if( iPacketsInBuffer < iCurrentPlayThreshold )
            {
            // Give comfort noise when we are buffering for playback start
            __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL THRESHOLD NOT REACHED" )
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL iCurrentPlayThreshold",
                iCurrentPlayThreshold )
            
            iCnGenerator->GenerateSidPacket(
                playBuffer, dataBuffer.RequestSize() );
            }
        else
            {
            // Not buffering
            HandleNotifications( EFalse );
            
            // Set the playout threshold to zero so we play the current talkspurt to the
            // end and do not affect the speech quality. This means that we will play the
            // buffer until it is empty. After that it is either DTX period or it is packet
            // loss.
            iCurrentPlayThreshold = 0;
            
            // Next sequence number that should be in the buffer
            TInt64 nextSeqNum = iLastGetSeqNum + iSeqNumIncrement;
            if( -1 == iLastGetSeqNum )
                {
                // Last get sequence number was -1, so add one
                __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL LAST WAS -1 ADJUSTING" )

                nextSeqNum += 1;
                }
                
            // The actual sequence number which is in the buffer
            const TInt64 nextFrameSeqNum(
                iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber );
            
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL NEXT_GET_SEQ_NUM:", nextSeqNum )
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL FRAME_SEQ_NUM:", nextFrameSeqNum )
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL PACKETS:", iPacketsInBuffer )
            __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL LAST_GET_SEQ_NUM:", iLastGetSeqNum )
            
            // Check if the next sequence number is smaller than the frame number of the next
            // frame to be played out. This will mean that a frame was lost and we need to do
            // error concealment. 
            // Also we need to take account the start of the playout when iLastGetSeqNum == -1 
            // so we play the first frame in the buffer regardless of it's sequence number.
            // This should also take the sequence number wrap around situation.
            
            // As if(0) is intentional, PC-lint warning disabled:
            /*lint -e506 */
            //if ( nextSeqNum < nextFrameSeqNum )
            if ( 0 )
                {
                __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL LOST FRAME" )

                iFramesLost++;
                iLastGetSeqNum = iLastGetSeqNum + iSeqNumIncrement;
                
                __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL 1. LAST_GET_SEQ_NUM IS NOW:", iLastGetSeqNum )
                
                iCnGenerator->GenerateSidPacket( playBuffer,
                    dataBuffer.RequestSize() );
                }
            else
                {
                __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL NORMAL" )
                
                TJitterBufferElement& bufferElement = iBuffer[ iPacketsInBuffer - 1 ];
                if ( playBuffer.MaxLength() >= bufferElement.iDataFrame->Data().Length() )
                    {
                    playBuffer.Copy( bufferElement.iDataFrame->Data() );
                    }
                else
                    {
                    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL ERROR - output buffer too small, maxlen:", 
                                          playBuffer.MaxLength() )

                    iCnGenerator->GenerateSidPacket( playBuffer, dataBuffer.RequestSize() );
                    }
                
                iLastGetSeqNum = bufferElement.iSequenceNumber;
                bufferElement.iSequenceNumber = -1;
                bufferElement.iStamp = -1;
                iPacketsInBuffer--;
                iInactivityTime = -1;
                
                // Lets see if the new buffer will lead into a DTX period
                iCnGenerator->DoDtxDecision( playBuffer );
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::FindLargestSeqNum
// Find Largest SequenceNumber
// -----------------------------------------------------------------------------
//
TInt CMccJitterBufferImpl::FindLargestSeqNum() const
    {
    TInt64 num = 0;
    TInt pos = 0;
    const TInt len( BufferLength() );
    
    for( TInt i = 0; i < len; i++ )
        {
        if( iBuffer[i].iSequenceNumber > num ) 
            {
            num = iBuffer[i].iSequenceNumber;
            pos = i;
            }
        }

    return pos;
    }
    
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::InsertBufferElement
// Insert Buffer Element
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::InsertBufferElement( const TDesC8& aBuffer, 
    TInt64 aLargestSeqNum, TInt64 aFrameNumber )
    {
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::InsertBufferElement aFrameNumber:",
        aFrameNumber )
    
    if ( !aBuffer.Length() || aBuffer.Length() > iFrameSize )
        {
        __JITTER_BUFFER( "CMccJitterBufferImpl::InsertBufferElement INVALID DATA, IGNORING" )
        return;
        }
    
    const TInt len( this->BufferLength() );
    iBuffer[len-1].iDataFrame->Data().Copy( aBuffer );
    iBuffer[len-1].iSequenceNumber = aFrameNumber;
    
    if ( IsSeqNumWrappedAround( iBuffer[aLargestSeqNum].iSequenceNumber, aFrameNumber ) )
        {
        for ( TInt i = 0; i < len; i++ )
            {
            if( iBuffer[i].iStamp > aLargestSeqNum ) 
                {
                aLargestSeqNum = iBuffer[i].iStamp;
                }
            }
        
        iBuffer[len-1].iStamp = aLargestSeqNum + 1;
        iBuffer.Sort( iBufStampSorter );
        iLastGetSeqNum = -1;
        }
    else 
        {
        iBuffer[len-1].iStamp = aFrameNumber - BufferLength();
        iBuffer.Sort( iBufSequenceSorter );
        }
    
    iPacketsInBuffer++;
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::BufferLength()
// Return bufferlength.
// -----------------------------------------------------------------------------
//
TInt CMccJitterBufferImpl::BufferLength() const
    {
    return iBuffer.Count();
    }

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

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

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::IsSeqNumWrappedAround
// Checks if sequence number wrapped around from aSeqNum1 to aSeqNum2
// The sequence number wrap-around condition cannot be reliably detected by 
// checking for exact numbers, i.e., aSeqNum1==KMaxSeqNumber and aSeqNum2==0, 
// because, due to packet losses in network, sequence numbers may have gaps 
// between them.
// -----------------------------------------------------------------------------
//
TBool CMccJitterBufferImpl::IsSeqNumWrappedAround( TInt64 aSeqNum1, 
    TInt64 aSeqNum2 ) const
    {
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::IsSeqNumWrappedAround aSeqNum1:", aSeqNum1 )
    __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::IsSeqNumWrappedAround aSeqNum2:", aSeqNum2 )
    
    if ( aSeqNum1 > ( KMaxSeqNumber - BufferLength() ) && 
         aSeqNum2 < BufferLength() )
        {
        __JITTER_BUFFER( "CMccJitterBufferImpl::IsSeqNumWrappedAround ETrue" )
        return ETrue;
        }
    else
        {
        __JITTER_BUFFER( "CMccJitterBufferImpl::IsSeqNumWrappedAround EFalse" )
        return EFalse;
        }
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::GenerateStatistics
// Generates the statistics from the jitterbuffer
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::GenerateStatistics( TMccJitterBufferEventData& aEvent ) const
    {
    aEvent.iFramesReceived = iFramesReceived;
    aEvent.iBufferLength = BufferLength();
    aEvent.iFramesInBuffer = iPacketsInBuffer;
    aEvent.iFrameLoss = iFramesLost;
    aEvent.iLateFrames = iNumOfLateFrames;
    aEvent.iFramesRemoved = iFramesRemoved;
    aEvent.iFramesPlayed = iFramesPlayed;
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::HandleNotifications
// -----------------------------------------------------------------------------
//
void CMccJitterBufferImpl::HandleNotifications( TBool aBuffering )
    {
    if ( aBuffering )
        {
        if ( BufferState() < EPlaying )
            {
            __JITTER_BUFFER( "CMccJitterBufferImpl::HandleNotifications, to buffering state" )
            
            SetBufferState( EBuffering );
            SendStreamEventToClient( KMccStreamBuffering, ETrue );
            }
        }
    else
        {
        if ( BufferState() >= EBuffering )
            {
            // Do not notify about streaming until we know that speaker
            // is really playing provided data. This cannot be known when
            // first buffer is sent to speaker.
            if ( BufferState() == EDetermining )
                {
                __JITTER_BUFFER( "CMccJitterBufferImpl::HandleNotifications, to playing state" )

                SetBufferState( EPlaying );
                iNotifyUser = ETrue;
                
                // As we are hiding buffer state changes when doing several
                // speaker starting attempts in row:
                // idle->buffering->streaming->idle->noresources->(buffering)->
                // (streaming)->(idle)->(noresources)->(buffering)-> etc.
                // We have to "fake" the buffering event when we are
                // sure that playback is fine.
                if ( KMccStreamBuffering != iLatestNotifiedEvent )
                    {
                    SendStreamEventToClient( KMccStreamBuffering, ETrue );
                    }
                
                SendStreamEventToClient( KMccStreamPlaying, ETrue );
                }
            else
                {
                SetBufferState( EDetermining );
                }
            }
        }
    }
  
// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::CheckThresholdBufferLength
// -----------------------------------------------------------------------------
//  
void CMccJitterBufferImpl::CheckThresholdBufferLength(
    TInt& aBufferLength, TInt aTreshhold ) const
    {
    const TInt numTen = 10;
    if ( ( aBufferLength - aTreshhold ) < numTen )  
        {
        aBufferLength = aTreshhold + numTen;
        }
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::SendStreamEventToClient
// -----------------------------------------------------------------------------
//	
void CMccJitterBufferImpl::SendStreamEventToClient( 
    TMccEventType aEventType,
    TBool aAllEndpoints )
    {
    __JITTER_BUFFER( "CMccJitterBufferImpl::SendStreamEventToClient" )
    
    if ( iEventHandler && iNotifyUser )
	    {
	    const TUint32 endpointId = aAllEndpoints ? 0 : iEndpointId;
	    // Clear old event
	    { 
	    iMccEvent = TMccEvent();
	    }
	    
	    iMccEvent.iEndpointId = endpointId;
	    iMccEvent.iEventCategory = KMccEventCategoryStream;
	    iMccEvent.iEventType = aEventType;

		TMccInternalEvent internalEvent( KMccJitterBufferUid, 
		                                 EMccInternalEventNone,
		                                 iMccEvent );
		                         
		iEventHandler->SendEventToClient( internalEvent );
		iLatestNotifiedEvent = aEventType;
	    }
	else
		{
		__JITTER_BUFFER( "CMccJitterBufferImpl::SendStreamEventToClient, sending ignored" )
		}
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::SetBufferState
// -----------------------------------------------------------------------------
//		
void CMccJitterBufferImpl::SetBufferState( TMccJitterBufferImplState aState )
    {
    iBufferState = aState;
    }

// -----------------------------------------------------------------------------
// CMccJitterBufferImpl::BufferState
// -----------------------------------------------------------------------------
//   
CMccJitterBufferImpl::TMccJitterBufferImplState 
    CMccJitterBufferImpl::BufferState() const
    {
    return iBufferState;
    }
        
// ========================== OTHER EXPORTED FUNCTIONS =========================

//  End of File