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}
This commit is contained in:
Evan Shrubsole
2020-02-05 13:30:33 +01:00
committed by Commit Bot
parent 78c7c5247c
commit e331a122aa
5 changed files with 163 additions and 203 deletions

View File

@ -91,10 +91,13 @@ class ResourceAdaptationModuleInterface {
// 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

View File

@ -221,128 +221,103 @@ class OveruseFrameDetectorResourceAdaptationModule::VideoSourceRestrictor {
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 @@ OveruseFrameDetectorResourceAdaptationModule::
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::SetDegradationPreference(
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::SetTargetBitrate(
}
}
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::OnFrameDropped(
void OveruseFrameDetectorResourceAdaptationModule::OnMaybeEncodeFrame() {
initial_framedrop_ = kMaxInitialFramedrop;
MaybePerformQualityRampupExperiment();
}
bool OveruseFrameDetectorResourceAdaptationModule::DropInitialFrames() const {
@ -994,4 +981,38 @@ bool OveruseFrameDetectorResourceAdaptationModule::CanAdaptUpResolution(
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

View File

@ -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 @@ class OveruseFrameDetectorResourceAdaptationModule
DegradationPreference degradation_preference() const {
return degradation_preference_;
}
QualityScaler* quality_scaler() const { return quality_scaler_.get(); }
// ResourceAdaptationModuleInterface implementation.
void StartResourceAdaptation(
@ -78,7 +78,8 @@ class OveruseFrameDetectorResourceAdaptationModule
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 @@ class OveruseFrameDetectorResourceAdaptationModule
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 @@ class OveruseFrameDetectorResourceAdaptationModule
// (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 @@ class OveruseFrameDetectorResourceAdaptationModule
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 @@ class OveruseFrameDetectorResourceAdaptationModule
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 @@ class OveruseFrameDetectorResourceAdaptationModule
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 @@ class OveruseFrameDetectorResourceAdaptationModule
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_;

View File

@ -249,8 +249,6 @@ VideoStreamEncoder::VideoStreamEncoder(
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 @@ void VideoStreamEncoder::ReconfigureEncoder() {
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 @@ void VideoStreamEncoder::SetEncoderRates(
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 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
}
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 @@ bool VideoStreamEncoder::DropDueToSize(uint32_t pixel_count) const {
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_);

View File

@ -173,7 +173,6 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
// 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 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
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_;