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 <eshr@google.com>
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30498}
This commit is contained in:
Ilya Nikolaevskiy
2020-02-11 12:50:38 +01:00
committed by Commit Bot
parent ccd49d9af6
commit 03d909634b
12 changed files with 152 additions and 47 deletions

View File

@ -207,11 +207,15 @@ int GetTotalMaxBitrateBps(const std::vector<webrtc::VideoStream>& 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<webrtc::VideoStream> GetSimulcastConfig(
size_t min_layers,
size_t max_layers,
int width,
int height,
@ -229,6 +234,7 @@ std::vector<webrtc::VideoStream> 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<webrtc::VideoStream> 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,

View File

@ -32,13 +32,14 @@ int NormalizeSimulcastSize(int size, size_t simulcast_layers);
// Gets simulcast settings.
std::vector<webrtc::VideoStream> 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<webrtc::VideoStream> GetNormalSimulcastLayers(

View File

@ -78,9 +78,11 @@ TEST(SimulcastTest, BandwidthAboveTotalMaxBitrateGivenToHighestStream) {
TEST(SimulcastTest, GetConfig) {
const std::vector<VideoStream> kExpected = GetSimulcastBitrates720p();
const size_t kMinLayers = 1;
const size_t kMaxLayers = 3;
std::vector<VideoStream> 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<VideoStream> kExpected = GetSimulcastBitrates720p();
const size_t kMinLayers = 1;
const size_t kMaxLayers = 3;
std::vector<VideoStream> 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<int>(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<VideoStream> 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<VideoStream> 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<VideoStream> streams = cricket::GetSimulcastConfig(
kMaxLayers, 100, 100, kBitratePriority, kQpMax, kScreenshare);
std::vector<VideoStream> 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<VideoStream> 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<VideoStream> 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<VideoStream> 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<VideoStream> 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<VideoStream> 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<VideoStream> 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<VideoStream> streams = cricket::GetSimulcastConfig(
kMaxLayers, 1400, 800, kBitratePriority, kQpMax, kScreenshare);
std::vector<VideoStream> 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<VideoStream> streams = cricket::GetSimulcastConfig(
kMaxLayers, 1400, 800, kBitratePriority, kQpMax, kScreenshare);
std::vector<VideoStream> 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<VideoStream> streams = cricket::GetSimulcastConfig(
kMaxLayers, 1920, 1080, kBitratePriority, kQpMax, kScreenshare);
std::vector<VideoStream> 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<VideoStream> 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<VideoStream> kExpectedNear = GetSimulcastBitrates720p();
std::vector<VideoStream> 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);

View File

@ -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);

View File

@ -7630,8 +7630,8 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test {
std::vector<webrtc::VideoStream> 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) {

View File

@ -62,8 +62,8 @@ const int kMaxQp = 56;
void ConfigureSimulcast(VideoCodec* codec_settings) {
const std::vector<webrtc::VideoStream> 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<SpatialLayer> 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())

View File

@ -61,8 +61,10 @@ std::vector<SpatialLayer> ConfigureSvcScreenSharing(size_t input_width,
std::vector<SpatialLayer> 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<SpatialLayer> spatial_layers;
// Limit number of layers for given resolution.
@ -74,6 +76,7 @@ std::vector<SpatialLayer> 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<SpatialLayer> ConfigureSvcNormalVideo(size_t input_width,
std::vector<SpatialLayer> 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<SpatialLayer> 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);
}
}

View File

@ -21,6 +21,7 @@ namespace webrtc {
std::vector<SpatialLayer> 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);

View File

@ -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<SpatialLayer> 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<SpatialLayer> 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<SpatialLayer> 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<SpatialLayer> spatial_layers =
GetSvcConfig(1920, 1080, 30, 3, 3, true);
GetSvcConfig(1920, 1080, 30, 1, 3, 3, true);
EXPECT_EQ(spatial_layers.size(), 3UL);

View File

@ -34,8 +34,8 @@ static VideoCodec Configure(size_t width,
: VideoCodecMode::kRealtimeVideo;
std::vector<SpatialLayer> 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 =

View File

@ -114,8 +114,8 @@ class TestVp9Impl : public VideoCodecUnitTest {
std::vector<SpatialLayer> 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];
}

View File

@ -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);