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:
Seth Hampson
2017-11-14 10:49:06 -08:00
committed by Commit Bot
parent 1b23e3782c
commit fe73d6ab87
3 changed files with 392 additions and 13 deletions

View File

@ -12,6 +12,7 @@
#include "call/bitrate_allocator.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <utility>
@ -48,7 +49,6 @@ double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
: limit_observer_(limit_observer),
bitrate_observer_configs_(),
last_bitrate_bps_(0),
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
last_fraction_loss_(0),
@ -128,8 +128,11 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
uint32_t max_bitrate_bps,
uint32_t pad_up_bitrate_bps,
bool enforce_min_bitrate,
std::string track_id) {
std::string track_id,
double bitrate_priority) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
RTC_DCHECK_GT(bitrate_priority, 0);
RTC_DCHECK(std::isnormal(bitrate_priority));
auto it = FindObserverConfig(observer);
// 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->pad_up_bitrate_bps = pad_up_bitrate_bps;
it->enforce_min_bitrate = enforce_min_bitrate;
it->bitrate_priority = bitrate_priority;
} else {
bitrate_observer_configs_.push_back(
ObserverConfig(observer, min_bitrate_bps, max_bitrate_bps,
pad_up_bitrate_bps, enforce_min_bitrate, track_id));
bitrate_observer_configs_.push_back(ObserverConfig(
observer, min_bitrate_bps, max_bitrate_bps, pad_up_bitrate_bps,
enforce_min_bitrate, track_id, bitrate_priority));
}
ObserverAllocation allocation;
@ -287,7 +291,8 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates(
if (!EnoughBitrateForAllObservers(bitrate, sum_min_bitrates))
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)
return NormalRateAllocation(bitrate, sum_min_bitrates);
@ -357,17 +362,30 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
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) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
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;
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)
DistributeBitrateEvenly(bitrate, true, 1, &allocation);
DistributeBitrateRelatively(bitrate, observers_capacities, &allocation);
return allocation;
}
@ -468,4 +486,80 @@ bool BitrateAllocator::EnoughBitrateForAllObservers(uint32_t bitrate,
}
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

View File

@ -86,7 +86,11 @@ class BitrateAllocator {
uint32_t max_bitrate_bps,
uint32_t pad_up_bitrate_bps,
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
// allocation.
@ -104,14 +108,14 @@ class BitrateAllocator {
bitrate_allocation_strategy);
private:
// Note: All bitrates for member variables and methods are in bps.
struct ObserverConfig : rtc::BitrateAllocationStrategy::TrackConfig {
ObserverConfig(BitrateAllocatorObserver* observer,
uint32_t min_bitrate_bps,
uint32_t max_bitrate_bps,
uint32_t pad_up_bitrate_bps,
bool enforce_min_bitrate,
std::string track_id)
std::string track_id,
double bitrate_priority)
: TrackConfig(min_bitrate_bps,
max_bitrate_bps,
enforce_min_bitrate,
@ -119,12 +123,17 @@ class BitrateAllocator {
observer(observer),
pad_up_bitrate_bps(pad_up_bitrate_bps),
allocated_bitrate_bps(-1),
media_ratio(1.0) {}
media_ratio(1.0),
bitrate_priority(bitrate_priority) {}
BitrateAllocatorObserver* observer;
uint32_t pad_up_bitrate_bps;
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;
};
// Calculates the minimum requested send bitrate and max padding bitrate and
@ -140,10 +149,18 @@ class BitrateAllocator {
ObserverAllocation AllocateBitrates(uint32_t bitrate);
// Allocates zero bitrate to all observers.
ObserverAllocation ZeroRateAllocation();
// Allocates bitrate to observers when there isn't enough to allocate the
// minimum to all observers.
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,
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,
uint32_t sum_max_bitrates);
@ -162,6 +179,17 @@ class BitrateAllocator {
bool EnoughBitrateForAllObservers(uint32_t bitrate,
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_;
LimitObserver* const limit_observer_ RTC_GUARDED_BY(&sequenced_checker_);
// 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_
RTC_GUARDED_BY(&sequenced_checker_);
};
} // namespace webrtc
#endif // CALL_BITRATE_ALLOCATOR_H_

View File

@ -17,7 +17,8 @@
#include "test/gmock.h"
#include "test/gtest.h"
using testing::NiceMock;
using ::testing::NiceMock;
using ::testing::_;
namespace webrtc {
@ -518,4 +519,259 @@ TEST_F(BitrateAllocatorTest, PassProbingInterval) {
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