diff --git a/call/BUILD.gn b/call/BUILD.gn index 8769efce90..c35b54b882 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -206,6 +206,7 @@ rtc_source_set("bitrate_allocator") { "../system_wrappers", "../system_wrappers:field_trial", "../system_wrappers:metrics", + "//third_party/abseil-cpp/absl/algorithm:container", ] } diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc index 9fe4c54254..a5259f240c 100644 --- a/call/bitrate_allocator.cc +++ b/call/bitrate_allocator.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/algorithm/container.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "rtc_base/checks.h" @@ -122,7 +123,7 @@ void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps, ObserverAllocation stable_bitrate_allocation = AllocateBitrates(stable_target_bitrate_bps); - for (auto& config : bitrate_observer_configs_) { + for (auto& config : allocatable_tracks_) { uint32_t allocated_bitrate = allocation[config.observer]; uint32_t allocated_bandwidth = bandwidth_allocation[config.observer]; uint32_t allocated_stable_target_rate = @@ -142,17 +143,19 @@ void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps, // The protection bitrate is an estimate based on the ratio between media // and protection used before this observer was muted. uint32_t predicted_protection_bps = - (1.0 - config.media_ratio) * config.min_bitrate_bps; + (1.0 - config.media_ratio) * config.config.min_bitrate_bps; RTC_LOG(LS_INFO) << "Pausing observer " << config.observer << " with configured min bitrate " - << config.min_bitrate_bps << " and current estimate of " - << target_bitrate_bps << " and protection bitrate " + << config.config.min_bitrate_bps + << " and current estimate of " << target_bitrate_bps + << " and protection bitrate " << predicted_protection_bps; } else if (allocated_bitrate > 0 && config.allocated_bitrate_bps == 0) { if (target_bitrate_bps > 0) ++num_pause_events_; RTC_LOG(LS_INFO) << "Resuming observer " << config.observer - << ", configured min bitrate " << config.min_bitrate_bps + << ", configured min bitrate " + << config.config.min_bitrate_bps << ", current allocation " << allocated_bitrate << " and protection bitrate " << protection_bitrate; } @@ -170,20 +173,14 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, RTC_DCHECK_RUN_ON(&sequenced_checker_); RTC_DCHECK_GT(config.bitrate_priority, 0); RTC_DCHECK(std::isnormal(config.bitrate_priority)); - auto it = FindObserverConfig(observer); - + auto it = absl::c_find_if( + allocatable_tracks_, + [observer](const auto& config) { return config.observer == observer; }); // Update settings if the observer already exists, create a new one otherwise. - if (it != bitrate_observer_configs_.end()) { - it->min_bitrate_bps = config.min_bitrate_bps; - it->max_bitrate_bps = config.max_bitrate_bps; - it->pad_up_bitrate_bps = config.pad_up_bitrate_bps; - it->enforce_min_bitrate = config.enforce_min_bitrate; - it->bitrate_priority = config.bitrate_priority; + if (it != allocatable_tracks_.end()) { + it->config = config; } else { - bitrate_observer_configs_.push_back( - ObserverConfig(observer, config.min_bitrate_bps, config.max_bitrate_bps, - config.pad_up_bitrate_bps, config.priority_bitrate_bps, - config.enforce_min_bitrate, config.bitrate_priority)); + allocatable_tracks_.push_back(AllocatableTrack(observer, config)); } if (last_target_bps_ > 0) { @@ -194,7 +191,7 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, AllocateBitrates(last_bandwidth_bps_); ObserverAllocation stable_bitrate_allocation = AllocateBitrates(last_stable_target_bps_); - for (auto& config : bitrate_observer_configs_) { + for (auto& config : allocatable_tracks_) { uint32_t allocated_bitrate = allocation[config.observer]; uint32_t allocated_stable_bitrate = stable_bitrate_allocation[config.observer]; @@ -232,16 +229,16 @@ void BitrateAllocator::UpdateAllocationLimits() { uint32_t total_requested_padding_bitrate = 0; uint32_t total_requested_min_bitrate = 0; uint32_t total_requested_max_bitrate = 0; - for (const auto& config : bitrate_observer_configs_) { - uint32_t stream_padding = config.pad_up_bitrate_bps; - if (config.enforce_min_bitrate) { - total_requested_min_bitrate += config.min_bitrate_bps; + for (const auto& config : allocatable_tracks_) { + uint32_t stream_padding = config.config.pad_up_bitrate_bps; + if (config.config.enforce_min_bitrate) { + total_requested_min_bitrate += config.config.min_bitrate_bps; } else if (config.allocated_bitrate_bps == 0) { stream_padding = std::max(config.MinBitrateWithHysteresis(), stream_padding); } total_requested_padding_bitrate += stream_padding; - total_requested_max_bitrate += config.max_bitrate_bps; + total_requested_max_bitrate += config.config.max_bitrate_bps; } if (total_requested_padding_bitrate == total_requested_padding_bitrate_ && @@ -267,10 +264,12 @@ void BitrateAllocator::UpdateAllocationLimits() { void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) { RTC_DCHECK_RUN_ON(&sequenced_checker_); - - auto it = FindObserverConfig(observer); - if (it != bitrate_observer_configs_.end()) { - bitrate_observer_configs_.erase(it); + for (auto it = allocatable_tracks_.begin(); it != allocatable_tracks_.end(); + ++it) { + if (it->observer == observer) { + allocatable_tracks_.erase(it); + break; + } } UpdateAllocationLimits(); @@ -279,45 +278,26 @@ void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) { int BitrateAllocator::GetStartBitrate( BitrateAllocatorObserver* observer) const { RTC_DCHECK_RUN_ON(&sequenced_checker_); - const auto& it = FindObserverConfig(observer); - if (it == bitrate_observer_configs_.end()) { + auto it = absl::c_find_if( + allocatable_tracks_, + [observer](const auto& config) { return config.observer == observer; }); + if (it == allocatable_tracks_.end()) { // This observer hasn't been added yet, just give it its fair share. return last_non_zero_bitrate_bps_ / - static_cast((bitrate_observer_configs_.size() + 1)); + static_cast((allocatable_tracks_.size() + 1)); } else if (it->allocated_bitrate_bps == -1) { // This observer hasn't received an allocation yet, so do the same. return last_non_zero_bitrate_bps_ / - static_cast(bitrate_observer_configs_.size()); + static_cast(allocatable_tracks_.size()); } else { // This observer already has an allocation. return it->allocated_bitrate_bps; } } -BitrateAllocator::ObserverConfigs::const_iterator -BitrateAllocator::FindObserverConfig( - const BitrateAllocatorObserver* observer) const { - for (auto it = bitrate_observer_configs_.begin(); - it != bitrate_observer_configs_.end(); ++it) { - if (it->observer == observer) - return it; - } - return bitrate_observer_configs_.end(); -} - -BitrateAllocator::ObserverConfigs::iterator -BitrateAllocator::FindObserverConfig(const BitrateAllocatorObserver* observer) { - for (auto it = bitrate_observer_configs_.begin(); - it != bitrate_observer_configs_.end(); ++it) { - if (it->observer == observer) - return it; - } - return bitrate_observer_configs_.end(); -} - BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates( uint32_t bitrate) const { - if (bitrate_observer_configs_.empty()) + if (allocatable_tracks_.empty()) return ObserverAllocation(); if (bitrate == 0) @@ -325,9 +305,9 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates( uint32_t sum_min_bitrates = 0; uint32_t sum_max_bitrates = 0; - for (const auto& observer_config : bitrate_observer_configs_) { - sum_min_bitrates += observer_config.min_bitrate_bps; - sum_max_bitrates += observer_config.max_bitrate_bps; + for (const auto& observer_config : allocatable_tracks_) { + sum_min_bitrates += observer_config.config.min_bitrate_bps; + sum_max_bitrates += observer_config.config.max_bitrate_bps; } // Not enough for all observers to get an allocation, allocate according to: @@ -348,7 +328,7 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates( BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation() const { ObserverAllocation allocation; - for (const auto& observer_config : bitrate_observer_configs_) + for (const auto& observer_config : allocatable_tracks_) allocation[observer_config.observer] = 0; return allocation; } @@ -359,10 +339,10 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( // Start by allocating bitrate to observers enforcing a min bitrate, hence // remaining_bitrate might turn negative. int64_t remaining_bitrate = bitrate; - for (const auto& observer_config : bitrate_observer_configs_) { + for (const auto& observer_config : allocatable_tracks_) { int32_t allocated_bitrate = 0; - if (observer_config.enforce_min_bitrate) - allocated_bitrate = observer_config.min_bitrate_bps; + if (observer_config.config.enforce_min_bitrate) + allocated_bitrate = observer_config.config.min_bitrate_bps; allocation[observer_config.observer] = allocated_bitrate; remaining_bitrate -= allocated_bitrate; @@ -370,8 +350,8 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( // Allocate bitrate to all previously active streams. if (remaining_bitrate > 0) { - for (const auto& observer_config : bitrate_observer_configs_) { - if (observer_config.enforce_min_bitrate || + for (const auto& observer_config : allocatable_tracks_) { + if (observer_config.config.enforce_min_bitrate || observer_config.LastAllocatedBitrate() == 0) continue; @@ -385,7 +365,7 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( // Allocate bitrate to previously paused streams. if (remaining_bitrate > 0) { - for (const auto& observer_config : bitrate_observer_configs_) { + for (const auto& observer_config : allocatable_tracks_) { if (observer_config.LastAllocatedBitrate() != 0) continue; @@ -402,7 +382,7 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( if (remaining_bitrate > 0) DistributeBitrateEvenly(remaining_bitrate, false, 1, &allocation); - RTC_DCHECK_EQ(allocation.size(), bitrate_observer_configs_.size()); + RTC_DCHECK_EQ(allocation.size(), allocatable_tracks_.size()); return allocation; } @@ -418,18 +398,20 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation( uint32_t sum_min_bitrates) const { ObserverAllocation allocation; ObserverAllocation observers_capacities; - for (const auto& observer_config : bitrate_observer_configs_) { - allocation[observer_config.observer] = observer_config.min_bitrate_bps; + for (const auto& observer_config : allocatable_tracks_) { + allocation[observer_config.observer] = + observer_config.config.min_bitrate_bps; observers_capacities[observer_config.observer] = - observer_config.max_bitrate_bps - observer_config.min_bitrate_bps; + observer_config.config.max_bitrate_bps - + observer_config.config.min_bitrate_bps; } bitrate -= sum_min_bitrates; // TODO(srte): Implement fair sharing between prioritized streams, currently // they are treated on a first come first serve basis. - for (const auto& observer_config : bitrate_observer_configs_) { - int64_t priority_margin = observer_config.priority_bitrate_bps - + for (const auto& observer_config : allocatable_tracks_) { + int64_t priority_margin = observer_config.config.priority_bitrate_bps - allocation[observer_config.observer]; if (priority_margin > 0 && bitrate > 0) { int64_t extra_bitrate = std::min(priority_margin, bitrate); @@ -453,24 +435,26 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation( uint32_t sum_max_bitrates) const { ObserverAllocation allocation; - for (const auto& observer_config : bitrate_observer_configs_) { - allocation[observer_config.observer] = observer_config.max_bitrate_bps; - bitrate -= observer_config.max_bitrate_bps; + for (const auto& observer_config : allocatable_tracks_) { + allocation[observer_config.observer] = + observer_config.config.max_bitrate_bps; + bitrate -= observer_config.config.max_bitrate_bps; } DistributeBitrateEvenly(bitrate, true, transmission_max_bitrate_multiplier_, &allocation); return allocation; } -uint32_t BitrateAllocator::ObserverConfig::LastAllocatedBitrate() const { +uint32_t BitrateAllocator::AllocatableTrack::LastAllocatedBitrate() const { // Return the configured minimum bitrate for newly added observers, to avoid // requiring an extra high bitrate for the observer to get an allocated // bitrate. - return allocated_bitrate_bps == -1 ? min_bitrate_bps : allocated_bitrate_bps; + return allocated_bitrate_bps == -1 ? config.min_bitrate_bps + : allocated_bitrate_bps; } -uint32_t BitrateAllocator::ObserverConfig::MinBitrateWithHysteresis() const { - uint32_t min_bitrate = min_bitrate_bps; +uint32_t BitrateAllocator::AllocatableTrack::MinBitrateWithHysteresis() const { + uint32_t min_bitrate = config.min_bitrate_bps; if (LastAllocatedBitrate() == 0) { min_bitrate += std::max(static_cast(kToggleFactor * min_bitrate), kMinToggleBitrateBps); @@ -492,14 +476,14 @@ void BitrateAllocator::DistributeBitrateEvenly( bool include_zero_allocations, int max_multiplier, ObserverAllocation* allocation) const { - RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size()); + RTC_DCHECK_EQ(allocation->size(), allocatable_tracks_.size()); ObserverSortingMap list_max_bitrates; - for (const auto& observer_config : bitrate_observer_configs_) { + for (const auto& observer_config : allocatable_tracks_) { if (include_zero_allocations || allocation->at(observer_config.observer) != 0) { - list_max_bitrates.insert(std::pair( - observer_config.max_bitrate_bps, &observer_config)); + list_max_bitrates.insert(std::pair( + observer_config.config.max_bitrate_bps, &observer_config)); } } auto it = list_max_bitrates.begin(); @@ -530,9 +514,9 @@ bool BitrateAllocator::EnoughBitrateForAllObservers( uint32_t extra_bitrate_per_observer = (bitrate - sum_min_bitrates) / - static_cast(bitrate_observer_configs_.size()); - for (const auto& observer_config : bitrate_observer_configs_) { - if (observer_config.min_bitrate_bps + extra_bitrate_per_observer < + static_cast(allocatable_tracks_.size()); + for (const auto& observer_config : allocatable_tracks_) { + if (observer_config.config.min_bitrate_bps + extra_bitrate_per_observer < observer_config.MinBitrateWithHysteresis()) { return false; } @@ -544,8 +528,8 @@ void BitrateAllocator::DistributeBitrateRelatively( uint32_t remaining_bitrate, const ObserverAllocation& observers_capacities, ObserverAllocation* allocation) const { - RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size()); - RTC_DCHECK_EQ(observers_capacities.size(), bitrate_observer_configs_.size()); + RTC_DCHECK_EQ(allocation->size(), allocatable_tracks_.size()); + RTC_DCHECK_EQ(observers_capacities.size(), allocatable_tracks_.size()); struct PriorityRateObserverConfig { PriorityRateObserverConfig(BitrateAllocatorObserver* allocation_key, @@ -574,11 +558,12 @@ void BitrateAllocator::DistributeBitrateRelatively( double bitrate_priority_sum = 0; std::vector priority_rate_observers; - for (const auto& observer_config : bitrate_observer_configs_) { + for (const auto& observer_config : allocatable_tracks_) { uint32_t capacity_bps = observers_capacities.at(observer_config.observer); - priority_rate_observers.emplace_back(observer_config.observer, capacity_bps, - observer_config.bitrate_priority); - bitrate_priority_sum += observer_config.bitrate_priority; + priority_rate_observers.emplace_back( + observer_config.observer, capacity_bps, + observer_config.config.bitrate_priority); + bitrate_priority_sum += observer_config.config.bitrate_priority; } // Iterate in the order observers can be allocated their full capacity. diff --git a/call/bitrate_allocator.h b/call/bitrate_allocator.h index bfa9a0afba..769ab0faef 100644 --- a/call/bitrate_allocator.h +++ b/call/bitrate_allocator.h @@ -41,21 +41,23 @@ class BitrateAllocatorObserver { }; // Struct describing parameters for how a media stream should get bitrate -// allocated to it. |min_bitrate_bps| = 0 equals no min bitrate. -// |max_bitrate_bps| = 0 equals no max bitrate. -// |enforce_min_bitrate| = 'true' will allocate at least |min_bitrate_bps| for -// this observer, even if the BWE is too low, 'false' will allocate 0 to -// the observer if BWE doesn't allow |min_bitrate_bps|. -// Note that |observer|->OnBitrateUpdated() will be called -// within the scope of this method with the current rtt, fraction_loss and -// available bitrate and that the bitrate in OnBitrateUpdated will be zero if -// the |observer| is currently not allowed to send data. +// allocated to it. + struct MediaStreamAllocationConfig { + // Minimum bitrate supported by track. 0 equals no min bitrate. uint32_t min_bitrate_bps; + // Maximum bitrate supported by track. 0 equals no max bitrate. uint32_t max_bitrate_bps; uint32_t pad_up_bitrate_bps; int64_t priority_bitrate_bps; + // True means track may not be paused by allocating 0 bitrate will allocate at + // least |min_bitrate_bps| for this observer, even if the BWE is too low, + // false will allocate 0 to the observer if BWE doesn't allow + // |min_bitrate_bps|. bool enforce_min_bitrate; + // The amount of bitrate allocated to this observer relative to all other + // observers. If an observer has twice the bitrate_priority of other + // observers, it should be allocated twice the bitrate above its min. double bitrate_priority; }; @@ -104,6 +106,10 @@ class BitrateAllocator : public BitrateAllocatorInterface { // Set the configuration used by the bandwidth management. // |observer| updates bitrates if already in use. // |config| is the configuration to use for allocation. + // Note that |observer|->OnBitrateUpdated() will be called + // within the scope of this method with the current rtt, fraction_loss and + // available bitrate and that the bitrate in OnBitrateUpdated will be zero if + // the |observer| is currently not allowed to send data. void AddObserver(BitrateAllocatorObserver* observer, MediaStreamAllocationConfig config) override; @@ -116,42 +122,17 @@ class BitrateAllocator : public BitrateAllocatorInterface { int GetStartBitrate(BitrateAllocatorObserver* observer) const override; private: - struct ObserverConfig { - ObserverConfig(BitrateAllocatorObserver* observer, - uint32_t min_bitrate_bps, - uint32_t max_bitrate_bps, - uint32_t pad_up_bitrate_bps, - int64_t priority_bitrate_bps, - bool enforce_min_bitrate, - double bitrate_priority) + struct AllocatableTrack { + AllocatableTrack(BitrateAllocatorObserver* observer, + MediaStreamAllocationConfig allocation_config) : observer(observer), - pad_up_bitrate_bps(pad_up_bitrate_bps), - priority_bitrate_bps(priority_bitrate_bps), + config(allocation_config), allocated_bitrate_bps(-1), - media_ratio(1.0), - bitrate_priority(bitrate_priority), - min_bitrate_bps(min_bitrate_bps), - max_bitrate_bps(max_bitrate_bps), - enforce_min_bitrate(enforce_min_bitrate) {} - + media_ratio(1.0) {} BitrateAllocatorObserver* observer; - uint32_t pad_up_bitrate_bps; - int64_t priority_bitrate_bps; + MediaStreamAllocationConfig config; int64_t allocated_bitrate_bps; double media_ratio; // Part of the total bitrate used for media [0.0, 1.0]. - // The amount of bitrate allocated to this observer relative to all other - // observers. If an observer has twice the bitrate_priority of other - // observers, it should be allocated twice the bitrate above its min. - double bitrate_priority; - - // Minimum bitrate supported by track. - uint32_t min_bitrate_bps; - - // Maximum bitrate supported by track. - uint32_t max_bitrate_bps; - - // True means track may not be paused by allocating 0 bitrate. - bool enforce_min_bitrate; uint32_t LastAllocatedBitrate() const; // The minimum bitrate required by this observer, including @@ -163,14 +144,7 @@ class BitrateAllocator : public BitrateAllocatorInterface { // calls LimitObserver::OnAllocationLimitsChanged. void UpdateAllocationLimits() RTC_RUN_ON(&sequenced_checker_); - typedef std::vector ObserverConfigs; - ObserverConfigs::const_iterator FindObserverConfig( - const BitrateAllocatorObserver* observer) const - RTC_RUN_ON(&sequenced_checker_); - ObserverConfigs::iterator FindObserverConfig( - const BitrateAllocatorObserver* observer) RTC_RUN_ON(&sequenced_checker_); - - typedef std::multimap ObserverSortingMap; + typedef std::multimap ObserverSortingMap; typedef std::map ObserverAllocation; ObserverAllocation AllocateBitrates(uint32_t bitrate) const @@ -227,7 +201,8 @@ class BitrateAllocator : public BitrateAllocatorInterface { SequenceChecker sequenced_checker_; LimitObserver* const limit_observer_ RTC_GUARDED_BY(&sequenced_checker_); // Stored in a list to keep track of the insertion order. - ObserverConfigs bitrate_observer_configs_ RTC_GUARDED_BY(&sequenced_checker_); + std::vector allocatable_tracks_ + RTC_GUARDED_BY(&sequenced_checker_); uint32_t last_target_bps_ RTC_GUARDED_BY(&sequenced_checker_); uint32_t last_stable_target_bps_ RTC_GUARDED_BY(&sequenced_checker_); uint32_t last_bandwidth_bps_ RTC_GUARDED_BY(&sequenced_checker_);