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