• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "modules/audio_coding/neteq/statistics_calculator.h"
12 
13 #include <assert.h>
14 #include <string.h>  // memset
15 
16 #include <algorithm>
17 
18 #include "modules/audio_coding/neteq/delay_manager.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/numerics/safe_conversions.h"
21 #include "system_wrappers/include/metrics.h"
22 
23 namespace webrtc {
24 
25 namespace {
AddIntToSizeTWithLowerCap(int a,size_t b)26 size_t AddIntToSizeTWithLowerCap(int a, size_t b) {
27   const size_t ret = b + a;
28   // If a + b is negative, resulting in a negative wrap, cap it to zero instead.
29   static_assert(sizeof(size_t) >= sizeof(int),
30                 "int must not be wider than size_t for this to work");
31   return (a < 0 && ret > b) ? 0 : ret;
32 }
33 
34 constexpr int kInterruptionLenMs = 150;
35 }  // namespace
36 
37 // Allocating the static const so that it can be passed by reference to
38 // RTC_DCHECK.
39 const size_t StatisticsCalculator::kLenWaitingTimes;
40 
PeriodicUmaLogger(const std::string & uma_name,int report_interval_ms,int max_value)41 StatisticsCalculator::PeriodicUmaLogger::PeriodicUmaLogger(
42     const std::string& uma_name,
43     int report_interval_ms,
44     int max_value)
45     : uma_name_(uma_name),
46       report_interval_ms_(report_interval_ms),
47       max_value_(max_value),
48       timer_(0) {}
49 
50 StatisticsCalculator::PeriodicUmaLogger::~PeriodicUmaLogger() = default;
51 
AdvanceClock(int step_ms)52 void StatisticsCalculator::PeriodicUmaLogger::AdvanceClock(int step_ms) {
53   timer_ += step_ms;
54   if (timer_ < report_interval_ms_) {
55     return;
56   }
57   LogToUma(Metric());
58   Reset();
59   timer_ -= report_interval_ms_;
60   RTC_DCHECK_GE(timer_, 0);
61 }
62 
LogToUma(int value) const63 void StatisticsCalculator::PeriodicUmaLogger::LogToUma(int value) const {
64   RTC_HISTOGRAM_COUNTS_SPARSE(uma_name_, value, 1, max_value_, 50);
65 }
66 
PeriodicUmaCount(const std::string & uma_name,int report_interval_ms,int max_value)67 StatisticsCalculator::PeriodicUmaCount::PeriodicUmaCount(
68     const std::string& uma_name,
69     int report_interval_ms,
70     int max_value)
71     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {}
72 
~PeriodicUmaCount()73 StatisticsCalculator::PeriodicUmaCount::~PeriodicUmaCount() {
74   // Log the count for the current (incomplete) interval.
75   LogToUma(Metric());
76 }
77 
RegisterSample()78 void StatisticsCalculator::PeriodicUmaCount::RegisterSample() {
79   ++counter_;
80 }
81 
Metric() const82 int StatisticsCalculator::PeriodicUmaCount::Metric() const {
83   return counter_;
84 }
85 
Reset()86 void StatisticsCalculator::PeriodicUmaCount::Reset() {
87   counter_ = 0;
88 }
89 
PeriodicUmaAverage(const std::string & uma_name,int report_interval_ms,int max_value)90 StatisticsCalculator::PeriodicUmaAverage::PeriodicUmaAverage(
91     const std::string& uma_name,
92     int report_interval_ms,
93     int max_value)
94     : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {}
95 
~PeriodicUmaAverage()96 StatisticsCalculator::PeriodicUmaAverage::~PeriodicUmaAverage() {
97   // Log the average for the current (incomplete) interval.
98   LogToUma(Metric());
99 }
100 
RegisterSample(int value)101 void StatisticsCalculator::PeriodicUmaAverage::RegisterSample(int value) {
102   sum_ += value;
103   ++counter_;
104 }
105 
Metric() const106 int StatisticsCalculator::PeriodicUmaAverage::Metric() const {
107   return counter_ == 0 ? 0 : static_cast<int>(sum_ / counter_);
108 }
109 
Reset()110 void StatisticsCalculator::PeriodicUmaAverage::Reset() {
111   sum_ = 0.0;
112   counter_ = 0;
113 }
114 
StatisticsCalculator()115 StatisticsCalculator::StatisticsCalculator()
116     : preemptive_samples_(0),
117       accelerate_samples_(0),
118       added_zero_samples_(0),
119       expanded_speech_samples_(0),
120       expanded_noise_samples_(0),
121       discarded_packets_(0),
122       lost_timestamps_(0),
123       timestamps_since_last_report_(0),
124       secondary_decoded_samples_(0),
125       discarded_secondary_packets_(0),
126       delayed_packet_outage_counter_(
127           "WebRTC.Audio.DelayedPacketOutageEventsPerMinute",
128           60000,  // 60 seconds report interval.
129           100),
130       excess_buffer_delay_("WebRTC.Audio.AverageExcessBufferDelayMs",
131                            60000,  // 60 seconds report interval.
132                            1000),
133       buffer_full_counter_("WebRTC.Audio.JitterBufferFullPerMinute",
134                            60000,  // 60 seconds report interval.
135                            100) {}
136 
137 StatisticsCalculator::~StatisticsCalculator() = default;
138 
Reset()139 void StatisticsCalculator::Reset() {
140   preemptive_samples_ = 0;
141   accelerate_samples_ = 0;
142   added_zero_samples_ = 0;
143   expanded_speech_samples_ = 0;
144   expanded_noise_samples_ = 0;
145   secondary_decoded_samples_ = 0;
146   discarded_secondary_packets_ = 0;
147   waiting_times_.clear();
148 }
149 
ResetMcu()150 void StatisticsCalculator::ResetMcu() {
151   discarded_packets_ = 0;
152   lost_timestamps_ = 0;
153   timestamps_since_last_report_ = 0;
154 }
155 
ExpandedVoiceSamples(size_t num_samples,bool is_new_concealment_event)156 void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples,
157                                                 bool is_new_concealment_event) {
158   expanded_speech_samples_ += num_samples;
159   ConcealedSamplesCorrection(rtc::dchecked_cast<int>(num_samples), true);
160   lifetime_stats_.concealment_events += is_new_concealment_event;
161 }
162 
ExpandedNoiseSamples(size_t num_samples,bool is_new_concealment_event)163 void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples,
164                                                 bool is_new_concealment_event) {
165   expanded_noise_samples_ += num_samples;
166   ConcealedSamplesCorrection(rtc::dchecked_cast<int>(num_samples), false);
167   lifetime_stats_.concealment_events += is_new_concealment_event;
168 }
169 
ExpandedVoiceSamplesCorrection(int num_samples)170 void StatisticsCalculator::ExpandedVoiceSamplesCorrection(int num_samples) {
171   expanded_speech_samples_ =
172       AddIntToSizeTWithLowerCap(num_samples, expanded_speech_samples_);
173   ConcealedSamplesCorrection(num_samples, true);
174 }
175 
ExpandedNoiseSamplesCorrection(int num_samples)176 void StatisticsCalculator::ExpandedNoiseSamplesCorrection(int num_samples) {
177   expanded_noise_samples_ =
178       AddIntToSizeTWithLowerCap(num_samples, expanded_noise_samples_);
179   ConcealedSamplesCorrection(num_samples, false);
180 }
181 
DecodedOutputPlayed()182 void StatisticsCalculator::DecodedOutputPlayed() {
183   decoded_output_played_ = true;
184 }
185 
EndExpandEvent(int fs_hz)186 void StatisticsCalculator::EndExpandEvent(int fs_hz) {
187   RTC_DCHECK_GE(lifetime_stats_.concealed_samples,
188                 concealed_samples_at_event_end_);
189   const int event_duration_ms =
190       1000 *
191       (lifetime_stats_.concealed_samples - concealed_samples_at_event_end_) /
192       fs_hz;
193   if (event_duration_ms >= kInterruptionLenMs && decoded_output_played_) {
194     lifetime_stats_.interruption_count++;
195     lifetime_stats_.total_interruption_duration_ms += event_duration_ms;
196     RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AudioInterruptionMs", event_duration_ms,
197                          /*min=*/150, /*max=*/5000, /*bucket_count=*/50);
198   }
199   concealed_samples_at_event_end_ = lifetime_stats_.concealed_samples;
200 }
201 
ConcealedSamplesCorrection(int num_samples,bool is_voice)202 void StatisticsCalculator::ConcealedSamplesCorrection(int num_samples,
203                                                       bool is_voice) {
204   if (num_samples < 0) {
205     // Store negative correction to subtract from future positive additions.
206     // See also the function comment in the header file.
207     concealed_samples_correction_ -= num_samples;
208     if (!is_voice) {
209       silent_concealed_samples_correction_ -= num_samples;
210     }
211     return;
212   }
213 
214   const size_t canceled_out =
215       std::min(static_cast<size_t>(num_samples), concealed_samples_correction_);
216   concealed_samples_correction_ -= canceled_out;
217   lifetime_stats_.concealed_samples += num_samples - canceled_out;
218 
219   if (!is_voice) {
220     const size_t silent_canceled_out = std::min(
221         static_cast<size_t>(num_samples), silent_concealed_samples_correction_);
222     silent_concealed_samples_correction_ -= silent_canceled_out;
223     lifetime_stats_.silent_concealed_samples +=
224         num_samples - silent_canceled_out;
225   }
226 }
227 
PreemptiveExpandedSamples(size_t num_samples)228 void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
229   preemptive_samples_ += num_samples;
230   operations_and_state_.preemptive_samples += num_samples;
231   lifetime_stats_.inserted_samples_for_deceleration += num_samples;
232 }
233 
AcceleratedSamples(size_t num_samples)234 void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
235   accelerate_samples_ += num_samples;
236   operations_and_state_.accelerate_samples += num_samples;
237   lifetime_stats_.removed_samples_for_acceleration += num_samples;
238 }
239 
AddZeros(size_t num_samples)240 void StatisticsCalculator::AddZeros(size_t num_samples) {
241   added_zero_samples_ += num_samples;
242 }
243 
PacketsDiscarded(size_t num_packets)244 void StatisticsCalculator::PacketsDiscarded(size_t num_packets) {
245   operations_and_state_.discarded_primary_packets += num_packets;
246 }
247 
SecondaryPacketsDiscarded(size_t num_packets)248 void StatisticsCalculator::SecondaryPacketsDiscarded(size_t num_packets) {
249   discarded_secondary_packets_ += num_packets;
250   lifetime_stats_.fec_packets_discarded += num_packets;
251 }
252 
SecondaryPacketsReceived(size_t num_packets)253 void StatisticsCalculator::SecondaryPacketsReceived(size_t num_packets) {
254   lifetime_stats_.fec_packets_received += num_packets;
255 }
256 
LostSamples(size_t num_samples)257 void StatisticsCalculator::LostSamples(size_t num_samples) {
258   lost_timestamps_ += num_samples;
259 }
260 
IncreaseCounter(size_t num_samples,int fs_hz)261 void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
262   const int time_step_ms =
263       rtc::CheckedDivExact(static_cast<int>(1000 * num_samples), fs_hz);
264   delayed_packet_outage_counter_.AdvanceClock(time_step_ms);
265   excess_buffer_delay_.AdvanceClock(time_step_ms);
266   buffer_full_counter_.AdvanceClock(time_step_ms);
267   timestamps_since_last_report_ += static_cast<uint32_t>(num_samples);
268   if (timestamps_since_last_report_ >
269       static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
270     lost_timestamps_ = 0;
271     timestamps_since_last_report_ = 0;
272     discarded_packets_ = 0;
273   }
274   lifetime_stats_.total_samples_received += num_samples;
275 }
276 
JitterBufferDelay(size_t num_samples,uint64_t waiting_time_ms,uint64_t target_delay_ms)277 void StatisticsCalculator::JitterBufferDelay(size_t num_samples,
278                                              uint64_t waiting_time_ms,
279                                              uint64_t target_delay_ms) {
280   lifetime_stats_.jitter_buffer_delay_ms += waiting_time_ms * num_samples;
281   lifetime_stats_.jitter_buffer_target_delay_ms +=
282       target_delay_ms * num_samples;
283   lifetime_stats_.jitter_buffer_emitted_count += num_samples;
284 }
285 
SecondaryDecodedSamples(int num_samples)286 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
287   secondary_decoded_samples_ += num_samples;
288 }
289 
FlushedPacketBuffer()290 void StatisticsCalculator::FlushedPacketBuffer() {
291   operations_and_state_.packet_buffer_flushes++;
292   buffer_full_counter_.RegisterSample();
293 }
294 
ReceivedPacket()295 void StatisticsCalculator::ReceivedPacket() {
296   ++lifetime_stats_.jitter_buffer_packets_received;
297 }
298 
RelativePacketArrivalDelay(size_t delay_ms)299 void StatisticsCalculator::RelativePacketArrivalDelay(size_t delay_ms) {
300   lifetime_stats_.relative_packet_arrival_delay_ms += delay_ms;
301 }
302 
LogDelayedPacketOutageEvent(int num_samples,int fs_hz)303 void StatisticsCalculator::LogDelayedPacketOutageEvent(int num_samples,
304                                                        int fs_hz) {
305   int outage_duration_ms = num_samples / (fs_hz / 1000);
306   RTC_HISTOGRAM_COUNTS("WebRTC.Audio.DelayedPacketOutageEventMs",
307                        outage_duration_ms, 1 /* min */, 2000 /* max */,
308                        100 /* bucket count */);
309   delayed_packet_outage_counter_.RegisterSample();
310   lifetime_stats_.delayed_packet_outage_samples += num_samples;
311 }
312 
StoreWaitingTime(int waiting_time_ms)313 void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
314   excess_buffer_delay_.RegisterSample(waiting_time_ms);
315   RTC_DCHECK_LE(waiting_times_.size(), kLenWaitingTimes);
316   if (waiting_times_.size() == kLenWaitingTimes) {
317     // Erase first value.
318     waiting_times_.pop_front();
319   }
320   waiting_times_.push_back(waiting_time_ms);
321   operations_and_state_.last_waiting_time_ms = waiting_time_ms;
322 }
323 
GetNetworkStatistics(int fs_hz,size_t num_samples_in_buffers,size_t samples_per_packet,NetEqNetworkStatistics * stats)324 void StatisticsCalculator::GetNetworkStatistics(int fs_hz,
325                                                 size_t num_samples_in_buffers,
326                                                 size_t samples_per_packet,
327                                                 NetEqNetworkStatistics* stats) {
328   RTC_DCHECK_GT(fs_hz, 0);
329   RTC_DCHECK(stats);
330 
331   stats->added_zero_samples = added_zero_samples_;
332   stats->current_buffer_size_ms =
333       static_cast<uint16_t>(num_samples_in_buffers * 1000 / fs_hz);
334 
335   stats->packet_loss_rate =
336       CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_);
337 
338   stats->accelerate_rate =
339       CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);
340 
341   stats->preemptive_rate =
342       CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);
343 
344   stats->expand_rate =
345       CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
346                         timestamps_since_last_report_);
347 
348   stats->speech_expand_rate = CalculateQ14Ratio(expanded_speech_samples_,
349                                                 timestamps_since_last_report_);
350 
351   stats->secondary_decoded_rate = CalculateQ14Ratio(
352       secondary_decoded_samples_, timestamps_since_last_report_);
353 
354   const size_t discarded_secondary_samples =
355       discarded_secondary_packets_ * samples_per_packet;
356   stats->secondary_discarded_rate =
357       CalculateQ14Ratio(discarded_secondary_samples,
358                         static_cast<uint32_t>(discarded_secondary_samples +
359                                               secondary_decoded_samples_));
360 
361   if (waiting_times_.size() == 0) {
362     stats->mean_waiting_time_ms = -1;
363     stats->median_waiting_time_ms = -1;
364     stats->min_waiting_time_ms = -1;
365     stats->max_waiting_time_ms = -1;
366   } else {
367     std::sort(waiting_times_.begin(), waiting_times_.end());
368     // Find mid-point elements. If the size is odd, the two values
369     // |middle_left| and |middle_right| will both be the one middle element; if
370     // the size is even, they will be the the two neighboring elements at the
371     // middle of the list.
372     const int middle_left = waiting_times_[(waiting_times_.size() - 1) / 2];
373     const int middle_right = waiting_times_[waiting_times_.size() / 2];
374     // Calculate the average of the two. (Works also for odd sizes.)
375     stats->median_waiting_time_ms = (middle_left + middle_right) / 2;
376     stats->min_waiting_time_ms = waiting_times_.front();
377     stats->max_waiting_time_ms = waiting_times_.back();
378     double sum = 0;
379     for (auto time : waiting_times_) {
380       sum += time;
381     }
382     stats->mean_waiting_time_ms = static_cast<int>(sum / waiting_times_.size());
383   }
384 
385   // Reset counters.
386   ResetMcu();
387   Reset();
388 }
389 
GetLifetimeStatistics() const390 NetEqLifetimeStatistics StatisticsCalculator::GetLifetimeStatistics() const {
391   return lifetime_stats_;
392 }
393 
GetOperationsAndState() const394 NetEqOperationsAndState StatisticsCalculator::GetOperationsAndState() const {
395   return operations_and_state_;
396 }
397 
CalculateQ14Ratio(size_t numerator,uint32_t denominator)398 uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
399                                                  uint32_t denominator) {
400   if (numerator == 0) {
401     return 0;
402   } else if (numerator < denominator) {
403     // Ratio must be smaller than 1 in Q14.
404     assert((numerator << 14) / denominator < (1 << 14));
405     return static_cast<uint16_t>((numerator << 14) / denominator);
406   } else {
407     // Will not produce a ratio larger than 1, since this is probably an error.
408     return 1 << 14;
409   }
410 }
411 
412 }  // namespace webrtc
413