diff --git a/src/internal_modules/roc_audio/feedback_monitor.cpp b/src/internal_modules/roc_audio/feedback_monitor.cpp index e9d23ba01..7ff11e99b 100644 --- a/src/internal_modules/roc_audio/feedback_monitor.cpp +++ b/src/internal_modules/roc_audio/feedback_monitor.cpp @@ -7,6 +7,7 @@ */ #include "roc_audio/feedback_monitor.h" +#include "roc_audio/packetizer.h" #include "roc_core/log.h" #include "roc_core/panic.h" #include "roc_core/time.h" @@ -15,15 +16,17 @@ namespace roc { namespace audio { FeedbackMonitor::FeedbackMonitor(IFrameWriter& writer, + Packetizer& packetizer, ResamplerWriter* resampler, const FeedbackConfig& feedback_config, const LatencyConfig& latency_config, const SampleSpec& sample_spec) : tuner_(latency_config, sample_spec) - , latency_metrics_() + , use_packetizer_(false) , has_feedback_(false) , last_feedback_ts_(0) , feedback_timeout_(feedback_config.source_timeout) + , packetizer_(packetizer) , writer_(writer) , resampler_(resampler) , enable_scaling_(latency_config.tuner_profile != audio::LatencyTunerProfile_Intact) @@ -100,6 +103,13 @@ void FeedbackMonitor::process_feedback(packet::stream_source_t source_id, latency_metrics_ = latency_metrics; link_metrics_ = link_metrics; + if (link_metrics_.total_packets == 0 || use_packetizer_) { + // If packet counter is not reported from receiver, fallback to + // counter from sender. + link_metrics_.total_packets = packetizer_.metrics().packet_count; + use_packetizer_ = true; + } + has_feedback_ = true; last_feedback_ts_ = core::timestamp(core::ClockMonotonic); } @@ -128,21 +138,21 @@ size_t FeedbackMonitor::num_participants() const { return has_feedback_ ? 1 : 0; } -const LatencyMetrics& FeedbackMonitor::latency_metrics(size_t part_index) const { - roc_panic_if_msg(part_index >= num_participants(), +const LatencyMetrics& FeedbackMonitor::latency_metrics(size_t party_index) const { + roc_panic_if_msg(party_index >= num_participants(), "feedback monitor: participant index out of bounds:" " index=%lu max=%lu", - (unsigned long)part_index, (unsigned long)num_participants()); + (unsigned long)party_index, (unsigned long)num_participants()); // TODO(gh-674): collect per-session metrics return latency_metrics_; } -const packet::LinkMetrics& FeedbackMonitor::link_metrics(size_t part_index) const { - roc_panic_if_msg(part_index >= num_participants(), +const packet::LinkMetrics& FeedbackMonitor::link_metrics(size_t party_index) const { + roc_panic_if_msg(party_index >= num_participants(), "feedback monitor: participant index out of bounds:" " index=%lu max=%lu", - (unsigned long)part_index, (unsigned long)num_participants()); + (unsigned long)party_index, (unsigned long)num_participants()); // TODO(gh-674): collect per-session metrics return link_metrics_; diff --git a/src/internal_modules/roc_audio/feedback_monitor.h b/src/internal_modules/roc_audio/feedback_monitor.h index 34385bfaa..43dbb3e50 100644 --- a/src/internal_modules/roc_audio/feedback_monitor.h +++ b/src/internal_modules/roc_audio/feedback_monitor.h @@ -14,6 +14,7 @@ #include "roc_audio/iframe_writer.h" #include "roc_audio/latency_tuner.h" +#include "roc_audio/packetizer.h" #include "roc_audio/resampler_writer.h" #include "roc_audio/sample_spec.h" #include "roc_core/noncopyable.h" @@ -63,6 +64,7 @@ class FeedbackMonitor : public IFrameWriter, public core::NonCopyable<> { public: //! Constructor. FeedbackMonitor(IFrameWriter& writer, + Packetizer& packetizer, ResamplerWriter* resampler, const FeedbackConfig& feedback_config, const LatencyConfig& latency_config, @@ -91,12 +93,12 @@ class FeedbackMonitor : public IFrameWriter, public core::NonCopyable<> { size_t num_participants() const; //! Get latest latency metrics for session. - //! @p part_index should be in range [0; num_participants()-1]. - const LatencyMetrics& latency_metrics(size_t part_index) const; + //! @p party_index should be in range [0; num_participants()-1]. + const LatencyMetrics& latency_metrics(size_t party_index) const; //! Get latest link metrics for session. - //! @p part_index should be in range [0; num_participants()-1]. - const packet::LinkMetrics& link_metrics(size_t part_index) const; + //! @p party_index should be in range [0; num_participants()-1]. + const packet::LinkMetrics& link_metrics(size_t party_index) const; private: bool update_tuner_(packet::stream_timestamp_t duration); @@ -108,11 +110,13 @@ class FeedbackMonitor : public IFrameWriter, public core::NonCopyable<> { LatencyMetrics latency_metrics_; packet::LinkMetrics link_metrics_; + bool use_packetizer_; bool has_feedback_; core::nanoseconds_t last_feedback_ts_; const core::nanoseconds_t feedback_timeout_; + Packetizer& packetizer_; IFrameWriter& writer_; ResamplerWriter* resampler_; diff --git a/src/internal_modules/roc_audio/latency_monitor.h b/src/internal_modules/roc_audio/latency_monitor.h index cff3a41a3..0972b053b 100644 --- a/src/internal_modules/roc_audio/latency_monitor.h +++ b/src/internal_modules/roc_audio/latency_monitor.h @@ -60,17 +60,6 @@ namespace audio { class LatencyMonitor : public IFrameReader, public core::NonCopyable<> { public: //! Constructor. - //! - //! @b Parameters - //! - @p frame_reader is inner frame reader for E2E latency calculation - //! - @p incoming_queue and @p depacketizer are used to NIQ latency calculation - //! - @p link_meter is used to obtain link metrics - //! - @p resampler is used to set the scaling factor to compensate clock - //! drift according to calculated latency - //! - @p config defines calculation parameters - //! - @p packet_sample_spec is the sample spec of the input packets - //! - @p frame_sample_spec is the sample spec of the output frames (after - //! resampling) LatencyMonitor(IFrameReader& frame_reader, const packet::SortedQueue& incoming_queue, const Depacketizer& depacketizer, diff --git a/src/internal_modules/roc_packet/ilink_meter.h b/src/internal_modules/roc_packet/ilink_meter.h index 37da16ae9..0198f88f3 100644 --- a/src/internal_modules/roc_packet/ilink_meter.h +++ b/src/internal_modules/roc_packet/ilink_meter.h @@ -44,13 +44,7 @@ struct LinkMetrics { //! packets actually received, where the number of packets received includes any //! which are late or duplicates. Packets that arrive late are not counted as lost, //! and the loss may be negative if there are duplicates. - int64_t cum_lost_packets; - - //! Fraction of lost packets from 0 to 1. - //! The fraction of RTP data packets lost since the previous report was sent. - //! Defined to be the number of packets lost divided by the number of packets - //! expected. If the loss is negative due to duplicates, set to zero. - float fract_lost_packets; + int64_t lost_packets; //! Estimated interarrival jitter. //! An estimate of the statistical variance of the RTP data packet @@ -67,8 +61,7 @@ struct LinkMetrics { : ext_first_seqnum(0) , ext_last_seqnum(0) , total_packets(0) - , cum_lost_packets(0) - , fract_lost_packets(0) + , lost_packets(0) , jitter(0) , rtt(0) { } diff --git a/src/internal_modules/roc_packet/units.h b/src/internal_modules/roc_packet/units.h index 4a8d0c64f..0ce71569c 100644 --- a/src/internal_modules/roc_packet/units.h +++ b/src/internal_modules/roc_packet/units.h @@ -102,6 +102,26 @@ inline bool seqnum_le(const seqnum_t a, const seqnum_t b) { //! Sequence number extended to 32 bits. typedef uint32_t ext_seqnum_t; +//! Extended sequence number delta. +//! @remarks +//! Signed version of ext_seqnum_t. +typedef int32_t ext_seqnum_diff_t; + +//! Compute difference between two extended seqnums. +inline ext_seqnum_diff_t ext_seqnum_diff(const ext_seqnum_t a, const ext_seqnum_t b) { + return ext_seqnum_diff_t(a - b); +} + +//! Check if `a` is before `b`, taking possible wrap into account. +inline bool ext_seqnum_lt(const ext_seqnum_t a, const ext_seqnum_t b) { + return ext_seqnum_diff(a, b) < 0; +} + +//! Check if `a` is before or equal to `b`, taking possible wrap into account. +inline bool ext_seqnum_le(const ext_seqnum_t a, const ext_seqnum_t b) { + return ext_seqnum_diff(a, b) <= 0; +} + //! FEC packet block number. //! @remarks //! Defines position of FEC packet block within stream. diff --git a/src/internal_modules/roc_pipeline/receiver_session.cpp b/src/internal_modules/roc_pipeline/receiver_session.cpp index 37f7d76ea..6809a9c26 100644 --- a/src/internal_modules/roc_pipeline/receiver_session.cpp +++ b/src/internal_modules/roc_pipeline/receiver_session.cpp @@ -318,8 +318,8 @@ void ReceiverSession::generate_reports(const char* report_cname, report.sample_rate = source_meter_->encoding().sample_spec.sample_rate(); report.ext_first_seqnum = link_metrics.ext_first_seqnum; report.ext_last_seqnum = link_metrics.ext_last_seqnum; - report.cum_loss = link_metrics.cum_lost_packets; - report.fract_loss = link_metrics.fract_lost_packets; + report.packet_count = link_metrics.total_packets; + report.cum_loss = link_metrics.lost_packets; report.jitter = link_metrics.jitter; report.niq_latency = latency_metrics.niq_latency; report.niq_stalling = latency_metrics.niq_stalling; @@ -343,8 +343,8 @@ void ReceiverSession::generate_reports(const char* report_cname, report.sample_rate = repair_meter_->encoding().sample_spec.sample_rate(); report.ext_first_seqnum = link_metrics.ext_first_seqnum; report.ext_last_seqnum = link_metrics.ext_last_seqnum; - report.cum_loss = link_metrics.cum_lost_packets; - report.fract_loss = link_metrics.fract_lost_packets; + report.packet_count = link_metrics.total_packets; + report.cum_loss = link_metrics.lost_packets; report.jitter = link_metrics.jitter; reports++; diff --git a/src/internal_modules/roc_pipeline/sender_session.cpp b/src/internal_modules/roc_pipeline/sender_session.cpp index 70b90add3..33e2e9b5c 100644 --- a/src/internal_modules/roc_pipeline/sender_session.cpp +++ b/src/internal_modules/roc_pipeline/sender_session.cpp @@ -188,8 +188,8 @@ bool SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, } feedback_monitor_.reset(new (feedback_monitor_) audio::FeedbackMonitor( - *frm_writer, resampler_writer_.get(), config_.feedback, config_.latency, - config_.input_sample_spec)); + *frm_writer, *packetizer_, resampler_writer_.get(), config_.feedback, + config_.latency, config_.input_sample_spec)); if (!feedback_monitor_ || !feedback_monitor_->is_valid()) { return false; } @@ -322,8 +322,8 @@ rtcp::SendReport SenderSession::query_send_stream(core::nanoseconds_t report_tim report.report_timestamp = report_time; report.stream_timestamp = timestamp_extractor_->get_mapping(report_time); report.sample_rate = packetizer_->sample_rate(); - report.packet_count = (uint32_t)packet_metrics.packet_count; - report.byte_count = (uint32_t)packet_metrics.payload_count; + report.packet_count = packet_metrics.packet_count; + report.byte_count = packet_metrics.payload_count; return report; } @@ -342,8 +342,8 @@ SenderSession::notify_send_stream(packet::stream_source_t recv_source_id, packet::LinkMetrics link_metrics; link_metrics.ext_first_seqnum = recv_report.ext_first_seqnum; link_metrics.ext_last_seqnum = recv_report.ext_last_seqnum; - link_metrics.cum_lost_packets = recv_report.cum_loss; - link_metrics.fract_lost_packets = recv_report.fract_loss; + link_metrics.total_packets = recv_report.packet_count; + link_metrics.lost_packets = recv_report.cum_loss; link_metrics.jitter = recv_report.jitter; link_metrics.rtt = recv_report.rtt; diff --git a/src/internal_modules/roc_rtcp/communicator.cpp b/src/internal_modules/roc_rtcp/communicator.cpp index 18f0302ad..f911e09bd 100644 --- a/src/internal_modules/roc_rtcp/communicator.cpp +++ b/src/internal_modules/roc_rtcp/communicator.cpp @@ -533,6 +533,7 @@ bool Communicator::next_send_stream_(size_t new_stream_index) { } bool Communicator::next_recv_stream_(size_t new_stream_index) { + // See comment in next_send_stream_(). const size_t next_pkt_recv_stream = std::max(cur_pkt_recv_stream_, new_stream_index - recv_stream_index_ + 1); diff --git a/src/internal_modules/roc_rtcp/loss_estimator.cpp b/src/internal_modules/roc_rtcp/loss_estimator.cpp new file mode 100644 index 000000000..ad89cccea --- /dev/null +++ b/src/internal_modules/roc_rtcp/loss_estimator.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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 "roc_rtcp/loss_estimator.h" + +namespace roc { +namespace rtcp { + +LossEstimator::LossEstimator() + : prev_total_(0) + , prev_lost_(0) { +} + +float LossEstimator::update(const uint64_t total_packets, const int64_t lost_packets) { + float fract_loss = 0; + + if (total_packets > prev_total_) { + fract_loss = + float(lost_packets - prev_lost_) / float(total_packets - prev_total_); + } + + if (fract_loss < 0) { + fract_loss = 0; + } + + prev_total_ = total_packets; + prev_lost_ = lost_packets; + + return fract_loss; +} + +} // namespace rtcp +} // namespace roc diff --git a/src/internal_modules/roc_rtcp/loss_estimator.h b/src/internal_modules/roc_rtcp/loss_estimator.h new file mode 100644 index 000000000..c1fe0e997 --- /dev/null +++ b/src/internal_modules/roc_rtcp/loss_estimator.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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/. + */ + +//! @file roc_rtcp/loss_estimator.h +//! @brief Loss estimator. + +#ifndef ROC_RTCP_LOSS_ESTIMATOR_H_ +#define ROC_RTCP_LOSS_ESTIMATOR_H_ + +#include "roc_core/stddefs.h" + +namespace roc { +namespace rtcp { + +//! Computes fractions loss ration since last report. +class LossEstimator { +public: + //! Initialize. + LossEstimator(); + + //! Update and return fractional loss ration since previous update. + //! @p total_packets defines total count of packets expected. + //! @p lost_packets defines count of packets not received, + //! probably negative dues to duplicates. + float update(uint64_t total_packets, int64_t lost_packets); + +private: + uint64_t prev_total_; + int64_t prev_lost_; +}; + +} // namespace rtcp +} // namespace roc + +#endif // ROC_RTCP_LOSS_ESTIMATOR_H_ diff --git a/src/internal_modules/roc_rtcp/packet_counter.cpp b/src/internal_modules/roc_rtcp/packet_counter.cpp new file mode 100644 index 000000000..ebac413e6 --- /dev/null +++ b/src/internal_modules/roc_rtcp/packet_counter.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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 "roc_rtcp/packet_counter.h" + +namespace roc { +namespace rtcp { + +PacketCounter::PacketCounter() + : first_update_(true) + , begin64_(0) + , end64_hi_(0) + , end64_lo_(0) + , counter_(0) { +} + +uint64_t PacketCounter::update(const uint32_t begin, const uint32_t end) { + // If this is first update, or begin was changed, reset state. + if (first_update_ || begin != begin64_) { + begin64_ = begin; + end64_hi_ = 0; + end64_lo_ = end; + first_update_ = false; + } + + // Update end. + if (int32_t(end - end64_lo_) > 0) { + if (end < end64_lo_) { + end64_hi_ += (uint32_t)-1; + } + end64_lo_ = end; + } + + // Update counter. + if (begin64_ <= (end64_hi_ + end64_lo_)) { + counter_ = (end64_hi_ + end64_lo_) - begin64_; + } + + return counter_; +} + +} // namespace rtcp +} // namespace roc diff --git a/src/internal_modules/roc_rtcp/packet_counter.h b/src/internal_modules/roc_rtcp/packet_counter.h new file mode 100644 index 000000000..bce7999a2 --- /dev/null +++ b/src/internal_modules/roc_rtcp/packet_counter.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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/. + */ + +//! @file roc_rtcp/packet_counter.h +//! @brief Packet counter. + +#ifndef ROC_RTCP_PACKET_COUNTER_H_ +#define ROC_RTCP_PACKET_COUNTER_H_ + +#include "roc_core/stddefs.h" + +namespace roc { +namespace rtcp { + +//! Computes number of packets in interval. +class PacketCounter { +public: + //! Initialize. + PacketCounter(); + + //! Update and return packet counter. + //! @p begin defines interval beginning. + //! @p end defines interval end (exclusive). + //! Packet counter is computes as the maximum seen distance from begin to end. + //! If begin changes, the maximum is cleared. + //! If end wraps around 32-bit boundary, this is taken into account. + uint64_t update(uint32_t begin, uint32_t end); + +private: + bool first_update_; + + uint64_t begin64_; + uint64_t end64_hi_; + uint32_t end64_lo_; + + uint64_t counter_; +}; + +} // namespace rtcp +} // namespace roc + +#endif // ROC_RTCP_PACKET_COUNTER_H_ diff --git a/src/internal_modules/roc_rtcp/reporter.cpp b/src/internal_modules/roc_rtcp/reporter.cpp index 3ae210511..eceb8d21c 100644 --- a/src/internal_modules/roc_rtcp/reporter.cpp +++ b/src/internal_modules/roc_rtcp/reporter.cpp @@ -218,8 +218,11 @@ void Reporter::process_sr(const header::SenderReportPacket& sr) { stream->remote_send_report.report_timestamp = packet::ntp_2_unix(sr.ntp_timestamp()); stream->remote_send_report.stream_timestamp = sr.rtp_timestamp(); - stream->remote_send_report.packet_count = sr.packet_count(); - stream->remote_send_report.byte_count = sr.byte_count(); + stream->remote_send_report.packet_count = + stream->remote_send_packet_count.update(0, sr.packet_count()); + + stream->remote_send_report.byte_count = + stream->remote_send_byte_count.update(0, sr.byte_count()); // Update remote sender address. if (stream->remote_address != report_addr_) { @@ -281,7 +284,6 @@ void Reporter::process_reception_block(const packet::stream_source_t ssrc, stream->remote_recv_report.receiver_source_id = stream->source_id; stream->remote_recv_report.sender_source_id = local_source_id_; - stream->remote_recv_report.fract_loss = blk.fract_loss(); stream->remote_recv_report.cum_loss = blk.cum_loss(); stream->remote_recv_report.ext_last_seqnum = blk.last_seqnum(); stream->remote_recv_report.jitter = @@ -462,6 +464,10 @@ void Reporter::process_measurement_info_block(const header::XrPacket& xr, stream->remote_recv_report.ext_first_seqnum = blk.first_seq(); + stream->remote_recv_report.packet_count = stream->remote_recv_packet_count.update( + stream->remote_recv_report.ext_first_seqnum, + stream->remote_recv_report.ext_last_seqnum + 1); + update_stream_(*stream); } @@ -671,8 +677,8 @@ void Reporter::generate_sr(header::SenderReportPacket& sr) { sr.set_ntp_timestamp(packet::unix_2_ntp(report_time_)); sr.set_rtp_timestamp(local_send_report_.stream_timestamp); - sr.set_packet_count(local_send_report_.packet_count); - sr.set_byte_count(local_send_report_.byte_count); + sr.set_packet_count((uint32_t)local_send_report_.packet_count); + sr.set_byte_count((uint32_t)local_send_report_.byte_count); // Update time of most recent SR. current_sr_ = report_time_; @@ -710,8 +716,9 @@ void Reporter::generate_reception_block(size_t addr_index, blk.set_ssrc(stream->source_id); blk.set_last_seqnum(stream->local_recv_report->ext_last_seqnum); - blk.set_fract_loss(stream->local_recv_report->fract_loss); blk.set_cum_loss(stream->local_recv_report->cum_loss); + blk.set_fract_loss(stream->local_recv_loss.update( + stream->local_recv_report->packet_count, stream->local_recv_report->cum_loss)); if (stream->local_recv_report->jitter > 0) { blk.set_jitter(packet::ns_2_stream_timestamp( @@ -1377,7 +1384,7 @@ void Reporter::validate_recv_report_(const RecvReport& recv_report) { if (recv_report.clock_offset != 0 || recv_report.rtt != 0) { roc_panic("rtcp reporter:" " query returned invalid receiver report:" - " clock_offset and rtt are read-only fields and should not be set"); + " clock_offset and rtt are read-only fields and should be set to zero"); } } diff --git a/src/internal_modules/roc_rtcp/reporter.h b/src/internal_modules/roc_rtcp/reporter.h index ca76694bd..923bd2c9f 100644 --- a/src/internal_modules/roc_rtcp/reporter.h +++ b/src/internal_modules/roc_rtcp/reporter.h @@ -28,9 +28,11 @@ #include "roc_rtcp/config.h" #include "roc_rtcp/headers.h" #include "roc_rtcp/iparticipant.h" +#include "roc_rtcp/loss_estimator.h" #include "roc_rtcp/reports.h" #include "roc_rtcp/rtt_estimator.h" #include "roc_rtcp/sdes.h" +#include "roc_rtcp/packet_counter.h" #include "roc_status/status_code.h" namespace roc { @@ -292,18 +294,22 @@ class Reporter : public core::NonCopyable<> { bool has_remote_recv_report; RecvReport remote_recv_report; RttEstimator remote_recv_rtt; + PacketCounter remote_recv_packet_count; // Stream is receiving from remote participant and we obtained // sender report from it. bool has_remote_send_report; SendReport remote_send_report; RttEstimator remote_send_rtt; + PacketCounter remote_send_packet_count; + PacketCounter remote_send_byte_count; // Stream is receiving from remote participant and this is our // receiver report to be delivered to remote side. // Points to an element of local_recv_reports_ array. Whenever // array is resized, rebuild_index_() updates the pointers. RecvReport* local_recv_report; + LossEstimator local_recv_loss; // Remote address from where reports are coming. address::SocketAddr remote_address; diff --git a/src/internal_modules/roc_rtcp/reports.h b/src/internal_modules/roc_rtcp/reports.h index 261eabae5..47e035970 100644 --- a/src/internal_modules/roc_rtcp/reports.h +++ b/src/internal_modules/roc_rtcp/reports.h @@ -52,13 +52,13 @@ struct SendReport { //! Number of packets sent. //! The total number of RTP data packets transmitted by the sender since starting //! transmission up until the time of this report. - uint32_t packet_count; + uint64_t packet_count; //! Number of bytes sent. //! The total number of payload octets (i.e., not including header or padding) //! transmitted in RTP data packets by the sender since starting transmission //! up until the time this report. - uint32_t byte_count; + uint64_t byte_count; //! Estimated offset of remote clock relative to local clock. //! If you add it to local timestamp, you get estimated remote timestamp. @@ -127,6 +127,11 @@ struct RecvReport { //! count of sequence number cycles. packet::ext_seqnum_t ext_last_seqnum; + //! Number of packets expected. + //! On sender it's derived from ext_first_seqnum and ext_last_seqnum and may + //! be zero if necessary XR blocks are not supported. + uint64_t packet_count; + //! Cumulative count of lost packets. //! The total number of RTP data packets that have been lost since the beginning //! of reception. Defined to be the number of packets expected minus the number of @@ -135,12 +140,6 @@ struct RecvReport { //! and the loss may be negative if there are duplicates. int64_t cum_loss; - //! Fraction of lost packets from 0 to 1. - //! The fraction of RTP data packets lost since the previous report was sent. - //! Defined to be the number of packets lost divided by the number of packets - //! expected. If the loss is negative due to duplicates, set to zero. - float fract_loss; - //! Estimated interarrival jitter. //! An estimate of the statistical variance of the RTP data packet //! interarrival time. @@ -179,8 +178,8 @@ struct RecvReport { , sample_rate(0) , ext_first_seqnum(0) , ext_last_seqnum(0) + , packet_count(0) , cum_loss(0) - , fract_loss(0) , jitter(0) , niq_latency(0) , niq_stalling(0) diff --git a/src/internal_modules/roc_rtp/link_meter.cpp b/src/internal_modules/roc_rtp/link_meter.cpp index 03b5f24d5..aed950b22 100644 --- a/src/internal_modules/roc_rtp/link_meter.cpp +++ b/src/internal_modules/roc_rtp/link_meter.cpp @@ -121,8 +121,7 @@ void LinkMeter::update_metrics_(const packet::Packet& packet) { // TODO(gh-688): // - fill total_packets - // - fill cum_lost_packets - // - fill fract_lost_packets + // - fill lost_packets // - fill jitter (use encoding_->sample_spec to convert to nanoseconds) first_packet_ = false; diff --git a/src/tests/roc_rtcp/test_communicator.cpp b/src/tests/roc_rtcp/test_communicator.cpp index a32c017d8..0fb971d8f 100644 --- a/src/tests/roc_rtcp/test_communicator.cpp +++ b/src/tests/roc_rtcp/test_communicator.cpp @@ -376,7 +376,6 @@ RecvReport make_recv_report(core::nanoseconds_t time, report.sample_rate = SampleRate; report.ext_first_seqnum = seed * 10; report.ext_last_seqnum = seed * 2000; - report.fract_loss = seed * 0.001f; report.cum_loss = (long)seed * 3000; report.jitter = seed * 400000; report.niq_latency = seed * 500000; @@ -408,7 +407,10 @@ void expect_recv_report(const RecvReport& report, CHECK_EQUAL(0, report.ext_first_seqnum); } CHECK_EQUAL(seed * 2000, report.ext_last_seqnum); - DOUBLES_EQUAL(seed * 0.001f, report.fract_loss, 0.005); + if (expect_xr) { + CHECK_EQUAL(report.ext_last_seqnum - report.ext_first_seqnum + 1, + report.packet_count); + } CHECK_EQUAL((long)seed * 3000, report.cum_loss); expect_timestamp("jitter", seed * 400000, report.jitter, TimestampEpsilon); if (expect_xr) { diff --git a/src/tests/roc_rtcp/test_loss_estimator.cpp b/src/tests/roc_rtcp/test_loss_estimator.cpp new file mode 100644 index 000000000..84dee7f7d --- /dev/null +++ b/src/tests/roc_rtcp/test_loss_estimator.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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 + +#include "roc_rtcp/loss_estimator.h" + +namespace roc { +namespace rtcp { + +namespace { + +const double Epsilon = 1e-8; + +} // namespace + +TEST_GROUP(loss_estimator) {}; + +TEST(loss_estimator, regular) { + LossEstimator le; + + // 0, 0 + DOUBLES_EQUAL(0.0, le.update(0, 0), Epsilon); + // +10, +0 + DOUBLES_EQUAL(0.0, le.update(10, 0), Epsilon); + // +10, +5 + DOUBLES_EQUAL(0.5, le.update(20, 5), Epsilon); + // +10, +1 + DOUBLES_EQUAL(0.1, le.update(30, 6), Epsilon); + // +10, +0 + DOUBLES_EQUAL(0.0, le.update(40, 6), Epsilon); + // +10, -1 + DOUBLES_EQUAL(0.0, le.update(50, 5), Epsilon); + // +10, -10 + DOUBLES_EQUAL(0.0, le.update(60, -5), Epsilon); + // +10, +10 + DOUBLES_EQUAL(1.0, le.update(70, 5), Epsilon); + // +10, +2 + DOUBLES_EQUAL(0.2, le.update(80, 7), Epsilon); +} + +TEST(loss_estimator, jump_nackwards) { + LossEstimator le; + + // +40, +4 + DOUBLES_EQUAL(0.1, le.update(40, 4), Epsilon); + // -30, +2 + DOUBLES_EQUAL(0.0, le.update(10, 6), Epsilon); + // +10, +2 + DOUBLES_EQUAL(0.2, le.update(20, 8), Epsilon); +} + +} // namespace rtcp +} // namespace roc diff --git a/src/tests/roc_rtcp/test_packet_counter.cpp b/src/tests/roc_rtcp/test_packet_counter.cpp new file mode 100644 index 000000000..6c377b7d3 --- /dev/null +++ b/src/tests/roc_rtcp/test_packet_counter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Roc Streaming authors + * + * 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 + +#include "roc_rtcp/packet_counter.h" + +namespace roc { +namespace rtcp { + +TEST_GROUP(packet_counter) {}; + +TEST(packet_counter, no_wrap) { + PacketCounter pc; + + CHECK_EQUAL(10, pc.update(200, 210)); + CHECK_EQUAL(30, pc.update(200, 230)); + CHECK_EQUAL(30, pc.update(200, 220)); + CHECK_EQUAL(40, pc.update(200, 240)); + + CHECK_EQUAL(10, pc.update(100, 110)); + CHECK_EQUAL(20, pc.update(100, 120)); + + CHECK_EQUAL(10, pc.update(300, 310)); + CHECK_EQUAL(20, pc.update(300, 320)); +} + +TEST(packet_counter, wrap) { + PacketCounter pc; + + CHECK_EQUAL(10, pc.update(0xFFFFFFFF - 30, 0xFFFFFFFF - 20)); + CHECK_EQUAL(20, pc.update(0xFFFFFFFF - 30, 0xFFFFFFFF - 10)); + CHECK_EQUAL(40, pc.update(0xFFFFFFFF - 30, 10)); + CHECK_EQUAL(60, pc.update(0xFFFFFFFF - 30, 30)); + CHECK_EQUAL(60, pc.update(0xFFFFFFFF - 30, 20)); + CHECK_EQUAL(70, pc.update(0xFFFFFFFF - 30, 40)); + + CHECK_EQUAL(10, pc.update(10, 20)); +} + +} // namespace rtcp +} // namespace roc