Apply resolution-bitrate limits collected from field trial (cl/294600) for AV1.
Bug: webrtc:14931
Change-Id: I1e8471a499bc884cb9479609a2b093de90f638d8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/296120
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Michael Horowitz <mhoro@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39582}
diff --git a/call/adaptation/BUILD.gn b/call/adaptation/BUILD.gn
index 58fadc4..b69196f 100644
--- a/call/adaptation/BUILD.gn
+++ b/call/adaptation/BUILD.gn
@@ -44,6 +44,7 @@
"../../api/video:video_stream_encoder",
"../../api/video_codecs:video_codecs_api",
"../../modules/video_coding:video_coding_utility",
+ "../../modules/video_coding/svc:scalability_mode_util",
"../../rtc_base:checks",
"../../rtc_base:logging",
"../../rtc_base:macromagic",
diff --git a/call/adaptation/video_stream_adapter.cc b/call/adaptation/video_stream_adapter.cc
index f30a4d7..5a970fb 100644
--- a/call/adaptation/video_stream_adapter.cc
+++ b/call/adaptation/video_stream_adapter.cc
@@ -22,6 +22,7 @@
#include "api/video_codecs/video_encoder.h"
#include "call/adaptation/video_source_restrictions.h"
#include "call/adaptation/video_stream_input_state.h"
+#include "modules/video_coding/svc/scalability_mode_util.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
@@ -720,7 +721,17 @@
const VideoCodec& codec) {
int num_active = 0;
absl::optional<uint32_t> pixels;
- if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
+ if (codec.codecType == VideoCodecType::kVideoCodecAV1 &&
+ codec.GetScalabilityMode().has_value()) {
+ for (int i = 0;
+ i < ScalabilityModeToNumSpatialLayers(*(codec.GetScalabilityMode()));
+ ++i) {
+ if (codec.spatialLayers[i].active) {
+ ++num_active;
+ pixels = codec.spatialLayers[i].width * codec.spatialLayers[i].height;
+ }
+ }
+ } else if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
for (int i = 0; i < codec.VP9().numberOfSpatialLayers; ++i) {
if (codec.spatialLayers[i].active) {
++num_active;
diff --git a/rtc_base/experiments/encoder_info_settings.cc b/rtc_base/experiments/encoder_info_settings.cc
index 062ba02..2e90a21 100644
--- a/rtc_base/experiments/encoder_info_settings.cc
+++ b/rtc_base/experiments/encoder_info_settings.cc
@@ -38,7 +38,8 @@
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimits(
VideoCodecType codec_type) {
- // Specific limits for VP9. Other codecs use VP8 limits.
+ // Specific limits for VP9. Determining specific limits for AV1 via
+ // field trial experiment is a work in progress. Other codecs use VP8 limits.
if (codec_type == kVideoCodecVP9) {
return {{320 * 180, 0, 30000, 150000},
{480 * 270, 120000, 30000, 300000},
@@ -46,6 +47,10 @@
{960 * 540, 350000, 30000, 1000000},
{1280 * 720, 480000, 30000, 1500000}};
}
+ // Don't override existing AV1 limits with default values.
+ if (codec_type == kVideoCodecAV1) {
+ return {};
+ }
return {{320 * 180, 0, 30000, 300000},
{480 * 270, 200000, 30000, 500000},
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 9acb0a5..3161435 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -403,6 +403,7 @@
"../modules/video_coding:video_codec_interface",
"../modules/video_coding:video_coding_utility",
"../modules/video_coding:webrtc_vp9_helpers",
+ "../modules/video_coding/svc:scalability_mode_util",
"../modules/video_coding/svc:scalability_structures",
"../modules/video_coding/svc:svc_rate_allocator",
"../rtc_base:checks",
diff --git a/video/end_to_end_tests/resolution_bitrate_limits_tests.cc b/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
index 8455832..d550339 100644
--- a/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
+++ b/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
@@ -41,7 +41,18 @@
};
BitrateLimits GetLayerBitrateLimits(int pixels, const VideoCodec& codec) {
- if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
+ if (codec.codecType == VideoCodecType::kVideoCodecAV1) {
+ EXPECT_TRUE(codec.GetScalabilityMode().has_value());
+ for (int i = 0;
+ i < ScalabilityModeToNumSpatialLayers(*(codec.GetScalabilityMode()));
+ ++i) {
+ if (codec.spatialLayers[i].width * codec.spatialLayers[i].height ==
+ pixels) {
+ return {DataRate::KilobitsPerSec(codec.spatialLayers[i].minBitrate),
+ DataRate::KilobitsPerSec(codec.spatialLayers[i].maxBitrate)};
+ }
+ }
+ } else if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
for (size_t i = 0; i < codec.VP9().numberOfSpatialLayers; ++i) {
if (codec.spatialLayers[i].width * codec.spatialLayers[i].height ==
pixels) {
@@ -62,6 +73,10 @@
return BitrateLimits();
}
+bool SupportsSpatialLayers(const std::string& payload_name) {
+ return payload_name == "VP9" || payload_name == "AV1";
+}
+
} // namespace
class ResolutionBitrateLimitsWithScalabilityModeTest : public test::CallTest {};
@@ -122,7 +137,7 @@
const rtc::VideoSinkWants& wants) override {}
size_t GetNumVideoStreams() const override {
- return (payload_name_ == "VP9") ? 1 : configs_.size();
+ return SupportsSpatialLayers(payload_name_) ? 1 : configs_.size();
}
void ModifyVideoConfigs(
@@ -142,7 +157,7 @@
encoder_config->max_bitrate_bps = -1;
if (configs_.size() == 1 && configs_[0].bitrate.max)
encoder_config->max_bitrate_bps = configs_[0].bitrate.max->bps();
- if (payload_name_ == "VP9") {
+ if (SupportsSpatialLayers(payload_name_)) {
// Simulcast layers indicates which spatial layers are active.
encoder_config->simulcast_layers.resize(configs_.size());
}
@@ -156,7 +171,7 @@
if (configs_[i].bitrate.max)
stream.max_bitrate_bps = configs_[i].bitrate.max->bps();
stream.scale_resolution_down_by = scale_factor;
- scale_factor *= (payload_name_ == "VP9") ? 1.0 : 2.0;
+ scale_factor *= SupportsSpatialLayers(payload_name_) ? 1.0 : 2.0;
}
SetEncoderSpecific(encoder_config, codec_type, configs_.size());
}
@@ -458,6 +473,67 @@
RunBaseTest(&test);
}
+TEST_F(ResolutionBitrateLimitsWithScalabilityModeTest,
+ OneStreamLimitsAppliedForAv1OneSpatialLayer) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-GetEncoderInfoOverride/"
+ "frame_size_pixels:921600,"
+ "min_start_bitrate_bps:0,"
+ "min_bitrate_bps:32000,"
+ "max_bitrate_bps:133000/");
+
+ InitEncodeTest test(
+ "AV1", {{.active = true, .scalability_mode = ScalabilityMode::kL1T1}},
+ // Expectations:
+ {{.pixels = 1280 * 720,
+ .eq_bitrate = {DataRate::KilobitsPerSec(32),
+ DataRate::KilobitsPerSec(133)}}});
+ RunBaseTest(&test);
+}
+
+TEST_F(ResolutionBitrateLimitsWithScalabilityModeTest,
+ LimitsAppliedForAv1Simulcast) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-GetEncoderInfoOverride/"
+ "frame_size_pixels:230400|921600,"
+ "min_start_bitrate_bps:0|0,"
+ "min_bitrate_bps:25000|80000,"
+ "max_bitrate_bps:400000|1200000/");
+
+ InitEncodeTest test(
+ "AV1",
+ {{.active = true, .scalability_mode = ScalabilityMode::kL1T1},
+ {.active = false}},
+ // Expectations:
+ {{.pixels = 1280 * 720,
+ .eq_bitrate = {DataRate::KilobitsPerSec(80),
+ DataRate::KilobitsPerSec(1200)}}});
+ RunBaseTest(&test);
+}
+
+TEST_F(ResolutionBitrateLimitsWithScalabilityModeTest,
+ LimitsNotAppliedForAv1MultipleSpatialLayers) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-GetEncoderInfoOverride/"
+ "frame_size_pixels:230400|921600,"
+ "min_start_bitrate_bps:0|0,"
+ "min_bitrate_bps:20000|25000,"
+ "max_bitrate_bps:900000|1333000/");
+
+ InitEncodeTest test(
+ "AV1",
+ {{.active = true, .scalability_mode = ScalabilityMode::kL2T1},
+ {.active = false}},
+ // Expectations:
+ {{.pixels = 640 * 360,
+ .ne_bitrate = {DataRate::KilobitsPerSec(20),
+ DataRate::KilobitsPerSec(900)}},
+ {.pixels = 1280 * 720,
+ .ne_bitrate = {DataRate::KilobitsPerSec(25),
+ DataRate::KilobitsPerSec(1333)}}});
+ RunBaseTest(&test);
+}
+
TEST_P(ResolutionBitrateLimitsTest, LimitsNotAppliedSimulcast) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc
index f34388e..5fa2af3 100644
--- a/video/video_send_stream_impl.cc
+++ b/video/video_send_stream_impl.cc
@@ -52,6 +52,9 @@
constexpr double kVideoHysteresis = 1.2;
constexpr double kScreenshareHysteresis = 1.35;
+constexpr int kMinDefaultAv1BitrateBps =
+ 15000; // This value acts as an absolute minimum AV1 bitrate limit.
+
// When send-side BWE is used a stricter 1.1x pacing factor is used, rather than
// the 2.5x which is used with receive-side BWE. Provides a more careful
// bandwidth rampup with less risk of overshoots causing adverse effects like
@@ -194,6 +197,13 @@
return kFallbackMaxBitrateBps;
}
+int GetDefaultMinVideoBitrateBps(VideoCodecType codec_type) {
+ if (codec_type == VideoCodecType::kVideoCodecAV1) {
+ return kMinDefaultAv1BitrateBps;
+ }
+ return kDefaultMinVideoBitrateBps;
+}
+
} // namespace
PacingConfig::PacingConfig(const FieldTrialsView& field_trials)
@@ -485,7 +495,8 @@
encoder_min_bitrate_bps_ =
experimental_min_bitrate
? experimental_min_bitrate->bps()
- : std::max(streams[0].min_bitrate_bps, kDefaultMinVideoBitrateBps);
+ : std::max(streams[0].min_bitrate_bps,
+ GetDefaultMinVideoBitrateBps(codec_type));
encoder_max_bitrate_bps_ = 0;
double stream_bitrate_priority_sum = 0;
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 34fa362..39f46b9 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -37,6 +37,7 @@
#include "call/adaptation/video_stream_adapter.h"
#include "media/base/media_channel.h"
#include "modules/video_coding/include/video_codec_initializer.h"
+#include "modules/video_coding/svc/scalability_mode_util.h"
#include "modules/video_coding/svc/svc_rate_allocator.h"
#include "modules/video_coding/utility/vp8_constants.h"
#include "rtc_base/arraysize.h"
@@ -82,6 +83,17 @@
constexpr int kDefaultMinScreenSharebps = 1200000;
+int GetNumSpatialLayers(const VideoCodec& codec) {
+ if (codec.codecType == kVideoCodecVP9) {
+ return codec.VP9().numberOfSpatialLayers;
+ } else if (codec.codecType == kVideoCodecAV1 &&
+ codec.GetScalabilityMode().has_value()) {
+ return ScalabilityModeToNumSpatialLayers(*(codec.GetScalabilityMode()));
+ } else {
+ return 0;
+ }
+}
+
bool RequiresEncoderReset(const VideoCodec& prev_send_codec,
const VideoCodec& new_send_codec,
bool was_encode_called_since_last_initialization) {
@@ -383,13 +395,18 @@
return num_active;
}
-void ApplyVp9BitrateLimits(const VideoEncoder::EncoderInfo& encoder_info,
- const VideoEncoderConfig& encoder_config,
- VideoCodec* codec) {
- if (codec->codecType != VideoCodecType::kVideoCodecVP9 ||
- encoder_config.simulcast_layers.size() <= 1 ||
- VideoStreamEncoderResourceManager::IsSimulcastOrMultipleSpatialLayers(
- encoder_config)) {
+void ApplySpatialLayerBitrateLimits(
+ const VideoEncoder::EncoderInfo& encoder_info,
+ const VideoEncoderConfig& encoder_config,
+ VideoCodec* codec) {
+ if (!(GetNumSpatialLayers(*codec) > 0)) {
+ // ApplySpatialLayerBitrateLimits() supports VP9 and AV1 (the latter with
+ // scalability mode set) only.
+ return;
+ }
+ if (VideoStreamEncoderResourceManager::IsSimulcastOrMultipleSpatialLayers(
+ encoder_config) ||
+ encoder_config.simulcast_layers.size() <= 1) {
// Resolution bitrate limits usage is restricted to singlecast.
return;
}
@@ -405,7 +422,6 @@
if (!bitrate_limits.has_value()) {
return;
}
-
// Index for the active stream.
absl::optional<size_t> index;
for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i) {
@@ -415,7 +431,6 @@
if (!index.has_value()) {
return;
}
-
int min_bitrate_bps;
if (encoder_config.simulcast_layers[*index].min_bitrate_bps <= 0) {
min_bitrate_bps = bitrate_limits->min_bitrate_bps;
@@ -439,7 +454,7 @@
return;
}
- for (int i = 0; i < codec->VP9()->numberOfSpatialLayers; ++i) {
+ for (int i = 0; i < GetNumSpatialLayers(*codec); ++i) {
if (codec->spatialLayers[i].active) {
codec->spatialLayers[i].minBitrate = min_bitrate_bps / 1000;
codec->spatialLayers[i].maxBitrate = max_bitrate_bps / 1000;
@@ -1141,15 +1156,17 @@
RTC_LOG(LS_ERROR) << "Failed to create encoder configuration.";
}
- if (encoder_config_.codec_type == kVideoCodecVP9) {
+ if (encoder_config_.codec_type == kVideoCodecVP9 ||
+ encoder_config_.codec_type == kVideoCodecAV1) {
// Spatial layers configuration might impose some parity restrictions,
// thus some cropping might be needed.
crop_width_ = last_frame_info_->width - codec.width;
crop_height_ = last_frame_info_->height - codec.height;
- ApplyVp9BitrateLimits(GetEncoderInfoWithBitrateLimitUpdate(
- encoder_->GetEncoderInfo(), encoder_config_,
- default_limits_allowed_),
- encoder_config_, &codec);
+ ApplySpatialLayerBitrateLimits(
+ GetEncoderInfoWithBitrateLimitUpdate(encoder_->GetEncoderInfo(),
+ encoder_config_,
+ default_limits_allowed_),
+ encoder_config_, &codec);
}
char log_stream_buf[4 * 1024];
@@ -1168,10 +1185,10 @@
<< " active: "
<< (codec.simulcastStream[i].active ? "true" : "false") << "\n";
}
- if (encoder_config_.codec_type == kVideoCodecVP9) {
- size_t num_spatial_layers = codec.VP9()->numberOfSpatialLayers;
+ if (encoder_config_.codec_type == kVideoCodecVP9 ||
+ encoder_config_.codec_type == kVideoCodecAV1) {
log_stream << "Spatial layers:\n";
- for (size_t i = 0; i < num_spatial_layers; ++i) {
+ for (int i = 0; i < GetNumSpatialLayers(codec); ++i) {
log_stream << i << ": " << codec.spatialLayers[i].width << "x"
<< codec.spatialLayers[i].height
<< " min_kbps: " << codec.spatialLayers[i].minBitrate
@@ -1331,6 +1348,10 @@
num_layers = codec.VP8()->numberOfTemporalLayers;
} else if (codec.codecType == kVideoCodecVP9) {
num_layers = codec.VP9()->numberOfTemporalLayers;
+ } else if (codec.codecType == kVideoCodecAV1 &&
+ codec.GetScalabilityMode().has_value()) {
+ num_layers =
+ ScalabilityModeToNumTemporalLayers(*(codec.GetScalabilityMode()));
} else if (codec.codecType == kVideoCodecH264) {
num_layers = codec.H264()->numberOfTemporalLayers;
} else if (codec.codecType == kVideoCodecGeneric &&
@@ -1375,8 +1396,9 @@
bool is_svc = false;
// Set min_bitrate_bps, max_bitrate_bps, and max padding bit rate for VP9
- // and leave only one stream containing all necessary information.
- if (encoder_config_.codec_type == kVideoCodecVP9 &&
+ // and AV1 and leave only one stream containing all necessary information.
+ if ((encoder_config_.codec_type == kVideoCodecVP9 ||
+ encoder_config_.codec_type == kVideoCodecAV1) &&
encoder_config_.number_of_streams == 1) {
// Lower max bitrate to the level codec actually can produce.
streams[0].max_bitrate_bps =
@@ -1388,7 +1410,7 @@
SvcRateAllocator::GetPaddingBitrate(codec).bps<int>();
streams[0].width = streams.back().width;
streams[0].height = streams.back().height;
- is_svc = codec.VP9()->numberOfSpatialLayers > 1;
+ is_svc = GetNumSpatialLayers(codec) > 1;
streams.resize(1);
}