Line data Source code
1 : /*
2 : * Copyright (c) 2016 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/video/stats_counter.h"
12 :
13 : #include <algorithm>
14 : #include <limits>
15 : #include <map>
16 :
17 : #include "webrtc/base/checks.h"
18 : #include "webrtc/system_wrappers/include/clock.h"
19 :
20 : namespace webrtc {
21 :
22 : namespace {
23 : // Default periodic time interval for processing samples.
24 : const int64_t kDefaultProcessIntervalMs = 2000;
25 : const uint32_t kStreamId0 = 0;
26 : } // namespace
27 :
28 0 : std::string AggregatedStats::ToString() const {
29 0 : return ToStringWithMultiplier(1);
30 : }
31 :
32 0 : std::string AggregatedStats::ToStringWithMultiplier(int multiplier) const {
33 0 : std::stringstream ss;
34 0 : ss << "periodic_samples:" << num_samples << ", {";
35 0 : ss << "min:" << (min * multiplier) << ", ";
36 0 : ss << "avg:" << (average * multiplier) << ", ";
37 0 : ss << "max:" << (max * multiplier) << "}";
38 0 : return ss.str();
39 : }
40 :
41 : // Class holding periodically computed metrics.
42 : class AggregatedCounter {
43 : public:
44 0 : AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
45 0 : ~AggregatedCounter() {}
46 :
47 0 : void Add(int sample) {
48 0 : last_sample_ = sample;
49 0 : sum_samples_ += sample;
50 0 : ++stats_.num_samples;
51 0 : if (stats_.num_samples == 1) {
52 0 : stats_.min = sample;
53 0 : stats_.max = sample;
54 : }
55 0 : stats_.min = std::min(sample, stats_.min);
56 0 : stats_.max = std::max(sample, stats_.max);
57 0 : }
58 :
59 0 : AggregatedStats ComputeStats() {
60 0 : Compute();
61 0 : return stats_;
62 : }
63 :
64 0 : bool Empty() const { return stats_.num_samples == 0; }
65 :
66 0 : int last_sample() const { return last_sample_; }
67 :
68 : private:
69 0 : void Compute() {
70 0 : if (stats_.num_samples == 0)
71 0 : return;
72 :
73 0 : stats_.average =
74 0 : (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
75 : }
76 : int last_sample_;
77 : int64_t sum_samples_;
78 : AggregatedStats stats_;
79 : };
80 :
81 : // Class holding gathered samples within a process interval.
82 : class Samples {
83 : public:
84 0 : Samples() : total_count_(0) {}
85 0 : ~Samples() {}
86 :
87 0 : void Add(int sample, uint32_t stream_id) {
88 0 : samples_[stream_id].Add(sample);
89 0 : ++total_count_;
90 0 : }
91 0 : void Set(int sample, uint32_t stream_id) {
92 0 : samples_[stream_id].Set(sample);
93 0 : ++total_count_;
94 0 : }
95 :
96 0 : int64_t Count() const { return total_count_; }
97 0 : bool Empty() const { return total_count_ == 0; }
98 :
99 0 : int64_t Sum() const {
100 0 : int64_t sum = 0;
101 0 : for (const auto& it : samples_)
102 0 : sum += it.second.sum_;
103 0 : return sum;
104 : }
105 :
106 0 : int Max() const {
107 0 : int max = std::numeric_limits<int>::min();
108 0 : for (const auto& it : samples_)
109 0 : max = std::max(it.second.max_, max);
110 0 : return max;
111 : }
112 :
113 0 : void Reset() {
114 0 : for (auto& it : samples_)
115 0 : it.second.Reset();
116 0 : total_count_ = 0;
117 0 : }
118 :
119 0 : int64_t Diff() const {
120 0 : int64_t sum_diff = 0;
121 0 : int count = 0;
122 0 : for (const auto& it : samples_) {
123 0 : if (it.second.count_ > 0) {
124 0 : int64_t diff = it.second.sum_ - it.second.last_sum_;
125 0 : if (diff >= 0) {
126 0 : sum_diff += diff;
127 0 : ++count;
128 : }
129 : }
130 : }
131 0 : return (count > 0) ? sum_diff : -1;
132 : }
133 :
134 : private:
135 0 : struct Stats {
136 0 : void Add(int sample) {
137 0 : sum_ += sample;
138 0 : ++count_;
139 0 : max_ = std::max(sample, max_);
140 0 : }
141 0 : void Set(int sample) {
142 0 : sum_ = sample;
143 0 : ++count_;
144 0 : }
145 0 : void Reset() {
146 0 : if (count_ > 0)
147 0 : last_sum_ = sum_;
148 0 : sum_ = 0;
149 0 : count_ = 0;
150 0 : max_ = std::numeric_limits<int>::min();
151 0 : }
152 :
153 0 : int max_ = std::numeric_limits<int>::min();
154 : int64_t count_ = 0;
155 : int64_t sum_ = 0;
156 : int64_t last_sum_ = 0;
157 : };
158 :
159 : int64_t total_count_;
160 : std::map<uint32_t, Stats> samples_; // Gathered samples mapped by stream id.
161 : };
162 :
163 : // StatsCounter class.
164 0 : StatsCounter::StatsCounter(Clock* clock,
165 : int64_t process_intervals_ms,
166 : bool include_empty_intervals,
167 0 : StatsCounterObserver* observer)
168 : : include_empty_intervals_(include_empty_intervals),
169 : process_intervals_ms_(process_intervals_ms),
170 0 : aggregated_counter_(new AggregatedCounter()),
171 0 : samples_(new Samples()),
172 : clock_(clock),
173 : observer_(observer),
174 : last_process_time_ms_(-1),
175 0 : paused_(false) {
176 0 : RTC_DCHECK_GT(process_intervals_ms_, 0);
177 0 : }
178 :
179 0 : StatsCounter::~StatsCounter() {}
180 :
181 0 : AggregatedStats StatsCounter::GetStats() {
182 0 : return aggregated_counter_->ComputeStats();
183 : }
184 :
185 0 : AggregatedStats StatsCounter::ProcessAndGetStats() {
186 0 : if (HasSample())
187 0 : TryProcess();
188 0 : return aggregated_counter_->ComputeStats();
189 : }
190 :
191 0 : void StatsCounter::ProcessAndPause() {
192 0 : if (HasSample())
193 0 : TryProcess();
194 0 : paused_ = true;
195 0 : }
196 :
197 0 : bool StatsCounter::HasSample() const {
198 0 : return last_process_time_ms_ != -1;
199 : }
200 :
201 0 : bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
202 0 : int64_t now = clock_->TimeInMilliseconds();
203 0 : if (last_process_time_ms_ == -1)
204 0 : last_process_time_ms_ = now;
205 :
206 0 : int64_t diff_ms = now - last_process_time_ms_;
207 0 : if (diff_ms < process_intervals_ms_)
208 0 : return false;
209 :
210 : // Advance number of complete |process_intervals_ms_| that have passed.
211 0 : int64_t num_intervals = diff_ms / process_intervals_ms_;
212 0 : last_process_time_ms_ += num_intervals * process_intervals_ms_;
213 :
214 0 : *elapsed_intervals = num_intervals;
215 0 : return true;
216 : }
217 :
218 0 : void StatsCounter::Set(int sample, uint32_t stream_id) {
219 0 : TryProcess();
220 0 : samples_->Set(sample, stream_id);
221 0 : paused_ = false;
222 0 : }
223 :
224 0 : void StatsCounter::Add(int sample) {
225 0 : TryProcess();
226 0 : samples_->Add(sample, kStreamId0);
227 0 : paused_ = false;
228 0 : }
229 :
230 : // Reports periodically computed metric.
231 0 : void StatsCounter::ReportMetricToAggregatedCounter(
232 : int value,
233 : int num_values_to_add) const {
234 0 : for (int i = 0; i < num_values_to_add; ++i) {
235 0 : aggregated_counter_->Add(value);
236 0 : if (observer_)
237 0 : observer_->OnMetricUpdated(value);
238 : }
239 0 : }
240 :
241 0 : void StatsCounter::TryProcess() {
242 : int elapsed_intervals;
243 0 : if (!TimeToProcess(&elapsed_intervals))
244 0 : return;
245 :
246 : // Get and report periodically computed metric.
247 : int metric;
248 0 : if (GetMetric(&metric))
249 0 : ReportMetricToAggregatedCounter(metric, 1);
250 :
251 : // Report value for elapsed intervals without samples.
252 0 : if (IncludeEmptyIntervals()) {
253 : // If there are no samples, all elapsed intervals are empty (otherwise one
254 : // interval contains sample(s), discard this interval).
255 : int empty_intervals =
256 0 : samples_->Empty() ? elapsed_intervals : (elapsed_intervals - 1);
257 0 : ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
258 0 : empty_intervals);
259 : }
260 :
261 : // Reset samples for elapsed interval.
262 0 : samples_->Reset();
263 : }
264 :
265 0 : bool StatsCounter::IncludeEmptyIntervals() const {
266 0 : return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
267 : }
268 :
269 : // StatsCounter sub-classes.
270 0 : AvgCounter::AvgCounter(Clock* clock,
271 : StatsCounterObserver* observer,
272 0 : bool include_empty_intervals)
273 : : StatsCounter(clock,
274 : kDefaultProcessIntervalMs,
275 : include_empty_intervals,
276 0 : observer) {}
277 :
278 0 : void AvgCounter::Add(int sample) {
279 0 : StatsCounter::Add(sample);
280 0 : }
281 :
282 0 : bool AvgCounter::GetMetric(int* metric) const {
283 0 : int64_t count = samples_->Count();
284 0 : if (count == 0)
285 0 : return false;
286 :
287 0 : *metric = (samples_->Sum() + count / 2) / count;
288 0 : return true;
289 : }
290 :
291 0 : int AvgCounter::GetValueForEmptyInterval() const {
292 0 : return aggregated_counter_->last_sample();
293 : }
294 :
295 0 : MaxCounter::MaxCounter(Clock* clock,
296 : StatsCounterObserver* observer,
297 0 : int64_t process_intervals_ms)
298 : : StatsCounter(clock,
299 : process_intervals_ms,
300 : false, // |include_empty_intervals|
301 0 : observer) {}
302 :
303 0 : void MaxCounter::Add(int sample) {
304 0 : StatsCounter::Add(sample);
305 0 : }
306 :
307 0 : bool MaxCounter::GetMetric(int* metric) const {
308 0 : if (samples_->Empty())
309 0 : return false;
310 :
311 0 : *metric = samples_->Max();
312 0 : return true;
313 : }
314 :
315 0 : int MaxCounter::GetValueForEmptyInterval() const {
316 0 : RTC_NOTREACHED();
317 0 : return 0;
318 : }
319 :
320 0 : PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
321 : : StatsCounter(clock,
322 : kDefaultProcessIntervalMs,
323 : false, // |include_empty_intervals|
324 0 : observer) {}
325 :
326 0 : void PercentCounter::Add(bool sample) {
327 0 : StatsCounter::Add(sample ? 1 : 0);
328 0 : }
329 :
330 0 : bool PercentCounter::GetMetric(int* metric) const {
331 0 : int64_t count = samples_->Count();
332 0 : if (count == 0)
333 0 : return false;
334 :
335 0 : *metric = (samples_->Sum() * 100 + count / 2) / count;
336 0 : return true;
337 : }
338 :
339 0 : int PercentCounter::GetValueForEmptyInterval() const {
340 0 : RTC_NOTREACHED();
341 0 : return 0;
342 : }
343 :
344 0 : PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
345 : : StatsCounter(clock,
346 : kDefaultProcessIntervalMs,
347 : false, // |include_empty_intervals|
348 0 : observer) {}
349 :
350 0 : void PermilleCounter::Add(bool sample) {
351 0 : StatsCounter::Add(sample ? 1 : 0);
352 0 : }
353 :
354 0 : bool PermilleCounter::GetMetric(int* metric) const {
355 0 : int64_t count = samples_->Count();
356 0 : if (count == 0)
357 0 : return false;
358 :
359 0 : *metric = (samples_->Sum() * 1000 + count / 2) / count;
360 0 : return true;
361 : }
362 :
363 0 : int PermilleCounter::GetValueForEmptyInterval() const {
364 0 : RTC_NOTREACHED();
365 0 : return 0;
366 : }
367 :
368 0 : RateCounter::RateCounter(Clock* clock,
369 : StatsCounterObserver* observer,
370 0 : bool include_empty_intervals)
371 : : StatsCounter(clock,
372 : kDefaultProcessIntervalMs,
373 : include_empty_intervals,
374 0 : observer) {}
375 :
376 0 : void RateCounter::Add(int sample) {
377 0 : StatsCounter::Add(sample);
378 0 : }
379 :
380 0 : bool RateCounter::GetMetric(int* metric) const {
381 0 : if (samples_->Empty())
382 0 : return false;
383 :
384 0 : *metric = (samples_->Sum() * 1000 + process_intervals_ms_ / 2) /
385 0 : process_intervals_ms_;
386 0 : return true;
387 : }
388 :
389 0 : int RateCounter::GetValueForEmptyInterval() const {
390 0 : return 0;
391 : }
392 :
393 0 : RateAccCounter::RateAccCounter(Clock* clock,
394 : StatsCounterObserver* observer,
395 0 : bool include_empty_intervals)
396 : : StatsCounter(clock,
397 : kDefaultProcessIntervalMs,
398 : include_empty_intervals,
399 0 : observer) {}
400 :
401 0 : void RateAccCounter::Set(int sample, uint32_t stream_id) {
402 0 : StatsCounter::Set(sample, stream_id);
403 0 : }
404 :
405 0 : bool RateAccCounter::GetMetric(int* metric) const {
406 0 : int64_t diff = samples_->Diff();
407 0 : if (diff < 0 || (!include_empty_intervals_ && diff == 0))
408 0 : return false;
409 :
410 0 : *metric = (diff * 1000 + process_intervals_ms_ / 2) / process_intervals_ms_;
411 0 : return true;
412 : }
413 :
414 0 : int RateAccCounter::GetValueForEmptyInterval() const {
415 0 : return 0;
416 : }
417 :
418 : } // namespace webrtc
|