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