diff -r 000000000000 -r 1bce908db942 multimediacommscontroller/mmccjitterbuffer/src/mccjitterbufferimpl.cpp --- /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::CompareStamp ) + ), + iBufSequenceSorter( + TLinearOrder( 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( 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( *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 +