jamulus/src/buffer.cpp

353 lines
10 KiB
C++
Raw Normal View History

/******************************************************************************\
* Copyright (c) 2004-2009
*
* Author(s):
* Volker Fischer
2006-01-28 12:29:22 +01:00
*
* Note: we assuming here that put and get operations are secured by a mutex
* and do not take place at the same time
*
******************************************************************************
*
* 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
* version.
*
* 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
* 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.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
#include "buffer.h"
/* Implementation *************************************************************/
2009-08-02 19:44:45 +02:00
void CNetBuf::Init ( const int iNewBlockSize,
const int iNewNumBlocks )
{
2007-12-31 14:09:12 +01:00
// total size -> size of one block times number of blocks
iBlockSize = iNewBlockSize;
iMemSize = iNewBlockSize * iNewNumBlocks;
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// allocate and clear memory for actual data buffer
vecbyMemory.Init ( iMemSize );
2007-12-31 14:09:12 +01:00
// use the "get" flag to make sure the buffer is cleared
2007-09-08 12:45:14 +02:00
Clear ( CT_GET );
// init statistic
ErrorRateStatistic.Init ( MAX_STATISTIC_COUNT );
}
2009-07-31 20:53:40 +02:00
bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
const int iInSize )
{
bool bPutOK = true;
2007-12-31 14:09:12 +01:00
// Check if there is not enough space available -> correct
2007-09-08 12:45:14 +02:00
if ( GetAvailSpace() < iInSize )
{
2007-12-31 14:09:12 +01:00
// not enough space in buffer for put operation, correct buffer to
// prepare for new data
2007-09-08 12:45:14 +02:00
Clear ( CT_PUT );
2007-12-31 14:09:12 +01:00
bPutOK = false; // return error flag
// check for special case: buffer memory is not sufficient
if ( iInSize > iMemSize )
{
// do nothing here, just return error code
return bPutOK;
}
}
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// copy new data in internal buffer
int iCurPos = 0;
2007-09-08 12:45:14 +02:00
if ( iPutPos + iInSize > iMemSize )
{
2007-12-31 14:09:12 +01:00
// remaining space size for second block
const int iRemSpace = iPutPos + iInSize - iMemSize;
2007-12-31 14:09:12 +01:00
// data must be written in two steps because of wrap around
while ( iPutPos < iMemSize )
{
vecbyMemory[iPutPos++] = vecbyData[iCurPos++];
}
2007-09-08 12:45:14 +02:00
for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ )
{
vecbyMemory[iPutPos] = vecbyData[iCurPos++];
}
}
else
{
2007-12-31 14:09:12 +01:00
// data can be written in one step
const int iEnd = iPutPos + iInSize;
2007-09-08 12:45:14 +02:00
while ( iPutPos < iEnd )
{
vecbyMemory[iPutPos++] = vecbyData[iCurPos++];
}
}
2007-12-31 14:09:12 +01:00
// set buffer state flag
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_FULL;
}
else
{
eBufState = CNetBuf::BS_OK;
}
// update statistic
ErrorRateStatistic.Update ( !bPutOK );
return bPutOK;
}
bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
{
bool bGetOK = true; // init return value
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// get size of data to be get from the buffer
const int iInSize = vecbyData.Size();
// check size
2009-08-13 18:12:49 +02:00
if ( ( iInSize == 0 ) || ( iInSize != iBlockSize ) )
{
return false;
}
// check for invalid data in buffer
if ( iNumInvalidElements > 0 )
{
// decrease number of invalid elements by the queried number (input
// size)
iNumInvalidElements -= iInSize;
bGetOK = false; // return error flag
}
2007-12-31 14:09:12 +01:00
// Check if there is not enough data available -> correct
2007-09-08 12:45:14 +02:00
if ( GetAvailData() < iInSize )
{
2007-12-31 14:09:12 +01:00
// not enough data in buffer for get operation, correct buffer to
// prepare for getting data
2007-09-08 12:45:14 +02:00
Clear ( CT_GET );
2007-12-31 14:09:12 +01:00
bGetOK = false; // return error flag
// check for special case: buffer memory is not sufficient
if ( iInSize > iMemSize )
{
// do nothing here, just return error code
return bGetOK;
}
}
2007-12-31 14:09:12 +01:00
// copy data from internal buffer in output buffer
int iCurPos = 0;
2007-09-08 12:45:14 +02:00
if ( iGetPos + iInSize > iMemSize )
{
2007-12-31 14:09:12 +01:00
// remaining data size for second block
const int iRemData = iGetPos + iInSize - iMemSize;
2007-12-31 14:09:12 +01:00
// data must be read in two steps because of wrap around
2007-09-08 12:45:14 +02:00
while ( iGetPos < iMemSize )
{
vecbyData[iCurPos++] = vecbyMemory[iGetPos++];
}
2007-09-08 12:45:14 +02:00
for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ )
{
vecbyData[iCurPos++] = vecbyMemory[iGetPos];
}
}
else
{
2007-12-31 14:09:12 +01:00
// data can be read in one step
const int iEnd = iGetPos + iInSize;
2007-09-08 12:45:14 +02:00
while ( iGetPos < iEnd )
{
vecbyData[iCurPos++] = vecbyMemory[iGetPos++];
}
}
2007-12-31 14:09:12 +01:00
// set buffer state flag
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_EMPTY;
}
else
{
eBufState = CNetBuf::BS_OK;
}
// update statistic
ErrorRateStatistic.Update ( !bGetOK );
return bGetOK;
}
int CNetBuf::GetAvailSpace() const
{
2007-12-31 14:09:12 +01:00
// calculate available space in buffer
int iAvSpace = iGetPos - iPutPos;
2007-12-31 14:09:12 +01:00
// check for special case and wrap around
2007-09-08 12:45:14 +02:00
if ( iAvSpace < 0 )
{
2007-12-31 14:09:12 +01:00
iAvSpace += iMemSize; // wrap around
}
2007-09-08 12:45:14 +02:00
else
{
if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) )
{
iAvSpace = iMemSize;
}
}
return iAvSpace;
}
int CNetBuf::GetAvailData() const
{
2007-12-31 14:09:12 +01:00
// calculate available data in buffer
int iAvData = iPutPos - iGetPos;
2007-12-31 14:09:12 +01:00
// check for special case and wrap around
2007-09-08 12:45:14 +02:00
if ( iAvData < 0 )
{
2007-12-31 14:09:12 +01:00
iAvData += iMemSize; // wrap around
}
2007-09-08 12:45:14 +02:00
else
{
if ( ( iAvData == 0 ) && ( eBufState == BS_FULL ) )
{
iAvData = iMemSize;
}
}
return iAvData;
}
2007-09-08 12:45:14 +02:00
void CNetBuf::Clear ( const EClearType eClearType )
2006-01-28 12:29:22 +01:00
{
// 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;
2006-01-28 12:29:22 +01:00
2009-08-02 19:44:45 +02:00
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
// 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;
}
2009-08-02 19:44:45 +02:00
}
2007-09-08 12:45:14 +02:00
2007-12-31 14:09:12 +01:00
// different behaviour for get and put corrections
2007-09-08 12:45:14 +02:00
if ( eClearType == CT_GET )
{
// clear buffer since we had a buffer underrun
vecbyMemory.Reset ( 0 );
2009-12-31 17:58:48 +01:00
// reset buffer pointers so that they are at maximum distance after
// the get operation (assign new fill level value to the get pointer)
iPutPos = 0;
iGetPos = iNewFillLevel;
// The buffer was cleared, the next time blocks are read from the
// buffer, these are invalid ones. Calculate the number of invalid
// elements
iNumInvalidElements = iMemSize - iNewFillLevel;
// check for special case
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_FULL;
}
else
{
eBufState = CNetBuf::BS_OK;
}
}
else
{
2007-12-31 14:09:12 +01:00
// in case of "put" correction, do not delete old data but only shift
// the pointers
iPutPos = iNewFillLevel;
2007-12-31 14:09:12 +01:00
// adjust put pointer relative to current get pointer, take care of
// wrap around
iPutPos += iGetPos;
2007-09-08 12:45:14 +02:00
if ( iPutPos > iMemSize )
{
iPutPos -= iMemSize;
}
// in case of put correction, no invalid blocks are inserted
iNumInvalidElements = 0;
2007-12-31 14:09:12 +01:00
// check for special case
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_EMPTY;
}
else
{
eBufState = CNetBuf::BS_OK;
}
}
}