- make jitter calculation closer to RFC

This commit is contained in:
dmytro.bogovych 2019-03-04 19:11:54 +01:00
parent 3cd89fe12c
commit 34d963be40
2 changed files with 236 additions and 195 deletions

View File

@ -9,27 +9,65 @@ using namespace MT;
void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate) void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
{ {
jrtplib::RTPTime receiveTime = packet->GetReceiveTime(); uint32_t arrival = 0;
uint32_t timestamp = packet->GetTimestamp(); int d = 0;
uint32_t transit = 0;
if (!mLastJitter.is_initialized()) uint64_t current_time = TimeHelper::getTimestamp();
{ int interarrival_time_ms = current_time - mPrevRxTimestamp;
mReceiveTime = receiveTime;
mReceiveTimestamp = timestamp;
mLastJitter = 0.0;
}
else
{
double delta = (receiveTime.GetDouble() - mReceiveTime.GetDouble()) - double(timestamp - mReceiveTimestamp) / rate;
if (fabs(delta) > mMaxDelta)
mMaxDelta = fabs(delta);
mLastJitter = mLastJitter.value() + (fabs(delta) - mLastJitter.value()) / 16.0; // get the 'arrival time' of this packet as measured in 'timestamp units' and offset
mReceiveTime = receiveTime; // to match the timestamp range in this stream
mReceiveTimestamp = timestamp; arrival = interarrival_time_ms * (rate / 1000);
mJitter.process(mLastJitter.value()); if (mPrevArrival == 0)
} arrival = packet->GetTimestamp();
else
arrival += mPrevArrival;;
mPrevArrival = packet->GetTimestamp();
transit = arrival - packet->GetTimestamp();
jrtplib::RTPTime receiveTime = packet->GetReceiveTime();
d = transit - mPrevTransit;
mPrevTransit = transit;
if (d < 0)
d = -d;
mJitter += (1.0/16.0) * ((double)d - mJitter);
mPrevRxTimestamp = current_time;
if (mMaxJitter < mJitter)
mMaxJitter = mJitter;
uint32_t timestamp = packet->GetTimestamp();
if (!mLastJitter.is_initialized())
{
mReceiveTime = receiveTime;
mReceiveTimestamp = timestamp;
mLastJitter = 0.0;
}
else
{
double receiveDelta = receiveTime.GetDouble() - mReceiveTime.GetDouble();
double timestampDelta = double(timestamp - mReceiveTimestamp);
if (timestampDelta == 0.0)
// Skip current packet silently. Most probably it is error in RTP stream like duplicated packet.
return;
double delta = receiveDelta / timestampDelta;
if (fabs(delta) > mMaxDelta)
mMaxDelta = fabs(delta);
mLastJitter = mLastJitter.value() + (fabs(delta) - mLastJitter.value()) / 16.0;
mReceiveTime = receiveTime;
mReceiveTimestamp = timestamp;
mJitter.process(mLastJitter.value());
}
} }
@ -37,18 +75,18 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
Statistics::Statistics() Statistics::Statistics()
:mReceived(0), mSent(0), mReceivedRtp(0), mSentRtp(0), :mReceived(0), mSent(0), mReceivedRtp(0), mSentRtp(0),
mReceivedRtcp(0), mSentRtcp(0), mDuplicatedRtp(0), mOldRtp(0), mIllegalRtp(0), mReceivedRtcp(0), mSentRtcp(0), mDuplicatedRtp(0), mOldRtp(0), mIllegalRtp(0),
mPacketLoss(0), mJitter(0.0), mAudioTime(0), mSsrc(0) mPacketLoss(0), mJitter(0.0), mAudioTime(0), mSsrc(0)
{ {
#if defined(USE_AMR_CODEC) #if defined(USE_AMR_CODEC)
mBitrateSwitchCounter = 0; mBitrateSwitchCounter = 0;
#endif #endif
memset(mLoss, 0, sizeof mLoss); memset(mLoss, 0, sizeof mLoss);
// It is to keep track of statistics instance via grep | wc -l // It is to keep track of statistics instance via grep | wc -l
//ICELogDebug(<< "Create statistics instance."); //ICELogDebug(<< "Create statistics instance.");
} }
Statistics::~Statistics() Statistics::~Statistics()
@ -57,19 +95,19 @@ Statistics::~Statistics()
void Statistics::reset() void Statistics::reset()
{ {
mReceived = 0; mReceived = 0;
mSent = 0; mSent = 0;
mReceivedRtp = 0; mReceivedRtp = 0;
mSentRtp = 0; mSentRtp = 0;
mReceivedRtcp = 0; mReceivedRtcp = 0;
mSentRtcp = 0; mSentRtcp = 0;
mDuplicatedRtp = 0; mDuplicatedRtp = 0;
mOldRtp = 0; mOldRtp = 0;
mPacketLoss = 0; mPacketLoss = 0;
mIllegalRtp = 0; mIllegalRtp = 0;
mJitter = 0.0; mJitter = 0.0;
mAudioTime = 0; mAudioTime = 0;
memset(mLoss, 0, sizeof mLoss); memset(mLoss, 0, sizeof mLoss);
} }
/* /*
@ -129,148 +167,148 @@ return calculate_mos_g711(ppl, burstr, 2);
void Statistics::calculateBurstr(double* burstr, double* lossr) const void Statistics::calculateBurstr(double* burstr, double* lossr) const
{ {
int lost = 0; int lost = 0;
int bursts = 0; int bursts = 0;
for (int i = 0; i < 128; i++) for (int i = 0; i < 128; i++)
{ {
lost += i * mLoss[i]; lost += i * mLoss[i];
bursts += mLoss[i]; bursts += mLoss[i];
} }
if (lost < 5) if (lost < 5)
{ {
// ignore such small packet loss // ignore such small packet loss
*lossr = *burstr = 0; *lossr = *burstr = 0;
return; return;
} }
if (mReceivedRtp > 0 && bursts > 0) if (mReceivedRtp > 0 && bursts > 0)
{ {
*burstr = (double)((double)lost / (double)bursts) / (double)(1.0 / (1.0 - (double)lost / (double)mReceivedRtp)); *burstr = (double)((double)lost / (double)bursts) / (double)(1.0 / (1.0 - (double)lost / (double)mReceivedRtp));
if (*burstr < 0) if (*burstr < 0)
*burstr = -*burstr; *burstr = -*burstr;
else if (*burstr < 1) else if (*burstr < 1)
*burstr = 1; *burstr = 1;
} }
else else
*burstr = 0; *burstr = 0;
//printf("total loss: %d\n", lost); //printf("total loss: %d\n", lost);
if (mReceivedRtp > 0) if (mReceivedRtp > 0)
*lossr = (double)((double)lost / (double)mReceivedRtp); *lossr = (double)((double)lost / (double)mReceivedRtp);
else else
*lossr = 0; *lossr = 0;
} }
double Statistics::calculateMos(double maximalMos) const double Statistics::calculateMos(double maximalMos) const
{ {
// calculate lossrate and burst rate // calculate lossrate and burst rate
double burstr, lossr; double burstr, lossr;
calculateBurstr(&burstr, &lossr); calculateBurstr(&burstr, &lossr);
double r; double r;
double bpl = 8.47627; //mos = -4.23836 + 0.29873 * r - 0.00416744 * r * r + 0.0000209855 * r * r * r; double bpl = 8.47627; //mos = -4.23836 + 0.29873 * r - 0.00416744 * r * r + 0.0000209855 * r * r * r;
double mos; double mos;
if (mReceivedRtp < 100) if (mReceivedRtp < 100)
return 0.0; return 0.0;
if (lossr == 0 || burstr == 0) if (lossr == 0 || burstr == 0)
{ {
return maximalMos; return maximalMos;
} }
if (lossr > 0.5) if (lossr > 0.5)
return 1; return 1;
bpl = 17.2647; bpl = 17.2647;
r = 93.2062077233 - 95.0 * (lossr * 100 / (lossr * 100 / burstr + bpl)); r = 93.2062077233 - 95.0 * (lossr * 100 / (lossr * 100 / burstr + bpl));
mos = 2.06405 + 0.031738 * r - 0.000356641 * r * r + 2.93143 * pow(10, -6) * r * r * r; mos = 2.06405 + 0.031738 * r - 0.000356641 * r * r + 2.93143 * pow(10, -6) * r * r * r;
if (mos < 1) if (mos < 1)
return 1; return 1;
if (mos > maximalMos) if (mos > maximalMos)
return maximalMos; return maximalMos;
return mos; return mos;
} }
Statistics& Statistics::operator += (const Statistics& src) Statistics& Statistics::operator += (const Statistics& src)
{ {
mReceived += src.mReceived; mReceived += src.mReceived;
mSent += src.mSent; mSent += src.mSent;
mReceivedRtp += src.mReceivedRtp; mReceivedRtp += src.mReceivedRtp;
mSentRtp += src.mSentRtp; mSentRtp += src.mSentRtp;
mReceivedRtcp += src.mReceivedRtcp; mReceivedRtcp += src.mReceivedRtcp;
mSentRtcp += src.mSentRtcp; mSentRtcp += src.mSentRtcp;
mDuplicatedRtp += src.mDuplicatedRtp; mDuplicatedRtp += src.mDuplicatedRtp;
mOldRtp += src.mOldRtp; mOldRtp += src.mOldRtp;
mPacketLoss += src.mPacketLoss; mPacketLoss += src.mPacketLoss;
mAudioTime += src.mAudioTime; mAudioTime += src.mAudioTime;
for (auto codecStat: src.mCodecCount) for (auto codecStat: src.mCodecCount)
{
if (mCodecCount.find(codecStat.first) == mCodecCount.end())
mCodecCount[codecStat.first] = codecStat.second;
else
mCodecCount[codecStat.first] += codecStat.second;
}
mJitter = src.mJitter;
mRttDelay = src.mRttDelay;
if (!src.mCodecName.empty())
mCodecName = src.mCodecName;
// Find minimal
if (mFirstRtpTime.is_initialized())
{
if (src.mFirstRtpTime.is_initialized())
{ {
if (mFirstRtpTime.value() > src.mFirstRtpTime.value()) if (mCodecCount.find(codecStat.first) == mCodecCount.end())
mFirstRtpTime = src.mFirstRtpTime; mCodecCount[codecStat.first] = codecStat.second;
else
mCodecCount[codecStat.first] += codecStat.second;
} }
}
else mJitter = src.mJitter;
if (src.mFirstRtpTime.is_initialized()) mRttDelay = src.mRttDelay;
mFirstRtpTime = src.mFirstRtpTime; if (!src.mCodecName.empty())
mCodecName = src.mCodecName;
// Find minimal
if (mFirstRtpTime.is_initialized())
{
if (src.mFirstRtpTime.is_initialized())
{
if (mFirstRtpTime.value() > src.mFirstRtpTime.value())
mFirstRtpTime = src.mFirstRtpTime;
}
}
else
if (src.mFirstRtpTime.is_initialized())
mFirstRtpTime = src.mFirstRtpTime;
#if defined(USE_AMR_CODEC) #if defined(USE_AMR_CODEC)
mBitrateSwitchCounter += src.mBitrateSwitchCounter; mBitrateSwitchCounter += src.mBitrateSwitchCounter;
#endif #endif
mRemotePeer = src.mRemotePeer; mRemotePeer = src.mRemotePeer;
mSsrc = src.mSsrc; mSsrc = src.mSsrc;
return *this; return *this;
} }
Statistics& Statistics::operator -= (const Statistics& src) Statistics& Statistics::operator -= (const Statistics& src)
{ {
mReceived -= src.mReceived; mReceived -= src.mReceived;
mSent -= src.mSent; mSent -= src.mSent;
mReceivedRtp -= src.mReceivedRtp; mReceivedRtp -= src.mReceivedRtp;
mIllegalRtp -= src.mIllegalRtp; mIllegalRtp -= src.mIllegalRtp;
mSentRtp -= src.mSentRtp; mSentRtp -= src.mSentRtp;
mReceivedRtcp -= src.mReceivedRtcp; mReceivedRtcp -= src.mReceivedRtcp;
mSentRtcp -= src.mSentRtcp; mSentRtcp -= src.mSentRtcp;
mDuplicatedRtp -= src.mDuplicatedRtp; mDuplicatedRtp -= src.mDuplicatedRtp;
mOldRtp -= src.mOldRtp; mOldRtp -= src.mOldRtp;
mPacketLoss -= src.mPacketLoss; mPacketLoss -= src.mPacketLoss;
mAudioTime -= src.mAudioTime; mAudioTime -= src.mAudioTime;
for (auto codecStat: src.mCodecCount) for (auto codecStat: src.mCodecCount)
{ {
if (mCodecCount.find(codecStat.first) != mCodecCount.end()) if (mCodecCount.find(codecStat.first) != mCodecCount.end())
mCodecCount[codecStat.first] -= codecStat.second; mCodecCount[codecStat.first] -= codecStat.second;
} }
return *this; return *this;
} }
std::string Statistics::toShortString() const std::string Statistics::toShortString() const
{ {
std::ostringstream oss; std::ostringstream oss;
oss << "Received: " << mReceivedRtp oss << "Received: " << mReceivedRtp
<< ", lost: " << mPacketLoss << ", lost: " << mPacketLoss
<< ", sent: " << mSentRtp; << ", sent: " << mSentRtp;
return oss.str(); return oss.str();
} }

View File

@ -15,28 +15,28 @@ using std::experimental::optional;
namespace MT namespace MT
{ {
template<typename T> template<typename T>
struct Average struct Average
{ {
int mCount = 0; int mCount = 0;
T mSum = 0; T mSum = 0;
T getAverage() const T getAverage() const
{ {
if (!mCount) if (!mCount)
return 0; return 0;
return mSum / mCount; return mSum / mCount;
} }
void process(T value) void process(T value)
{ {
mCount++; mCount++;
mSum += value; mSum += value;
} }
}; };
template<typename T, int minimum = 100000, int maximum = 0> template<typename T, int minimum = 100000, int maximum = 0>
struct ProbeStats struct ProbeStats
{ {
T mMin = minimum; T mMin = minimum;
T mMax = maximum; T mMax = maximum;
Average<T> mAverage; Average<T> mAverage;
@ -44,66 +44,69 @@ namespace MT
void process(T value) void process(T value)
{ {
if (mMin > value) if (mMin > value)
mMin = value; mMin = value;
if (mMax < value) if (mMax < value)
mMax = value; mMax = value;
mCurrent = value; mCurrent = value;
mAverage.process(value); mAverage.process(value);
} }
bool isInitialized() const bool isInitialized() const
{ {
return mAverage.mCount > 0; return mAverage.mCount > 0;
} }
T getCurrent() const T getCurrent() const
{ {
if (isInitialized()) if (isInitialized())
return mCurrent; return mCurrent;
else else
return 0; return 0;
} }
}; };
template<typename T> template<typename T>
struct StreamStats struct StreamStats
{ {
T mChunk; T mChunk;
T mTotal; T mTotal;
}; };
class JitterStatistics class JitterStatistics
{ {
public: public:
void process(jrtplib::RTPPacket* packet, int samplerate); void process(jrtplib::RTPPacket* packet, int samplerate);
ProbeStats<double> get() const { return mJitter; } ProbeStats<double> get() const { return mJitter; }
double getMaxDelta() const { return mMaxDelta; } double getMaxDelta() const { return mMaxDelta; }
protected: protected:
// Jitter calculation // Jitter calculation
jrtplib::RTPTime mReceiveTime = jrtplib::RTPTime(0,0); jrtplib::RTPTime mReceiveTime = jrtplib::RTPTime(0,0);
uint32_t mReceiveTimestamp = 0; uint32_t mReceiveTimestamp = 0;
optional<double> mLastJitter; optional<double> mLastJitter;
ProbeStats<double> mJitter; ProbeStats<double> mJitter;
double mMaxDelta = 0.0; double mMaxDelta = 0.0;
}; uint64_t mPrevRxTimestamp = 0;
uint64_t mPrevArrival = 0;
uint64_t mPrevTransit = 0;
};
class Statistics class Statistics
{ {
public: public:
int mReceived, // Received traffic in bytes int mReceived, // Received traffic in bytes
mSent, // Sent traffic in bytes mSent, // Sent traffic in bytes
mReceivedRtp, // Number of received rtp packets mReceivedRtp, // Number of received rtp packets
mSentRtp, // Number of sent rtp packets mSentRtp, // Number of sent rtp packets
mReceivedRtcp, // Number of received rtcp packets mReceivedRtcp, // Number of received rtcp packets
mSentRtcp, // Number of sent rtcp packets mSentRtcp, // Number of sent rtcp packets
mDuplicatedRtp, // Number of received duplicated rtp packets mDuplicatedRtp, // Number of received duplicated rtp packets
mOldRtp, // Number of late rtp packets mOldRtp, // Number of late rtp packets
mPacketLoss, // Number of lost packets mPacketLoss, // Number of lost packets
mIllegalRtp; // Number of rtp packets with bad payload type mIllegalRtp; // Number of rtp packets with bad payload type
int mLoss[128]; // Every item is number of loss of corresping length int mLoss[128]; // Every item is number of loss of corresping length
int mAudioTime; // Decoded/found time in milliseconds int mAudioTime; // Decoded/found time in milliseconds
uint16_t mSsrc; // Last known SSRC ID in a RTP stream uint16_t mSsrc; // Last known SSRC ID in a RTP stream
@ -136,13 +139,13 @@ namespace MT
Statistics& operator -= (const Statistics& src); Statistics& operator -= (const Statistics& src);
float mNetworkMos = 0.0; float mNetworkMos = 0.0;
#if defined(USE_PVQA_LIBRARY) && !defined(PVQA_SERVER) #if defined(USE_PVQA_LIBRARY) && !defined(PVQA_SERVER)
float mPvqaMos = 0.0; float mPvqaMos = 0.0;
std::string mPvqaReport; std::string mPvqaReport;
#endif #endif
std::string toShortString() const; std::string toShortString() const;
}; };
} // end of namespace MT } // end of namespace MT