Files
platform-external-webrtc/call/bitrate_allocator.cc
Sebastian Jansson 35fa280229 Adds allocated rate without feedback to new congestion controller.
When bitrate is allocated to streams that does not have packet feedback,
the allocated bitrate should be included in the estimate. This was
previously only implemented for the old congestion controller and not
for the new task queue based version.

To make the behavior more robust, the responsibility for tracking this
is moved to BitrateAllocator where it's handled consistently for
multiple streams without feedback.

Bug: webrtc:9586, webrtc:8243
Change-Id: I8af7fec23e1bdc08cc61cf1b4ff10461c3711fb0
Reviewed-on: https://webrtc-review.googlesource.com/102681
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24905}
2018-10-01 07:48:02 +00:00

586 lines
24 KiB
C++

/*
* Copyright (c) 2015 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/bitrate_allocator.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <utility>
#include "modules/bitrate_controller/include/bitrate_controller.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
// Allow packets to be transmitted in up to 2 times max video bitrate if the
// bandwidth estimate allows it.
const uint8_t kTransmissionMaxBitrateMultiplier = 2;
const int kDefaultBitrateBps = 300000;
// Require a bitrate increase of max(10%, 20kbps) to resume paused streams.
const double kToggleFactor = 0.1;
const uint32_t kMinToggleBitrateBps = 20000;
const int64_t kBweLogIntervalMs = 5000;
namespace {
double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
RTC_DCHECK_GT(allocated_bitrate, 0);
if (protection_bitrate == 0)
return 1.0;
uint32_t media_bitrate = allocated_bitrate - protection_bitrate;
return media_bitrate / static_cast<double>(allocated_bitrate);
}
} // namespace
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
: limit_observer_(limit_observer),
last_bitrate_bps_(0),
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
last_fraction_loss_(0),
last_rtt_(0),
num_pause_events_(0),
clock_(Clock::GetRealTimeClock()),
last_bwe_log_time_(0),
total_requested_padding_bitrate_(0),
total_requested_min_bitrate_(0),
total_requested_max_bitrate_(0),
allocated_without_feedback_(0),
has_packet_feedback_(false),
bitrate_allocation_strategy_(nullptr),
transmission_max_bitrate_multiplier_(
GetTransmissionMaxBitrateMultiplier()) {
sequenced_checker_.Detach();
}
BitrateAllocator::~BitrateAllocator() {
RTC_HISTOGRAM_COUNTS_100("WebRTC.Call.NumberOfPauseEvents",
num_pause_events_);
}
// 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::OnNetworkChanged(uint32_t target_bitrate_bps,
uint8_t fraction_loss,
int64_t rtt,
int64_t bwe_period_ms) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
last_bitrate_bps_ = target_bitrate_bps;
last_non_zero_bitrate_bps_ =
target_bitrate_bps > 0 ? target_bitrate_bps : last_non_zero_bitrate_bps_;
last_fraction_loss_ = fraction_loss;
last_rtt_ = rtt;
last_bwe_period_ms_ = bwe_period_ms;
// Periodically log the incoming BWE.
int64_t now = clock_->TimeInMilliseconds();
if (now > last_bwe_log_time_ + kBweLogIntervalMs) {
RTC_LOG(LS_INFO) << "Current BWE " << target_bitrate_bps;
last_bwe_log_time_ = now;
}
ObserverAllocation allocation = AllocateBitrates(target_bitrate_bps);
for (auto& config : bitrate_observer_configs_) {
uint32_t allocated_bitrate = allocation[config.observer];
uint32_t protection_bitrate = config.observer->OnBitrateUpdated(
allocated_bitrate, last_fraction_loss_, last_rtt_, last_bwe_period_ms_);
if (allocated_bitrate == 0 && config.allocated_bitrate_bps > 0) {
if (target_bitrate_bps > 0)
++num_pause_events_;
// 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;
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 "
<< 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
<< ", current allocation " << allocated_bitrate
<< " and protection bitrate " << protection_bitrate;
}
// Only update the media ratio if the observer got an allocation.
if (allocated_bitrate > 0)
config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate);
config.allocated_bitrate_bps = allocated_bitrate;
}
UpdateAllocationLimits();
}
void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
MediaStreamAllocationConfig config) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
RTC_DCHECK_GT(config.bitrate_priority, 0);
RTC_DCHECK(std::isnormal(config.bitrate_priority));
auto it = FindObserverConfig(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;
} else {
bitrate_observer_configs_.push_back(ObserverConfig(
observer, config.min_bitrate_bps, config.max_bitrate_bps,
config.pad_up_bitrate_bps, config.enforce_min_bitrate, config.track_id,
config.bitrate_priority, config.has_packet_feedback));
}
ObserverAllocation allocation;
if (last_bitrate_bps_ > 0) {
// Calculate a new allocation and update all observers.
allocation = AllocateBitrates(last_bitrate_bps_);
for (auto& config : bitrate_observer_configs_) {
uint32_t allocated_bitrate = allocation[config.observer];
uint32_t protection_bitrate = config.observer->OnBitrateUpdated(
allocated_bitrate, last_fraction_loss_, last_rtt_,
last_bwe_period_ms_);
config.allocated_bitrate_bps = allocated_bitrate;
if (allocated_bitrate > 0)
config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate);
}
} else {
// Currently, an encoder is not allowed to produce frames.
// But we still have to return the initial config bitrate + let the
// observer know that it can not produce frames.
allocation = AllocateBitrates(last_non_zero_bitrate_bps_);
observer->OnBitrateUpdated(0, last_fraction_loss_, last_rtt_,
last_bwe_period_ms_);
}
UpdateAllocationLimits();
}
void BitrateAllocator::UpdateAllocationLimits() {
uint32_t total_requested_padding_bitrate = 0;
uint32_t total_requested_min_bitrate = 0;
uint32_t total_requested_max_bitrate = 0;
bool has_packet_feedback = false;
uint32_t allocated_without_feedback = 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;
} 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;
if (config.allocated_bitrate_bps > 0 && config.has_packet_feedback)
has_packet_feedback = true;
// TODO(srte): Remove field trial check.
if (!config.has_packet_feedback &&
field_trial::IsEnabled("WebRTC-Audio-ABWENoTWCC"))
allocated_without_feedback += config.allocated_bitrate_bps;
}
if (total_requested_padding_bitrate == total_requested_padding_bitrate_ &&
total_requested_min_bitrate == total_requested_min_bitrate_ &&
total_requested_max_bitrate == total_requested_max_bitrate_ &&
allocated_without_feedback == allocated_without_feedback_ &&
has_packet_feedback == has_packet_feedback_) {
return;
}
total_requested_min_bitrate_ = total_requested_min_bitrate;
total_requested_padding_bitrate_ = total_requested_padding_bitrate;
total_requested_max_bitrate_ = total_requested_max_bitrate;
allocated_without_feedback_ = allocated_without_feedback;
has_packet_feedback_ = has_packet_feedback;
RTC_LOG(LS_INFO) << "UpdateAllocationLimits : total_requested_min_bitrate: "
<< total_requested_min_bitrate
<< "bps, total_requested_padding_bitrate: "
<< total_requested_padding_bitrate
<< "bps, total_requested_max_bitrate: "
<< total_requested_max_bitrate << "bps";
limit_observer_->OnAllocationLimitsChanged(
total_requested_min_bitrate, total_requested_padding_bitrate,
total_requested_max_bitrate, allocated_without_feedback,
has_packet_feedback);
}
void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
auto it = FindObserverConfig(observer);
if (it != bitrate_observer_configs_.end()) {
bitrate_observer_configs_.erase(it);
}
UpdateAllocationLimits();
}
int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
const auto& it = FindObserverConfig(observer);
if (it == bitrate_observer_configs_.end()) {
// This observer hasn't been added yet, just give it its fair share.
return last_non_zero_bitrate_bps_ /
static_cast<int>((bitrate_observer_configs_.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<int>(bitrate_observer_configs_.size());
} else {
// This observer already has an allocation.
return it->allocated_bitrate_bps;
}
}
void BitrateAllocator::SetBitrateAllocationStrategy(
std::unique_ptr<rtc::BitrateAllocationStrategy>
bitrate_allocation_strategy) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
bitrate_allocation_strategy_ = std::move(bitrate_allocation_strategy);
}
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) {
if (bitrate_observer_configs_.empty())
return ObserverAllocation();
if (bitrate_allocation_strategy_ != nullptr) {
std::vector<const rtc::BitrateAllocationStrategy::TrackConfig*>
track_configs(bitrate_observer_configs_.size());
int i = 0;
for (const auto& c : bitrate_observer_configs_) {
track_configs[i++] = &c;
}
std::vector<uint32_t> track_allocations =
bitrate_allocation_strategy_->AllocateBitrates(bitrate, track_configs);
// The strategy should return allocation for all tracks.
RTC_CHECK(track_allocations.size() == bitrate_observer_configs_.size());
ObserverAllocation allocation;
auto track_allocations_it = track_allocations.begin();
for (const auto& observer_config : bitrate_observer_configs_) {
allocation[observer_config.observer] = *track_allocations_it++;
}
return allocation;
}
if (bitrate == 0)
return ZeroRateAllocation();
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;
}
// 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() {
ObserverAllocation allocation;
for (const auto& observer_config : bitrate_observer_configs_)
allocation[observer_config.observer] = 0;
return allocation;
}
BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
uint32_t bitrate) {
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 : bitrate_observer_configs_) {
int32_t allocated_bitrate = 0;
if (observer_config.enforce_min_bitrate)
allocated_bitrate = observer_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 : bitrate_observer_configs_) {
if (observer_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 : bitrate_observer_configs_) {
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(), bitrate_observer_configs_.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) {
ObserverAllocation allocation;
ObserverAllocation observers_capacities;
for (const auto& observer_config : bitrate_observer_configs_) {
allocation[observer_config.observer] = observer_config.min_bitrate_bps;
observers_capacities[observer_config.observer] =
observer_config.max_bitrate_bps - observer_config.min_bitrate_bps;
}
bitrate -= sum_min_bitrates;
// 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) {
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;
}
DistributeBitrateEvenly(bitrate, true, transmission_max_bitrate_multiplier_,
&allocation);
return allocation;
}
uint32_t BitrateAllocator::ObserverConfig::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;
}
uint32_t BitrateAllocator::ObserverConfig::MinBitrateWithHysteresis() const {
uint32_t min_bitrate = min_bitrate_bps;
if (LastAllocatedBitrate() == 0) {
min_bitrate += std::max(static_cast<uint32_t>(kToggleFactor * min_bitrate),
kMinToggleBitrateBps);
}
// Account for protection bitrate used by this observer in the previous
// allocation.
// Note: the ratio will only be updated when the stream is active, meaning a
// paused stream won't get any ratio updates. This might lead to waiting a bit
// longer than necessary if the network condition improves, but this is to
// avoid too much toggling.
if (media_ratio > 0.0 && media_ratio < 1.0)
min_bitrate += min_bitrate * (1.0 - media_ratio);
return min_bitrate;
}
void BitrateAllocator::DistributeBitrateEvenly(uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
ObserverAllocation* allocation) {
RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size());
ObserverSortingMap list_max_bitrates;
for (const auto& observer_config : bitrate_observer_configs_) {
if (include_zero_allocations ||
allocation->at(observer_config.observer) != 0) {
list_max_bitrates.insert(std::pair<uint32_t, const ObserverConfig*>(
observer_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) {
if (bitrate < sum_min_bitrates)
return false;
uint32_t extra_bitrate_per_observer =
(bitrate - sum_min_bitrates) /
static_cast<uint32_t>(bitrate_observer_configs_.size());
for (const auto& observer_config : bitrate_observer_configs_) {
if (observer_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) {
RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size());
RTC_DCHECK_EQ(observers_capacities.size(), bitrate_observer_configs_.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 : bitrate_observer_configs_) {
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;
}
// 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