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:
parent
3808ffe7f5
commit
06defc4320
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user