improved jitter buffer correction algorithm

This commit is contained in:
Volker Fischer 2010-01-03 13:29:42 +00:00
parent 52fc35357e
commit ca945124cf
5 changed files with 121 additions and 41 deletions

View file

@ -41,6 +41,9 @@ void CNetBuf::Init ( const int iNewBlockSize,
// use the "get" flag to make sure the buffer is cleared // use the "get" flag to make sure the buffer is cleared
Clear ( CT_GET ); Clear ( CT_GET );
// init statistic
ErrorRateStatistic.Init ( MAX_STATISTIC_COUNT );
} }
bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData, bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
@ -103,6 +106,9 @@ bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
eBufState = CNetBuf::BS_OK; eBufState = CNetBuf::BS_OK;
} }
// update statistic
ErrorRateStatistic.Update ( !bPutOK );
return bPutOK; return bPutOK;
} }
@ -184,6 +190,9 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
eBufState = CNetBuf::BS_OK; eBufState = CNetBuf::BS_OK;
} }
// update statistic
ErrorRateStatistic.Update ( !bGetOK );
return bGetOK; return bGetOK;
} }
@ -231,14 +240,52 @@ int CNetBuf::GetAvailData() const
void CNetBuf::Clear ( const EClearType eClearType ) void CNetBuf::Clear ( const EClearType eClearType )
{ {
// TEST defines // Define the number of blocks bound for the "random offset" (1) algorithm.
const bool bUseRandomInit = false; // If we are above the bound, we use the "middle of buffer" (2) algorithm.
const int iNumBlocksBoundForRandom = 6; // Test results (with different jitter buffer sizes), given is the error
// probability of jitter buffer (probability of corrections in the buffer):
// kX, 128 samples, WLAN:
// 2: (1) 5 %, (2) 12.3 %
// 3: (1) 18.3 %, (2) 17.1 %
// 5: (1) 0.9 %, (2) 0.8 %
// kX, 128 samples, localhost:
// 2: (1) 2.5 %, (2) 13 %
// 3: (1) 0.9 %, (2) 1.1 %
// 5: (1) 0.7 %, (2) 0.6 %
// Behringer, 128 samples, WLAN:
// 2: (1) 5.8 %, (2) 9.4 %
// 3: (1) 0.9 %, (2) 0.8 %
// 5: (1) 0.4 %, (2) 0.3 %
// Behringer, 128 samples, localhost:
// 2: (1) 1 %, (2) 9.8 %
// 3: (1) 0.57 %, (2) 0.6 %
// 5: (1) 0.6 %, (2) 0.56 %
// kX, 256 samples, WLAN:
// 3: (1) 24.2 %, (2) 18.4 %
// 4: (1) 1.5 %, (2) 2.5 %
// 5: (1) 1 %, (2) 1 %
// ASIO4All, 256 samples, WLAN:
// 3: (1) 14.9 %, (2) 11.9 %
// 4: (1) 1.5 %, (2) 7 %
// 5: (1) 1.2 %, (2) 1.3 %
const int iNumBlocksBoundInclForRandom = 4; // by extensive testing: 4
int iNewFillLevel = 0; int iNewFillLevel = 0;
if ( iBlockSize != 0 ) if ( iBlockSize != 0 )
{ {
const int iNumBlocks = iMemSize / iBlockSize;
if ( iNumBlocks <= iNumBlocksBoundInclForRandom ) // just for small buffers
{
// random position algorithm
// overwrite fill level with random value, the range
// is 0 to (iMemSize - iBlockSize)
iNewFillLevel = static_cast<int> ( static_cast<double> ( rand() ) *
iNumBlocks / RAND_MAX ) * iBlockSize;
}
else
{
// middle of buffer algorithm
// with the following operation we set the fill level to a block // with the following operation we set the fill level to a block
// boundary (one block below the middle of the buffer in case of odd // boundary (one block below the middle of the buffer in case of odd
// number of blocks, e.g.: // number of blocks, e.g.:
@ -246,28 +293,9 @@ const int iNumBlocksBoundForRandom = 6;
// 1: 0 / 2: 0 / 3: 1 / 4: 1 / 5: 2 ...) // 1: 0 / 2: 0 / 3: 1 / 4: 1 / 5: 2 ...)
iNewFillLevel = iNewFillLevel =
( ( ( iMemSize - iBlockSize) / 2 ) / iBlockSize ) * iBlockSize; ( ( ( iMemSize - iBlockSize) / 2 ) / iBlockSize ) * iBlockSize;
// TEST random init position
if ( bUseRandomInit )
{
const int iNumBlocks = iMemSize / iBlockSize;
if ( iNumBlocks < iNumBlocksBoundForRandom ) // just for very small buffers
{
// overwrite fill level with random value, the range
// is 0 to (iMemSize - iBlockSize)
iNewFillLevel = static_cast<int> ( static_cast<double> ( rand() ) *
iNumBlocks / RAND_MAX ) * iBlockSize;
} }
} }
/*
// TEST
static FILE* pFile = fopen ( "test.dat", "w" );
fprintf ( pFile, "%d %d %d\n", iMemSize, iBlockSize, iNewFillLevel );
fflush ( pFile );
*/
}
// different behaviour for get and put corrections // different behaviour for get and put corrections
if ( eClearType == CT_GET ) if ( eClearType == CT_GET )
{ {

View file

@ -29,7 +29,14 @@
#include "global.h" #include "global.h"
/* Definitions ****************************************************************/
// each regular buffer access lead to a count for put and get, assuming 2.33 ms
// blocks we have 30 s / 2.33 ms * 2 = 25714
#define MAX_STATISTIC_COUNT 25714
/* Classes ********************************************************************/ /* Classes ********************************************************************/
// Network buffer (jitter buffer) ----------------------------------------------
class CNetBuf class CNetBuf
{ {
public: public:
@ -42,6 +49,8 @@ public:
bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize ); bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize );
bool Get ( CVector<uint8_t>& vecbyData ); bool Get ( CVector<uint8_t>& vecbyData );
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); }
protected: protected:
enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
enum EClearType { CT_PUT, CT_GET }; enum EClearType { CT_PUT, CT_GET };
@ -55,10 +64,13 @@ protected:
int iNumInvalidElements; int iNumInvalidElements;
int iGetPos, iPutPos; int iGetPos, iPutPos;
EBufState eBufState; EBufState eBufState;
// statistic
CErrorRate ErrorRateStatistic;
}; };
// conversion buffer (very simple buffer) // Conversion buffer (very simple buffer) --------------------------------------
template<class TData> class CConvBuf template<class TData> class CConvBuf
{ {
public: public:

View file

@ -104,6 +104,7 @@ public:
int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; } int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; }
int GetNetwFrameSize() const { return iNetwFrameSize; } int GetNetwFrameSize() const { return iNetwFrameSize; }
double GetJitterBufferErrorRate() { return SockBuf.GetErrorRate(); }
// network protocol interface // network protocol interface
void CreateJitBufMes ( const int iJitBufSize ) void CreateJitBufMes ( const int iJitBufSize )

