diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 4c19f9c666..29db5d0bb4 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -259,7 +259,8 @@ std::vector GetPayloadTypesAndDefaultCodecs( bool IsTemporalLayersSupported(const std::string& codec_name) { return absl::EqualsIgnoreCase(codec_name, kVp8CodecName) || - absl::EqualsIgnoreCase(codec_name, kVp9CodecName); + absl::EqualsIgnoreCase(codec_name, kVp9CodecName) || + absl::EqualsIgnoreCase(codec_name, kAv1CodecName); } static std::string CodecVectorToString(const std::vector& codecs) { diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn index 520ed0aee0..73b22d46dc 100644 --- a/modules/video_coding/codecs/av1/BUILD.gn +++ b/modules/video_coding/codecs/av1/BUILD.gn @@ -18,6 +18,8 @@ rtc_library("av1_svc_config") { "../../../../api/video_codecs:video_codecs_api", "../../../../rtc_base:checks", "../../../../rtc_base:logging", + "../../../../rtc_base:stringutils", + "../../svc:scalability_mode_util", "../../svc:scalability_structures", "../../svc:scalable_video_controller", ] diff --git a/modules/video_coding/codecs/av1/av1_svc_config.cc b/modules/video_coding/codecs/av1/av1_svc_config.cc index 465a03ab6f..dd66de03aa 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config.cc @@ -15,11 +15,26 @@ #include #include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "modules/video_coding/svc/scalable_video_controller.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" namespace webrtc { +namespace { +absl::optional BuildScalabilityMode(int num_temporal_layers, + int num_spatial_layers) { + char name[20]; + rtc::SimpleStringBuilder ss(name); + ss << "L" << num_spatial_layers << "T" << num_temporal_layers; + if (num_spatial_layers > 1) { + ss << "_KEY"; + } + + return ScalabilityModeFromString(name); +} +} // namespace bool LibaomAv1EncoderSupportsScalabilityMode(ScalabilityMode scalability_mode) { // For libaom AV1, the scalability mode is supported if we can create the @@ -27,14 +42,20 @@ bool LibaomAv1EncoderSupportsScalabilityMode(ScalabilityMode scalability_mode) { return ScalabilityStructureConfig(scalability_mode) != absl::nullopt; } -bool SetAv1SvcConfig(VideoCodec& video_codec) { +bool SetAv1SvcConfig(VideoCodec& video_codec, + int num_temporal_layers, + int num_spatial_layers) { RTC_DCHECK_EQ(video_codec.codecType, kVideoCodecAV1); absl::optional scalability_mode = video_codec.GetScalabilityMode(); if (!scalability_mode.has_value()) { - RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'L1T1'."; - scalability_mode = ScalabilityMode::kL1T1; + scalability_mode = + BuildScalabilityMode(num_temporal_layers, num_spatial_layers); + if (!scalability_mode) { + RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'L1T1'."; + scalability_mode = ScalabilityMode::kL1T1; + } } std::unique_ptr structure = @@ -45,6 +66,8 @@ bool SetAv1SvcConfig(VideoCodec& video_codec) { return false; } + video_codec.SetScalabilityMode(*scalability_mode); + ScalableVideoController::StreamLayersConfig info = structure->StreamConfig(); for (int sl_idx = 0; sl_idx < info.num_spatial_layers; ++sl_idx) { SpatialLayer& spatial_layer = video_codec.spatialLayers[sl_idx]; diff --git a/modules/video_coding/codecs/av1/av1_svc_config.h b/modules/video_coding/codecs/av1/av1_svc_config.h index c1d32736ed..56cc2b3e9e 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.h +++ b/modules/video_coding/codecs/av1/av1_svc_config.h @@ -17,7 +17,9 @@ namespace webrtc { bool LibaomAv1EncoderSupportsScalabilityMode(ScalabilityMode scalability_mode); // Fills `video_codec.spatialLayers` using other members. -bool SetAv1SvcConfig(VideoCodec& video_codec); +bool SetAv1SvcConfig(VideoCodec& video_codec, + int num_temporal_layers, + int num_spatial_layers); } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc index b4e264b5f7..9f1da9865c 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc @@ -16,24 +16,48 @@ namespace webrtc { namespace { +constexpr int kDontCare = 0; TEST(Av1SvcConfigTest, TreatsEmptyAsL1T1) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_TRUE(video_codec.spatialLayers[0].active); EXPECT_EQ(video_codec.spatialLayers[0].numberOfTemporalLayers, 1); EXPECT_FALSE(video_codec.spatialLayers[1].active); } +TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfTemporalLayers) { + VideoCodec video_codec; + video_codec.codecType = kVideoCodecAV1; + + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/3, + /*num_spatial_layers=*/1)); + EXPECT_EQ(video_codec.spatialLayers[0].numberOfTemporalLayers, 3); +} + +TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfSpatialLayers) { + VideoCodec video_codec; + video_codec.codecType = kVideoCodecAV1; + + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/3, + /*num_spatial_layers=*/2)); + EXPECT_EQ(video_codec.spatialLayers[0].numberOfTemporalLayers, 3); + EXPECT_TRUE(video_codec.spatialLayers[0].active); + EXPECT_TRUE(video_codec.spatialLayers[1].active); + EXPECT_FALSE(video_codec.spatialLayers[2].active); +} + TEST(Av1SvcConfigTest, SetsActiveSpatialLayersFromScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_TRUE(video_codec.spatialLayers[0].active); EXPECT_TRUE(video_codec.spatialLayers[1].active); @@ -47,7 +71,8 @@ TEST(Av1SvcConfigTest, ConfiguresDobuleResolutionRatioFromScalabilityMode) { video_codec.width = 1200; video_codec.height = 800; - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_EQ(video_codec.spatialLayers[0].width, 600); EXPECT_EQ(video_codec.spatialLayers[0].height, 400); @@ -63,7 +88,8 @@ TEST(Av1SvcConfigTest, ConfiguresSmallResolutionRatioFromScalabilityMode) { video_codec.width = 1500; video_codec.height = 900; - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_EQ(video_codec.spatialLayers[0].width, 1000); EXPECT_EQ(video_codec.spatialLayers[0].height, 600); @@ -78,7 +104,8 @@ TEST(Av1SvcConfigTest, CopiesFramrate) { video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); video_codec.maxFramerate = 27; - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_EQ(video_codec.spatialLayers[0].maxFramerate, 27); EXPECT_EQ(video_codec.spatialLayers[1].maxFramerate, 27); @@ -89,7 +116,8 @@ TEST(Av1SvcConfigTest, SetsNumberOfTemporalLayers) { video_codec.codecType = kVideoCodecAV1; video_codec.SetScalabilityMode(ScalabilityMode::kL1T3); - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_EQ(video_codec.spatialLayers[0].numberOfTemporalLayers, 3); } @@ -101,7 +129,8 @@ TEST(Av1SvcConfigTest, CopiesMinMaxBitrateForSingleSpatialLayer) { video_codec.minBitrate = 100; video_codec.maxBitrate = 500; - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_EQ(video_codec.spatialLayers[0].minBitrate, 100u); EXPECT_EQ(video_codec.spatialLayers[0].maxBitrate, 500u); @@ -116,7 +145,8 @@ TEST(Av1SvcConfigTest, SetsBitratesForMultipleSpatialLayers) { video_codec.codecType = kVideoCodecAV1; video_codec.SetScalabilityMode(ScalabilityMode::kL3T3); - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); EXPECT_GT(video_codec.spatialLayers[0].minBitrate, 0u); EXPECT_LE(video_codec.spatialLayers[0].minBitrate, diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 80b9dc98f8..1cec6d2aea 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -263,7 +263,11 @@ VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec( break; } case kVideoCodecAV1: - if (SetAv1SvcConfig(video_codec)) { + if (SetAv1SvcConfig(video_codec, + /*num_temporal_layers=*/ + streams.back().num_temporal_layers.value_or(1), + /*num_spatial_layers=*/ + std::max(config.spatial_layers.size(), 1))) { for (size_t i = 0; i < config.spatial_layers.size(); ++i) { video_codec.spatialLayers[i].active = config.spatial_layers[i].active; }