Add field trial for rampup in quality based on available bandwidth.
Bug: none Change-Id: I32e1ea6fb2f2e20fc631e09b02c8f3a11b6c9fac Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158888 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Sergey Silkin <ssilkin@webrtc.org> Commit-Queue: Åsa Persson <asapersson@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29751}
This commit is contained in:
parent
a4c1aaad8d
commit
e644a03195
@ -166,6 +166,21 @@ void QualityScaler::ReportQp(int qp, int64_t time_sent_us) {
|
||||
qp_smoother_low_->Add(qp, time_sent_us);
|
||||
}
|
||||
|
||||
bool QualityScaler::QpFastFilterLow() const {
|
||||
RTC_DCHECK_RUN_ON(&task_checker_);
|
||||
size_t num_frames = config_.use_all_drop_reasons
|
||||
? framedrop_percent_all_.Size()
|
||||
: framedrop_percent_media_opt_.Size();
|
||||
const size_t kMinNumFrames = 10;
|
||||
if (num_frames < kMinNumFrames) {
|
||||
return false; // Wait for more frames before making a decision.
|
||||
}
|
||||
absl::optional<int> avg_qp_high = qp_smoother_high_
|
||||
? qp_smoother_high_->GetAvg()
|
||||
: average_qp_.GetAverageRoundedDown();
|
||||
return (avg_qp_high) ? (avg_qp_high.value() <= thresholds_.low) : false;
|
||||
}
|
||||
|
||||
void QualityScaler::CheckQp() {
|
||||
RTC_DCHECK_RUN_ON(&task_checker_);
|
||||
// Should be set through InitEncode -> Should be set by now.
|
||||
|
||||
@ -64,6 +64,7 @@ class QualityScaler {
|
||||
void ReportQp(int qp, int64_t time_sent_us);
|
||||
|
||||
void SetQpThresholds(VideoEncoder::QpThresholds thresholds);
|
||||
bool QpFastFilterLow() const;
|
||||
|
||||
// The following members declared protected for testing purposes.
|
||||
protected:
|
||||
|
||||
@ -46,6 +46,21 @@ rtc_library("field_trial_parser") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("quality_rampup_experiment") {
|
||||
sources = [
|
||||
"quality_rampup_experiment.cc",
|
||||
"quality_rampup_experiment.h",
|
||||
]
|
||||
deps = [
|
||||
":field_trial_parser",
|
||||
"../:rtc_base_approved",
|
||||
"../../api/transport:field_trial_based_config",
|
||||
"../../api/transport:webrtc_key_value_config",
|
||||
"../../system_wrappers:field_trial",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("quality_scaler_settings") {
|
||||
sources = [
|
||||
"quality_scaler_settings.cc",
|
||||
@ -221,6 +236,7 @@ if (rtc_include_tests) {
|
||||
"keyframe_interval_settings_unittest.cc",
|
||||
"min_video_bitrate_experiment_unittest.cc",
|
||||
"normalize_simulcast_size_experiment_unittest.cc",
|
||||
"quality_rampup_experiment_unittest.cc",
|
||||
"quality_scaler_settings_unittest.cc",
|
||||
"quality_scaling_experiment_unittest.cc",
|
||||
"rate_control_settings_unittest.cc",
|
||||
@ -235,6 +251,7 @@ if (rtc_include_tests) {
|
||||
":keyframe_interval_settings_experiment",
|
||||
":min_video_bitrate_experiment",
|
||||
":normalize_simulcast_size_experiment",
|
||||
":quality_rampup_experiment",
|
||||
":quality_scaler_settings",
|
||||
":quality_scaling_experiment",
|
||||
":rate_control_settings",
|
||||
|
||||
73
rtc_base/experiments/quality_rampup_experiment.cc
Normal file
73
rtc_base/experiments/quality_rampup_experiment.cc
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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_rampup_experiment.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
QualityRampupExperiment::QualityRampupExperiment(
|
||||
const WebRtcKeyValueConfig* const key_value_config)
|
||||
: min_pixels_("min_pixels"),
|
||||
min_duration_ms_("min_duration_ms"),
|
||||
max_bitrate_factor_("max_bitrate_factor") {
|
||||
ParseFieldTrial(
|
||||
{&min_pixels_, &min_duration_ms_, &max_bitrate_factor_},
|
||||
key_value_config->Lookup("WebRTC-Video-QualityRampupSettings"));
|
||||
}
|
||||
|
||||
QualityRampupExperiment QualityRampupExperiment::ParseSettings() {
|
||||
FieldTrialBasedConfig field_trial_config;
|
||||
return QualityRampupExperiment(&field_trial_config);
|
||||
}
|
||||
|
||||
absl::optional<int> QualityRampupExperiment::MinPixels() const {
|
||||
return min_pixels_.GetOptional();
|
||||
}
|
||||
|
||||
absl::optional<int> QualityRampupExperiment::MinDurationMs() const {
|
||||
return min_duration_ms_.GetOptional();
|
||||
}
|
||||
|
||||
absl::optional<double> QualityRampupExperiment::MaxBitrateFactor() const {
|
||||
return max_bitrate_factor_.GetOptional();
|
||||
}
|
||||
|
||||
void QualityRampupExperiment::SetMaxBitrate(int pixels,
|
||||
uint32_t max_bitrate_kbps) {
|
||||
if (!min_pixels_ || pixels < min_pixels_.Value() || max_bitrate_kbps == 0) {
|
||||
return;
|
||||
}
|
||||
max_bitrate_kbps_ = std::max(max_bitrate_kbps_.value_or(0), max_bitrate_kbps);
|
||||
}
|
||||
|
||||
bool QualityRampupExperiment::BwHigh(int64_t now_ms,
|
||||
uint32_t available_bw_kbps) {
|
||||
if (!min_pixels_ || !min_duration_ms_ || !max_bitrate_kbps_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (available_bw_kbps <
|
||||
max_bitrate_kbps_.value() * MaxBitrateFactor().value_or(1)) {
|
||||
start_ms_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!start_ms_)
|
||||
start_ms_ = now_ms;
|
||||
|
||||
return (now_ms - *start_ms_) >= min_duration_ms_.Value();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
50
rtc_base/experiments/quality_rampup_experiment.h
Normal file
50
rtc_base/experiments/quality_rampup_experiment.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
|
||||
#define RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/transport/webrtc_key_value_config.h"
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class QualityRampupExperiment final {
|
||||
public:
|
||||
static QualityRampupExperiment ParseSettings();
|
||||
|
||||
absl::optional<int> MinPixels() const;
|
||||
absl::optional<int> MinDurationMs() const;
|
||||
absl::optional<double> MaxBitrateFactor() const;
|
||||
|
||||
// Sets the max bitrate and the frame size.
|
||||
// The call has no effect if the frame size is less than |min_pixels_|.
|
||||
void SetMaxBitrate(int pixels, uint32_t max_bitrate_kbps);
|
||||
|
||||
// Returns true if the available bandwidth is a certain percentage
|
||||
// (max_bitrate_factor_) above |max_bitrate_kbps_| for |min_duration_ms_|.
|
||||
bool BwHigh(int64_t now_ms, uint32_t available_bw_kbps);
|
||||
|
||||
private:
|
||||
explicit QualityRampupExperiment(
|
||||
const WebRtcKeyValueConfig* const key_value_config);
|
||||
|
||||
FieldTrialOptional<int> min_pixels_;
|
||||
FieldTrialOptional<int> min_duration_ms_;
|
||||
FieldTrialOptional<double> max_bitrate_factor_;
|
||||
|
||||
absl::optional<int64_t> start_ms_;
|
||||
absl::optional<uint32_t> max_bitrate_kbps_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
|
||||
139
rtc_base/experiments/quality_rampup_experiment_unittest.cc
Normal file
139
rtc_base/experiments/quality_rampup_experiment_unittest.cc
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2019 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_rampup_experiment.h"
|
||||
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
class QualityRampupExperimentTest : public ::testing::Test {
|
||||
protected:
|
||||
int64_t NowMs() const { return current_ms_; }
|
||||
int64_t AdvanceMs(int64_t delta_ms) {
|
||||
current_ms_ += delta_ms;
|
||||
return current_ms_;
|
||||
}
|
||||
int64_t current_ms_ = 2345;
|
||||
};
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ValuesNotSetByDefault) {
|
||||
const auto settings = QualityRampupExperiment::ParseSettings();
|
||||
EXPECT_FALSE(settings.MinPixels());
|
||||
EXPECT_FALSE(settings.MinDurationMs());
|
||||
EXPECT_FALSE(settings.MaxBitrateFactor());
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ParseMinPixels) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/min_pixels:10000/");
|
||||
EXPECT_EQ(10000, QualityRampupExperiment::ParseSettings().MinPixels());
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ParseMinDuration) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/min_duration_ms:987/");
|
||||
EXPECT_EQ(987, QualityRampupExperiment::ParseSettings().MinDurationMs());
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ParseMaxBitrateFactor) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/max_bitrate_factor:1.23/");
|
||||
EXPECT_EQ(1.23, QualityRampupExperiment::ParseSettings().MaxBitrateFactor());
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ReportsBwHighWhenDurationPassed) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/"
|
||||
"min_pixels:10000,min_duration_ms:2000/");
|
||||
auto exp = QualityRampupExperiment::ParseSettings();
|
||||
EXPECT_EQ(10000, exp.MinPixels());
|
||||
EXPECT_EQ(2000, exp.MinDurationMs());
|
||||
|
||||
const uint32_t kMaxKbps = 800;
|
||||
exp.SetMaxBitrate(/*pixels*/ 10000, kMaxKbps);
|
||||
|
||||
const uint32_t kAvailableKbps = kMaxKbps;
|
||||
EXPECT_FALSE(exp.BwHigh(NowMs(), kAvailableKbps));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000 - 1), kAvailableKbps));
|
||||
EXPECT_TRUE(exp.BwHigh(AdvanceMs(1), kAvailableKbps));
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, UsesMaxSetBitrate) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/"
|
||||
"min_pixels:10000,min_duration_ms:2000/");
|
||||
auto exp = QualityRampupExperiment::ParseSettings();
|
||||
|
||||
const uint32_t kMaxKbps = 800;
|
||||
exp.SetMaxBitrate(/*pixels*/ 10000, kMaxKbps);
|
||||
exp.SetMaxBitrate(/*pixels*/ 10000, kMaxKbps - 1);
|
||||
|
||||
EXPECT_FALSE(exp.BwHigh(NowMs(), kMaxKbps - 1));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000), kMaxKbps - 1));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(1), kMaxKbps));
|
||||
EXPECT_TRUE(exp.BwHigh(AdvanceMs(2000), kMaxKbps));
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, DoesNotReportBwHighIfBelowMinPixels) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/"
|
||||
"min_pixels:10000,min_duration_ms:2000/");
|
||||
auto exp = QualityRampupExperiment::ParseSettings();
|
||||
|
||||
const uint32_t kMaxKbps = 800;
|
||||
exp.SetMaxBitrate(/*pixels*/ 9999, kMaxKbps);
|
||||
|
||||
const uint32_t kAvailableKbps = kMaxKbps;
|
||||
EXPECT_FALSE(exp.BwHigh(NowMs(), kAvailableKbps));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000), kAvailableKbps));
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ReportsBwHighWithMaxBitrateFactor) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/"
|
||||
"min_pixels:10000,min_duration_ms:2000,max_bitrate_factor:1.5/");
|
||||
auto exp = QualityRampupExperiment::ParseSettings();
|
||||
EXPECT_EQ(10000, exp.MinPixels());
|
||||
EXPECT_EQ(2000, exp.MinDurationMs());
|
||||
EXPECT_EQ(1.5, exp.MaxBitrateFactor());
|
||||
|
||||
const uint32_t kMaxKbps = 800;
|
||||
exp.SetMaxBitrate(/*pixels*/ 10000, kMaxKbps);
|
||||
|
||||
const uint32_t kAvailableKbps = kMaxKbps * 1.5;
|
||||
EXPECT_FALSE(exp.BwHigh(NowMs(), kAvailableKbps - 1));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000), kAvailableKbps - 1));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(1), kAvailableKbps));
|
||||
EXPECT_TRUE(exp.BwHigh(AdvanceMs(2000), kAvailableKbps));
|
||||
}
|
||||
|
||||
TEST_F(QualityRampupExperimentTest, ReportsBwHigh) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/"
|
||||
"min_pixels:10000,min_duration_ms:2000/");
|
||||
auto exp = QualityRampupExperiment::ParseSettings();
|
||||
|
||||
const uint32_t kMaxKbps = 800;
|
||||
exp.SetMaxBitrate(/*pixels*/ 10000, kMaxKbps);
|
||||
|
||||
const uint32_t kAvailableKbps = kMaxKbps;
|
||||
EXPECT_FALSE(exp.BwHigh(NowMs(), kAvailableKbps));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000 - 1), kAvailableKbps));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(1), kAvailableKbps - 1)); // Below, reset.
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(1), kAvailableKbps));
|
||||
EXPECT_FALSE(exp.BwHigh(AdvanceMs(2000 - 1), kAvailableKbps));
|
||||
EXPECT_TRUE(exp.BwHigh(AdvanceMs(1), kAvailableKbps));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
@ -72,6 +72,11 @@ void FakeEncoder::SetMaxBitrate(int max_kbps) {
|
||||
SetRates(current_rate_settings_);
|
||||
}
|
||||
|
||||
void FakeEncoder::SetQp(int qp) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
qp_ = qp;
|
||||
}
|
||||
|
||||
int32_t FakeEncoder::InitEncode(const VideoCodec* config,
|
||||
const Settings& settings) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
@ -93,6 +98,7 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
|
||||
VideoCodecMode mode;
|
||||
bool keyframe;
|
||||
uint32_t counter;
|
||||
absl::optional<int> qp;
|
||||
{
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
max_framerate = config_.maxFramerate;
|
||||
@ -109,6 +115,7 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
|
||||
keyframe = pending_keyframe_;
|
||||
pending_keyframe_ = false;
|
||||
counter = counter_++;
|
||||
qp = qp_;
|
||||
}
|
||||
|
||||
FrameInfo frame_info =
|
||||
@ -134,6 +141,8 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
|
||||
: VideoFrameType::kVideoFrameDelta;
|
||||
encoded._encodedWidth = simulcast_streams[i].width;
|
||||
encoded._encodedHeight = simulcast_streams[i].height;
|
||||
if (qp)
|
||||
encoded.qp_ = *qp;
|
||||
encoded.SetSpatialIndex(i);
|
||||
CodecSpecificInfo codec_specific;
|
||||
std::unique_ptr<RTPFragmentationHeader> fragmentation =
|
||||
|
||||
@ -41,6 +41,7 @@ class FakeEncoder : public VideoEncoder {
|
||||
|
||||
// Sets max bitrate. Not thread-safe, call before registering the encoder.
|
||||
void SetMaxBitrate(int max_kbps);
|
||||
void SetQp(int qp);
|
||||
|
||||
void SetFecControllerOverride(
|
||||
FecControllerOverride* fec_controller_override) override;
|
||||
@ -98,6 +99,7 @@ class FakeEncoder : public VideoEncoder {
|
||||
uint32_t counter_ RTC_GUARDED_BY(crit_sect_);
|
||||
rtc::CriticalSection crit_sect_;
|
||||
bool used_layers_[kMaxSimulcastStreams];
|
||||
absl::optional<int> qp_ RTC_GUARDED_BY(crit_sect_);
|
||||
|
||||
// Current byte debt to be payed over a number of frames.
|
||||
// The debt is acquired by keyframes overshooting the bitrate target.
|
||||
|
||||
@ -218,6 +218,7 @@ rtc_library("video_stream_encoder_impl") {
|
||||
"../rtc_base/experiments:alr_experiment",
|
||||
"../rtc_base/experiments:balanced_degradation_settings",
|
||||
"../rtc_base/experiments:field_trial_parser",
|
||||
"../rtc_base/experiments:quality_rampup_experiment",
|
||||
"../rtc_base/experiments:quality_scaler_settings",
|
||||
"../rtc_base/experiments:quality_scaling_experiment",
|
||||
"../rtc_base/experiments:rate_control_settings",
|
||||
|
||||
@ -483,6 +483,8 @@ VideoStreamEncoder::VideoStreamEncoder(
|
||||
initial_framedrop_(0),
|
||||
initial_framedrop_on_bwe_enabled_(
|
||||
webrtc::field_trial::IsEnabled(kInitialFramedropFieldTrial)),
|
||||
quality_rampup_done_(false),
|
||||
quality_rampup_experiment_(QualityRampupExperiment::ParseSettings()),
|
||||
quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
|
||||
source_proxy_(new VideoSourceProxy(this)),
|
||||
sink_(nullptr),
|
||||
@ -879,6 +881,8 @@ 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
|
||||
@ -1359,6 +1363,16 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
|
||||
}
|
||||
initial_framedrop_ = kMaxInitialFramedrop;
|
||||
|
||||
if (!quality_rampup_done_ && TryQualityRampup(now_ms) &&
|
||||
GetConstAdaptCounter().ResolutionCount(kQuality) > 0 &&
|
||||
GetConstAdaptCounter().TotalCount(kCpu) == 0) {
|
||||
RTC_LOG(LS_INFO) << "Reset quality limitations.";
|
||||
last_adaptation_request_.reset();
|
||||
source_proxy_->ResetPixelFpsCount();
|
||||
adapt_counters_.clear();
|
||||
quality_rampup_done_ = true;
|
||||
}
|
||||
|
||||
if (EncoderPaused()) {
|
||||
// Storing references to a native buffer risks blocking frame capture.
|
||||
if (video_frame.video_frame_buffer()->type() !=
|
||||
@ -1900,6 +1914,25 @@ bool VideoStreamEncoder::DropDueToSize(uint32_t pixel_count) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoStreamEncoder::TryQualityRampup(int64_t now_ms) {
|
||||
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_start_bitrate_bps_ == send_codec_.maxBitrate * 1000 &&
|
||||
quality_scaler_->QpFastFilterLow()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoStreamEncoder::AdaptDown(AdaptReason reason) {
|
||||
RTC_DCHECK_RUN_ON(&encoder_queue_);
|
||||
AdaptationRequest adaptation_request = {
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/event.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 "rtc_base/experiments/rate_control_settings.h"
|
||||
#include "rtc_base/numerics/exp_filter.h"
|
||||
@ -158,6 +159,7 @@ 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(
|
||||
@ -238,6 +240,9 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
|
||||
int initial_framedrop_;
|
||||
const bool initial_framedrop_on_bwe_enabled_;
|
||||
bool has_seen_first_significant_bwe_change_ = false;
|
||||
bool quality_rampup_done_ RTC_GUARDED_BY(&encoder_queue_);
|
||||
QualityRampupExperiment quality_rampup_experiment_
|
||||
RTC_GUARDED_BY(&encoder_queue_);
|
||||
|
||||
const bool quality_scaling_experiment_enabled_;
|
||||
|
||||
|
||||
@ -55,6 +55,8 @@ using ::testing::StrictMock;
|
||||
|
||||
namespace {
|
||||
const int kMinPixelsPerFrame = 320 * 180;
|
||||
const int kQpLow = 1;
|
||||
const int kQpHigh = 2;
|
||||
const int kMinFramerateFps = 2;
|
||||
const int kMinBalancedFramerateFps = 7;
|
||||
const int64_t kFrameTimeoutMs = 100;
|
||||
@ -683,8 +685,8 @@ class VideoStreamEncoderTest : public ::testing::Test {
|
||||
EncoderInfo info;
|
||||
if (initialized_ == EncoderState::kInitialized) {
|
||||
if (quality_scaling_) {
|
||||
info.scaling_settings =
|
||||
VideoEncoder::ScalingSettings(1, 2, kMinPixelsPerFrame);
|
||||
info.scaling_settings = VideoEncoder::ScalingSettings(
|
||||
kQpLow, kQpHigh, kMinPixelsPerFrame);
|
||||
}
|
||||
info.is_hardware_accelerated = is_hardware_accelerated_;
|
||||
for (int i = 0; i < kMaxSpatialLayers; ++i) {
|
||||
@ -3704,6 +3706,69 @@ TEST_F(VideoStreamEncoderTest, InitialFrameDropActivatesWhenBweDrops) {
|
||||
video_stream_encoder_->Stop();
|
||||
}
|
||||
|
||||
TEST_F(VideoStreamEncoderTest, RampsUpInQualityWhenBwIsHigh) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-QualityRampupSettings/min_pixels:1,min_duration_ms:2000/");
|
||||
|
||||
// Reset encoder for field trials to take effect.
|
||||
VideoEncoderConfig config = video_encoder_config_.Copy();
|
||||
config.max_bitrate_bps = kTargetBitrateBps;
|
||||
ConfigureEncoder(std::move(config));
|
||||
fake_encoder_.SetQp(kQpLow);
|
||||
|
||||
// Enable MAINTAIN_FRAMERATE preference.
|
||||
AdaptingFrameForwarder source;
|
||||
source.set_adaptation_enabled(true);
|
||||
video_stream_encoder_->SetSource(&source,
|
||||
DegradationPreference::MAINTAIN_FRAMERATE);
|
||||
|
||||
// Start at low bitrate.
|
||||
const int kLowBitrateBps = 200000;
|
||||
video_stream_encoder_->OnBitrateUpdated(DataRate::bps(kLowBitrateBps),
|
||||
DataRate::bps(kLowBitrateBps),
|
||||
DataRate::bps(kLowBitrateBps), 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;
|
||||
int64_t timestamp_ms = kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
ExpectDroppedFrame();
|
||||
EXPECT_LT(source.sink_wants().max_pixel_count, kWidth * kHeight);
|
||||
|
||||
// Increase bitrate to encoder max.
|
||||
video_stream_encoder_->OnBitrateUpdated(DataRate::bps(config.max_bitrate_bps),
|
||||
DataRate::bps(config.max_bitrate_bps),
|
||||
DataRate::bps(config.max_bitrate_bps),
|
||||
0, 0);
|
||||
|
||||
// Insert frames and advance |min_duration_ms|.
|
||||
for (size_t i = 1; i <= 10; i++) {
|
||||
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);
|
||||
|
||||
fake_clock_.AdvanceTime(TimeDelta::ms(2000));
|
||||
|
||||
// Insert frame should trigger high BW and release quality limitation.
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
WaitForEncodedFrame(timestamp_ms);
|
||||
VerifyFpsMaxResolutionMax(source.sink_wants());
|
||||
|
||||
// Frame should not be adapted.
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
WaitForEncodedFrame(kWidth, kHeight);
|
||||
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
|
||||
video_stream_encoder_->Stop();
|
||||
}
|
||||
|
||||
TEST_F(VideoStreamEncoderTest,
|
||||
ResolutionNotAdaptedForTooSmallFrame_MaintainFramerateMode) {
|
||||
const int kTooSmallWidth = 10;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user