diff --git a/src/buffer.cpp b/src/buffer.cpp index be099fd9..36b64792 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -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& vecbyData, @@ -103,6 +106,9 @@ bool CNetBuf::Put ( const CVector& vecbyData, eBufState = CNetBuf::BS_OK; } + // update statistic + ErrorRateStatistic.Update ( !bPutOK ); + return bPutOK; } @@ -184,6 +190,9 @@ bool CNetBuf::Get ( CVector& 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 ( static_cast ( 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 ( static_cast ( 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 diff --git a/src/buffer.h b/src/buffer.h index de4731cd..18469517 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -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& vecbyData, const int iInSize ); bool Get ( CVector& 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 CConvBuf { public: diff --git a/src/channel.h b/src/channel.h index 5361566d..0d659970 100755 --- a/src/channel.h +++ b/src/channel.h @@ -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 ) diff --git a/src/llconclientdlg.cpp b/src/llconclientdlg.cpp index d2954030..dc3af2d6 100755 --- a/src/llconclientdlg.cpp +++ b/src/llconclientdlg.cpp @@ -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 ) ); } diff --git a/src/util.h b/src/util.h index c7ee3eb7..fc7fe8d1 100755 --- a/src/util.h +++ b/src/util.h @@ -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 ErrorsMovAvBuf; +}; + #endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */