diff --git a/apps/statswriter.cpp b/apps/statswriter.cpp index c176248ab..d9fb708b9 100644 --- a/apps/statswriter.cpp +++ b/apps/statswriter.cpp @@ -67,6 +67,7 @@ struct SrtStatsTableInit STAT(SEND, msBuf, msSndBuf); STAT(SEND, mbitRate, mbpsSendRate); STAT(SEND, sendPeriod, usPktSndPeriod); + STAT(SEND, sendPeriod, usSendingJitter); STAT(RECV, packets, pktRecv); STAT(RECV, packetsUnique, pktRecvUnique); @@ -85,6 +86,8 @@ struct SrtStatsTableInit STAT(RECV, msBuf, msRcvBuf); STAT(RECV, mbitRate, mbpsRecvRate); STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); + STAT(RECV, usInterArrivalJitter, usInterArrivalJitter); + STAT(RECV, usDeliveryJitter, usDeliveryJitter); } } g_SrtStatsTableInit (g_SrtStatsTable); @@ -304,8 +307,9 @@ class SrtStatsCols : public SrtStatsWriter output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl; output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl; output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl; - output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; + output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl; + output << "Jitter (us): SND: " << setw(11) << mon.usSendingJitter << " RCV (TsbPd):" << setw(11) << mon.usDeliveryJitter << endl; return output.str(); } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c8b2ed8c6..5bd45b876 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6920,6 +6920,8 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)"); #endif } + + m_JitterTracer.onDataPktDelivery(srt::sync::steady_clock::time_point() + microseconds_from(w_mctrl.srctime)); return res; } @@ -7053,6 +7055,8 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); } + m_JitterTracer.onDataPktDelivery(srt::sync::steady_clock::time_point() + microseconds_from(w_mctrl.srctime)); + return res; } @@ -7402,6 +7406,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; + perf->usDeliveryJitter = m_JitterTracer.deliveryJitter(); + perf->usSendingJitter = m_JitterTracer.sendingJitter(); + perf->usInterArrivalJitter = m_JitterTracer.jitter(); + if (clear) { m_stats.sndr.resetTrace(); @@ -9830,6 +9838,14 @@ int srt::CUDT::processData(CUnit* in_unit) // otherwise measurement must be rejected. m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted); + // The 17th packet is a probing packet that arrives with reduced IAT + if (!retransmitted && ((packet.m_iSeqNo & PUMASK_SEQNO_PROBE) != 1)) + { + const uint32_t timestamp = packet.getMsgTimeStamp(); + const time_point tsbpdTime = m_pRcvBuffer->getTsbPdTimeBase(timestamp); + m_JitterTracer.onDataPktArrival(packet, tsbpdTime); + } + enterCS(m_StatsLock); m_stats.rcvr.recvd.count(pktsz); leaveCS(m_StatsLock); diff --git a/srtcore/core.h b/srtcore/core.h index 4f87ad181..25253954b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -69,6 +69,7 @@ modified by #include "handshake.h" #include "congctl.h" #include "packetfilter.h" +#include "jitter_tracer.h" #include "socketconfig.h" #include "utilities.h" #include "logger_defs.h" @@ -905,6 +906,7 @@ class CUDT CACKWindow m_ACKWindow; // ACK history window CPktTimeWindow<16, 64> m_RcvTimeWindow; // Packet arrival time window + CJitterTracer m_JitterTracer; //< RFC3550-like packet jitter tracer int32_t m_iRcvLastAck; // First unacknowledged packet seqno sent in the latest ACK. #ifdef ENABLE_LOGGING @@ -1101,6 +1103,7 @@ class CUDT int traceReorderDistance; double traceBelatedTime; + uint64_t ullJitter; // Packet inter-arrival jitter (microseconds) int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration } m_stats; diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index b800fdc44..881fd8bed 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -12,6 +12,7 @@ crypto.cpp epoll.cpp fec.cpp handshake.cpp +jitter_tracer.cpp list.cpp logger_default.cpp logger_defs.cpp @@ -62,6 +63,7 @@ core.h crypto.h epoll.h handshake.h +jitter_tracer.h list.h logging.h md5.h diff --git a/srtcore/jitter_tracer.cpp b/srtcore/jitter_tracer.cpp new file mode 100644 index 000000000..c79bd053d --- /dev/null +++ b/srtcore/jitter_tracer.cpp @@ -0,0 +1,67 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2020 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "jitter_tracer.h" + +namespace srt { +using namespace sync; + + +void CJitterTracer::onDataPktArrival(const CPacket& pkt, const time_point& tsbpdBaseTime) +{ + const uint32_t timestamp_us = pkt.getMsgTimeStamp(); + + // RFC 3550 suggests to calculate the relative transit time. + // The relative transit time is the difference between a packet's + // timestamp and the receiver's clock at the time of arrival, + // measured in the same units. + // SRT Data packet does not have an absolute time, the relative time is used instead. + // The timestamp of SRT data packet added to the TSBPD base time is the prediction + // of the current time on the receiver. + // Therefore we calculate the difference between the prediction and the actual value. + // Note the measure difference also includes packet processing delay. + const steady_clock::duration delay = + steady_clock::now() - (tsbpdBaseTime + microseconds_from(timestamp_us)); + + const uint64_t di = abs(count_microseconds(delay - m_dPrevArrivalDelay)); + m_uiJitter = avg_iir<16>(m_uiJitter, di); + m_dPrevArrivalDelay = delay; +} + +void CJitterTracer::onDataPktDelivery(const time_point& tsbpdTargetTime) +{ + // RFC 3550 suggests to calculate the relative transit time. + // The relative transit time is the difference between a packet's + // timestamp and the receiver's clock at the time of arrival, + // measured in the same units. + // SRT Data packet does not have an absolute time, the relative time is used instead. + // The timestamp of SRT data packet added to the TSBPD base time is the prediction + // of the current time on the receiver. + // Therefore we calculate the difference between the prediction and the actual value. + // Note the measure difference also includes packet processing delay. + const steady_clock::duration delay = + steady_clock::now() - tsbpdTargetTime; + + const uint64_t di = abs(count_microseconds(delay - m_dPrevDeliverDelay)); + m_uiDeliverJitter = avg_iir<16>(m_uiDeliverJitter, di); + m_dPrevDeliverDelay = delay; +} + +void CJitterTracer::onDataPktSent(const time_point& tsPktOrigin) +{ + const steady_clock::duration delay = + steady_clock::now() - tsPktOrigin; + + const uint64_t di = abs(count_microseconds(delay - m_dPrevSendDelay)); + m_uiSendJitter = avg_iir<16>(m_uiSendJitter, di); + m_dPrevSendDelay = delay; +} + +} // namespace srt diff --git a/srtcore/jitter_tracer.h b/srtcore/jitter_tracer.h new file mode 100644 index 000000000..7d664ddfd --- /dev/null +++ b/srtcore/jitter_tracer.h @@ -0,0 +1,50 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2020 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +#pragma once +#ifndef INC_JITTER_TRACER_H +#define INC_JITTER_TRACER_H + +#include "packet.h" +#include "sync.h" + +namespace srt { + +class CJitterTracer +{ + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + +public: + CJitterTracer() + : m_uiJitter(0) + {} + +public: + void onDataPktArrival(const CPacket& pkt, const time_point& tsbpdBaseTime); + void onDataPktDelivery(const time_point& tsbpdTargetTime); + void onDataPktSent(const time_point& tsPktOrigin); + + uint64_t jitter() const { return m_uiJitter; } + uint64_t deliveryJitter() const { return m_uiDeliverJitter; } + uint64_t sendingJitter() const { return m_uiSendJitter; } + +private: + duration m_dPrevArrivalDelay; + duration m_dPrevDeliverDelay; + duration m_dPrevSendDelay; + uint64_t m_uiJitter; + uint64_t m_uiDeliverJitter; + uint64_t m_uiSendJitter; +}; + + +} // namespace srt + +#endif // INC_JITTER_TRACER_H diff --git a/srtcore/srt.h b/srtcore/srt.h index 4cc2a377f..1ad788610 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -412,6 +412,12 @@ struct CBytePerfMon int64_t pktRecvUnique; // number of packets to be received by the application uint64_t byteSentUnique; // number of data bytes, sent by the application uint64_t byteRecvUnique; // number of data bytes to be received by the application + + uint64_t usInterSendingJitter; // NOT IMPLEMENTED! RFC3550 inter jitter jitter of data packets + uint64_t usInterArrivalJitter; // RFC3550 inter arrival jitter of data packets + uint64_t usDeliveryJitter; // RFC3550 inter delivery jitter of data packets + uint64_t usSendingJitter; // RFC3550 inter sending jitter of data packets (excluding probing packets) + int64_t usDrift; }; ////////////////////////////////////////////////////////////////////////////////