QualityRampupExperiment: SetMaxBitrate may not be set correctly.

Call SetMaxBitrate when encoder is configured instead of in OnMaybeEncodeFrame (which is called after the initial frame dropping ->
max bitrate is not set for dropped frames).

Added support for single active stream configuration.

Bug: none
Change-Id: I33ff96e7feed70b9ea3c9b3da89f117859108347
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231681
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34973}
This commit is contained in:
Åsa Persson 2021-09-10 15:28:48 +02:00 committed by WebRTC LUCI CQ
parent 3808ffe7f5
commit 06defc4320
6 changed files with 82 additions and 18 deletions

View File

@ -70,8 +70,13 @@ bool QualityRampupExperiment::BwHigh(int64_t now_ms,
return (now_ms - *start_ms_) >= min_duration_ms_.Value();
}
void QualityRampupExperiment::Reset() {
start_ms_.reset();
max_bitrate_kbps_.reset();
}
bool QualityRampupExperiment::Enabled() const {
return min_pixels_ || min_duration_ms_ || max_bitrate_kbps_;
return min_pixels_ && min_duration_ms_;
}
} // namespace webrtc

View File

@ -33,6 +33,7 @@ class QualityRampupExperiment final {
// (max_bitrate_factor_) above `max_bitrate_kbps_` for `min_duration_ms_`.
bool BwHigh(int64_t now_ms, uint32_t available_bw_kbps);
void Reset();
bool Enabled() const;
private:

View File

@ -43,22 +43,30 @@ QualityRampUpExperimentHelper::CreateIfEnabled(
return nullptr;
}
void QualityRampUpExperimentHelper::ConfigureQualityRampupExperiment(
bool reset,
absl::optional<uint32_t> pixels,
absl::optional<DataRate> max_bitrate) {
if (reset)
quality_rampup_experiment_.Reset();
if (pixels && max_bitrate)
quality_rampup_experiment_.SetMaxBitrate(*pixels, max_bitrate->kbps());
}
void QualityRampUpExperimentHelper::PerformQualityRampupExperiment(
rtc::scoped_refptr<QualityScalerResource> quality_scaler_resource,
DataRate bandwidth,
DataRate encoder_target_bitrate,
DataRate max_bitrate,
int pixels) {
if (!quality_scaler_resource->is_started())
absl::optional<DataRate> max_bitrate) {
if (!quality_scaler_resource->is_started() || !max_bitrate)
return;
int64_t now_ms = clock_->TimeInMilliseconds();
quality_rampup_experiment_.SetMaxBitrate(pixels, max_bitrate.kbps());
bool try_quality_rampup = false;
if (quality_rampup_experiment_.BwHigh(now_ms, bandwidth.kbps())) {
// Verify that encoder is at max bitrate and the QP is low.
if (encoder_target_bitrate == max_bitrate &&
if (encoder_target_bitrate == *max_bitrate &&
quality_scaler_resource->QpFastFilterLow()) {
try_quality_rampup = true;
}

View File

@ -44,12 +44,15 @@ class QualityRampUpExperimentHelper {
void cpu_adapted(bool cpu_adapted);
void qp_resolution_adaptations(int qp_adaptations);
void ConfigureQualityRampupExperiment(bool reset,
absl::optional<uint32_t> pixels,
absl::optional<DataRate> max_bitrate);
void PerformQualityRampupExperiment(
rtc::scoped_refptr<QualityScalerResource> quality_scaler_resource,
DataRate bandwidth,
DataRate encoder_target_bitrate,
DataRate max_bitrate,
int pixels);
absl::optional<DataRate> max_bitrate);
private:
QualityRampUpExperimentHelper(

View File

@ -88,6 +88,30 @@ bool EqualFlags(const std::vector<bool>& a, const std::vector<bool>& b) {
return std::equal(a.begin(), a.end(), b.begin());
}
absl::optional<DataRate> GetSingleActiveLayerMaxBitrate(
const VideoCodec& codec) {
int num_active = 0;
absl::optional<DataRate> max_bitrate;
if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
for (int i = 0; i < codec.VP9().numberOfSpatialLayers; ++i) {
if (codec.spatialLayers[i].active) {
++num_active;
max_bitrate =
DataRate::KilobitsPerSec(codec.spatialLayers[i].maxBitrate);
}
}
} else {
for (int i = 0; i < codec.numberOfSimulcastStreams; ++i) {
if (codec.simulcastStream[i].active) {
++num_active;
max_bitrate =
DataRate::KilobitsPerSec(codec.simulcastStream[i].maxBitrate);
}
}
}
return (num_active > 1) ? absl::nullopt : max_bitrate;
}
} // namespace
class VideoStreamEncoderResourceManager::InitialFrameDropper {
@ -103,7 +127,8 @@ class VideoStreamEncoderResourceManager::InitialFrameDropper {
use_bandwidth_allocation_(false),
bandwidth_allocation_(DataRate::Zero()),
last_input_width_(0),
last_input_height_(0) {
last_input_height_(0),
last_stream_configuration_changed_(false) {
RTC_DCHECK(quality_scaler_resource_);
}
@ -123,6 +148,10 @@ class VideoStreamEncoderResourceManager::InitialFrameDropper {
: absl::nullopt;
}
bool last_stream_configuration_changed() const {
return last_stream_configuration_changed_;
}
// Input signals.
void SetStartBitrate(DataRate start_bitrate, int64_t now_ms) {
set_start_bitrate_ = start_bitrate;
@ -156,6 +185,7 @@ class VideoStreamEncoderResourceManager::InitialFrameDropper {
void OnEncoderSettingsUpdated(
const VideoCodec& codec,
const VideoAdaptationCounters& adaptation_counters) {
last_stream_configuration_changed_ = false;
std::vector<bool> active_flags = GetActiveLayersFlags(codec);
// Check if the source resolution has changed for the external reasons,
// i.e. without any adaptation from WebRTC.
@ -167,6 +197,7 @@ class VideoStreamEncoderResourceManager::InitialFrameDropper {
if (!EqualFlags(active_flags, last_active_flags_) ||
source_resolution_changed) {
// Streams configuration has changed.
last_stream_configuration_changed_ = true;
// Initial frame drop must be enabled because BWE might be way too low
// for the selected resolution.
if (quality_scaler_resource_->is_started()) {
@ -226,6 +257,7 @@ class VideoStreamEncoderResourceManager::InitialFrameDropper {
VideoAdaptationCounters last_adaptation_counters_;
int last_input_width_;
int last_input_height_;
bool last_stream_configuration_changed_;
};
VideoStreamEncoderResourceManager::VideoStreamEncoderResourceManager(
@ -394,6 +426,12 @@ void VideoStreamEncoderResourceManager::SetEncoderSettings(
initial_frame_dropper_->OnEncoderSettingsUpdated(
encoder_settings_->video_codec(), current_adaptation_counters_);
MaybeUpdateTargetFrameRate();
if (quality_rampup_experiment_) {
quality_rampup_experiment_->ConfigureQualityRampupExperiment(
initial_frame_dropper_->last_stream_configuration_changed(),
initial_frame_dropper_->single_active_stream_pixels(),
GetSingleActiveLayerMaxBitrate(encoder_settings_->video_codec()));
}
}
void VideoStreamEncoderResourceManager::SetStartBitrate(
@ -497,8 +535,7 @@ void VideoStreamEncoderResourceManager::OnMaybeEncodeFrame() {
quality_rampup_experiment_->PerformQualityRampupExperiment(
quality_scaler_resource_, bandwidth,
DataRate::BitsPerSec(encoder_target_bitrate_bps_.value_or(0)),
DataRate::KilobitsPerSec(encoder_settings_->video_codec().maxBitrate),
LastFrameSizeOrDefault());
GetSingleActiveLayerMaxBitrate(encoder_settings_->video_codec()));
}
}

View File

@ -6140,7 +6140,13 @@ TEST_F(VideoStreamEncoderTest,
TEST_F(VideoStreamEncoderTest, RampsUpInQualityWhenBwIsHigh) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-QualityRampupSettings/min_pixels:1,min_duration_ms:2000/");
"WebRTC-Video-QualityRampupSettings/"
"min_pixels:921600,min_duration_ms:2000/");
const int kWidth = 1280;
const int kHeight = 720;
const int kFps = 10;
max_framerate_ = kFps;
// Reset encoder for field trials to take effect.
VideoEncoderConfig config = video_encoder_config_.Copy();
@ -6163,9 +6169,7 @@ TEST_F(VideoStreamEncoderTest, RampsUpInQualityWhenBwIsHigh) {
DataRate::BitsPerSec(kLowBitrateBps), 0, 0, 0);
// Expect first frame to be dropped and resolution to be limited.
const int kWidth = 1280;
const int kHeight = 720;
const int64_t kFrameIntervalMs = 100;
const int64_t kFrameIntervalMs = 1000 / kFps;
int64_t timestamp_ms = kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
ExpectDroppedFrame();
@ -6177,17 +6181,23 @@ TEST_F(VideoStreamEncoderTest, RampsUpInQualityWhenBwIsHigh) {
max_bitrate, max_bitrate, max_bitrate, 0, 0, 0);
// Insert frames and advance `min_duration_ms`.
const int64_t start_bw_high_ms = CurrentTimeMs();
for (size_t i = 1; i <= 10; i++) {
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
WaitForEncodedFrame(timestamp_ms);
}
// Advance to `min_duration_ms` - 1, frame should not trigger high BW.
int64_t elapsed_bw_high_ms = CurrentTimeMs() - start_bw_high_ms;
AdvanceTime(TimeDelta::Millis(2000 - elapsed_bw_high_ms - 1));
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
WaitForEncodedFrame(timestamp_ms);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_LT(source.sink_wants().max_pixel_count, kWidth * kHeight);
AdvanceTime(TimeDelta::Millis(2000));
// Insert frame should trigger high BW and release quality limitation.
// Frame should trigger high BW and release quality limitation.
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
WaitForEncodedFrame(timestamp_ms);