Add class which periodically computes statistics.

During a period (e.g. 2 seconds), different metrics can be computed, e.g.:
- average of samples
- percentage/permille of samples
- units per second

Each periodic metric can be either:
- reported to an observer each period
- aggregated during the call (e.g. min, max, average)

BUG=webrtc:5283

Review-Url: https://codereview.webrtc.org/1640053003
Cr-Commit-Position: refs/heads/master@{#12847}
This commit is contained in:
asapersson
2016-05-23 06:07:55 -07:00
committed by Commit bot
parent bca568bfc5
commit 0e9d6d9e0c
6 changed files with 783 additions and 0 deletions

View File

@ -30,6 +30,8 @@ source_set("video") {
"send_delay_stats.h",
"send_statistics_proxy.cc",
"send_statistics_proxy.h",
"stats_counter.cc",
"stats_counter.h",
"stream_synchronization.cc",
"stream_synchronization.h",
"video_capture_input.cc",

View File

@ -0,0 +1,231 @@
/*
* 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)
: StatsCounter(clock,
true, // |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)
: StatsCounter(clock,
true, // |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

View File

@ -0,0 +1,227 @@
/*
* 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.
*/
#ifndef WEBRTC_VIDEO_STATS_COUNTER_H_
#define WEBRTC_VIDEO_STATS_COUNTER_H_
#include <memory>
#include "webrtc/base/constructormagic.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class AggregatedCounter;
class Clock;
// |StatsCounterObserver| is called periodically when a metric is updated.
class StatsCounterObserver {
public:
virtual void OnMetricUpdated(int sample) = 0;
virtual ~StatsCounterObserver() {}
};
struct AggregatedStats {
int64_t num_samples = 0;
int min = -1;
int max = -1;
int average = -1;
// TODO(asapersson): Consider adding median/percentiles.
};
// Classes which periodically computes a metric.
//
// During a period, |kProcessIntervalMs|, different metrics can be computed e.g:
// - |AvgCounter|: average of samples
// - |PercentCounter|: percentage of samples
// - |PermilleCounter|: permille of samples
//
// Each periodic metric can be either:
// - reported to an |observer| each period
// - aggregated during the call (e.g. min, max, average)
//
// periodically computed
// GetMetric() GetMetric() => AggregatedStats
// ^ ^ (e.g. min/max/avg)
// | |
// | * * * * | ** * * * * | ...
// |<- process interval ->|
//
// (*) - samples
//
//
// Example usage:
//
// AvgCounter counter(&clock, nullptr);
// counter.Add(5);
// counter.Add(1);
// counter.Add(6); // process interval passed -> GetMetric() avg:4
// counter.Add(7);
// counter.Add(3); // process interval passed -> GetMetric() avg:5
// counter.Add(10);
// counter.Add(20); // process interval passed -> GetMetric() avg:15
// AggregatedStats stats = counter.GetStats();
// stats: {min:4, max:15, avg:8}
//
// Note: StatsCounter takes ownership of |observer|.
class StatsCounter {
public:
virtual ~StatsCounter();
virtual bool GetMetric(int* metric) const = 0;
AggregatedStats GetStats();
protected:
StatsCounter(Clock* clock,
bool include_empty_intervals,
StatsCounterObserver* observer);
void Add(int sample);
void Set(int sample);
int max_;
int64_t sum_;
int64_t num_samples_;
int64_t last_sum_;
private:
bool TimeToProcess();
void TryProcess();
Clock* const clock_;
const bool include_empty_intervals_;
const std::unique_ptr<StatsCounterObserver> observer_;
const std::unique_ptr<AggregatedCounter> aggregated_counter_;
int64_t last_process_time_ms_;
};
// AvgCounter: average of samples
//
// | * * * | * * | ...
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
// GetMetric | (5 + 1 + 6) / 3 | (5 + 5) / 2 |
//
class AvgCounter : public StatsCounter {
public:
AvgCounter(Clock* clock, StatsCounterObserver* observer);
~AvgCounter() override {}
void Add(int sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(AvgCounter);
};
// MaxCounter: maximum of samples
//
// | * * * | * * | ...
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
// GetMetric | max: (5, 1, 6) | max: (5, 5) |
//
class MaxCounter : public StatsCounter {
public:
MaxCounter(Clock* clock, StatsCounterObserver* observer);
~MaxCounter() override {}
void Add(int sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(MaxCounter);
};
// PercentCounter: percentage of samples
//
// | * * * | * * | ...
// | Add(T) Add(F) Add(T) | Add(F) Add(T) |
// GetMetric | 100 * 2 / 3 | 100 * 1 / 2 |
//
class PercentCounter : public StatsCounter {
public:
PercentCounter(Clock* clock, StatsCounterObserver* observer);
~PercentCounter() override {}
void Add(bool sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(PercentCounter);
};
// PermilleCounter: permille of samples
//
// | * * * | * * | ...
// | Add(T) Add(F) Add(T) | Add(F) Add(T) |
// GetMetric | 1000 * 2 / 3 | 1000 * 1 / 2 |
//
class PermilleCounter : public StatsCounter {
public:
PermilleCounter(Clock* clock, StatsCounterObserver* observer);
~PermilleCounter() override {}
void Add(bool sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(PermilleCounter);
};
// RateCounter: units per second
//
// | * * * | * * | ...
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
// |<------ 2 sec ------->| |
// GetMetric | (5 + 1 + 6) / 2 | (5 + 5) / 2 |
//
class RateCounter : public StatsCounter {
public:
RateCounter(Clock* clock, StatsCounterObserver* observer);
~RateCounter() override {}
void Add(int sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(RateCounter);
};
// RateAccCounter: units per second (used for counters)
//
// | * * * | * * | ...
// | Set(5) Set(6) Set(8) | Set(11) Set(13) |
// |<------ 2 sec ------->| |
// GetMetric | 8 / 2 | (13 - 8) / 2 |
//
class RateAccCounter : public StatsCounter {
public:
RateAccCounter(Clock* clock, StatsCounterObserver* observer);
~RateAccCounter() override {}
void Set(int sample);
private:
bool GetMetric(int* metric) const override;
RTC_DISALLOW_COPY_AND_ASSIGN(RateAccCounter);
};
} // namespace webrtc
#endif // WEBRTC_VIDEO_STATS_COUNTER_H_

View File

@ -0,0 +1,320 @@
/*
* 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 "testing/gtest/include/gtest/gtest.h"
#include "webrtc/system_wrappers/include/clock.h"
namespace webrtc {
namespace {
const int kProcessIntervalMs = 2000;
class StatsCounterObserverImpl : public StatsCounterObserver {
public:
StatsCounterObserverImpl() : num_calls_(0), last_sample_(-1) {}
void OnMetricUpdated(int sample) override {
++num_calls_;
last_sample_ = sample;
}
int num_calls_;
int last_sample_;
};
} // namespace
class StatsCounterTest : public ::testing::Test {
protected:
StatsCounterTest()
: clock_(1234) {}
void AddSampleAndAdvance(int sample, int interval_ms, AvgCounter* counter) {
counter->Add(sample);
clock_.AdvanceTimeMilliseconds(interval_ms);
}
void SetSampleAndAdvance(int sample,
int interval_ms,
RateAccCounter* counter) {
counter->Set(sample);
clock_.AdvanceTimeMilliseconds(interval_ms);
}
void VerifyStatsIsNotSet(const AggregatedStats& stats) {
EXPECT_EQ(0, stats.num_samples);
EXPECT_EQ(-1, stats.min);
EXPECT_EQ(-1, stats.max);
EXPECT_EQ(-1, stats.average);
}
SimulatedClock clock_;
};
TEST_F(StatsCounterTest, NoSamples) {
AvgCounter counter(&clock_, nullptr);
VerifyStatsIsNotSet(counter.GetStats());
}
TEST_F(StatsCounterTest, TestRegisterObserver) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
const int kSample = 22;
AvgCounter counter(&clock_, observer);
AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter);
// Trigger process (sample included in next interval).
counter.Add(111);
EXPECT_EQ(1, observer->num_calls_);
}
TEST_F(StatsCounterTest, VerifyProcessInterval) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
AvgCounter counter(&clock_, observer);
counter.Add(4);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1);
// Try trigger process (interval has not passed).
counter.Add(8);
EXPECT_EQ(0, observer->num_calls_);
VerifyStatsIsNotSet(counter.GetStats());
// Make process interval pass.
clock_.AdvanceTimeMilliseconds(1);
// Trigger process (sample included in next interval).
counter.Add(111);
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(6, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
}
TEST_F(StatsCounterTest, TestMetric_AvgCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
AvgCounter counter(&clock_, observer);
counter.Add(4);
counter.Add(8);
counter.Add(9);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Add(111);
// Average per interval.
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(7, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(7, stats.min);
EXPECT_EQ(7, stats.max);
EXPECT_EQ(7, stats.average);
}
TEST_F(StatsCounterTest, TestMetric_MaxCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
MaxCounter counter(&clock_, observer);
counter.Add(4);
counter.Add(9);
counter.Add(8);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Add(111);
// Average per interval.
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(9, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(9, stats.min);
EXPECT_EQ(9, stats.max);
EXPECT_EQ(9, stats.average);
}
TEST_F(StatsCounterTest, TestMetric_PercentCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
PercentCounter counter(&clock_, observer);
counter.Add(true);
counter.Add(false);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Add(false);
// Percentage per interval.
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(50, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(50, stats.min);
EXPECT_EQ(50, stats.max);
}
TEST_F(StatsCounterTest, TestMetric_PermilleCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
PermilleCounter counter(&clock_, observer);
counter.Add(true);
counter.Add(false);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Add(false);
// Permille per interval.
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(500, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(500, stats.min);
EXPECT_EQ(500, stats.max);
}
TEST_F(StatsCounterTest, TestMetric_RateCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
RateCounter counter(&clock_, observer);
counter.Add(186);
counter.Add(350);
counter.Add(22);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Add(111);
// Rate per interval, (186 + 350 + 22) / 2 sec = 279 samples/sec
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(279, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(279, stats.min);
EXPECT_EQ(279, stats.max);
}
TEST_F(StatsCounterTest, TestMetric_RateAccCounter) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
RateAccCounter counter(&clock_, observer);
counter.Set(175);
counter.Set(188);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
// Trigger process (sample included in next interval).
counter.Set(192);
// Rate per interval: (188 - 0) / 2 sec = 94 samples/sec
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(94, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(94, stats.min);
EXPECT_EQ(94, stats.max);
}
TEST_F(StatsCounterTest, TestGetStats_MultipleIntervals) {
AvgCounter counter(&clock_, nullptr);
const int kSample1 = 1;
const int kSample2 = 5;
const int kSample3 = 8;
const int kSample4 = 11;
const int kSample5 = 50;
AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
AddSampleAndAdvance(kSample2, kProcessIntervalMs, &counter);
AddSampleAndAdvance(kSample3, kProcessIntervalMs, &counter);
AddSampleAndAdvance(kSample4, kProcessIntervalMs, &counter);
AddSampleAndAdvance(kSample5, kProcessIntervalMs, &counter);
// Trigger process (sample included in next interval).
counter.Add(111);
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(5, stats.num_samples);
EXPECT_EQ(kSample1, stats.min);
EXPECT_EQ(kSample5, stats.max);
EXPECT_EQ(15, stats.average);
}
TEST_F(StatsCounterTest, TestGetStatsTwice) {
const int kSample1 = 4;
const int kSample2 = 7;
AvgCounter counter(&clock_, nullptr);
AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
// Trigger process (sample included in next interval).
counter.Add(kSample2);
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(1, stats.num_samples);
EXPECT_EQ(kSample1, stats.min);
EXPECT_EQ(kSample1, stats.max);
// Trigger process (sample included in next interval).
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
counter.Add(111);
stats = counter.GetStats();
EXPECT_EQ(2, stats.num_samples);
EXPECT_EQ(kSample1, stats.min);
EXPECT_EQ(kSample2, stats.max);
EXPECT_EQ(6, stats.average);
}
TEST_F(StatsCounterTest, TestRateAccCounter_NegativeRateIgnored) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
const int kSample1 = 200; // 200 / 2 sec
const int kSample2 = 100; // -100 / 2 sec - negative ignored
const int kSample3 = 700; // 600 / 2 sec
RateAccCounter counter(&clock_, observer);
SetSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
SetSampleAndAdvance(kSample2, kProcessIntervalMs, &counter);
SetSampleAndAdvance(kSample3, kProcessIntervalMs, &counter);
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(100, observer->last_sample_);
// Trigger process (sample included in next interval).
counter.Set(2000);
EXPECT_EQ(2, observer->num_calls_);
EXPECT_EQ(300, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(2, stats.num_samples);
EXPECT_EQ(100, stats.min);
EXPECT_EQ(300, stats.max);
EXPECT_EQ(200, stats.average);
}
TEST_F(StatsCounterTest, TestAvgCounter_IntervalsWithoutSamplesIgnored) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
AvgCounter counter(&clock_, observer);
AddSampleAndAdvance(6, kProcessIntervalMs * 4 - 1, &counter);
// Trigger process (sample included in next interval).
counter.Add(8);
// [6:1], two intervals without samples passed.
EXPECT_EQ(1, observer->num_calls_);
EXPECT_EQ(6, observer->last_sample_);
// Make last interval pass.
clock_.AdvanceTimeMilliseconds(1);
counter.Add(111); // Trigger process (sample included in next interval).
// [6:1],[8:1]
EXPECT_EQ(2, observer->num_calls_);
EXPECT_EQ(8, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(2, stats.num_samples);
EXPECT_EQ(6, stats.min);
EXPECT_EQ(8, stats.max);
}
TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIncluded) {
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
const int kSample1 = 50; // 50 / 2 sec
const int kSample2 = 20; // 20 / 2 sec
RateCounter counter(&clock_, observer);
counter.Add(kSample1);
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 3 - 1);
// Trigger process (sample included in next interval).
counter.Add(kSample2);
// [0:1],[25:1], one interval without samples passed.
EXPECT_EQ(2, observer->num_calls_);
EXPECT_EQ(25, observer->last_sample_);
// Make last interval pass.
clock_.AdvanceTimeMilliseconds(1);
counter.Add(111); // Trigger process (sample included in next interval).
// [0:1],[10:1],[25:1]
EXPECT_EQ(3, observer->num_calls_);
EXPECT_EQ(10, observer->last_sample_);
// Aggregated stats.
AggregatedStats stats = counter.GetStats();
EXPECT_EQ(3, stats.num_samples);
EXPECT_EQ(0, stats.min);
EXPECT_EQ(25, stats.max);
}
} // namespace webrtc

View File

@ -44,6 +44,8 @@
'video/send_delay_stats.h',
'video/send_statistics_proxy.cc',
'video/send_statistics_proxy.h',
'video/stats_counter.cc',
'video/stats_counter.h',
'video/stream_synchronization.cc',
'video/stream_synchronization.h',
'video/video_capture_input.cc',

View File

@ -170,6 +170,7 @@
'video/report_block_stats_unittest.cc',
'video/send_delay_stats_unittest.cc',
'video/send_statistics_proxy_unittest.cc',
'video/stats_counter_unittest.cc',
'video/stream_synchronization_unittest.cc',
'video/video_capture_input_unittest.cc',
'video/video_decoder_unittest.cc',