Seperate NetEq stats getter to use in other tools.
Bug: webrtc:9147
Change-Id: I251618bbb542d89b3d38c3ea424b1e55c0a5f2b2
Reviewed-on: https://webrtc-review.googlesource.com/69806
Commit-Queue: Minyue Li <minyue@webrtc.org>
Reviewed-by: Alex Narest <alexnarest@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Benjamin Wright <benwright@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22971}
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index 31c1f6f..7cea425 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -1210,6 +1210,8 @@
"neteq/tools/neteq_delay_analyzer.h",
"neteq/tools/neteq_replacement_input.cc",
"neteq/tools/neteq_replacement_input.h",
+ "neteq/tools/neteq_stats_getter.cc",
+ "neteq/tools/neteq_stats_getter.h",
]
public_configs = [ ":neteq_tools_config" ]
diff --git a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
index fb937cd..7c071db 100644
--- a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
+++ b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
@@ -10,22 +10,18 @@
#include <errno.h>
#include <inttypes.h>
+#include <iostream>
#include <limits.h> // For ULONG_MAX returned by strtoul.
+#include <memory>
#include <stdio.h>
#include <stdlib.h> // For strtoul.
-#include <string.h>
-
-#include <algorithm>
-#include <ios>
-#include <iostream>
-#include <memory>
-#include <numeric>
#include <string>
#include "modules/audio_coding/neteq/include/neteq.h"
#include "modules/audio_coding/neteq/tools/fake_decode_from_file.h"
#include "modules/audio_coding/neteq/tools/input_audio_file.h"
#include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
+#include "modules/audio_coding/neteq/tools/neteq_stats_getter.h"
#include "modules/audio_coding/neteq/tools/neteq_packet_source_input.h"
#include "modules/audio_coding/neteq/tools/neteq_replacement_input.h"
#include "modules/audio_coding/neteq/tools/neteq_test.h"
@@ -308,168 +304,6 @@
rtc::Optional<uint32_t> 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 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 secondary_discarded_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;
- };
-
- struct ConcealmentEvent {
- uint64_t duration_ms;
- size_t concealment_event_number;
- int64_t time_from_previous_event_end_ms;
-
- friend std::ostream& operator<<(std::ostream& stream,
- const ConcealmentEvent& concealment_event) {
- stream << "ConcealmentEvent duration_ms:" << concealment_event.duration_ms
- << " event_number:" << concealment_event.concealment_event_number
- << " time_from_previous_event_end_ms:"
- << concealment_event.time_from_previous_event_end_ms << "\n";
- return stream;
- }
- };
-
- // 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);
- }
- const auto lifetime_stat = neteq->GetLifetimeStatistics();
- if (current_concealment_event_ != lifetime_stat.concealment_events &&
- voice_concealed_samples_until_last_event_ <
- lifetime_stat.voice_concealed_samples) {
- if (last_event_end_time_ms_ > 0) {
- // Do not account for the first event to avoid start of the call
- // skewing.
- ConcealmentEvent concealment_event;
- uint64_t last_event_voice_concealed_samples =
- lifetime_stat.voice_concealed_samples -
- voice_concealed_samples_until_last_event_;
- RTC_CHECK_GT(last_event_voice_concealed_samples, 0);
- concealment_event.duration_ms = last_event_voice_concealed_samples /
- (audio_frame.sample_rate_hz_ / 1000);
- concealment_event.concealment_event_number = current_concealment_event_;
- concealment_event.time_from_previous_event_end_ms =
- time_now_ms - last_event_end_time_ms_;
- concealment_events_.emplace_back(concealment_event);
- voice_concealed_samples_until_last_event_ =
- lifetime_stat.voice_concealed_samples;
- }
- last_event_end_time_ms_ = time_now_ms;
- voice_concealed_samples_until_last_event_ =
- lifetime_stat.voice_concealed_samples;
- current_concealment_event_ = lifetime_stat.concealment_events;
- }
-
- if (other_callback_) {
- other_callback_->AfterGetAudio(time_now_ms, audio_frame, muted, 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<double>(b.speech_expand_rate);
- });
- return sum_speech_expand / 16384.0 / stats_.size();
- }
-
- const std::vector<ConcealmentEvent>& concealment_events() {
- // Do not account for the last concealment event to avoid potential end
- // call skewing.
- return concealment_events_;
- }
-
- 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.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.secondary_discarded_rate += b.secondary_discarded_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 =
- std::min(a.min_waiting_time_ms,
- static_cast<double>(b.min_waiting_time_ms));
- a.max_waiting_time_ms =
- std::max(a.max_waiting_time_ms,
- static_cast<double>(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.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.secondary_discarded_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();
-
- return sum_stats;
- }
-
- private:
- NetEqGetAudioCallback* other_callback_;
- size_t counter_ = 0;
- std::vector<NetEqNetworkStatistics> stats_;
- size_t current_concealment_event_ = 1;
- uint64_t voice_concealed_samples_until_last_event_ = 0;
- std::vector<ConcealmentEvent> concealment_events_;
- int64_t last_event_end_time_ms_ = 0;
-};
-
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"
@@ -675,7 +509,7 @@
SsrcSwitchDetector ssrc_switch_detector(delay_analyzer.get());
callbacks.post_insert_packet = &ssrc_switch_detector;
- StatsGetter stats_getter(delay_analyzer.get());
+ NetEqStatsGetter stats_getter(std::move(delay_analyzer));
callbacks.get_audio_callback = &stats_getter;
NetEq::Config config;
config.sample_rate_hz = *sample_rate_hz;
@@ -721,11 +555,10 @@
printf(" current_buffer_size_ms: %f ms\n", stats.current_buffer_size_ms);
printf(" preferred_buffer_size_ms: %f ms\n", stats.preferred_buffer_size_ms);
if (FLAG_concealment_events) {
- std::cout << " concealment_events_ms:"
- << "\n";
+ std::cout << " concealment_events_ms:" << std::endl;
for (auto concealment_event : stats_getter.concealment_events())
- std::cout << concealment_event;
- std::cout << " end of concealment_events_ms\n";
+ std::cout << concealment_event.ToString() << std::endl;
+ std::cout << " end of concealment_events_ms" << std::endl;
}
return 0;
}
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_getter.cc b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
new file mode 100644
index 0000000..12e10b6
--- /dev/null
+++ b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_coding/neteq/tools/neteq_stats_getter.h"
+
+#include <algorithm>
+#include <numeric>
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+namespace test {
+
+std::string NetEqStatsGetter::ConcealmentEvent::ToString() const {
+ char ss_buf[256];
+ rtc::SimpleStringBuilder ss(ss_buf);
+ ss << "ConcealmentEvent duration_ms:" << duration_ms
+ << " event_number:" << concealment_event_number
+ << " time_from_previous_event_end_ms:"
+ << time_from_previous_event_end_ms;
+ return ss.str();
+}
+
+NetEqStatsGetter::NetEqStatsGetter(
+ std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer)
+ : delay_analyzer_(std::move(delay_analyzer)) {}
+
+void NetEqStatsGetter::BeforeGetAudio(NetEq* neteq) {
+ if (delay_analyzer_) {
+ delay_analyzer_->BeforeGetAudio(neteq);
+ }
+}
+
+void NetEqStatsGetter::AfterGetAudio(int64_t time_now_ms,
+ const AudioFrame& audio_frame,
+ bool muted,
+ NetEq* neteq) {
+ if (++counter_ >= 100) {
+ counter_ = 0;
+ NetEqNetworkStatistics stats;
+ RTC_CHECK_EQ(neteq->NetworkStatistics(&stats), 0);
+ stats_.push_back(stats);
+ }
+ const auto lifetime_stat = neteq->GetLifetimeStatistics();
+ if (current_concealment_event_ != lifetime_stat.concealment_events &&
+ voice_concealed_samples_until_last_event_ <
+ lifetime_stat.voice_concealed_samples) {
+ if (last_event_end_time_ms_ > 0) {
+ // Do not account for the first event to avoid start of the call
+ // skewing.
+ ConcealmentEvent concealment_event;
+ uint64_t last_event_voice_concealed_samples =
+ lifetime_stat.voice_concealed_samples -
+ voice_concealed_samples_until_last_event_;
+ RTC_CHECK_GT(last_event_voice_concealed_samples, 0);
+ concealment_event.duration_ms = last_event_voice_concealed_samples /
+ (audio_frame.sample_rate_hz_ / 1000);
+ concealment_event.concealment_event_number = current_concealment_event_;
+ concealment_event.time_from_previous_event_end_ms =
+ time_now_ms - last_event_end_time_ms_;
+ concealment_events_.emplace_back(concealment_event);
+ voice_concealed_samples_until_last_event_ =
+ lifetime_stat.voice_concealed_samples;
+ }
+ last_event_end_time_ms_ = time_now_ms;
+ voice_concealed_samples_until_last_event_ =
+ lifetime_stat.voice_concealed_samples;
+ current_concealment_event_ = lifetime_stat.concealment_events;
+ }
+
+ if (delay_analyzer_) {
+ delay_analyzer_->AfterGetAudio(time_now_ms, audio_frame, muted, neteq);
+ }
+}
+
+double NetEqStatsGetter::AverageSpeechExpandRate() const {
+ double sum_speech_expand =
+ std::accumulate(stats_.begin(), stats_.end(), double{0.0},
+ [](double a, NetEqNetworkStatistics b) {
+ return a + static_cast<double>(b.speech_expand_rate);
+ });
+ return sum_speech_expand / 16384.0 / stats_.size();
+}
+
+NetEqStatsGetter::Stats NetEqStatsGetter::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.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.secondary_discarded_rate += b.secondary_discarded_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 =
+ std::min(a.min_waiting_time_ms,
+ static_cast<double>(b.min_waiting_time_ms));
+ a.max_waiting_time_ms =
+ std::max(a.max_waiting_time_ms,
+ static_cast<double>(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.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.secondary_discarded_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();
+
+ return sum_stats;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_getter.h b/modules/audio_coding/neteq/tools/neteq_stats_getter.h
new file mode 100644
index 0000000..07d53c7
--- /dev/null
+++ b/modules/audio_coding/neteq/tools/neteq_stats_getter.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_STATS_GETTER_H_
+#define MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_STATS_GETTER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
+#include "modules/audio_coding/neteq/tools/neteq_test.h"
+
+namespace webrtc {
+namespace test {
+
+class NetEqStatsGetter : 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 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 secondary_discarded_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;
+ };
+
+ struct ConcealmentEvent {
+ uint64_t duration_ms;
+ size_t concealment_event_number;
+ int64_t time_from_previous_event_end_ms;
+ std::string ToString() const;
+ };
+
+ // 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 NetEqStatsGetter(std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer);
+
+ void BeforeGetAudio(NetEq* neteq) override;
+
+ void AfterGetAudio(int64_t time_now_ms,
+ const AudioFrame& audio_frame,
+ bool muted,
+ NetEq* neteq) override;
+
+ double AverageSpeechExpandRate() const;
+
+ NetEqDelayAnalyzer* delay_analyzer() const {
+ return delay_analyzer_.get();
+ }
+
+ const std::vector<ConcealmentEvent>& concealment_events() const {
+ // Do not account for the last concealment event to avoid potential end
+ // call skewing.
+ return concealment_events_;
+ }
+
+ Stats AverageStats() const;
+
+ private:
+ std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer_;
+ size_t counter_ = 0;
+ std::vector<NetEqNetworkStatistics> stats_;
+ size_t current_concealment_event_ = 1;
+ uint64_t voice_concealed_samples_until_last_event_ = 0;
+ std::vector<ConcealmentEvent> concealment_events_;
+ int64_t last_event_end_time_ms_ = 0;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_STATS_GETTER_H_