/******************************************************************************\ * Copyright (c) 2004-2018 * * Author(s): * Volker Fischer * ****************************************************************************** * * 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 * \******************************************************************************/ #if !defined ( BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_ ) #define BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_ #include "util.h" #include "global.h" /* Definitions ****************************************************************/ // each regular buffer access lead to a count for put and get, assuming 2.66 ms // blocks we have 15 s / 2.66 ms * 2 = approx. 11000 #define MAX_STATISTIC_COUNT 11000 // definition of the upper error bound of the jitter buffers #define ERROR_RATE_BOUND 0.001 // definition of the upper jitter buffer error bound, if that one is reached we // have to speed up the filtering to quickly get out of a incorrect buffer // size state #define UP_MAX_ERROR_BOUND 0.01 // number of simulation network jitter buffers for evaluating the statistic #define NUM_STAT_SIMULATION_BUFFERS 10 /* Classes ********************************************************************/ // Buffer base class ----------------------------------------------------------- template class CBufferBase { public: CBufferBase ( const bool bNIsSim = false ) : bIsSimulation ( bNIsSim ), bIsInitialized ( false ) {} void SetIsSimulation ( const bool bNIsSim ) { bIsSimulation = bNIsSim; } void Init ( const int iNewMemSize, const bool bPreserve = false ) { // in simulation mode the size is not changed during operation -> we do // not have to implement special code for this case // only enter the "preserve" branch, if object was already initialized if ( bPreserve && ( !bIsSimulation ) && bIsInitialized ) { // copy old data in new vector using get pointer as zero per // definition int iCurPos; // copy current data in temporary vector CVector vecTempMemory ( vecMemory ); // resize actual buffer memory vecMemory.Init ( iNewMemSize ); // get maximum number of data to be copied int iCopyLen = GetAvailData(); if ( iCopyLen > iNewMemSize ) { iCopyLen = iNewMemSize; } // set correct buffer state if ( iCopyLen == iNewMemSize ) { eBufState = CBufferBase::BS_FULL; } else { if ( iCopyLen == 0 ) { eBufState = CBufferBase::BS_EMPTY; } else { eBufState = CBufferBase::BS_OK; } } if ( iGetPos < iPutPos ) { // "get" position is before "put" position -> no wrap around for ( iCurPos = 0; iCurPos < iCopyLen; iCurPos++ ) { vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos]; } } else { // "put" position is before "get" position -> wrap around bool bEnoughSpaceForSecondPart = true; int iFirstPartLen = iMemSize - iGetPos; // check that first copy length is not larger then new memory if ( iFirstPartLen >= iCopyLen ) { iFirstPartLen = iCopyLen; bEnoughSpaceForSecondPart = false; } for ( iCurPos = 0; iCurPos < iFirstPartLen; iCurPos++ ) { vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos]; } if ( bEnoughSpaceForSecondPart ) { // calculate remaining copy length const int iRemainingCopyLen = iCopyLen - iFirstPartLen; // perform copying of second part for ( iCurPos = 0; iCurPos < iRemainingCopyLen; iCurPos++ ) { vecMemory[iCurPos + iFirstPartLen] = vecTempMemory[iCurPos]; } } } // update put pointer if ( eBufState == CBufferBase::BS_FULL ) { iPutPos = 0; } else { iPutPos = iCopyLen; } // update get position -> zero per definition iGetPos = 0; } else { // allocate memory for actual data buffer if ( !bIsSimulation ) { vecMemory.Init ( iNewMemSize ); } // init buffer pointers and buffer state (empty buffer) iGetPos = 0; iPutPos = 0; eBufState = CBufferBase::BS_EMPTY; } // store total memory size value iMemSize = iNewMemSize; // set initialized flag bIsInitialized = true; } virtual bool Put ( const CVector& vecData, const int iInSize ) { if ( bIsSimulation ) { // in this simulation only the buffer pointers and the buffer state // is updated, no actual data is transferred iPutPos += iInSize; if ( iPutPos >= iMemSize ) { iPutPos -= iMemSize; } } else { // copy new data in internal buffer int iCurPos = 0; if ( iPutPos + iInSize > iMemSize ) { // remaining space size for second block const int iRemSpace = iPutPos + iInSize - iMemSize; // data must be written in two steps because of wrap around while ( iPutPos < iMemSize ) { vecMemory[iPutPos++] = vecData[iCurPos++]; } for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ ) { vecMemory[iPutPos] = vecData[iCurPos++]; } } else { // data can be written in one step std::copy ( vecData.begin(), vecData.begin() + iInSize, vecMemory.begin() + iPutPos ); // set the put position one block further (no wrap around needs // to be considered here) iPutPos += iInSize; } } // take care about wrap around of put pointer if ( iPutPos == iMemSize ) { iPutPos = 0; } // set buffer state flag if ( iPutPos == iGetPos ) { eBufState = CBufferBase::BS_FULL; } else { eBufState = CBufferBase::BS_OK; } return true; // no error check in base class, alyways return ok } virtual bool Get ( CVector& vecData, const int iOutSize ) { if ( bIsSimulation ) { // in this simulation only the buffer pointers and the buffer state // is updated, no actual data is transferred iGetPos += iOutSize; if ( iGetPos >= iMemSize ) { iGetPos -= iMemSize; } } else { // copy data from internal buffer in output buffer int iCurPos = 0; if ( iGetPos + iOutSize > iMemSize ) { // remaining data size for second block const int iRemData = iGetPos + iOutSize - iMemSize; // data must be read in two steps because of wrap around while ( iGetPos < iMemSize ) { vecData[iCurPos++] = vecMemory[iGetPos++]; } for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ ) { vecData[iCurPos++] = vecMemory[iGetPos]; } } else { // data can be read in one step std::copy ( vecMemory.begin() + iGetPos, vecMemory.begin() + iGetPos + iOutSize, vecData.begin() ); // set the get position one block further (no wrap around needs // to be considered here) iGetPos += iOutSize; } } // take care about wrap around of get pointer if ( iGetPos == iMemSize ) { iGetPos = 0; } // set buffer state flag if ( iPutPos == iGetPos ) { eBufState = CBufferBase::BS_EMPTY; } else { eBufState = CBufferBase::BS_OK; } return true; // no error check in base class, alyways return ok } virtual int GetAvailSpace() const { // calculate available space in buffer int iAvSpace = iGetPos - iPutPos; // check for special case and wrap around if ( iAvSpace < 0 ) { iAvSpace += iMemSize; // wrap around } else { if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) ) { iAvSpace = iMemSize; } } return iAvSpace; } virtual int GetAvailData() const { // calculate available data in buffer int iAvData = iPutPos - iGetPos; // check for special case and wrap around if ( iAvData < 0 ) { iAvData += iMemSize; // wrap around } else { if ( ( iAvData == 0 ) && ( eBufState == BS_FULL ) ) { iAvData = iMemSize; } } return iAvData; } protected: enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; virtual void Clear() { // clear memory if ( !bIsSimulation ) { vecMemory.Reset ( 0 ); } // init buffer pointers and buffer state (empty buffer) iGetPos = 0; iPutPos = 0; eBufState = CBufferBase::BS_EMPTY; } CVector vecMemory; int iMemSize; int iGetPos; int iPutPos; EBufState eBufState; bool bIsSimulation; bool bIsInitialized; }; // Network buffer (jitter buffer) ---------------------------------------------- class CNetBuf : public CBufferBase { public: CNetBuf ( const bool bNewIsSim = false ) : CBufferBase ( bNewIsSim ) {} void Init ( const int iNewBlockSize, const int iNewNumBlocks, const bool bPreserve = false ); int GetSize() { return iMemSize / iBlockSize; } virtual bool Put ( const CVector& vecbyData, const int iInSize ); virtual bool Get ( CVector& vecbyData, const int iOutSize ); protected: int iBlockSize; }; // Network buffer (jitter buffer) with statistic calculations ------------------ class CNetBufWithStats : public CNetBuf { public: CNetBufWithStats(); void Init ( const int iNewBlockSize, const int iNewNumBlocks, const bool bPreserve = false ); virtual bool Put ( const CVector& vecbyData, const int iInSize ); virtual bool Get ( CVector& vecbyData, const int iOutSize ); int GetAutoSetting() { return iCurAutoBufferSizeSetting; } void GetErrorRates ( CVector& vecErrRates, double& dLimit, double& dMaxUpLimit ); protected: void UpdateAutoSetting(); void ResetInitCounter(); // statistic (do not use the vector class since the classes do not have // appropriate copy constructor/operator) CErrorRate ErrorRateStatistic[NUM_STAT_SIMULATION_BUFFERS]; CNetBuf SimulationBuffer[NUM_STAT_SIMULATION_BUFFERS]; int viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS]; double dCurIIRFilterResult; int iCurDecidedResult; int iInitCounter; int iCurAutoBufferSizeSetting; }; // Conversion buffer (very simple buffer) -------------------------------------- // For this very simple buffer no wrap around mechanism is implemented. We // assume here, that the applied buffers are an integer fraction of the total // buffer size. template class CConvBuf { public: CConvBuf() { Init ( 0 ); } void Init ( const int iNewMemSize ) { // set memory size iMemSize = iNewMemSize; // allocate and clear memory for actual data buffer vecsMemory.Init ( iMemSize ); iPutPos = 0; } int GetSize() const { return iMemSize; } bool Put ( const CVector& vecsData, const int iVecSize ) { // calculate the input size and the end position after copying const int iEnd = iPutPos + iVecSize; // first check for buffer overrun if ( iEnd <= iMemSize ) { // copy new data in internal buffer std::copy ( vecsData.begin(), vecsData.begin() + iVecSize, vecsMemory.begin() + iPutPos ); // set buffer pointer one block further iPutPos = iEnd; // return "buffer is ready for readout" flag return ( iEnd == iMemSize ); } // buffer overrun or not initialized, return "not ready" return false; } const CVector& Get() { iPutPos = 0; return vecsMemory; } protected: CVector vecsMemory; int iMemSize; int iPutPos; }; #endif /* !defined ( BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_ ) */