Extended the bitrate allocator to allow allocation to tracks based upon priorities which are planned to be defined as a relative bitrate in the RTCRtpEncodingParameters.
Bug: None Change-Id: I80bc6c1e36b03537eb08f53df8c21d540e7408be Reviewed-on: https://webrtc-review.googlesource.com/16262 Commit-Queue: Seth Hampson <shampson@webrtc.org> Reviewed-by: Stefan Holmer <stefan@webrtc.org> Reviewed-by: Elad Alon <eladalon@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20680}
This commit is contained in:
@ -12,6 +12,7 @@
|
|||||||
#include "call/bitrate_allocator.h"
|
#include "call/bitrate_allocator.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -48,7 +49,6 @@ double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
|
|||||||
|
|
||||||
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
|
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
|
||||||
: limit_observer_(limit_observer),
|
: limit_observer_(limit_observer),
|
||||||
bitrate_observer_configs_(),
|
|
||||||
last_bitrate_bps_(0),
|
last_bitrate_bps_(0),
|
||||||
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
|
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
|
||||||
last_fraction_loss_(0),
|
last_fraction_loss_(0),
|
||||||
@ -128,8 +128,11 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
|
|||||||
uint32_t max_bitrate_bps,
|
uint32_t max_bitrate_bps,
|
||||||
uint32_t pad_up_bitrate_bps,
|
uint32_t pad_up_bitrate_bps,
|
||||||
bool enforce_min_bitrate,
|
bool enforce_min_bitrate,
|
||||||
std::string track_id) {
|
std::string track_id,
|
||||||
|
double bitrate_priority) {
|
||||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
|
||||||
|
RTC_DCHECK_GT(bitrate_priority, 0);
|
||||||
|
RTC_DCHECK(std::isnormal(bitrate_priority));
|
||||||
auto it = FindObserverConfig(observer);
|
auto it = FindObserverConfig(observer);
|
||||||
|
|
||||||
// Update settings if the observer already exists, create a new one otherwise.
|
// Update settings if the observer already exists, create a new one otherwise.
|
||||||
@ -138,10 +141,11 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
|
|||||||
it->max_bitrate_bps = max_bitrate_bps;
|
it->max_bitrate_bps = max_bitrate_bps;
|
||||||
it->pad_up_bitrate_bps = pad_up_bitrate_bps;
|
it->pad_up_bitrate_bps = pad_up_bitrate_bps;
|
||||||
it->enforce_min_bitrate = enforce_min_bitrate;
|
it->enforce_min_bitrate = enforce_min_bitrate;
|
||||||
|
it->bitrate_priority = bitrate_priority;
|
||||||
} else {
|
} else {
|
||||||
bitrate_observer_configs_.push_back(
|
bitrate_observer_configs_.push_back(ObserverConfig(
|
||||||
ObserverConfig(observer, min_bitrate_bps, max_bitrate_bps,
|
observer, min_bitrate_bps, max_bitrate_bps, pad_up_bitrate_bps,
|
||||||
pad_up_bitrate_bps, enforce_min_bitrate, track_id));
|
enforce_min_bitrate, track_id, bitrate_priority));
|
||||||
}
|
}
|
||||||
|
|
||||||
ObserverAllocation allocation;
|
ObserverAllocation allocation;
|
||||||
@ -287,7 +291,8 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates(
|
|||||||
if (!EnoughBitrateForAllObservers(bitrate, sum_min_bitrates))
|
if (!EnoughBitrateForAllObservers(bitrate, sum_min_bitrates))
|
||||||
return LowRateAllocation(bitrate);
|
return LowRateAllocation(bitrate);
|
||||||
|
|
||||||
// All observers will get their min bitrate plus an even share of the rest.
|
// 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)
|
if (bitrate <= sum_max_bitrates)
|
||||||
return NormalRateAllocation(bitrate, sum_min_bitrates);
|
return NormalRateAllocation(bitrate, sum_min_bitrates);
|
||||||
|
|
||||||
@ -357,17 +362,30 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
|
|||||||
return allocation;
|
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(
|
BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation(
|
||||||
uint32_t bitrate,
|
uint32_t bitrate,
|
||||||
uint32_t sum_min_bitrates) {
|
uint32_t sum_min_bitrates) {
|
||||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
|
||||||
ObserverAllocation allocation;
|
ObserverAllocation allocation;
|
||||||
for (const auto& observer_config : bitrate_observer_configs_)
|
ObserverAllocation observers_capacities;
|
||||||
|
for (const auto& observer_config : bitrate_observer_configs_) {
|
||||||
allocation[observer_config.observer] = observer_config.min_bitrate_bps;
|
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;
|
bitrate -= sum_min_bitrates;
|
||||||
|
// From the remaining bitrate, allocate a proportional amount to each observer
|
||||||
|
// above the min bitrate already allocated.
|
||||||
if (bitrate > 0)
|
if (bitrate > 0)
|
||||||
DistributeBitrateEvenly(bitrate, true, 1, &allocation);
|
DistributeBitrateRelatively(bitrate, observers_capacities, &allocation);
|
||||||
|
|
||||||
return allocation;
|
return allocation;
|
||||||
}
|
}
|
||||||
@ -468,4 +486,80 @@ bool BitrateAllocator::EnoughBitrateForAllObservers(uint32_t bitrate,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitrateAllocator::DistributeBitrateRelatively(
|
||||||
|
uint32_t remaining_bitrate,
|
||||||
|
const ObserverAllocation& observers_capacities,
|
||||||
|
ObserverAllocation* allocation) {
|
||||||
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
|
||||||
|
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
|
} // namespace webrtc
|
||||||
|
|||||||
@ -86,7 +86,11 @@ class BitrateAllocator {
|
|||||||
uint32_t max_bitrate_bps,
|
uint32_t max_bitrate_bps,
|
||||||
uint32_t pad_up_bitrate_bps,
|
uint32_t pad_up_bitrate_bps,
|
||||||
bool enforce_min_bitrate,
|
bool enforce_min_bitrate,
|
||||||
std::string track_id);
|
std::string track_id,
|
||||||
|
// TODO(shampson): Take out default value and wire the
|
||||||
|
// bitrate_priority up to the AudioSendStream::Config and
|
||||||
|
// VideoSendStream::Config.
|
||||||
|
double bitrate_priority = 1.0);
|
||||||
|
|
||||||
// Removes a previously added observer, but will not trigger a new bitrate
|
// Removes a previously added observer, but will not trigger a new bitrate
|
||||||
// allocation.
|
// allocation.
|
||||||
@ -104,14 +108,14 @@ class BitrateAllocator {
|
|||||||
bitrate_allocation_strategy);
|
bitrate_allocation_strategy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Note: All bitrates for member variables and methods are in bps.
|
|
||||||
struct ObserverConfig : rtc::BitrateAllocationStrategy::TrackConfig {
|
struct ObserverConfig : rtc::BitrateAllocationStrategy::TrackConfig {
|
||||||
ObserverConfig(BitrateAllocatorObserver* observer,
|
ObserverConfig(BitrateAllocatorObserver* observer,
|
||||||
uint32_t min_bitrate_bps,
|
uint32_t min_bitrate_bps,
|
||||||
uint32_t max_bitrate_bps,
|
uint32_t max_bitrate_bps,
|
||||||
uint32_t pad_up_bitrate_bps,
|
uint32_t pad_up_bitrate_bps,
|
||||||
bool enforce_min_bitrate,
|
bool enforce_min_bitrate,
|
||||||
std::string track_id)
|
std::string track_id,
|
||||||
|
double bitrate_priority)
|
||||||
: TrackConfig(min_bitrate_bps,
|
: TrackConfig(min_bitrate_bps,
|
||||||
max_bitrate_bps,
|
max_bitrate_bps,
|
||||||
enforce_min_bitrate,
|
enforce_min_bitrate,
|
||||||
@ -119,12 +123,17 @@ class BitrateAllocator {
|
|||||||
observer(observer),
|
observer(observer),
|
||||||
pad_up_bitrate_bps(pad_up_bitrate_bps),
|
pad_up_bitrate_bps(pad_up_bitrate_bps),
|
||||||
allocated_bitrate_bps(-1),
|
allocated_bitrate_bps(-1),
|
||||||
media_ratio(1.0) {}
|
media_ratio(1.0),
|
||||||
|
bitrate_priority(bitrate_priority) {}
|
||||||
|
|
||||||
BitrateAllocatorObserver* observer;
|
BitrateAllocatorObserver* observer;
|
||||||
uint32_t pad_up_bitrate_bps;
|
uint32_t pad_up_bitrate_bps;
|
||||||
int64_t allocated_bitrate_bps;
|
int64_t allocated_bitrate_bps;
|
||||||
double media_ratio; // Part of the total bitrate used for media [0.0, 1.0].
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculates the minimum requested send bitrate and max padding bitrate and
|
// Calculates the minimum requested send bitrate and max padding bitrate and
|
||||||
@ -140,10 +149,18 @@ class BitrateAllocator {
|
|||||||
|
|
||||||
ObserverAllocation AllocateBitrates(uint32_t bitrate);
|
ObserverAllocation AllocateBitrates(uint32_t bitrate);
|
||||||
|
|
||||||
|
// Allocates zero bitrate to all observers.
|
||||||
ObserverAllocation ZeroRateAllocation();
|
ObserverAllocation ZeroRateAllocation();
|
||||||
|
// Allocates bitrate to observers when there isn't enough to allocate the
|
||||||
|
// minimum to all observers.
|
||||||
ObserverAllocation LowRateAllocation(uint32_t bitrate);
|
ObserverAllocation LowRateAllocation(uint32_t bitrate);
|
||||||
|
// 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,
|
ObserverAllocation NormalRateAllocation(uint32_t bitrate,
|
||||||
uint32_t sum_min_bitrates);
|
uint32_t sum_min_bitrates);
|
||||||
|
// Allocates bitrate to observers when there is enough available bandwidth
|
||||||
|
// for all observers to be allocated their max bitrate.
|
||||||
ObserverAllocation MaxRateAllocation(uint32_t bitrate,
|
ObserverAllocation MaxRateAllocation(uint32_t bitrate,
|
||||||
uint32_t sum_max_bitrates);
|
uint32_t sum_max_bitrates);
|
||||||
|
|
||||||
@ -162,6 +179,17 @@ class BitrateAllocator {
|
|||||||
bool EnoughBitrateForAllObservers(uint32_t bitrate,
|
bool EnoughBitrateForAllObservers(uint32_t bitrate,
|
||||||
uint32_t sum_min_bitrates);
|
uint32_t sum_min_bitrates);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
rtc::SequencedTaskChecker sequenced_checker_;
|
rtc::SequencedTaskChecker sequenced_checker_;
|
||||||
LimitObserver* const limit_observer_ RTC_GUARDED_BY(&sequenced_checker_);
|
LimitObserver* const limit_observer_ RTC_GUARDED_BY(&sequenced_checker_);
|
||||||
// Stored in a list to keep track of the insertion order.
|
// Stored in a list to keep track of the insertion order.
|
||||||
@ -180,5 +208,6 @@ class BitrateAllocator {
|
|||||||
std::unique_ptr<rtc::BitrateAllocationStrategy> bitrate_allocation_strategy_
|
std::unique_ptr<rtc::BitrateAllocationStrategy> bitrate_allocation_strategy_
|
||||||
RTC_GUARDED_BY(&sequenced_checker_);
|
RTC_GUARDED_BY(&sequenced_checker_);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
#endif // CALL_BITRATE_ALLOCATOR_H_
|
#endif // CALL_BITRATE_ALLOCATOR_H_
|
||||||
|
|||||||
@ -17,7 +17,8 @@
|
|||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
|
|
||||||
using testing::NiceMock;
|
using ::testing::NiceMock;
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -518,4 +519,259 @@ TEST_F(BitrateAllocatorTest, PassProbingInterval) {
|
|||||||
allocator_->RemoveObserver(&observer);
|
allocator_->RemoveObserver(&observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateOneObserverBasic) {
|
||||||
|
TestBitrateObserver observer;
|
||||||
|
const uint32_t kMinSendBitrateBps = 10;
|
||||||
|
const uint32_t kMaxSendBitrateBps = 60;
|
||||||
|
const uint32_t kNetworkBandwidthBps = 30;
|
||||||
|
|
||||||
|
allocator_->AddObserver(&observer, kMinSendBitrateBps, kMaxSendBitrateBps, 0,
|
||||||
|
true, "", 2.0);
|
||||||
|
allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(kNetworkBandwidthBps, observer.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that two observers with the same bitrate priority are allocated
|
||||||
|
// their bitrate evenly.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBasic) {
|
||||||
|
TestBitrateObserver observer_low_1;
|
||||||
|
TestBitrateObserver observer_low_2;
|
||||||
|
const uint32_t kMinSendBitrateBps = 10;
|
||||||
|
const uint32_t kMaxSendBitrateBps = 60;
|
||||||
|
const uint32_t kNetworkBandwidthBps = 60;
|
||||||
|
allocator_->AddObserver(&observer_low_1, kMinSendBitrateBps,
|
||||||
|
kMaxSendBitrateBps, 0, false, "low1", 2.0);
|
||||||
|
allocator_->AddObserver(&observer_low_2, kMinSendBitrateBps,
|
||||||
|
kMaxSendBitrateBps, 0, false, "low2", 2.0);
|
||||||
|
allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_1.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_2.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low_1);
|
||||||
|
allocator_->RemoveObserver(&observer_low_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that there is no difference in functionality when the min bitrate is
|
||||||
|
// enforced.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBasicMinEnforced) {
|
||||||
|
TestBitrateObserver observer_low_1;
|
||||||
|
TestBitrateObserver observer_low_2;
|
||||||
|
const uint32_t kMinSendBitrateBps = 0;
|
||||||
|
const uint32_t kMaxSendBitrateBps = 60;
|
||||||
|
const uint32_t kNetworkBandwidthBps = 60;
|
||||||
|
allocator_->AddObserver(&observer_low_1, kMinSendBitrateBps,
|
||||||
|
kMaxSendBitrateBps, 0, true, "low1", 2.0);
|
||||||
|
allocator_->AddObserver(&observer_low_2, kMinSendBitrateBps,
|
||||||
|
kMaxSendBitrateBps, 0, true, "low2", 2.0);
|
||||||
|
allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_1.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kNetworkBandwidthBps / 2, observer_low_2.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low_1);
|
||||||
|
allocator_->RemoveObserver(&observer_low_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that if the available bandwidth is the sum of the max bitrate
|
||||||
|
// of all observers, they will be allocated their max.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversBothAllocatedMax) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
const uint32_t kMinSendBitrateBps = 0;
|
||||||
|
const uint32_t kMaxSendBitrateBps = 60;
|
||||||
|
const uint32_t kNetworkBandwidthBps = kMaxSendBitrateBps * 2;
|
||||||
|
allocator_->AddObserver(&observer_low, kMinSendBitrateBps, kMaxSendBitrateBps,
|
||||||
|
0, true, "low", 2.0);
|
||||||
|
allocator_->AddObserver(&observer_mid, kMinSendBitrateBps, kMaxSendBitrateBps,
|
||||||
|
0, true, "mid", 4.0);
|
||||||
|
allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(kMaxSendBitrateBps, observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kMaxSendBitrateBps, observer_mid.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that after a higher bitrate priority observer has been allocated its
|
||||||
|
// max bitrate the lower priority observer will then be allocated the remaining
|
||||||
|
// bitrate.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateTwoObserversOneAllocatedToMax) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
allocator_->AddObserver(&observer_low, 10, 50, 0, false, "low", 2.0);
|
||||||
|
allocator_->AddObserver(&observer_mid, 10, 50, 0, false, "mid", 4.0);
|
||||||
|
allocator_->OnNetworkChanged(90, 0, 0, kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(40u, observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(50u, observer_mid.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that three observers with three different bitrate priorities will all
|
||||||
|
// be allocated bitrate according to their relative bitrate priority.
|
||||||
|
TEST_F(BitrateAllocatorTest,
|
||||||
|
PriorityRateThreeObserversAllocatedRelativeAmounts) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
TestBitrateObserver observer_high;
|
||||||
|
const uint32_t kMaxBitrate = 100;
|
||||||
|
// Not enough bandwidth to fill any observer's max bitrate.
|
||||||
|
const uint32_t kNetworkBandwidthBps = 70;
|
||||||
|
const double kLowBitratePriority = 2.0;
|
||||||
|
const double kMidBitratePriority = 4.0;
|
||||||
|
const double kHighBitratePriority = 8.0;
|
||||||
|
const double kTotalBitratePriority =
|
||||||
|
kLowBitratePriority + kMidBitratePriority + kHighBitratePriority;
|
||||||
|
allocator_->AddObserver(&observer_low, 0, kMaxBitrate, 0, false, "low",
|
||||||
|
kLowBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_mid, 0, kMaxBitrate, 0, false, "mid",
|
||||||
|
kMidBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_high, 0, kMaxBitrate, 0, false, "high",
|
||||||
|
kHighBitratePriority);
|
||||||
|
allocator_->OnNetworkChanged(kNetworkBandwidthBps, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
const double kLowFractionAllocated =
|
||||||
|
kLowBitratePriority / kTotalBitratePriority;
|
||||||
|
const double kMidFractionAllocated =
|
||||||
|
kMidBitratePriority / kTotalBitratePriority;
|
||||||
|
const double kHighFractionAllocated =
|
||||||
|
kHighBitratePriority / kTotalBitratePriority;
|
||||||
|
EXPECT_EQ(kLowFractionAllocated * kNetworkBandwidthBps,
|
||||||
|
observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kMidFractionAllocated * kNetworkBandwidthBps,
|
||||||
|
observer_mid.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kHighFractionAllocated * kNetworkBandwidthBps,
|
||||||
|
observer_high.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
allocator_->RemoveObserver(&observer_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that after the high priority observer has been allocated its maximum
|
||||||
|
// bitrate, the other two observers are still allocated bitrate according to
|
||||||
|
// their relative bitrate priority.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversHighAllocatedToMax) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
const double kLowBitratePriority = 2.0;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
const double kMidBitratePriority = 4.0;
|
||||||
|
TestBitrateObserver observer_high;
|
||||||
|
const double kHighBitratePriority = 8.0;
|
||||||
|
|
||||||
|
const uint32_t kAvailableBitrate = 90;
|
||||||
|
const uint32_t kMaxBitrate = 40;
|
||||||
|
const uint32_t kMinBitrate = 10;
|
||||||
|
// Remaining bitrate after allocating to all mins and knowing that the high
|
||||||
|
// priority observer will have its max bitrate allocated.
|
||||||
|
const uint32_t kRemainingBitrate =
|
||||||
|
kAvailableBitrate - kMaxBitrate - (2 * kMinBitrate);
|
||||||
|
|
||||||
|
allocator_->AddObserver(&observer_low, kMinBitrate, kMaxBitrate, 0, false,
|
||||||
|
"low", kLowBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_mid, kMinBitrate, kMaxBitrate, 0, false,
|
||||||
|
"mid", kMidBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_high, kMinBitrate, kMaxBitrate, 0, false,
|
||||||
|
"high", kHighBitratePriority);
|
||||||
|
allocator_->OnNetworkChanged(kAvailableBitrate, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
const double kLowFractionAllocated =
|
||||||
|
kLowBitratePriority / (kLowBitratePriority + kMidBitratePriority);
|
||||||
|
const double kMidFractionAllocated =
|
||||||
|
kMidBitratePriority / (kLowBitratePriority + kMidBitratePriority);
|
||||||
|
EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kLowFractionAllocated),
|
||||||
|
observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kMidFractionAllocated),
|
||||||
|
observer_mid.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(40u, observer_high.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
allocator_->RemoveObserver(&observer_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that after the low priority observer has been allocated its maximum
|
||||||
|
// bitrate, the other two observers are still allocated bitrate according to
|
||||||
|
// their relative bitrate priority.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversLowAllocatedToMax) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
const double kLowBitratePriority = 2.0;
|
||||||
|
const uint32_t kLowMaxBitrate = 10;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
const double kMidBitratePriority = 4.0;
|
||||||
|
TestBitrateObserver observer_high;
|
||||||
|
const double kHighBitratePriority = 8.0;
|
||||||
|
|
||||||
|
const uint32_t kMinBitrate = 0;
|
||||||
|
const uint32_t kMaxBitrate = 60;
|
||||||
|
const uint32_t kAvailableBitrate = 100;
|
||||||
|
// Remaining bitrate knowing that the low priority observer is allocated its
|
||||||
|
// max bitrate. We know this because it is allocated 2.0/14.0 (1/7) of the
|
||||||
|
// available bitrate, so 70 bps would be sufficient network bandwidth.
|
||||||
|
const uint32_t kRemainingBitrate = kAvailableBitrate - kLowMaxBitrate;
|
||||||
|
|
||||||
|
allocator_->AddObserver(&observer_low, kMinBitrate, kLowMaxBitrate, 0, false,
|
||||||
|
"low", kLowBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_mid, kMinBitrate, kMaxBitrate, 0, false,
|
||||||
|
"mid", kMidBitratePriority);
|
||||||
|
allocator_->AddObserver(&observer_high, kMinBitrate, kMaxBitrate, 0, false,
|
||||||
|
"high", kHighBitratePriority);
|
||||||
|
allocator_->OnNetworkChanged(kAvailableBitrate, 0, 0,
|
||||||
|
kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
const double kMidFractionAllocated =
|
||||||
|
kMidBitratePriority / (kMidBitratePriority + kHighBitratePriority);
|
||||||
|
const double kHighFractionAllocated =
|
||||||
|
kHighBitratePriority / (kMidBitratePriority + kHighBitratePriority);
|
||||||
|
EXPECT_EQ(kLowMaxBitrate, observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kMidFractionAllocated),
|
||||||
|
observer_mid.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(kMinBitrate + (kRemainingBitrate * kHighFractionAllocated),
|
||||||
|
observer_high.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
allocator_->RemoveObserver(&observer_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that after two observers are allocated bitrate to their max, the
|
||||||
|
// the remaining observer is allocated what's left appropriately. This test
|
||||||
|
// handles an edge case where the medium and high observer reach their
|
||||||
|
// "relative" max allocation at the same time. The high has 40 to allocate
|
||||||
|
// above its min, and the mid has 20 to allocate above its min, which scaled
|
||||||
|
// by their bitrate priority is the same for each.
|
||||||
|
TEST_F(BitrateAllocatorTest, PriorityRateThreeObserversTwoAllocatedToMax) {
|
||||||
|
TestBitrateObserver observer_low;
|
||||||
|
TestBitrateObserver observer_mid;
|
||||||
|
TestBitrateObserver observer_high;
|
||||||
|
allocator_->AddObserver(&observer_low, 10, 40, 0, false, "low", 2.0);
|
||||||
|
// Scaled allocation above the min allocation is the same for these two,
|
||||||
|
// meaning they will get allocated their max at the same time.
|
||||||
|
// Scaled (target allocation) = (max - min) / bitrate priority
|
||||||
|
allocator_->AddObserver(&observer_mid, 10, 30, 0, false, "mid", 4.0);
|
||||||
|
allocator_->AddObserver(&observer_high, 10, 50, 0, false, "high", 8.0);
|
||||||
|
allocator_->OnNetworkChanged(110, 0, 0, kDefaultProbingIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_EQ(30u, observer_low.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(30u, observer_mid.last_bitrate_bps_);
|
||||||
|
EXPECT_EQ(50u, observer_high.last_bitrate_bps_);
|
||||||
|
|
||||||
|
allocator_->RemoveObserver(&observer_low);
|
||||||
|
allocator_->RemoveObserver(&observer_mid);
|
||||||
|
allocator_->RemoveObserver(&observer_high);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Reference in New Issue
Block a user