From e0287f27970338ca53e7c18439b0fb0e6ec065a5 Mon Sep 17 00:00:00 2001 From: Johannes Kron Date: Tue, 2 Jul 2024 00:27:10 +0200 Subject: [PATCH] Add default values and field trials to QualityConvergenceMonitor Add default values and a static Create() function that determines the parameters to use based on the specified codec and potential field trial overrides. Bug: chromium:328598314 Change-Id: I7a9331a1fd0ed4bd258788760592ea84e535e43b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/355903 Reviewed-by: Ilya Nikolaevskiy Reviewed-by: Markus Handell Commit-Queue: Johannes Kron Cr-Commit-Position: refs/heads/main@{#42567} --- experiments/field_trials.py | 9 ++ video/quality_convergence_monitor.cc | 78 ++++++++++++ video/quality_convergence_monitor.h | 10 ++ video/quality_convergence_monitor_unittest.cc | 120 +++++++++++++++++- 4 files changed, 210 insertions(+), 7 deletions(-) diff --git a/experiments/field_trials.py b/experiments/field_trials.py index 5949cdc4df..12338e360a 100755 --- a/experiments/field_trials.py +++ b/experiments/field_trials.py @@ -107,6 +107,15 @@ ACTIVE_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([ FieldTrial('WebRTC-PermuteTlsClientHello', 42225803, date(2024, 7, 1)), + FieldTrial('WebRTC-QCM-Dynamic-AV1', + 349860657, + date(2025, 7, 1)), + FieldTrial('WebRTC-QCM-Dynamic-VP8', + 349860657, + date(2025, 7, 1)), + FieldTrial('WebRTC-QCM-Dynamic-VP9', + 349860657, + date(2025, 7, 1)), FieldTrial('WebRTC-ReceiveBufferSize', 42225927, date(2024, 4, 1)), diff --git a/video/quality_convergence_monitor.cc b/video/quality_convergence_monitor.cc index c05f0d3175..d170a4df46 100644 --- a/video/quality_convergence_monitor.cc +++ b/video/quality_convergence_monitor.cc @@ -13,8 +13,76 @@ #include #include "rtc_base/checks.h" +#include "rtc_base/experiments/struct_parameters_parser.h" namespace webrtc { +namespace { +constexpr size_t kDefaultRecentWindowLength = 6; +constexpr size_t kDefaultPastWindowLength = 6; +constexpr float kDefaultAlpha = 0.06; + +struct DynamicDetectionConfig { + bool enabled = false; + // alpha is a percentage of the codec-specific max QP value that is used to + // determine the dynamic QP threshold: + // dynamic_qp_threshold = static_qp_threshold + alpha * max_QP + double alpha = kDefaultAlpha; + int recent_length = kDefaultRecentWindowLength; + int past_length = kDefaultPastWindowLength; + std::unique_ptr Parser(); +}; + +std::unique_ptr DynamicDetectionConfig::Parser() { + // The empty comments ensures that each pair is on a separate line. + return StructParametersParser::Create("enabled", &enabled, // + "alpha", &alpha, // + "recent_length", &recent_length, // + "past_length", &past_length); +} + +QualityConvergenceMonitor::Parameters GetParameters( + int static_qp_threshold, + VideoCodecType codec, + const FieldTrialsView& trials) { + QualityConvergenceMonitor::Parameters params; + params.static_qp_threshold = static_qp_threshold; + + DynamicDetectionConfig dynamic_config; + // Apply codec specific settings. + int max_qp = 0; + switch (codec) { + case kVideoCodecVP8: + dynamic_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Dynamic-VP8")); + max_qp = 127; + break; + case kVideoCodecVP9: + // Change to enabled by default for VP9. + dynamic_config.enabled = true; + dynamic_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Dynamic-VP9")); + max_qp = 255; + break; + case kVideoCodecAV1: + // Change to enabled by default for AV1. + dynamic_config.enabled = true; + dynamic_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Dynamic-AV1")); + max_qp = 255; + break; + case kVideoCodecGeneric: + case kVideoCodecH264: + case kVideoCodecH265: + break; + } + + if (dynamic_config.enabled) { + params.dynamic_detection_enabled = dynamic_config.enabled; + params.dynamic_qp_threshold = + params.static_qp_threshold + max_qp * dynamic_config.alpha; + params.recent_window_length = dynamic_config.recent_length; + params.past_window_length = dynamic_config.past_length; + } + return params; +} +} // namespace QualityConvergenceMonitor::QualityConvergenceMonitor(const Parameters& params) : params_(params) { @@ -121,4 +189,14 @@ bool QualityConvergenceMonitor::AtTargetQuality() const { return at_target_quality_; } +// Static +std::unique_ptr QualityConvergenceMonitor::Create( + int static_qp_threshold, + VideoCodecType codec, + const FieldTrialsView& trials) { + Parameters params = GetParameters(static_qp_threshold, codec, trials); + return std::unique_ptr( + new QualityConvergenceMonitor(params)); +} + } // namespace webrtc diff --git a/video/quality_convergence_monitor.h b/video/quality_convergence_monitor.h index 6bb3acbad5..326372db43 100644 --- a/video/quality_convergence_monitor.h +++ b/video/quality_convergence_monitor.h @@ -14,6 +14,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/video/video_codec_type.h" namespace webrtc { @@ -47,6 +48,11 @@ class QualityConvergenceMonitor { explicit QualityConvergenceMonitor(const Parameters& params); + static std::unique_ptr Create( + int static_qp_threshold, + VideoCodecType codec, + const FieldTrialsView& trials); + // Add the supplied `qp` value to the detection window. // `is_refresh_frame` must only be `true` if the corresponding // video frame is a refresh frame that is used to improve the visual quality. @@ -56,6 +62,10 @@ class QualityConvergenceMonitor { // have converged and reached the target quality. bool AtTargetQuality() const; + // Used in tests to verify that default values and field trials are set + // correctly. + Parameters GetParametersForTesting() const { return params_; } + private: const Parameters params_; bool at_target_quality_ = false; diff --git a/video/quality_convergence_monitor_unittest.cc b/video/quality_convergence_monitor_unittest.cc index fe5fd97944..3f45f99e4d 100644 --- a/video/quality_convergence_monitor_unittest.cc +++ b/video/quality_convergence_monitor_unittest.cc @@ -13,18 +13,21 @@ #include #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace { - +constexpr int kStaticQpThreshold = 13; constexpr QualityConvergenceMonitor::Parameters kParametersOnlyStaticThreshold = - {.static_qp_threshold = 13, .dynamic_detection_enabled = false}; + {.static_qp_threshold = kStaticQpThreshold, + .dynamic_detection_enabled = false}; constexpr QualityConvergenceMonitor::Parameters - kParametersWithDynamicDetection = {.static_qp_threshold = 13, - .dynamic_detection_enabled = true, - .recent_window_length = 3, - .past_window_length = 9, - .dynamic_qp_threshold = 24}; + kParametersWithDynamicDetection = { + .static_qp_threshold = kStaticQpThreshold, + .dynamic_detection_enabled = true, + .recent_window_length = 3, + .past_window_length = 9, + .dynamic_qp_threshold = 24}; // Test the basics of the algorithm. @@ -192,5 +195,108 @@ TEST(QualityConvergenceMonitorAlgorithm, EXPECT_TRUE(monitor->AtTargetQuality()); } +// Test default values and that they can be overridden with field trials. + +TEST(QualityConvergenceMonitorSetup, DefaultParameters) { + test::ScopedKeyValueConfig field_trials; + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecVP8, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters vp8_parameters = + monitor->GetParametersForTesting(); + EXPECT_EQ(vp8_parameters.static_qp_threshold, kStaticQpThreshold); + EXPECT_FALSE(vp8_parameters.dynamic_detection_enabled); + + monitor = QualityConvergenceMonitor::Create(kStaticQpThreshold, + kVideoCodecVP9, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters vp9_parameters = + monitor->GetParametersForTesting(); + EXPECT_EQ(vp9_parameters.static_qp_threshold, kStaticQpThreshold); + EXPECT_TRUE(vp9_parameters.dynamic_detection_enabled); + EXPECT_EQ(vp9_parameters.dynamic_qp_threshold, 28); // 13 + 15. + EXPECT_EQ(vp9_parameters.recent_window_length, 6u); + EXPECT_EQ(vp9_parameters.past_window_length, 6u); + + monitor = QualityConvergenceMonitor::Create(kStaticQpThreshold, + kVideoCodecAV1, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters av1_parameters = + monitor->GetParametersForTesting(); + EXPECT_EQ(av1_parameters.static_qp_threshold, kStaticQpThreshold); + EXPECT_TRUE(av1_parameters.dynamic_detection_enabled); + EXPECT_EQ(av1_parameters.dynamic_qp_threshold, 28); // 13 + 15. + EXPECT_EQ(av1_parameters.recent_window_length, 6u); + EXPECT_EQ(av1_parameters.past_window_length, 6u); +} + +TEST(QualityConvergenceMonitorSetup, OverrideVp8Parameters) { + test::ScopedKeyValueConfig field_trials( + "WebRTC-QCM-Dynamic-VP8/" + "enabled:1,alpha:0.08,recent_length:6,past_length:4/"); + + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecVP8, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting(); + EXPECT_EQ(p.static_qp_threshold, kStaticQpThreshold); + EXPECT_TRUE(p.dynamic_detection_enabled); + EXPECT_EQ(p.dynamic_qp_threshold, 23); // 13 + 10. + EXPECT_EQ(p.recent_window_length, 6u); + EXPECT_EQ(p.past_window_length, 4u); +} + +TEST(QualityConvergenceMonitorSetup, OverrideVp9Parameters) { + test::ScopedKeyValueConfig field_trials( + "WebRTC-QCM-Dynamic-VP9/" + "enabled:1,alpha:0.08,recent_length:6,past_length:4/"); + + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecVP9, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting(); + EXPECT_EQ(p.static_qp_threshold, kStaticQpThreshold); + EXPECT_TRUE(p.dynamic_detection_enabled); + EXPECT_EQ(p.dynamic_qp_threshold, 33); // 13 + 20. + EXPECT_EQ(p.recent_window_length, 6u); + EXPECT_EQ(p.past_window_length, 4u); +} + +TEST(QualityConvergenceMonitorSetup, OverrideAv1Parameters) { + test::ScopedKeyValueConfig field_trials( + "WebRTC-QCM-Dynamic-AV1/" + "enabled:1,alpha:0.10,recent_length:8,past_length:8/"); + + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecAV1, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting(); + EXPECT_EQ(p.static_qp_threshold, kStaticQpThreshold); + EXPECT_TRUE(p.dynamic_detection_enabled); + EXPECT_EQ(p.dynamic_qp_threshold, 38); // 13 + 25. + EXPECT_EQ(p.recent_window_length, 8u); + EXPECT_EQ(p.past_window_length, 8u); +} + +TEST(QualityConvergenceMonitorSetup, DisableVp9Dynamic) { + test::ScopedKeyValueConfig field_trials("WebRTC-QCM-Dynamic-VP9/enabled:0/"); + + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecVP9, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting(); + EXPECT_FALSE(p.dynamic_detection_enabled); +} + +TEST(QualityConvergenceMonitorSetup, DisableAv1Dynamic) { + test::ScopedKeyValueConfig field_trials("WebRTC-QCM-Dynamic-AV1/enabled:0/"); + + auto monitor = QualityConvergenceMonitor::Create( + kStaticQpThreshold, kVideoCodecAV1, field_trials); + ASSERT_TRUE(monitor); + QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting(); + EXPECT_FALSE(p.dynamic_detection_enabled); +} + } // namespace } // namespace webrtc