blob: 3672ef520ca3422bc372d2108b75abf9b5390666 [file] [log] [blame]
/*
* 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 "webrtc/call/bitrate_allocator.h"
#include <algorithm>
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
namespace webrtc {
// Allow packets to be transmitted in up to 2 times max video bitrate if the
// bandwidth estimate allows it.
const int kTransmissionMaxBitrateMultiplier = 2;
const int kDefaultBitrateBps = 300000;
BitrateAllocator::BitrateAllocator()
: bitrate_observer_configs_(),
enforce_min_bitrate_(true),
last_bitrate_bps_(kDefaultBitrateBps),
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
last_fraction_loss_(0),
last_rtt_(0) {}
uint32_t BitrateAllocator::OnNetworkChanged(uint32_t bitrate,
uint8_t fraction_loss,
int64_t rtt) {
rtc::CritScope lock(&crit_sect_);
last_bitrate_bps_ = bitrate;
last_non_zero_bitrate_bps_ =
bitrate > 0 ? bitrate : last_non_zero_bitrate_bps_;
last_fraction_loss_ = fraction_loss;
last_rtt_ = rtt;
uint32_t allocated_bitrate_bps = 0;
ObserverAllocation allocation = AllocateBitrates(bitrate);
for (const auto& kv : allocation) {
kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_);
allocated_bitrate_bps += kv.second;
}
return allocated_bitrate_bps;
}
int BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
uint32_t min_bitrate_bps,
uint32_t max_bitrate_bps,
bool enforce_min_bitrate) {
rtc::CritScope lock(&crit_sect_);
// TODO(mflodman): Enforce this per observer.
EnforceMinBitrate(enforce_min_bitrate);
auto it = FindObserverConfig(observer);
// Allow the max bitrate to be exceeded for FEC and retransmissions.
// TODO(holmer): We have to get rid of this hack as it makes it difficult to
// properly allocate bitrate. The allocator should instead distribute any
// extra bitrate after all streams have maxed out.
max_bitrate_bps *= kTransmissionMaxBitrateMultiplier;
if (it != bitrate_observer_configs_.end()) {
// Update current configuration.
it->min_bitrate_bps = min_bitrate_bps;
it->max_bitrate_bps = max_bitrate_bps;
} else {
// Add new settings.
bitrate_observer_configs_.push_back(ObserverConfig(
observer, min_bitrate_bps, max_bitrate_bps, enforce_min_bitrate));
}
int new_observer_bitrate_bps = 0;
if (last_bitrate_bps_ > 0) { // We have a bitrate to allocate.
ObserverAllocation allocation = AllocateBitrates(last_bitrate_bps_);
for (auto& kv : allocation) {
// Update all observers with the new allocation.
kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_);
if (kv.first == observer)
new_observer_bitrate_bps = kv.second;
}
} 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.
ObserverAllocation allocation =
AllocateBitrates(last_non_zero_bitrate_bps_);
observer->OnBitrateUpdated(0, last_fraction_loss_, last_rtt_);
new_observer_bitrate_bps = allocation[observer];
}
return new_observer_bitrate_bps;
}
void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) {
rtc::CritScope lock(&crit_sect_);
auto it = FindObserverConfig(observer);
if (it != bitrate_observer_configs_.end()) {
bitrate_observer_configs_.erase(it);
}
}
void BitrateAllocator::EnforceMinBitrate(bool enforce_min_bitrate) {
enforce_min_bitrate_ = enforce_min_bitrate;
}
BitrateAllocator::ObserverConfigList::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 == 0)
return ZeroRateAllocation();
uint32_t sum_min_bitrates = 0;
for (const auto& observer_config : bitrate_observer_configs_)
sum_min_bitrates += observer_config.min_bitrate_bps;
if (bitrate <= sum_min_bitrates)
return LowRateAllocation(bitrate);
return NormalRateAllocation(bitrate, sum_min_bitrates);
}
BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation(
uint32_t bitrate,
uint32_t sum_min_bitrates) {
uint32_t num_remaining_observers =
static_cast<uint32_t>(bitrate_observer_configs_.size());
RTC_DCHECK_GT(num_remaining_observers, 0u);
uint32_t bitrate_per_observer =
(bitrate - sum_min_bitrates) / num_remaining_observers;
// Use map to sort list based on max bitrate.
ObserverSortingMap list_max_bitrates;
for (const auto& config : bitrate_observer_configs_) {
list_max_bitrates.insert(std::pair<uint32_t, const ObserverConfig*>(
config.max_bitrate_bps, &config));
}
ObserverAllocation allocation;
ObserverSortingMap::iterator max_it = list_max_bitrates.begin();
while (max_it != list_max_bitrates.end()) {
num_remaining_observers--;
uint32_t observer_allowance =
max_it->second->min_bitrate_bps + bitrate_per_observer;
if (max_it->first < observer_allowance) {
// We have more than enough for this observer.
// Carry the remainder forward.
uint32_t remainder = observer_allowance - max_it->first;
if (num_remaining_observers != 0)
bitrate_per_observer += remainder / num_remaining_observers;
allocation[max_it->second->observer] = max_it->first;
} else {
allocation[max_it->second->observer] = observer_allowance;
}
list_max_bitrates.erase(max_it);
// Prepare next iteration.
max_it = list_max_bitrates.begin();
}
return allocation;
}
BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation() {
ObserverAllocation allocation;
// Zero bitrate to all observers.
for (const auto& observer_config : bitrate_observer_configs_)
allocation[observer_config.observer] = 0;
return allocation;
}
BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
uint32_t bitrate) {
ObserverAllocation allocation;
if (enforce_min_bitrate_) {
// Min bitrate to all observers.
for (const auto& observer_config : bitrate_observer_configs_)
allocation[observer_config.observer] = observer_config.min_bitrate_bps;
} else {
// Allocate up to |min_bitrate_bps| to one observer at a time, until
// |bitrate| is depleted.
uint32_t remainder = bitrate;
for (const auto& observer_config : bitrate_observer_configs_) {
uint32_t allocated_bitrate =
std::min(remainder, observer_config.min_bitrate_bps);
allocation[observer_config.observer] = allocated_bitrate;
remainder -= allocated_bitrate;
}
}
return allocation;
}
} // namespace webrtc