diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index 15954af663..b5e1b225de 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -240,8 +240,6 @@ rtc_source_set("video_coding_utility") { "utility/framerate_controller.h", "utility/ivf_file_writer.cc", "utility/ivf_file_writer.h", - "utility/moving_average.cc", - "utility/moving_average.h", "utility/quality_scaler.cc", "utility/quality_scaler.h", "utility/simulcast_rate_allocator.cc", @@ -858,7 +856,6 @@ if (rtc_include_tests) { "utility/frame_dropper_unittest.cc", "utility/framerate_controller_unittest.cc", "utility/ivf_file_writer_unittest.cc", - "utility/moving_average_unittest.cc", "utility/quality_scaler_unittest.cc", "utility/simulcast_rate_allocator_unittest.cc", "video_codec_initializer_unittest.cc", diff --git a/modules/video_coding/utility/moving_average.cc b/modules/video_coding/utility/moving_average.cc deleted file mode 100644 index eb23e3d92f..0000000000 --- a/modules/video_coding/utility/moving_average.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 "modules/video_coding/utility/moving_average.h" - -#include - -namespace webrtc { - -MovingAverage::MovingAverage(size_t s) : sum_history_(s + 1, 0) {} -MovingAverage::~MovingAverage() = default; - -void MovingAverage::AddSample(int sample) { - count_++; - sum_ += sample; - sum_history_[count_ % sum_history_.size()] = sum_; -} - -absl::optional MovingAverage::GetAverage() const { - return GetAverage(size()); -} - -absl::optional MovingAverage::GetAverage(size_t num_samples) const { - if (num_samples > size() || num_samples == 0) - return absl::nullopt; - int sum = sum_ - sum_history_[(count_ - num_samples) % sum_history_.size()]; - return sum / static_cast(num_samples); -} - -void MovingAverage::Reset() { - count_ = 0; - sum_ = 0; - std::fill(sum_history_.begin(), sum_history_.end(), 0); -} - -size_t MovingAverage::size() const { - return std::min(count_, sum_history_.size() - 1); -} - -} // namespace webrtc diff --git a/modules/video_coding/utility/moving_average.h b/modules/video_coding/utility/moving_average.h deleted file mode 100644 index 77ce633b18..0000000000 --- a/modules/video_coding/utility/moving_average.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2015 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 MODULES_VIDEO_CODING_UTILITY_MOVING_AVERAGE_H_ -#define MODULES_VIDEO_CODING_UTILITY_MOVING_AVERAGE_H_ - -#include - -#include "absl/types/optional.h" - -namespace webrtc { -class MovingAverage { - public: - explicit MovingAverage(size_t s); - ~MovingAverage(); - void AddSample(int sample); - absl::optional GetAverage() const; - absl::optional GetAverage(size_t num_samples) const; - void Reset(); - size_t size() const; - - private: - size_t count_ = 0; - int sum_ = 0; - std::vector sum_history_; -}; -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_UTILITY_MOVING_AVERAGE_H_ diff --git a/modules/video_coding/utility/moving_average_unittest.cc b/modules/video_coding/utility/moving_average_unittest.cc deleted file mode 100644 index 72fc775a82..0000000000 --- a/modules/video_coding/utility/moving_average_unittest.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 "modules/video_coding/utility/moving_average.h" - -#include "test/gtest.h" - -TEST(MovingAverageTest, EmptyAverage) { - webrtc::MovingAverage moving_average(1); - EXPECT_EQ(0u, moving_average.size()); - EXPECT_FALSE(moving_average.GetAverage(0)); -} - -// Test single value. -TEST(MovingAverageTest, OneElement) { - webrtc::MovingAverage moving_average(1); - moving_average.AddSample(3); - EXPECT_EQ(1u, moving_average.size()); - EXPECT_EQ(3, *moving_average.GetAverage()); - EXPECT_EQ(3, *moving_average.GetAverage(1)); - EXPECT_FALSE(moving_average.GetAverage(2)); -} - -TEST(MovingAverageTest, GetAverage) { - webrtc::MovingAverage moving_average(1024); - moving_average.AddSample(1); - moving_average.AddSample(1); - moving_average.AddSample(3); - moving_average.AddSample(3); - EXPECT_EQ(*moving_average.GetAverage(4), 2); - EXPECT_EQ(*moving_average.GetAverage(2), 3); - EXPECT_FALSE(moving_average.GetAverage(0)); -} - -TEST(MovingAverageTest, Reset) { - webrtc::MovingAverage moving_average(5); - moving_average.AddSample(1); - EXPECT_EQ(1, *moving_average.GetAverage(1)); - moving_average.Reset(); - EXPECT_FALSE(moving_average.GetAverage(1)); - EXPECT_FALSE(moving_average.GetAverage(6)); -} - -TEST(MovingAverageTest, ManySamples) { - webrtc::MovingAverage moving_average(10); - for (int i = 1; i < 11; i++) { - moving_average.AddSample(i); - } - EXPECT_EQ(*moving_average.GetAverage(), 5); - moving_average.Reset(); - for (int i = 1; i < 2001; i++) { - moving_average.AddSample(i); - } - EXPECT_EQ(*moving_average.GetAverage(), 1995); -} diff --git a/modules/video_coding/utility/quality_scaler.cc b/modules/video_coding/utility/quality_scaler.cc index ea19e418c9..32ae166a9c 100644 --- a/modules/video_coding/utility/quality_scaler.cc +++ b/modules/video_coding/utility/quality_scaler.cc @@ -173,8 +173,8 @@ void QualityScaler::CheckQp() { // If we have not observed at least this many frames we can't make a good // scaling decision. const size_t frames = config_.use_all_drop_reasons - ? framedrop_percent_all_.size() - : framedrop_percent_media_opt_.size(); + ? framedrop_percent_all_.Size() + : framedrop_percent_media_opt_.Size(); if (frames < kMinFramesNeededToScale) { observed_enough_frames_ = false; return; @@ -183,8 +183,9 @@ void QualityScaler::CheckQp() { // Check if we should scale down due to high frame drop. const absl::optional drop_rate = - config_.use_all_drop_reasons ? framedrop_percent_all_.GetAverage() - : framedrop_percent_media_opt_.GetAverage(); + config_.use_all_drop_reasons + ? framedrop_percent_all_.GetAverageRoundedDown() + : framedrop_percent_media_opt_.GetAverageRoundedDown(); if (drop_rate && *drop_rate >= kFramedropPercentThreshold) { RTC_LOG(LS_INFO) << "Reporting high QP, framedrop percent " << *drop_rate; ReportQpHigh(); @@ -192,11 +193,12 @@ void QualityScaler::CheckQp() { } // Check if we should scale up or down based on QP. - const absl::optional avg_qp_high = qp_smoother_high_ - ? qp_smoother_high_->GetAvg() - : average_qp_.GetAverage(); + const absl::optional avg_qp_high = + qp_smoother_high_ ? qp_smoother_high_->GetAvg() + : average_qp_.GetAverageRoundedDown(); const absl::optional avg_qp_low = - qp_smoother_low_ ? qp_smoother_low_->GetAvg() : average_qp_.GetAverage(); + qp_smoother_low_ ? qp_smoother_low_->GetAvg() + : average_qp_.GetAverageRoundedDown(); if (avg_qp_high && avg_qp_low) { RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp_high << " (" << *avg_qp_low << ")."; diff --git a/modules/video_coding/utility/quality_scaler.h b/modules/video_coding/utility/quality_scaler.h index 3091ebf36c..272c02e53a 100644 --- a/modules/video_coding/utility/quality_scaler.h +++ b/modules/video_coding/utility/quality_scaler.h @@ -17,8 +17,8 @@ #include "absl/types/optional.h" #include "api/video_codecs/video_encoder.h" #include "common_types.h" // NOLINT(build/include) -#include "modules/video_coding/utility/moving_average.h" #include "rtc_base/experiments/quality_scaling_experiment.h" +#include "rtc_base/numerics/moving_average.h" #include "rtc_base/sequenced_task_checker.h" namespace webrtc { @@ -79,9 +79,10 @@ class QualityScaler { const VideoEncoder::QpThresholds thresholds_; const int64_t sampling_period_ms_; bool fast_rampup_ RTC_GUARDED_BY(&task_checker_); - MovingAverage average_qp_ RTC_GUARDED_BY(&task_checker_); - MovingAverage framedrop_percent_media_opt_ RTC_GUARDED_BY(&task_checker_); - MovingAverage framedrop_percent_all_ RTC_GUARDED_BY(&task_checker_); + rtc::MovingAverage average_qp_ RTC_GUARDED_BY(&task_checker_); + rtc::MovingAverage framedrop_percent_media_opt_ + RTC_GUARDED_BY(&task_checker_); + rtc::MovingAverage framedrop_percent_all_ RTC_GUARDED_BY(&task_checker_); // Used by QualityScalingExperiment. const bool experiment_enabled_; diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 20ba8244bc..2adeaced54 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -68,7 +68,7 @@ rtc_source_set("compile_assert_c") { } # The subset of rtc_base approved for use outside of libjingle. -# TODO(bugs.webrtc.org/9838): Create small and focues build targets and remove +# TODO(bugs.webrtc.org/9838): Create small and focused build targets and remove # the old concept of rtc_base and rtc_base_approved. rtc_source_set("rtc_base_approved") { visibility = [ "*" ] @@ -656,6 +656,8 @@ rtc_static_library("rtc_numerics") { sources = [ "numerics/exp_filter.cc", "numerics/exp_filter.h", + "numerics/moving_average.cc", + "numerics/moving_average.h", "numerics/moving_median_filter.h", "numerics/percentile_filter.h", "numerics/sequence_number_util.h", @@ -1211,6 +1213,7 @@ if (rtc_include_tests) { sources = [ "numerics/exp_filter_unittest.cc", + "numerics/moving_average_unittest.cc", "numerics/moving_median_filter_unittest.cc", "numerics/percentile_filter_unittest.cc", "numerics/sequence_number_util_unittest.cc", diff --git a/rtc_base/numerics/moving_average.cc b/rtc_base/numerics/moving_average.cc new file mode 100644 index 0000000000..c825839227 --- /dev/null +++ b/rtc_base/numerics/moving_average.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 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 "rtc_base/numerics/moving_average.h" + +#include + +#include "rtc_base/checks.h" + +namespace rtc { + +MovingAverage::MovingAverage(size_t window_size) : history_(window_size, 0) { + // Limit window size to avoid overflow. + RTC_DCHECK_LE(window_size, (int64_t{1} << 32) - 1); +} +MovingAverage::~MovingAverage() = default; + +void MovingAverage::AddSample(int sample) { + count_++; + size_t index = count_ % history_.size(); + if (count_ > history_.size()) + sum_ -= history_[index]; + sum_ += sample; + history_[index] = sample; +} + +absl::optional MovingAverage::GetAverageRoundedDown() const { + if (count_ == 0) + return absl::nullopt; + return sum_ / Size(); +} + +absl::optional MovingAverage::GetAverageRoundedToClosest() const { + if (count_ == 0) + return absl::nullopt; + return (sum_ + Size() / 2) / Size(); +} + +absl::optional MovingAverage::GetUnroundedAverage() const { + if (count_ == 0) + return absl::nullopt; + return sum_ / static_cast(Size()); +} + +void MovingAverage::Reset() { + count_ = 0; + sum_ = 0; +} + +size_t MovingAverage::Size() const { + return std::min(count_, history_.size()); +} +} // namespace rtc diff --git a/rtc_base/numerics/moving_average.h b/rtc_base/numerics/moving_average.h new file mode 100644 index 0000000000..770e47d86f --- /dev/null +++ b/rtc_base/numerics/moving_average.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 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 RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ +#define RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ + +#include + +#include "absl/types/optional.h" + +namespace rtc { + +// Calculates average over fixed size window. If there are less than window +// size elements, calculates average of all inserted so far elements. +// +class MovingAverage { + public: + // Maximum supported window size is 2^32 - 1. + explicit MovingAverage(size_t window_size); + ~MovingAverage(); + // MovingAverage is neither copyable nor movable. + MovingAverage(const MovingAverage&) = delete; + MovingAverage& operator=(const MovingAverage&) = delete; + + // Adds new sample. If the window is full, the oldest element is pushed out. + void AddSample(int sample); + + // Returns rounded down average of last |window_size| elements or all + // elements if there are not enough of them. Returns nullopt if there were + // no elements added. + absl::optional GetAverageRoundedDown() const; + + // Same as above but rounded to the closest integer. + absl::optional GetAverageRoundedToClosest() const; + + // Returns unrounded average over the window. + absl::optional GetUnroundedAverage() const; + + // Resets to the initial state before any elements were added. + void Reset(); + + // Returns number of elements in the window. + size_t Size() const; + + private: + // Total number of samples added to the class since last reset. + size_t count_ = 0; + // Sum of the samples in the moving window. + int64_t sum_ = 0; + // Circular buffer for all the samples in the moving window. + // Size is always |window_size| + std::vector history_; +}; + +} // namespace rtc +#endif // RTC_BASE_NUMERICS_MOVING_AVERAGE_H_ diff --git a/rtc_base/numerics/moving_average_unittest.cc b/rtc_base/numerics/moving_average_unittest.cc new file mode 100644 index 0000000000..9bc9a1aef8 --- /dev/null +++ b/rtc_base/numerics/moving_average_unittest.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018 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 "rtc_base/numerics/moving_average.h" + +#include "test/gtest.h" + +namespace test { + +TEST(MovingAverageTest, EmptyAverage) { + rtc::MovingAverage moving_average(1); + EXPECT_EQ(0u, moving_average.Size()); + EXPECT_EQ(absl::nullopt, moving_average.GetAverageRoundedDown()); +} + +// Test single value. +TEST(MovingAverageTest, OneElement) { + rtc::MovingAverage moving_average(1); + moving_average.AddSample(3); + EXPECT_EQ(1u, moving_average.Size()); + EXPECT_EQ(3, *moving_average.GetAverageRoundedDown()); +} + +TEST(MovingAverageTest, GetAverage) { + rtc::MovingAverage moving_average(1024); + moving_average.AddSample(1); + moving_average.AddSample(1); + moving_average.AddSample(3); + moving_average.AddSample(3); + EXPECT_EQ(*moving_average.GetAverageRoundedDown(), 2); + EXPECT_EQ(*moving_average.GetAverageRoundedToClosest(), 2); +} + +TEST(MovingAverageTest, GetAverageRoundedDownRounds) { + rtc::MovingAverage moving_average(1024); + moving_average.AddSample(1); + moving_average.AddSample(2); + moving_average.AddSample(2); + moving_average.AddSample(2); + EXPECT_EQ(*moving_average.GetAverageRoundedDown(), 1); +} + +TEST(MovingAverageTest, GetAverageRoundedToClosestRounds) { + rtc::MovingAverage moving_average(1024); + moving_average.AddSample(1); + moving_average.AddSample(2); + moving_average.AddSample(2); + moving_average.AddSample(2); + EXPECT_EQ(*moving_average.GetAverageRoundedToClosest(), 2); +} + +TEST(MovingAverageTest, Reset) { + rtc::MovingAverage moving_average(5); + moving_average.AddSample(1); + EXPECT_EQ(1, *moving_average.GetAverageRoundedDown()); + EXPECT_EQ(1, *moving_average.GetAverageRoundedToClosest()); + + moving_average.Reset(); + + EXPECT_FALSE(moving_average.GetAverageRoundedDown()); + moving_average.AddSample(10); + EXPECT_EQ(10, *moving_average.GetAverageRoundedDown()); + EXPECT_EQ(10, *moving_average.GetAverageRoundedToClosest()); +} + +TEST(MovingAverageTest, ManySamples) { + rtc::MovingAverage moving_average(10); + for (int i = 1; i < 11; i++) { + moving_average.AddSample(i); + } + EXPECT_EQ(*moving_average.GetAverageRoundedDown(), 5); + EXPECT_EQ(*moving_average.GetAverageRoundedToClosest(), 6); + for (int i = 1; i < 2001; i++) { + moving_average.AddSample(i); + } + EXPECT_EQ(*moving_average.GetAverageRoundedDown(), 1995); + EXPECT_EQ(*moving_average.GetAverageRoundedToClosest(), 1996); +} + +} // namespace test