From a2af0008820babcef23aef399fe838460ce7cf71 Mon Sep 17 00:00:00 2001 From: Henrik Lundin Date: Tue, 20 Jun 2017 16:54:39 +0200 Subject: [PATCH] Improve the simulation stats aggregation in neteq_rtpplay The network stats used to be polled from the NetEq object once at the very end of the simulation. With this change, the stats are polled once every second, and then aggregated at the end of the run. This leads to more meaningful numbers. Bug: webrtc:2692 Change-Id: I9e0f4ddada2f9e42fb9234970deb1af235fffc8c Reviewed-on: https://chromium-review.googlesource.com/541218 Commit-Queue: Henrik Lundin Reviewed-by: Ivo Creusen Cr-Commit-Position: refs/heads/master@{#18682} --- .../audio_coding/neteq/tools/neteq_rtpplay.cc | 146 +++++++++++++++--- 1 file changed, 127 insertions(+), 19 deletions(-) diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc index d468c6f549..c9082f4a01 100644 --- a/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc +++ b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "gflags/gflags.h" @@ -319,10 +320,11 @@ class SsrcSwitchDetector : public NetEqPostInsertPacket { // Takes a pointer to another callback object, which will be invoked after // this object finishes. This does not transfer ownership, and null is a // valid value. - SsrcSwitchDetector(NetEqPostInsertPacket* other_callback) + explicit SsrcSwitchDetector(NetEqPostInsertPacket* other_callback) : other_callback_(other_callback) {} - void AfterInsertPacket(const NetEqInput::PacketData& packet, NetEq* neteq) { + void AfterInsertPacket(const NetEqInput::PacketData& packet, + NetEq* neteq) override { if (last_ssrc_ && packet.header.ssrc != *last_ssrc_) { std::cout << "Changing streams from 0x" << std::hex << *last_ssrc_ << " to 0x" << std::hex << packet.header.ssrc @@ -341,6 +343,114 @@ class SsrcSwitchDetector : public NetEqPostInsertPacket { rtc::Optional last_ssrc_; }; +class StatsGetter : public NetEqGetAudioCallback { + public: + // This struct is a replica of webrtc::NetEqNetworkStatistics, but with all + // values stored in double precision. + struct Stats { + double current_buffer_size_ms = 0.0; + double preferred_buffer_size_ms = 0.0; + double jitter_peaks_found = 0.0; + double packet_loss_rate = 0.0; + double packet_discard_rate = 0.0; + double expand_rate = 0.0; + double speech_expand_rate = 0.0; + double preemptive_rate = 0.0; + double accelerate_rate = 0.0; + double secondary_decoded_rate = 0.0; + double clockdrift_ppm = 0.0; + double added_zero_samples = 0.0; + double mean_waiting_time_ms = 0.0; + double median_waiting_time_ms = 0.0; + double min_waiting_time_ms = 0.0; + double max_waiting_time_ms = 0.0; + }; + + // Takes a pointer to another callback object, which will be invoked after + // this object finishes. This does not transfer ownership, and null is a + // valid value. + explicit StatsGetter(NetEqGetAudioCallback* other_callback) + : other_callback_(other_callback) {} + + void BeforeGetAudio(NetEq* neteq) override { + if (other_callback_) { + other_callback_->BeforeGetAudio(neteq); + } + } + + void AfterGetAudio(int64_t time_now_ms, + const AudioFrame& audio_frame, + bool muted, + NetEq* neteq) override { + if (++counter_ >= 100) { + counter_ = 0; + NetEqNetworkStatistics stats; + RTC_CHECK_EQ(neteq->NetworkStatistics(&stats), 0); + stats_.push_back(stats); + } + if (other_callback_) { + other_callback_->BeforeGetAudio(neteq); + } + } + + double AverageSpeechExpandRate() const { + double sum_speech_expand = + std::accumulate(stats_.begin(), stats_.end(), double{0.0}, + [](double a, NetEqNetworkStatistics b) { + return a + static_cast(b.speech_expand_rate); + }); + return sum_speech_expand / 16384.0 / stats_.size(); + } + + Stats AverageStats() const { + Stats sum_stats = std::accumulate( + stats_.begin(), stats_.end(), Stats(), + [](Stats a, NetEqNetworkStatistics b) { + a.current_buffer_size_ms += b.current_buffer_size_ms; + a.preferred_buffer_size_ms += b.preferred_buffer_size_ms; + a.jitter_peaks_found += b.jitter_peaks_found; + a.packet_loss_rate += b.packet_loss_rate / 16384.0; + a.packet_discard_rate += b.packet_discard_rate / 16384.0; + a.expand_rate += b.expand_rate / 16384.0; + a.speech_expand_rate += b.speech_expand_rate / 16384.0; + a.preemptive_rate += b.preemptive_rate / 16384.0; + a.accelerate_rate += b.accelerate_rate / 16384.0; + a.secondary_decoded_rate += b.secondary_decoded_rate / 16384.0; + a.clockdrift_ppm += b.clockdrift_ppm; + a.added_zero_samples += b.added_zero_samples; + a.mean_waiting_time_ms += b.mean_waiting_time_ms; + a.median_waiting_time_ms += b.median_waiting_time_ms; + a.min_waiting_time_ms += b.min_waiting_time_ms; + a.max_waiting_time_ms += b.max_waiting_time_ms; + return a; + }); + + sum_stats.current_buffer_size_ms /= stats_.size(); + sum_stats.preferred_buffer_size_ms /= stats_.size(); + sum_stats.jitter_peaks_found /= stats_.size(); + sum_stats.packet_loss_rate /= stats_.size(); + sum_stats.packet_discard_rate /= stats_.size(); + sum_stats.expand_rate /= stats_.size(); + sum_stats.speech_expand_rate /= stats_.size(); + sum_stats.preemptive_rate /= stats_.size(); + sum_stats.accelerate_rate /= stats_.size(); + sum_stats.secondary_decoded_rate /= stats_.size(); + sum_stats.clockdrift_ppm /= stats_.size(); + sum_stats.added_zero_samples /= stats_.size(); + sum_stats.mean_waiting_time_ms /= stats_.size(); + sum_stats.median_waiting_time_ms /= stats_.size(); + sum_stats.min_waiting_time_ms /= stats_.size(); + sum_stats.max_waiting_time_ms /= stats_.size(); + + return sum_stats; + } + + private: + NetEqGetAudioCallback* other_callback_; + size_t counter_ = 0; + std::vector stats_; +}; + int RunTest(int argc, char* argv[]) { std::string program_name = argv[0]; std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" @@ -515,14 +625,14 @@ int RunTest(int argc, char* argv[]) { SsrcSwitchDetector ssrc_switch_detector(delay_analyzer.get()); callbacks.post_insert_packet = &ssrc_switch_detector; - callbacks.get_audio_callback = delay_analyzer.get(); + StatsGetter stats_getter(delay_analyzer.get()); + callbacks.get_audio_callback = &stats_getter; NetEq::Config config; config.sample_rate_hz = *sample_rate_hz; NetEqTest test(config, codecs, ext_codecs, std::move(input), std::move(output), callbacks); int64_t test_duration_ms = test.Run(); - NetEqNetworkStatistics stats = test.SimulationStats(); if (FLAGS_matlabplot) { std::cout << "Creating Matlab plot script " << output_file_name + ".m" @@ -532,22 +642,20 @@ int RunTest(int argc, char* argv[]) { printf("Simulation statistics:\n"); printf(" output duration: %" PRId64 " ms\n", test_duration_ms); - printf(" packet_loss_rate: %f %%\n", - 100.0 * stats.packet_loss_rate / 16384.0); - printf(" packet_discard_rate: %f %%\n", - 100.0 * stats.packet_discard_rate / 16384.0); - printf(" expand_rate: %f %%\n", 100.0 * stats.expand_rate / 16384.0); - printf(" speech_expand_rate: %f %%\n", - 100.0 * stats.speech_expand_rate / 16384.0); - printf(" preemptive_rate: %f %%\n", 100.0 * stats.preemptive_rate / 16384.0); - printf(" accelerate_rate: %f %%\n", 100.0 * stats.accelerate_rate / 16384.0); + auto stats = stats_getter.AverageStats(); + printf(" packet_loss_rate: %f %%\n", 100.0 * stats.packet_loss_rate); + printf(" packet_discard_rate: %f %%\n", 100.0 * stats.packet_discard_rate); + printf(" expand_rate: %f %%\n", 100.0 * stats.expand_rate); + printf(" speech_expand_rate: %f %%\n", 100.0 * stats.speech_expand_rate); + printf(" preemptive_rate: %f %%\n", 100.0 * stats.preemptive_rate); + printf(" accelerate_rate: %f %%\n", 100.0 * stats.accelerate_rate); printf(" secondary_decoded_rate: %f %%\n", - 100.0 * stats.secondary_decoded_rate / 16384.0); - printf(" clockdrift_ppm: %d ppm\n", stats.clockdrift_ppm); - printf(" mean_waiting_time_ms: %d ms\n", stats.mean_waiting_time_ms); - printf(" median_waiting_time_ms: %d ms\n", stats.median_waiting_time_ms); - printf(" min_waiting_time_ms: %d ms\n", stats.min_waiting_time_ms); - printf(" max_waiting_time_ms: %d ms\n", stats.max_waiting_time_ms); + 100.0 * stats.secondary_decoded_rate); + printf(" clockdrift_ppm: %f ppm\n", stats.clockdrift_ppm); + printf(" mean_waiting_time_ms: %f ms\n", stats.mean_waiting_time_ms); + printf(" median_waiting_time_ms: %f ms\n", stats.median_waiting_time_ms); + printf(" min_waiting_time_ms: %f ms\n", stats.min_waiting_time_ms); + printf(" max_waiting_time_ms: %f ms\n", stats.max_waiting_time_ms); return 0; }