BalancedDegradationSettings: add option to configure min bitrate.
Add possibility to configure min bitrate based on resolution. Only adapt up if bw estimate is above the min bitrate for next higher resolution. Bug: none Change-Id: Ie38faae07d23336675ec33697ace6f6fed322efa Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/148598 Reviewed-by: Magnus Flodman <mflodman@webrtc.org> Commit-Queue: Åsa Persson <asapersson@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28863}
This commit is contained in:
@ -24,9 +24,9 @@ constexpr int kMinFps = 1;
|
||||
constexpr int kMaxFps = 100; // 100 means unlimited fps.
|
||||
|
||||
std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
|
||||
return {{320 * 240, 7, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{480 * 270, 10, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{640 * 480, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
|
||||
return {{320 * 240, 7, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{480 * 270, 10, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{640 * 480, 15, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
|
||||
}
|
||||
|
||||
bool IsValidConfig(
|
||||
@ -75,6 +75,16 @@ bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int last_kbps = configs[0].kbps;
|
||||
for (size_t i = 1; i < configs.size(); ++i) {
|
||||
if (configs[i].kbps > 0) {
|
||||
if (configs[i].kbps < last_kbps) {
|
||||
RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
|
||||
return false;
|
||||
}
|
||||
last_kbps = configs[i].kbps;
|
||||
}
|
||||
}
|
||||
for (size_t i = 1; i < configs.size(); ++i) {
|
||||
if (configs[i].pixels < configs[i - 1].pixels ||
|
||||
configs[i].fps < configs[i - 1].fps) {
|
||||
@ -189,12 +199,14 @@ BalancedDegradationSettings::Config::Config() = default;
|
||||
|
||||
BalancedDegradationSettings::Config::Config(int pixels,
|
||||
int fps,
|
||||
int kbps,
|
||||
CodecTypeSpecific vp8,
|
||||
CodecTypeSpecific vp9,
|
||||
CodecTypeSpecific h264,
|
||||
CodecTypeSpecific generic)
|
||||
: pixels(pixels),
|
||||
fps(fps),
|
||||
kbps(kbps),
|
||||
vp8(vp8),
|
||||
vp9(vp9),
|
||||
h264(h264),
|
||||
@ -204,6 +216,7 @@ BalancedDegradationSettings::BalancedDegradationSettings() {
|
||||
FieldTrialStructList<Config> configs(
|
||||
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
|
||||
FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
|
||||
FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
|
||||
FieldTrialStructMember("vp8_qp_low",
|
||||
[](Config* c) { return &c->vp8.qp_low; }),
|
||||
FieldTrialStructMember("vp8_qp_high",
|
||||
@ -267,6 +280,18 @@ BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
|
||||
int pixels) const {
|
||||
for (size_t i = 0; i < configs_.size() - 1; ++i) {
|
||||
if (pixels <= configs_[i].pixels) {
|
||||
return (configs_[i + 1].kbps > 0)
|
||||
? absl::optional<int>(configs_[i + 1].kbps)
|
||||
: absl::nullopt;
|
||||
}
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<VideoEncoder::QpThresholds>
|
||||
BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
|
||||
int pixels) const {
|
||||
|
||||
@ -44,19 +44,23 @@ class BalancedDegradationSettings {
|
||||
Config();
|
||||
Config(int pixels,
|
||||
int fps,
|
||||
int kbps,
|
||||
CodecTypeSpecific vp8,
|
||||
CodecTypeSpecific vp9,
|
||||
CodecTypeSpecific h264,
|
||||
CodecTypeSpecific generic);
|
||||
|
||||
bool operator==(const Config& o) const {
|
||||
return pixels == o.pixels && fps == o.fps && vp8 == o.vp8 &&
|
||||
vp9 == o.vp9 && h264 == o.h264 && generic == o.generic;
|
||||
return pixels == o.pixels && fps == o.fps && kbps == o.kbps &&
|
||||
vp8 == o.vp8 && vp9 == o.vp9 && h264 == o.h264 &&
|
||||
generic == o.generic;
|
||||
}
|
||||
|
||||
int pixels = 0; // The video frame size.
|
||||
int fps = 0; // The framerate and thresholds to be used if the
|
||||
CodecTypeSpecific vp8; // frame size is less than or equal to |pixels|.
|
||||
int pixels = 0; // Video frame size.
|
||||
// If the frame size is less than or equal to |pixels|:
|
||||
int fps = 0; // Min framerate to be used.
|
||||
int kbps = 0; // Min bitrate needed to adapt up to this resolution.
|
||||
CodecTypeSpecific vp8;
|
||||
CodecTypeSpecific vp9;
|
||||
CodecTypeSpecific h264;
|
||||
CodecTypeSpecific generic;
|
||||
@ -69,6 +73,9 @@ class BalancedDegradationSettings {
|
||||
int MinFps(VideoCodecType type, int pixels) const;
|
||||
int MaxFps(VideoCodecType type, int pixels) const;
|
||||
|
||||
// Gets the bitrate for the first resolution above |pixels|.
|
||||
absl::optional<int> NextHigherBitrateKbps(int pixels) const;
|
||||
|
||||
// Gets QpThresholds for the codec |type| based on |pixels|.
|
||||
absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
|
||||
VideoCodecType type,
|
||||
|
||||
@ -25,11 +25,11 @@ void VerifyIsDefault(
|
||||
config,
|
||||
::testing::ElementsAre(
|
||||
BalancedDegradationSettings::Config{
|
||||
320 * 240, 7, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
320 * 240, 7, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
480 * 270, 10, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
480 * 270, 10, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
640 * 480, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
|
||||
640 * 480, 15, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -37,6 +37,7 @@ TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) {
|
||||
webrtc::test::ScopedFieldTrials field_trials("");
|
||||
BalancedDegradationSettings settings;
|
||||
VerifyIsDefault(settings.GetConfigs());
|
||||
EXPECT_FALSE(settings.NextHigherBitrateKbps(1));
|
||||
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1));
|
||||
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1));
|
||||
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecH264, 1));
|
||||
@ -52,11 +53,11 @@ TEST(BalancedDegradationSettings, GetsConfig) {
|
||||
EXPECT_THAT(settings.GetConfigs(),
|
||||
::testing::ElementsAre(
|
||||
BalancedDegradationSettings::Config{
|
||||
11, 5, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
11, 5, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
22, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
22, 15, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
33, 25, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
|
||||
33, 25, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroFpsValue) {
|
||||
@ -93,11 +94,11 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
|
||||
settings.GetConfigs(),
|
||||
::testing::ElementsAre(
|
||||
BalancedDegradationSettings::Config{
|
||||
1000, 5, {0, 0, 7}, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}},
|
||||
1000, 5, 0, {0, 0, 7}, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}},
|
||||
BalancedDegradationSettings::Config{
|
||||
2000, 15, {0, 0, 8}, {0, 0, 10}, {0, 0, 12}, {0, 0, 14}},
|
||||
2000, 15, 0, {0, 0, 8}, {0, 0, 10}, {0, 0, 12}, {0, 0, 14}},
|
||||
BalancedDegradationSettings::Config{
|
||||
3000, 25, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}, {0, 0, 15}}));
|
||||
3000, 25, 0, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}, {0, 0, 15}}));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroVp8FpsValue) {
|
||||
@ -220,6 +221,62 @@ TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) {
|
||||
EXPECT_EQ(kUnlimitedFps, settings.MinFps(kVideoCodecVP8, 1001));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:11|22|33,fps:5|15|25,kbps:44|88|99/");
|
||||
BalancedDegradationSettings settings;
|
||||
EXPECT_THAT(settings.GetConfigs(),
|
||||
::testing::ElementsAre(
|
||||
BalancedDegradationSettings::Config{
|
||||
11, 5, 44, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
22, 15, 88, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
33, 25, 99, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreases) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:11|22|33,fps:5|15|25,kbps:44|43|99/");
|
||||
BalancedDegradationSettings settings;
|
||||
VerifyIsDefault(settings.GetConfigs());
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings,
|
||||
GetsDefaultConfigIfBitrateDecreasesWithUnsetValue) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:11|22|33,fps:5|15|25,kbps:44|0|43/");
|
||||
BalancedDegradationSettings settings;
|
||||
VerifyIsDefault(settings.GetConfigs());
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsNextHigherBitrate) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:1000|2000|3000,fps:5|15|25,kbps:44|88|99/");
|
||||
BalancedDegradationSettings settings;
|
||||
EXPECT_EQ(88, settings.NextHigherBitrateKbps(1));
|
||||
EXPECT_EQ(88, settings.NextHigherBitrateKbps(1000));
|
||||
EXPECT_EQ(99, settings.NextHigherBitrateKbps(1001));
|
||||
EXPECT_EQ(99, settings.NextHigherBitrateKbps(2000));
|
||||
EXPECT_FALSE(settings.NextHigherBitrateKbps(2001));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsNextHigherBitrateWithUnsetValue) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:1000|2000|3000,fps:5|15|25,kbps:10|0|20/");
|
||||
BalancedDegradationSettings settings;
|
||||
EXPECT_FALSE(settings.NextHigherBitrateKbps(1));
|
||||
EXPECT_FALSE(settings.NextHigherBitrateKbps(1000));
|
||||
EXPECT_EQ(20, settings.NextHigherBitrateKbps(1001));
|
||||
EXPECT_EQ(20, settings.NextHigherBitrateKbps(2000));
|
||||
EXPECT_FALSE(settings.NextHigherBitrateKbps(2001));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
@ -235,7 +292,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:1000|2000|3000,fps:5|15|25,vp8_qp_low:89|90|88,"
|
||||
"vp8_qp_high:90|91|92,vp9_qp_low:27|28|29,vp9_qp_high:120|130|140,"
|
||||
"vp8_qp_high:90|91|92,vp9_qp_low:27|28|29,vp9_qp_high:82|83|84,"
|
||||
"h264_qp_low:12|13|14,h264_qp_high:20|30|40,generic_qp_low:7|6|5,"
|
||||
"generic_qp_high:22|23|24/");
|
||||
BalancedDegradationSettings settings;
|
||||
@ -243,11 +300,11 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) {
|
||||
settings.GetConfigs(),
|
||||
::testing::ElementsAre(
|
||||
BalancedDegradationSettings::Config{
|
||||
1000, 5, {89, 90, 0}, {27, 120, 0}, {12, 20, 0}, {7, 22, 0}},
|
||||
1000, 5, 0, {89, 90, 0}, {27, 82, 0}, {12, 20, 0}, {7, 22, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
2000, 15, {90, 91, 0}, {28, 130, 0}, {13, 30, 0}, {6, 23, 0}},
|
||||
2000, 15, 0, {90, 91, 0}, {28, 83, 0}, {13, 30, 0}, {6, 23, 0}},
|
||||
BalancedDegradationSettings::Config{
|
||||
3000, 25, {88, 92, 0}, {29, 140, 0}, {14, 40, 0}, {5, 24, 0}}));
|
||||
3000, 25, 0, {88, 92, 0}, {29, 84, 0}, {14, 40, 0}, {5, 24, 0}}));
|
||||
}
|
||||
|
||||
TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasLowThreshold) {
|
||||
|
||||
@ -1900,6 +1900,16 @@ void VideoStreamEncoder::AdaptUp(AdaptReason reason) {
|
||||
|
||||
switch (degradation_preference_) {
|
||||
case DegradationPreference::BALANCED: {
|
||||
// Do not adapt up if bwe is less than min bitrate for next resolution.
|
||||
absl::optional<int> next_layer_min_kbps =
|
||||
balanced_settings_.NextHigherBitrateKbps(
|
||||
last_frame_info_->pixel_count());
|
||||
if (next_layer_min_kbps && encoder_start_bitrate_bps_ > 0 &&
|
||||
reason == kQuality &&
|
||||
encoder_start_bitrate_bps_ <
|
||||
static_cast<uint32_t>(next_layer_min_kbps.value() * 1000)) {
|
||||
return;
|
||||
}
|
||||
// Try scale up framerate, if higher.
|
||||
int fps = balanced_settings_.MaxFps(encoder_config_.codec_type,
|
||||
last_frame_info_->pixel_count());
|
||||
|
||||
@ -2599,6 +2599,80 @@ TEST_F(VideoStreamEncoderTest,
|
||||
video_stream_encoder_->Stop();
|
||||
}
|
||||
|
||||
TEST_F(VideoStreamEncoderTest, NoAdaptUpIfBwEstimateIsLessThanMinBitrate) {
|
||||
webrtc::test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-Video-BalancedDegradationSettings/"
|
||||
"pixels:57600|129600|230400,fps:7|10|14,kbps:0|0|425/");
|
||||
// Reset encoder for field trials to take effect.
|
||||
ConfigureEncoder(video_encoder_config_.Copy());
|
||||
|
||||
const int kWidth = 640; // pixels:640x360=230400
|
||||
const int kHeight = 360;
|
||||
const int64_t kFrameIntervalMs = 150;
|
||||
const int kMinBitrateBps = 425000;
|
||||
const int kTooLowMinBitrateBps = 424000;
|
||||
video_stream_encoder_->OnBitrateUpdated(DataRate::bps(kTooLowMinBitrateBps),
|
||||
DataRate::bps(kTooLowMinBitrateBps),
|
||||
0, 0);
|
||||
|
||||
// Enable BALANCED preference, no initial limitation.
|
||||
AdaptingFrameForwarder source;
|
||||
source.set_adaptation_enabled(true);
|
||||
video_stream_encoder_->SetSource(&source,
|
||||
webrtc::DegradationPreference::BALANCED);
|
||||
|
||||
int64_t timestamp_ms = kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
sink_.WaitForEncodedFrame(kWidth, kHeight);
|
||||
VerifyFpsMaxResolutionMax(source.sink_wants());
|
||||
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
|
||||
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
|
||||
|
||||
// Trigger adapt down, expect scaled down framerate (640x360@14fps).
|
||||
video_stream_encoder_->TriggerQualityLow();
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
sink_.WaitForEncodedFrame(timestamp_ms);
|
||||
VerifyFpsEqResolutionMax(source.sink_wants(), 14);
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
|
||||
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
|
||||
|
||||
// Trigger adapt down, expect scaled down resolution (480x270@14fps).
|
||||
video_stream_encoder_->TriggerQualityLow();
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
sink_.WaitForEncodedFrame(timestamp_ms);
|
||||
VerifyFpsEqResolutionLt(source.sink_wants(), source.last_wants());
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
|
||||
|
||||
// Trigger adapt up, expect no upscale (target bitrate < min bitrate).
|
||||
video_stream_encoder_->TriggerQualityHigh();
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
sink_.WaitForEncodedFrame(timestamp_ms);
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
|
||||
|
||||
// Trigger adapt up, expect upscaled resolution (target bitrate == min
|
||||
// bitrate).
|
||||
video_stream_encoder_->OnBitrateUpdated(DataRate::bps(kMinBitrateBps),
|
||||
DataRate::bps(kMinBitrateBps), 0, 0);
|
||||
video_stream_encoder_->TriggerQualityHigh();
|
||||
timestamp_ms += kFrameIntervalMs;
|
||||
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
|
||||
sink_.WaitForEncodedFrame(timestamp_ms);
|
||||
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
|
||||
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
|
||||
EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
|
||||
|
||||
video_stream_encoder_->Stop();
|
||||
}
|
||||
|
||||
TEST_F(VideoStreamEncoderTest,
|
||||
AdaptsResolutionOnOveruseAndLowQuality_MaintainFramerateMode) {
|
||||
const int kWidth = 1280;
|
||||
|
||||
Reference in New Issue
Block a user