1 /*
2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "webrtc/modules/audio_coding/neteq/statistics_calculator.h"
12 
13 #include <assert.h>
14 #include <string.h>  // memset
15 #include <algorithm>
16 
17 #include "webrtc/base/checks.h"
18 #include "webrtc/base/safe_conversions.h"
19 #include "webrtc/modules/audio_coding/neteq/decision_logic.h"
20 #include "webrtc/modules/audio_coding/neteq/delay_manager.h"
21 #include "webrtc/system_wrappers/include/metrics.h"
22 
23 namespace webrtc {
24 
25 // Allocating the static const so that it can be passed by reference to
26 // RTC_DCHECK.
27 const size_t StatisticsCalculator::kLenWaitingTimes;
28 
PeriodicUmaLogger(const std::string & uma_name,int report_interval_ms,int max_value)29 StatisticsCalculator::PeriodicUmaLogger::PeriodicUmaLogger(
30     const std::string& uma_name,
31     int report_interval_ms,
32     int max_value)
33     : uma_name_(uma_name),
34       report_interval_ms_(report_interval_ms),
35       max_value_(max_value),
36       timer_(0) {
37 }
38 
39 StatisticsCalculator::PeriodicUmaLogger::~PeriodicUmaLogger() = default;
40 
AdvanceClock(int step_ms)41 void StatisticsCalculator::PeriodicUmaLogger::AdvanceClock(int step_ms) {
42   timer_ += step_ms;
43   if (timer_ < report_interval_ms_) {
44     return;
45   }
46   LogToUma(Metric());
47   Reset();
48   timer_ -= report_interval_ms_;
49   RTC_DCHECK_GE(timer_, 0);
50 }
51 
LogToUma(int value) const52 void StatisticsCalculator::PeriodicUmaLogger::LogToUma(int value) const {
53   RTC_HISTOGRAM_COUNTS_SPARSE(uma_name_, value, 1, max_value_, 50);
54 }
55 
PeriodicUmaCount(const std::string & uma_name,int report_interval_ms,int max_value)56 StatisticsCalculator::PeriodicUmaCount::PeriodicUmaCount(
57     const std::string& uma_name,
58     int report_interval_ms,
59     int max_value)
60     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
61 }
62 
~PeriodicUmaCount()63 StatisticsCalculator::PeriodicUmaCount::~PeriodicUmaCount() {
64   // Log the count for the current (incomplete) interval.
65   LogToUma(Metric());
66 }
67 
RegisterSample()68 void StatisticsCalculator::PeriodicUmaCount::RegisterSample() {
69   ++counter_;
70 }
71 
Metric() const72 int StatisticsCalculator::PeriodicUmaCount::Metric() const {
73   return counter_;
74 }
75 
Reset()76 void StatisticsCalculator::PeriodicUmaCount::Reset() {
77   counter_ = 0;
78 }
79 
PeriodicUmaAverage(const std::string & uma_name,int report_interval_ms,int max_value)80 StatisticsCalculator::PeriodicUmaAverage::PeriodicUmaAverage(
81     const std::string& uma_name,
82     int report_interval_ms,
83     int max_value)
84     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
85 }
86 
~PeriodicUmaAverage()87 StatisticsCalculator::PeriodicUmaAverage::~PeriodicUmaAverage() {
88   // Log the average for the current (incomplete) interval.
89   LogToUma(Metric());
90 }
91 
RegisterSample(int value)92 void StatisticsCalculator::PeriodicUmaAverage::RegisterSample(int value) {
93   sum_ += value;
94   ++counter_;
95 }
96 
Metric() const97 int StatisticsCalculator::PeriodicUmaAverage::Metric() const {
98   return static_cast<int>(sum_ / counter_);
99 }
100 
Reset()101 void StatisticsCalculator::PeriodicUmaAverage::Reset() {
102   sum_ = 0.0;
103   counter_ = 0;
104 }
105 
StatisticsCalculator()106 StatisticsCalculator::StatisticsCalculator()
107     : preemptive_samples_(0),
108       accelerate_samples_(0),
109       added_zero_samples_(0),
110       expanded_speech_samples_(0),
111       expanded_noise_samples_(0),
112       discarded_packets_(0),
113       lost_timestamps_(0),
114       timestamps_since_last_report_(0),
115       secondary_decoded_samples_(0),
116       delayed_packet_outage_counter_(
117           "WebRTC.Audio.DelayedPacketOutageEventsPerMinute",
118           60000,  // 60 seconds report interval.
119           100),
120       excess_buffer_delay_("WebRTC.Audio.AverageExcessBufferDelayMs",
121                            60000,  // 60 seconds report interval.
122                            1000) {
123 }
124 
125 StatisticsCalculator::~StatisticsCalculator() = default;
126 
Reset()127 void StatisticsCalculator::Reset() {
128   preemptive_samples_ = 0;
129   accelerate_samples_ = 0;
130   added_zero_samples_ = 0;
131   expanded_speech_samples_ = 0;
132   expanded_noise_samples_ = 0;
133   secondary_decoded_samples_ = 0;
134   waiting_times_.clear();
135 }
136 
ResetMcu()137 void StatisticsCalculator::ResetMcu() {
138   discarded_packets_ = 0;
139   lost_timestamps_ = 0;
140   timestamps_since_last_report_ = 0;
141 }
142 
ExpandedVoiceSamples(size_t num_samples)143 void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples) {
144   expanded_speech_samples_ += num_samples;
145 }
146 
ExpandedNoiseSamples(size_t num_samples)147 void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples) {
148   expanded_noise_samples_ += num_samples;
149 }
150 
PreemptiveExpandedSamples(size_t num_samples)151 void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
152   preemptive_samples_ += num_samples;
153 }
154 
AcceleratedSamples(size_t num_samples)155 void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
156   accelerate_samples_ += num_samples;
157 }
158 
AddZeros(size_t num_samples)159 void StatisticsCalculator::AddZeros(size_t num_samples) {
160   added_zero_samples_ += num_samples;
161 }
162 
PacketsDiscarded(size_t num_packets)163 void StatisticsCalculator::PacketsDiscarded(size_t num_packets) {
164   discarded_packets_ += num_packets;
165 }
166 
LostSamples(size_t num_samples)167 void StatisticsCalculator::LostSamples(size_t num_samples) {
168   lost_timestamps_ += num_samples;
169 }
170 
IncreaseCounter(size_t num_samples,int fs_hz)171 void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
172   const int time_step_ms =
173       rtc::CheckedDivExact(static_cast<int>(1000 * num_samples), fs_hz);
174   delayed_packet_outage_counter_.AdvanceClock(time_step_ms);
175   excess_buffer_delay_.AdvanceClock(time_step_ms);
176   timestamps_since_last_report_ += static_cast<uint32_t>(num_samples);
177   if (timestamps_since_last_report_ >
178       static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
179     lost_timestamps_ = 0;
180     timestamps_since_last_report_ = 0;
181     discarded_packets_ = 0;
182   }
183 }
184 
SecondaryDecodedSamples(int num_samples)185 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
186   secondary_decoded_samples_ += num_samples;
187 }
188 
LogDelayedPacketOutageEvent(int outage_duration_ms)189 void StatisticsCalculator::LogDelayedPacketOutageEvent(int outage_duration_ms) {
190   RTC_HISTOGRAM_COUNTS_SPARSE("WebRTC.Audio.DelayedPacketOutageEventMs",
191                               outage_duration_ms, 1 /* min */, 2000 /* max */,
192                               100 /* bucket count */);
193   delayed_packet_outage_counter_.RegisterSample();
194 }
195 
StoreWaitingTime(int waiting_time_ms)196 void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
197   excess_buffer_delay_.RegisterSample(waiting_time_ms);
198   RTC_DCHECK_LE(waiting_times_.size(), kLenWaitingTimes);
199   if (waiting_times_.size() == kLenWaitingTimes) {
200     // Erase first value.
201     waiting_times_.pop_front();
202   }
203   waiting_times_.push_back(waiting_time_ms);
204 }
205 
GetNetworkStatistics(int fs_hz,size_t num_samples_in_buffers,size_t samples_per_packet,const DelayManager & delay_manager,const DecisionLogic & decision_logic,NetEqNetworkStatistics * stats)206 void StatisticsCalculator::GetNetworkStatistics(
207     int fs_hz,
208     size_t num_samples_in_buffers,
209     size_t samples_per_packet,
210     const DelayManager& delay_manager,
211     const DecisionLogic& decision_logic,
212     NetEqNetworkStatistics *stats) {
213   if (fs_hz <= 0 || !stats) {
214     assert(false);
215     return;
216   }
217 
218   stats->added_zero_samples = added_zero_samples_;
219   stats->current_buffer_size_ms =
220       static_cast<uint16_t>(num_samples_in_buffers * 1000 / fs_hz);
221   const int ms_per_packet = rtc::checked_cast<int>(
222       decision_logic.packet_length_samples() / (fs_hz / 1000));
223   stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) *
224       ms_per_packet;
225   stats->jitter_peaks_found = delay_manager.PeakFound();
226   stats->clockdrift_ppm = delay_manager.AverageIAT();
227 
228   stats->packet_loss_rate =
229       CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_);
230 
231   const size_t discarded_samples = discarded_packets_ * samples_per_packet;
232   stats->packet_discard_rate =
233       CalculateQ14Ratio(discarded_samples, timestamps_since_last_report_);
234 
235   stats->accelerate_rate =
236       CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);
237 
238   stats->preemptive_rate =
239       CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);
240 
241   stats->expand_rate =
242       CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
243                         timestamps_since_last_report_);
244 
245   stats->speech_expand_rate =
246       CalculateQ14Ratio(expanded_speech_samples_,
247                         timestamps_since_last_report_);
248 
249   stats->secondary_decoded_rate =
250       CalculateQ14Ratio(secondary_decoded_samples_,
251                         timestamps_since_last_report_);
252 
253   if (waiting_times_.size() == 0) {
254     stats->mean_waiting_time_ms = -1;
255     stats->median_waiting_time_ms = -1;
256     stats->min_waiting_time_ms = -1;
257     stats->max_waiting_time_ms = -1;
258   } else {
259     std::sort(waiting_times_.begin(), waiting_times_.end());
260     // Find mid-point elements. If the size is odd, the two values
261     // |middle_left| and |middle_right| will both be the one middle element; if
262     // the size is even, they will be the the two neighboring elements at the
263     // middle of the list.
264     const int middle_left = waiting_times_[(waiting_times_.size() - 1) / 2];
265     const int middle_right = waiting_times_[waiting_times_.size() / 2];
266     // Calculate the average of the two. (Works also for odd sizes.)
267     stats->median_waiting_time_ms = (middle_left + middle_right) / 2;
268     stats->min_waiting_time_ms = waiting_times_.front();
269     stats->max_waiting_time_ms = waiting_times_.back();
270     double sum = 0;
271     for (auto time : waiting_times_) {
272       sum += time;
273     }
274     stats->mean_waiting_time_ms = static_cast<int>(sum / waiting_times_.size());
275   }
276 
277   // Reset counters.
278   ResetMcu();
279   Reset();
280 }
281 
CalculateQ14Ratio(size_t numerator,uint32_t denominator)282 uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
283                                                  uint32_t denominator) {
284   if (numerator == 0) {
285     return 0;
286   } else if (numerator < denominator) {
287     // Ratio must be smaller than 1 in Q14.
288     assert((numerator << 14) / denominator < (1 << 14));
289     return static_cast<uint16_t>((numerator << 14) / denominator);
290   } else {
291     // Will not produce a ratio larger than 1, since this is probably an error.
292     return 1 << 14;
293   }
294 }
295 
296 }  // namespace webrtc
297