diff --git a/modules/video_coding/codecs/av1/av1_svc_config.cc b/modules/video_coding/codecs/av1/av1_svc_config.cc index 43dcf96ab7..c480737096 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config.cc @@ -23,6 +23,22 @@ namespace webrtc { namespace { +const int kMinAv1SpatialLayerLongSideLength = 240; +const int kMinAv1SpatialLayerShortSideLength = 135; + +int GetLimitedNumSpatialLayers(int width, int height) { + const bool is_landscape = width >= height; + const int min_width = is_landscape ? kMinAv1SpatialLayerLongSideLength + : kMinAv1SpatialLayerShortSideLength; + const int min_height = is_landscape ? kMinAv1SpatialLayerShortSideLength + : kMinAv1SpatialLayerLongSideLength; + const int num_layers_fit_horz = static_cast( + std::floor(1 + std::max(0.0f, std::log2(1.0f * width / min_width)))); + const int num_layers_fit_vert = static_cast( + std::floor(1 + std::max(0.0f, std::log2(1.0f * height / min_height)))); + return std::min(num_layers_fit_horz, num_layers_fit_vert); +} + absl::optional BuildScalabilityMode(int num_temporal_layers, int num_spatial_layers) { char name[20]; @@ -69,6 +85,16 @@ bool SetAv1SvcConfig(VideoCodec& video_codec, } } + if (ScalabilityMode reduced = LimitNumSpatialLayers( + *scalability_mode, + GetLimitedNumSpatialLayers(video_codec.width, video_codec.height)); + *scalability_mode != reduced) { + RTC_LOG(LS_WARNING) << "Reduced number of spatial layers from " + << ScalabilityModeToString(*scalability_mode) << " to " + << ScalabilityModeToString(reduced); + scalability_mode = reduced; + } + std::unique_ptr structure = CreateScalabilityStructure(*scalability_mode); if (structure == nullptr) { 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 9f1da9865c..7ba7986f0d 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc @@ -18,9 +18,16 @@ namespace webrtc { namespace { constexpr int kDontCare = 0; -TEST(Av1SvcConfigTest, TreatsEmptyAsL1T1) { +VideoCodec GetDefaultVideoCodec() { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; + video_codec.width = 1280; + video_codec.height = 720; + return video_codec; +} + +TEST(Av1SvcConfigTest, TreatsEmptyAsL1T1) { + VideoCodec video_codec = GetDefaultVideoCodec(); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, /*num_spatial_layers=*/kDontCare)); @@ -31,8 +38,7 @@ TEST(Av1SvcConfigTest, TreatsEmptyAsL1T1) { } TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfTemporalLayers) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; + VideoCodec video_codec = GetDefaultVideoCodec(); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/3, /*num_spatial_layers=*/1)); @@ -40,8 +46,7 @@ TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfTemporalLayers) { } TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfSpatialLayers) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; + VideoCodec video_codec = GetDefaultVideoCodec(); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/3, /*num_spatial_layers=*/2)); @@ -52,8 +57,7 @@ TEST(Av1SvcConfigTest, ScalabilityModeFromNumberOfSpatialLayers) { } TEST(Av1SvcConfigTest, SetsActiveSpatialLayersFromScalabilityMode) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; + VideoCodec video_codec = GetDefaultVideoCodec(); video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, @@ -98,9 +102,7 @@ TEST(Av1SvcConfigTest, ConfiguresSmallResolutionRatioFromScalabilityMode) { } TEST(Av1SvcConfigTest, CopiesFramrate) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; - // h mode uses 1.5:1 ratio + VideoCodec video_codec = GetDefaultVideoCodec(); video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); video_codec.maxFramerate = 27; @@ -112,8 +114,7 @@ TEST(Av1SvcConfigTest, CopiesFramrate) { } TEST(Av1SvcConfigTest, SetsNumberOfTemporalLayers) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; + VideoCodec video_codec = GetDefaultVideoCodec(); video_codec.SetScalabilityMode(ScalabilityMode::kL1T3); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, @@ -141,8 +142,7 @@ TEST(Av1SvcConfigTest, CopiesMinMaxBitrateForSingleSpatialLayer) { } TEST(Av1SvcConfigTest, SetsBitratesForMultipleSpatialLayers) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; + VideoCodec video_codec = GetDefaultVideoCodec(); video_codec.SetScalabilityMode(ScalabilityMode::kL3T3); EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, @@ -167,5 +167,17 @@ TEST(Av1SvcConfigTest, SetsBitratesForMultipleSpatialLayers) { video_codec.spatialLayers[2].maxBitrate); } +TEST(Av1SvcConfigTest, ReduceSpatialLayersOnInsufficentInputResolution) { + VideoCodec video_codec = GetDefaultVideoCodec(); + video_codec.width = 640; + video_codec.height = 360; + video_codec.SetScalabilityMode(ScalabilityMode::kL3T3); + + EXPECT_TRUE(SetAv1SvcConfig(video_codec, /*num_temporal_layers=*/kDontCare, + /*num_spatial_layers=*/kDontCare)); + + EXPECT_EQ(*video_codec.GetScalabilityMode(), ScalabilityMode::kL2T3); +} + } // namespace } // namespace webrtc