View file

@ -704,6 +704,7 @@ void CLlconClientDlg::UpdateDisplay()
// TEST // TEST
//TextLabelStatus->setText ( QString( "Time: %1, Netw: %2" ).arg ( pClient->GetTimingStdDev() ).arg ( pClient->GetChannel()->GetTimingStdDev() ) ); //TextLabelStatus->setText ( QString( "Time: %1, Netw: %2" ).arg ( pClient->GetTimingStdDev() ).arg ( pClient->GetChannel()->GetTimingStdDev() ) );
//TextLabelStatus->setText ( QString( "Buf. Err. Rate: %1 %" ).arg ( pClient->GetChannel()->GetJitterBufferErrorRate() * 100.0 ) );
} }

View file

@ -562,15 +562,15 @@ public:
/******************************************************************************\ /******************************************************************************\
* Cycle Time Variance Measurement * * Statistics *
\******************************************************************************/ \******************************************************************************/
// Cycle time variance measurement ---------------------------------------------
// use for, e.g., measuring the variance of a timer // use for, e.g., measuring the variance of a timer
class CCycleTimeVariance class CCycleTimeVariance
{ {
public: public:
CCycleTimeVariance() : iBlockLengthAtSystemSampleRate ( 0 ), CCycleTimeVariance() : iBlockLengthAtSystemSampleRate ( 0 ),
dIntervalTime ( 0.0 ), iNewValueBoundFactor ( 0 ) {} dIntervalTime ( 0.0 ), iNewValueBoundFactor ( 0 ) {}
virtual ~CCycleTimeVariance() {}
void Init ( const int iNewBlockLengthAtSystemSampleRate, void Init ( const int iNewBlockLengthAtSystemSampleRate,
const int iNewSystemSampleRate, const int iNewSystemSampleRate,
@ -644,4 +644,42 @@ protected:
int iNewValueBoundFactor; int iNewValueBoundFactor;
}; };
// Error rate measurement ------------------------------------------------------
class CErrorRate
{
public:
CErrorRate() {}
void Init ( const int iHistoryLength )
{
// initialize buffer
ErrorsMovAvBuf.Init ( iHistoryLength );
}
void Reset()
{
ErrorsMovAvBuf.Reset();
}
void Update ( const bool bState )
{
// add errors as values 0 and 1 to get correct error rate average
if ( bState )
{
ErrorsMovAvBuf.Add ( 1.0 );
}
else
{
ErrorsMovAvBuf.Add ( 0.0 );
}
}
// return the standard deviation, for that we need to calculate
// the sqaure root
double GetAverage() { return ErrorsMovAvBuf.GetAverage(); }
protected:
CMovingAv<double> ErrorsMovAvBuf;
};
#endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */ #endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */