diff --git a/call/BUILD.gn b/call/BUILD.gn index a3d23164ef..caa17b2bb1 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -100,6 +100,18 @@ rtc_source_set("rtp_sender") { ] } +rtc_source_set("bitrate_configurator") { + sources = [ + "rtp_bitrate_configurator.cc", + "rtp_bitrate_configurator.h", + ] + deps = [ + ":rtp_interfaces", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + ] +} + rtc_source_set("bitrate_allocator") { sources = [ "bitrate_allocator.cc", @@ -135,6 +147,7 @@ rtc_static_library("call") { deps = [ ":bitrate_allocator", + ":bitrate_configurator", ":call_interfaces", ":rtp_interfaces", ":rtp_receiver", @@ -201,12 +214,14 @@ if (rtc_include_tests) { "call_unittest.cc", "flexfec_receive_stream_unittest.cc", "rtcp_demuxer_unittest.cc", + "rtp_bitrate_configurator_unittest.cc", "rtp_demuxer_unittest.cc", "rtp_rtcp_demuxer_helper_unittest.cc", "rtx_receive_stream_unittest.cc", ] deps = [ ":bitrate_allocator", + ":bitrate_configurator", ":call", ":call_interfaces", ":mock_rtp_interfaces", diff --git a/call/call.cc b/call/call.cc index 750718a67d..fe5f20a8f2 100644 --- a/call/call.cc +++ b/call/call.cc @@ -24,6 +24,7 @@ #include "call/bitrate_allocator.h" #include "call/call.h" #include "call/flexfec_receive_stream_impl.h" +#include "call/rtp_bitrate_configurator.h" #include "call/rtp_stream_receiver_controller.h" #include "call/rtp_transport_controller_send.h" #include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" @@ -261,10 +262,6 @@ class Call : public webrtc::Call, void UpdateHistograms(); void UpdateAggregateNetworkState(); - // Applies update to the BitrateConstraints cached in |config_|, restarting - // bandwidth estimation from |new_start| if set. - void UpdateCurrentBitrateConfig(const rtc::Optional& new_start); - Clock* const clock_; const int num_cpu_cores_; @@ -372,14 +369,7 @@ class Call : public webrtc::Call, // and deleted before any other members. rtc::TaskQueue worker_queue_; - // The config mask set by SetBitrateConfigMask. - // 0 <= min <= start <= max - BitrateConstraintsMask bitrate_config_mask_; - - // The config set by SetBitrateConfig. - // min >= 0, start != 0, max == -1 || max > 0 - BitrateConstraints base_bitrate_config_; - + RtpBitrateConfigurator bitrate_configurator_; RTC_DISALLOW_COPY_AND_ASSIGN(Call); }; } // namespace internal @@ -446,21 +436,15 @@ Call::Call(const Call::Config& config, video_send_delay_stats_(new SendDelayStats(clock_)), start_ms_(clock_->TimeInMilliseconds()), worker_queue_("call_worker_queue"), - base_bitrate_config_(config.bitrate_config) { + bitrate_configurator_(config.bitrate_config) { RTC_DCHECK(config.event_log != nullptr); - RTC_DCHECK_GE(config.bitrate_config.min_bitrate_bps, 0); - RTC_DCHECK_GE(config.bitrate_config.start_bitrate_bps, - config.bitrate_config.min_bitrate_bps); - if (config.bitrate_config.max_bitrate_bps != -1) { - RTC_DCHECK_GE(config.bitrate_config.max_bitrate_bps, - config.bitrate_config.start_bitrate_bps); - } transport_send->RegisterNetworkObserver(this); transport_send_ = std::move(transport_send); transport_send_->OnNetworkAvailability(false); - transport_send_->SetBweBitrates(config_.bitrate_config.min_bitrate_bps, - config_.bitrate_config.start_bitrate_bps, - config_.bitrate_config.max_bitrate_bps); + transport_send_->SetBweBitrates( + bitrate_configurator_.GetConfig().min_bitrate_bps, + bitrate_configurator_.GetConfig().start_bitrate_bps, + bitrate_configurator_.GetConfig().max_bitrate_bps); call_stats_->RegisterStatsObserver(&receive_side_cc_); call_stats_->RegisterStatsObserver(transport_send_->GetCallStatsObserver()); @@ -957,80 +941,29 @@ Call::Stats Call::GetStats() const { void Call::SetBitrateConfig(const BitrateConstraints& bitrate_config) { TRACE_EVENT0("webrtc", "Call::SetBitrateConfig"); RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); - RTC_DCHECK_GE(bitrate_config.min_bitrate_bps, 0); - RTC_DCHECK_NE(bitrate_config.start_bitrate_bps, 0); - if (bitrate_config.max_bitrate_bps != -1) { - RTC_DCHECK_GT(bitrate_config.max_bitrate_bps, 0); - } - - rtc::Optional new_start; - // Only update the "start" bitrate if it's set, and different from the old - // value. In practice, this value comes from the x-google-start-bitrate codec - // parameter in SDP, and setting the same remote description twice shouldn't - // restart bandwidth estimation. - if (bitrate_config.start_bitrate_bps != -1 && - bitrate_config.start_bitrate_bps != - base_bitrate_config_.start_bitrate_bps) { - new_start.emplace(bitrate_config.start_bitrate_bps); - } - base_bitrate_config_ = bitrate_config; - UpdateCurrentBitrateConfig(new_start); -} - -void Call::SetBitrateConfigMask(const BitrateConstraintsMask& mask) { - TRACE_EVENT0("webrtc", "Call::SetBitrateConfigMask"); - RTC_DCHECK_CALLED_SEQUENTIALLY(&configuration_sequence_checker_); - - bitrate_config_mask_ = mask; - UpdateCurrentBitrateConfig(mask.start_bitrate_bps); -} - -void Call::UpdateCurrentBitrateConfig(const rtc::Optional& new_start) { - BitrateConstraints updated; - updated.min_bitrate_bps = - std::max(bitrate_config_mask_.min_bitrate_bps.value_or(0), - base_bitrate_config_.min_bitrate_bps); - - updated.max_bitrate_bps = - MinPositive(bitrate_config_mask_.max_bitrate_bps.value_or(-1), - base_bitrate_config_.max_bitrate_bps); - - // If the combined min ends up greater than the combined max, the max takes - // priority. - if (updated.max_bitrate_bps != -1 && - updated.min_bitrate_bps > updated.max_bitrate_bps) { - updated.min_bitrate_bps = updated.max_bitrate_bps; - } - - // If there is nothing to update (min/max unchanged, no new bandwidth - // estimation start value), return early. - if (updated.min_bitrate_bps == config_.bitrate_config.min_bitrate_bps && - updated.max_bitrate_bps == config_.bitrate_config.max_bitrate_bps && - !new_start) { - RTC_LOG(LS_VERBOSE) << "WebRTC.Call.UpdateCurrentBitrateConfig: " - << "nothing to update"; - return; - } - - if (new_start) { - // Clamp start by min and max. - updated.start_bitrate_bps = MinPositive( - std::max(*new_start, updated.min_bitrate_bps), updated.max_bitrate_bps); + rtc::Optional config = + bitrate_configurator_.UpdateWithSdpParameters(bitrate_config); + if (config.has_value()) { + transport_send_->SetBweBitrates(config->min_bitrate_bps, + config->start_bitrate_bps, + config->max_bitrate_bps); } else { - updated.start_bitrate_bps = -1; + RTC_LOG(LS_VERBOSE) << "WebRTC.Call.SetBitrateConfig: " + << "nothing to update"; } +} - RTC_LOG(INFO) << "WebRTC.Call.UpdateCurrentBitrateConfig: " - << "calling SetBweBitrates with args (" - << updated.min_bitrate_bps << ", " << updated.start_bitrate_bps - << ", " << updated.max_bitrate_bps << ")"; - transport_send_->SetBweBitrates(updated.min_bitrate_bps, - updated.start_bitrate_bps, - updated.max_bitrate_bps); - if (!new_start) { - updated.start_bitrate_bps = config_.bitrate_config.start_bitrate_bps; +void Call::SetBitrateConfigMask(const BitrateConstraintsMask& bitrate_mask) { + rtc::Optional config = + bitrate_configurator_.UpdateWithClientPreferences(bitrate_mask); + if (config.has_value()) { + transport_send_->SetBweBitrates(config->min_bitrate_bps, + config->start_bitrate_bps, + config->max_bitrate_bps); + } else { + RTC_LOG(LS_VERBOSE) << "WebRTC.Call.SetBitrateConfigMask: " + << "nothing to update"; } - config_.bitrate_config = updated; } void Call::SetBitrateAllocationStrategy( @@ -1134,19 +1067,21 @@ void Call::OnNetworkRouteChanged(const std::string& transport_name, } if (kv->second != network_route) { kv->second = network_route; - RTC_LOG(LS_INFO) - << "Network route changed on transport " << transport_name - << ": new local network id " << network_route.local_network_id - << " new remote network id " << network_route.remote_network_id - << " Reset bitrates to min: " << config_.bitrate_config.min_bitrate_bps - << " bps, start: " << config_.bitrate_config.start_bitrate_bps - << " bps, max: " << config_.bitrate_config.start_bitrate_bps - << " bps."; - RTC_DCHECK_GT(config_.bitrate_config.start_bitrate_bps, 0); + BitrateConstraints bitrate_config = bitrate_configurator_.GetConfig(); + RTC_LOG(LS_INFO) << "Network route changed on transport " << transport_name + << ": new local network id " + << network_route.local_network_id + << " new remote network id " + << network_route.remote_network_id + << " Reset bitrates to min: " + << bitrate_config.min_bitrate_bps + << " bps, start: " << bitrate_config.start_bitrate_bps + << " bps, max: " << bitrate_config.max_bitrate_bps + << " bps."; + RTC_DCHECK_GT(bitrate_config.start_bitrate_bps, 0); transport_send_->OnNetworkRouteChanged( - network_route, config_.bitrate_config.start_bitrate_bps, - config_.bitrate_config.min_bitrate_bps, - config_.bitrate_config.max_bitrate_bps); + network_route, bitrate_config.start_bitrate_bps, + bitrate_config.min_bitrate_bps, bitrate_config.max_bitrate_bps); } } diff --git a/call/rtp_bitrate_configurator.cc b/call/rtp_bitrate_configurator.cc new file mode 100644 index 0000000000..828b955ee3 --- /dev/null +++ b/call/rtp_bitrate_configurator.cc @@ -0,0 +1,107 @@ +/* + * 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 + +#include "rtc_base/checks.h" + +namespace webrtc { +RtpBitrateConfigurator::RtpBitrateConfigurator( + const BitrateConstraints& bitrate_config) + : bitrate_config_(bitrate_config), base_bitrate_config_(bitrate_config) { + RTC_DCHECK_GE(bitrate_config.min_bitrate_bps, 0); + RTC_DCHECK_GE(bitrate_config.start_bitrate_bps, + bitrate_config.min_bitrate_bps); + if (bitrate_config.max_bitrate_bps != -1) { + RTC_DCHECK_GE(bitrate_config.max_bitrate_bps, + bitrate_config.start_bitrate_bps); + } +} + +RtpBitrateConfigurator::~RtpBitrateConfigurator() = default; + +BitrateConstraints RtpBitrateConfigurator::GetConfig() const { + return bitrate_config_; +} + +rtc::Optional +RtpBitrateConfigurator::UpdateWithSdpParameters( + const BitrateConstraints& bitrate_config) { + RTC_DCHECK_GE(bitrate_config.min_bitrate_bps, 0); + RTC_DCHECK_NE(bitrate_config.start_bitrate_bps, 0); + if (bitrate_config.max_bitrate_bps != -1) { + RTC_DCHECK_GT(bitrate_config.max_bitrate_bps, 0); + } + + rtc::Optional new_start; + // Only update the "start" bitrate if it's set, and different from the old + // value. In practice, this value comes from the x-google-start-bitrate codec + // parameter in SDP, and setting the same remote description twice shouldn't + // restart bandwidth estimation. + if (bitrate_config.start_bitrate_bps != -1 && + bitrate_config.start_bitrate_bps != + base_bitrate_config_.start_bitrate_bps) { + new_start.emplace(bitrate_config.start_bitrate_bps); + } + base_bitrate_config_ = bitrate_config; + return UpdateConstraints(new_start); +} + +rtc::Optional +RtpBitrateConfigurator::UpdateWithClientPreferences( + const BitrateConstraintsMask& bitrate_mask) { + bitrate_config_mask_ = bitrate_mask; + return UpdateConstraints(bitrate_mask.start_bitrate_bps); +} + +rtc::Optional RtpBitrateConfigurator::UpdateConstraints( + const rtc::Optional& new_start) { + BitrateConstraints updated; + updated.min_bitrate_bps = + std::max(bitrate_config_mask_.min_bitrate_bps.value_or(0), + base_bitrate_config_.min_bitrate_bps); + + updated.max_bitrate_bps = + MinPositive(bitrate_config_mask_.max_bitrate_bps.value_or(-1), + base_bitrate_config_.max_bitrate_bps); + + // If the combined min ends up greater than the combined max, the max takes + // priority. + if (updated.max_bitrate_bps != -1 && + updated.min_bitrate_bps > updated.max_bitrate_bps) { + updated.min_bitrate_bps = updated.max_bitrate_bps; + } + + // If there is nothing to update (min/max unchanged, no new bandwidth + // estimation start value), return early. + if (updated.min_bitrate_bps == bitrate_config_.min_bitrate_bps && + updated.max_bitrate_bps == bitrate_config_.max_bitrate_bps && + !new_start) { + return rtc::nullopt; + } + + if (new_start) { + // Clamp start by min and max. + updated.start_bitrate_bps = MinPositive( + std::max(*new_start, updated.min_bitrate_bps), updated.max_bitrate_bps); + } else { + updated.start_bitrate_bps = -1; + } + BitrateConstraints config_to_return = updated; + if (!new_start) { + updated.start_bitrate_bps = bitrate_config_.start_bitrate_bps; + } + bitrate_config_ = updated; + return config_to_return; +} + +} // namespace webrtc diff --git a/call/rtp_bitrate_configurator.h b/call/rtp_bitrate_configurator.h new file mode 100644 index 0000000000..df84b5fad9 --- /dev/null +++ b/call/rtp_bitrate_configurator.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef CALL_RTP_BITRATE_CONFIGURATOR_H_ +#define CALL_RTP_BITRATE_CONFIGURATOR_H_ + +#include "call/bitrate_constraints.h" +#include "rtc_base/constructormagic.h" + +namespace webrtc { + +// RtpBitrateConfigurator calculates the bitrate configuration based on received +// remote configuration combined with local overrides. +class RtpBitrateConfigurator { + public: + explicit RtpBitrateConfigurator(const BitrateConstraints& bitrate_config); + ~RtpBitrateConfigurator(); + BitrateConstraints GetConfig() const; + + // The greater min and smaller max set by this and SetBitrateConfigMask will + // be used. The latest non-negative start value from either call will be used. + // Specifying a start bitrate (>0) will reset the current bitrate estimate. + // This is due to how the 'x-google-start-bitrate' flag is currently + // implemented. Passing -1 leaves the start bitrate unchanged. Behavior is not + // guaranteed for other negative values or 0. + // The optional return value is set with new configuration if it was updated. + rtc::Optional UpdateWithSdpParameters( + const BitrateConstraints& bitrate_config_); + + // The greater min and smaller max set by this and SetBitrateConfig will be + // used. The latest non-negative start value form either call will be used. + // Specifying a start bitrate will reset the current bitrate estimate. + // Assumes 0 <= min <= start <= max holds for set parameters. + // Update the bitrate configuration + // The optional return value is set with new configuration if it was updated. + rtc::Optional UpdateWithClientPreferences( + const BitrateConstraintsMask& bitrate_mask); + + private: + // Applies update to the BitrateConstraints cached in |config_|, resetting + // with |new_start| if set. + rtc::Optional UpdateConstraints( + const rtc::Optional& new_start); + + // Bitrate config used until valid bitrate estimates are calculated. Also + // used to cap total bitrate used. This comes from the remote connection. + BitrateConstraints bitrate_config_; + + // The config mask set by SetBitrateConfigMask. + // 0 <= min <= start <= max + BitrateConstraintsMask bitrate_config_mask_; + + // The config set by SetBitrateConfig. + // min >= 0, start != 0, max == -1 || max > 0 + BitrateConstraints base_bitrate_config_; + + RTC_DISALLOW_COPY_AND_ASSIGN(RtpBitrateConfigurator); +}; +} // namespace webrtc + +#endif // CALL_RTP_BITRATE_CONFIGURATOR_H_ diff --git a/call/rtp_bitrate_configurator_unittest.cc b/call/rtp_bitrate_configurator_unittest.cc new file mode 100644 index 0000000000..fd4f3a56d6 --- /dev/null +++ b/call/rtp_bitrate_configurator_unittest.cc @@ -0,0 +1,299 @@ +/* + * 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 + +#include "call/rtp_bitrate_configurator.h" +#include "test/gtest.h" + +namespace webrtc { +using rtc::nullopt; + +class RtpBitrateConfiguratorTest : public testing::Test { + public: + RtpBitrateConfiguratorTest() + : configurator_(new RtpBitrateConfigurator(BitrateConstraints())) {} + std::unique_ptr configurator_; + void UpdateConfigMatches(BitrateConstraints bitrate_config, + rtc::Optional min_bitrate_bps, + rtc::Optional start_bitrate_bps, + rtc::Optional max_bitrate_bps) { + rtc::Optional 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(BitrateConstraintsMask bitrate_mask, + rtc::Optional min_bitrate_bps, + rtc::Optional start_bitrate_bps, + rtc::Optional max_bitrate_bps) { + rtc::Optional 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) { + BitrateConstraintsMask mask; + mask.min_bitrate_bps = 1234; + UpdateMaskMatches(mask, *mask.min_bitrate_bps, nullopt, nullopt); +} + +TEST_F(RtpBitrateConfiguratorTest, BiggerConfigMinUsed) { + BitrateConstraintsMask 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) { + BitrateConstraintsMask 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)); + + BitrateConstraintsMask 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)); + + BitrateConstraintsMask 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)); + + BitrateConstraintsMask 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)); + + BitrateConstraintsMask 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)); + + BitrateConstraintsMask mask; + mask.max_bitrate_bps = 1000; + + UpdateMaskMatches(mask, 1000, nullopt, 1000); +} + +TEST_F(RtpBitrateConfiguratorTest, SettingMaskStartForcesUpdate) { + BitrateConstraintsMask 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. + BitrateConstraintsMask 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) { + BitrateConstraintsMask 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 BitrateConstraintsMask applies a +// "start" value, the new config won't return that start value a +// second time. +TEST_F(RtpBitrateConfiguratorTest, NewConfigAfterBitrateConfigMaskWithStart) { + BitrateConstraintsMask 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). + BitrateConstraintsMask 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