| /* |
| * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/video/stats_counter.h" |
| |
| #include <algorithm> |
| |
| #include "webrtc/system_wrappers/include/clock.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| // Periodic time interval for processing samples. |
| const int64_t kProcessIntervalMs = 2000; |
| } // namespace |
| |
| // Class holding periodically computed metrics. |
| class AggregatedCounter { |
| public: |
| AggregatedCounter() : sum_(0) {} |
| ~AggregatedCounter() {} |
| |
| void Add(int sample) { |
| sum_ += sample; |
| ++stats_.num_samples; |
| if (stats_.num_samples == 1) { |
| stats_.min = sample; |
| stats_.max = sample; |
| } |
| stats_.min = std::min(sample, stats_.min); |
| stats_.max = std::max(sample, stats_.max); |
| } |
| |
| AggregatedStats ComputeStats() { |
| Compute(); |
| return stats_; |
| } |
| |
| private: |
| void Compute() { |
| if (stats_.num_samples == 0) |
| return; |
| |
| stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples; |
| } |
| int64_t sum_; |
| AggregatedStats stats_; |
| }; |
| |
| // StatsCounter class. |
| StatsCounter::StatsCounter(Clock* clock, |
| bool include_empty_intervals, |
| StatsCounterObserver* observer) |
| : max_(0), |
| sum_(0), |
| num_samples_(0), |
| last_sum_(0), |
| clock_(clock), |
| include_empty_intervals_(include_empty_intervals), |
| observer_(observer), |
| aggregated_counter_(new AggregatedCounter()), |
| last_process_time_ms_(-1) {} |
| |
| StatsCounter::~StatsCounter() {} |
| |
| AggregatedStats StatsCounter::GetStats() { |
| return aggregated_counter_->ComputeStats(); |
| } |
| |
| bool StatsCounter::TimeToProcess() { |
| int64_t now = clock_->TimeInMilliseconds(); |
| if (last_process_time_ms_ == -1) |
| last_process_time_ms_ = now; |
| |
| int64_t diff_ms = now - last_process_time_ms_; |
| if (diff_ms < kProcessIntervalMs) |
| return false; |
| |
| // Advance number of complete kProcessIntervalMs that have passed. |
| int64_t num_intervals = diff_ms / kProcessIntervalMs; |
| last_process_time_ms_ += num_intervals * kProcessIntervalMs; |
| |
| // Add zero for intervals without samples. |
| if (include_empty_intervals_) { |
| for (int64_t i = 0; i < num_intervals - 1; ++i) { |
| aggregated_counter_->Add(0); |
| if (observer_) |
| observer_->OnMetricUpdated(0); |
| } |
| } |
| return true; |
| } |
| |
| void StatsCounter::Set(int sample) { |
| TryProcess(); |
| ++num_samples_; |
| sum_ = sample; |
| } |
| |
| void StatsCounter::Add(int sample) { |
| TryProcess(); |
| ++num_samples_; |
| sum_ += sample; |
| |
| if (num_samples_ == 1) |
| max_ = sample; |
| max_ = std::max(sample, max_); |
| } |
| |
| void StatsCounter::TryProcess() { |
| if (!TimeToProcess()) |
| return; |
| |
| int metric; |
| if (GetMetric(&metric)) { |
| aggregated_counter_->Add(metric); |
| if (observer_) |
| observer_->OnMetricUpdated(metric); |
| } |
| last_sum_ = sum_; |
| sum_ = 0; |
| max_ = 0; |
| num_samples_ = 0; |
| } |
| |
| // StatsCounter sub-classes. |
| AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer) |
| : StatsCounter(clock, |
| false, // |include_empty_intervals| |
| observer) {} |
| |
| void AvgCounter::Add(int sample) { |
| StatsCounter::Add(sample); |
| } |
| |
| bool AvgCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0) |
| return false; |
| *metric = (sum_ + num_samples_ / 2) / num_samples_; |
| return true; |
| } |
| |
| MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) |
| : StatsCounter(clock, |
| false, // |include_empty_intervals| |
| observer) {} |
| |
| void MaxCounter::Add(int sample) { |
| StatsCounter::Add(sample); |
| } |
| |
| bool MaxCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0) |
| return false; |
| *metric = max_; |
| return true; |
| } |
| |
| PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) |
| : StatsCounter(clock, |
| false, // |include_empty_intervals| |
| observer) {} |
| |
| void PercentCounter::Add(bool sample) { |
| StatsCounter::Add(sample ? 1 : 0); |
| } |
| |
| bool PercentCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0) |
| return false; |
| *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; |
| return true; |
| } |
| |
| PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) |
| : StatsCounter(clock, |
| false, // |include_empty_intervals| |
| observer) {} |
| |
| void PermilleCounter::Add(bool sample) { |
| StatsCounter::Add(sample ? 1 : 0); |
| } |
| |
| bool PermilleCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0) |
| return false; |
| *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; |
| return true; |
| } |
| |
| RateCounter::RateCounter(Clock* clock, |
| StatsCounterObserver* observer, |
| bool include_empty_intervals) |
| : StatsCounter(clock, include_empty_intervals, observer) {} |
| |
| void RateCounter::Add(int sample) { |
| StatsCounter::Add(sample); |
| } |
| |
| bool RateCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0) |
| return false; |
| *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; |
| return true; |
| } |
| |
| RateAccCounter::RateAccCounter(Clock* clock, |
| StatsCounterObserver* observer, |
| bool include_empty_intervals) |
| : StatsCounter(clock, include_empty_intervals, observer) {} |
| |
| void RateAccCounter::Set(int sample) { |
| StatsCounter::Set(sample); |
| } |
| |
| bool RateAccCounter::GetMetric(int* metric) const { |
| if (num_samples_ == 0 || last_sum_ > sum_) |
| return false; |
| *metric = |
| ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; |
| return true; |
| } |
| |
| } // namespace webrtc |