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 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 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 52 void StatisticsCalculator::PeriodicUmaLogger::LogToUma(int value) const { 53 RTC_HISTOGRAM_COUNTS_SPARSE(uma_name_, value, 1, max_value_, 50); 54 } 55 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 63 StatisticsCalculator::PeriodicUmaCount::~PeriodicUmaCount() { 64 // Log the count for the current (incomplete) interval. 65 LogToUma(Metric()); 66 } 67 68 void StatisticsCalculator::PeriodicUmaCount::RegisterSample() { 69 ++counter_; 70 } 71 72 int StatisticsCalculator::PeriodicUmaCount::Metric() const { 73 return counter_; 74 } 75 76 void StatisticsCalculator::PeriodicUmaCount::Reset() { 77 counter_ = 0; 78 } 79 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 87 StatisticsCalculator::PeriodicUmaAverage::~PeriodicUmaAverage() { 88 // Log the average for the current (incomplete) interval. 89 LogToUma(Metric()); 90 } 91 92 void StatisticsCalculator::PeriodicUmaAverage::RegisterSample(int value) { 93 sum_ += value; 94 ++counter_; 95 } 96 97 int StatisticsCalculator::PeriodicUmaAverage::Metric() const { 98 return static_cast<int>(sum_ / counter_); 99 } 100 101 void StatisticsCalculator::PeriodicUmaAverage::Reset() { 102 sum_ = 0.0; 103 counter_ = 0; 104 } 105 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 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 137 void StatisticsCalculator::ResetMcu() { 138 discarded_packets_ = 0; 139 lost_timestamps_ = 0; 140 timestamps_since_last_report_ = 0; 141 } 142 143 void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples) { 144 expanded_speech_samples_ += num_samples; 145 } 146 147 void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples) { 148 expanded_noise_samples_ += num_samples; 149 } 150 151 void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) { 152 preemptive_samples_ += num_samples; 153 } 154 155 void StatisticsCalculator::AcceleratedSamples(size_t num_samples) { 156 accelerate_samples_ += num_samples; 157 } 158 159 void StatisticsCalculator::AddZeros(size_t num_samples) { 160 added_zero_samples_ += num_samples; 161 } 162 163 void StatisticsCalculator::PacketsDiscarded(size_t num_packets) { 164 discarded_packets_ += num_packets; 165 } 166 167 void StatisticsCalculator::LostSamples(size_t num_samples) { 168 lost_timestamps_ += num_samples; 169 } 170 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 185 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) { 186 secondary_decoded_samples_ += num_samples; 187 } 188 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 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 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 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