diff --git a/media/engine/simulcast.cc b/media/engine/simulcast.cc index 79ff6f5e49..9308db35d0 100644 --- a/media/engine/simulcast.cc +++ b/media/engine/simulcast.cc @@ -207,11 +207,15 @@ int GetTotalMaxBitrateBps(const std::vector& layers) { return total_max_bitrate_bps; } -int LimitSimulcastLayerCount(int width, int height, int layer_count) { +size_t LimitSimulcastLayerCount(int width, + int height, + size_t need_layers, + size_t layer_count) { if (!webrtc::field_trial::IsDisabled( kUseLegacySimulcastLayerLimitFieldTrial)) { - int adaptive_layer_count = - kSimulcastFormats[FindSimulcastFormatIndex(width, height)].max_layers; + size_t adaptive_layer_count = std::max( + need_layers, + kSimulcastFormats[FindSimulcastFormatIndex(width, height)].max_layers); if (layer_count > adaptive_layer_count) { RTC_LOG(LS_WARNING) << "Reducing simulcast layer count from " << layer_count << " to " << adaptive_layer_count; @@ -222,6 +226,7 @@ int LimitSimulcastLayerCount(int width, int height, int layer_count) { } std::vector GetSimulcastConfig( + size_t min_layers, size_t max_layers, int width, int height, @@ -229,6 +234,7 @@ std::vector GetSimulcastConfig( int max_qp, bool is_screenshare_with_conference_mode, bool temporal_layers_supported) { + RTC_DCHECK_LE(min_layers, max_layers); RTC_DCHECK(max_layers > 1 || is_screenshare_with_conference_mode); const bool base_heavy_tl3_rate_alloc = @@ -242,7 +248,8 @@ std::vector GetSimulcastConfig( // Some applications rely on the old behavior limiting the simulcast layer // count based on the resolution automatically, which they can get through // the WebRTC-LegacySimulcastLayerLimit field trial until they update. - max_layers = LimitSimulcastLayerCount(width, height, max_layers); + max_layers = + LimitSimulcastLayerCount(width, height, min_layers, max_layers); return GetNormalSimulcastLayers(max_layers, width, height, bitrate_priority, max_qp, temporal_layers_supported, diff --git a/media/engine/simulcast.h b/media/engine/simulcast.h index 7b6af6db9c..6af13c10f3 100644 --- a/media/engine/simulcast.h +++ b/media/engine/simulcast.h @@ -32,13 +32,14 @@ int NormalizeSimulcastSize(int size, size_t simulcast_layers); // Gets simulcast settings. std::vector GetSimulcastConfig( + size_t min_layers, size_t max_layers, int width, int height, double bitrate_priority, int max_qp, bool is_screenshare_with_conference_mode, - bool temporal_layers_supported = true); + bool temporal_layers_supported); // Gets the simulcast config layers for a non-screensharing case. std::vector GetNormalSimulcastLayers( diff --git a/media/engine/simulcast_unittest.cc b/media/engine/simulcast_unittest.cc index 9e17a8928b..c8db8a32ef 100644 --- a/media/engine/simulcast_unittest.cc +++ b/media/engine/simulcast_unittest.cc @@ -78,9 +78,11 @@ TEST(SimulcastTest, BandwidthAboveTotalMaxBitrateGivenToHighestStream) { TEST(SimulcastTest, GetConfig) { const std::vector kExpected = GetSimulcastBitrates720p(); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1280, 720, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax, + !kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(320u, streams[0].width); @@ -111,9 +113,11 @@ TEST(SimulcastTest, GetConfigWithBaseHeavyVP8TL3RateAllocation) { const std::vector kExpected = GetSimulcastBitrates720p(); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1280, 720, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax, + !kScreenshare, true); EXPECT_EQ(kExpected[0].min_bitrate_bps, streams[0].min_bitrate_bps); EXPECT_EQ(static_cast(0.4 * kExpected[0].target_bitrate_bps / 0.6), @@ -128,9 +132,11 @@ TEST(SimulcastTest, GetConfigWithBaseHeavyVP8TL3RateAllocation) { } TEST(SimulcastTest, GetConfigWithLimitedMaxLayers) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1280, 720, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax, + !kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(640u, streams[0].width); @@ -142,9 +148,11 @@ TEST(SimulcastTest, GetConfigWithLimitedMaxLayers) { TEST(SimulcastTest, GetConfigWithLimitedMaxLayersForResolution) { test::ScopedFieldTrials field_trials( "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(2u, streams.size()); EXPECT_EQ(400u, streams[0].width); @@ -156,9 +164,11 @@ TEST(SimulcastTest, GetConfigWithLimitedMaxLayersForResolution) { TEST(SimulcastTest, GetConfigWithLowResolutionScreenshare) { test::ScopedFieldTrials field_trials( "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; - std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 100, 100, kBitratePriority, kQpMax, kScreenshare); + std::vector streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 100, 100, + kBitratePriority, kQpMax, kScreenshare, true); // Simulcast streams number is never decreased for screenshare, // even for very low resolution. @@ -168,9 +178,11 @@ TEST(SimulcastTest, GetConfigWithLowResolutionScreenshare) { TEST(SimulcastTest, GetConfigWithNotLimitedMaxLayersForResolution) { test::ScopedFieldTrials field_trials( "WebRTC-LegacySimulcastLayerLimit/Disabled/"); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(200u, streams[0].width); @@ -182,9 +194,11 @@ TEST(SimulcastTest, GetConfigWithNotLimitedMaxLayersForResolution) { } TEST(SimulcastTest, GetConfigWithNormalizedResolution) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 640 + 1, 360 + 1, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 640 + 1, 360 + 1, kBitratePriority, kQpMax, + !kScreenshare, true); // Must be divisible by |2 ^ (num_layers - 1)|. EXPECT_EQ(kMaxLayers, streams.size()); @@ -198,9 +212,11 @@ TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy4) { test::ScopedFieldTrials field_trials( "WebRTC-NormalizeSimulcastResolution/Enabled-2/"); + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare, + true); // Must be divisible by |2 ^ 2|. EXPECT_EQ(kMaxLayers, streams.size()); @@ -214,9 +230,11 @@ TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy8) { test::ScopedFieldTrials field_trials( "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare, + true); // Must be divisible by |2 ^ 3|. EXPECT_EQ(kMaxLayers, streams.size()); @@ -230,24 +248,52 @@ TEST(SimulcastTest, GetConfigForLegacyLayerLimit) { test::ScopedFieldTrials field_trials( "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + const size_t kMinLayers = 1; const int kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 320, 180, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 320, 180, kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(1u, streams.size()); - streams = cricket::GetSimulcastConfig(kMaxLayers, 640, 360, kBitratePriority, - kQpMax, !kScreenshare); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360, + kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(2u, streams.size()); - streams = cricket::GetSimulcastConfig( - kMaxLayers, 1920, 1080, kBitratePriority, kQpMax, !kScreenshare); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080, + kBitratePriority, kQpMax, !kScreenshare, + true); + EXPECT_EQ(3u, streams.size()); +} + +TEST(SimulcastTest, GetConfigForLegacyLayerLimitWithRequiredHD) { + test::ScopedFieldTrials field_trials( + "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + + const size_t kMinLayers = 3; // "HD" layer must be present! + const int kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 320, 180, kBitratePriority, kQpMax, !kScreenshare, + true); + EXPECT_EQ(3u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360, + kBitratePriority, kQpMax, !kScreenshare, + true); + EXPECT_EQ(3u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080, + kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(3u, streams.size()); } TEST(SimulcastTest, GetConfigForScreenshareSimulcast) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; - std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1400, 800, kBitratePriority, kQpMax, kScreenshare); + std::vector streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1400, 800, + kBitratePriority, kQpMax, kScreenshare, true); EXPECT_GT(streams.size(), 1u); for (size_t i = 0; i < streams.size(); ++i) { @@ -264,9 +310,11 @@ TEST(SimulcastTest, GetConfigForScreenshareSimulcast) { } TEST(SimulcastTest, GetConfigForScreenshareSimulcastWithLimitedMaxLayers) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 1; - std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1400, 800, kBitratePriority, kQpMax, kScreenshare); + std::vector streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1400, 800, + kBitratePriority, kQpMax, kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); } @@ -277,17 +325,20 @@ TEST(SimulcastTest, SimulcastScreenshareMaxBitrateAdjustedForResolution) { constexpr int kMaxBitrate960_540 = 1200000; // Normal case, max bitrate not limited by resolution. + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; - std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 1920, 1080, kBitratePriority, kQpMax, kScreenshare); + std::vector streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080, + kBitratePriority, kQpMax, kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(streams[1].max_bitrate_bps, kScreenshareHighStreamMaxBitrateBps); EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps); EXPECT_GE(streams[1].max_bitrate_bps, streams[1].min_bitrate_bps); // At 960x540, the max bitrate is limited to 900kbps. - streams = cricket::GetSimulcastConfig(kMaxLayers, 960, 540, kBitratePriority, - kQpMax, kScreenshare); + streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 540, + kBitratePriority, kQpMax, kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(streams[1].max_bitrate_bps, kMaxBitrate960_540); EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps); @@ -295,8 +346,9 @@ TEST(SimulcastTest, SimulcastScreenshareMaxBitrateAdjustedForResolution) { // At 480x270, the max bitrate is limited to 450kbps. This is lower than // the min bitrate, so use that as a lower bound. - streams = cricket::GetSimulcastConfig(kMaxLayers, 480, 270, kBitratePriority, - kQpMax, kScreenshare); + streams = + cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 270, + kBitratePriority, kQpMax, kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(streams[1].max_bitrate_bps, kScreenshareHighStreamMinBitrateBps); EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps); @@ -304,9 +356,11 @@ TEST(SimulcastTest, SimulcastScreenshareMaxBitrateAdjustedForResolution) { } TEST(SimulcastTest, AveragesBitratesForNonStandardResolution) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, 900, 800, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, 900, 800, kBitratePriority, kQpMax, !kScreenshare, + true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(900u, streams[2].width); @@ -317,6 +371,7 @@ TEST(SimulcastTest, AveragesBitratesForNonStandardResolution) { } TEST(SimulcastTest, BitratesForCloseToStandardResolution) { + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; // Resolution very close to 720p in number of pixels const size_t kWidth = 1280; @@ -324,7 +379,8 @@ TEST(SimulcastTest, BitratesForCloseToStandardResolution) { const std::vector kExpectedNear = GetSimulcastBitrates720p(); std::vector streams = cricket::GetSimulcastConfig( - kMaxLayers, kWidth, kHeight, kBitratePriority, kQpMax, !kScreenshare); + kMinLayers, kMaxLayers, kWidth, kHeight, kBitratePriority, kQpMax, + !kScreenshare, true); EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(kWidth, streams[2].width); diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index f2426ce0c6..4166a5f3c5 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -302,6 +302,17 @@ bool IsLayerActive(const webrtc::RtpEncodingParameters& layer) { (!layer.max_framerate || *layer.max_framerate > 0); } +size_t FindRequiredActiveLayers( + const webrtc::VideoEncoderConfig& encoder_config) { + // Need enough layers so that at least the first active one is present. + for (size_t i = 0; i < encoder_config.number_of_streams; ++i) { + if (encoder_config.simulcast_layers[i].active) { + return i + 1; + } + } + return 0; +} + } // namespace // This constant is really an on/off, lower-level configurable NACK history @@ -3250,7 +3261,8 @@ EncoderStreamFactory::CreateSimulcastOrConfereceModeScreenshareStreams( absl::EqualsIgnoreCase(codec_name_, kH264CodecName); // Use legacy simulcast screenshare if conference mode is explicitly enabled // or use the regular simulcast configuration path which is generic. - layers = GetSimulcastConfig(encoder_config.number_of_streams, width, height, + layers = GetSimulcastConfig(FindRequiredActiveLayers(encoder_config), + encoder_config.number_of_streams, width, height, encoder_config.bitrate_priority, max_qp_, is_screenshare_ && conference_mode_, temporal_layers_supported); diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index fbff8c92f9..e5ebc5e816 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -7630,8 +7630,8 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { std::vector expected_streams; if (num_configured_streams > 1 || conference_mode) { expected_streams = GetSimulcastConfig( - num_configured_streams, capture_width, capture_height, - webrtc::kDefaultBitratePriority, kDefaultQpMax, + /*min_layers=*/1, num_configured_streams, capture_width, + capture_height, webrtc::kDefaultBitratePriority, kDefaultQpMax, screenshare && conference_mode, true); if (screenshare && conference_mode) { for (const webrtc::VideoStream& stream : expected_streams) { diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc index 0eb256ea99..fe42039468 100644 --- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc +++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc @@ -62,8 +62,8 @@ const int kMaxQp = 56; void ConfigureSimulcast(VideoCodec* codec_settings) { const std::vector streams = cricket::GetSimulcastConfig( - codec_settings->numberOfSimulcastStreams, codec_settings->width, - codec_settings->height, kBitratePriority, kMaxQp, + /*min_layer=*/1, codec_settings->numberOfSimulcastStreams, + codec_settings->width, codec_settings->height, kBitratePriority, kMaxQp, /* is_screenshare = */ false, true); for (size_t i = 0; i < streams.size(); ++i) { @@ -85,7 +85,7 @@ void ConfigureSvc(VideoCodec* codec_settings) { const std::vector layers = GetSvcConfig( codec_settings->width, codec_settings->height, kMaxFramerateFps, - codec_settings->VP9()->numberOfSpatialLayers, + /*min_spatial_layers=*/1, codec_settings->VP9()->numberOfSpatialLayers, codec_settings->VP9()->numberOfTemporalLayers, /* is_screen_sharing = */ false); ASSERT_EQ(codec_settings->VP9()->numberOfSpatialLayers, layers.size()) diff --git a/modules/video_coding/codecs/vp9/svc_config.cc b/modules/video_coding/codecs/vp9/svc_config.cc index a3bf56d90c..764c1a209d 100644 --- a/modules/video_coding/codecs/vp9/svc_config.cc +++ b/modules/video_coding/codecs/vp9/svc_config.cc @@ -61,8 +61,10 @@ std::vector ConfigureSvcScreenSharing(size_t input_width, std::vector ConfigureSvcNormalVideo(size_t input_width, size_t input_height, float max_framerate_fps, + size_t min_spatial_layers, size_t num_spatial_layers, size_t num_temporal_layers) { + RTC_DCHECK_LE(min_spatial_layers, num_spatial_layers); std::vector spatial_layers; // Limit number of layers for given resolution. @@ -74,6 +76,7 @@ std::vector ConfigureSvcNormalVideo(size_t input_width, kMinVp9SpatialLayerHeight)))); num_spatial_layers = std::min({num_spatial_layers, num_layers_fit_horz, num_layers_fit_vert}); + num_spatial_layers = std::max(num_spatial_layers, min_spatial_layers); for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) { SpatialLayer spatial_layer = {0}; @@ -109,6 +112,7 @@ std::vector ConfigureSvcNormalVideo(size_t input_width, std::vector GetSvcConfig(size_t input_width, size_t input_height, float max_framerate_fps, + size_t min_spatial_layers, size_t num_spatial_layers, size_t num_temporal_layers, bool is_screen_sharing) { @@ -122,7 +126,8 @@ std::vector GetSvcConfig(size_t input_width, max_framerate_fps, num_spatial_layers); } else { return ConfigureSvcNormalVideo(input_width, input_height, max_framerate_fps, - num_spatial_layers, num_temporal_layers); + min_spatial_layers, num_spatial_layers, + num_temporal_layers); } } diff --git a/modules/video_coding/codecs/vp9/svc_config.h b/modules/video_coding/codecs/vp9/svc_config.h index 6e9ae9b2e5..3bc9ba7a34 100644 --- a/modules/video_coding/codecs/vp9/svc_config.h +++ b/modules/video_coding/codecs/vp9/svc_config.h @@ -21,6 +21,7 @@ namespace webrtc { std::vector GetSvcConfig(size_t input_width, size_t input_height, float max_framerate_fps, + size_t min_spatial_layers, size_t num_spatial_layers, size_t num_temporal_layers, bool is_screen_sharing); diff --git a/modules/video_coding/codecs/vp9/svc_config_unittest.cc b/modules/video_coding/codecs/vp9/svc_config_unittest.cc index bda6a5573c..07a2ebe5ad 100644 --- a/modules/video_coding/codecs/vp9/svc_config_unittest.cc +++ b/modules/video_coding/codecs/vp9/svc_config_unittest.cc @@ -19,22 +19,35 @@ namespace webrtc { TEST(SvcConfig, NumSpatialLayers) { const size_t max_num_spatial_layers = 6; + const size_t min_spatial_layers = 1; const size_t num_spatial_layers = 2; std::vector spatial_layers = GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1), kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30, - max_num_spatial_layers, 1, false); + min_spatial_layers, max_num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), num_spatial_layers); } +TEST(SvcConfig, NumSpatialLayersRespectsMinNumberOfLayers) { + const size_t max_num_spatial_layers = 6; + const size_t min_spatial_layers = 2; + + std::vector spatial_layers = + GetSvcConfig(kMinVp9SpatialLayerWidth, kMinVp9SpatialLayerHeight, 30, + min_spatial_layers, max_num_spatial_layers, 1, false); + + EXPECT_EQ(spatial_layers.size(), 2u); +} + TEST(SvcConfig, BitrateThresholds) { + const size_t min_spatial_layers = 1; const size_t num_spatial_layers = 3; std::vector spatial_layers = GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1), kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30, - num_spatial_layers, 1, false); + min_spatial_layers, num_spatial_layers, 1, false); EXPECT_EQ(spatial_layers.size(), num_spatial_layers); @@ -47,7 +60,7 @@ TEST(SvcConfig, BitrateThresholds) { TEST(SvcConfig, ScreenSharing) { std::vector spatial_layers = - GetSvcConfig(1920, 1080, 30, 3, 3, true); + GetSvcConfig(1920, 1080, 30, 1, 3, 3, true); EXPECT_EQ(spatial_layers.size(), 3UL); diff --git a/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc b/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc index 6a677a2a6f..9635eae586 100644 --- a/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc +++ b/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc @@ -34,8 +34,8 @@ static VideoCodec Configure(size_t width, : VideoCodecMode::kRealtimeVideo; std::vector spatial_layers = - GetSvcConfig(width, height, 30, num_spatial_layers, num_temporal_layers, - is_screen_sharing); + GetSvcConfig(width, height, 30, /*min_spatial_layers=*/1, + num_spatial_layers, num_temporal_layers, is_screen_sharing); RTC_CHECK_LE(spatial_layers.size(), kMaxSpatialLayers); codec.VP9()->numberOfSpatialLayers = diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc index ed15ee0a2c..1a237ca913 100644 --- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc +++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc @@ -114,8 +114,8 @@ class TestVp9Impl : public VideoCodecUnitTest { std::vector layers = GetSvcConfig(codec_settings_.width, codec_settings_.height, - codec_settings_.maxFramerate, num_spatial_layers, - num_temporal_layers, false); + codec_settings_.maxFramerate, /*min_spatial_layers=*/1, + num_spatial_layers, num_temporal_layers, false); for (size_t i = 0; i < layers.size(); ++i) { codec_settings_.spatialLayers[i] = layers[i]; } diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 46d055fab7..bd40385a51 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -179,9 +179,19 @@ VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec( // Layering is set explicitly. spatial_layers = config.spatial_layers; } else { + size_t min_required_layers = 0; + // Need at least enough layers for the first active one to be present. + for (size_t spatial_idx = 0; + spatial_idx < config.simulcast_layers.size(); ++spatial_idx) { + if (config.simulcast_layers[spatial_idx].active) { + min_required_layers = spatial_idx + 1; + break; + } + } + spatial_layers = GetSvcConfig( video_codec.width, video_codec.height, video_codec.maxFramerate, - video_codec.VP9()->numberOfSpatialLayers, + min_required_layers, video_codec.VP9()->numberOfSpatialLayers, video_codec.VP9()->numberOfTemporalLayers, video_codec.mode == VideoCodecMode::kScreensharing);