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 expanded_speech_samples_(0),
119 expanded_noise_samples_(0),
120 timestamps_since_last_report_(0),
121 secondary_decoded_samples_(0),
122 discarded_secondary_packets_(0),
123 delayed_packet_outage_counter_(
124 "WebRTC.Audio.DelayedPacketOutageEventsPerMinute",
125 60000, // 60 seconds report interval.
126 100),
127 excess_buffer_delay_("WebRTC.Audio.AverageExcessBufferDelayMs",
128 60000, // 60 seconds report interval.
129 1000),
130 buffer_full_counter_("WebRTC.Audio.JitterBufferFullPerMinute",
131 60000, // 60 seconds report interval.
132 100) {}
133
134 StatisticsCalculator::~StatisticsCalculator() = default;
135
Reset()136 void StatisticsCalculator::Reset() {
137 preemptive_samples_ = 0;
138 accelerate_samples_ = 0;
139 expanded_speech_samples_ = 0;
140 expanded_noise_samples_ = 0;
141 secondary_decoded_samples_ = 0;
142 discarded_secondary_packets_ = 0;
143 waiting_times_.clear();
144 }
145
ResetMcu()146 void StatisticsCalculator::ResetMcu() {
147 timestamps_since_last_report_ = 0;
148 }
149
ExpandedVoiceSamples(size_t num_samples,bool is_new_concealment_event)150 void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples,
151 bool is_new_concealment_event) {
152 expanded_speech_samples_ += num_samples;
153 ConcealedSamplesCorrection(rtc::dchecked_cast<int>(num_samples), true);
154 lifetime_stats_.concealment_events += is_new_concealment_event;
155 }
156
ExpandedNoiseSamples(size_t num_samples,bool is_new_concealment_event)157 void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples,
158 bool is_new_concealment_event) {
159 expanded_noise_samples_ += num_samples;
160 ConcealedSamplesCorrection(rtc::dchecked_cast<int>(num_samples), false);
161 lifetime_stats_.concealment_events += is_new_concealment_event;
162 }
163
ExpandedVoiceSamplesCorrection(int num_samples)164 void StatisticsCalculator::ExpandedVoiceSamplesCorrection(int num_samples) {
165 expanded_speech_samples_ =
166 AddIntToSizeTWithLowerCap(num_samples, expanded_speech_samples_);
167 ConcealedSamplesCorrection(num_samples, true);
168 }
169
ExpandedNoiseSamplesCorrection(int num_samples)170 void StatisticsCalculator::ExpandedNoiseSamplesCorrection(int num_samples) {
171 expanded_noise_samples_ =
172 AddIntToSizeTWithLowerCap(num_samples, expanded_noise_samples_);
173 ConcealedSamplesCorrection(num_samples, false);
174 }
175
DecodedOutputPlayed()176 void StatisticsCalculator::DecodedOutputPlayed() {
177 decoded_output_played_ = true;
178 }
179
EndExpandEvent(int fs_hz)180 void StatisticsCalculator::EndExpandEvent(int fs_hz) {
181 RTC_DCHECK_GE(lifetime_stats_.concealed_samples,
182 concealed_samples_at_event_end_);
183 const int event_duration_ms =
184 1000 *
185 (lifetime_stats_.concealed_samples - concealed_samples_at_event_end_) /
186 fs_hz;
187 if (event_duration_ms >= kInterruptionLenMs && decoded_output_played_) {
188 lifetime_stats_.interruption_count++;
189 lifetime_stats_.total_interruption_duration_ms += event_duration_ms;
190 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AudioInterruptionMs", event_duration_ms,
191 /*min=*/150, /*max=*/5000, /*bucket_count=*/50);
192 }
193 concealed_samples_at_event_end_ = lifetime_stats_.concealed_samples;
194 }
195
ConcealedSamplesCorrection(int num_samples,bool is_voice)196 void StatisticsCalculator::ConcealedSamplesCorrection(int num_samples,
197 bool is_voice) {
198 if (num_samples < 0) {
199 // Store negative correction to subtract from future positive additions.
200 // See also the function comment in the header file.
201 concealed_samples_correction_ -= num_samples;
202 if (!is_voice) {
203 silent_concealed_samples_correction_ -= num_samples;
204 }
205 return;
206 }
207
208 const size_t canceled_out =
209 std::min(static_cast<size_t>(num_samples), concealed_samples_correction_);
210 concealed_samples_correction_ -= canceled_out;
211 lifetime_stats_.concealed_samples += num_samples - canceled_out;
212
213 if (!is_voice) {
214 const size_t silent_canceled_out = std::min(
215 static_cast<size_t>(num_samples), silent_concealed_samples_correction_);
216 silent_concealed_samples_correction_ -= silent_canceled_out;
217 lifetime_stats_.silent_concealed_samples +=
218 num_samples - silent_canceled_out;
219 }
220 }
221
PreemptiveExpandedSamples(size_t num_samples)222 void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
223 preemptive_samples_ += num_samples;
224 operations_and_state_.preemptive_samples += num_samples;
225 lifetime_stats_.inserted_samples_for_deceleration += num_samples;
226 }
227
AcceleratedSamples(size_t num_samples)228 void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
229 accelerate_samples_ += num_samples;
230 operations_and_state_.accelerate_samples += num_samples;
231 lifetime_stats_.removed_samples_for_acceleration += num_samples;
232 }
233
PacketsDiscarded(size_t num_packets)234 void StatisticsCalculator::PacketsDiscarded(size_t num_packets) {
235 operations_and_state_.discarded_primary_packets += num_packets;
236 }
237
SecondaryPacketsDiscarded(size_t num_packets)238 void StatisticsCalculator::SecondaryPacketsDiscarded(size_t num_packets) {
239 discarded_secondary_packets_ += num_packets;
240 lifetime_stats_.fec_packets_discarded += num_packets;
241 }
242
SecondaryPacketsReceived(size_t num_packets)243 void StatisticsCalculator::SecondaryPacketsReceived(size_t num_packets) {
244 lifetime_stats_.fec_packets_received += num_packets;
245 }
246
IncreaseCounter(size_t num_samples,int fs_hz)247 void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
248 const int time_step_ms =
249 rtc::CheckedDivExact(static_cast<int>(1000 * num_samples), fs_hz);
250 delayed_packet_outage_counter_.AdvanceClock(time_step_ms);
251 excess_buffer_delay_.AdvanceClock(time_step_ms);
252 buffer_full_counter_.AdvanceClock(time_step_ms);
253 timestamps_since_last_report_ += static_cast<uint32_t>(num_samples);
254 if (timestamps_since_last_report_ >
255 static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
256 timestamps_since_last_report_ = 0;
257 }
258 lifetime_stats_.total_samples_received += num_samples;
259 }
260
JitterBufferDelay(size_t num_samples,uint64_t waiting_time_ms,uint64_t target_delay_ms)261 void StatisticsCalculator::JitterBufferDelay(size_t num_samples,
262 uint64_t waiting_time_ms,
263 uint64_t target_delay_ms) {
264 lifetime_stats_.jitter_buffer_delay_ms += waiting_time_ms * num_samples;
265 lifetime_stats_.jitter_buffer_target_delay_ms +=
266 target_delay_ms * num_samples;
267 lifetime_stats_.jitter_buffer_emitted_count += num_samples;
268 }
269
SecondaryDecodedSamples(int num_samples)270 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
271 secondary_decoded_samples_ += num_samples;
272 }
273
FlushedPacketBuffer()274 void StatisticsCalculator::FlushedPacketBuffer() {
275 operations_and_state_.packet_buffer_flushes++;
276 buffer_full_counter_.RegisterSample();
277 }
278
ReceivedPacket()279 void StatisticsCalculator::ReceivedPacket() {
280 ++lifetime_stats_.jitter_buffer_packets_received;
281 }
282
RelativePacketArrivalDelay(size_t delay_ms)283 void StatisticsCalculator::RelativePacketArrivalDelay(size_t delay_ms) {
284 lifetime_stats_.relative_packet_arrival_delay_ms += delay_ms;
285 }
286
LogDelayedPacketOutageEvent(int num_samples,int fs_hz)287 void StatisticsCalculator::LogDelayedPacketOutageEvent(int num_samples,
288 int fs_hz) {
289 int outage_duration_ms = num_samples / (fs_hz / 1000);
290 RTC_HISTOGRAM_COUNTS("WebRTC.Audio.DelayedPacketOutageEventMs",
291 outage_duration_ms, 1 /* min */, 2000 /* max */,
292 100 /* bucket count */);
293 delayed_packet_outage_counter_.RegisterSample();
294 lifetime_stats_.delayed_packet_outage_samples += num_samples;
295 }
296
StoreWaitingTime(int waiting_time_ms)297 void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
298 excess_buffer_delay_.RegisterSample(waiting_time_ms);
299 RTC_DCHECK_LE(waiting_times_.size(), kLenWaitingTimes);
300 if (waiting_times_.size() == kLenWaitingTimes) {
301 // Erase first value.
302 waiting_times_.pop_front();
303 }
304 waiting_times_.push_back(waiting_time_ms);
305 operations_and_state_.last_waiting_time_ms = waiting_time_ms;
306 }
307
GetNetworkStatistics(size_t samples_per_packet,NetEqNetworkStatistics * stats)308 void StatisticsCalculator::GetNetworkStatistics(size_t samples_per_packet,
309 NetEqNetworkStatistics* stats) {
310 RTC_DCHECK(stats);
311
312 stats->accelerate_rate =
313 CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);
314
315 stats->preemptive_rate =
316 CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);
317
318 stats->expand_rate =
319 CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
320 timestamps_since_last_report_);
321
322 stats->speech_expand_rate = CalculateQ14Ratio(expanded_speech_samples_,
323 timestamps_since_last_report_);
324
325 stats->secondary_decoded_rate = CalculateQ14Ratio(
326 secondary_decoded_samples_, timestamps_since_last_report_);
327
328 const size_t discarded_secondary_samples =
329 discarded_secondary_packets_ * samples_per_packet;
330 stats->secondary_discarded_rate =
331 CalculateQ14Ratio(discarded_secondary_samples,
332 static_cast<uint32_t>(discarded_secondary_samples +
333 secondary_decoded_samples_));
334
335 if (waiting_times_.size() == 0) {
336 stats->mean_waiting_time_ms = -1;
337 stats->median_waiting_time_ms = -1;
338 stats->min_waiting_time_ms = -1;
339 stats->max_waiting_time_ms = -1;
340 } else {
341 std::sort(waiting_times_.begin(), waiting_times_.end());
342 // Find mid-point elements. If the size is odd, the two values
343 // |middle_left| and |middle_right| will both be the one middle element; if
344 // the size is even, they will be the the two neighboring elements at the
345 // middle of the list.
346 const int middle_left = waiting_times_[(waiting_times_.size() - 1) / 2];
347 const int middle_right = waiting_times_[waiting_times_.size() / 2];
348 // Calculate the average of the two. (Works also for odd sizes.)
349 stats->median_waiting_time_ms = (middle_left + middle_right) / 2;
350 stats->min_waiting_time_ms = waiting_times_.front();
351 stats->max_waiting_time_ms = waiting_times_.back();
352 double sum = 0;
353 for (auto time : waiting_times_) {
354 sum += time;
355 }
356 stats->mean_waiting_time_ms = static_cast<int>(sum / waiting_times_.size());
357 }
358
359 // Reset counters.
360 ResetMcu();
361 Reset();
362 }
363
GetLifetimeStatistics() const364 NetEqLifetimeStatistics StatisticsCalculator::GetLifetimeStatistics() const {
365 return lifetime_stats_;
366 }
367
GetOperationsAndState() const368 NetEqOperationsAndState StatisticsCalculator::GetOperationsAndState() const {
369 return operations_and_state_;
370 }
371
CalculateQ14Ratio(size_t numerator,uint32_t denominator)372 uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
373 uint32_t denominator) {
374 if (numerator == 0) {
375 return 0;
376 } else if (numerator < denominator) {
377 // Ratio must be smaller than 1 in Q14.
378 assert((numerator << 14) / denominator < (1 << 14));
379 return static_cast<uint16_t>((numerator << 14) / denominator);
380 } else {
381 // Will not produce a ratio larger than 1, since this is probably an error.
382 return 1 << 14;
383 }
384 }
385
386 } // namespace webrtc
387