diff --git a/src/internal_modules/roc_core/mov_stats.h b/src/internal_modules/roc_core/mov_stats.h index 52b4bc464..b13f3aba1 100644 --- a/src/internal_modules/roc_core/mov_stats.h +++ b/src/internal_modules/roc_core/mov_stats.h @@ -134,11 +134,7 @@ class MovStats { T mov_var() const { const T n = full_ ? T(win_len_) : T(buffer_i_+1); - if (n == 1) { - return (T)sqrt(movsum2_ - movsum_ * movsum_); - } else { - return (T)sqrt((n*movsum2_ - movsum_ * movsum_) / (n * n)); - } + return (T)sqrt((n*movsum2_ - movsum_ * movsum_) / (n * n)); } T mov_max() const diff --git a/src/internal_modules/roc_rtp/link_meter.cpp b/src/internal_modules/roc_rtp/link_meter.cpp index 0850fa8ce..9f6ae72db 100644 --- a/src/internal_modules/roc_rtp/link_meter.cpp +++ b/src/internal_modules/roc_rtp/link_meter.cpp @@ -211,10 +211,6 @@ core::nanoseconds_t rtp::LinkMeter::mean_jitter() const { return (core::nanoseconds_t)packet_jitter_stats_.mov_avg(); } -core::nanoseconds_t rtp::LinkMeter::var_jitter() const { - return (core::nanoseconds_t)packet_jitter_stats_.mov_var(); -} - size_t LinkMeter::running_window_len() const { return win_len_; } diff --git a/src/internal_modules/roc_rtp/link_meter.h b/src/internal_modules/roc_rtp/link_meter.h index 3bbcf8a26..f30c996a8 100644 --- a/src/internal_modules/roc_rtp/link_meter.h +++ b/src/internal_modules/roc_rtp/link_meter.h @@ -95,7 +95,6 @@ class LinkMeter : public packet::ILinkMeter, //! Get metrics. core::nanoseconds_t mean_jitter() const; - core::nanoseconds_t var_jitter() const; size_t running_window_len() const; diff --git a/src/tests/roc_rtp/test_link_meter.cpp b/src/tests/roc_rtp/test_link_meter.cpp index f50e0e14b..81e90b960 100644 --- a/src/tests/roc_rtp/test_link_meter.cpp +++ b/src/tests/roc_rtp/test_link_meter.cpp @@ -8,6 +8,7 @@ #include +#include "roc_audio/latency_monitor.h" #include "roc_audio/channel_defs.h" #include "roc_audio/sample_spec.h" #include "roc_core/fast_random.h" @@ -27,13 +28,17 @@ core::HeapArena arena; packet::PacketFactory packet_factory(arena); EncodingMap encoding_map(arena); -enum { ChMask = 3, PacketSz = 128, SampleRate = 10000, Duration = 100, RunningWinLen = Duration}; +enum { ChMask = 3, PacketSz = 128, SampleRate = 10000, Duration = 100}; audio::SampleSpec sample_spec(SampleRate, audio::Sample_RawFormat, audio::ChanLayout_Surround, audio::ChanOrder_Smpte, ChMask); const core::nanoseconds_t start_ts = 1691499037871419405; const core::nanoseconds_t step_ts = Duration * core::Second / SampleRate; -packet::PacketPtr new_packet(packet::seqnum_t sn, const core::nanoseconds_t ts) { +const packet::stream_timestamp_t stream_start_ts = 6134803; +const packet::stream_timestamp_t stream_step_ts = Duration; + +packet::PacketPtr new_packet(packet::seqnum_t sn, const core::nanoseconds_t ts, + const packet::stream_timestamp_t stream_ts) { packet::PacketPtr packet = packet_factory.new_packet(); CHECK(packet); @@ -41,6 +46,7 @@ packet::PacketPtr new_packet(packet::seqnum_t sn, const core::nanoseconds_t ts) packet->rtp()->payload_type = PayloadType_L16_Stereo; packet->rtp()->seqnum = sn; packet->rtp()->duration = Duration; + packet->rtp()->stream_timestamp = stream_ts; packet->udp()->enqueue_ts = ts; return packet; @@ -64,45 +70,37 @@ class StatusWriter : public packet::IWriter { TEST_GROUP(link_meter) { }; -TEST(link_meter, has_metrics) { - packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); - meter.set_writer(queue); - - CHECK(!meter.has_metrics()); - - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(100, start_ts))); - UNSIGNED_LONGS_EQUAL(1, queue.size()); - - CHECK(!meter.has_metrics()); -} - TEST(link_meter, last_seqnum) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); meter.set_writer(queue); core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; UNSIGNED_LONGS_EQUAL(0, meter.metrics().ext_last_seqnum); - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(100, ts))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(100, ts, sts))); UNSIGNED_LONGS_EQUAL(100, meter.metrics().ext_last_seqnum); ts += step_ts; + sts += stream_step_ts; // seqnum increased, metric updated - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(102, ts + step_ts))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(102, ts + step_ts, + sts + stream_step_ts))); UNSIGNED_LONGS_EQUAL(102, meter.metrics().ext_last_seqnum); // seqnum decreased, ignored - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(101, ts))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(101, ts, sts))); UNSIGNED_LONGS_EQUAL(102, meter.metrics().ext_last_seqnum); ts += step_ts * 2; + sts += stream_step_ts * 2; // seqnum increased, metric updated - CHECK_EQUAL(status::StatusOK, meter.write(new_packet(103, ts))); + CHECK_EQUAL(status::StatusOK, meter.write(new_packet(103, ts, sts))); CHECK_EQUAL(0, meter.mean_jitter()); - CHECK_EQUAL(0, meter.var_jitter()); const packet::LinkMetrics &metrics = meter.metrics(); CHECK_EQUAL(0, metrics.jitter); @@ -113,30 +111,37 @@ TEST(link_meter, last_seqnum) { TEST(link_meter, last_seqnum_wrap) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); meter.set_writer(queue); core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; UNSIGNED_LONGS_EQUAL(0, meter.metrics().ext_last_seqnum); // no overflow - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65533, ts))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65533, ts, sts))); UNSIGNED_LONGS_EQUAL(65533, meter.metrics().ext_last_seqnum); // no overflow - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65535, ts + step_ts * 2))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65535, ts + step_ts * 2, + sts + stream_step_ts * 2))); UNSIGNED_LONGS_EQUAL(65535, meter.metrics().ext_last_seqnum); // overflow - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(2, ts + step_ts * 3))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(1, ts + step_ts * 3, + sts + stream_step_ts * 3))); UNSIGNED_LONGS_EQUAL(65537, meter.metrics().ext_last_seqnum); // late packet, ignored - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65534, ts + step_ts))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(65534, ts + step_ts, + sts + stream_step_ts))); UNSIGNED_LONGS_EQUAL(65537, meter.metrics().ext_last_seqnum); // new packet - LONGS_EQUAL(status::StatusOK, meter.write(new_packet(5, ts + step_ts * 6))); + LONGS_EQUAL(status::StatusOK, meter.write(new_packet(4, ts + step_ts * 6, + sts + stream_step_ts * 6))); UNSIGNED_LONGS_EQUAL(65540, meter.metrics().ext_last_seqnum); UNSIGNED_LONGS_EQUAL(5, queue.size()); @@ -144,32 +149,42 @@ TEST(link_meter, last_seqnum_wrap) { TEST(link_meter, forward_error) { StatusWriter writer(status::StatusNoMem); - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); meter.set_writer(writer); - CHECK_EQUAL(status::StatusNoMem, meter.write(new_packet(100, start_ts))); + CHECK_EQUAL(status::StatusNoMem, meter.write(new_packet(100, start_ts, + stream_start_ts))); } TEST(link_meter, jitter_test) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); + const ssize_t RunningWinLen = (ssize_t)meter.running_window_len(); meter.set_writer(queue); const size_t num_packets = Duration * 100; core::nanoseconds_t ts_store[num_packets]; core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; for (size_t i = 0; i < num_packets; i++) { packet::seqnum_t seqnum = 65500 + i; ts_store[i] = ts; - CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts))); - ts += step_ts + (core::nanoseconds_t)(core::fast_random_gaussian() * core::Millisecond); + CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts, sts))); + const core::nanoseconds_t jitter_ns = + (core::nanoseconds_t)(core::fast_random_gaussian() * core::Millisecond); + ts += step_ts + jitter_ns; + sts += stream_step_ts; - if (i > RunningWinLen) { + if (i > (size_t)RunningWinLen) { // Check meter metrics running max in min jitter in last Duration number // of packets in ts_store. core::nanoseconds_t min_jitter = core::Second; core::nanoseconds_t max_jitter = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { core::nanoseconds_t jitter = std::abs(ts_store[i - j] - ts_store[i - j - 1] - step_ts); min_jitter = std::min(min_jitter, jitter); max_jitter = std::max(max_jitter, jitter); @@ -179,21 +194,19 @@ TEST(link_meter, jitter_test) { // Reference average and variance of jitter from ts_store values. core::nanoseconds_t sum = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { sum += std::abs(ts_store[i - j] - ts_store[ i - j - 1] - step_ts); } const core::nanoseconds_t mean = sum / RunningWinLen; sum = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { core::nanoseconds_t jitter = std::abs(ts_store[i - j] - ts_store[i - j - 1] - step_ts); sum += (jitter - mean) * (jitter - mean); } - const core::nanoseconds_t var = sum / RunningWinLen; // Check the jitter value DOUBLES_EQUAL(mean, meter.mean_jitter(), core::Microsecond * 1); - DOUBLES_EQUAL(sqrt(var), meter.var_jitter(), core::Microsecond * 1); } } @@ -201,24 +214,29 @@ TEST(link_meter, jitter_test) { TEST(link_meter, ascending_test) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); + const ssize_t RunningWinLen = (ssize_t)meter.running_window_len(); meter.set_writer(queue); const size_t num_packets = Duration * 100; core::nanoseconds_t ts_store[num_packets]; core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; for (size_t i = 0; i < num_packets; i++) { packet::seqnum_t seqnum = 65500 + i; ts_store[i] = ts; - CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts))); + CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts, sts))); ts += step_ts + (core::nanoseconds_t)i * core::Microsecond; // Removed the random component to create an increasing sequence + sts += stream_step_ts; - if (i > RunningWinLen) { + if (i > (size_t)RunningWinLen) { // Check meter metrics running max in min jitter in last Duration number // of packets in ts_store. core::nanoseconds_t min_jitter = core::Second; core::nanoseconds_t max_jitter = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { core::nanoseconds_t jitter = std::abs(ts_store[i - j] - ts_store[i - j - 1] - step_ts); min_jitter = std::min(min_jitter, jitter); max_jitter = std::max(max_jitter, jitter); @@ -231,24 +249,29 @@ TEST(link_meter, ascending_test) { TEST(link_meter, descending_test) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); + const ssize_t RunningWinLen = (ssize_t)meter.running_window_len(); meter.set_writer(queue); const size_t num_packets = Duration * 100; core::nanoseconds_t ts_store[num_packets]; core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; for (size_t i = 0; i < num_packets; i++) { packet::seqnum_t seqnum = 65500 + i; ts_store[i] = ts; - CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts))); + CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts, sts))); ts += step_ts - (core::nanoseconds_t)i * core::Nanosecond * 10; // Removed the random component to create an increasing sequence + sts += stream_step_ts; - if (i > RunningWinLen) { + if (i > (size_t)RunningWinLen) { // Check meter metrics running max in min jitter in last Duration number // of packets in ts_store. core::nanoseconds_t min_jitter = core::Second; core::nanoseconds_t max_jitter = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { core::nanoseconds_t jitter = std::abs(ts_store[i - j] - ts_store[i - j - 1] - step_ts); min_jitter = std::min(min_jitter, jitter); max_jitter = std::max(max_jitter, jitter); @@ -261,7 +284,10 @@ TEST(link_meter, descending_test) { TEST(link_meter, saw_test) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); + const ssize_t RunningWinLen = (ssize_t)meter.running_window_len(); meter.set_writer(queue); const size_t num_packets = Duration * 100; core::nanoseconds_t ts_store[num_packets]; @@ -269,22 +295,24 @@ TEST(link_meter, saw_test) { core::nanoseconds_t step_ts_ = step_ts; core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; for (size_t i = 0; i < num_packets; i++) { packet::seqnum_t seqnum = 65500 + i; ts_store[i] = ts; - CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts))); + CHECK_EQUAL(status::StatusOK, meter.write(new_packet(seqnum, ts, sts))); ts += step_ts_; + sts += stream_step_ts; step_ts_ += step_ts_inc; - if (i > 0 && i % RunningWinLen == 0) { + if (i > 0 && i % (size_t)RunningWinLen == 0) { step_ts_inc = -step_ts_inc; } - if (i > RunningWinLen) { + if (i > (size_t)RunningWinLen) { // Check meter metrics running max in min jitter in last Duration number // of packets in ts_store. core::nanoseconds_t min_jitter = core::Second; core::nanoseconds_t max_jitter = 0; - for (size_t j = 0; j < RunningWinLen; j++) { + for (size_t j = 0; j < (size_t)RunningWinLen; j++) { core::nanoseconds_t jitter = std::abs(ts_store[i - j] - ts_store[i - j - 1] - step_ts); min_jitter = std::min(min_jitter, jitter); max_jitter = std::max(max_jitter, jitter); @@ -297,24 +325,29 @@ TEST(link_meter, saw_test) { TEST(link_meter, losses_test) { packet::Queue queue; - LinkMeter meter(arena, encoding_map, sample_spec, RunningWinLen); + audio::LatencyConfig latency_config; + latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; + LinkMeter meter(arena, encoding_map, sample_spec, latency_config); + meter.set_writer(queue); meter.set_reader(queue); const size_t num_packets = Duration * 100; size_t total_losses = 0; size_t fract_losses_cntr = 0; core::nanoseconds_t ts = start_ts; + packet::stream_timestamp_t sts = stream_start_ts; for (size_t i = 0; i < num_packets; i++) { packet::seqnum_t seqnum = 65500 + i; - packet::PacketPtr p = new_packet(seqnum, ts); + packet::PacketPtr p = new_packet(seqnum, ts, sts); ts += step_ts; + sts += stream_step_ts; if (i > 0 && core::fast_random_range(0, 100) < 30) { total_losses++; fract_losses_cntr++; continue; } else { - CHECK_EQUAL(status::StatusOK, queue.write(p)); + CHECK_EQUAL(status::StatusOK, meter.write(p)); } packet::PacketPtr pr;