diff --git a/video/config/BUILD.gn b/video/config/BUILD.gn index 9c1c0642dc..4c20e02f55 100644 --- a/video/config/BUILD.gn +++ b/video/config/BUILD.gn @@ -18,10 +18,8 @@ rtc_library("streams_config") { deps = [ ":encoder_config", - "../../api:array_view", "../../api:field_trials_view", "../../api/units:data_rate", - "../../api/video:resolution", "../../api/video:video_codec_constants", "../../api/video_codecs:video_codecs_api", "../../call/adaptation:resource_adaptation", @@ -71,7 +69,6 @@ if (rtc_include_tests) { ] deps = [ ":streams_config", - "../../api/video_codecs:scalability_mode", "../../call/adaptation:resource_adaptation", "../../media:media_constants", "../../rtc_base/experiments:min_video_bitrate_experiment", diff --git a/video/config/encoder_stream_factory.cc b/video/config/encoder_stream_factory.cc index ffb1affa9d..7dd53ab389 100644 --- a/video/config/encoder_stream_factory.cc +++ b/video/config/encoder_stream_factory.cc @@ -111,22 +111,6 @@ int GetDefaultMaxQp(webrtc::VideoCodecType codec_type) { } } -// Round size to nearest simulcast-friendly size. -// Simulcast stream width and height must both be dividable by -// |2 ^ (simulcast_layers - 1)|. -int NormalizeSimulcastSize(const FieldTrialsView& field_trials, - int size, - size_t simulcast_layers) { - int base2_exponent = static_cast(simulcast_layers) - 1; - const absl::optional experimental_base2_exponent = - webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent(field_trials); - if (experimental_base2_exponent && - (size > (1 << *experimental_base2_exponent))) { - base2_exponent = *experimental_base2_exponent; - } - return ((size >> base2_exponent) << base2_exponent); -} - } // namespace EncoderStreamFactory::EncoderStreamFactory( @@ -338,17 +322,16 @@ EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams( webrtc::VideoEncoderConfig::ContentType::kScreen; const bool is_legacy_screencast = webrtc::SimulcastUtility::IsConferenceModeScreenshare(encoder_config); - - std::vector resolutions = - GetStreamResolutions(trials, width, height, encoder_config); + std::vector layers; const bool temporal_layers_supported = IsTemporalLayersSupported(encoder_config.codec_type); // Use legacy simulcast screenshare if conference mode is explicitly enabled // or use the regular simulcast configuration path which is generic. - std::vector layers = GetSimulcastConfig( - resolutions, is_legacy_screencast, temporal_layers_supported, trials, - encoder_config.codec_type); + layers = GetSimulcastConfig(FindRequiredActiveLayers(encoder_config), + encoder_config.number_of_streams, width, height, + is_legacy_screencast, temporal_layers_supported, + trials, encoder_config.codec_type); // Allow an experiment to override the minimum bitrate for the lowest // spatial layer. The experiment's configuration has the lowest priority. layers[0].min_bitrate_bps = experimental_min_bitrate @@ -356,6 +339,31 @@ EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams( webrtc::kDefaultMinVideoBitrateBps)) .bps(); + const bool has_scale_resolution_down_by = absl::c_any_of( + encoder_config.simulcast_layers, [](const webrtc::VideoStream& layer) { + return layer.scale_resolution_down_by != -1.; + }); + + bool default_scale_factors_used = true; + if (has_scale_resolution_down_by) { + default_scale_factors_used = IsScaleFactorsPowerOfTwo(encoder_config); + } + const bool norm_size_configured = + webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent(trials) + .has_value(); + const int normalized_width = + (default_scale_factors_used || norm_size_configured) && + (width >= kMinLayerSize) + ? NormalizeSimulcastSize(trials, width, + encoder_config.number_of_streams) + : width; + const int normalized_height = + (default_scale_factors_used || norm_size_configured) && + (height >= kMinLayerSize) + ? NormalizeSimulcastSize(trials, height, + encoder_config.number_of_streams) + : height; + // Update the active simulcast layers and configured bitrates. for (size_t i = 0; i < layers.size(); ++i) { layers[i].active = encoder_config.simulcast_layers[i].active; @@ -373,6 +381,20 @@ EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams( layers[i].max_framerate = encoder_config.simulcast_layers[i].max_framerate; } + if (encoder_config.simulcast_layers[i].requested_resolution.has_value()) { + auto res = GetLayerResolutionFromRequestedResolution( + normalized_width, normalized_height, + *encoder_config.simulcast_layers[i].requested_resolution); + layers[i].width = res.width; + layers[i].height = res.height; + } else if (has_scale_resolution_down_by) { + const double scale_resolution_down_by = std::max( + encoder_config.simulcast_layers[i].scale_resolution_down_by, 1.0); + layers[i].width = ScaleDownResolution( + normalized_width, scale_resolution_down_by, kMinLayerSize); + layers[i].height = ScaleDownResolution( + normalized_height, scale_resolution_down_by, kMinLayerSize); + } // Update simulcast bitrates with configured min and max bitrate. if (encoder_config.simulcast_layers[i].min_bitrate_bps > 0) { layers[i].min_bitrate_bps = @@ -494,71 +516,4 @@ EncoderStreamFactory::GetLayerResolutionFromRequestedResolution( return {.width = out_width, .height = out_height}; } -std::vector EncoderStreamFactory::GetStreamResolutions( - const webrtc::FieldTrialsView& trials, - int width, - int height, - const webrtc::VideoEncoderConfig& encoder_config) const { - std::vector resolutions; - if (webrtc::SimulcastUtility::IsConferenceModeScreenshare(encoder_config)) { - for (size_t i = 0; i < encoder_config.number_of_streams; ++i) { - resolutions.push_back({.width = width, .height = height}); - } - } else { - size_t min_num_layers = FindRequiredActiveLayers(encoder_config); - size_t max_num_layers = LimitSimulcastLayerCount( - min_num_layers, encoder_config.number_of_streams, width, height, trials, - encoder_config.codec_type); - RTC_DCHECK_LE(max_num_layers, encoder_config.number_of_streams); - - const bool has_scale_resolution_down_by = absl::c_any_of( - encoder_config.simulcast_layers, [](const webrtc::VideoStream& layer) { - return layer.scale_resolution_down_by != -1.; - }); - - bool default_scale_factors_used = true; - if (has_scale_resolution_down_by) { - default_scale_factors_used = IsScaleFactorsPowerOfTwo(encoder_config); - } - - const bool norm_size_configured = - webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent(trials) - .has_value(); - const int normalized_width = - (default_scale_factors_used || norm_size_configured) && - (width >= kMinLayerSize) - ? NormalizeSimulcastSize(trials, width, max_num_layers) - : width; - const int normalized_height = - (default_scale_factors_used || norm_size_configured) && - (height >= kMinLayerSize) - ? NormalizeSimulcastSize(trials, height, max_num_layers) - : height; - - resolutions.resize(max_num_layers); - for (size_t i = 0; i < max_num_layers; i++) { - if (encoder_config.simulcast_layers[i].requested_resolution.has_value()) { - resolutions[i] = GetLayerResolutionFromRequestedResolution( - normalized_width, normalized_height, - *encoder_config.simulcast_layers[i].requested_resolution); - } else if (has_scale_resolution_down_by) { - const double scale_resolution_down_by = std::max( - encoder_config.simulcast_layers[i].scale_resolution_down_by, 1.0); - resolutions[i].width = ScaleDownResolution( - normalized_width, scale_resolution_down_by, kMinLayerSize); - resolutions[i].height = ScaleDownResolution( - normalized_height, scale_resolution_down_by, kMinLayerSize); - } else { - // Resolutions with default 1/2 scale factor, from low to high. - resolutions[i].width = - normalized_width >> (encoder_config.number_of_streams - i - 1); - resolutions[i].height = - normalized_height >> (encoder_config.number_of_streams - i - 1); - } - } - } - - return resolutions; -} - } // namespace cricket diff --git a/video/config/encoder_stream_factory.h b/video/config/encoder_stream_factory.h index 7792a81c6a..ef27fb87cf 100644 --- a/video/config/encoder_stream_factory.h +++ b/video/config/encoder_stream_factory.h @@ -54,12 +54,6 @@ class EncoderStreamFactory int in_frame_height, webrtc::Resolution requested_resolution) const; - std::vector GetStreamResolutions( - const webrtc::FieldTrialsView& trials, - int width, - int height, - const webrtc::VideoEncoderConfig& encoder_config) const; - const int encoder_info_requested_resolution_alignment_; const absl::optional restrictions_; }; diff --git a/video/config/encoder_stream_factory_unittest.cc b/video/config/encoder_stream_factory_unittest.cc index 881be91ae0..7daf0b2eea 100644 --- a/video/config/encoder_stream_factory_unittest.cc +++ b/video/config/encoder_stream_factory_unittest.cc @@ -10,9 +10,6 @@ #include "video/config/encoder_stream_factory.h" -#include - -#include "api/video_codecs/scalability_mode.h" #include "call/adaptation/video_source_restrictions.h" #include "rtc_base/experiments/min_video_bitrate_experiment.h" #include "test/explicit_key_value_config.h" @@ -23,37 +20,8 @@ namespace webrtc { namespace { using ::cricket::EncoderStreamFactory; using test::ExplicitKeyValueConfig; -using ::testing::Combine; -using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::Not; -using ::testing::SizeIs; -using ::testing::Values; - -struct CreateVideoStreamParams { - int width = 0; - int height = 0; - int max_framerate_fps = -1; - int min_bitrate_bps = -1; - int target_bitrate_bps = -1; - int max_bitrate_bps = -1; - int scale_resolution_down_by = -1; - std::optional scalability_mode; -}; - -// A helper function that creates `VideoStream` with given settings. -VideoStream CreateVideoStream(const CreateVideoStreamParams& params) { - VideoStream stream; - stream.width = params.width; - stream.height = params.height; - stream.max_framerate = params.max_framerate_fps; - stream.min_bitrate_bps = params.min_bitrate_bps; - stream.target_bitrate_bps = params.target_bitrate_bps; - stream.max_bitrate_bps = params.max_bitrate_bps; - stream.scale_resolution_down_by = params.scale_resolution_down_by; - stream.scalability_mode = params.scalability_mode; - return stream; -} std::vector GetStreamResolutions( const std::vector& streams) { @@ -67,29 +35,24 @@ std::vector GetStreamResolutions( return res; } -std::vector CreateEncoderStreams( - const FieldTrialsView& field_trials, - const Resolution& resolution, - const VideoEncoderConfig& encoder_config, - absl::optional restrictions = absl::nullopt) { - VideoEncoder::EncoderInfo encoder_info; - auto factory = - rtc::make_ref_counted(encoder_info, restrictions); - return factory->CreateEncoderStreams(field_trials, resolution.width, - resolution.height, encoder_config); +VideoStream LayerWithRequestedResolution(Resolution res) { + VideoStream s; + s.requested_resolution = res; + return s; } } // namespace TEST(EncoderStreamFactory, SinglecastRequestedResolution) { ExplicitKeyValueConfig field_trials(""); + VideoEncoder::EncoderInfo encoder_info; + auto factory = rtc::make_ref_counted(encoder_info); VideoEncoderConfig encoder_config; encoder_config.number_of_streams = 1; - encoder_config.simulcast_layers.resize(1); - encoder_config.simulcast_layers[0].requested_resolution = {.width = 640, - .height = 360}; - auto streams = CreateEncoderStreams( - field_trials, {.width = 1280, .height = 720}, encoder_config); + encoder_config.simulcast_layers.push_back( + LayerWithRequestedResolution({.width = 640, .height = 360})); + auto streams = + factory->CreateEncoderStreams(field_trials, 1280, 720, encoder_config); EXPECT_EQ(streams[0].requested_resolution, (Resolution{.width = 640, .height = 360})); EXPECT_EQ(GetStreamResolutions(streams), (std::vector{ @@ -103,14 +66,15 @@ TEST(EncoderStreamFactory, SinglecastRequestedResolutionWithAdaptation) { /* max_pixels_per_frame= */ (320 * 320), /* target_pixels_per_frame= */ absl::nullopt, /* max_frame_rate= */ absl::nullopt); + VideoEncoder::EncoderInfo encoder_info; + auto factory = + rtc::make_ref_counted(encoder_info, restrictions); VideoEncoderConfig encoder_config; encoder_config.number_of_streams = 1; - encoder_config.simulcast_layers.resize(1); - encoder_config.simulcast_layers[0].requested_resolution = {.width = 640, - .height = 360}; + encoder_config.simulcast_layers.push_back( + LayerWithRequestedResolution({.width = 640, .height = 360})); auto streams = - CreateEncoderStreams(field_trials, {.width = 1280, .height = 720}, - encoder_config, restrictions); + factory->CreateEncoderStreams(field_trials, 1280, 720, encoder_config); EXPECT_EQ(streams[0].requested_resolution, (Resolution{.width = 640, .height = 360})); EXPECT_EQ(GetStreamResolutions(streams), (std::vector{ @@ -120,14 +84,18 @@ TEST(EncoderStreamFactory, SinglecastRequestedResolutionWithAdaptation) { TEST(EncoderStreamFactory, BitratePriority) { constexpr double kBitratePriority = 0.123; + ExplicitKeyValueConfig field_trials(""); + VideoEncoder::EncoderInfo encoder_info; + auto factory = rtc::make_ref_counted(encoder_info); VideoEncoderConfig encoder_config; encoder_config.number_of_streams = 2; - encoder_config.simulcast_layers.resize(encoder_config.number_of_streams); encoder_config.bitrate_priority = kBitratePriority; - auto streams = CreateEncoderStreams( - /*field_trials=*/ExplicitKeyValueConfig(""), - {.width = 640, .height = 360}, encoder_config); - ASSERT_THAT(streams, SizeIs(2)); + encoder_config.simulcast_layers = { + LayerWithRequestedResolution({.width = 320, .height = 180}), + LayerWithRequestedResolution({.width = 640, .height = 360})}; + auto streams = + factory->CreateEncoderStreams(field_trials, 640, 360, encoder_config); + ASSERT_EQ(streams.size(), 2u); EXPECT_EQ(streams[0].bitrate_priority, kBitratePriority); EXPECT_FALSE(streams[1].bitrate_priority); } @@ -157,186 +125,4 @@ TEST(EncoderStreamFactory, SetsMinBitrateToExperimentalValue) { EXPECT_NE(streams[0].min_bitrate_bps, kDefaultMinVideoBitrateBps); EXPECT_EQ(streams[0].min_bitrate_bps, 1000); } - -struct ResolutionAlignmentTestParams { - absl::string_view field_trials; - size_t number_of_streams = 1; - Resolution resolution = {.width = 640, .height = 480}; -}; - -std::vector CreateAlignedResolution( - const ResolutionAlignmentTestParams& test_params) { - VideoEncoderConfig encoder_config; - encoder_config.codec_type = VideoCodecType::kVideoCodecVP8; - encoder_config.number_of_streams = test_params.number_of_streams; - encoder_config.simulcast_layers.resize(test_params.number_of_streams); - return GetStreamResolutions( - CreateEncoderStreams(ExplicitKeyValueConfig(test_params.field_trials), - test_params.resolution, encoder_config)); -} - -TEST(EncoderStreamFactory, KeepsResolutionUnchangedWhenAligned) { - EXPECT_THAT( - CreateAlignedResolution({.number_of_streams = 2, - .resolution = {.width = 516, .height = 526}}), - ElementsAre(Resolution{.width = 516 / 2, .height = 526 / 2}, - Resolution{.width = 516, .height = 526})); -} - -TEST(EncoderStreamFactory, AdjustsResolutionWhenUnaligned) { - // By default width and height of the smallest simulcast stream are required - // to be whole numbers. To achieve that, the resolution of the highest - // simulcast stream is adjusted to be multiple of (2 ^ (number_of_streams - - // 1)) by rounding down. - EXPECT_THAT( - CreateAlignedResolution({.number_of_streams = 2, - .resolution = {.width = 515, .height = 517}}), - ElementsAre(Resolution{.width = 514 / 2, .height = 516 / 2}, - Resolution{.width = 514, .height = 516})); -} - -TEST(EncoderStreamFactory, MakesResolutionDivisibleBy4) { - EXPECT_THAT( - CreateAlignedResolution( - {.field_trials = "WebRTC-NormalizeSimulcastResolution/Enabled-2/", - .number_of_streams = 2, - .resolution = {.width = 515, .height = 517}}), - ElementsAre(Resolution{.width = 512 / 2, .height = 516 / 2}, - Resolution{.width = 512, .height = 516})); -} - -struct StreamCountTestParams { - std::string field_trials; - Resolution input_resolution; - bool is_legacy_screencast = false; - size_t first_active_layer_idx = 0; - size_t number_of_streams = 0; -}; - -size_t GetStreamCount(const StreamCountTestParams& test_params) { - VideoEncoderConfig encoder_config; - encoder_config.codec_type = VideoCodecType::kVideoCodecVP8; - encoder_config.number_of_streams = test_params.number_of_streams; - encoder_config.simulcast_layers.resize(test_params.number_of_streams); - for (size_t i = 0; i < encoder_config.number_of_streams; ++i) { - encoder_config.simulcast_layers[i].active = - (i >= test_params.first_active_layer_idx); - } - if (test_params.is_legacy_screencast) { - encoder_config.content_type = VideoEncoderConfig::ContentType::kScreen; - encoder_config.legacy_conference_mode = true; - } - return CreateEncoderStreams(ExplicitKeyValueConfig(test_params.field_trials), - test_params.input_resolution, encoder_config) - .size(); -} - -TEST(EncoderStreamFactory, KeepsStreamCountUnchangedWhenResolutionIsHigh) { - EXPECT_EQ(GetStreamCount({.input_resolution = {.width = 1000, .height = 1000}, - .number_of_streams = 3}), - 3u); -} - -TEST(EncoderStreamFactory, ReducesStreamCountWhenResolutionIsLow) { - EXPECT_EQ(GetStreamCount({.input_resolution = {.width = 100, .height = 100}, - .number_of_streams = 3}), - 1u); -} - -TEST(EncoderStreamFactory, ReducesStreamCountDownToFirstActiveStream) { - EXPECT_EQ(GetStreamCount({.input_resolution = {.width = 100, .height = 100}, - .first_active_layer_idx = 1, - .number_of_streams = 3}), - 2u); -} - -TEST(EncoderStreamFactory, - ReducesLegacyScreencastStreamCountWhenResolutionIsLow) { - // At least 2 streams are expected to be configured in legacy screencast mode. - EXPECT_EQ(GetStreamCount({.input_resolution = {.width = 100, .height = 100}, - .is_legacy_screencast = true, - .number_of_streams = 3}), - 2u); -} - -TEST(EncoderStreamFactory, KeepsStreamCountUnchangedWhenLegacyLimitIsDisabled) { - EXPECT_EQ(GetStreamCount( - {.field_trials = "WebRTC-LegacySimulcastLayerLimit/Disabled/", - .input_resolution = {.width = 100, .height = 100}, - .number_of_streams = 3}), - 3u); -} - -struct OverrideStreamSettingsTestParams { - std::string field_trials; - Resolution input_resolution; - VideoEncoderConfig::ContentType content_type; - std::vector requested_streams; - std::vector expected_streams; -}; - -class EncoderStreamFactoryOverrideStreamSettinsTest - : public ::testing::TestWithParam< - std::tuple> {}; - -TEST_P(EncoderStreamFactoryOverrideStreamSettinsTest, OverrideStreamSettings) { - OverrideStreamSettingsTestParams test_params = std::get<0>(GetParam()); - VideoEncoderConfig encoder_config; - encoder_config.codec_type = std::get<1>(GetParam()); - encoder_config.number_of_streams = test_params.requested_streams.size(); - encoder_config.simulcast_layers = test_params.requested_streams; - encoder_config.content_type = test_params.content_type; - auto streams = - CreateEncoderStreams(ExplicitKeyValueConfig(test_params.field_trials), - test_params.input_resolution, encoder_config); - ASSERT_EQ(streams.size(), test_params.expected_streams.size()); - for (size_t i = 0; i < streams.size(); ++i) { - SCOPED_TRACE(i); - const VideoStream& expected = test_params.expected_streams[i]; - EXPECT_EQ(streams[i].width, expected.width); - EXPECT_EQ(streams[i].height, expected.height); - EXPECT_EQ(streams[i].max_framerate, expected.max_framerate); - EXPECT_EQ(streams[i].min_bitrate_bps, expected.min_bitrate_bps); - EXPECT_EQ(streams[i].target_bitrate_bps, expected.target_bitrate_bps); - EXPECT_EQ(streams[i].max_bitrate_bps, expected.max_bitrate_bps); - EXPECT_EQ(streams[i].scalability_mode, expected.scalability_mode); - } -} - -INSTANTIATE_TEST_SUITE_P( - Screencast, - EncoderStreamFactoryOverrideStreamSettinsTest, - Combine(Values(OverrideStreamSettingsTestParams{ - .input_resolution = {.width = 1920, .height = 1080}, - .content_type = VideoEncoderConfig::ContentType::kScreen, - .requested_streams = - {CreateVideoStream( - {.max_framerate_fps = 5, - .max_bitrate_bps = 420'000, - .scale_resolution_down_by = 1, - .scalability_mode = ScalabilityMode::kL1T2}), - CreateVideoStream( - {.max_framerate_fps = 30, - .max_bitrate_bps = 2'500'000, - .scale_resolution_down_by = 1, - .scalability_mode = ScalabilityMode::kL1T2})}, - .expected_streams = - {CreateVideoStream( - {.width = 1920, - .height = 1080, - .max_framerate_fps = 5, - .min_bitrate_bps = 30'000, - .target_bitrate_bps = 420'000, - .max_bitrate_bps = 420'000, - .scalability_mode = ScalabilityMode::kL1T2}), - CreateVideoStream( - {.width = 1920, - .height = 1080, - .max_framerate_fps = 30, - .min_bitrate_bps = 800'000, - .target_bitrate_bps = 2'500'000, - .max_bitrate_bps = 2'500'000, - .scalability_mode = ScalabilityMode::kL1T2})}}), - Values(VideoCodecType::kVideoCodecVP8, - VideoCodecType::kVideoCodecAV1))); } // namespace webrtc diff --git a/video/config/simulcast.cc b/video/config/simulcast.cc index a655c2e9d6..809845762d 100644 --- a/video/config/simulcast.cc +++ b/video/config/simulcast.cc @@ -229,25 +229,31 @@ SimulcastFormat InterpolateSimulcastFormat( } std::vector GetNormalSimulcastLayers( - rtc::ArrayView resolutions, + size_t layer_count, + int width, + int height, bool temporal_layers_supported, bool base_heavy_tl3_rate_alloc, const webrtc::FieldTrialsView& trials, webrtc::VideoCodecType codec) { + std::vector layers(layer_count); const bool enable_lowres_bitrate_interpolation = EnableLowresBitrateInterpolation(trials); const int num_temporal_layers = temporal_layers_supported ? kDefaultNumTemporalLayers : 1; + // Format width and height has to be divisible by |2 ^ num_simulcast_layers - + // 1|. + width = NormalizeSimulcastSize(trials, width, layer_count); + height = NormalizeSimulcastSize(trials, height, layer_count); // Add simulcast streams, from highest resolution (`s` = num_simulcast_layers // -1) to lowest resolution at `s` = 0. - std::vector layers(resolutions.size()); - for (size_t s = 0; s < resolutions.size(); ++s) { - layers[s].width = resolutions[s].width; - layers[s].height = resolutions[s].height; + for (size_t s = layer_count - 1;; --s) { + layers[s].width = width; + layers[s].height = height; layers[s].num_temporal_layers = num_temporal_layers; SimulcastFormat interpolated_format = InterpolateSimulcastFormat( - layers[s].width, layers[s].height, /*max_roundup_rate=*/absl::nullopt, + width, height, /*max_roundup_rate=*/absl::nullopt, enable_lowres_bitrate_interpolation, codec); layers[s].max_bitrate_bps = interpolated_format.max_bitrate.bps(); @@ -287,6 +293,13 @@ std::vector GetNormalSimulcastLayers( std::max(layers[s].min_bitrate_bps, layers[s].target_bitrate_bps); layers[s].max_framerate = kDefaultVideoMaxFramerate; + + width /= 2; + height /= 2; + + if (s == 0) { + break; + } } return layers; @@ -349,12 +362,10 @@ std::vector GetScreenshareLayers( return layers; } -} // namespace - -size_t LimitSimulcastLayerCount(size_t min_num_layers, - size_t max_num_layers, - int width, +size_t LimitSimulcastLayerCount(int width, int height, + size_t need_layers, + size_t layer_count, const webrtc::FieldTrialsView& trials, webrtc::VideoCodecType codec) { if (!absl::StartsWith(trials.Lookup(kUseLegacySimulcastLayerLimitFieldTrial), @@ -367,19 +378,36 @@ size_t LimitSimulcastLayerCount(size_t min_num_layers, webrtc::ParseFieldTrial({&max_ratio}, trials.Lookup("WebRTC-SimulcastLayerLimitRoundUp")); - size_t reduced_num_layers = - std::max(min_num_layers, - InterpolateSimulcastFormat( - width, height, max_ratio.GetOptional(), - /*enable_lowres_bitrate_interpolation=*/false, codec) - .max_layers); - if (max_num_layers > reduced_num_layers) { + size_t adaptive_layer_count = std::max( + need_layers, InterpolateSimulcastFormat( + width, height, max_ratio.GetOptional(), + /*enable_lowres_bitrate_interpolation=*/false, codec) + .max_layers); + if (layer_count > adaptive_layer_count) { RTC_LOG(LS_WARNING) << "Reducing simulcast layer count from " - << max_num_layers << " to " << reduced_num_layers; - return reduced_num_layers; + << layer_count << " to " << adaptive_layer_count; + layer_count = adaptive_layer_count; } } - return max_num_layers; + return layer_count; +} + +} // namespace + +// Round size to nearest simulcast-friendly size. +// Simulcast stream width and height must both be dividable by +// |2 ^ (simulcast_layers - 1)|. +int NormalizeSimulcastSize(const FieldTrialsView& field_trials, + int size, + size_t simulcast_layers) { + int base2_exponent = static_cast(simulcast_layers) - 1; + const absl::optional experimental_base2_exponent = + webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent(field_trials); + if (experimental_base2_exponent && + (size > (1 << *experimental_base2_exponent))) { + base2_exponent = *experimental_base2_exponent; + } + return ((size >> base2_exponent) << base2_exponent); } void BoostMaxSimulcastLayer(webrtc::DataRate max_bitrate, @@ -411,21 +439,32 @@ webrtc::DataRate GetTotalMaxBitrate( } std::vector GetSimulcastConfig( - rtc::ArrayView resolutions, + size_t min_layers, + size_t max_layers, + int width, + int height, bool is_screenshare_with_conference_mode, bool temporal_layers_supported, const webrtc::FieldTrialsView& trials, webrtc::VideoCodecType codec) { - RTC_DCHECK(!resolutions.empty()); + RTC_DCHECK_LE(min_layers, max_layers); + RTC_DCHECK(max_layers > 1 || is_screenshare_with_conference_mode); const bool base_heavy_tl3_rate_alloc = webrtc::RateControlSettings(trials).Vp8BaseHeavyTl3RateAllocation(); if (is_screenshare_with_conference_mode) { - return GetScreenshareLayers( - resolutions.size(), resolutions[0].width, resolutions[0].height, - temporal_layers_supported, base_heavy_tl3_rate_alloc, trials); + return GetScreenshareLayers(max_layers, width, height, + temporal_layers_supported, + base_heavy_tl3_rate_alloc, trials); } else { - return GetNormalSimulcastLayers(resolutions, temporal_layers_supported, + // 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, min_layers, max_layers, + trials, codec); + + return GetNormalSimulcastLayers(max_layers, width, height, + temporal_layers_supported, base_heavy_tl3_rate_alloc, trials, codec); } } diff --git a/video/config/simulcast.h b/video/config/simulcast.h index 75b4e28417..480cf4bb78 100644 --- a/video/config/simulcast.h +++ b/video/config/simulcast.h @@ -15,10 +15,8 @@ #include -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/units/data_rate.h" -#include "api/video/resolution.h" #include "video/config/video_encoder_config.h" namespace cricket { @@ -32,19 +30,17 @@ webrtc::DataRate GetTotalMaxBitrate( void BoostMaxSimulcastLayer(webrtc::DataRate max_bitrate, std::vector* layers); -// Returns number of simulcast streams. The value depends on the resolution and -// is restricted to the range from `min_num_layers` to `max_num_layers`, -// inclusive. -size_t LimitSimulcastLayerCount(size_t min_num_layers, - size_t max_num_layers, - int width, - int height, - const webrtc::FieldTrialsView& trials, - webrtc::VideoCodecType codec); +// Round size to nearest simulcast-friendly size +int NormalizeSimulcastSize(const webrtc::FieldTrialsView& field_trials, + int size, + size_t simulcast_layers); // Gets simulcast settings. std::vector GetSimulcastConfig( - rtc::ArrayView resolutions, + size_t min_layers, + size_t max_layers, + int width, + int height, bool is_screenshare_with_conference_mode, bool temporal_layers_supported, const webrtc::FieldTrialsView& trials, diff --git a/video/config/simulcast_unittest.cc b/video/config/simulcast_unittest.cc index 3dd4417374..87859208e7 100644 --- a/video/config/simulcast_unittest.cc +++ b/video/config/simulcast_unittest.cc @@ -12,13 +12,12 @@ #include "media/base/media_constants.h" #include "test/explicit_key_value_config.h" -#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { + using test::ExplicitKeyValueConfig; -using ::testing::SizeIs; constexpr bool kScreenshare = true; constexpr int kDefaultTemporalLayers = 3; // Value from simulcast.cc. @@ -37,19 +36,6 @@ const std::vector GetSimulcastBitrates720p() { streams[2].max_bitrate_bps = 2500000; return streams; } - -// Creates a vector of resolutions scaled down with 1/2 factor ordered from low -// to high. -std::vector CreateResolutions(int max_width, - int max_height, - int num_streams) { - std::vector resolutions(num_streams); - for (int i = 0; i < num_streams; ++i) { - resolutions[i].width = max_width >> (num_streams - i - 1); - resolutions[i].height = max_height >> (num_streams - i - 1); - } - return resolutions; -} } // namespace TEST(SimulcastTest, TotalMaxBitrateIsZeroForNoStreams) { @@ -97,12 +83,13 @@ TEST(SimulcastTest, GetConfig) { const std::vector kExpected = GetSimulcastBitrates720p(); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(1280, 720, kMaxLayers), !kScreenshare, true, trials, + kMinLayers, kMaxLayers, 1280, 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(320u, streams[0].width); EXPECT_EQ(180u, streams[0].height); EXPECT_EQ(640u, streams[1].width); @@ -127,12 +114,12 @@ TEST(SimulcastTest, GetConfigWithBaseHeavyVP8TL3RateAllocation) { const std::vector kExpected = GetSimulcastBitrates720p(); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(1280, 720, kMaxLayers), !kScreenshare, true, trials, + kMinLayers, kMaxLayers, 1280, 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); 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), streams[0].target_bitrate_bps); @@ -148,27 +135,176 @@ TEST(SimulcastTest, GetConfigWithBaseHeavyVP8TL3RateAllocation) { TEST(SimulcastTest, GetConfigWithLimitedMaxLayers) { ExplicitKeyValueConfig trials(""); + const size_t kMinLayers = 1; const size_t kMaxLayers = 2; std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(1280, 720, kMaxLayers), !kScreenshare, true, trials, + kMinLayers, kMaxLayers, 1280, 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(640u, streams[0].width); EXPECT_EQ(360u, streams[0].height); EXPECT_EQ(1280u, streams[1].width); EXPECT_EQ(720u, streams[1].height); } +TEST(SimulcastTest, GetConfigWithLimitedMaxLayersForResolution) { + ExplicitKeyValueConfig trials( + "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 800, 600, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + EXPECT_EQ(2u, streams.size()); + EXPECT_EQ(400u, streams[0].width); + EXPECT_EQ(300u, streams[0].height); + EXPECT_EQ(800u, streams[1].width); + EXPECT_EQ(600u, streams[1].height); +} + +TEST(SimulcastTest, GetConfigWithLowResolutionScreenshare) { + ExplicitKeyValueConfig trials( + "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 100, 100, kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + // Simulcast streams number is never decreased for screenshare, + // even for very low resolution. + EXPECT_GT(streams.size(), 1u); +} + +TEST(SimulcastTest, GetConfigWithNotLimitedMaxLayersForResolution) { + ExplicitKeyValueConfig trials( + "WebRTC-LegacySimulcastLayerLimit/Disabled/"); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 800, 600, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + EXPECT_EQ(kMaxLayers, streams.size()); + EXPECT_EQ(200u, streams[0].width); + EXPECT_EQ(150u, streams[0].height); + EXPECT_EQ(400u, streams[1].width); + EXPECT_EQ(300u, streams[1].height); + EXPECT_EQ(800u, streams[2].width); + EXPECT_EQ(600u, streams[2].height); +} + +TEST(SimulcastTest, GetConfigWithNormalizedResolution) { + ExplicitKeyValueConfig trials(""); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 2; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 640 + 1, 360 + 1, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + // Must be divisible by |2 ^ (num_layers - 1)|. + EXPECT_EQ(kMaxLayers, streams.size()); + EXPECT_EQ(320u, streams[0].width); + EXPECT_EQ(180u, streams[0].height); + EXPECT_EQ(640u, streams[1].width); + EXPECT_EQ(360u, streams[1].height); +} + +TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy4) { + ExplicitKeyValueConfig trials( + "WebRTC-NormalizeSimulcastResolution/Enabled-2/"); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 2; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 709, 501, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + // Must be divisible by |2 ^ 2|. + EXPECT_EQ(kMaxLayers, streams.size()); + EXPECT_EQ(354u, streams[0].width); + EXPECT_EQ(250u, streams[0].height); + EXPECT_EQ(708u, streams[1].width); + EXPECT_EQ(500u, streams[1].height); +} + +TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy8) { + ExplicitKeyValueConfig trials( + "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + + const size_t kMinLayers = 1; + const size_t kMaxLayers = 2; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 709, 501, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + // Must be divisible by |2 ^ 3|. + EXPECT_EQ(kMaxLayers, streams.size()); + EXPECT_EQ(352u, streams[0].width); + EXPECT_EQ(248u, streams[0].height); + EXPECT_EQ(704u, streams[1].width); + EXPECT_EQ(496u, streams[1].height); +} + +TEST(SimulcastTest, GetConfigForLegacyLayerLimit) { + ExplicitKeyValueConfig trials( + "WebRTC-LegacySimulcastLayerLimit/Enabled/"); + + const size_t kMinLayers = 1; + const int kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( + kMinLayers, kMaxLayers, 320, 180, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(1u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); +} + +TEST(SimulcastTest, GetConfigForLegacyLayerLimitWithRequiredHD) { + ExplicitKeyValueConfig 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, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); + + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); +} + TEST(SimulcastTest, GetConfigForScreenshareSimulcast) { ExplicitKeyValueConfig trials(""); + const size_t kMinLayers = 1; + const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - std::vector{{.width = 1400, .height = 800}, - {.width = 1400, .height = 800}, - {.width = 1400, .height = 800}}, - kScreenshare, true, trials, webrtc::kVideoCodecVP8); + kMinLayers, kMaxLayers, 1400, 800, kScreenshare, true, trials, + webrtc::kVideoCodecVP8); - EXPECT_THAT(streams, SizeIs(2)); + EXPECT_GT(streams.size(), 1u); for (size_t i = 0; i < streams.size(); ++i) { EXPECT_EQ(1400u, streams[i].width) << "Screen content never scaled."; EXPECT_EQ(800u, streams[i].height) << "Screen content never scaled."; @@ -184,29 +320,35 @@ TEST(SimulcastTest, GetConfigForScreenshareSimulcast) { TEST(SimulcastTest, GetConfigForScreenshareSimulcastWithLimitedMaxLayers) { ExplicitKeyValueConfig trials(""); + const size_t kMinLayers = 1; + const size_t kMaxLayers = 1; std::vector streams = cricket::GetSimulcastConfig( - std::vector{{.width = 1400, .height = 800}}, kScreenshare, - true, trials, webrtc::kVideoCodecVP8); - EXPECT_THAT(streams, SizeIs(1)); + kMinLayers, kMaxLayers, 1400, 800, kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + + EXPECT_EQ(kMaxLayers, streams.size()); } TEST(SimulcastTest, AveragesBitratesForNonStandardResolution) { ExplicitKeyValueConfig trials(""); + const size_t kMinLayers = 1; + const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - std::vector{{.width = 900, .height = 800}}, !kScreenshare, - true, trials, webrtc::kVideoCodecVP8); + kMinLayers, kMaxLayers, 900, 800, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(1)); - EXPECT_EQ(900u, streams[0].width); - EXPECT_EQ(800u, streams[0].height); - EXPECT_EQ(1850000, streams[0].max_bitrate_bps); - EXPECT_EQ(1850000, streams[0].target_bitrate_bps); - EXPECT_EQ(475000, streams[0].min_bitrate_bps); + EXPECT_EQ(kMaxLayers, streams.size()); + EXPECT_EQ(900u, streams[2].width); + EXPECT_EQ(800u, streams[2].height); + EXPECT_EQ(1850000, streams[2].max_bitrate_bps); + EXPECT_EQ(1850000, streams[2].target_bitrate_bps); + EXPECT_EQ(475000, streams[2].min_bitrate_bps); } TEST(SimulcastTest, BitratesForCloseToStandardResolution) { ExplicitKeyValueConfig trials(""); + const size_t kMinLayers = 1; const size_t kMaxLayers = 3; // Resolution very close to 720p in number of pixels const size_t kWidth = 1280; @@ -214,10 +356,10 @@ TEST(SimulcastTest, BitratesForCloseToStandardResolution) { const std::vector kExpectedNear = GetSimulcastBitrates720p(); std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(kWidth, kHeight, kMaxLayers), !kScreenshare, true, - trials, webrtc::kVideoCodecVP8); + kMinLayers, kMaxLayers, kWidth, kHeight, !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + EXPECT_EQ(kMaxLayers, streams.size()); EXPECT_EQ(kWidth, streams[2].width); EXPECT_EQ(kHeight, streams[2].height); for (size_t i = 0; i < streams.size(); ++i) { @@ -237,20 +379,25 @@ TEST(SimulcastTest, MaxLayersWithRoundUpDisabled) { const size_t kMinLayers = 1; const int kMaxLayers = 3; - size_t num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 960, 540, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 3u); + std::vector streams; + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 540, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); // <960x540: 2 layers - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 960, 539, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 270, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 539, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 270, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); // <480x270: 1 layer - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 269, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 1u); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 269, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(1u, streams.size()); } TEST(SimulcastTest, MaxLayersWithDefaultRoundUpRatio) { @@ -259,26 +406,33 @@ TEST(SimulcastTest, MaxLayersWithDefaultRoundUpRatio) { const size_t kMinLayers = 1; const int kMaxLayers = 3; - size_t num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 960, 540, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 3u); + std::vector streams; + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 540, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); // Lowest cropped height where max layers from higher resolution is used. - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 960, 512, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 3u); - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 960, 508, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 270, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 512, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(3u, streams.size()); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 508, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 270, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); // Lowest cropped height where max layers from higher resolution is used. - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 256, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 254, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 1u); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 256, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 254, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(1u, streams.size()); } TEST(SimulcastTest, MaxLayersWithRoundUpRatio) { @@ -288,16 +442,20 @@ TEST(SimulcastTest, MaxLayersWithRoundUpRatio) { const size_t kMinLayers = 1; const int kMaxLayers = 3; - size_t num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 270, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); + std::vector streams; + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 270, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); // Lowest cropped height where max layers from higher resolution is used. - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 252, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 2u); - num_layers = cricket::LimitSimulcastLayerCount( - kMinLayers, kMaxLayers, 480, 250, trials, webrtc::kVideoCodecVP8); - EXPECT_EQ(num_layers, 1u); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 252, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(2u, streams.size()); + streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 250, + !kScreenshare, true, trials, + webrtc::kVideoCodecVP8); + EXPECT_EQ(1u, streams.size()); } TEST(SimulcastTest, BitratesInterpolatedForResBelow180p) { @@ -307,10 +465,10 @@ TEST(SimulcastTest, BitratesInterpolatedForResBelow180p) { const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/960, /*max_height=*/540, kMaxLayers), + /* min_layers = */ 1, kMaxLayers, /* width = */ 960, /* height = */ 540, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + ASSERT_EQ(streams.size(), kMaxLayers); EXPECT_EQ(240u, streams[0].width); EXPECT_EQ(135u, streams[0].height); EXPECT_EQ(streams[0].max_bitrate_bps, 112500); @@ -324,10 +482,10 @@ TEST(SimulcastTest, BitratesConsistentForVerySmallRes) { "WebRTC-LowresSimulcastBitrateInterpolation/Enabled/"); std::vector streams = cricket::GetSimulcastConfig( - std::vector{{.width = 1, .height = 1}}, !kScreenshare, true, - trials, webrtc::kVideoCodecVP8); + /* min_layers = */ 1, /* max_layers = */ 3, /* width = */ 1, + /* height = */ 1, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(1)); + ASSERT_TRUE(!streams.empty()); EXPECT_EQ(1u, streams[0].width); EXPECT_EQ(1u, streams[0].height); EXPECT_EQ(streams[0].max_bitrate_bps, 30000); @@ -342,10 +500,10 @@ TEST(SimulcastTest, const size_t kMaxLayers = 3; std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/960, /*max_height=*/540, kMaxLayers), + /* min_layers = */ 1, kMaxLayers, /* width = */ 960, /* height = */ 540, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + ASSERT_EQ(streams.size(), kMaxLayers); EXPECT_EQ(240u, streams[0].width); EXPECT_EQ(135u, streams[0].height); EXPECT_EQ(streams[0].max_bitrate_bps, 200000); @@ -358,15 +516,15 @@ TEST(SimulcastTest, BitratesBasedOnCodec) { const size_t kMaxLayers = 3; std::vector streams_vp8 = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/1280, /*max_height=*/720, kMaxLayers), - !kScreenshare, true, trials, webrtc::kVideoCodecVP8); + /* min_layers = */ 1, /* max_layers = */ 3, /* width = */ 1280, + /* height = */ 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP8); std::vector streams_vp9 = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/1280, /*max_height=*/720, kMaxLayers), - !kScreenshare, true, trials, webrtc::kVideoCodecVP9); + /* min_layers = */ 1, /* max_layers = */ 3, /* width = */ 1280, + /* height = */ 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP9); - ASSERT_THAT(streams_vp8, SizeIs(kMaxLayers)); - ASSERT_THAT(streams_vp9, SizeIs(kMaxLayers)); + ASSERT_EQ(streams_vp8.size(), kMaxLayers); + ASSERT_EQ(streams_vp9.size(), kMaxLayers); EXPECT_EQ(streams_vp9[0].width, streams_vp8[0].width); EXPECT_EQ(streams_vp9[0].height, streams_vp8[0].height); @@ -390,11 +548,12 @@ TEST(SimulcastTest, BitratesForVP9) { ExplicitKeyValueConfig trials(""); const size_t kMaxLayers = 3; + std::vector streams = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/1280, /*max_height=*/720, kMaxLayers), + /* min_layers = */ 1, kMaxLayers, /* width = */ 1280, /* height = */ 720, !kScreenshare, true, trials, webrtc::kVideoCodecVP9); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + ASSERT_EQ(streams.size(), kMaxLayers); EXPECT_EQ(1280u, streams[2].width); EXPECT_EQ(720u, streams[2].height); EXPECT_EQ(streams[2].max_bitrate_bps, 1524000); @@ -402,10 +561,10 @@ TEST(SimulcastTest, BitratesForVP9) { EXPECT_EQ(streams[2].min_bitrate_bps, 481000); streams = cricket::GetSimulcastConfig( - CreateResolutions(/*max_width=*/1276, /*max_height=*/716, kMaxLayers), + /* min_layers = */ 1, kMaxLayers, /* width = */ 1276, /* height = */ 716, !kScreenshare, true, trials, webrtc::kVideoCodecVP9); - ASSERT_THAT(streams, SizeIs(kMaxLayers)); + ASSERT_EQ(streams.size(), kMaxLayers); EXPECT_EQ(1276u, streams[2].width); EXPECT_EQ(716u, streams[2].height); EXPECT_NEAR(streams[2].max_bitrate_bps, 1524000, 20000); diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc index 9aade033d2..ac4aa3ef0c 100644 --- a/video/video_stream_encoder_unittest.cc +++ b/video/video_stream_encoder_unittest.cc @@ -2666,11 +2666,9 @@ TEST_P(ResolutionAlignmentTest, SinkWantsAlignmentApplied) { config.video_stream_factory = nullptr; video_stream_encoder_->ConfigureEncoder(std::move(config), kMaxPayloadLength); - // We can get up to 3 streams of 1280x720 resolution each in this test. Make - // available bitrate large enough to get all streams encoded. - const DataRate kAvailableBitrate = 3 * kSimulcastTargetBitrate; video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( - kAvailableBitrate, kAvailableBitrate, kAvailableBitrate, 0, 0, 0); + kSimulcastTargetBitrate, kSimulcastTargetBitrate, kSimulcastTargetBitrate, + 0, 0, 0); // Wait for all layers before triggering event. sink_.SetNumExpectedLayers(num_streams);