BalancedDegradationSettings: add option to configure a min framerate diff.

If a framerate reduction (input fps - restricted fps) is less than the
configured diff, shorten interval to next qp check.

Bug: none
Change-Id: Ia0b9e0638e5ba75cdc20a1bb45bfcb7d858c5f89
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149040
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Magnus Flodman <mflodman@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28880}
This commit is contained in:
Åsa Persson 2019-08-16 17:24:59 +02:00 committed by Commit Bot
parent df625f46c0
commit f5e5d250bc
10 changed files with 266 additions and 60 deletions

View File

@ -96,7 +96,8 @@ QualityScaler::QualityScaler(rtc::TaskQueue* task_queue,
.value_or(kSamplePeriodScaleFactor)),
scale_factor_(
QualityScalerSettings::ParseFromFieldTrials().ScaleFactor()),
last_adapted_(false) {
adapt_called_(false),
adapt_failed_(false) {
RTC_DCHECK_RUN_ON(&task_checker_);
if (experiment_enabled_) {
config_ = QualityScalingExperiment::GetConfig();
@ -127,8 +128,12 @@ int64_t QualityScaler::GetSamplingPeriodMs() const {
// Use half the interval while waiting for enough frames.
return sampling_period_ms_ / 2;
}
if (scale_factor_ && !last_adapted_) {
// Last check did not result in a AdaptDown/Up, possibly reduce interval.
if (adapt_failed_) {
// Check shortly again.
return sampling_period_ms_ / 8;
}
if (scale_factor_ && !adapt_called_) {
// Last CheckQp did not call AdaptDown/Up, possibly reduce interval.
return sampling_period_ms_ * scale_factor_.value();
}
return sampling_period_ms_ * initial_scale_factor_;
@ -165,7 +170,8 @@ void QualityScaler::CheckQp() {
RTC_DCHECK_RUN_ON(&task_checker_);
// Should be set through InitEncode -> Should be set by now.
RTC_DCHECK_GE(thresholds_.low, 0);
last_adapted_ = false;
adapt_failed_ = false;
adapt_called_ = false;
// If we have not observed at least this many frames we can't make a good
// scaling decision.
@ -215,18 +221,24 @@ void QualityScaler::ReportQpLow() {
RTC_DCHECK_RUN_ON(&task_checker_);
ClearSamples();
observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
last_adapted_ = true;
adapt_called_ = true;
}
void QualityScaler::ReportQpHigh() {
RTC_DCHECK_RUN_ON(&task_checker_);
ClearSamples();
observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
if (observer_->AdaptDown(
AdaptationObserverInterface::AdaptReason::kQuality)) {
ClearSamples();
} else {
adapt_failed_ = true;
}
// If we've scaled down, wait longer before scaling up again.
if (fast_rampup_) {
fast_rampup_ = false;
}
last_adapted_ = true;
adapt_called_ = true;
}
void QualityScaler::ClearSamples() {

View File

@ -37,7 +37,9 @@ class AdaptationObserverInterface {
// Called to signal that we can handle larger or more frequent frames.
virtual void AdaptUp(AdaptReason reason) = 0;
// Called to signal that the source should reduce the resolution or framerate.
virtual void AdaptDown(AdaptReason reason) = 0;
// Returns false if a downgrade was requested but the request did not result
// in a new limiting resolution or fps.
virtual bool AdaptDown(AdaptReason reason) = 0;
protected:
virtual ~AdaptationObserverInterface() {}
@ -101,7 +103,8 @@ class QualityScaler {
const size_t min_frames_needed_;
const double initial_scale_factor_;
const absl::optional<double> scale_factor_;
bool last_adapted_ RTC_GUARDED_BY(&task_checker_);
bool adapt_called_ RTC_GUARDED_BY(&task_checker_);
bool adapt_failed_ RTC_GUARDED_BY(&task_checker_);
};
} // namespace webrtc

View File

@ -36,9 +36,10 @@ class MockAdaptationObserver : public AdaptationObserverInterface {
adapt_up_events_++;
event.Set();
}
void AdaptDown(AdaptReason r) override {
bool AdaptDown(AdaptReason r) override {
adapt_down_events_++;
event.Set();
return true;
}
rtc::Event event;

View File

@ -24,9 +24,30 @@ 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, 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}}};
return {{320 * 240,
7,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}},
{480 * 270,
10,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}},
{640 * 480,
15,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}}};
}
bool IsValidConfig(
@ -200,6 +221,7 @@ BalancedDegradationSettings::Config::Config() = default;
BalancedDegradationSettings::Config::Config(int pixels,
int fps,
int kbps,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
@ -207,6 +229,7 @@ BalancedDegradationSettings::Config::Config(int pixels,
: pixels(pixels),
fps(fps),
kbps(kbps),
fps_diff(fps_diff),
vp8(vp8),
vp9(vp9),
h264(h264),
@ -217,6 +240,8 @@ BalancedDegradationSettings::BalancedDegradationSettings() {
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
FieldTrialStructMember("fps_diff",
[](Config* c) { return &c->fps_diff; }),
FieldTrialStructMember("vp8_qp_low",
[](Config* c) { return &c->vp8.qp_low; }),
FieldTrialStructMember("vp8_qp_high",
@ -292,6 +317,17 @@ absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
return absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels) {
return (config.fps_diff > kNoFpsDiff)
? absl::optional<int>(config.fps_diff)
: absl::nullopt;
}
}
return absl::nullopt;
}
absl::optional<VideoEncoder::QpThresholds>
BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
int pixels) const {

View File

@ -20,6 +20,8 @@ namespace webrtc {
class BalancedDegradationSettings {
public:
static constexpr int kNoFpsDiff = -100;
BalancedDegradationSettings();
~BalancedDegradationSettings();
@ -45,6 +47,7 @@ class BalancedDegradationSettings {
Config(int pixels,
int fps,
int kbps,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
@ -52,14 +55,17 @@ class BalancedDegradationSettings {
bool operator==(const Config& o) const {
return pixels == o.pixels && fps == o.fps && kbps == o.kbps &&
vp8 == o.vp8 && vp9 == o.vp9 && h264 == o.h264 &&
generic == o.generic;
fps_diff == o.fps_diff && vp8 == o.vp8 && vp9 == o.vp9 &&
h264 == o.h264 && generic == o.generic;
}
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.
int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - |fps|)
// w/o triggering a new subsequent downgrade
// check.
CodecTypeSpecific vp8;
CodecTypeSpecific vp9;
CodecTypeSpecific h264;
@ -76,6 +82,9 @@ class BalancedDegradationSettings {
// Gets the bitrate for the first resolution above |pixels|.
absl::optional<int> NextHigherBitrateKbps(int pixels) const;
// Gets the min framerate diff from |configs_| based on |pixels|.
absl::optional<int> MinFpsDiff(int pixels) const;
// Gets QpThresholds for the codec |type| based on |pixels|.
absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
VideoCodecType type,

View File

@ -21,15 +21,34 @@ namespace {
void VerifyIsDefault(
const std::vector<BalancedDegradationSettings::Config>& config) {
EXPECT_THAT(
config,
::testing::ElementsAre(
BalancedDegradationSettings::Config{
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, 0}},
BalancedDegradationSettings::Config{
640 * 480, 15, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
EXPECT_THAT(config, ::testing::ElementsAre(
BalancedDegradationSettings::Config{
320 * 240,
7,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}},
BalancedDegradationSettings::Config{
480 * 270,
10,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}},
BalancedDegradationSettings::Config{
640 * 480,
15,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}}));
}
} // namespace
@ -38,6 +57,7 @@ TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) {
BalancedDegradationSettings settings;
VerifyIsDefault(settings.GetConfigs());
EXPECT_FALSE(settings.NextHigherBitrateKbps(1));
EXPECT_FALSE(settings.MinFpsDiff(1));
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1));
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1));
EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecH264, 1));
@ -53,11 +73,32 @@ 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, 0}},
11,
5,
0,
BalancedDegradationSettings::kNoFpsDiff,
{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, 0}},
22,
15,
0,
BalancedDegradationSettings::kNoFpsDiff,
{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, 0}}));
33,
25,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}}));
}
TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroFpsValue) {
@ -90,15 +131,35 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
"pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|8|9,vp9_fps:9|10|11,"
"h264_fps:11|12|13,generic_fps:13|14|15/");
BalancedDegradationSettings settings;
EXPECT_THAT(
settings.GetConfigs(),
::testing::ElementsAre(
BalancedDegradationSettings::Config{
1000, 5, 0, {0, 0, 7}, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}},
BalancedDegradationSettings::Config{
2000, 15, 0, {0, 0, 8}, {0, 0, 10}, {0, 0, 12}, {0, 0, 14}},
BalancedDegradationSettings::Config{
3000, 25, 0, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}, {0, 0, 15}}));
EXPECT_THAT(settings.GetConfigs(),
::testing::ElementsAre(
BalancedDegradationSettings::Config{
1000,
5,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 7},
{0, 0, 9},
{0, 0, 11},
{0, 0, 13}},
BalancedDegradationSettings::Config{
2000,
15,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 8},
{0, 0, 10},
{0, 0, 12},
{0, 0, 14}},
BalancedDegradationSettings::Config{
3000,
25,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 9},
{0, 0, 11},
{0, 0, 13},
{0, 0, 15}}));
}
TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroVp8FpsValue) {
@ -229,11 +290,32 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) {
EXPECT_THAT(settings.GetConfigs(),
::testing::ElementsAre(
BalancedDegradationSettings::Config{
11, 5, 44, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
11,
5,
44,
BalancedDegradationSettings::kNoFpsDiff,
{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}},
22,
15,
88,
BalancedDegradationSettings::kNoFpsDiff,
{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}}));
33,
25,
99,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}}));
}
TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreases) {
@ -277,6 +359,31 @@ TEST(BalancedDegradationSettings, GetsNextHigherBitrateWithUnsetValue) {
EXPECT_FALSE(settings.NextHigherBitrateKbps(2001));
}
TEST(BalancedDegradationSettings, GetsFpsDiff) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:1000|2000|3000,fps:5|15|25,fps_diff:0|-2|3/");
BalancedDegradationSettings settings;
EXPECT_EQ(0, settings.MinFpsDiff(1));
EXPECT_EQ(0, settings.MinFpsDiff(1000));
EXPECT_EQ(-2, settings.MinFpsDiff(1001));
EXPECT_EQ(-2, settings.MinFpsDiff(2000));
EXPECT_EQ(3, settings.MinFpsDiff(2001));
EXPECT_EQ(3, settings.MinFpsDiff(3000));
EXPECT_FALSE(settings.MinFpsDiff(3001));
}
TEST(BalancedDegradationSettings, GetsNoFpsDiffIfValueBelowMinSetting) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
"pixels:1000|2000|3000,fps:5|15|25,fps_diff:-100|-99|-101/");
// Min valid fps_diff setting: -99.
BalancedDegradationSettings settings;
EXPECT_FALSE(settings.MinFpsDiff(1000));
EXPECT_EQ(-99, settings.MinFpsDiff(2000));
EXPECT_FALSE(settings.MinFpsDiff(3000));
}
TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-Video-BalancedDegradationSettings/"
@ -292,19 +399,39 @@ 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:82|83|84,"
"vp8_qp_high:90|91|92,vp9_qp_low:27|28|29,vp9_qp_high:120|130|140,"
"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;
EXPECT_THAT(
settings.GetConfigs(),
::testing::ElementsAre(
BalancedDegradationSettings::Config{
1000, 5, 0, {89, 90, 0}, {27, 82, 0}, {12, 20, 0}, {7, 22, 0}},
BalancedDegradationSettings::Config{
2000, 15, 0, {90, 91, 0}, {28, 83, 0}, {13, 30, 0}, {6, 23, 0}},
BalancedDegradationSettings::Config{
3000, 25, 0, {88, 92, 0}, {29, 84, 0}, {14, 40, 0}, {5, 24, 0}}));
EXPECT_THAT(settings.GetConfigs(),
::testing::ElementsAre(
BalancedDegradationSettings::Config{
1000,
5,
0,
BalancedDegradationSettings::kNoFpsDiff,
{89, 90, 0},
{27, 120, 0},
{12, 20, 0},
{7, 22, 0}},
BalancedDegradationSettings::Config{
2000,
15,
0,
BalancedDegradationSettings::kNoFpsDiff,
{90, 91, 0},
{28, 130, 0},
{13, 30, 0},
{6, 23, 0}},
BalancedDegradationSettings::Config{
3000,
25,
0,
BalancedDegradationSettings::kNoFpsDiff,
{88, 92, 0},
{29, 140, 0},
{14, 40, 0},
{5, 24, 0}}));
}
TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasLowThreshold) {

View File

@ -42,7 +42,7 @@ class MockCpuOveruseObserver : public AdaptationObserverInterface {
virtual ~MockCpuOveruseObserver() {}
MOCK_METHOD1(AdaptUp, void(AdaptReason));
MOCK_METHOD1(AdaptDown, void(AdaptReason));
MOCK_METHOD1(AdaptDown, bool(AdaptReason));
};
class CpuOveruseObserverImpl : public AdaptationObserverInterface {
@ -50,7 +50,10 @@ class CpuOveruseObserverImpl : public AdaptationObserverInterface {
CpuOveruseObserverImpl() : overuse_(0), normaluse_(0) {}
virtual ~CpuOveruseObserverImpl() {}
void AdaptDown(AdaptReason) { ++overuse_; }
bool AdaptDown(AdaptReason) {
++overuse_;
return true;
}
void AdaptUp(AdaptReason) { ++normaluse_; }
int overuse_;

View File

@ -1781,7 +1781,7 @@ bool VideoStreamEncoder::DropDueToSize(uint32_t pixel_count) const {
return false;
}
void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
bool VideoStreamEncoder::AdaptDown(AdaptReason reason) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
AdaptationRequest adaptation_request = {
last_frame_info_->pixel_count(),
@ -1792,6 +1792,8 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
last_adaptation_request_ &&
last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
bool did_adapt = true;
switch (degradation_preference_) {
case DegradationPreference::BALANCED:
break;
@ -1801,7 +1803,7 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
last_adaptation_request_->input_pixel_count_) {
// Don't request lower resolution if the current resolution is not
// lower than the last time we asked for the resolution to be lowered.
return;
return true;
}
break;
case DegradationPreference::MAINTAIN_RESOLUTION:
@ -1814,11 +1816,11 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
// we have to estimate, and can fluctuate naturally over time, don't
// make the same kind of limitations as for resolution, but trust the
// overuse detector to not trigger too often.
return;
return true;
}
break;
case DegradationPreference::DISABLED:
return;
return true;
}
switch (degradation_preference_) {
@ -1828,6 +1830,15 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
last_frame_info_->pixel_count());
if (source_proxy_->RestrictFramerate(fps)) {
GetAdaptCounter().IncrementFramerate(reason);
// Check if requested fps is higher (or close to) input fps.
absl::optional<int> min_diff =
balanced_settings_.MinFpsDiff(last_frame_info_->pixel_count());
if (min_diff && adaptation_request.framerate_fps_ > 0) {
int fps_diff = adaptation_request.framerate_fps_ - fps;
if (fps_diff < min_diff.value()) {
did_adapt = false;
}
}
break;
}
// Scale down resolution.
@ -1842,7 +1853,7 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
&min_pixels_reached)) {
if (min_pixels_reached)
encoder_stats_observer_->OnMinPixelLimitReached();
return;
return true;
}
GetAdaptCounter().IncrementResolution(reason);
break;
@ -1852,7 +1863,7 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
const int requested_framerate = source_proxy_->RequestFramerateLowerThan(
adaptation_request.framerate_fps_);
if (requested_framerate == -1)
return;
return true;
RTC_DCHECK_NE(max_framerate_, -1);
overuse_detector_->OnTargetFramerateUpdated(
std::min(max_framerate_, requested_framerate));
@ -1868,6 +1879,7 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
UpdateAdaptationStats(reason);
RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
return did_adapt;
}
void VideoStreamEncoder::AdaptUp(AdaptReason reason) {

View File

@ -103,7 +103,7 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
// AdaptationObserverInterface implementation.
// These methods are protected for easier testing.
void AdaptUp(AdaptReason reason) override;
void AdaptDown(AdaptReason reason) override;
bool AdaptDown(AdaptReason reason) override;
private:
class VideoSourceProxy;

View File

@ -148,7 +148,10 @@ class VideoStreamEncoderUnderTest : public VideoStreamEncoder {
void PostTaskAndWait(bool down, AdaptReason reason) {
rtc::Event event;
encoder_queue()->PostTask([this, &event, reason, down] {
down ? AdaptDown(reason) : AdaptUp(reason);
if (down)
AdaptDown(reason);
else
AdaptUp(reason);
event.Set();
});
ASSERT_TRUE(event.Wait(5000));