/* | |
* Copyright 2021 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 "modules/video_coding/utility/bandwidth_quality_scaler.h" | |
#include <memory> | |
#include <string> | |
#include "rtc_base/checks.h" | |
#include "rtc_base/event.h" | |
#include "rtc_base/experiments/encoder_info_settings.h" | |
#include "rtc_base/task_queue_for_test.h" | |
#include "rtc_base/time_utils.h" | |
#include "test/field_trial.h" | |
#include "test/gtest.h" | |
namespace webrtc { | |
namespace { | |
constexpr int kFramerateFps = 30; | |
constexpr int kDefaultBitrateStateUpdateIntervalSeconds = 5; | |
constexpr int kDefaultEncodeDeltaTimeMs = 33; // 1/30(s) => 33(ms) | |
} // namespace | |
class FakeBandwidthQualityScalerHandler | |
: public BandwidthQualityScalerUsageHandlerInterface { | |
public: | |
~FakeBandwidthQualityScalerHandler() override = default; | |
void OnReportUsageBandwidthHigh() override { | |
adapt_down_event_count_++; | |
event_.Set(); | |
} | |
void OnReportUsageBandwidthLow() override { | |
adapt_up_event_count_++; | |
event_.Set(); | |
} | |
rtc::Event event_; | |
int adapt_up_event_count_ = 0; | |
int adapt_down_event_count_ = 0; | |
}; | |
class BandwidthQualityScalerUnderTest : public BandwidthQualityScaler { | |
public: | |
explicit BandwidthQualityScalerUnderTest( | |
BandwidthQualityScalerUsageHandlerInterface* handler) | |
: BandwidthQualityScaler(handler) {} | |
int GetBitrateStateUpdateIntervalMs() { | |
return this->kBitrateStateUpdateInterval.ms() + 200; | |
} | |
}; | |
class BandwidthQualityScalerTest | |
: public ::testing::Test, | |
public ::testing::WithParamInterface<std::string> { | |
protected: | |
enum ScaleDirection { | |
kKeepScaleNormalBandwidth, | |
kKeepScaleAboveMaxBandwidth, | |
kKeepScaleUnderMinBandwidth, | |
}; | |
enum FrameType { | |
kKeyFrame, | |
kNormalFrame, | |
kNormalFrame_Overuse, | |
kNormalFrame_Underuse, | |
}; | |
struct FrameConfig { | |
FrameConfig(int frame_num, | |
FrameType frame_type, | |
int actual_width, | |
int actual_height) | |
: frame_num(frame_num), | |
frame_type(frame_type), | |
actual_width(actual_width), | |
actual_height(actual_height) {} | |
int frame_num; | |
FrameType frame_type; | |
int actual_width; | |
int actual_height; | |
}; | |
BandwidthQualityScalerTest() | |
: scoped_field_trial_(GetParam()), | |
task_queue_("BandwidthQualityScalerTestQueue"), | |
handler_(std::make_unique<FakeBandwidthQualityScalerHandler>()) { | |
task_queue_.SendTask( | |
[this] { | |
bandwidth_quality_scaler_ = | |
std::unique_ptr<BandwidthQualityScalerUnderTest>( | |
new BandwidthQualityScalerUnderTest(handler_.get())); | |
bandwidth_quality_scaler_->SetResolutionBitrateLimits( | |
EncoderInfoSettings:: | |
GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted()); | |
// Only for testing. Set first_timestamp_ in RateStatistics to 0. | |
bandwidth_quality_scaler_->ReportEncodeInfo(0, 0, 0, 0); | |
}, | |
RTC_FROM_HERE); | |
} | |
~BandwidthQualityScalerTest() { | |
task_queue_.SendTask([this] { bandwidth_quality_scaler_ = nullptr; }, | |
RTC_FROM_HERE); | |
} | |
int GetFrameSizeBytes( | |
const FrameConfig& config, | |
const VideoEncoder::ResolutionBitrateLimits& bitrate_limits) { | |
int scale = 8 * kFramerateFps; | |
switch (config.frame_type) { | |
case FrameType::kKeyFrame: { | |
// 4 is experimental value. Based on the test, the number of bytes of | |
// the key frame is about four times of the normal frame | |
return bitrate_limits.max_bitrate_bps * 4 / scale; | |
} | |
case FrameType::kNormalFrame_Overuse: { | |
return bitrate_limits.max_bitrate_bps * 3 / 2 / scale; | |
} | |
case FrameType::kNormalFrame_Underuse: { | |
return bitrate_limits.min_start_bitrate_bps * 3 / 4 / scale; | |
} | |
case FrameType::kNormalFrame: { | |
return (bitrate_limits.max_bitrate_bps + | |
bitrate_limits.min_start_bitrate_bps) / | |
2 / scale; | |
} | |
} | |
return -1; | |
} | |
absl::optional<VideoEncoder::ResolutionBitrateLimits> | |
GetDefaultSuitableBitrateLimit(int frame_size_pixels) { | |
return EncoderInfoSettings:: | |
GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( | |
frame_size_pixels, | |
EncoderInfoSettings:: | |
GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted()); | |
} | |
void TriggerBandwidthQualityScalerTest( | |
const std::vector<FrameConfig>& frame_configs) { | |
task_queue_.SendTask( | |
[frame_configs, this] { | |
RTC_CHECK(!frame_configs.empty()); | |
int total_frame_nums = 0; | |
for (const FrameConfig& frame_config : frame_configs) { | |
total_frame_nums += frame_config.frame_num; | |
} | |
EXPECT_EQ(kFramerateFps * kDefaultBitrateStateUpdateIntervalSeconds, | |
total_frame_nums); | |
uint32_t time_send_to_scaler_ms_ = rtc::TimeMillis(); | |
for (size_t i = 0; i < frame_configs.size(); ++i) { | |
const FrameConfig& config = frame_configs[i]; | |
absl::optional<VideoEncoder::ResolutionBitrateLimits> | |
suitable_bitrate = GetDefaultSuitableBitrateLimit( | |
config.actual_width * config.actual_height); | |
EXPECT_TRUE(suitable_bitrate); | |
for (int j = 0; j <= config.frame_num; ++j) { | |
time_send_to_scaler_ms_ += kDefaultEncodeDeltaTimeMs; | |
int frame_size_bytes = | |
GetFrameSizeBytes(config, suitable_bitrate.value()); | |
RTC_CHECK(frame_size_bytes > 0); | |
bandwidth_quality_scaler_->ReportEncodeInfo( | |
frame_size_bytes, time_send_to_scaler_ms_, | |
config.actual_width, config.actual_height); | |
} | |
} | |
}, | |
RTC_FROM_HERE); | |
} | |
test::ScopedFieldTrials scoped_field_trial_; | |
TaskQueueForTest task_queue_; | |
std::unique_ptr<BandwidthQualityScalerUnderTest> bandwidth_quality_scaler_; | |
std::unique_ptr<FakeBandwidthQualityScalerHandler> handler_; | |
}; | |
INSTANTIATE_TEST_SUITE_P( | |
FieldTrials, | |
BandwidthQualityScalerTest, | |
::testing::Values("WebRTC-Video-BandwidthQualityScalerSettings/" | |
"bitrate_state_update_interval_s_:1/", | |
"WebRTC-Video-BandwidthQualityScalerSettings/" | |
"bitrate_state_update_interval_s_:2/")); | |
TEST_P(BandwidthQualityScalerTest, AllNormalFrame_640x360) { | |
const std::vector<FrameConfig> frame_configs{ | |
FrameConfig(150, FrameType::kNormalFrame, 640, 360)}; | |
TriggerBandwidthQualityScalerTest(frame_configs); | |
// When resolution is 640*360, experimental working bitrate range is | |
// [500000,800000] bps. Encoded bitrate is 654253, so it falls in the range | |
// without any operation(up/down). | |
EXPECT_FALSE(handler_->event_.Wait( | |
bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); | |
EXPECT_EQ(0, handler_->adapt_down_event_count_); | |
EXPECT_EQ(0, handler_->adapt_up_event_count_); | |
} | |
TEST_P(BandwidthQualityScalerTest, AllNoramlFrame_AboveMaxBandwidth_640x360) { | |
const std::vector<FrameConfig> frame_configs{ | |
FrameConfig(150, FrameType::kNormalFrame_Overuse, 640, 360)}; | |
TriggerBandwidthQualityScalerTest(frame_configs); | |
// When resolution is 640*360, experimental working bitrate range is | |
// [500000,800000] bps. Encoded bitrate is 1208000 > 800000 * 0.95, so it | |
// triggers adapt_up_event_count_. | |
EXPECT_TRUE(handler_->event_.Wait( | |
bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); | |
EXPECT_EQ(0, handler_->adapt_down_event_count_); | |
EXPECT_EQ(1, handler_->adapt_up_event_count_); | |
} | |
TEST_P(BandwidthQualityScalerTest, AllNormalFrame_Underuse_640x360) { | |
const std::vector<FrameConfig> frame_configs{ | |
FrameConfig(150, FrameType::kNormalFrame_Underuse, 640, 360)}; | |
TriggerBandwidthQualityScalerTest(frame_configs); | |
// When resolution is 640*360, experimental working bitrate range is | |
// [500000,800000] bps. Encoded bitrate is 377379 < 500000 * 0.8, so it | |
// triggers adapt_down_event_count_. | |
EXPECT_TRUE(handler_->event_.Wait( | |
bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); | |
EXPECT_EQ(1, handler_->adapt_down_event_count_); | |
EXPECT_EQ(0, handler_->adapt_up_event_count_); | |
} | |
TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest1_640x360) { | |
const std::vector<FrameConfig> frame_configs{ | |
FrameConfig(5, FrameType::kNormalFrame_Underuse, 640, 360), | |
FrameConfig(110, FrameType::kNormalFrame, 640, 360), | |
FrameConfig(20, FrameType::kNormalFrame_Overuse, 640, 360), | |
FrameConfig(15, FrameType::kKeyFrame, 640, 360), | |
}; | |
TriggerBandwidthQualityScalerTest(frame_configs); | |
// When resolution is 640*360, experimental working bitrate range is | |
// [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it | |
// triggers adapt_up_event_count_. | |
EXPECT_TRUE(handler_->event_.Wait( | |
bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); | |
EXPECT_EQ(0, handler_->adapt_down_event_count_); | |
EXPECT_EQ(1, handler_->adapt_up_event_count_); | |
} | |
TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest2_640x360) { | |
const std::vector<FrameConfig> frame_configs{ | |
FrameConfig(10, FrameType::kNormalFrame_Underuse, 640, 360), | |
FrameConfig(50, FrameType::kNormalFrame, 640, 360), | |
FrameConfig(5, FrameType::kKeyFrame, 640, 360), | |
FrameConfig(85, FrameType::kNormalFrame_Overuse, 640, 360), | |
}; | |
TriggerBandwidthQualityScalerTest(frame_configs); | |
// When resolution is 640*360, experimental working bitrate range is | |
// [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it | |
// triggers adapt_up_event_count_. | |
EXPECT_TRUE(handler_->event_.Wait( | |
bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())); | |
EXPECT_EQ(0, handler_->adapt_down_event_count_); | |
EXPECT_EQ(1, handler_->adapt_up_event_count_); | |
} | |
} // namespace webrtc |