Move quality rampup experiment to overuse module
Bug: webrtc:11222
Change-Id: I8d0860bfe8bdfe0a051f5a6165cdcfa0cc25cfb5
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168181
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@google.com>
Cr-Commit-Position: refs/heads/master@{#30465}
diff --git a/call/adaptation/resource_adaptation_module_interface.h b/call/adaptation/resource_adaptation_module_interface.h
index 4efb7ac..623a414 100644
--- a/call/adaptation/resource_adaptation_module_interface.h
+++ b/call/adaptation/resource_adaptation_module_interface.h
@@ -91,10 +91,13 @@
// VideoStreamEncoderInterface::SetStartBitrate.
virtual void SetStartBitrate(DataRate start_bitrate) = 0;
virtual void SetTargetBitrate(DataRate target_bitrate) = 0;
- // Removes all restrictions; the module will need to adapt all over again.
- // TODO(hbos): It's not clear why anybody should be able to tell the module to
- // reset like this; can we get rid of this method?
- virtual void ResetVideoSourceRestrictions() = 0;
+ // The encoder rates are the target encoder bitrate distributed across spatial
+ // and temporal layers. This may be different than target bitrate depending on
+ // encoder configuration, e.g. if we can encode at desired quality in less
+ // than the allowed target bitrate or if the encoder has not been initialized
+ // yet.
+ virtual void SetEncoderRates(
+ const VideoEncoder::RateControlParameters& encoder_rates) = 0;
// The following methods correspond to the pipeline that a frame goes through.
// Note that if the encoder is parallelized, multiple frames may be processed
diff --git a/video/overuse_frame_detector_resource_adaptation_module.cc b/video/overuse_frame_detector_resource_adaptation_module.cc
index f0f9815..edced6d 100644
--- a/video/overuse_frame_detector_resource_adaptation_module.cc
+++ b/video/overuse_frame_detector_resource_adaptation_module.cc
@@ -221,128 +221,103 @@
RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceRestrictor);
};
-// Class holding adaptation information.
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::AdaptCounter() {
- fps_counters_.resize(kScaleReasonSize);
- resolution_counters_.resize(kScaleReasonSize);
- static_assert(kScaleReasonSize == 2, "Update MoveCount.");
-}
-
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::~AdaptCounter() {}
-
-std::string
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString() const {
- rtc::StringBuilder ss;
- ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
- ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
- return ss.Release();
-}
-
-VideoStreamEncoderObserver::AdaptationSteps
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Counts(
- int reason) const {
- VideoStreamEncoderObserver::AdaptationSteps counts;
- counts.num_framerate_reductions = fps_counters_[reason];
- counts.num_resolution_reductions = resolution_counters_[reason];
- return counts;
-}
-
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- IncrementFramerate(int reason) {
- ++(fps_counters_[reason]);
-}
-
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- IncrementResolution(int reason) {
- ++(resolution_counters_[reason]);
-}
-
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- DecrementFramerate(int reason) {
- if (fps_counters_[reason] == 0) {
- // Balanced mode: Adapt up is in a different order, switch reason.
- // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
- // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
- // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
- // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
- // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0}
- RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
- RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
- MoveCount(&resolution_counters_, reason);
- MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
+class OveruseFrameDetectorResourceAdaptationModule::AdaptCounter final {
+ public:
+ AdaptCounter() {
+ fps_counters_.resize(kScaleReasonSize);
+ resolution_counters_.resize(kScaleReasonSize);
+ static_assert(kScaleReasonSize == 2, "Update MoveCount.");
}
- --(fps_counters_[reason]);
- RTC_DCHECK_GE(fps_counters_[reason], 0);
-}
+ ~AdaptCounter() = default;
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- DecrementResolution(int reason) {
- if (resolution_counters_[reason] == 0) {
- // Balanced mode: Adapt up is in a different order, switch reason.
- RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
- RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
- MoveCount(&fps_counters_, reason);
- MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
+ // Get number of adaptation downscales for |reason|.
+ VideoStreamEncoderObserver::AdaptationSteps Counts(int reason) const {
+ VideoStreamEncoderObserver::AdaptationSteps counts;
+ counts.num_framerate_reductions = fps_counters_[reason];
+ counts.num_resolution_reductions = resolution_counters_[reason];
+ return counts;
}
- --(resolution_counters_[reason]);
- RTC_DCHECK_GE(resolution_counters_[reason], 0);
-}
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- DecrementFramerate(int reason, int cur_fps) {
- DecrementFramerate(reason);
- // Reset if at max fps (i.e. in case of fewer steps up than down).
- if (cur_fps == std::numeric_limits<int>::max())
- absl::c_fill(fps_counters_, 0);
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount()
- const {
- return Count(fps_counters_);
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
- ResolutionCount() const {
- return Count(resolution_counters_);
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount(
- int reason) const {
- return fps_counters_[reason];
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ResolutionCount(
- int reason) const {
- return resolution_counters_[reason];
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::TotalCount(
- int reason) const {
- return FramerateCount(reason) + ResolutionCount(reason);
-}
-
-int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Count(
- const std::vector<int>& counters) const {
- return absl::c_accumulate(counters, 0);
-}
-
-void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::MoveCount(
- std::vector<int>* counters,
- int from_reason) {
- int to_reason = (from_reason + 1) % kScaleReasonSize;
- ++((*counters)[to_reason]);
- --((*counters)[from_reason]);
-}
-
-std::string
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString(
- const std::vector<int>& counters) const {
- rtc::StringBuilder ss;
- for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
- ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
+ std::string ToString() const {
+ rtc::StringBuilder ss;
+ ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
+ ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
+ return ss.Release();
}
- return ss.Release();
-}
+
+ void IncrementFramerate(int reason) { ++(fps_counters_[reason]); }
+ void IncrementResolution(int reason) { ++(resolution_counters_[reason]); }
+ void DecrementFramerate(int reason) {
+ if (fps_counters_[reason] == 0) {
+ // Balanced mode: Adapt up is in a different order, switch reason.
+ // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
+ // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
+ // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
+ // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
+ // 4. Up resolution (quality):res={quality:0,cpu:0}, fps={quality:0,cpu:0}
+ RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
+ RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
+ MoveCount(&resolution_counters_, reason);
+ MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
+ }
+ --(fps_counters_[reason]);
+ RTC_DCHECK_GE(fps_counters_[reason], 0);
+ }
+
+ void DecrementResolution(int reason) {
+ if (resolution_counters_[reason] == 0) {
+ // Balanced mode: Adapt up is in a different order, switch reason.
+ RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
+ RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
+ MoveCount(&fps_counters_, reason);
+ MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
+ }
+ --(resolution_counters_[reason]);
+ RTC_DCHECK_GE(resolution_counters_[reason], 0);
+ }
+
+ void DecrementFramerate(int reason, int cur_fps) {
+ DecrementFramerate(reason);
+ // Reset if at max fps (i.e. in case of fewer steps up than down).
+ if (cur_fps == std::numeric_limits<int>::max())
+ absl::c_fill(fps_counters_, 0);
+ }
+
+ // Gets the total number of downgrades (for all adapt reasons).
+ int FramerateCount() const { return Count(fps_counters_); }
+ int ResolutionCount() const { return Count(resolution_counters_); }
+
+ // Gets the total number of downgrades for |reason|.
+ int FramerateCount(int reason) const { return fps_counters_[reason]; }
+ int ResolutionCount(int reason) const { return resolution_counters_[reason]; }
+ int TotalCount(int reason) const {
+ return FramerateCount(reason) + ResolutionCount(reason);
+ }
+
+ private:
+ std::string ToString(const std::vector<int>& counters) const {
+ rtc::StringBuilder ss;
+ for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
+ ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
+ }
+ return ss.Release();
+ }
+
+ int Count(const std::vector<int>& counters) const {
+ return absl::c_accumulate(counters, 0);
+ }
+
+ void MoveCount(std::vector<int>* counters, int from_reason) {
+ int to_reason = (from_reason + 1) % kScaleReasonSize;
+ ++((*counters)[to_reason]);
+ --((*counters)[from_reason]);
+ }
+
+ // Degradation counters holding number of framerate/resolution reductions
+ // per adapt reason.
+ std::vector<int> fps_counters_;
+ std::vector<int> resolution_counters_;
+};
OveruseFrameDetectorResourceAdaptationModule::
OveruseFrameDetectorResourceAdaptationModule(
@@ -368,6 +343,8 @@
quality_scaler_(nullptr),
quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
quality_scaler_settings_(QualityScalerSettings::ParseFromFieldTrials()),
+ quality_rampup_done_(false),
+ quality_rampup_experiment_(QualityRampupExperiment::ParseSettings()),
encoder_settings_(absl::nullopt),
encoder_stats_observer_(encoder_stats_observer),
initial_framedrop_(0) {
@@ -432,6 +409,10 @@
void OveruseFrameDetectorResourceAdaptationModule::SetEncoderSettings(
EncoderSettings encoder_settings) {
encoder_settings_ = std::move(encoder_settings);
+
+ quality_rampup_experiment_.SetMaxBitrate(
+ LastInputFrameSizeOrDefault(),
+ encoder_settings_->video_codec().maxBitrate);
MaybeUpdateTargetFrameRate();
}
@@ -468,6 +449,11 @@
}
}
+void OveruseFrameDetectorResourceAdaptationModule::SetEncoderRates(
+ const VideoEncoder::RateControlParameters& encoder_rates) {
+ encoder_rates_ = encoder_rates;
+}
+
void OveruseFrameDetectorResourceAdaptationModule::
ResetVideoSourceRestrictions() {
last_adaptation_request_.reset();
@@ -540,6 +526,7 @@
void OveruseFrameDetectorResourceAdaptationModule::OnMaybeEncodeFrame() {
initial_framedrop_ = kMaxInitialFramedrop;
+ MaybePerformQualityRampupExperiment();
}
bool OveruseFrameDetectorResourceAdaptationModule::DropInitialFrames() const {
@@ -994,4 +981,38 @@
static_cast<uint32_t>(bitrate_limits->min_start_bitrate_bps);
}
+void OveruseFrameDetectorResourceAdaptationModule::
+ MaybePerformQualityRampupExperiment() {
+ if (!quality_scaler_)
+ return;
+
+ if (quality_rampup_done_)
+ return;
+
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ uint32_t bw_kbps = encoder_rates_.has_value()
+ ? encoder_rates_.value().bandwidth_allocation.kbps()
+ : 0;
+
+ bool try_quality_rampup = false;
+ if (quality_rampup_experiment_.BwHigh(now_ms, bw_kbps)) {
+ // Verify that encoder is at max bitrate and the QP is low.
+ if (encoder_settings_ &&
+ encoder_target_bitrate_bps_.value_or(0) ==
+ encoder_settings_->video_codec().maxBitrate * 1000 &&
+ quality_scaler_->QpFastFilterLow()) {
+ try_quality_rampup = true;
+ }
+ }
+ if (try_quality_rampup &&
+ GetConstAdaptCounter().ResolutionCount(
+ AdaptationObserverInterface::AdaptReason::kQuality) > 0 &&
+ GetConstAdaptCounter().TotalCount(
+ AdaptationObserverInterface::AdaptReason::kCpu) == 0) {
+ RTC_LOG(LS_INFO) << "Reset quality limitations.";
+ ResetVideoSourceRestrictions();
+ quality_rampup_done_ = true;
+ }
+}
+
} // namespace webrtc
diff --git a/video/overuse_frame_detector_resource_adaptation_module.h b/video/overuse_frame_detector_resource_adaptation_module.h
index 8767607..6c841dc 100644
--- a/video/overuse_frame_detector_resource_adaptation_module.h
+++ b/video/overuse_frame_detector_resource_adaptation_module.h
@@ -27,6 +27,7 @@
#include "api/video_codecs/video_encoder_config.h"
#include "call/adaptation/resource_adaptation_module_interface.h"
#include "rtc_base/experiments/balanced_degradation_settings.h"
+#include "rtc_base/experiments/quality_rampup_experiment.h"
#include "rtc_base/experiments/quality_scaler_settings.h"
#include "system_wrappers/include/clock.h"
#include "video/overuse_frame_detector.h"
@@ -66,7 +67,6 @@
DegradationPreference degradation_preference() const {
return degradation_preference_;
}
- QualityScaler* quality_scaler() const { return quality_scaler_.get(); }
// ResourceAdaptationModuleInterface implementation.
void StartResourceAdaptation(
@@ -78,7 +78,8 @@
void SetEncoderSettings(EncoderSettings encoder_settings) override;
void SetStartBitrate(DataRate start_bitrate) override;
void SetTargetBitrate(DataRate target_bitrate) override;
- void ResetVideoSourceRestrictions() override;
+ void SetEncoderRates(
+ const VideoEncoder::RateControlParameters& encoder_rates) override;
void OnFrame(const VideoFrame& frame) override;
void OnFrameDroppedDueToSize() override;
@@ -89,6 +90,7 @@
int64_t time_sent_in_us,
absl::optional<int> encode_duration_us) override;
void OnFrameDropped(EncodedImageCallback::DropReason reason) override;
+
bool DropInitialFrames() const;
// TODO(eshr): This can be made private if we configure on
@@ -96,42 +98,6 @@
// (https://crbug.com/webrtc/11338)
void ConfigureQualityScaler(const VideoEncoder::EncoderInfo& encoder_info);
- class AdaptCounter final {
- public:
- AdaptCounter();
- ~AdaptCounter();
-
- // Get number of adaptation downscales for |reason|.
- VideoStreamEncoderObserver::AdaptationSteps Counts(int reason) const;
-
- std::string ToString() const;
-
- void IncrementFramerate(int reason);
- void IncrementResolution(int reason);
- void DecrementFramerate(int reason);
- void DecrementResolution(int reason);
- void DecrementFramerate(int reason, int cur_fps);
-
- // Gets the total number of downgrades (for all adapt reasons).
- int FramerateCount() const;
- int ResolutionCount() const;
-
- // Gets the total number of downgrades for |reason|.
- int FramerateCount(int reason) const;
- int ResolutionCount(int reason) const;
- int TotalCount(int reason) const;
-
- private:
- std::string ToString(const std::vector<int>& counters) const;
- int Count(const std::vector<int>& counters) const;
- void MoveCount(std::vector<int>* counters, int from_reason);
-
- // Degradation counters holding number of framerate/resolution reductions
- // per adapt reason.
- std::vector<int> fps_counters_;
- std::vector<int> resolution_counters_;
- };
-
// AdaptationObserverInterface implementation. Used both "internally" as
// feedback from |overuse_detector_|, and externally from VideoStreamEncoder:
// - It is wired to the VideoStreamEncoder::quality_scaler_.
@@ -143,12 +109,8 @@
void AdaptUp(AdaptReason reason) override;
bool AdaptDown(AdaptReason reason) override;
- // Used by VideoStreamEncoder::MaybeEncodeVideoFrame().
- // TODO(hbos): VideoStreamEncoder should not be responsible for any part of
- // the adaptation. Move this logic to this module?
- const AdaptCounter& GetConstAdaptCounter();
-
private:
+ class AdaptCounter;
class VideoSourceRestrictor;
struct AdaptationRequest {
@@ -171,6 +133,7 @@
int LastInputFrameSizeOrDefault() const;
VideoStreamEncoderObserver::AdaptationSteps GetActiveCounts(
AdaptReason reason);
+ const AdaptCounter& GetConstAdaptCounter();
// Makes |video_source_restrictions_| up-to-date and informs the
// |adaptation_listener_| if restrictions are changed, allowing the listener
@@ -190,6 +153,14 @@
AdaptCounter& GetAdaptCounter();
bool CanAdaptUpResolution(int pixels, uint32_t bitrate_bps) const;
+ // Checks to see if we should execute the quality rampup experiment. The
+ // experiment resets all video restrictions at the start of the call in the
+ // case the bandwidth estimate is high enough.
+ // TODO(https://crbug.com/webrtc/11222) Move experiment details into an inner
+ // class.
+ void MaybePerformQualityRampupExperiment();
+ void ResetVideoSourceRestrictions();
+
ResourceAdaptationModuleListener* const adaptation_listener_;
Clock* clock_;
const bool experiment_cpu_load_estimator_;
@@ -215,9 +186,12 @@
absl::optional<double> target_frame_rate_;
// This is the last non-zero target bitrate for the encoder.
absl::optional<uint32_t> encoder_target_bitrate_bps_;
+ absl::optional<VideoEncoder::RateControlParameters> encoder_rates_;
std::unique_ptr<QualityScaler> quality_scaler_;
const bool quality_scaling_experiment_enabled_;
const QualityScalerSettings quality_scaler_settings_;
+ bool quality_rampup_done_;
+ QualityRampupExperiment quality_rampup_experiment_;
StartBitrate start_bitrate_;
absl::optional<EncoderSettings> encoder_settings_;
VideoStreamEncoderObserver* const encoder_stats_observer_;
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index fe27bc0..e90dd0c 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -249,8 +249,6 @@
TaskQueueFactory* task_queue_factory)
: shutdown_event_(true /* manual_reset */, false),
number_of_cores_(number_of_cores),
- quality_rampup_done_(false),
- quality_rampup_experiment_(QualityRampupExperiment::ParseSettings()),
quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
sink_(nullptr),
settings_(settings),
@@ -650,8 +648,6 @@
send_codec_ = codec;
encoder_switch_experiment_.SetCodec(send_codec_.codecType);
- quality_rampup_experiment_.SetMaxBitrate(
- last_frame_info_->width * last_frame_info_->height, codec.maxBitrate);
// Keep the same encoder, as long as the video_format is unchanged.
// Encoder creation block is split in two since EncoderInfo needed to start
@@ -981,6 +977,7 @@
frame_encode_metadata_writer_.OnSetRates(
rate_settings.rate_control.bitrate,
static_cast<uint32_t>(rate_settings.rate_control.framerate_fps + 0.5));
+ resource_adaptation_module_->SetEncoderRates(rate_settings.rate_control);
}
}
@@ -1065,16 +1062,6 @@
}
resource_adaptation_module_->OnMaybeEncodeFrame();
- if (!quality_rampup_done_ && TryQualityRampup(now_ms) &&
- resource_adaptation_module_->GetConstAdaptCounter().ResolutionCount(
- AdaptationObserverInterface::AdaptReason::kQuality) > 0 &&
- resource_adaptation_module_->GetConstAdaptCounter().TotalCount(
- AdaptationObserverInterface::AdaptReason::kCpu) == 0) {
- RTC_LOG(LS_INFO) << "Reset quality limitations.";
- resource_adaptation_module_->ResetVideoSourceRestrictions();
- quality_rampup_done_ = true;
- }
-
if (EncoderPaused()) {
// Storing references to a native buffer risks blocking frame capture.
if (video_frame.video_frame_buffer()->type() !=
@@ -1590,27 +1577,6 @@
return false;
}
-bool VideoStreamEncoder::TryQualityRampup(int64_t now_ms) {
- QualityScaler* quality_scaler = resource_adaptation_module_->quality_scaler();
- if (!quality_scaler)
- return false;
-
- uint32_t bw_kbps = last_encoder_rate_settings_
- ? last_encoder_rate_settings_->rate_control
- .bandwidth_allocation.kbps()
- : 0;
-
- if (quality_rampup_experiment_.BwHigh(now_ms, bw_kbps)) {
- // Verify that encoder is at max bitrate and the QP is low.
- if (encoder_target_bitrate_bps_.value_or(0) ==
- send_codec_.maxBitrate * 1000 &&
- quality_scaler->QpFastFilterLow()) {
- return true;
- }
- }
- return false;
-}
-
bool VideoStreamEncoder::TriggerAdaptDown(
AdaptationObserverInterface::AdaptReason reason) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
diff --git a/video/video_stream_encoder.h b/video/video_stream_encoder.h
index bba2d8d..465d611 100644
--- a/video/video_stream_encoder.h
+++ b/video/video_stream_encoder.h
@@ -173,7 +173,6 @@
// Indicates wether frame should be dropped because the pixel count is too
// large for the current bitrate configuration.
bool DropDueToSize(uint32_t pixel_count) const RTC_RUN_ON(&encoder_queue_);
- bool TryQualityRampup(int64_t now_ms) RTC_RUN_ON(&encoder_queue_);
// Implements EncodedImageCallback.
EncodedImageCallback::Result OnEncodedImage(
@@ -211,9 +210,6 @@
rtc::Event shutdown_event_;
const uint32_t number_of_cores_;
- bool quality_rampup_done_ RTC_GUARDED_BY(&encoder_queue_);
- QualityRampupExperiment quality_rampup_experiment_
- RTC_GUARDED_BY(&encoder_queue_);
const bool quality_scaling_experiment_enabled_;