From 517678cc49f73227e077525c955f046d0e2c12f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85sa=20Persson?= Date: Mon, 6 May 2019 14:17:35 +0200 Subject: [PATCH] Add ability to configure quality scaler settings through field trial. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit optional min_frames: The minimum number frames to observe to make a scaling decision. Default: kMinFramesNeededToScale in quality_scaler.cc optional initial_scale_factor: The sample period scale factor. Default: kSamplePeriodScaleFactor in quality_scaler.cc optional scale_factor: Option to use a reduced sampling interval when last check did not result in an adaptation (if unset the initial_scale_factor is used). Bug: none Change-Id: I3bb955d1f8d7d7d49bc118361614b5aa59605231 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/135125 Commit-Queue: Åsa Persson Reviewed-by: Stefan Holmer Cr-Commit-Position: refs/heads/master@{#27860} --- modules/video_coding/BUILD.gn | 1 + .../video_coding/utility/quality_scaler.cc | 26 +++++-- modules/video_coding/utility/quality_scaler.h | 6 ++ rtc_base/experiments/BUILD.gn | 17 +++++ .../experiments/quality_scaler_settings.cc | 62 +++++++++++++++ .../experiments/quality_scaler_settings.h | 39 ++++++++++ .../quality_scaler_settings_unittest.cc | 76 +++++++++++++++++++ 7 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 rtc_base/experiments/quality_scaler_settings.cc create mode 100644 rtc_base/experiments/quality_scaler_settings.h create mode 100644 rtc_base/experiments/quality_scaler_settings_unittest.cc diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index 23d0935456..baa78f4ad4 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -257,6 +257,7 @@ rtc_source_set("video_coding_utility") { "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", + "../../rtc_base/experiments:quality_scaler_settings", "../../rtc_base/experiments:quality_scaling_experiment", "../../rtc_base/experiments:rate_control_settings", "../../rtc_base/synchronization:sequence_checker", diff --git a/modules/video_coding/utility/quality_scaler.cc b/modules/video_coding/utility/quality_scaler.cc index 54ef76a8eb..1f1f192964 100644 --- a/modules/video_coding/utility/quality_scaler.cc +++ b/modules/video_coding/utility/quality_scaler.cc @@ -13,8 +13,8 @@ #include #include -#include "absl/types/optional.h" #include "rtc_base/checks.h" +#include "rtc_base/experiments/quality_scaler_settings.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/exp_filter.h" #include "rtc_base/task_queue.h" @@ -33,7 +33,7 @@ namespace { static const int kMeasureMs = 2000; static const float kSamplePeriodScaleFactor = 2.5; static const int kFramedropPercentThreshold = 60; -static const int kMinFramesNeededToScale = 2 * 30; +static const size_t kMinFramesNeededToScale = 2 * 30; } // namespace @@ -87,7 +87,16 @@ QualityScaler::QualityScaler(rtc::TaskQueue* task_queue, framedrop_percent_media_opt_(5 * 30), framedrop_percent_all_(5 * 30), experiment_enabled_(QualityScalingExperiment::Enabled()), - observed_enough_frames_(false) { + observed_enough_frames_(false), + min_frames_needed_( + QualityScalerSettings::ParseFromFieldTrials().MinFrames().value_or( + kMinFramesNeededToScale)), + initial_scale_factor_(QualityScalerSettings::ParseFromFieldTrials() + .InitialScaleFactor() + .value_or(kSamplePeriodScaleFactor)), + scale_factor_( + QualityScalerSettings::ParseFromFieldTrials().ScaleFactor()), + last_adapted_(false) { RTC_DCHECK_RUN_ON(&task_checker_); if (experiment_enabled_) { config_ = QualityScalingExperiment::GetConfig(); @@ -118,7 +127,11 @@ int64_t QualityScaler::GetSamplingPeriodMs() const { // Use half the interval while waiting for enough frames. return sampling_period_ms_ / 2; } - return sampling_period_ms_ * kSamplePeriodScaleFactor; + if (scale_factor_ && !last_adapted_) { + // Last check did not result in a AdaptDown/Up, possibly reduce interval. + return sampling_period_ms_ * scale_factor_.value(); + } + return sampling_period_ms_ * initial_scale_factor_; } void QualityScaler::ReportDroppedFrameByMediaOpt() { @@ -147,13 +160,14 @@ void QualityScaler::CheckQp() { RTC_DCHECK_RUN_ON(&task_checker_); // Should be set through InitEncode -> Should be set by now. RTC_DCHECK_GE(thresholds_.low, 0); + last_adapted_ = false; // 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(); - if (frames < kMinFramesNeededToScale) { + if (frames < min_frames_needed_) { observed_enough_frames_ = false; return; } @@ -196,6 +210,7 @@ void QualityScaler::ReportQpLow() { RTC_DCHECK_RUN_ON(&task_checker_); ClearSamples(); observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality); + last_adapted_ = true; } void QualityScaler::ReportQpHigh() { @@ -206,6 +221,7 @@ void QualityScaler::ReportQpHigh() { if (fast_rampup_) { fast_rampup_ = false; } + last_adapted_ = true; } void QualityScaler::ClearSamples() { diff --git a/modules/video_coding/utility/quality_scaler.h b/modules/video_coding/utility/quality_scaler.h index 500d931b58..7886fc0908 100644 --- a/modules/video_coding/utility/quality_scaler.h +++ b/modules/video_coding/utility/quality_scaler.h @@ -15,6 +15,7 @@ #include #include +#include "absl/types/optional.h" #include "api/video_codecs/video_encoder.h" #include "rtc_base/experiments/quality_scaling_experiment.h" #include "rtc_base/numerics/moving_average.h" @@ -93,6 +94,11 @@ class QualityScaler { std::unique_ptr qp_smoother_high_ RTC_GUARDED_BY(&task_checker_); std::unique_ptr qp_smoother_low_ RTC_GUARDED_BY(&task_checker_); bool observed_enough_frames_ RTC_GUARDED_BY(&task_checker_); + + const size_t min_frames_needed_; + const double initial_scale_factor_; + const absl::optional scale_factor_; + bool last_adapted_ RTC_GUARDED_BY(&task_checker_); }; } // namespace webrtc diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn index fc9673a651..703aba74bf 100644 --- a/rtc_base/experiments/BUILD.gn +++ b/rtc_base/experiments/BUILD.gn @@ -59,6 +59,21 @@ rtc_static_library("field_trial_parser") { ] } +rtc_static_library("quality_scaler_settings") { + sources = [ + "quality_scaler_settings.cc", + "quality_scaler_settings.h", + ] + deps = [ + ":field_trial_parser", + "../:rtc_base_approved", + "../../api/transport:field_trial_based_config", + "../../api/transport:webrtc_key_value_config", + "../../system_wrappers:field_trial", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + rtc_static_library("quality_scaling_experiment") { sources = [ "quality_scaling_experiment.cc", @@ -160,6 +175,7 @@ if (rtc_include_tests) { "field_trial_units_unittest.cc", "keyframe_interval_settings_unittest.cc", "normalize_simulcast_size_experiment_unittest.cc", + "quality_scaler_settings_unittest.cc", "quality_scaling_experiment_unittest.cc", "rate_control_settings_unittest.cc", "rtt_mult_experiment_unittest.cc", @@ -169,6 +185,7 @@ if (rtc_include_tests) { ":field_trial_parser", ":keyframe_interval_settings_experiment", ":normalize_simulcast_size_experiment", + ":quality_scaler_settings", ":quality_scaling_experiment", ":rate_control_settings", ":rtt_mult_experiment", diff --git a/rtc_base/experiments/quality_scaler_settings.cc b/rtc_base/experiments/quality_scaler_settings.cc new file mode 100644 index 0000000000..f32a2965a9 --- /dev/null +++ b/rtc_base/experiments/quality_scaler_settings.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 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/experiments/quality_scaler_settings.h" + +#include "api/transport/field_trial_based_config.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { +const int kMinFrames = 10; +const double kMinScaleFactor = 0.01; +} // namespace + +QualityScalerSettings::QualityScalerSettings( + const WebRtcKeyValueConfig* const key_value_config) + : min_frames_("min_frames"), + initial_scale_factor_("initial_scale_factor"), + scale_factor_("scale_factor") { + ParseFieldTrial( + {&min_frames_, &initial_scale_factor_, &scale_factor_}, + key_value_config->Lookup("WebRTC-Video-QualityScalerSettings")); +} + +QualityScalerSettings QualityScalerSettings::ParseFromFieldTrials() { + FieldTrialBasedConfig field_trial_config; + return QualityScalerSettings(&field_trial_config); +} + +absl::optional QualityScalerSettings::MinFrames() const { + if (min_frames_ && min_frames_.Value() < kMinFrames) { + RTC_LOG(LS_WARNING) << "Unsupported min_frames value, ignored."; + return absl::nullopt; + } + return min_frames_.GetOptional(); +} + +absl::optional QualityScalerSettings::InitialScaleFactor() const { + if (initial_scale_factor_ && + initial_scale_factor_.Value() < kMinScaleFactor) { + RTC_LOG(LS_WARNING) << "Unsupported initial_scale_factor value, ignored."; + return absl::nullopt; + } + return initial_scale_factor_.GetOptional(); +} + +absl::optional QualityScalerSettings::ScaleFactor() const { + if (scale_factor_ && scale_factor_.Value() < kMinScaleFactor) { + RTC_LOG(LS_WARNING) << "Unsupported scale_factor value, ignored."; + return absl::nullopt; + } + return scale_factor_.GetOptional(); +} + +} // namespace webrtc diff --git a/rtc_base/experiments/quality_scaler_settings.h b/rtc_base/experiments/quality_scaler_settings.h new file mode 100644 index 0000000000..0b26d82332 --- /dev/null +++ b/rtc_base/experiments/quality_scaler_settings.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ +#define RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ + +#include "absl/types/optional.h" +#include "api/transport/webrtc_key_value_config.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { + +class QualityScalerSettings final { + public: + static QualityScalerSettings ParseFromFieldTrials(); + + absl::optional MinFrames() const; + absl::optional InitialScaleFactor() const; + absl::optional ScaleFactor() const; + + private: + explicit QualityScalerSettings( + const WebRtcKeyValueConfig* const key_value_config); + + FieldTrialOptional min_frames_; + FieldTrialOptional initial_scale_factor_; + FieldTrialOptional scale_factor_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ diff --git a/rtc_base/experiments/quality_scaler_settings_unittest.cc b/rtc_base/experiments/quality_scaler_settings_unittest.cc new file mode 100644 index 0000000000..497c078f85 --- /dev/null +++ b/rtc_base/experiments/quality_scaler_settings_unittest.cc @@ -0,0 +1,76 @@ +/* + * Copyright 2019 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/experiments/quality_scaler_settings.h" + +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(QualityScalerSettingsTest, ValuesNotSetByDefault) { + EXPECT_FALSE(QualityScalerSettings::ParseFromFieldTrials().MinFrames()); + EXPECT_FALSE( + QualityScalerSettings::ParseFromFieldTrials().InitialScaleFactor()); + EXPECT_FALSE(QualityScalerSettings::ParseFromFieldTrials().ScaleFactor()); +} + +TEST(QualityScalerSettingsTest, ParseMinFrames) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/min_frames:100/"); + EXPECT_EQ(100, QualityScalerSettings::ParseFromFieldTrials().MinFrames()); +} + +TEST(QualityScalerSettingsTest, ParseInitialScaleFactor) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/initial_scale_factor:1.5/"); + EXPECT_EQ(1.5, + QualityScalerSettings::ParseFromFieldTrials().InitialScaleFactor()); +} + +TEST(QualityScalerSettingsTest, ParseScaleFactor) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/scale_factor:1.1/"); + EXPECT_EQ(1.1, QualityScalerSettings::ParseFromFieldTrials().ScaleFactor()); +} + +TEST(QualityScalerSettingsTest, ParseAll) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/" + "min_frames:100,initial_scale_factor:1.5,scale_factor:0.9/"); + const auto settings = QualityScalerSettings::ParseFromFieldTrials(); + EXPECT_EQ(100, settings.MinFrames()); + EXPECT_EQ(1.5, settings.InitialScaleFactor()); + EXPECT_EQ(0.9, settings.ScaleFactor()); +} + +TEST(QualityScalerSettingsTest, DoesNotParseIncorrectValue) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/" + "min_frames:a,initial_scale_factor:b,scale_factor:c/"); + const auto settings = QualityScalerSettings::ParseFromFieldTrials(); + EXPECT_FALSE(settings.MinFrames()); + EXPECT_FALSE(settings.InitialScaleFactor()); + EXPECT_FALSE(settings.ScaleFactor()); +} + +TEST(QualityScalerSettingsTest, DoesNotReturnTooSmallValue) { + test::ScopedFieldTrials field_trials( + "WebRTC-Video-QualityScalerSettings/" + "min_frames:0,initial_scale_factor:0.0,scale_factor:0.0/"); + const auto settings = QualityScalerSettings::ParseFromFieldTrials(); + EXPECT_FALSE(settings.MinFrames()); + EXPECT_FALSE(settings.InitialScaleFactor()); + EXPECT_FALSE(settings.ScaleFactor()); +} + +} // namespace +} // namespace webrtc