From 03d909634b2ab0b06fd2a230bb3c8641b431567f Mon Sep 17 00:00:00 2001 From: Ilya Nikolaevskiy Date: Tue, 11 Feb 2020 12:50:38 +0100 Subject: [PATCH] Ensure that the first active layer isn't disabled by too low input resolution If e.g. CPU adaptation reduces input video size too much, video pipeline would reduce the number of used simulcast streams/spatial layers. This may result in disabled video if some streams are disabled by Rtp encoding parameters API. Bug: webrtc:11319 Change-Id: Id7f157255599dcb6f494129b83477cda4bea982a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168480 Reviewed-by: Evan Shrubsole Commit-Queue: Ilya Nikolaevskiy Cr-Commit-Position: refs/heads/master@{#30498} --- media/engine/simulcast.cc | 15 ++- media/engine/simulcast.h | 3 +- media/engine/simulcast_unittest.cc | 110 +++++++++++++----- media/engine/webrtc_video_engine.cc | 14 ++- media/engine/webrtc_video_engine_unittest.cc | 4 +- .../test/videocodec_test_fixture_impl.cc | 6 +- modules/video_coding/codecs/vp9/svc_config.cc | 7 +- modules/video_coding/codecs/vp9/svc_config.h | 1 + .../codecs/vp9/svc_config_unittest.cc | 19 ++- .../codecs/vp9/svc_rate_allocator_unittest.cc | 4 +- .../codecs/vp9/test/vp9_impl_unittest.cc | 4 +- .../video_coding/video_codec_initializer.cc | 12 +- 12 files changed, 152 insertions(+), 47 deletions(-) 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);