From df023aa6b434e6845fa06a0dc3be0d6268658063 Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Tue, 20 Feb 2018 19:38:37 +0100 Subject: [PATCH] Extracted bitrate configuration from call class. This separates the bitrate configuration logic from other call specific logic, creating a greater separation of concern and simplifying testing. The old call tests are kept but can be removed in the future reducing the dependencies on rtp transport control interface and congestion control in the system, which will simplify future refactoring. This also prepares for moving the bitrate configuration responsibility to the rtp transport controller in a later CL. Bug: webrtc:8415 Change-Id: I97126e89f30b63fc9b5d98a0bed1c29f18a6ed44 Reviewed-on: https://webrtc-review.googlesource.com/54401 Reviewed-by: Zach Stein Reviewed-by: Niels Moller Commit-Queue: Sebastian Jansson Cr-Commit-Position: refs/heads/master@{#22124} --- call/BUILD.gn | 15 ++ call/call.cc | 145 +++-------- call/rtp_bitrate_configurator.cc | 107 ++++++++ call/rtp_bitrate_configurator.h | 68 +++++ call/rtp_bitrate_configurator_unittest.cc | 299 ++++++++++++++++++++++ 5 files changed, 529 insertions(+), 105 deletions(-) create mode 100644 call/rtp_bitrate_configurator.cc create mode 100644 call/rtp_bitrate_configurator.h create mode 100644 call/rtp_bitrate_configurator_unittest.cc 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