Line data Source code
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 0 : StatisticsCalculator::PeriodicUmaLogger::PeriodicUmaLogger(
30 : const std::string& uma_name,
31 : int report_interval_ms,
32 0 : int max_value)
33 : : uma_name_(uma_name),
34 : report_interval_ms_(report_interval_ms),
35 : max_value_(max_value),
36 0 : timer_(0) {
37 0 : }
38 :
39 : StatisticsCalculator::PeriodicUmaLogger::~PeriodicUmaLogger() = default;
40 :
41 0 : void StatisticsCalculator::PeriodicUmaLogger::AdvanceClock(int step_ms) {
42 0 : timer_ += step_ms;
43 0 : if (timer_ < report_interval_ms_) {
44 0 : return;
45 : }
46 0 : LogToUma(Metric());
47 0 : Reset();
48 0 : timer_ -= report_interval_ms_;
49 0 : RTC_DCHECK_GE(timer_, 0);
50 : }
51 :
52 0 : void StatisticsCalculator::PeriodicUmaLogger::LogToUma(int value) const {
53 0 : RTC_HISTOGRAM_COUNTS_SPARSE(uma_name_, value, 1, max_value_, 50);
54 0 : }
55 :
56 0 : StatisticsCalculator::PeriodicUmaCount::PeriodicUmaCount(
57 : const std::string& uma_name,
58 : int report_interval_ms,
59 0 : int max_value)
60 0 : : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
61 0 : }
62 :
63 0 : StatisticsCalculator::PeriodicUmaCount::~PeriodicUmaCount() {
64 : // Log the count for the current (incomplete) interval.
65 0 : LogToUma(Metric());
66 0 : }
67 :
68 0 : void StatisticsCalculator::PeriodicUmaCount::RegisterSample() {
69 0 : ++counter_;
70 0 : }
71 :
72 0 : int StatisticsCalculator::PeriodicUmaCount::Metric() const {
73 0 : return counter_;
74 : }
75 :
76 0 : void StatisticsCalculator::PeriodicUmaCount::Reset() {
77 0 : counter_ = 0;
78 0 : }
79 :
80 0 : StatisticsCalculator::PeriodicUmaAverage::PeriodicUmaAverage(
81 : const std::string& uma_name,
82 : int report_interval_ms,
83 0 : int max_value)
84 0 : : PeriodicUmaLogger(uma_name, report_interval_ms, max_value) {
85 0 : }
86 :
87 0 : StatisticsCalculator::PeriodicUmaAverage::~PeriodicUmaAverage() {
88 : // Log the average for the current (incomplete) interval.
89 0 : LogToUma(Metric());
90 0 : }
91 :
92 0 : void StatisticsCalculator::PeriodicUmaAverage::RegisterSample(int value) {
93 0 : sum_ += value;
94 0 : ++counter_;
95 0 : }
96 :
97 0 : int StatisticsCalculator::PeriodicUmaAverage::Metric() const {
98 0 : return counter_ == 0 ? 0 : static_cast<int>(sum_ / counter_);
99 : }
100 :
101 0 : void StatisticsCalculator::PeriodicUmaAverage::Reset() {
102 0 : sum_ = 0.0;
103 0 : counter_ = 0;
104 0 : }
105 :
106 0 : 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 0 : 1000) {
123 0 : }
124 :
125 : StatisticsCalculator::~StatisticsCalculator() = default;
126 :
127 0 : void StatisticsCalculator::Reset() {
128 0 : preemptive_samples_ = 0;
129 0 : accelerate_samples_ = 0;
130 0 : added_zero_samples_ = 0;
131 0 : expanded_speech_samples_ = 0;
132 0 : expanded_noise_samples_ = 0;
133 0 : secondary_decoded_samples_ = 0;
134 0 : waiting_times_.clear();
135 0 : }
136 :
137 0 : void StatisticsCalculator::ResetMcu() {
138 0 : discarded_packets_ = 0;
139 0 : lost_timestamps_ = 0;
140 0 : timestamps_since_last_report_ = 0;
141 0 : }
142 :
143 0 : void StatisticsCalculator::ExpandedVoiceSamples(size_t num_samples) {
144 0 : expanded_speech_samples_ += num_samples;
145 0 : }
146 :
147 0 : void StatisticsCalculator::ExpandedNoiseSamples(size_t num_samples) {
148 0 : expanded_noise_samples_ += num_samples;
149 0 : }
150 :
151 0 : void StatisticsCalculator::PreemptiveExpandedSamples(size_t num_samples) {
152 0 : preemptive_samples_ += num_samples;
153 0 : }
154 :
155 0 : void StatisticsCalculator::AcceleratedSamples(size_t num_samples) {
156 0 : accelerate_samples_ += num_samples;
157 0 : }
158 :
159 0 : void StatisticsCalculator::AddZeros(size_t num_samples) {
160 0 : added_zero_samples_ += num_samples;
161 0 : }
162 :
163 0 : void StatisticsCalculator::PacketsDiscarded(size_t num_packets) {
164 0 : discarded_packets_ += num_packets;
165 0 : }
166 :
167 0 : void StatisticsCalculator::LostSamples(size_t num_samples) {
168 0 : lost_timestamps_ += num_samples;
169 0 : }
170 :
171 0 : void StatisticsCalculator::IncreaseCounter(size_t num_samples, int fs_hz) {
172 : const int time_step_ms =
173 0 : rtc::CheckedDivExact(static_cast<int>(1000 * num_samples), fs_hz);
174 0 : delayed_packet_outage_counter_.AdvanceClock(time_step_ms);
175 0 : excess_buffer_delay_.AdvanceClock(time_step_ms);
176 0 : timestamps_since_last_report_ += static_cast<uint32_t>(num_samples);
177 0 : if (timestamps_since_last_report_ >
178 0 : static_cast<uint32_t>(fs_hz * kMaxReportPeriod)) {
179 0 : lost_timestamps_ = 0;
180 0 : timestamps_since_last_report_ = 0;
181 0 : discarded_packets_ = 0;
182 : }
183 0 : }
184 :
185 0 : void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
186 0 : secondary_decoded_samples_ += num_samples;
187 0 : }
188 :
189 0 : void StatisticsCalculator::LogDelayedPacketOutageEvent(int outage_duration_ms) {
190 0 : RTC_HISTOGRAM_COUNTS("WebRTC.Audio.DelayedPacketOutageEventMs",
191 : outage_duration_ms, 1 /* min */, 2000 /* max */,
192 : 100 /* bucket count */);
193 0 : delayed_packet_outage_counter_.RegisterSample();
194 0 : }
195 :
196 0 : void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
197 0 : excess_buffer_delay_.RegisterSample(waiting_time_ms);
198 0 : RTC_DCHECK_LE(waiting_times_.size(), kLenWaitingTimes);
199 0 : if (waiting_times_.size() == kLenWaitingTimes) {
200 : // Erase first value.
201 0 : waiting_times_.pop_front();
202 : }
203 0 : waiting_times_.push_back(waiting_time_ms);
204 0 : }
205 :
206 0 : 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 0 : if (fs_hz <= 0 || !stats) {
214 0 : assert(false);
215 : return;
216 : }
217 :
218 0 : stats->added_zero_samples = added_zero_samples_;
219 0 : stats->current_buffer_size_ms =
220 0 : static_cast<uint16_t>(num_samples_in_buffers * 1000 / fs_hz);
221 0 : const int ms_per_packet = rtc::checked_cast<int>(
222 0 : decision_logic.packet_length_samples() / (fs_hz / 1000));
223 0 : stats->preferred_buffer_size_ms = (delay_manager.TargetLevel() >> 8) *
224 0 : ms_per_packet;
225 0 : stats->jitter_peaks_found = delay_manager.PeakFound();
226 0 : stats->clockdrift_ppm =
227 0 : rtc::saturated_cast<int32_t>(delay_manager.EstimatedClockDriftPpm());
228 :
229 0 : stats->packet_loss_rate =
230 0 : CalculateQ14Ratio(lost_timestamps_, timestamps_since_last_report_);
231 :
232 0 : const size_t discarded_samples = discarded_packets_ * samples_per_packet;
233 0 : stats->packet_discard_rate =
234 0 : CalculateQ14Ratio(discarded_samples, timestamps_since_last_report_);
235 :
236 0 : stats->accelerate_rate =
237 0 : CalculateQ14Ratio(accelerate_samples_, timestamps_since_last_report_);
238 :
239 0 : stats->preemptive_rate =
240 0 : CalculateQ14Ratio(preemptive_samples_, timestamps_since_last_report_);
241 :
242 0 : stats->expand_rate =
243 0 : CalculateQ14Ratio(expanded_speech_samples_ + expanded_noise_samples_,
244 : timestamps_since_last_report_);
245 :
246 0 : stats->speech_expand_rate =
247 0 : CalculateQ14Ratio(expanded_speech_samples_,
248 : timestamps_since_last_report_);
249 :
250 0 : stats->secondary_decoded_rate =
251 0 : CalculateQ14Ratio(secondary_decoded_samples_,
252 : timestamps_since_last_report_);
253 :
254 0 : if (waiting_times_.size() == 0) {
255 0 : stats->mean_waiting_time_ms = -1;
256 0 : stats->median_waiting_time_ms = -1;
257 0 : stats->min_waiting_time_ms = -1;
258 0 : stats->max_waiting_time_ms = -1;
259 : } else {
260 0 : std::sort(waiting_times_.begin(), waiting_times_.end());
261 : // Find mid-point elements. If the size is odd, the two values
262 : // |middle_left| and |middle_right| will both be the one middle element; if
263 : // the size is even, they will be the the two neighboring elements at the
264 : // middle of the list.
265 0 : const int middle_left = waiting_times_[(waiting_times_.size() - 1) / 2];
266 0 : const int middle_right = waiting_times_[waiting_times_.size() / 2];
267 : // Calculate the average of the two. (Works also for odd sizes.)
268 0 : stats->median_waiting_time_ms = (middle_left + middle_right) / 2;
269 0 : stats->min_waiting_time_ms = waiting_times_.front();
270 0 : stats->max_waiting_time_ms = waiting_times_.back();
271 0 : double sum = 0;
272 0 : for (auto time : waiting_times_) {
273 0 : sum += time;
274 : }
275 0 : stats->mean_waiting_time_ms = static_cast<int>(sum / waiting_times_.size());
276 : }
277 :
278 : // Reset counters.
279 0 : ResetMcu();
280 0 : Reset();
281 : }
282 :
283 0 : uint16_t StatisticsCalculator::CalculateQ14Ratio(size_t numerator,
284 : uint32_t denominator) {
285 0 : if (numerator == 0) {
286 0 : return 0;
287 0 : } else if (numerator < denominator) {
288 : // Ratio must be smaller than 1 in Q14.
289 0 : assert((numerator << 14) / denominator < (1 << 14));
290 0 : return static_cast<uint16_t>((numerator << 14) / denominator);
291 : } else {
292 : // Will not produce a ratio larger than 1, since this is probably an error.
293 0 : return 1 << 14;
294 : }
295 : }
296 :
297 : } // namespace webrtc
|