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_