Skip to content

Commit

Permalink
Jitter and packets counters to C-API metrics
Browse files Browse the repository at this point in the history
Recovered packets counter is not supported
by generic RTCP. Should be transfered to sender
by other means
  • Loading branch information
baranovmv committed Jun 30, 2024
1 parent d598c68 commit c8c4525
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/internal_modules/roc_packet/ilink_meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ struct LinkMetrics {
//! and the loss may be negative if there are duplicates.
int64_t lost_packets;

//! Cumulate count of recovered packets.
//! How many packets lost packets receiver was able to recover
//! by FEC. The sender is not getting this metric so far.
uint64_t recovered_packets;

//! Estimated interarrival jitter.
//! An estimate of the statistical variance of the RTP data packet
//! interarrival time.
Expand All @@ -68,6 +73,7 @@ struct LinkMetrics {
, ext_last_seqnum(0)
, total_packets(0)
, lost_packets(0)
, recovered_packets(0)
, jitter(0)
, max_jitter(0)
, min_jitter(0)
Expand Down
3 changes: 3 additions & 0 deletions src/internal_modules/roc_pipeline/receiver_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config,
}
pkt_reader = timestamp_injector_.get();

source_meter_->set_reader(*pkt_reader);
pkt_reader = source_meter_.get();

// Third part of pipeline: chained frame readers from depacketizer to mixer.
// Mixed reads frames from this pipeline, and in the end it requests packets
// from packet readers pipeline.
Expand Down
18 changes: 18 additions & 0 deletions src/internal_modules/roc_rtp/link_meter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ LinkMeter::LinkMeter(core::IArena& arena,
: encoding_map_(encoding_map)
, encoding_(NULL)
, writer_(NULL)
, reader_(NULL)
, sample_spec_(sample_spec)
, first_packet_jitter_(true)
, win_len_(latency_config.tuner_profile == audio::LatencyTunerProfile_Responsive
Expand Down Expand Up @@ -92,10 +93,27 @@ status::StatusCode LinkMeter::write(const packet::PacketPtr& packet) {
return writer_->write(packet);
}

status::StatusCode LinkMeter::read(packet::PacketPtr& packet, packet::PacketReadMode mode) {
if (!reader_) {
roc_panic ("link meter: forgot to call set_reader()");
}

status::StatusCode result = reader_->read(packet, mode);
if (packet && packet->has_flags(packet::Packet::FlagRestored)) {
metrics_.recovered_packets++;
}

return result;
}

void LinkMeter::set_writer(packet::IWriter& writer) {
writer_ = &writer;
}

void LinkMeter::set_reader(packet::IReader& reader) {
reader_ = &reader;
}

void LinkMeter::update_jitter_(const packet::Packet& packet) {
// Do not calculate jitter on recovered packets.
if (packet.has_flags(packet::Packet::FlagRestored)) {
Expand Down
10 changes: 10 additions & 0 deletions src/internal_modules/roc_rtp/link_meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace rtp {
//! writer/reader, and updates metrics.
class LinkMeter : public packet::ILinkMeter,
public packet::IWriter,
public packet::IReader,
public core::NonCopyable<> {
public:
//! Initialize.
Expand Down Expand Up @@ -82,11 +83,19 @@ class LinkMeter : public packet::ILinkMeter,
//! Invoked early in pipeline right after the packet is received.
virtual ROC_ATTR_NODISCARD status::StatusCode write(const packet::PacketPtr& packet);

//! Read packet and update restored packet counter.
//! @remarks
//! Invoked near the end of pipeline so as to be aware of recovered packets.
virtual ROC_ATTR_NODISCARD status::StatusCode read(packet::PacketPtr& packet,
packet::PacketReadMode mode);

//! Set nested packet writer.
//! @remarks
//! Should be called before first write() call.
void set_writer(packet::IWriter& writer);

void set_reader(packet::IReader& reader);

//! Get metrics.
core::nanoseconds_t mean_jitter() const;

Expand All @@ -100,6 +109,7 @@ class LinkMeter : public packet::ILinkMeter,
const Encoding* encoding_;

packet::IWriter* writer_;
packet::IReader* reader_;

const audio::SampleSpec sample_spec_;

Expand Down
44 changes: 44 additions & 0 deletions src/public_api/include/roc/metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,50 @@ typedef struct roc_connection_metrics {
* May be zero initially, until enough statistics is accumulated.
*/
unsigned long long e2e_latency;

/** Estimated interarrival jitter, in nanoseconds.
*
* Determines expected variance of inter-packet arrival period.
*
* Estimated on receiver.
*/
unsigned long long mean_jitter;

/** Minimal recent interarrival jitter, in nanoseconds.
*
* Estimated on receiver.
*/
unsigned long long min_jitter;

/** Maximal recent interarrival jitter, in nanoseconds.
*
* Estimated on receiver.
*/
unsigned long long max_jitter;

/** Total amount of packets sent or expected to be received.
*
* On sender, this counter is just incremented every packet.
* On receiver, it is derived from seqnums.
*/
unsigned long long total_packets;

/** 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
* 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.
*/
long long lost_packets;

/** Cumulate count of recovered packets.
*
* How many packets lost packets receiver was able to recover
* by FEC. The sender is not getting this metric so far.
*/
unsigned long long restored_packets;
} roc_connection_metrics;

/** Receiver metrics.
Expand Down
24 changes: 24 additions & 0 deletions src/public_api/src/adapters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,18 @@ void receiver_participant_metrics_to_user(
if (party_metrics.latency.e2e_latency > 0) {
out.e2e_latency = (unsigned long long)party_metrics.latency.e2e_latency;
}

if (party_metrics.link.jitter > 0) {
out.mean_jitter = (unsigned long long)party_metrics.link.jitter;
out.max_jitter = (unsigned long long)party_metrics.link.max_jitter;
out.min_jitter = (unsigned long long)party_metrics.link.min_jitter;
}

if (party_metrics.link.total_packets > 0) {
out.total_packets = (unsigned long long)party_metrics.link.total_packets;
out.lost_packets = (long long)party_metrics.link.lost_packets;
out.restored_packets = (unsigned long long )party_metrics.link.recovered_packets;
}
}

ROC_ATTR_NO_SANITIZE_UB
Expand All @@ -668,6 +680,18 @@ void sender_participant_metrics_to_user(
if (party_metrics.latency.e2e_latency > 0) {
out.e2e_latency = (unsigned long long)party_metrics.latency.e2e_latency;
}

if (party_metrics.link.jitter > 0) {
out.mean_jitter = (unsigned long long)party_metrics.link.jitter;
out.max_jitter = (unsigned long long)party_metrics.link.max_jitter;
out.min_jitter = (unsigned long long)party_metrics.link.min_jitter;
}

if (party_metrics.link.total_packets > 0) {
out.total_packets = (unsigned long long)party_metrics.link.total_packets;
out.lost_packets = (long long)party_metrics.link.lost_packets;
out.restored_packets = (unsigned long long )party_metrics.link.recovered_packets;
}
}

ROC_ATTR_NO_SANITIZE_UB
Expand Down
34 changes: 32 additions & 2 deletions src/tests/public_api/test_loopback_encoder_2_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ TEST_GROUP(loopback_encoder_2_decoder) {
bool leading_zeros = true;

size_t iface_packets[10] = {};
size_t iface_recv_packets_stats = 0;
size_t recovered_packets_stats = 0;
size_t iface_sent_packets_stats = 0;
int64_t recv_lost_packets_stats = 0;
size_t feedback_packets = 0;
size_t zero_samples = 0, total_samples = 0;
size_t n_pkt = 0;
Expand All @@ -108,7 +112,8 @@ TEST_GROUP(loopback_encoder_2_decoder) {
}
}

for (size_t nf = 0; nf < NumFrames || !got_all_metrics; nf++) {
const size_t last_frame = NumFrames - 1;
for (size_t nf = 0; nf <= last_frame || !got_all_metrics; nf++) {
{ // write frame to encoder
float samples[test::FrameSamples] = {};

Expand Down Expand Up @@ -139,7 +144,8 @@ TEST_GROUP(loopback_encoder_2_decoder) {

const bool loss = (flags & FlagLosses)
&& (ifaces[n_if] == ROC_INTERFACE_AUDIO_SOURCE)
&& ((n_pkt + 3) % LossRatio == 0);
&& ((n_pkt + 3) % LossRatio == 0)
&& nf < last_frame;

if (!loss) {
CHECK(roc_receiver_decoder_push_packet(decoder, ifaces[n_if],
Expand Down Expand Up @@ -227,6 +233,12 @@ TEST_GROUP(loopback_encoder_2_decoder) {

max_recv_e2e_latency =
std::max(max_recv_e2e_latency, conn_metrics.e2e_latency);

iface_recv_packets_stats = std::max(iface_recv_packets_stats,
(size_t)conn_metrics.total_packets);
recv_lost_packets_stats = (int64_t)conn_metrics.lost_packets;
recovered_packets_stats = std::max(recovered_packets_stats,
(size_t)conn_metrics.restored_packets);
}
{ // check sender metrics
roc_sender_metrics send_metrics;
Expand All @@ -242,6 +254,10 @@ TEST_GROUP(loopback_encoder_2_decoder) {

max_send_e2e_latency =
std::max(max_send_e2e_latency, conn_metrics.e2e_latency);

iface_sent_packets_stats = std::max(iface_sent_packets_stats,
(size_t)conn_metrics.total_packets);

}
}

Expand All @@ -255,6 +271,20 @@ TEST_GROUP(loopback_encoder_2_decoder) {
// check we have received enough good samples
CHECK(zero_samples < MaxLeadingZeros);

// TODO(gh-674): collect metrics in FeedbackMonitor
for (size_t n_if = 0; n_if < num_ifaces; n_if++) {
if (ifaces[n_if] == ROC_INTERFACE_AUDIO_SOURCE) {
UNSIGNED_LONGS_EQUAL(iface_packets[n_if], iface_recv_packets_stats);
}
}
// check lost packets metrics
UNSIGNED_LONGS_EQUAL(n_lost, recv_lost_packets_stats);
// check recovered packets metrics
CHECK(recovered_packets_stats > 0);
// We don't check the exact metric of recovered packets share as it would take to
// wait till empty receiver's jitterbuffer.
CHECK(recovered_packets_stats <= recv_lost_packets_stats);

// check that there were packets on all active interfaces
for (size_t n_if = 0; n_if < num_ifaces; n_if++) {
CHECK(iface_packets[n_if] > 0);
Expand Down

0 comments on commit c8c4525

Please sign in to comment.