/* * Copyright 2021 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "video/adaptation/bitrate_constraint.h" #include #include #include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/video_encoder.h" #include "call/adaptation/encoder_settings.h" #include "call/adaptation/test/fake_frame_rate_provider.h" #include "call/adaptation/video_source_restrictions.h" #include "call/adaptation/video_stream_input_state_provider.h" #include "test/gtest.h" namespace webrtc { namespace { const VideoSourceRestrictions k180p{/*max_pixels_per_frame=*/320 * 180, /*target_pixels_per_frame=*/320 * 180, /*max_frame_rate=*/30}; const VideoSourceRestrictions k360p{/*max_pixels_per_frame=*/640 * 360, /*target_pixels_per_frame=*/640 * 360, /*max_frame_rate=*/30}; const VideoSourceRestrictions k720p{/*max_pixels_per_frame=*/1280 * 720, /*target_pixels_per_frame=*/1280 * 720, /*max_frame_rate=*/30}; struct TestParams { bool active; absl::optional scalability_mode; }; void FillCodecConfig(VideoCodec* video_codec, VideoEncoderConfig* encoder_config, int width_px, int height_px, const std::vector& params, bool svc) { size_t num_layers = params.size(); video_codec->codecType = kVideoCodecVP8; video_codec->numberOfSimulcastStreams = svc ? 1 : num_layers; encoder_config->number_of_streams = svc ? 1 : num_layers; encoder_config->simulcast_layers.resize(num_layers); for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { int layer_width_px = width_px >> (num_layers - 1 - layer_idx); int layer_height_px = height_px >> (num_layers - 1 - layer_idx); if (params[layer_idx].scalability_mode) video_codec->SetScalabilityMode(*params[layer_idx].scalability_mode); video_codec->simulcastStream[layer_idx].active = params[layer_idx].active; video_codec->simulcastStream[layer_idx].width = layer_width_px; video_codec->simulcastStream[layer_idx].height = layer_height_px; encoder_config->simulcast_layers[layer_idx].scalability_mode = params[layer_idx].scalability_mode; encoder_config->simulcast_layers[layer_idx].active = params[layer_idx].active; encoder_config->simulcast_layers[layer_idx].width = layer_width_px; encoder_config->simulcast_layers[layer_idx].height = layer_height_px; } } constexpr int kStartBitrateBps360p = 500000; constexpr int kStartBitrateBps720p = 1000000; VideoEncoder::EncoderInfo MakeEncoderInfo() { VideoEncoder::EncoderInfo encoder_info; encoder_info.resolution_bitrate_limits = { {640 * 360, kStartBitrateBps360p, 0, 5000000}, {1280 * 720, kStartBitrateBps720p, 0, 5000000}, {1920 * 1080, 2000000, 0, 5000000}}; return encoder_info; } } // namespace class BitrateConstraintTest : public ::testing::Test { public: BitrateConstraintTest() : frame_rate_provider_(), input_state_provider_(&frame_rate_provider_) {} protected: void OnEncoderSettingsUpdated(int width_px, int height_px, const std::vector& params, bool svc = false) { VideoCodec video_codec; VideoEncoderConfig encoder_config; FillCodecConfig(&video_codec, &encoder_config, width_px, height_px, params, svc); EncoderSettings encoder_settings(MakeEncoderInfo(), std::move(encoder_config), video_codec); bitrate_constraint_.OnEncoderSettingsUpdated(encoder_settings); input_state_provider_.OnEncoderSettingsChanged(encoder_settings); } FakeFrameRateProvider frame_rate_provider_; VideoStreamInputStateProvider input_state_provider_; BitrateConstraint bitrate_constraint_; }; TEST_F(BitrateConstraintTest, AdaptUpAllowedAtSinglecastIfBitrateIsEnough) { OnEncoderSettingsUpdated(/*width_px=*/640, /*height_px=*/360, {{.active = true}}); bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpDisallowedAtSinglecastIfBitrateIsNotEnough) { OnEncoderSettingsUpdated(/*width_px=*/640, /*height_px=*/360, {{.active = true}}); // 1 bps less than needed for 720p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p - 1); EXPECT_FALSE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedAtSinglecastIfBitrateIsEnoughForOneSpatialLayer) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL1T1}}); bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpDisallowedAtSinglecastIfBitrateIsNotEnoughForOneSpatialLayer) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL1T1}}); // 1 bps less than needed for 720p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p - 1); EXPECT_FALSE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedAtSinglecastIfBitrateIsNotEnoughForMultipleSpatialLayers) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL2T1}}); // 1 bps less than needed for 720p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p - 1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedAtSinglecastUpperLayerActiveIfBitrateIsEnough) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = false, .scalability_mode = ScalabilityMode::kL2T1}, {.active = true}}); bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpDisallowedAtSinglecastUpperLayerActiveIfBitrateIsNotEnough) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = false, .scalability_mode = ScalabilityMode::kL2T1}, {.active = true}}); // 1 bps less than needed for 720p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p - 1); EXPECT_FALSE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedLowestActiveIfBitrateIsNotEnough) { OnEncoderSettingsUpdated(/*width_px=*/640, /*height_px=*/360, {{.active = true}, {.active = false}}); // 1 bps less than needed for 360p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps360p - 1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k180p, /*restrictions_after=*/k360p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedLowestActiveIfBitrateIsNotEnoughForOneSpatialLayer) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL1T2}, {.active = false}}); // 1 bps less than needed for 360p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps360p - 1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k180p, /*restrictions_after=*/k360p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedLowestActiveIfBitrateIsEnoughForOneSpatialLayerSvc) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL1T1}, {.active = false}}, /*svc=*/true); bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps360p); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k180p, /*restrictions_after=*/k360p)); } TEST_F(BitrateConstraintTest, AdaptUpDisallowedLowestActiveIfBitrateIsNotEnoughForOneSpatialLayerSvc) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL1T1}, {.active = false}}, /*svc=*/true); // 1 bps less than needed for 360p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps360p - 1); EXPECT_FALSE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k180p, /*restrictions_after=*/k360p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedLowestActiveIfBitrateIsNotEnoughForTwoSpatialLayersSvc) { OnEncoderSettingsUpdated( /*width_px=*/640, /*height_px=*/360, {{.active = true, .scalability_mode = ScalabilityMode::kL2T1}, {.active = false}}, /*svc=*/true); // 1 bps less than needed for 360p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps360p - 1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k180p, /*restrictions_after=*/k360p)); } TEST_F(BitrateConstraintTest, AdaptUpAllowedAtSimulcastIfBitrateIsNotEnough) { OnEncoderSettingsUpdated(/*width_px=*/640, /*height_px=*/360, {{.active = true}, {.active = true}}); // 1 bps less than needed for 720p. bitrate_constraint_.OnEncoderTargetBitrateUpdated(kStartBitrateBps720p - 1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k720p)); } TEST_F(BitrateConstraintTest, AdaptUpInFpsAllowedAtNoResolutionIncreaseIfBitrateIsNotEnough) { OnEncoderSettingsUpdated(/*width_px=*/640, /*height_px=*/360, {{.active = true}}); bitrate_constraint_.OnEncoderTargetBitrateUpdated(1); EXPECT_TRUE(bitrate_constraint_.IsAdaptationUpAllowed( input_state_provider_.InputState(), /*restrictions_before=*/k360p, /*restrictions_after=*/k360p)); } } // namespace webrtc