- improve packet loss reporting + initial DTMF RFC 2833 event reporting
This commit is contained in:
@@ -173,6 +173,11 @@ int RtpHelper::findPayloadLength(const void* buffer, size_t length)
|
|||||||
return static_cast<int>(payloadLen);
|
return static_cast<int>(payloadLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t)
|
||||||
|
{
|
||||||
|
return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000));
|
||||||
|
}
|
||||||
|
|
||||||
// --- RtpDump implementation ---
|
// --- RtpDump implementation ---
|
||||||
|
|
||||||
std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, size_t len)
|
std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, size_t len)
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
static unsigned findSsrc(const void* buffer, size_t length);
|
static unsigned findSsrc(const void* buffer, size_t length);
|
||||||
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
||||||
static int findPayloadLength(const void* buffer, size_t length);
|
static int findPayloadLength(const void* buffer, size_t length);
|
||||||
|
|
||||||
|
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter)
|
|||||||
// if (input.mWideband && f.mMode == 0xFF /* CNG */)
|
// if (input.mWideband && f.mMode == 0xFF /* CNG */)
|
||||||
// {
|
// {
|
||||||
// int a = 1;
|
// int a = 1;
|
||||||
// }
|
// }`
|
||||||
|
|
||||||
if (input.mWideband && f.mFrameType == 15)
|
if (input.mWideband && f.mFrameType == 15)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "MT_AudioReceiver.h"
|
#include "MT_AudioReceiver.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
|
#include "MT_Dtmf.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_Time.h"
|
#include "../helper/HL_Time.h"
|
||||||
#include "../audio/Audio_Interface.h"
|
#include "../audio/Audio_Interface.h"
|
||||||
@@ -207,7 +208,8 @@ RtpBuffer::FetchResult RtpBuffer::fetch()
|
|||||||
|
|
||||||
// Save it as last packet however - to not confuse loss packet counter
|
// Save it as last packet however - to not confuse loss packet counter
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
mLastSeqno = mFetchedPacket->rtp()->GetExtendedSequenceNumber();
|
||||||
|
mLastReceiveTime = mFetchedPacket->rtp()->GetReceiveTime();
|
||||||
|
|
||||||
// Erase from packet list
|
// Erase from packet list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
@@ -238,20 +240,27 @@ RtpBuffer::FetchResult RtpBuffer::fetch()
|
|||||||
|
|
||||||
// Gap between new packet and previous on
|
// Gap between new packet and previous on
|
||||||
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
|
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
|
||||||
// gap = std::min(gap, 127);
|
|
||||||
if (gap > 0)
|
if (gap > 0)
|
||||||
{
|
{
|
||||||
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
|
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
|
||||||
mStat.mPacketLoss += gap;
|
mStat.mPacketLoss += gap;
|
||||||
auto currentTimestamp = std::chrono::microseconds(uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000));
|
|
||||||
|
|
||||||
|
// Report is the onetime; there is no many sequential 1-packet gap reports
|
||||||
if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno))
|
if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno))
|
||||||
mStat.mPacketLossTimeline.push_back({.mStartSeqno = *mLastSeqno,
|
{
|
||||||
.mEndSeqno = seqno,
|
auto gapStart = RtpHelper::toMicroseconds(*mLastReceiveTime);
|
||||||
.mGap = gap,
|
auto gapEnd = RtpHelper::toMicroseconds(packet.rtp()->GetReceiveTime());
|
||||||
.mTimestamp = currentTimestamp});
|
mStat.mPacketLossTimeline.emplace_back(PacketLossEvent{.mStartSeqno = *mLastSeqno,
|
||||||
|
.mEndSeqno = seqno,
|
||||||
|
.mGap = gap,
|
||||||
|
.mTimestampStart = gapStart,
|
||||||
|
.mTimestampEnd = gapEnd});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: here we should decide smth - 2-packet gap shoud report Status::Gap two times at least; but current implementation gives only one.
|
||||||
|
// It is not big problem - as gap is detected when we have smth to return usually
|
||||||
mLastSeqno = seqno;
|
mLastSeqno = seqno;
|
||||||
|
mLastReceiveTime = packet.rtp()->GetReceiveTime();
|
||||||
result = {FetchResult::Status::Gap};
|
result = {FetchResult::Status::Gap};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -261,6 +270,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch()
|
|||||||
// Save last returned normal packet
|
// Save last returned normal packet
|
||||||
mFetchedPacket = result.mPacket;
|
mFetchedPacket = result.mPacket;
|
||||||
mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
|
mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
|
||||||
|
mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime();
|
||||||
|
|
||||||
// Remove returned packet from the list
|
// Remove returned packet from the list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
@@ -278,6 +288,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch()
|
|||||||
// Remember returned packet
|
// Remember returned packet
|
||||||
mFetchedPacket = result.mPacket;
|
mFetchedPacket = result.mPacket;
|
||||||
mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
|
mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
|
||||||
|
mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime();
|
||||||
|
|
||||||
// Remove returned packet from buffer list
|
// Remove returned packet from buffer list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
@@ -955,5 +966,25 @@ DtmfReceiver::DtmfReceiver(Statistics& stat)
|
|||||||
DtmfReceiver::~DtmfReceiver()
|
DtmfReceiver::~DtmfReceiver()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void DtmfReceiver::add(std::shared_ptr<RTPPacket> /*p*/)
|
void DtmfReceiver::add(const std::shared_ptr<RTPPacket>& p)
|
||||||
{}
|
{
|
||||||
|
// This receiver always work in context of single RTP stream; so there is no need to put SSRC map and so on
|
||||||
|
if (p->GetPayloadType() != 101)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()});
|
||||||
|
if (ev.mTone != mEvent || ev.mEnd != mEventEnded)
|
||||||
|
{
|
||||||
|
// New tone is here
|
||||||
|
if (mCallback)
|
||||||
|
mCallback(ev.mTone);
|
||||||
|
|
||||||
|
// Queue statistics item
|
||||||
|
mStat.mDtmf2833Timeline.emplace_back(Dtmf2833Event{.mTone = ev.mTone,
|
||||||
|
.mTimestamp = RtpHelper::toMicroseconds(p->GetReceiveTime())});
|
||||||
|
|
||||||
|
// Store to avoid triggering on the packet
|
||||||
|
mEvent = ev.mTone;
|
||||||
|
mEventEnded = ev.mEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ protected:
|
|||||||
jrtplib::RTPSourceStats mRtpStats;
|
jrtplib::RTPSourceStats mRtpStats;
|
||||||
std::shared_ptr<Packet> mFetchedPacket;
|
std::shared_ptr<Packet> mFetchedPacket;
|
||||||
std::optional<uint32_t> mLastSeqno;
|
std::optional<uint32_t> mLastSeqno;
|
||||||
|
std::optional<jrtplib::RTPTime> mLastReceiveTime;
|
||||||
|
|
||||||
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
||||||
float mLastAddTime = 0.0f;
|
float mLastAddTime = 0.0f;
|
||||||
@@ -248,11 +249,18 @@ protected:
|
|||||||
|
|
||||||
class DtmfReceiver: public Receiver
|
class DtmfReceiver: public Receiver
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
char mEvent = 0;
|
||||||
|
bool mEventEnded = false;
|
||||||
|
std::chrono::milliseconds mEventStart = 0ms;
|
||||||
|
std::function<void(char)> mCallback;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DtmfReceiver(Statistics& stat);
|
DtmfReceiver(Statistics& stat);
|
||||||
~DtmfReceiver();
|
~DtmfReceiver();
|
||||||
|
|
||||||
void add(std::shared_ptr<RTPPacket> p);
|
void add(const std::shared_ptr<RTPPacket>& p);
|
||||||
|
void setCallback(std::function<void(char tone)> callback);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,38 +16,68 @@
|
|||||||
|
|
||||||
using namespace MT;
|
using namespace MT;
|
||||||
|
|
||||||
void DtmfBuilder::buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output)
|
void DtmfBuilder::buildRfc2833(const Rfc2833Event& ev, void* output)
|
||||||
{
|
{
|
||||||
assert(duration);
|
assert(ev.mDuration != 0);
|
||||||
assert(output);
|
assert(output);
|
||||||
assert(tone);
|
assert(ev.mTone != 0);
|
||||||
|
|
||||||
unsigned char toneValue = 0;
|
unsigned char toneValue = 0;
|
||||||
if (tone >= '0' && tone <='9')
|
if (ev.mTone >= '0' && ev.mTone <='9')
|
||||||
toneValue = tone - '0';
|
toneValue = ev.mTone - '0';
|
||||||
else
|
else
|
||||||
if (tone >= 'A' && tone <='D' )
|
if (ev.mTone >= 'A' && ev.mTone <='D' )
|
||||||
toneValue = tone - 'A' + 12;
|
toneValue = ev.mTone - 'A' + 12;
|
||||||
else
|
else
|
||||||
if (tone == '*')
|
if (ev.mTone == '*')
|
||||||
toneValue = 10;
|
toneValue = 10;
|
||||||
else
|
else
|
||||||
if (tone == '#')
|
if (ev.mTone == '#')
|
||||||
toneValue = 11;
|
toneValue = 11;
|
||||||
|
|
||||||
char* packet = (char*)output;
|
char* packet = (char*)output;
|
||||||
|
|
||||||
packet[0] = toneValue;
|
packet[0] = toneValue;
|
||||||
packet[1] = 1 | (volume << 2);
|
packet[1] = 1 | (ev.mVolume << 2);
|
||||||
if (endOfEvent)
|
if (ev.mEnd)
|
||||||
packet[1] |= 128;
|
packet[1] |= 128;
|
||||||
else
|
else
|
||||||
packet[1] &= 127;
|
packet[1] &= 127;
|
||||||
|
|
||||||
unsigned short durationValue = htons(duration);
|
unsigned short durationValue = htons(ev.mDuration);
|
||||||
memcpy(packet + 2, &durationValue, 2);
|
memcpy(packet + 2, &durationValue, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DtmfBuilder::Rfc2833Event DtmfBuilder::parseRfc2833(std::span<uint8_t> payload)
|
||||||
|
{
|
||||||
|
Rfc2833Event r;
|
||||||
|
if (payload.size_bytes() < 4)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
uint8_t b0 = payload[0];
|
||||||
|
uint8_t b1 = payload[1];
|
||||||
|
|
||||||
|
if (b0 >=0 && b0 <= 9)
|
||||||
|
r.mTone = '0' + b0;
|
||||||
|
else
|
||||||
|
if (b0 >= 12 && b0 <= 17)
|
||||||
|
r.mTone = 'A' + b0;
|
||||||
|
else
|
||||||
|
if (b0 == 10)
|
||||||
|
r.mTone = '*';
|
||||||
|
else
|
||||||
|
if (b0 == 11)
|
||||||
|
r.mTone = '#';
|
||||||
|
|
||||||
|
r.mEnd = (b1 & 128);
|
||||||
|
r.mVolume = (b1 & 127) >> 2;
|
||||||
|
r.mDuration = ntohs(*(uint16_t*)payload.data()+2);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma region Inband DTMF support
|
#pragma region Inband DTMF support
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#ifndef TARGET_WIN
|
#ifndef TARGET_WIN
|
||||||
@@ -302,24 +332,24 @@ bool DtmfContext::getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& s
|
|||||||
{
|
{
|
||||||
// Emit rfc2833 packet
|
// Emit rfc2833 packet
|
||||||
output.resize(4);
|
output.resize(4);
|
||||||
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData());
|
DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData());
|
||||||
d.mDuration -= milliseconds;
|
d.mDuration -= milliseconds;
|
||||||
if(d.mDuration <= 0)
|
if(d.mDuration <= 0)
|
||||||
d.mStopped = true;
|
d.mStopped = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (!d.mStopped)
|
if (!d.mStopped)
|
||||||
{
|
{
|
||||||
output.resize(4);
|
output.resize(4);
|
||||||
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData());
|
DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
output.clear();
|
output.clear();
|
||||||
|
|
||||||
if (d.mStopped)
|
if (d.mStopped)
|
||||||
{
|
{
|
||||||
stopPacket.resize(4);
|
stopPacket.resize(4);
|
||||||
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, true, stopPacket.mutableData());
|
DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = true}, stopPacket.mutableData());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stopPacket.clear();
|
stopPacket.clear();
|
||||||
@@ -375,7 +405,7 @@ void zap_dtmf_detect_init(dtmf_detect_state_t *s);
|
|||||||
int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int isradio);
|
int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int isradio);
|
||||||
int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max);
|
int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max);
|
||||||
|
|
||||||
DTMFDetector::DTMFDetector()
|
InbandDtmfDetector::InbandDtmfDetector()
|
||||||
:mState(NULL)
|
:mState(NULL)
|
||||||
{
|
{
|
||||||
mState = malloc(sizeof(dtmf_detect_state_t));
|
mState = malloc(sizeof(dtmf_detect_state_t));
|
||||||
@@ -384,13 +414,13 @@ DTMFDetector::DTMFDetector()
|
|||||||
zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
|
zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
|
||||||
}
|
}
|
||||||
|
|
||||||
DTMFDetector::~DTMFDetector()
|
InbandDtmfDetector::~InbandDtmfDetector()
|
||||||
{
|
{
|
||||||
if (mState)
|
if (mState)
|
||||||
free(mState);
|
free(mState);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size)
|
std::string InbandDtmfDetector::streamPut(unsigned char* samples, unsigned int size)
|
||||||
{
|
{
|
||||||
char buf[16]; buf[0] = 0;
|
char buf[16]; buf[0] = 0;
|
||||||
if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0))
|
if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0))
|
||||||
@@ -398,7 +428,7 @@ std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DTMFDetector::resetState()
|
void InbandDtmfDetector::resetState()
|
||||||
{
|
{
|
||||||
zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
|
zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,18 +15,32 @@
|
|||||||
namespace MT
|
namespace MT
|
||||||
{
|
{
|
||||||
|
|
||||||
class DtmfBuilder
|
class DtmfBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct Rfc2833Event
|
||||||
|
{
|
||||||
|
char mTone = 0;
|
||||||
|
int mDuration = 0;
|
||||||
|
int mVolume = 0;
|
||||||
|
bool mEnd = false;
|
||||||
|
|
||||||
|
bool isValid() const {
|
||||||
|
return mTone != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Output should be 4 bytes length
|
// Output should be 4 bytes length
|
||||||
static void buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output);
|
static void buildRfc2833(const Rfc2833Event& ev, void* output);
|
||||||
|
|
||||||
|
static Rfc2833Event parseRfc2833(std::span<uint8_t> payload);
|
||||||
|
|
||||||
// Buf receives PCM audio
|
// Buf receives PCM audio
|
||||||
static void buildInband(int tone, int startTime, int finishTime, int rate, short* buf);
|
static void buildInband(int tone, int startTime, int finishTime, int rate, short* buf);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dtmf
|
struct Dtmf
|
||||||
{
|
{
|
||||||
Dtmf(): mTone(0), mDuration(0), mVolume(0), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
|
Dtmf(): mTone(0), mDuration(0), mVolume(0), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
|
||||||
Dtmf(int tone, int volume, int duration): mTone(tone), mVolume(volume), mDuration(duration), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
|
Dtmf(int tone, int volume, int duration): mTone(tone), mVolume(volume), mDuration(duration), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
|
||||||
|
|
||||||
@@ -36,17 +50,17 @@ namespace MT
|
|||||||
int mVolume;
|
int mVolume;
|
||||||
int mFinishCount;
|
int mFinishCount;
|
||||||
int mCurrentTime;
|
int mCurrentTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Dtmf> DtmfQueue;
|
typedef std::vector<Dtmf> DtmfQueue;
|
||||||
|
|
||||||
class DtmfContext
|
class DtmfContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Dtmf_Inband,
|
Dtmf_Inband,
|
||||||
Dtmf_Rfc2833
|
Dtmf_Rfc2833
|
||||||
};
|
};
|
||||||
|
|
||||||
DtmfContext();
|
DtmfContext();
|
||||||
@@ -65,33 +79,34 @@ namespace MT
|
|||||||
bool getInband(int milliseconds, int rate, ByteBuffer& output);
|
bool getInband(int milliseconds, int rate, ByteBuffer& output);
|
||||||
bool getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket);
|
bool getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Mutex mGuard;
|
Mutex mGuard;
|
||||||
Type mType;
|
Type mType;
|
||||||
DtmfQueue mQueue;
|
DtmfQueue mQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DTMFDetector
|
class InbandDtmfDetector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/*! The default constructor. Allocates space for detector context. */
|
/*! The default constructor. Allocates space for detector context. */
|
||||||
DTMFDetector();
|
InbandDtmfDetector();
|
||||||
|
|
||||||
/*! The destructor. Free the detector context's memory. */
|
/*! The destructor. Free the detector context's memory. */
|
||||||
~DTMFDetector();
|
~InbandDtmfDetector();
|
||||||
|
|
||||||
/*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation.
|
/*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation.
|
||||||
* @param samples Input PCM buffer pointer.
|
* @param samples Input PCM buffer pointer.
|
||||||
* @param size Size of input buffer in bytes
|
* @param size Size of input buffer in bytes
|
||||||
* @return Found DTMF event(s) in string representation. The returned value has variable length.
|
* @return Found DTMF event(s) in string representation. The returned value has variable length.
|
||||||
*/
|
*/
|
||||||
std::string streamPut(unsigned char* samples, unsigned int size);
|
std::string streamPut(unsigned char* samples, unsigned int size);
|
||||||
|
|
||||||
void resetState();
|
void resetState();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* mState; /// DTMF detector context
|
void* mState; /// DTMF detector context
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -74,13 +74,16 @@ Statistics::~Statistics()
|
|||||||
|
|
||||||
void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
||||||
{
|
{
|
||||||
int lost = 0;
|
int lost = 0; // Total packet lost
|
||||||
int bursts = 0;
|
for (const auto& item: mPacketLossTimeline)
|
||||||
for (int i = 0; i < 128; i++)
|
lost += item.mGap;
|
||||||
{
|
int bursts = mPacketLossTimeline.size(); // number of events
|
||||||
lost += i * mLoss[i];
|
|
||||||
bursts += mLoss[i];
|
// for (const auto& entry: mLoss)
|
||||||
}
|
// {
|
||||||
|
// lost += entry.first * entry.second;
|
||||||
|
// bursts += entry.second;
|
||||||
|
// }
|
||||||
|
|
||||||
if (lost < 5)
|
if (lost < 5)
|
||||||
{
|
{
|
||||||
@@ -109,14 +112,14 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
|||||||
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 = 0, lossr = 0;
|
||||||
calculateBurstr(&burstr, &lossr);
|
calculateBurstr(&burstr, &lossr);
|
||||||
|
|
||||||
double r;
|
double r = 0.0;
|
||||||
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 = 0.0;
|
||||||
|
|
||||||
if (mReceivedRtp < 100)
|
if (mReceivedRtp < 10)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
if (lossr == 0.0 || burstr == 0.0)
|
if (lossr == 0.0 || burstr == 0.0)
|
||||||
|
|||||||
@@ -4,16 +4,15 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "audio/Audio_DataWindow.h"
|
|
||||||
#include "helper/HL_Optional.hpp"
|
|
||||||
#include "helper/HL_Statistics.h"
|
#include "helper/HL_Statistics.h"
|
||||||
#include "helper/HL_Types.h"
|
#include "helper/HL_Types.h"
|
||||||
|
#include "helper/HL_InternetAddress.h"
|
||||||
|
|
||||||
#include "jrtplib/src/rtptimeutilities.h"
|
#include "jrtplib/src/rtptimeutilities.h"
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
{
|
{
|
||||||
@@ -56,47 +55,55 @@ struct PacketLossEvent
|
|||||||
uint32_t mStartSeqno = 0,
|
uint32_t mStartSeqno = 0,
|
||||||
mEndSeqno = 0;
|
mEndSeqno = 0;
|
||||||
int mGap = 0;
|
int mGap = 0;
|
||||||
|
std::chrono::microseconds mTimestampStart = 0us,
|
||||||
|
mTimestampEnd = 0us;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dtmf2833Event
|
||||||
|
{
|
||||||
|
char mTone;
|
||||||
std::chrono::microseconds mTimestamp;
|
std::chrono::microseconds mTimestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Statistics
|
class Statistics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
size_t mReceived = 0, // Received traffic in bytes
|
size_t mReceived = 0, // Received traffic in bytes
|
||||||
mSent = 0, // Sent traffic in bytes
|
mSent = 0, // Sent traffic in bytes
|
||||||
mReceivedRtp = 0, // Number of received rtp packets
|
mReceivedRtp = 0, // Number of received rtp packets
|
||||||
mSentRtp = 0, // Number of sent rtp packets
|
mSentRtp = 0, // Number of sent rtp packets
|
||||||
mReceivedRtcp = 0, // Number of received rtcp packets
|
mReceivedRtcp = 0, // Number of received rtcp packets
|
||||||
mSentRtcp = 0, // Number of sent rtcp packets
|
mSentRtcp = 0, // Number of sent rtcp packets
|
||||||
mDuplicatedRtp = 0, // Number of received duplicated rtp packets
|
mDuplicatedRtp = 0, // Number of received duplicated rtp packets
|
||||||
mOldRtp = 0, // Number of late rtp packets
|
mOldRtp = 0, // Number of late rtp packets
|
||||||
mPacketLoss = 0, // Number of lost packets
|
mPacketLoss = 0, // Number of lost packets
|
||||||
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
||||||
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
||||||
|
|
||||||
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
||||||
mDecodeRequested, // Average amount of requested audio frames to play
|
mDecodeRequested, // Average amount of requested audio frames to play
|
||||||
mPacketInterval; // Average interval between packet adding to jitter buffer
|
mPacketInterval; // Average interval between packet adding to jitter buffer
|
||||||
|
|
||||||
std::array<float, 128> mLoss = {0}; // Every item is number of loss of corresping length
|
std::map<int,int> mLoss; // Every item is number of loss of corresping length
|
||||||
size_t mAudioTime = 0; // Decoded/found time in milliseconds
|
size_t mAudioTime = 0; // Decoded/found time in milliseconds
|
||||||
size_t mDecodedSize = 0; // Number of decoded bytes
|
size_t mDecodedSize = 0; // Number of decoded bytes
|
||||||
uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream
|
uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream
|
||||||
ice::NetworkAddress mRemotePeer; // Last known remote RTP address
|
ice::NetworkAddress mRemotePeer; // Last known remote RTP address
|
||||||
|
|
||||||
// AMR codec bitrate switch counter
|
// AMR codec bitrate switch counter
|
||||||
int mBitrateSwitchCounter = 0;
|
int mBitrateSwitchCounter = 0;
|
||||||
int mCng = 0;
|
int mCng = 0;
|
||||||
std::string mCodecName;
|
std::string mCodecName;
|
||||||
float mJitter = 0.0f; // Jitter
|
float mJitter = 0.0f; // Jitter
|
||||||
TestResult<float> mRttDelay; // RTT delay
|
TestResult<float> mRttDelay; // RTT delay
|
||||||
|
|
||||||
// Timestamp when first RTP packet has arrived
|
// Timestamp when first RTP packet has arrived
|
||||||
std::optional<timepoint_t> mFirstRtpTime;
|
std::optional<timepoint_t> mFirstRtpTime;
|
||||||
|
|
||||||
std::map<int, int> mCodecCount; // Stats on used codecs
|
std::map<int, int> mCodecCount; // Stats on used codecs
|
||||||
|
|
||||||
std::vector<PacketLossEvent> mPacketLossTimeline; // Packet loss timeline
|
std::vector<PacketLossEvent> mPacketLossTimeline; // Packet loss timeline
|
||||||
|
std::vector<Dtmf2833Event> mDtmf2833Timeline;
|
||||||
|
|
||||||
// It is to calculate network MOS
|
// It is to calculate network MOS
|
||||||
void calculateBurstr(double* burstr, double* loss) const;
|
void calculateBurstr(double* burstr, double* loss) const;
|
||||||
|
|||||||
Reference in New Issue
Block a user