Converts const methods in BitrateAllocator to non-member functions.

Also cleaning up finished TransmissionMaxBitrateMultiplier trial.

Bug: webrtc:9883
Change-Id: I049f0e8e3051f91b57201e9ce750427018c74bfa
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/153889
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Ying Wang <yinwa@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29302}
This commit is contained in:
Sebastian Jansson 2019-09-24 19:47:26 +02:00 committed by Commit Bot
parent e32ae4f8fb
commit 538ca57b23
2 changed files with 331 additions and 368 deletions

View File

@ -29,6 +29,7 @@
namespace webrtc {
namespace {
using bitrate_allocator_impl::AllocatableTrack;
// Allow packets to be transmitted in up to 2 times max video bitrate if the
// bandwidth estimate allows it.
@ -50,6 +51,305 @@ double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
return media_bitrate / static_cast<double>(allocated_bitrate);
}
bool EnoughBitrateForAllObservers(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate,
uint32_t sum_min_bitrates) {
if (bitrate < sum_min_bitrates)
return false;
uint32_t extra_bitrate_per_observer =
(bitrate - sum_min_bitrates) /
static_cast<uint32_t>(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;
}
}
return true;
}
// Splits |bitrate| evenly to observers already in |allocation|.
// |include_zero_allocations| decides if zero allocations should be part of
// the distribution or not. The allowed max bitrate is |max_multiplier| x
// observer max bitrate.
void DistributeBitrateEvenly(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
std::map<BitrateAllocatorObserver*, int>* allocation) {
RTC_DCHECK_EQ(allocation->size(), allocatable_tracks.size());
std::multimap<uint32_t, const AllocatableTrack*> list_max_bitrates;
for (const auto& observer_config : allocatable_tracks) {
if (include_zero_allocations ||
allocation->at(observer_config.observer) != 0) {
list_max_bitrates.insert(
{observer_config.config.max_bitrate_bps, &observer_config});
}
}
auto it = list_max_bitrates.begin();
while (it != list_max_bitrates.end()) {
RTC_DCHECK_GT(bitrate, 0);
uint32_t extra_allocation =
bitrate / static_cast<uint32_t>(list_max_bitrates.size());
uint32_t total_allocation =
extra_allocation + allocation->at(it->second->observer);
bitrate -= extra_allocation;
if (total_allocation > max_multiplier * it->first) {
// There is more than we can fit for this observer, carry over to the
// remaining observers.
bitrate += total_allocation - max_multiplier * it->first;
total_allocation = max_multiplier * it->first;
}
// Finally, update the allocation for this observer.
allocation->at(it->second->observer) = total_allocation;
it = list_max_bitrates.erase(it);
}
}
// From the available |bitrate|, each observer will be allocated a
// proportional amount based upon its bitrate priority. If that amount is
// more than the observer's capacity, it will be allocated its capacity, and
// the excess bitrate is still allocated proportionally to other observers.
// Allocating the proportional amount means an observer with twice the
// bitrate_priority of another will be allocated twice the bitrate.
void DistributeBitrateRelatively(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t remaining_bitrate,
const std::map<BitrateAllocatorObserver*, int>& observers_capacities,
std::map<BitrateAllocatorObserver*, int>* allocation) {
RTC_DCHECK_EQ(allocation->size(), allocatable_tracks.size());
RTC_DCHECK_EQ(observers_capacities.size(), allocatable_tracks.size());
struct PriorityRateObserverConfig {
BitrateAllocatorObserver* allocation_key;
// The amount of bitrate bps that can be allocated to this observer.
int capacity_bps;
double bitrate_priority;
};
double bitrate_priority_sum = 0;
std::vector<PriorityRateObserverConfig> priority_rate_observers;
for (const auto& observer_config : allocatable_tracks) {
priority_rate_observers.push_back(PriorityRateObserverConfig{
observer_config.observer,
observers_capacities.at(observer_config.observer),
observer_config.config.bitrate_priority});
bitrate_priority_sum += observer_config.config.bitrate_priority;
}
// Iterate in the order observers can be allocated their full capacity.
// We want to sort by which observers will be allocated their full capacity
// first. By dividing each observer's capacity by its bitrate priority we
// are "normalizing" the capacity of an observer by the rate it will be
// filled. This is because the amount allocated is based upon bitrate
// priority. We allocate twice as much bitrate to an observer with twice the
// bitrate priority of another.
absl::c_sort(priority_rate_observers, [](const auto& a, const auto& b) {
return a.capacity_bps / a.bitrate_priority <
b.capacity_bps / b.bitrate_priority;
});
size_t i;
for (i = 0; i < priority_rate_observers.size(); ++i) {
const auto& priority_rate_observer = priority_rate_observers[i];
// We allocate the full capacity to an observer only if its relative
// portion from the remaining bitrate is sufficient to allocate its full
// capacity. This means we aren't greedily allocating the full capacity, but
// that it is only done when there is also enough bitrate to allocate the
// proportional amounts to all other observers.
double observer_share =
priority_rate_observer.bitrate_priority / bitrate_priority_sum;
double allocation_bps = observer_share * remaining_bitrate;
bool enough_bitrate = allocation_bps >= priority_rate_observer.capacity_bps;
if (!enough_bitrate)
break;
allocation->at(priority_rate_observer.allocation_key) +=
priority_rate_observer.capacity_bps;
remaining_bitrate -= priority_rate_observer.capacity_bps;
bitrate_priority_sum -= priority_rate_observer.bitrate_priority;
}
// From the remaining bitrate, allocate the proportional amounts to the
// observers that aren't allocated their max capacity.
for (; i < priority_rate_observers.size(); ++i) {
const auto& priority_rate_observer = priority_rate_observers[i];
double fraction_allocated =
priority_rate_observer.bitrate_priority / bitrate_priority_sum;
allocation->at(priority_rate_observer.allocation_key) +=
fraction_allocated * remaining_bitrate;
}
}
// Allocates bitrate to observers when there isn't enough to allocate the
// minimum to all observers.
std::map<BitrateAllocatorObserver*, int> LowRateAllocation(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate) {
std::map<BitrateAllocatorObserver*, int> allocation;
// 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 : allocatable_tracks) {
int32_t allocated_bitrate = 0;
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;
}
// Allocate bitrate to all previously active streams.
if (remaining_bitrate > 0) {
for (const auto& observer_config : allocatable_tracks) {
if (observer_config.config.enforce_min_bitrate ||
observer_config.LastAllocatedBitrate() == 0)
continue;
uint32_t required_bitrate = observer_config.MinBitrateWithHysteresis();
if (remaining_bitrate >= required_bitrate) {
allocation[observer_config.observer] = required_bitrate;
remaining_bitrate -= required_bitrate;
}
}
}
// Allocate bitrate to previously paused streams.
if (remaining_bitrate > 0) {
for (const auto& observer_config : allocatable_tracks) {
if (observer_config.LastAllocatedBitrate() != 0)
continue;
// Add a hysteresis to avoid toggling.
uint32_t required_bitrate = observer_config.MinBitrateWithHysteresis();
if (remaining_bitrate >= required_bitrate) {
allocation[observer_config.observer] = required_bitrate;
remaining_bitrate -= required_bitrate;
}
}
}
// Split a possible remainder evenly on all streams with an allocation.
if (remaining_bitrate > 0)
DistributeBitrateEvenly(allocatable_tracks, remaining_bitrate, false, 1,
&allocation);
RTC_DCHECK_EQ(allocation.size(), allocatable_tracks.size());
return allocation;
}
// Allocates bitrate to all observers when the available bandwidth is enough
// to allocate the minimum to all observers but not enough to allocate the
// max bitrate of each observer.
// Allocates the bitrate based on the bitrate priority of each observer. This
// bitrate priority defines the priority for bitrate to be allocated to that
// observer in relation to other observers. For example with two observers, if
// observer 1 had a bitrate_priority = 1.0, and observer 2 has a
// bitrate_priority = 2.0, the expected behavior is that observer 2 will be
// allocated twice the bitrate as observer 1 above the each observer's
// min_bitrate_bps values, until one of the observers hits its max_bitrate_bps.
std::map<BitrateAllocatorObserver*, int> NormalRateAllocation(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate,
uint32_t sum_min_bitrates) {
std::map<BitrateAllocatorObserver*, int> allocation;
std::map<BitrateAllocatorObserver*, int> observers_capacities;
for (const auto& observer_config : allocatable_tracks) {
allocation[observer_config.observer] =
observer_config.config.min_bitrate_bps;
observers_capacities[observer_config.observer] =
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 : 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<int64_t>(priority_margin, bitrate);
allocation[observer_config.observer] +=
rtc::dchecked_cast<int>(extra_bitrate);
observers_capacities[observer_config.observer] -= extra_bitrate;
bitrate -= extra_bitrate;
}
}
// From the remaining bitrate, allocate a proportional amount to each observer
// above the min bitrate already allocated.
if (bitrate > 0)
DistributeBitrateRelatively(allocatable_tracks, bitrate,
observers_capacities, &allocation);
return allocation;
}
// Allocates bitrate to observers when there is enough available bandwidth
// for all observers to be allocated their max bitrate.
std::map<BitrateAllocatorObserver*, int> MaxRateAllocation(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate,
uint32_t sum_max_bitrates) {
std::map<BitrateAllocatorObserver*, int> allocation;
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(allocatable_tracks, bitrate, true,
kTransmissionMaxBitrateMultiplier, &allocation);
return allocation;
}
// Allocates zero bitrate to all observers.
std::map<BitrateAllocatorObserver*, int> ZeroRateAllocation(
const std::vector<AllocatableTrack>& allocatable_tracks) {
std::map<BitrateAllocatorObserver*, int> allocation;
for (const auto& observer_config : allocatable_tracks)
allocation[observer_config.observer] = 0;
return allocation;
}
std::map<BitrateAllocatorObserver*, int> AllocateBitrates(
const std::vector<AllocatableTrack>& allocatable_tracks,
uint32_t bitrate) {
if (allocatable_tracks.empty())
return std::map<BitrateAllocatorObserver*, int>();
if (bitrate == 0)
return ZeroRateAllocation(allocatable_tracks);
uint32_t sum_min_bitrates = 0;
uint32_t sum_max_bitrates = 0;
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:
// enforced min bitrate -> allocated bitrate previous round -> restart paused
// streams.
if (!EnoughBitrateForAllObservers(allocatable_tracks, bitrate,
sum_min_bitrates))
return LowRateAllocation(allocatable_tracks, bitrate);
// All observers will get their min bitrate plus a share of the rest. This
// share is allocated to each observer based on its bitrate_priority.
if (bitrate <= sum_max_bitrates)
return NormalRateAllocation(allocatable_tracks, bitrate, sum_min_bitrates);
// All observers will get up to transmission_max_bitrate_multiplier_ x max.
return MaxRateAllocation(allocatable_tracks, bitrate, sum_max_bitrates);
}
} // namespace
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
@ -61,9 +361,7 @@ BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
last_rtt_(0),
last_bwe_period_ms_(1000),
num_pause_events_(0),
last_bwe_log_time_(0),
transmission_max_bitrate_multiplier_(
GetTransmissionMaxBitrateMultiplier()) {
last_bwe_log_time_(0) {
sequenced_checker_.Detach();
}
@ -77,20 +375,6 @@ void BitrateAllocator::UpdateStartRate(uint32_t start_rate_bps) {
last_non_zero_bitrate_bps_ = start_rate_bps;
}
// static
uint8_t BitrateAllocator::GetTransmissionMaxBitrateMultiplier() {
uint64_t multiplier = strtoul(webrtc::field_trial::FindFullName(
"WebRTC-TransmissionMaxBitrateMultiplier")
.c_str(),
nullptr, 10);
if (multiplier > 0 && multiplier <= kTransmissionMaxBitrateMultiplier) {
RTC_LOG(LS_INFO) << "TransmissionMaxBitrateMultiplier is set to "
<< multiplier;
return static_cast<uint8_t>(multiplier);
}
return kTransmissionMaxBitrateMultiplier;
}
void BitrateAllocator::OnNetworkEstimateChanged(TargetTransferRate msg) {
RTC_DCHECK_RUN_ON(&sequenced_checker_);
last_target_bps_ = msg.target_rate.bps();
@ -111,9 +395,9 @@ void BitrateAllocator::OnNetworkEstimateChanged(TargetTransferRate msg) {
last_bwe_log_time_ = now;
}
ObserverAllocation allocation = AllocateBitrates(last_target_bps_);
ObserverAllocation stable_bitrate_allocation =
AllocateBitrates(last_stable_target_bps_);
auto allocation = AllocateBitrates(allocatable_tracks_, last_target_bps_);
auto stable_bitrate_allocation =
AllocateBitrates(allocatable_tracks_, last_stable_target_bps_);
for (auto& config : allocatable_tracks_) {
uint32_t allocated_bitrate = allocation[config.observer];
@ -176,9 +460,9 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
if (last_target_bps_ > 0) {
// Calculate a new allocation and update all observers.
ObserverAllocation allocation = AllocateBitrates(last_target_bps_);
ObserverAllocation stable_bitrate_allocation =
AllocateBitrates(last_stable_target_bps_);
auto allocation = AllocateBitrates(allocatable_tracks_, last_target_bps_);
auto stable_bitrate_allocation =
AllocateBitrates(allocatable_tracks_, last_stable_target_bps_);
for (auto& config : allocatable_tracks_) {
uint32_t allocated_bitrate = allocation[config.observer];
uint32_t allocated_stable_bitrate =
@ -275,157 +559,8 @@ int BitrateAllocator::GetStartBitrate(
}
}
BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates(
uint32_t bitrate) const {
if (allocatable_tracks_.empty())
return ObserverAllocation();
if (bitrate == 0)
return ZeroRateAllocation();
uint32_t sum_min_bitrates = 0;
uint32_t sum_max_bitrates = 0;
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:
// enforced min bitrate -> allocated bitrate previous round -> restart paused
// streams.
if (!EnoughBitrateForAllObservers(bitrate, sum_min_bitrates))
return LowRateAllocation(bitrate);
// All observers will get their min bitrate plus a share of the rest. This
// share is allocated to each observer based on its bitrate_priority.
if (bitrate <= sum_max_bitrates)
return NormalRateAllocation(bitrate, sum_min_bitrates);
// All observers will get up to transmission_max_bitrate_multiplier_ x max.
return MaxRateAllocation(bitrate, sum_max_bitrates);
}
BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation()
uint32_t bitrate_allocator_impl::AllocatableTrack::LastAllocatedBitrate()
const {
ObserverAllocation allocation;
for (const auto& observer_config : allocatable_tracks_)
allocation[observer_config.observer] = 0;
return allocation;
}
BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
uint32_t bitrate) const {
ObserverAllocation allocation;
// 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 : allocatable_tracks_) {
int32_t allocated_bitrate = 0;
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;
}
// Allocate bitrate to all previously active streams.
if (remaining_bitrate > 0) {
for (const auto& observer_config : allocatable_tracks_) {
if (observer_config.config.enforce_min_bitrate ||
observer_config.LastAllocatedBitrate() == 0)
continue;
uint32_t required_bitrate = observer_config.MinBitrateWithHysteresis();
if (remaining_bitrate >= required_bitrate) {
allocation[observer_config.observer] = required_bitrate;
remaining_bitrate -= required_bitrate;
}
}
}
// Allocate bitrate to previously paused streams.
if (remaining_bitrate > 0) {
for (const auto& observer_config : allocatable_tracks_) {
if (observer_config.LastAllocatedBitrate() != 0)
continue;
// Add a hysteresis to avoid toggling.
uint32_t required_bitrate = observer_config.MinBitrateWithHysteresis();
if (remaining_bitrate >= required_bitrate) {
allocation[observer_config.observer] = required_bitrate;
remaining_bitrate -= required_bitrate;
}
}
}
// Split a possible remainder evenly on all streams with an allocation.
if (remaining_bitrate > 0)
DistributeBitrateEvenly(remaining_bitrate, false, 1, &allocation);
RTC_DCHECK_EQ(allocation.size(), allocatable_tracks_.size());
return allocation;
}
// Allocates the bitrate based on the bitrate priority of each observer. This
// bitrate priority defines the priority for bitrate to be allocated to that
// observer in relation to other observers. For example with two observers, if
// observer 1 had a bitrate_priority = 1.0, and observer 2 has a
// bitrate_priority = 2.0, the expected behavior is that observer 2 will be
// allocated twice the bitrate as observer 1 above the each observer's
// min_bitrate_bps values, until one of the observers hits its max_bitrate_bps.
BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation(
uint32_t bitrate,
uint32_t sum_min_bitrates) const {
ObserverAllocation allocation;
ObserverAllocation observers_capacities;
for (const auto& observer_config : allocatable_tracks_) {
allocation[observer_config.observer] =
observer_config.config.min_bitrate_bps;
observers_capacities[observer_config.observer] =
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 : 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<int64_t>(priority_margin, bitrate);
allocation[observer_config.observer] +=
rtc::dchecked_cast<int>(extra_bitrate);
observers_capacities[observer_config.observer] -= extra_bitrate;
bitrate -= extra_bitrate;
}
}
// From the remaining bitrate, allocate a proportional amount to each observer
// above the min bitrate already allocated.
if (bitrate > 0)
DistributeBitrateRelatively(bitrate, observers_capacities, &allocation);
return allocation;
}
BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation(
uint32_t bitrate,
uint32_t sum_max_bitrates) const {
ObserverAllocation allocation;
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::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.
@ -433,7 +568,8 @@ uint32_t BitrateAllocator::AllocatableTrack::LastAllocatedBitrate() const {
: allocated_bitrate_bps;
}
uint32_t BitrateAllocator::AllocatableTrack::MinBitrateWithHysteresis() const {
uint32_t bitrate_allocator_impl::AllocatableTrack::MinBitrateWithHysteresis()
const {
uint32_t min_bitrate = config.min_bitrate_bps;
if (LastAllocatedBitrate() == 0) {
min_bitrate += std::max(static_cast<uint32_t>(kToggleFactor * min_bitrate),
@ -451,132 +587,4 @@ uint32_t BitrateAllocator::AllocatableTrack::MinBitrateWithHysteresis() const {
return min_bitrate;
}
void BitrateAllocator::DistributeBitrateEvenly(
uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
ObserverAllocation* allocation) const {
RTC_DCHECK_EQ(allocation->size(), allocatable_tracks_.size());
ObserverSortingMap list_max_bitrates;
for (const auto& observer_config : allocatable_tracks_) {
if (include_zero_allocations ||
allocation->at(observer_config.observer) != 0) {
list_max_bitrates.insert(std::pair<uint32_t, const AllocatableTrack*>(
observer_config.config.max_bitrate_bps, &observer_config));
}
}
auto it = list_max_bitrates.begin();
while (it != list_max_bitrates.end()) {
RTC_DCHECK_GT(bitrate, 0);
uint32_t extra_allocation =
bitrate / static_cast<uint32_t>(list_max_bitrates.size());
uint32_t total_allocation =
extra_allocation + allocation->at(it->second->observer);
bitrate -= extra_allocation;
if (total_allocation > max_multiplier * it->first) {
// There is more than we can fit for this observer, carry over to the
// remaining observers.
bitrate += total_allocation - max_multiplier * it->first;
total_allocation = max_multiplier * it->first;
}
// Finally, update the allocation for this observer.
allocation->at(it->second->observer) = total_allocation;
it = list_max_bitrates.erase(it);
}
}
bool BitrateAllocator::EnoughBitrateForAllObservers(
uint32_t bitrate,
uint32_t sum_min_bitrates) const {
if (bitrate < sum_min_bitrates)
return false;
uint32_t extra_bitrate_per_observer =
(bitrate - sum_min_bitrates) /
static_cast<uint32_t>(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;
}
}
return true;
}
void BitrateAllocator::DistributeBitrateRelatively(
uint32_t remaining_bitrate,
const ObserverAllocation& observers_capacities,
ObserverAllocation* allocation) const {
RTC_DCHECK_EQ(allocation->size(), allocatable_tracks_.size());
RTC_DCHECK_EQ(observers_capacities.size(), allocatable_tracks_.size());
struct PriorityRateObserverConfig {
PriorityRateObserverConfig(BitrateAllocatorObserver* allocation_key,
uint32_t capacity_bps,
double bitrate_priority)
: allocation_key(allocation_key),
capacity_bps(capacity_bps),
bitrate_priority(bitrate_priority) {}
BitrateAllocatorObserver* allocation_key;
// The amount of bitrate bps that can be allocated to this observer.
uint32_t capacity_bps;
double bitrate_priority;
// We want to sort by which observers will be allocated their full capacity
// first. By dividing each observer's capacity by its bitrate priority we
// are "normalizing" the capacity of an observer by the rate it will be
// filled. This is because the amount allocated is based upon bitrate
// priority. We allocate twice as much bitrate to an observer with twice the
// bitrate priority of another.
bool operator<(const PriorityRateObserverConfig& other) const {
return capacity_bps / bitrate_priority <
other.capacity_bps / other.bitrate_priority;
}
};
double bitrate_priority_sum = 0;
std::vector<PriorityRateObserverConfig> priority_rate_observers;
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.config.bitrate_priority);
bitrate_priority_sum += observer_config.config.bitrate_priority;
}
// Iterate in the order observers can be allocated their full capacity.
std::sort(priority_rate_observers.begin(), priority_rate_observers.end());
size_t i;
for (i = 0; i < priority_rate_observers.size(); ++i) {
const auto& priority_rate_observer = priority_rate_observers[i];
// We allocate the full capacity to an observer only if its relative
// portion from the remaining bitrate is sufficient to allocate its full
// capacity. This means we aren't greedily allocating the full capacity, but
// that it is only done when there is also enough bitrate to allocate the
// proportional amounts to all other observers.
double observer_share =
priority_rate_observer.bitrate_priority / bitrate_priority_sum;
double allocation_bps = observer_share * remaining_bitrate;
bool enough_bitrate = allocation_bps >= priority_rate_observer.capacity_bps;
if (!enough_bitrate)
break;
allocation->at(priority_rate_observer.allocation_key) +=
priority_rate_observer.capacity_bps;
remaining_bitrate -= priority_rate_observer.capacity_bps;
bitrate_priority_sum -= priority_rate_observer.bitrate_priority;
}
// From the remaining bitrate, allocate the proportional amounts to the
// observers that aren't allocated their max capacity.
for (; i < priority_rate_observers.size(); ++i) {
const auto& priority_rate_observer = priority_rate_observers[i];
double fraction_allocated =
priority_rate_observer.bitrate_priority / bitrate_priority_sum;
allocation->at(priority_rate_observer.allocation_key) +=
fraction_allocated * remaining_bitrate;
}
}
} // namespace webrtc

View File

@ -74,6 +74,26 @@ class BitrateAllocatorInterface {
virtual ~BitrateAllocatorInterface() = default;
};
namespace bitrate_allocator_impl {
struct AllocatableTrack {
AllocatableTrack(BitrateAllocatorObserver* observer,
MediaStreamAllocationConfig allocation_config)
: observer(observer),
config(allocation_config),
allocated_bitrate_bps(-1),
media_ratio(1.0) {}
BitrateAllocatorObserver* observer;
MediaStreamAllocationConfig config;
int64_t allocated_bitrate_bps;
double media_ratio; // Part of the total bitrate used for media [0.0, 1.0].
uint32_t LastAllocatedBitrate() const;
// The minimum bitrate required by this observer, including
// enable-hysteresis if the observer is in a paused state.
uint32_t MinBitrateWithHysteresis() const;
};
} // namespace bitrate_allocator_impl
// Usage: this class will register multiple RtcpBitrateObserver's one at each
// RTCP module. It will aggregate the results and run one bandwidth estimation
// and push the result to the encoders via BitrateAllocatorObserver(s).
@ -116,76 +136,12 @@ class BitrateAllocator : public BitrateAllocatorInterface {
int GetStartBitrate(BitrateAllocatorObserver* observer) const override;
private:
struct AllocatableTrack {
AllocatableTrack(BitrateAllocatorObserver* observer,
MediaStreamAllocationConfig allocation_config)
: observer(observer),
config(allocation_config),
allocated_bitrate_bps(-1),
media_ratio(1.0) {}
BitrateAllocatorObserver* observer;
MediaStreamAllocationConfig config;
int64_t allocated_bitrate_bps;
double media_ratio; // Part of the total bitrate used for media [0.0, 1.0].
uint32_t LastAllocatedBitrate() const;
// The minimum bitrate required by this observer, including
// enable-hysteresis if the observer is in a paused state.
uint32_t MinBitrateWithHysteresis() const;
};
using AllocatableTrack = bitrate_allocator_impl::AllocatableTrack;
// Calculates the minimum requested send bitrate and max padding bitrate and
// calls LimitObserver::OnAllocationLimitsChanged.
void UpdateAllocationLimits() RTC_RUN_ON(&sequenced_checker_);
typedef std::multimap<uint32_t, const AllocatableTrack*> ObserverSortingMap;
typedef std::map<BitrateAllocatorObserver*, int> ObserverAllocation;
ObserverAllocation AllocateBitrates(uint32_t bitrate) const
RTC_RUN_ON(&sequenced_checker_);
// Allocates zero bitrate to all observers.
ObserverAllocation ZeroRateAllocation() const RTC_RUN_ON(&sequenced_checker_);
// Allocates bitrate to observers when there isn't enough to allocate the
// minimum to all observers.
ObserverAllocation LowRateAllocation(uint32_t bitrate) const
RTC_RUN_ON(&sequenced_checker_);
// Allocates bitrate to all observers when the available bandwidth is enough
// to allocate the minimum to all observers but not enough to allocate the
// max bitrate of each observer.
ObserverAllocation NormalRateAllocation(uint32_t bitrate,
uint32_t sum_min_bitrates) const
RTC_RUN_ON(&sequenced_checker_);
// Allocates bitrate to observers when there is enough available bandwidth
// for all observers to be allocated their max bitrate.
ObserverAllocation MaxRateAllocation(uint32_t bitrate,
uint32_t sum_max_bitrates) const
RTC_RUN_ON(&sequenced_checker_);
// Splits |bitrate| evenly to observers already in |allocation|.
// |include_zero_allocations| decides if zero allocations should be part of
// the distribution or not. The allowed max bitrate is |max_multiplier| x
// observer max bitrate.
void DistributeBitrateEvenly(uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
ObserverAllocation* allocation) const
RTC_RUN_ON(&sequenced_checker_);
bool EnoughBitrateForAllObservers(uint32_t bitrate,
uint32_t sum_min_bitrates) const
RTC_RUN_ON(&sequenced_checker_);
// From the available |bitrate|, each observer will be allocated a
// proportional amount based upon its bitrate priority. If that amount is
// more than the observer's capacity, it will be allocated its capacity, and
// the excess bitrate is still allocated proportionally to other observers.
// Allocating the proportional amount means an observer with twice the
// bitrate_priority of another will be allocated twice the bitrate.
void DistributeBitrateRelatively(
uint32_t bitrate,
const ObserverAllocation& observers_capacities,
ObserverAllocation* allocation) const RTC_RUN_ON(&sequenced_checker_);
// Allow packets to be transmitted in up to 2 times max video bitrate if the
// bandwidth estimate allows it.
// TODO(bugs.webrtc.org/8541): May be worth to refactor to keep this logic in
@ -207,7 +163,6 @@ class BitrateAllocator : public BitrateAllocatorInterface {
int num_pause_events_ RTC_GUARDED_BY(&sequenced_checker_);
int64_t last_bwe_log_time_ RTC_GUARDED_BY(&sequenced_checker_);
BitrateAllocationLimits current_limits_ RTC_GUARDED_BY(&sequenced_checker_);
const uint8_t transmission_max_bitrate_multiplier_;
};
} // namespace webrtc