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

@ -11,16 +11,16 @@
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
@ -41,6 +41,9 @@ void CNetBuf::Init ( const int iNewBlockSize,
// use the "get" flag to make sure the buffer is cleared
Clear ( CT_GET );
// init statistic
ErrorRateStatistic.Init ( MAX_STATISTIC_COUNT );
}
bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
@ -103,6 +106,9 @@ bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
eBufState = CNetBuf::BS_OK;
}
// update statistic
ErrorRateStatistic.Update ( !bPutOK );
return bPutOK;
}
@ -184,6 +190,9 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
eBufState = CNetBuf::BS_OK;
}
// update statistic
ErrorRateStatistic.Update ( !bGetOK );
return bGetOK;
}
@ -231,41 +240,60 @@ int CNetBuf::GetAvailData() const
void CNetBuf::Clear ( const EClearType eClearType )
{
// TEST defines
const bool bUseRandomInit = false;
const int iNumBlocksBoundForRandom = 6;
// Define the number of blocks bound for the "random offset" (1) algorithm.
// If we are above the bound, we use the "middle of buffer" (2) algorithm.
// 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;
if ( iBlockSize != 0 )
{
// 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
// number of blocks, e.g.:
// [buffer size]: [get pos]
// 1: 0 / 2: 0 / 3: 1 / 4: 1 / 5: 2 ...)
iNewFillLevel =
( ( ( 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 );
*/
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
// boundary (one block below the middle of the buffer in case of odd
// number of blocks, e.g.:
// [buffer size]: [get pos]
// 1: 0 / 2: 0 / 3: 1 / 4: 1 / 5: 2 ...)
iNewFillLevel =
( ( ( iMemSize - iBlockSize) / 2 ) / iBlockSize ) * iBlockSize;
}
}
// different behaviour for get and put corrections

View file

@ -8,16 +8,16 @@
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
@ -29,7 +29,14 @@
#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 ********************************************************************/
// Network buffer (jitter buffer) ----------------------------------------------
class CNetBuf
{
public:
@ -42,6 +49,8 @@ public:
bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize );
bool Get ( CVector<uint8_t>& vecbyData );
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); }
protected:
enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
enum EClearType { CT_PUT, CT_GET };
@ -55,10 +64,13 @@ protected:
int iNumInvalidElements;
int iGetPos, iPutPos;
EBufState eBufState;
// statistic
CErrorRate ErrorRateStatistic;
};
// conversion buffer (very simple buffer)
// Conversion buffer (very simple buffer) --------------------------------------
template<class TData> class CConvBuf
{
public:

View file

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

View file

@ -704,6 +704,7 @@ void CLlconClientDlg::UpdateDisplay()
// TEST
//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
class CCycleTimeVariance
{
public:
CCycleTimeVariance() : iBlockLengthAtSystemSampleRate ( 0 ),
dIntervalTime ( 0.0 ), iNewValueBoundFactor ( 0 ) {}
virtual ~CCycleTimeVariance() {}
void Init ( const int iNewBlockLengthAtSystemSampleRate,
const int iNewSystemSampleRate,
@ -644,4 +644,42 @@ protected:
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_ ) */