diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc index 2692c00667..004262e262 100644 --- a/call/bitrate_allocator.cc +++ b/call/bitrate_allocator.cc @@ -12,6 +12,7 @@ #include "call/bitrate_allocator.h" #include +#include #include #include @@ -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 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 cf518f6c3d..73258ad6d6 100644 --- a/call/bitrate_allocator.h +++ b/call/bitrate_allocator.h @@ -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 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 31828b6cc1..c0786a073d 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 @@ 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