1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
/*
* Copyright 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/experiments/quality_scaling_experiment.h"
#include <stdio.h>
#include <optional>
#include <string>
#include "absl/strings/match.h"
#include "api/field_trials_view.h"
#include "api/video/video_codec_type.h"
#include "api/video_codecs/video_encoder.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// This experiment controls QP thresholds for VP8, VP9, H264 and Generic codecs.
// Generic includes H265X but not standard H265.
constexpr char kFieldTrial[] = "WebRTC-Video-QualityScaling";
constexpr int kMinQp = 1;
constexpr int kMaxVp8Qp = 127;
constexpr int kMaxVp9Qp = 255;
constexpr int kMaxH264Qp = 51;
constexpr int kMaxGenericQp = 255;
#if !defined(WEBRTC_IOS)
// On non-iOS, this default string is used unless explicitly overriden.
// TODO(https://crbug.com/400338987): For use cases that does not explicitly
// turn the QP experiment on (e.g. Chrome), it does not make sense for this QP
// threshold to override the QP thresholds provided by the encoder
// implementation - we should trust that an encoder implementation that reports
// its own QP thresholds would know best, and only use these as a fallback for
// when the encoder does not specify any.
constexpr char kDefaultQualityScalingSetttings[] =
"Enabled-29,95,149,205,24,37,26,36,0.9995,0.9999,1";
#endif
std::optional<VideoEncoder::QpThresholds> GetThresholds(int low,
int high,
int max) {
if (low < kMinQp || high > max || high < low)
return std::nullopt;
RTC_LOG(LS_INFO) << "QP thresholds: low: " << low << ", high: " << high;
return std::optional<VideoEncoder::QpThresholds>(
VideoEncoder::QpThresholds(low, high));
}
// This experiment controls QP thresholds for standard H265 (not H265X).
// - Only for debugging/experimentation. Once QP thresholds have been determined
// it is up to the encoder implementation to provide
// VideoEncoder::EncoderInfo::scaling_settings.
//
// Example usage:
// --force-fieldtrials=WebRTC-H265-QualityScaling/low_qp:27,high_qp:35/
struct WebRTCH265QualityScaling {
static constexpr char kFieldTrialName[] = "WebRTC-H265-QualityScaling";
WebRTCH265QualityScaling(const FieldTrialsView& field_trials)
: low_qp("low_qp"), high_qp("high_qp") {
ParseFieldTrial({&low_qp, &high_qp}, field_trials.Lookup(kFieldTrialName));
}
bool IsEnabled() const { return low_qp && high_qp; }
VideoEncoder::QpThresholds ToQpThresholds() const {
RTC_DCHECK(IsEnabled());
return VideoEncoder::QpThresholds(*low_qp, *high_qp);
}
FieldTrialOptional<int> low_qp;
FieldTrialOptional<int> high_qp;
};
} // namespace
bool QualityScalingExperiment::Enabled(const FieldTrialsView& field_trials) {
WebRTCH265QualityScaling h265_quality_scaling(field_trials);
return
#if defined(WEBRTC_IOS)
absl::StartsWith(field_trials.Lookup(kFieldTrial), "Enabled") ||
#else
!absl::StartsWith(field_trials.Lookup(kFieldTrial), "Disabled") ||
#endif
h265_quality_scaling.IsEnabled();
}
std::optional<QualityScalingExperiment::Settings>
QualityScalingExperiment::ParseSettings(const FieldTrialsView& field_trials) {
std::string group = field_trials.Lookup(kFieldTrial);
// TODO(http://crbug.com/webrtc/12401): Completely remove the experiment code
// after few releases.
#if !defined(WEBRTC_IOS)
if (group.empty())
group = kDefaultQualityScalingSetttings;
#endif
Settings s;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%d",
&s.vp8_low, &s.vp8_high, &s.vp9_low, &s.vp9_high, &s.h264_low,
&s.h264_high, &s.generic_low, &s.generic_high, &s.alpha_high,
&s.alpha_low, &s.drop) != 11) {
RTC_LOG(LS_WARNING) << "Invalid number of parameters provided.";
return std::nullopt;
}
return s;
}
std::optional<VideoEncoder::QpThresholds>
QualityScalingExperiment::GetQpThresholds(VideoCodecType codec_type,
const FieldTrialsView& field_trials) {
if (codec_type == kVideoCodecH265) {
WebRTCH265QualityScaling h265_quality_scaling(field_trials);
if (h265_quality_scaling.IsEnabled()) {
return h265_quality_scaling.ToQpThresholds();
}
}
const auto settings = ParseSettings(field_trials);
if (!settings)
return std::nullopt;
switch (codec_type) {
case kVideoCodecVP8:
return GetThresholds(settings->vp8_low, settings->vp8_high, kMaxVp8Qp);
case kVideoCodecVP9:
return GetThresholds(settings->vp9_low, settings->vp9_high, kMaxVp9Qp);
case kVideoCodecH264:
return GetThresholds(settings->h264_low, settings->h264_high, kMaxH264Qp);
case kVideoCodecGeneric:
return GetThresholds(settings->generic_low, settings->generic_high,
kMaxGenericQp);
default:
return std::nullopt;
}
}
QualityScalingExperiment::Config QualityScalingExperiment::GetConfig(
const FieldTrialsView& field_trials) {
const auto settings = ParseSettings(field_trials);
if (!settings)
return Config();
Config config;
config.use_all_drop_reasons = settings->drop > 0;
if (settings->alpha_high < 0 || settings->alpha_low < settings->alpha_high) {
RTC_LOG(LS_WARNING) << "Invalid alpha value provided, using default.";
return config;
}
config.alpha_high = settings->alpha_high;
config.alpha_low = settings->alpha_low;
return config;
}
} // namespace webrtc
|