前言:
- AlrDetector即(应用受限区域探测器,Application limited region detector)模块,该模块利用某段时间值,以及这段时间发送的字节数判断当前输出网络流量是否受限。这些限制主要跟应用程序本身输出网络流量的能力有关,例如编码器性能,不能编码出设置的目标码率
- WebRTC GCC(Google Congestion Control) 模块中首先根据rtp包发送评估出一个实际码率(target_bitrate_bps),此时理论上程序会按照该码率进行数据发送,但是在实际的运行过程中,由于网络环境的复杂性,或者硬件的性能等一系列的原因如,编码速度跟不上,假设编码出的数据码率比(target_bitrate_bps)要低,使得数据发送码率低于目标码率,带宽利用率不足,发送码率占最大预算码率值比例低于某个值的话Alr(Application limited region)就会被触发了,恢复到某个值以上,Alr会停止。
工作流程
1)AlrDetector相关数据结构及其初始化
struct AlrDetectorConfig {
// Sent traffic ratio as a function of network capacity used to determine
// application-limited region. ALR region start when bandwidth usage drops
// below kAlrStartUsageRatio and ends when it raises above
// kAlrEndUsageRatio. NOTE: This is intentionally conservative at the moment
// until BW adjustments of application limited region is fine tuned.
double bandwidth_usage_ratio = 0.65;
double start_budget_level_ratio = 0.80;
double stop_budget_level_ratio = 0.50;
std::unique_ptr Parser();
};
- 配置结构,根据其注释可得当bandwidth降低到kAlrStartUsageRatio的时候和bandwidth高于kAlrEndUsageRatio的时候会出发ALR。
AlrDetector::AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log)
: conf_(config), alr_budget_(0, true), event_log_(event_log) {}
AlrDetector::AlrDetector(const WebRtcKeyValueConfig* key_value_config)
: AlrDetector(GetConfigFromTrials(key_value_config), nullptr) {}
AlrDetector::AlrDetector(const WebRtcKeyValueConfig* key_value_config,
RtcEventLog* event_log)
: AlrDetector(GetConfigFromTrials(key_value_config), event_log) {}
- 默认初始化设置
IntervalBudget
的can_build_up_underuse_成员变量为true。 -
IntervalBudget
根据输入的评估码率,计算当前还能发送多少数据。
2)AlrDetector主动函数调用栈及其原理
- 在
GoogCcNetworkController
模块中经过相应的算法处理,判断当前的网络状态,在MaybeTriggerOnNetworkChanged函数中会评估出一个新码率。 - 将新码率通过
AlrDetector
::SetEstimatedBitrate()函数作用到AlrDetector
模块,同时计算目标码率。 - 然后调用
IntervalBudget
::set_target_rate_kbps()将目标码率作用到IntervalBudget
。 - 最终
IntervalBudget
会根据目标码率更新预算。 - 其代码如下:
void AlrDetector::SetEstimatedBitrate(int bitrate_bps) {
RTC_DCHECK(bitrate_bps);
int target_rate_kbps =
static_cast(bitrate_bps) * conf_.bandwidth_usage_ratio / 1000;
alr_budget_.set_target_rate_kbps(target_rate_kbps);
}
- 现假设bitrate_bps为300kbps,bandwidth_usage_ratio = 0.8,bandwidth_usage_ratio:0.8 start_budget_level_ratio:0.4 stop_budget_level_ratio:-0.6
- 经过alr处理评估目标码率为300000 * 0.8 / 1000 = 240kbps
void IntervalBudget::set_target_rate_kbps(int target_rate_kbps) {
target_rate_kbps_ = target_rate_kbps;
max_bytes_in_budget_ = (kWindowMs * target_rate_kbps_) / 8;
bytes_remaining_ = std::min(std::max(-max_bytes_in_budget_, bytes_remaining_),
max_bytes_in_budget_);
}
- 在IntervalBudget中根据目标码率配合时间窗口进行发送预算。
- 按照上面的假设经计算得出max_bytes_in_budget_按照500ms最大时间窗口进行最大预算为15000字节。
- 计算剩余预算,bytes_remaining_首次默认情况下为0,那么经过赋值后本次剩余的预算为0(初始值)。
namespace {
constexpr int64_t kWindowMs = 500;
}
3)AlrDetector函数回调栈及其原理
- 静paced模块发送rtp包时,发送成功后会进行发送成功回调,经call模块最后作用到
GoogCcNetworkController
模块 - 最后在
AlrDetector
模块中根据本次发送的数据大小进行发送预算代码如下:
void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) {
if (!last_send_time_ms_.has_value()) {
last_send_time_ms_ = send_time_ms;
// Since the duration for sending the bytes is unknwon, return without
// updating alr state.
return;
}
int64_t delta_time_ms = send_time_ms - *last_send_time_ms_;
last_send_time_ms_ = send_time_ms;
alr_budget_.UseBudget(bytes_sent);
alr_budget_.IncreaseBudget(delta_time_ms);
bool state_changed = false;
if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio &&
!alr_started_time_ms_) {
alr_started_time_ms_.emplace(rtc::TimeMillis());
state_changed = true;
} else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio &&
alr_started_time_ms_) {
state_changed = true;
alr_started_time_ms_.reset();
}
if (event_log_ && state_changed) {
event_log_->Log(
std::make_unique(alr_started_time_ms_.has_value()));
}
}
- 首先得到时延(当前次发送时间-上一次发送的时间),并更新上次发送时间供下一次使用。
- 现在假设每隔10ms发送一次数据,并且以带宽利用率的90%进行数据发送,将码率和时间间隔转换成实际发送的字节数如下:
- kEstimatedBitrateBps * usage_percentage_ * kTimeStepMs / (8 * 100 * 1000) ,其中usage_percentage_ 为90,kTimeStepMs 为10ms,kEstimatedBitrateBps为300000bps 得到每隔10ms将发送337字节的数据。
- 那么首次发送bytes_sent = 337,delta_time_ms = 10,后续以该速度进行发送。
- 调用alr_budget_.UseBudget(bytes_sent)对剩余要发送的数据量进行更新。
- 调用alr_budget_.IncreaseBudget(bytes_sent)来增加预算。
void IntervalBudget::UseBudget(size_t bytes) {
bytes_remaining_ = std::max(bytes_remaining_ - static_cast(bytes),
-max_bytes_in_budget_);
}
- 实际本次发送了多少字节,用bytes_remaining_和bytes相减然后和最大预算的反值取最大
void IntervalBudget::IncreaseBudget(int64_t delta_time_ms) {
int64_t bytes = target_rate_kbps_ * delta_time_ms / 8;
if (bytes_remaining_ < 0 || can_build_up_underuse_) {
// We overused last interval, compensate this interval.
bytes_remaining_ = std::min(bytes_remaining_ + bytes, max_bytes_in_budget_);
} else {
// If we underused last interval we can't use it this interval.
bytes_remaining_ = std::min(bytes, max_bytes_in_budget_);
}
}
按照目标码率(240kbps),上面假设这个目标码率进行预算增加
最后调用alr_budget_.budget_ratio()和start_budget_level_ratio以及stop_budget_level_ratio进行比较
double IntervalBudget::budget_ratio() const {
if (max_bytes_in_budget_ == 0)
return 0.0;
return static_cast(bytes_remaining_) / max_bytes_in_budget_;
}
- budget_ratio()表示剩余要发送的数据占最大预算发送数据
max_bytes_in_budget_
的比例。 - 回到AlrDetector::OnBytesSent()函数如果
IntervalBudget::budget_ratio()
大于start_budget_level_ratio
,即剩余数据还有很多,发送码率低了,带宽没充分利用,Alr将被触发,若IntervalBudget::budget_ratio()
小于stop_budget_level_ratio
时,alr_started_time_ms_
将被重置,Alr停止。 - alr如果被触发则更新alr_started_time_ms_为rtc::TimeMillis(),被设置成当前时间。
- 最终
GoogCcNetworkController
模块会通过调用alr模块的GetApplicationLimitedRegionStartTime()函数来得到受限区域的时间,进而进行新的码率估计。 - 该函数在
GoogCcNetworkController
模块的三个地方被调用:
NetworkControlUpdate GoogCcNetworkController::OnProcessInterval(
ProcessInterval msg) {
...
bandwidth_estimation_->UpdateEstimate(msg.at_time);
absl::optional start_time_ms =
alr_detector_->GetApplicationLimitedRegionStartTime();
probe_controller_->SetAlrStartTimeMs(start_time_ms);
}
NetworkControlUpdate GoogCcNetworkController::OnSentPacket(
SentPacket sent_packet) {
alr_detector_->OnBytesSent(sent_packet.size.bytes(),
sent_packet.send_time.ms());
acknowledged_bitrate_estimator_->SetAlr(
alr_detector_->GetApplicationLimitedRegionStartTime().has_value());
.....
}
NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
absl::optional alr_start_time =
alr_detector_->GetApplicationLimitedRegionStartTime();
}
- 以上分别做了什么?后续进行分析
4)总结:
-
AlrDetector
模块的核心作用是根据每次数据的发送的字节数,以500ms为时间窗口,以及实际的发送码率,对当前网络带宽是否被有效利用或是否已经发送过载,进行检测。其最终的产物是一个时间区域的起点(受限区域起点)。 - 同理
GoogCcNetworkController
模块在其工作流程中会通过调用AlrDetector
模块的GetApplicationLimitedRegionStartTime()来获取受限时间起点时间是否触发,如果触发则会进行新的码率估计。