|  | /* | 
|  | *  Copyright (c) 2018 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 "call/rtp_bitrate_configurator.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <optional> | 
|  |  | 
|  | #include "api/transport/bitrate_settings.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | using std::nullopt; | 
|  |  | 
|  | class RtpBitrateConfiguratorTest : public ::testing::Test { | 
|  | public: | 
|  | RtpBitrateConfiguratorTest() | 
|  | : configurator_(new RtpBitrateConfigurator(BitrateConstraints())) {} | 
|  | std::unique_ptr<RtpBitrateConfigurator> configurator_; | 
|  | void UpdateConfigMatches(BitrateConstraints bitrate_config, | 
|  | std::optional<int> min_bitrate_bps, | 
|  | std::optional<int> start_bitrate_bps, | 
|  | std::optional<int> max_bitrate_bps) { | 
|  | std::optional<BitrateConstraints> result = | 
|  | configurator_->UpdateWithSdpParameters(bitrate_config); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | if (start_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->start_bitrate_bps, start_bitrate_bps); | 
|  | if (min_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->min_bitrate_bps, min_bitrate_bps); | 
|  | if (max_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->max_bitrate_bps, max_bitrate_bps); | 
|  | } | 
|  |  | 
|  | void UpdateMaskMatches(BitrateSettings bitrate_mask, | 
|  | std::optional<int> min_bitrate_bps, | 
|  | std::optional<int> start_bitrate_bps, | 
|  | std::optional<int> max_bitrate_bps) { | 
|  | std::optional<BitrateConstraints> result = | 
|  | configurator_->UpdateWithClientPreferences(bitrate_mask); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | if (start_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->start_bitrate_bps, start_bitrate_bps); | 
|  | if (min_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->min_bitrate_bps, min_bitrate_bps); | 
|  | if (max_bitrate_bps.has_value()) | 
|  | EXPECT_EQ(result->max_bitrate_bps, max_bitrate_bps); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigWithValidConfigReturnsNewConfig) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 1; | 
|  | bitrate_config.start_bitrate_bps = 2; | 
|  | bitrate_config.max_bitrate_bps = 3; | 
|  |  | 
|  | UpdateConfigMatches(bitrate_config, 1, 2, 3); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigWithDifferentMinReturnsNewConfig) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 10; | 
|  | bitrate_config.start_bitrate_bps = 20; | 
|  | bitrate_config.max_bitrate_bps = 30; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | bitrate_config.min_bitrate_bps = 11; | 
|  | UpdateConfigMatches(bitrate_config, 11, -1, 30); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, | 
|  | NewConfigWithDifferentStartReturnsNewConfig) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 10; | 
|  | bitrate_config.start_bitrate_bps = 20; | 
|  | bitrate_config.max_bitrate_bps = 30; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | bitrate_config.start_bitrate_bps = 21; | 
|  | UpdateConfigMatches(bitrate_config, 10, 21, 30); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigWithDifferentMaxReturnsNewConfig) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 10; | 
|  | bitrate_config.start_bitrate_bps = 20; | 
|  | bitrate_config.max_bitrate_bps = 30; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | bitrate_config.max_bitrate_bps = 31; | 
|  | UpdateConfigMatches(bitrate_config, 10, -1, 31); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigWithSameConfigElidesSecondCall) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 1; | 
|  | bitrate_config.start_bitrate_bps = 2; | 
|  | bitrate_config.max_bitrate_bps = 3; | 
|  |  | 
|  | UpdateConfigMatches(bitrate_config, 1, 2, 3); | 
|  | EXPECT_FALSE( | 
|  | configurator_->UpdateWithSdpParameters(bitrate_config).has_value()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, | 
|  | NewConfigWithSameMinMaxAndNegativeStartElidesSecondCall) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 1; | 
|  | bitrate_config.start_bitrate_bps = 2; | 
|  | bitrate_config.max_bitrate_bps = 3; | 
|  |  | 
|  | UpdateConfigMatches(bitrate_config, 1, 2, 3); | 
|  |  | 
|  | bitrate_config.start_bitrate_bps = -1; | 
|  | EXPECT_FALSE( | 
|  | configurator_->UpdateWithSdpParameters(bitrate_config).has_value()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, BiggerMaskMinUsed) { | 
|  | BitrateSettings mask; | 
|  | mask.min_bitrate_bps = 1234; | 
|  | UpdateMaskMatches(mask, *mask.min_bitrate_bps, nullopt, nullopt); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, BiggerConfigMinUsed) { | 
|  | BitrateSettings mask; | 
|  | mask.min_bitrate_bps = 1000; | 
|  | UpdateMaskMatches(mask, 1000, nullopt, nullopt); | 
|  |  | 
|  | BitrateConstraints config; | 
|  | config.min_bitrate_bps = 1234; | 
|  | UpdateConfigMatches(config, 1234, nullopt, nullopt); | 
|  | } | 
|  |  | 
|  | // The last call to set start should be used. | 
|  | TEST_F(RtpBitrateConfiguratorTest, LatestStartMaskPreferred) { | 
|  | BitrateSettings mask; | 
|  | mask.start_bitrate_bps = 1300; | 
|  | UpdateMaskMatches(mask, nullopt, *mask.start_bitrate_bps, nullopt); | 
|  |  | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.start_bitrate_bps = 1200; | 
|  |  | 
|  | UpdateConfigMatches(bitrate_config, nullopt, bitrate_config.start_bitrate_bps, | 
|  | nullopt); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, SmallerMaskMaxUsed) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | BitrateSettings mask; | 
|  | mask.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000; | 
|  |  | 
|  | UpdateMaskMatches(mask, nullopt, nullopt, *mask.max_bitrate_bps); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, SmallerConfigMaxUsed) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | BitrateSettings mask; | 
|  | mask.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000; | 
|  |  | 
|  | // Expect no return because nothing changes | 
|  | EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, MaskStartLessThanConfigMinClamped) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 2000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | BitrateSettings mask; | 
|  | mask.start_bitrate_bps = 1000; | 
|  | UpdateMaskMatches(mask, 2000, 2000, nullopt); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, MaskStartGreaterThanConfigMaxClamped) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.start_bitrate_bps = 2000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | BitrateSettings mask; | 
|  | mask.max_bitrate_bps = 1000; | 
|  |  | 
|  | UpdateMaskMatches(mask, nullopt, -1, 1000); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, MaskMinGreaterThanConfigMaxClamped) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.min_bitrate_bps = 2000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | BitrateSettings mask; | 
|  | mask.max_bitrate_bps = 1000; | 
|  |  | 
|  | UpdateMaskMatches(mask, 1000, nullopt, 1000); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, SettingMaskStartForcesUpdate) { | 
|  | BitrateSettings mask; | 
|  | mask.start_bitrate_bps = 1000; | 
|  |  | 
|  | // Config should be returned twice with the same params since | 
|  | // start_bitrate_bps is set. | 
|  | UpdateMaskMatches(mask, nullopt, 1000, nullopt); | 
|  | UpdateMaskMatches(mask, nullopt, 1000, nullopt); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigWithNoChangesDoesNotCallNewConfig) { | 
|  | BitrateConstraints config1; | 
|  | config1.min_bitrate_bps = 0; | 
|  | config1.start_bitrate_bps = 1000; | 
|  | config1.max_bitrate_bps = -1; | 
|  |  | 
|  | BitrateConstraints config2; | 
|  | config2.min_bitrate_bps = 0; | 
|  | config2.start_bitrate_bps = -1; | 
|  | config2.max_bitrate_bps = -1; | 
|  |  | 
|  | // The second call should not return anything because it doesn't | 
|  | // change any values. | 
|  | UpdateConfigMatches(config1, 0, 1000, -1); | 
|  | EXPECT_FALSE(configurator_->UpdateWithSdpParameters(config2).has_value()); | 
|  | } | 
|  |  | 
|  | // If config changes the max, but not the effective max, | 
|  | // new config shouldn't be returned, to avoid unnecessary encoder | 
|  | // reconfigurations. | 
|  | TEST_F(RtpBitrateConfiguratorTest, | 
|  | NewConfigNotReturnedWhenEffectiveMaxUnchanged) { | 
|  | BitrateConstraints config; | 
|  | config.min_bitrate_bps = 0; | 
|  | config.start_bitrate_bps = -1; | 
|  | config.max_bitrate_bps = 2000; | 
|  | UpdateConfigMatches(config, nullopt, nullopt, 2000); | 
|  |  | 
|  | // Reduce effective max to 1000 with the mask. | 
|  | BitrateSettings mask; | 
|  | mask.max_bitrate_bps = 1000; | 
|  | UpdateMaskMatches(mask, nullopt, nullopt, 1000); | 
|  |  | 
|  | // This leaves the effective max unchanged, so new config shouldn't be | 
|  | // returned again. | 
|  | config.max_bitrate_bps = 1000; | 
|  | EXPECT_FALSE(configurator_->UpdateWithSdpParameters(config).has_value()); | 
|  | } | 
|  |  | 
|  | // When the "start bitrate" mask is removed, new config shouldn't be returned | 
|  | // again, since nothing's changing. | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigNotReturnedWhenStartMaskRemoved) { | 
|  | BitrateSettings mask; | 
|  | mask.start_bitrate_bps = 1000; | 
|  | UpdateMaskMatches(mask, 0, 1000, -1); | 
|  |  | 
|  | mask.start_bitrate_bps.reset(); | 
|  | EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value()); | 
|  | } | 
|  |  | 
|  | // Test that if a new config is returned after BitrateSettings applies a | 
|  | // "start" value, the new config won't return that start value a | 
|  | // second time. | 
|  | TEST_F(RtpBitrateConfiguratorTest, NewConfigAfterBitrateConfigMaskWithStart) { | 
|  | BitrateSettings mask; | 
|  | mask.start_bitrate_bps = 1000; | 
|  | UpdateMaskMatches(mask, 0, 1000, -1); | 
|  |  | 
|  | BitrateConstraints config; | 
|  | config.min_bitrate_bps = 0; | 
|  | config.start_bitrate_bps = -1; | 
|  | config.max_bitrate_bps = 5000; | 
|  | // The start value isn't changing, so new config should be returned with | 
|  | // -1. | 
|  | UpdateConfigMatches(config, 0, -1, 5000); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpBitrateConfiguratorTest, | 
|  | NewConfigNotReturnedWhenClampedMinUnchanged) { | 
|  | BitrateConstraints bitrate_config; | 
|  | bitrate_config.start_bitrate_bps = 500; | 
|  | bitrate_config.max_bitrate_bps = 1000; | 
|  | configurator_.reset(new RtpBitrateConfigurator(bitrate_config)); | 
|  |  | 
|  | // Set min to 2000; it is clamped to the max (1000). | 
|  | BitrateSettings mask; | 
|  | mask.min_bitrate_bps = 2000; | 
|  | UpdateMaskMatches(mask, 1000, -1, 1000); | 
|  |  | 
|  | // Set min to 3000; the clamped value stays the same so nothing happens. | 
|  | mask.min_bitrate_bps = 3000; | 
|  | EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value()); | 
|  | } | 
|  | }  // namespace webrtc |