Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
463
TMessagesProj/jni/voip/webrtc/video/stats_counter.cc
Normal file
463
TMessagesProj/jni/voip/webrtc/video/stats_counter.cc
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* 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 "video/stats_counter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
// Default periodic time interval for processing samples.
|
||||
const int64_t kDefaultProcessIntervalMs = 2000;
|
||||
const uint32_t kStreamId0 = 0;
|
||||
} // namespace
|
||||
|
||||
std::string AggregatedStats::ToString() const {
|
||||
return ToStringWithMultiplier(1);
|
||||
}
|
||||
|
||||
std::string AggregatedStats::ToStringWithMultiplier(int multiplier) const {
|
||||
rtc::StringBuilder ss;
|
||||
ss << "periodic_samples:" << num_samples << ", {";
|
||||
ss << "min:" << (min * multiplier) << ", ";
|
||||
ss << "avg:" << (average * multiplier) << ", ";
|
||||
ss << "max:" << (max * multiplier) << "}";
|
||||
return ss.Release();
|
||||
}
|
||||
|
||||
// Class holding periodically computed metrics.
|
||||
class AggregatedCounter {
|
||||
public:
|
||||
AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
|
||||
~AggregatedCounter() {}
|
||||
|
||||
void Add(int sample) {
|
||||
last_sample_ = sample;
|
||||
sum_samples_ += 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_;
|
||||
}
|
||||
|
||||
bool Empty() const { return stats_.num_samples == 0; }
|
||||
|
||||
int last_sample() const { return last_sample_; }
|
||||
|
||||
private:
|
||||
void Compute() {
|
||||
if (stats_.num_samples == 0)
|
||||
return;
|
||||
|
||||
stats_.average =
|
||||
(sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
|
||||
}
|
||||
int last_sample_;
|
||||
int64_t sum_samples_;
|
||||
AggregatedStats stats_;
|
||||
};
|
||||
|
||||
// Class holding gathered samples within a process interval.
|
||||
class Samples {
|
||||
public:
|
||||
Samples() : total_count_(0) {}
|
||||
~Samples() {}
|
||||
|
||||
void Add(int sample, uint32_t stream_id) {
|
||||
samples_[stream_id].Add(sample);
|
||||
++total_count_;
|
||||
}
|
||||
void Set(int64_t sample, uint32_t stream_id) {
|
||||
samples_[stream_id].Set(sample);
|
||||
++total_count_;
|
||||
}
|
||||
void SetLast(int64_t sample, uint32_t stream_id) {
|
||||
samples_[stream_id].SetLast(sample);
|
||||
}
|
||||
int64_t GetLast(uint32_t stream_id) { return samples_[stream_id].GetLast(); }
|
||||
|
||||
int64_t Count() const { return total_count_; }
|
||||
bool Empty() const { return total_count_ == 0; }
|
||||
|
||||
int64_t Sum() const {
|
||||
int64_t sum = 0;
|
||||
for (const auto& it : samples_)
|
||||
sum += it.second.sum_;
|
||||
return sum;
|
||||
}
|
||||
|
||||
int Max() const {
|
||||
int max = std::numeric_limits<int>::min();
|
||||
for (const auto& it : samples_)
|
||||
max = std::max(it.second.max_, max);
|
||||
return max;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
for (auto& it : samples_)
|
||||
it.second.Reset();
|
||||
total_count_ = 0;
|
||||
}
|
||||
|
||||
int64_t Diff() const {
|
||||
int64_t sum_diff = 0;
|
||||
int count = 0;
|
||||
for (const auto& it : samples_) {
|
||||
if (it.second.count_ > 0) {
|
||||
int64_t diff = it.second.sum_ - it.second.last_sum_;
|
||||
if (diff >= 0) {
|
||||
sum_diff += diff;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count > 0) ? sum_diff : -1;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Stats {
|
||||
void Add(int sample) {
|
||||
sum_ += sample;
|
||||
++count_;
|
||||
max_ = std::max(sample, max_);
|
||||
}
|
||||
void Set(int64_t sample) {
|
||||
sum_ = sample;
|
||||
++count_;
|
||||
}
|
||||
void SetLast(int64_t sample) { last_sum_ = sample; }
|
||||
int64_t GetLast() const { return last_sum_; }
|
||||
void Reset() {
|
||||
if (count_ > 0)
|
||||
last_sum_ = sum_;
|
||||
sum_ = 0;
|
||||
count_ = 0;
|
||||
max_ = std::numeric_limits<int>::min();
|
||||
}
|
||||
|
||||
int max_ = std::numeric_limits<int>::min();
|
||||
int64_t count_ = 0;
|
||||
int64_t sum_ = 0;
|
||||
int64_t last_sum_ = 0;
|
||||
};
|
||||
|
||||
int64_t total_count_;
|
||||
std::map<uint32_t, Stats> samples_; // Gathered samples mapped by stream id.
|
||||
};
|
||||
|
||||
// StatsCounter class.
|
||||
StatsCounter::StatsCounter(Clock* clock,
|
||||
int64_t process_intervals_ms,
|
||||
bool include_empty_intervals,
|
||||
StatsCounterObserver* observer)
|
||||
: include_empty_intervals_(include_empty_intervals),
|
||||
process_intervals_ms_(process_intervals_ms),
|
||||
aggregated_counter_(new AggregatedCounter()),
|
||||
samples_(new Samples()),
|
||||
clock_(clock),
|
||||
observer_(observer),
|
||||
last_process_time_ms_(-1),
|
||||
paused_(false),
|
||||
pause_time_ms_(-1),
|
||||
min_pause_time_ms_(0) {
|
||||
RTC_DCHECK_GT(process_intervals_ms_, 0);
|
||||
}
|
||||
|
||||
StatsCounter::~StatsCounter() {}
|
||||
|
||||
AggregatedStats StatsCounter::GetStats() {
|
||||
return aggregated_counter_->ComputeStats();
|
||||
}
|
||||
|
||||
AggregatedStats StatsCounter::ProcessAndGetStats() {
|
||||
if (HasSample())
|
||||
TryProcess();
|
||||
return aggregated_counter_->ComputeStats();
|
||||
}
|
||||
|
||||
void StatsCounter::ProcessAndPauseForDuration(int64_t min_pause_time_ms) {
|
||||
ProcessAndPause();
|
||||
min_pause_time_ms_ = min_pause_time_ms;
|
||||
}
|
||||
|
||||
void StatsCounter::ProcessAndPause() {
|
||||
if (HasSample())
|
||||
TryProcess();
|
||||
paused_ = true;
|
||||
pause_time_ms_ = clock_->TimeInMilliseconds();
|
||||
}
|
||||
|
||||
void StatsCounter::ProcessAndStopPause() {
|
||||
if (HasSample())
|
||||
TryProcess();
|
||||
Resume();
|
||||
}
|
||||
|
||||
bool StatsCounter::HasSample() const {
|
||||
return last_process_time_ms_ != -1;
|
||||
}
|
||||
|
||||
bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
|
||||
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 < process_intervals_ms_)
|
||||
return false;
|
||||
|
||||
// Advance number of complete `process_intervals_ms_` that have passed.
|
||||
int64_t num_intervals = diff_ms / process_intervals_ms_;
|
||||
last_process_time_ms_ += num_intervals * process_intervals_ms_;
|
||||
|
||||
*elapsed_intervals = num_intervals;
|
||||
return true;
|
||||
}
|
||||
|
||||
void StatsCounter::Add(int sample) {
|
||||
TryProcess();
|
||||
samples_->Add(sample, kStreamId0);
|
||||
ResumeIfMinTimePassed();
|
||||
}
|
||||
|
||||
void StatsCounter::Set(int64_t sample, uint32_t stream_id) {
|
||||
if (paused_ && sample == samples_->GetLast(stream_id)) {
|
||||
// Do not add same sample while paused (will reset pause).
|
||||
return;
|
||||
}
|
||||
TryProcess();
|
||||
samples_->Set(sample, stream_id);
|
||||
ResumeIfMinTimePassed();
|
||||
}
|
||||
|
||||
void StatsCounter::SetLast(int64_t sample, uint32_t stream_id) {
|
||||
RTC_DCHECK(!HasSample()) << "Should be set before first sample is added.";
|
||||
samples_->SetLast(sample, stream_id);
|
||||
}
|
||||
|
||||
// Reports periodically computed metric.
|
||||
void StatsCounter::ReportMetricToAggregatedCounter(
|
||||
int value,
|
||||
int num_values_to_add) const {
|
||||
for (int i = 0; i < num_values_to_add; ++i) {
|
||||
aggregated_counter_->Add(value);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(value);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCounter::TryProcess() {
|
||||
int elapsed_intervals;
|
||||
if (!TimeToProcess(&elapsed_intervals))
|
||||
return;
|
||||
|
||||
// Get and report periodically computed metric.
|
||||
int metric;
|
||||
if (GetMetric(&metric))
|
||||
ReportMetricToAggregatedCounter(metric, 1);
|
||||
|
||||
// Report value for elapsed intervals without samples.
|
||||
if (IncludeEmptyIntervals()) {
|
||||
// If there are no samples, all elapsed intervals are empty (otherwise one
|
||||
// interval contains sample(s), discard this interval).
|
||||
int empty_intervals =
|
||||
samples_->Empty() ? elapsed_intervals : (elapsed_intervals - 1);
|
||||
ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
|
||||
empty_intervals);
|
||||
}
|
||||
|
||||
// Reset samples for elapsed interval.
|
||||
samples_->Reset();
|
||||
}
|
||||
|
||||
bool StatsCounter::IncludeEmptyIntervals() const {
|
||||
return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
|
||||
}
|
||||
void StatsCounter::ResumeIfMinTimePassed() {
|
||||
if (paused_ &&
|
||||
(clock_->TimeInMilliseconds() - pause_time_ms_) >= min_pause_time_ms_) {
|
||||
Resume();
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCounter::Resume() {
|
||||
paused_ = false;
|
||||
min_pause_time_ms_ = 0;
|
||||
}
|
||||
|
||||
// StatsCounter sub-classes.
|
||||
AvgCounter::AvgCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
: StatsCounter(clock,
|
||||
kDefaultProcessIntervalMs,
|
||||
include_empty_intervals,
|
||||
observer) {}
|
||||
|
||||
void AvgCounter::Add(int sample) {
|
||||
StatsCounter::Add(sample);
|
||||
}
|
||||
|
||||
bool AvgCounter::GetMetric(int* metric) const {
|
||||
int64_t count = samples_->Count();
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
*metric = (samples_->Sum() + count / 2) / count;
|
||||
return true;
|
||||
}
|
||||
|
||||
int AvgCounter::GetValueForEmptyInterval() const {
|
||||
return aggregated_counter_->last_sample();
|
||||
}
|
||||
|
||||
MaxCounter::MaxCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
int64_t process_intervals_ms)
|
||||
: StatsCounter(clock,
|
||||
process_intervals_ms,
|
||||
false, // `include_empty_intervals`
|
||||
observer) {}
|
||||
|
||||
void MaxCounter::Add(int sample) {
|
||||
StatsCounter::Add(sample);
|
||||
}
|
||||
|
||||
bool MaxCounter::GetMetric(int* metric) const {
|
||||
if (samples_->Empty())
|
||||
return false;
|
||||
|
||||
*metric = samples_->Max();
|
||||
return true;
|
||||
}
|
||||
|
||||
int MaxCounter::GetValueForEmptyInterval() const {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
kDefaultProcessIntervalMs,
|
||||
false, // `include_empty_intervals`
|
||||
observer) {}
|
||||
|
||||
void PercentCounter::Add(bool sample) {
|
||||
StatsCounter::Add(sample ? 1 : 0);
|
||||
}
|
||||
|
||||
bool PercentCounter::GetMetric(int* metric) const {
|
||||
int64_t count = samples_->Count();
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
*metric = (samples_->Sum() * 100 + count / 2) / count;
|
||||
return true;
|
||||
}
|
||||
|
||||
int PercentCounter::GetValueForEmptyInterval() const {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
kDefaultProcessIntervalMs,
|
||||
false, // `include_empty_intervals`
|
||||
observer) {}
|
||||
|
||||
void PermilleCounter::Add(bool sample) {
|
||||
StatsCounter::Add(sample ? 1 : 0);
|
||||
}
|
||||
|
||||
bool PermilleCounter::GetMetric(int* metric) const {
|
||||
int64_t count = samples_->Count();
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
*metric = (samples_->Sum() * 1000 + count / 2) / count;
|
||||
return true;
|
||||
}
|
||||
|
||||
int PermilleCounter::GetValueForEmptyInterval() const {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
RateCounter::RateCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
: StatsCounter(clock,
|
||||
kDefaultProcessIntervalMs,
|
||||
include_empty_intervals,
|
||||
observer) {}
|
||||
|
||||
void RateCounter::Add(int sample) {
|
||||
StatsCounter::Add(sample);
|
||||
}
|
||||
|
||||
bool RateCounter::GetMetric(int* metric) const {
|
||||
if (samples_->Empty())
|
||||
return false;
|
||||
|
||||
*metric = (samples_->Sum() * 1000 + process_intervals_ms_ / 2) /
|
||||
process_intervals_ms_;
|
||||
return true;
|
||||
}
|
||||
|
||||
int RateCounter::GetValueForEmptyInterval() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
RateAccCounter::RateAccCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
: StatsCounter(clock,
|
||||
kDefaultProcessIntervalMs,
|
||||
include_empty_intervals,
|
||||
observer) {}
|
||||
|
||||
void RateAccCounter::Set(int64_t sample, uint32_t stream_id) {
|
||||
StatsCounter::Set(sample, stream_id);
|
||||
}
|
||||
|
||||
void RateAccCounter::SetLast(int64_t sample, uint32_t stream_id) {
|
||||
StatsCounter::SetLast(sample, stream_id);
|
||||
}
|
||||
|
||||
bool RateAccCounter::GetMetric(int* metric) const {
|
||||
int64_t diff = samples_->Diff();
|
||||
if (diff < 0 || (!include_empty_intervals_ && diff == 0))
|
||||
return false;
|
||||
|
||||
*metric = (diff * 1000 + process_intervals_ms_ / 2) / process_intervals_ms_;
|
||||
return true;
|
||||
}
|
||||
|
||||
int RateAccCounter::GetValueForEmptyInterval() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
Add table
Add a link
Reference in a new issue