Index: webrtc/call/bitrate_allocator.cc |
diff --git a/webrtc/call/bitrate_allocator.cc b/webrtc/call/bitrate_allocator.cc |
index e82123a8b09c9069342aa4aaa62e0793d3da5190..7e6242210cff6c78845446a9b3f65b67dfce5fa5 100644 |
--- a/webrtc/call/bitrate_allocator.cc |
+++ b/webrtc/call/bitrate_allocator.cc |
@@ -15,7 +15,10 @@ |
#include <utility> |
#include "webrtc/base/checks.h" |
+#include "webrtc/base/logging.h" |
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" |
+#include "webrtc/system_wrappers/include/clock.h" |
+#include "webrtc/system_wrappers/include/metrics.h" |
namespace webrtc { |
@@ -28,13 +31,35 @@ const int kDefaultBitrateBps = 300000; |
const double kToggleFactor = 0.1; |
const uint32_t kMinToggleBitrateBps = 20000; |
+const int64_t kBweLogIntervalMs = 5000; |
+ |
+namespace { |
+ |
+double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) { |
+ RTC_DCHECK_GT(allocated_bitrate, 0u); |
+ if (protection_bitrate == 0) |
+ return 1.0; |
+ |
+ uint32_t media_bitrate = allocated_bitrate - protection_bitrate; |
+ return media_bitrate / static_cast<double>(allocated_bitrate); |
+} |
+} // namespace |
+ |
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer) |
: limit_observer_(limit_observer), |
bitrate_observer_configs_(), |
last_bitrate_bps_(kDefaultBitrateBps), |
last_non_zero_bitrate_bps_(kDefaultBitrateBps), |
last_fraction_loss_(0), |
- last_rtt_(0) {} |
+ last_rtt_(0), |
+ num_pause_events_(0), |
+ clock_(Clock::GetRealTimeClock()), |
+ last_bwe_log_time_(0) {} |
+ |
+BitrateAllocator::~BitrateAllocator() { |
+ RTC_LOGGED_HISTOGRAM_COUNTS_100("WebRTC.Call.NumberOfPauseEvents", |
+ num_pause_events_); |
+} |
void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps, |
uint8_t fraction_loss, |
@@ -46,11 +71,45 @@ void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps, |
last_fraction_loss_ = fraction_loss; |
last_rtt_ = rtt; |
+ // Periodically log the incoming BWE. |
+ int64_t now = clock_->TimeInMilliseconds(); |
+ if (now > last_bwe_log_time_ + kBweLogIntervalMs) { |
+ LOG(LS_INFO) << "Current BWE " << target_bitrate_bps; |
+ last_bwe_log_time_ = now; |
+ } |
+ |
ObserverAllocation allocation = AllocateBitrates(target_bitrate_bps); |
- for (const auto& kv : allocation) { |
- kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_); |
+ |
+ for (auto& config : bitrate_observer_configs_) { |
+ uint32_t allocated_bitrate = allocation[config.observer]; |
+ uint32_t protection_bitrate = config.observer->OnBitrateUpdated( |
+ allocated_bitrate, last_fraction_loss_, last_rtt_); |
+ |
+ if (allocated_bitrate == 0 && config.allocated_bitrate_bps > 0) { |
+ if (target_bitrate_bps > 0) |
+ ++num_pause_events_; |
+ // The protection bitrate is an estimate based on the ratio between media |
+ // and protection used before this observer was muted. |
+ uint32_t predicted_protection_bps = |
+ (1.0 - config.media_ratio) * config.min_bitrate_bps; |
+ LOG(LS_INFO) << "Pausing observer " << config.observer |
+ << " with configured min bitrate " << config.min_bitrate_bps |
+ << " and current estimate of " << target_bitrate_bps |
+ << " and protection bitrate " << predicted_protection_bps; |
+ } else if (allocated_bitrate > 0 && config.allocated_bitrate_bps == 0) { |
+ if (target_bitrate_bps > 0) |
+ ++num_pause_events_; |
+ LOG(LS_INFO) << "Resuming observer " << config.observer |
+ << ", configured min bitrate " << config.min_bitrate_bps |
+ << ", current allocation " << allocated_bitrate |
+ << " and protection bitrate " << protection_bitrate; |
+ } |
+ |
+ // Only update the media ratio if the observer got an allocation. |
+ if (allocated_bitrate > 0) |
+ config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate); |
+ config.allocated_bitrate_bps = allocated_bitrate; |
} |
- last_allocation_ = allocation; |
} |
void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, |
@@ -77,8 +136,14 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, |
if (last_bitrate_bps_ > 0) { |
// Calculate a new allocation and update all observers. |
allocation = AllocateBitrates(last_bitrate_bps_); |
- for (const auto& kv : allocation) |
- kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_); |
+ for (auto& config : bitrate_observer_configs_) { |
+ uint32_t allocated_bitrate = allocation[config.observer]; |
+ uint32_t protection_bitrate = config.observer->OnBitrateUpdated( |
+ allocated_bitrate, last_fraction_loss_, last_rtt_); |
+ config.allocated_bitrate_bps = allocated_bitrate; |
+ if (allocated_bitrate > 0) |
+ config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate); |
+ } |
} else { |
// Currently, an encoder is not allowed to produce frames. |
// But we still have to return the initial config bitrate + let the |
@@ -87,8 +152,6 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer, |
observer->OnBitrateUpdated(0, last_fraction_loss_, last_rtt_); |
} |
UpdateAllocationLimits(); |
- |
- last_allocation_ = allocation; |
} |
void BitrateAllocator::UpdateAllocationLimits() { |
@@ -122,17 +185,22 @@ void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) { |
int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) { |
rtc::CritScope lock(&crit_sect_); |
- const auto& it = last_allocation_.find(observer); |
- if (it != last_allocation_.end()) |
- return it->second; |
- |
- // This is a new observer that has not yet been started. Assume that if it is |
- // added, all observers would split the available bitrate evenly. |
- return last_non_zero_bitrate_bps_ / |
- static_cast<int>((bitrate_observer_configs_.size() + 1)); |
+ const auto& it = FindObserverConfig(observer); |
+ if (it == bitrate_observer_configs_.end()) { |
+ // This observer hasn't been added yet, just give it its fair share. |
+ return last_non_zero_bitrate_bps_ / |
+ static_cast<int>((bitrate_observer_configs_.size() + 1)); |
+ } else if (it->allocated_bitrate_bps == -1) { |
+ // This observer hasn't received an allocation yet, so do the same. |
+ return last_non_zero_bitrate_bps_ / |
+ static_cast<int>(bitrate_observer_configs_.size()); |
+ } else { |
+ // This observer already has an allocation. |
+ return it->allocated_bitrate_bps; |
+ } |
} |
-BitrateAllocator::ObserverConfigList::iterator |
+BitrateAllocator::ObserverConfigs::iterator |
BitrateAllocator::FindObserverConfig( |
const BitrateAllocatorObserver* observer) { |
for (auto it = bitrate_observer_configs_.begin(); |
@@ -202,9 +270,10 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation( |
LastAllocatedBitrate(observer_config) == 0) |
continue; |
- if (remaining_bitrate >= observer_config.min_bitrate_bps) { |
- allocation[observer_config.observer] = observer_config.min_bitrate_bps; |
- remaining_bitrate -= observer_config.min_bitrate_bps; |
+ uint32_t required_bitrate = MinBitrateWithHysteresis(observer_config); |
+ if (remaining_bitrate >= required_bitrate) { |
+ allocation[observer_config.observer] = required_bitrate; |
+ remaining_bitrate -= required_bitrate; |
} |
} |
} |
@@ -263,14 +332,11 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation( |
uint32_t BitrateAllocator::LastAllocatedBitrate( |
const ObserverConfig& observer_config) { |
- const auto& it = last_allocation_.find(observer_config.observer); |
- if (it != last_allocation_.end()) |
- return it->second; |
- |
// Return the configured minimum bitrate for newly added observers, to avoid |
// requiring an extra high bitrate for the observer to get an allocated |
// bitrate. |
- return observer_config.min_bitrate_bps; |
+ return observer_config.allocated_bitrate_bps == -1 ? |
+ observer_config.min_bitrate_bps : observer_config.allocated_bitrate_bps; |
} |
uint32_t BitrateAllocator::MinBitrateWithHysteresis( |
@@ -280,6 +346,15 @@ uint32_t BitrateAllocator::MinBitrateWithHysteresis( |
min_bitrate += std::max(static_cast<uint32_t>(kToggleFactor * min_bitrate), |
kMinToggleBitrateBps); |
} |
+ // Account for protection bitrate used by this observer in the previous |
+ // allocation. |
+ // Note: the ratio will only be updated when the stream is active, meaning a |
+ // paused stream won't get any ratio updates. This might lead to waiting a bit |
+ // longer than necessary if the network condition improves, but this is to |
+ // avoid too much toggling. |
+ if (observer_config.media_ratio > 0.0 && observer_config.media_ratio < 1.0) |
+ min_bitrate += min_bitrate * (1.0 - observer_config.media_ratio); |
+ |
return min_bitrate; |
} |