Add fieldtrials WebRTC-QCM-Static-{AV1, VP8, VP9}
The fieldtrials can be used to override the static QP threshold
that is used in QualityConvergenceMonitor to determine if an
encoded video stream has reached its target quality.
The fieldtrials do not change the dynamic detection.
Bug: chromium:328598314
Change-Id: I5995860eff461f0c712293e34cf75834ce414bed
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361201
Commit-Queue: Johannes Kron <kron@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42928}
diff --git a/experiments/field_trials.py b/experiments/field_trials.py
index c6b6da6..65926ad 100755
--- a/experiments/field_trials.py
+++ b/experiments/field_trials.py
@@ -119,6 +119,15 @@
FieldTrial('WebRTC-QCM-Dynamic-VP9',
349860657,
date(2025, 7, 1)),
+ FieldTrial('WebRTC-QCM-Static-AV1',
+ 349860657,
+ date(2025, 7, 1)),
+ FieldTrial('WebRTC-QCM-Static-VP8',
+ 349860657,
+ date(2025, 7, 1)),
+ FieldTrial('WebRTC-QCM-Static-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 d170a4d..4094c7f 100644
--- a/video/quality_convergence_monitor.cc
+++ b/video/quality_convergence_monitor.cc
@@ -10,6 +10,7 @@
#include "video/quality_convergence_monitor.h"
+#include <algorithm>
#include <numeric>
#include "rtc_base/checks.h"
@@ -21,11 +22,27 @@
constexpr size_t kDefaultPastWindowLength = 6;
constexpr float kDefaultAlpha = 0.06;
+struct StaticDetectionConfig {
+ // Overrides the static QP threshold if set to a higher value than what is
+ // reported by the encoder.
+ int static_qp_threshold_override = 0;
+ std::unique_ptr<StructParametersParser> Parser();
+};
+
+std::unique_ptr<StructParametersParser> StaticDetectionConfig::Parser() {
+ // The empty comments ensures that each pair is on a separate line.
+ return StructParametersParser::Create("static_qp_threshold",
+ &static_qp_threshold_override);
+}
+
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
+ // dynamic_qp_threshold = static_min_qp_threshold + alpha * max_QP
+ // Please note that although the static threshold is overridden, the dynamic
+ // threshold is calculated from static_min_qp_threshold reported by the
+ // encoder.
double alpha = kDefaultAlpha;
int recent_length = kDefaultRecentWindowLength;
int past_length = kDefaultPastWindowLength;
@@ -41,27 +58,30 @@
}
QualityConvergenceMonitor::Parameters GetParameters(
- int static_qp_threshold,
+ int static_min_qp_threshold,
VideoCodecType codec,
const FieldTrialsView& trials) {
QualityConvergenceMonitor::Parameters params;
- params.static_qp_threshold = static_qp_threshold;
+ StaticDetectionConfig static_config;
DynamicDetectionConfig dynamic_config;
// Apply codec specific settings.
int max_qp = 0;
switch (codec) {
case kVideoCodecVP8:
+ static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-VP8"));
dynamic_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Dynamic-VP8"));
max_qp = 127;
break;
case kVideoCodecVP9:
+ static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-VP9"));
// 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:
+ static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-AV1"));
// Change to enabled by default for AV1.
dynamic_config.enabled = true;
dynamic_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Dynamic-AV1"));
@@ -73,10 +93,13 @@
break;
}
+ params.static_qp_threshold = std::max(
+ static_min_qp_threshold, static_config.static_qp_threshold_override);
+
if (dynamic_config.enabled) {
params.dynamic_detection_enabled = dynamic_config.enabled;
params.dynamic_qp_threshold =
- params.static_qp_threshold + max_qp * dynamic_config.alpha;
+ static_min_qp_threshold + max_qp * dynamic_config.alpha;
params.recent_window_length = dynamic_config.recent_length;
params.past_window_length = dynamic_config.past_length;
}
diff --git a/video/quality_convergence_monitor_unittest.cc b/video/quality_convergence_monitor_unittest.cc
index 3f45f99..4560308 100644
--- a/video/quality_convergence_monitor_unittest.cc
+++ b/video/quality_convergence_monitor_unittest.cc
@@ -18,6 +18,8 @@
namespace webrtc {
namespace {
constexpr int kStaticQpThreshold = 13;
+constexpr int kDefaultDynamicThreshold = 28; // 13 + 0.06 * 255.
+
constexpr QualityConvergenceMonitor::Parameters kParametersOnlyStaticThreshold =
{.static_qp_threshold = kStaticQpThreshold,
.dynamic_detection_enabled = false};
@@ -298,5 +300,46 @@
EXPECT_FALSE(p.dynamic_detection_enabled);
}
+TEST(QualityConvergenceMonitorSetup, OverrideVp8StaticThreshold) {
+ test::ScopedKeyValueConfig field_trials(
+ "WebRTC-QCM-Static-VP8/static_qp_threshold:22/");
+
+ auto monitor = QualityConvergenceMonitor::Create(
+ kStaticQpThreshold, kVideoCodecVP8, field_trials);
+ ASSERT_TRUE(monitor);
+ QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting();
+ EXPECT_EQ(p.static_qp_threshold, 22);
+ EXPECT_NE(p.static_qp_threshold, kStaticQpThreshold);
+ // Dynamic threshold is not tested since it's not enabled by default for VP8.
+}
+
+TEST(QualityConvergenceMonitorSetup, OverrideVp9StaticThreshold) {
+ test::ScopedKeyValueConfig field_trials(
+ "WebRTC-QCM-Static-VP9/static_qp_threshold:44/");
+
+ auto monitor = QualityConvergenceMonitor::Create(
+ kStaticQpThreshold, kVideoCodecVP9, field_trials);
+ ASSERT_TRUE(monitor);
+ QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting();
+ EXPECT_EQ(p.static_qp_threshold, 44);
+ EXPECT_NE(p.static_qp_threshold, kStaticQpThreshold);
+ // Dynamic QP threshold is unchanged.
+ EXPECT_EQ(p.dynamic_qp_threshold, kDefaultDynamicThreshold);
+}
+
+TEST(QualityConvergenceMonitorSetup, OverrideAv1StaticThreshold) {
+ test::ScopedKeyValueConfig field_trials(
+ "WebRTC-QCM-Static-AV1/static_qp_threshold:46/");
+
+ auto monitor = QualityConvergenceMonitor::Create(
+ kStaticQpThreshold, kVideoCodecAV1, field_trials);
+ ASSERT_TRUE(monitor);
+ QualityConvergenceMonitor::Parameters p = monitor->GetParametersForTesting();
+ EXPECT_EQ(p.static_qp_threshold, 46);
+ EXPECT_NE(p.static_qp_threshold, kStaticQpThreshold);
+ // Dynamic QP threshold is unchanged.
+ EXPECT_EQ(p.dynamic_qp_threshold, kDefaultDynamicThreshold);
+}
+
} // namespace
} // namespace webrtc