BalancedDegradationSettings: add min bitrate configuration for resolution.

Add separate setting for configuring min bitrate that only applies when
adapting up in resolution.

Bug: none
Change-Id: I83d33ac3110a22602065b8d83130e3f619cb1eba
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150329
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Mirta Dvornicic <mirtad@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28970}
This commit is contained in:
Åsa Persson
2019-08-27 12:22:33 +02:00
committed by Commit Bot
parent 31d1bcef23
commit 30ab015fc9
5 changed files with 295 additions and 19 deletions

View File

@ -27,6 +27,7 @@ std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
return {{320 * 240, return {{320 * 240,
7, 7,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -35,6 +36,7 @@ std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
{480 * 270, {480 * 270,
10, 10,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -43,6 +45,7 @@ std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
{640 * 480, {640 * 480,
15, 15,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -221,6 +224,7 @@ BalancedDegradationSettings::Config::Config() = default;
BalancedDegradationSettings::Config::Config(int pixels, BalancedDegradationSettings::Config::Config(int pixels,
int fps, int fps,
int kbps, int kbps,
int kbps_res,
int fps_diff, int fps_diff,
CodecTypeSpecific vp8, CodecTypeSpecific vp8,
CodecTypeSpecific vp9, CodecTypeSpecific vp9,
@ -229,6 +233,7 @@ BalancedDegradationSettings::Config::Config(int pixels,
: pixels(pixels), : pixels(pixels),
fps(fps), fps(fps),
kbps(kbps), kbps(kbps),
kbps_res(kbps_res),
fps_diff(fps_diff), fps_diff(fps_diff),
vp8(vp8), vp8(vp8),
vp9(vp9), vp9(vp9),
@ -240,6 +245,8 @@ BalancedDegradationSettings::BalancedDegradationSettings() {
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }), {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }), FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }), FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
FieldTrialStructMember("kbps_res",
[](Config* c) { return &c->kbps_res; }),
FieldTrialStructMember("fps_diff", FieldTrialStructMember("fps_diff",
[](Config* c) { return &c->fps_diff; }), [](Config* c) { return &c->fps_diff; }),
FieldTrialStructMember("vp8_qp_low", FieldTrialStructMember("vp8_qp_low",
@ -317,6 +324,18 @@ absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
return absl::nullopt; return absl::nullopt;
} }
absl::optional<int>
BalancedDegradationSettings::ResolutionNextHigherBitrateKbps(int pixels) const {
for (size_t i = 0; i < configs_.size() - 1; ++i) {
if (pixels <= configs_[i].pixels) {
return (configs_[i + 1].kbps_res > 0)
? absl::optional<int>(configs_[i + 1].kbps_res)
: absl::nullopt;
}
}
return absl::nullopt;
}
bool BalancedDegradationSettings::CanAdaptUp(int pixels, bool BalancedDegradationSettings::CanAdaptUp(int pixels,
uint32_t bitrate_bps) const { uint32_t bitrate_bps) const {
absl::optional<int> next_layer_min_kbps = NextHigherBitrateKbps(pixels); absl::optional<int> next_layer_min_kbps = NextHigherBitrateKbps(pixels);
@ -327,6 +346,18 @@ bool BalancedDegradationSettings::CanAdaptUp(int pixels,
static_cast<uint32_t>(next_layer_min_kbps.value() * 1000); static_cast<uint32_t>(next_layer_min_kbps.value() * 1000);
} }
bool BalancedDegradationSettings::CanAdaptUpResolution(
int pixels,
uint32_t bitrate_bps) const {
absl::optional<int> next_layer_min_kbps =
ResolutionNextHigherBitrateKbps(pixels);
if (!next_layer_min_kbps.has_value() || bitrate_bps == 0) {
return true; // No limit configured or bitrate provided.
}
return bitrate_bps >=
static_cast<uint32_t>(next_layer_min_kbps.value() * 1000);
}
absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const { absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
for (const auto& config : configs_) { for (const auto& config : configs_) {
if (pixels <= config.pixels) { if (pixels <= config.pixels) {

View File

@ -47,6 +47,7 @@ class BalancedDegradationSettings {
Config(int pixels, Config(int pixels,
int fps, int fps,
int kbps, int kbps,
int kbps_res,
int fps_diff, int fps_diff,
CodecTypeSpecific vp8, CodecTypeSpecific vp8,
CodecTypeSpecific vp9, CodecTypeSpecific vp9,
@ -55,14 +56,15 @@ class BalancedDegradationSettings {
bool operator==(const Config& o) const { bool operator==(const Config& o) const {
return pixels == o.pixels && fps == o.fps && kbps == o.kbps && return pixels == o.pixels && fps == o.fps && kbps == o.kbps &&
fps_diff == o.fps_diff && vp8 == o.vp8 && vp9 == o.vp9 && kbps_res == o.kbps_res && fps_diff == o.fps_diff && vp8 == o.vp8 &&
h264 == o.h264 && generic == o.generic; vp9 == o.vp9 && h264 == o.h264 && generic == o.generic;
} }
int pixels = 0; // Video frame size. int pixels = 0; // Video frame size.
// If the frame size is less than or equal to |pixels|: // If the frame size is less than or equal to |pixels|:
int fps = 0; // Min framerate to be used. int fps = 0; // Min framerate to be used.
int kbps = 0; // Min bitrate needed to adapt up to this resolution. int kbps = 0; // Min bitrate needed to adapt up (resolution/fps).
int kbps_res = 0; // Min bitrate needed to adapt up in resolution.
int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - |fps|) int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - |fps|)
// w/o triggering a new subsequent downgrade // w/o triggering a new subsequent downgrade
// check. // check.
@ -81,9 +83,11 @@ class BalancedDegradationSettings {
// Gets the bitrate for the first resolution above |pixels|. // Gets the bitrate for the first resolution above |pixels|.
absl::optional<int> NextHigherBitrateKbps(int pixels) const; absl::optional<int> NextHigherBitrateKbps(int pixels) const;
absl::optional<int> ResolutionNextHigherBitrateKbps(int pixels) const;
// Checks if quality can be increased based on |pixels| and |bitrate_bps|. // Checks if quality can be increased based on |pixels| and |bitrate_bps|.
bool CanAdaptUp(int pixels, uint32_t bitrate_bps) const; bool CanAdaptUp(int pixels, uint32_t bitrate_bps) const;
bool CanAdaptUpResolution(int pixels, uint32_t bitrate_bps) const;
// Gets the min framerate diff from |configs_| based on |pixels|. // Gets the min framerate diff from |configs_| based on |pixels|.
absl::optional<int> MinFpsDiff(int pixels) const; absl::optional<int> MinFpsDiff(int pixels) const;

View File

@ -26,6 +26,7 @@ void VerifyIsDefault(
320 * 240, 320 * 240,
7, 7,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -35,6 +36,7 @@ void VerifyIsDefault(
480 * 270, 480 * 270,
10, 10,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -44,6 +46,7 @@ void VerifyIsDefault(
640 * 480, 640 * 480,
15, 15,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -57,6 +60,9 @@ TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) {
BalancedDegradationSettings settings; BalancedDegradationSettings settings;
VerifyIsDefault(settings.GetConfigs()); VerifyIsDefault(settings.GetConfigs());
EXPECT_FALSE(settings.NextHigherBitrateKbps(1)); EXPECT_FALSE(settings.NextHigherBitrateKbps(1));
EXPECT_FALSE(settings.ResolutionNextHigherBitrateKbps(1));
EXPECT_TRUE(settings.CanAdaptUp(1, /*bitrate_bps*/ 1));
EXPECT_TRUE(settings.CanAdaptUpResolution(1, /*bitrate_bps*/ 1));
EXPECT_FALSE(settings.MinFpsDiff(1)); EXPECT_FALSE(settings.MinFpsDiff(1));
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1));
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1));
@ -76,6 +82,7 @@ TEST(BalancedDegradationSettings, GetsConfig) {
11, 11,
5, 5,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -85,6 +92,7 @@ TEST(BalancedDegradationSettings, GetsConfig) {
22, 22,
15, 15,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -94,6 +102,7 @@ TEST(BalancedDegradationSettings, GetsConfig) {
33, 33,
25, 25,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -137,6 +146,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
1000, 1000,
5, 5,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 7}, {0, 0, 7},
{0, 0, 9}, {0, 0, 9},
@ -146,6 +156,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
2000, 2000,
15, 15,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 8}, {0, 0, 8},
{0, 0, 10}, {0, 0, 10},
@ -155,6 +166,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
3000, 3000,
25, 25,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 9}, {0, 0, 9},
{0, 0, 11}, {0, 0, 11},
@ -285,7 +297,7 @@ TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) {
TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
webrtc::test::ScopedFieldTrials field_trials( webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/" "WebRTC-Video-BalancedDegradationSettings/"
"pixels:11|22|33,fps:5|15|25,kbps:44|88|99/"); "pixels:11|22|33,fps:5|15|25,kbps:44|88|99,kbps_res:55|111|222/");
BalancedDegradationSettings settings; BalancedDegradationSettings settings;
EXPECT_THAT(settings.GetConfigs(), EXPECT_THAT(settings.GetConfigs(),
::testing::ElementsAre( ::testing::ElementsAre(
@ -293,6 +305,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
11, 11,
5, 5,
44, 44,
55,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -302,6 +315,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
22, 22,
15, 15,
88, 88,
111,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -311,6 +325,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
33, 33,
25, 25,
99, 99,
222,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0}, {0, 0, 0},
{0, 0, 0}, {0, 0, 0},
@ -373,6 +388,46 @@ TEST(BalancedDegradationSettings, CanAdaptUpIfBitrateGeNextHigherKbpsLimit) {
EXPECT_TRUE(settings.CanAdaptUp(3001, 1)); // No limit. EXPECT_TRUE(settings.CanAdaptUp(3001, 1)); // No limit.
} }
TEST(BalancedDegradationSettings, GetsResolutionNextHigherBitrate) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:1000|2000|3000,fps:5|15|25,kbps_res:44|88|99/");
BalancedDegradationSettings settings;
EXPECT_EQ(88, settings.ResolutionNextHigherBitrateKbps(1));
EXPECT_EQ(88, settings.ResolutionNextHigherBitrateKbps(1000));
EXPECT_EQ(99, settings.ResolutionNextHigherBitrateKbps(1001));
EXPECT_EQ(99, settings.ResolutionNextHigherBitrateKbps(2000));
EXPECT_FALSE(settings.ResolutionNextHigherBitrateKbps(2001));
}
TEST(BalancedDegradationSettings,
GetsResolutionNextHigherBitrateWithUnsetValue) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:1000|2000|3000,fps:5|15|25,kbps_res:10|0|20/");
BalancedDegradationSettings settings;
EXPECT_FALSE(settings.ResolutionNextHigherBitrateKbps(1));
EXPECT_FALSE(settings.ResolutionNextHigherBitrateKbps(1000));
EXPECT_EQ(20, settings.ResolutionNextHigherBitrateKbps(1001));
EXPECT_EQ(20, settings.ResolutionNextHigherBitrateKbps(2000));
EXPECT_FALSE(settings.ResolutionNextHigherBitrateKbps(2001));
}
TEST(BalancedDegradationSettings,
CanAdaptUpResolutionIfBitrateGeNextHigherKbpsLimit) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:1000|2000|3000|4000,fps:5|15|25|30,kbps_res:0|80|0|90/");
BalancedDegradationSettings settings;
EXPECT_TRUE(settings.CanAdaptUpResolution(1000, 0)); // No bitrate provided.
EXPECT_FALSE(settings.CanAdaptUpResolution(1000, 79000));
EXPECT_TRUE(settings.CanAdaptUpResolution(1000, 80000));
EXPECT_TRUE(settings.CanAdaptUpResolution(1001, 1)); // No limit configured.
EXPECT_FALSE(settings.CanAdaptUpResolution(3000, 89000));
EXPECT_TRUE(settings.CanAdaptUpResolution(3000, 90000));
EXPECT_TRUE(settings.CanAdaptUpResolution(3001, 1)); // No limit.
}
TEST(BalancedDegradationSettings, GetsFpsDiff) { TEST(BalancedDegradationSettings, GetsFpsDiff) {
webrtc::test::ScopedFieldTrials field_trials( webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/" "WebRTC-Video-BalancedDegradationSettings/"
@ -423,6 +478,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) {
1000, 1000,
5, 5,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{89, 90, 0}, {89, 90, 0},
{27, 120, 0}, {27, 120, 0},
@ -432,6 +488,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) {
2000, 2000,
15, 15,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{90, 91, 0}, {90, 91, 0},
{28, 130, 0}, {28, 130, 0},
@ -441,6 +498,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) {
3000, 3000,
25, 25,
0, 0,
0,
BalancedDegradationSettings::kNoFpsDiff, BalancedDegradationSettings::kNoFpsDiff,
{88, 92, 0}, {88, 92, 0},
{29, 140, 0}, {29, 140, 0},

View File

@ -1931,6 +1931,12 @@ void VideoStreamEncoder::AdaptUp(AdaptReason reason) {
} }
break; break;
} }
// Check if resolution should be increased based on bitrate.
if (reason == kQuality &&
!balanced_settings_.CanAdaptUpResolution(
last_frame_info_->pixel_count(), encoder_start_bitrate_bps_)) {
return;
}
// Scale up resolution. // Scale up resolution.
RTC_FALLTHROUGH(); RTC_FALLTHROUGH();
} }

View File

@ -2628,8 +2628,6 @@ TEST_F(VideoStreamEncoderTest, NoAdaptUpIfBwEstimateIsLessThanMinBitrate) {
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(kWidth, kHeight); sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyFpsMaxResolutionMax(source.sink_wants()); 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); EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt down, expect scaled down framerate (640x360@14fps). // Trigger adapt down, expect scaled down framerate (640x360@14fps).
@ -2638,8 +2636,6 @@ TEST_F(VideoStreamEncoderTest, NoAdaptUpIfBwEstimateIsLessThanMinBitrate) {
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms); sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsEqResolutionMax(source.sink_wants(), 14); 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); EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt down, expect scaled down resolution (480x270@14fps). // Trigger adapt down, expect scaled down resolution (480x270@14fps).
@ -2648,31 +2644,212 @@ TEST_F(VideoStreamEncoderTest, NoAdaptUpIfBwEstimateIsLessThanMinBitrate) {
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms); sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsEqResolutionLt(source.sink_wants(), source.last_wants()); 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); EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no upscale (target bitrate < min bitrate). // Trigger adapt down, expect scaled down framerate (480x270@10fps).
video_stream_encoder_->TriggerQualityLow();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
EXPECT_EQ(source.sink_wants().max_framerate_fps, 10);
EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no upscale in fps (target bitrate < min bitrate).
video_stream_encoder_->TriggerQualityHigh(); video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs; timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms); sink_.WaitForEncodedFrame(timestamp_ms);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate); EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
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 // Trigger adapt up, expect upscaled fps (target bitrate == min bitrate).
// bitrate).
video_stream_encoder_->OnBitrateUpdated(DataRate::bps(kMinBitrateBps), video_stream_encoder_->OnBitrateUpdated(DataRate::bps(kMinBitrateBps),
DataRate::bps(kMinBitrateBps), 0, 0); DataRate::bps(kMinBitrateBps), 0, 0);
video_stream_encoder_->TriggerQualityHigh(); video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs; timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms); sink_.WaitForEncodedFrame(timestamp_ms);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate); EXPECT_EQ(source.sink_wants().max_framerate_fps, 14);
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution); EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
video_stream_encoder_->Stop();
}
TEST_F(VideoStreamEncoderTest,
NoAdaptUpInResolutionIfBwEstimateIsLessThanMinBitrate) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:57600|129600|230400,fps:7|10|14,kbps_res:0|0|435/");
// 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 kResolutionMinBitrateBps = 435000;
const int kTooLowMinResolutionBitrateBps = 434000;
video_stream_encoder_->OnBitrateUpdated(
DataRate::bps(kTooLowMinResolutionBitrateBps),
DataRate::bps(kTooLowMinResolutionBitrateBps), 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_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_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_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt down, expect scaled down framerate (480x270@10fps).
video_stream_encoder_->TriggerQualityLow();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes); EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect upscaled fps (no bitrate limit) (480x270@14fps).
video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsGtResolutionEq(source.sink_wants(), source.last_wants());
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no upscale in res (target bitrate < min bitrate).
video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect upscaled res (target bitrate == min bitrate).
video_stream_encoder_->OnBitrateUpdated(
DataRate::bps(kResolutionMinBitrateBps),
DataRate::bps(kResolutionMinBitrateBps), 0, 0);
video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsEqResolutionGt(source.sink_wants(), source.last_wants());
EXPECT_EQ(5, stats_proxy_->GetStats().number_of_quality_adapt_changes);
video_stream_encoder_->Stop();
}
TEST_F(VideoStreamEncoderTest,
NoAdaptUpInFpsAndResolutionIfBwEstimateIsLessThanMinBitrate) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:57600|129600|230400,fps:7|10|14,kbps:0|0|425,kbps_res:0|0|435/");
// 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;
const int kResolutionMinBitrateBps = 435000;
const int kTooLowMinResolutionBitrateBps = 434000;
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_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_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_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt down, expect scaled down framerate (480x270@10fps).
video_stream_encoder_->TriggerQualityLow();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
EXPECT_EQ(3, 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_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect upscaled fps (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);
VerifyFpsGtResolutionEq(source.sink_wants(), source.last_wants());
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no upscale in res (target bitrate < min bitrate).
video_stream_encoder_->OnBitrateUpdated(
DataRate::bps(kTooLowMinResolutionBitrateBps),
DataRate::bps(kTooLowMinResolutionBitrateBps), 0, 0);
video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect upscaled res (target bitrate == min bitrate).
video_stream_encoder_->OnBitrateUpdated(
DataRate::bps(kResolutionMinBitrateBps),
DataRate::bps(kResolutionMinBitrateBps), 0, 0);
video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sink_.WaitForEncodedFrame(timestamp_ms);
VerifyFpsEqResolutionGt(source.sink_wants(), source.last_wants());
EXPECT_EQ(5, stats_proxy_->GetStats().number_of_quality_adapt_changes);
video_stream_encoder_->Stop(); video_stream_encoder_->Stop();
} }
@ -3727,7 +3904,7 @@ TEST_F(VideoStreamEncoderTest,
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate); EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
EXPECT_EQ(13, stats_proxy_->GetStats().number_of_quality_adapt_changes); EXPECT_EQ(13, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no restriction (1280x720fps@30fps). // Trigger adapt up, expect no restriction (1280x720fps@30fps).
video_stream_encoder_->TriggerQualityHigh(); video_stream_encoder_->TriggerQualityHigh();
timestamp_ms += kFrameIntervalMs; timestamp_ms += kFrameIntervalMs;
source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight)); source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));