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}
diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc
index 2692c00..004262e 100644
--- a/call/bitrate_allocator.cc
+++ b/call/bitrate_allocator.cc
@@ -12,6 +12,7 @@
#include "call/bitrate_allocator.h"
#include <algorithm>
+#include <cmath>
#include <memory>
#include <utility>
@@ -48,7 +49,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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
diff --git a/call/bitrate_allocator.h b/call/bitrate_allocator.h
index cf518f6..73258ad 100644
--- a/call/bitrate_allocator.h
+++ b/call/bitrate_allocator.h
@@ -86,7 +86,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
std::unique_ptr<rtc::BitrateAllocationStrategy> bitrate_allocation_strategy_
RTC_GUARDED_BY(&sequenced_checker_);
};
+
} // namespace webrtc
#endif // CALL_BITRATE_ALLOCATOR_H_
diff --git a/call/bitrate_allocator_unittest.cc b/call/bitrate_allocator_unittest.cc
index 31828b6..c0786a0 100644
--- a/call/bitrate_allocator_unittest.cc
+++ b/call/bitrate_allocator_unittest.cc
@@ -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 @@
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