--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/multimediacommscontroller/mmccjitterbuffer/src/mccjitterbufferimpl.cpp Tue Feb 02 01:04:58 2010 +0200
@@ -0,0 +1,1034 @@
+/*
+* 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
+