流媒体学习之路(WebRTC)——GCC整体分析(1)
——
我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost
目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全配置,提供全面的可视化算法观察能力。
欢迎大家使用
——
文章目录
本文大量引用了这位大佬的内容:https://www.cnblogs.com/ishen/category/1619028.html
一、简介
下图展示码率输出流向的模块图,自定义了同级关系线来方便理解,其他关系线则是使用了类图的线。
ProbeBitrateEstimator : 根据feedback计算探测码率,PacingController中会将包按照cluster进行划分,transport-CC报文能得到包所属的cluster以及发送和接收信息,通过发送和接收的数据大小比判断是否到达链路上限从而进行带宽探测
AcknowledgedBitrateEstimator : 估算当前的吞吐量
BitrateEstimator :使用滑动窗口 + 卡尔曼滤波计算当前发送吞吐量
DelayBasedBwe : 基于延迟预估码率
TrendlineEstimator : 使用线性回归计算当前网络拥堵情况
AimdRateControl : 通过TrendLine预测出来的网络状态对码率进行aimd方式调整
SendSideBandwidthEstimation : 基于丢包计算预估码率,结合延迟预估码率,得到最终的目标码率
ProbeController : 探测控制器,通过目标码率判断下次是否探测,探测码率大小
CongestionWindowPushbackController : 基于当前的rtt设置一个时间窗口,同时基于当前的码率设置当前时间窗口下的数据量,通过判断当前窗口的使用量,如果使用量过大的时候,降低编码时使用的目标码率,加速窗口消退,减少延迟
AlrDetector : 应用(码率)受限检测,检测当前的发送码率是否和目标码率由于编码器等原因相差过大受限了,受限情况下会触发带宽预测过程的特殊处理
NetworkStateEstimator 、 NetworkStatePredictor : 此两者属于待开发类,只是在代码中有,但是还没开发完,没用上.
接下来会以GoogCcNetworkController的码率预估过程为例, 详细介绍webrtc中带宽控制的架构和过程。
二、类分析
2.1 RtpTransportControllerSend
2.1.1 主要成员对象
该类为整体封装类,包含了以下几个主要成员对象:
// 支持rtp数据包路由的模块,主要为数据发送工作
PacketRouter* packet_router_;
// 发送控制模块,结合带宽估计进行发包控制
PacedSender pacer_;
// 带宽估计码率的回调者,在外部实现一个该类型的类传入来做接收
TargetTransferRateObserver* observer_;
// 网络控制模块接口,支持外部实现网络控制部分
NetworkControllerFactoryInterface* const controller_factory_override_;
// 反馈数据适配模块
TransportFeedbackAdapter transport_feedback_adapter_;
// 拥塞控制事件处理者
std::unique_ptr<CongestionControlHandler> control_handler_;
// 真正的网络控制模块
std::unique_ptr<NetworkControllerInterface> controller_ ;
2.1.2 关键调用函数
该类在使用默认网络控制模块时,对外暴露了两类接口:码率变化的观察者(回调类)、输入发送信息以及反馈信息的函数。观察者部分不展开说,下面对函数的逻辑进行简单分析。
2.1.2.1 函数分析
void OnAddPacket(const RtpPacketSendInfo& packet_info) override;
void OnTransportFeedback(const RTC::RTCP::FeedbackRtpTransportPacket& feedback) override;
void OnSentPacket(const rtc::SentPacket& sent_packet, size_t size) override;
OnAddPacket函数是在发送钱,给feedback模块记录发送信息使用的。带宽估计需要对比发送和接收的byte,因此两个信息都需要记录。
void RtpTransportControllerSend::OnAddPacket(
const RtpPacketSendInfo& packet_info) {
// 往里放入发包信息,当存在额外数据流拓展需求时,把额外的包数据一起统计放入,否自为 0
//(额外数据包开销,例如 IP 和 UDP 数据包标头,在某些情况下可能构成出站流量的重要部分。传统上,此开销不包含在 WebRTC 比特率计算中。通过包含数据包开销,WebRTC 的带宽估计将能够更准确地估计可用链路容量,这将有助于提高整体质量,尤其是在一些低带宽场景中。)
// 如果需要打开,则需要将特性加入 "WebRTC-SendSideBwe-WithOverhead"
transport_feedback_adapter_.AddPacket(
packet_info,
send_side_bwe_with_overhead_ ?
transport_overhead_bytes_per_packet_.load() : 0,
Timestamp::ms(cls::GetCurrentStamp64()));
}
OnTransportFeedback函数在接收到反馈包时放入feedback模块使用的。
void RtpTransportControllerSend::OnTransportFeedback(
const RTC::RTCP::FeedbackRtpTransportPacket& feedback) {
// 放入反馈信息,并传入当前时间戳用于计算数据包往返的rtt
absl::optional<TransportPacketsFeedback> feedback_msg =
transport_feedback_adapter_.ProcessTransportFeedback(
feedback, Timestamp::ms(cls::GetCurrentStamp64()));
// 当反馈信息更新成功(内部成功计算发送——接收两个时刻的信息),则把计算好的反馈信息放入网络控制模块进行带宽估计。
if (feedback_msg)
PostUpdates(controller_
->OnTransportPacketsFeedback(*feedback_msg));
// 网络控制更新完成后,更新发送控制模块的飞行数据
pacer_.UpdateOutstandingData(
transport_feedback_adapter_.GetOutstandingData().bytes());
}
OnSentPacket函数也为记录发送包时的信息,与OnAddPacket不同的是时序问题,只有调用add之后才能调用send,因为内部的send_time_history两个同名函数的调用时序需要如此。
void RtpTransportControllerSend::OnSentPacket(
const rtc::SentPacket& sent_packet, size_t size) {
// 将发送包放入feedback处理模块,从已add的包中找到信息返回
absl::optional<SentPacket> packet_msg =
transport_feedback_adapter_.ProcessSentPacket(sent_packet);
// 如果包存在,则会将该包的发送信息放入控制模块进行计算
if (packet_msg)
PostUpdates(controller_->OnSentPacket(*packet_msg));
// 发送模块更新飞行数据
pacer_.UpdateOutstandingData(
transport_feedback_adapter_.GetOutstandingData().bytes());
}
2.1.2.2 回调触发
每次反馈计算完成后,都会触发可用码率变化的函数。
void RtpTransportControllerSend::UpdateControlState() {
// 获取控制处理模块里的码率更新,一旦发生变化,update就不为空
absl::optional<TargetTransferRate> update = control_handler_->GetUpdate();
if (!update)
return;
// We won't create control_handler_ until we have an observers.
RTC_DCHECK(observer_ != nullptr);
MS_ASSERT(observer_ != nullptr, "no observer");
// 触发上层传入的观察者中的回调函数。
observer_->OnTargetTransferRate(*update);
}
2.1.3 流程图
下图展示了上述的三个主要函数的时序流程。
● 最初发送包时,先OnAddPacket把数据包填入send_time_history中;
● 随后发送包时,调用OnSentPacket查看send_time_history中记录的包数据,取出放入计算模块,随后检查是否需要进行码率调整(PostUpdates);
● 最后在反馈包到达时,调用OnTransportFeedback进行放入估计模块进行计算,随后检查是否需要码率调整
2.2 TransportFeedbackAdapter
TransportFeedbackAdapter这个类在上述的发送控制类中作为指针对象存在。该类主要是做发送信息以及回调信息的统计和适配的工作。
2.2.1 主要成员对象
// 记录发送数据的类
SendTimeHistory send_time_history_;
// 当前时间的偏移量
int64_t current_offset_ms_;
// 最后一次发送的时间戳
int64_t last_timestamp_us_;
// 最新的反馈数据存放数组
std::vector<PacketFeedback> last_packet_feedback_vector_;
// MS_NOTE: local_net_id_ and remote_net_id_ are not set.
// 自身的网络id
uint16_t local_net_id_;
// 对端的网络id
uint16_t remote_net_id_;
2.2.2 关键调用函数
该类的主要调用函数与上述类的三个近似,分为:
// 对应上述的添加函数
void AddPacket(const RtpPacketSendInfo& packet_info,
size_t overhead_bytes,
Timestamp creation_time);
// 对应上述发送函数
absl::optional<SentPacket> ProcessSentPacket(
const rtc::SentPacket& sent_packet);
// 对应上述反馈处理函数
void OnTransportFeedback(const
RTC::RTCP::FeedbackRtpTransportPacket& feedback);
2.2.3 函数分析
AddPacket函数可以将待发送的信息记录到send_time_history类中。
void TransportFeedbackAdapter::AddPacket(
const RtpPacketSendInfo& packet_info,
size_t overhead_bytes,
Timestamp creation_time) {
{
// 按包序号和数据量记录产生一个PacketFeedback类型
PacketFeedback packet_feedback(
creation_time.ms(),
packet_info.transport_sequence_number,
packet_info.length + overhead_bytes,
local_net_id_,
remote_net_id_,
packet_info.pacing_info);
// 存入包ssrc和序号
if (packet_info.has_rtp_sequence_number) {
packet_feedback.ssrc = packet_info.ssrc;
packet_feedback.rtp_sequence_number =
packet_info.rtp_sequence_number;
}
// 按当前时间清楚超时数据
send_time_history_.RemoveOld(creation_time.ms());
// 添加新的包到send_time_history对象中
send_time_history_.AddNewPacket(
std::move(packet_feedback));
}
// 通知这个适配层的所有观察者,目前看这个没啥用,没有赋值的地方
{
for (auto* observer : observers_) {
observer->OnPacketAdded(packet_info.ssrc,
packet_info.transport_sequence_number);
}
}
}
ProcessSentPacket函数则是对应上述的OnSentPacket类中。
absl::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
const rtc::SentPacket& sent_packet) {
// 如果这个数据包序号不是-1,那么就会进入send_time_history_对象中查找信息。一旦add过该信息就会返回对应的发送状态
if (sent_packet.info.included_in_feedback || sent_packet.packet_id != -1) {
SendTimeHistory::Status send_status =
send_time_history_.OnSentPacket(
sent_packet.packet_id, sent_packet.send_time_ms);
// 找到该数据包信息就生成对应的PacketFeedback
absl::optional<PacketFeedback> packet;
if (allow_duplicates_ ||
send_status != SendTimeHistory::Status::kDuplicate) {
packet = send_time_history_.GetPacket(sent_packet.packet_id);
}
// 如果该包存在就把包信息记录到msg中
if (packet) {
SentPacket msg;
msg.size = DataSize::bytes(packet->payload_size);
msg.send_time = Timestamp::ms(packet->send_time_ms);
msg.sequence_number = packet->long_sequence_number;
msg.prior_unacked_data = DataSize::bytes(
packet->unacknowledged_data);
msg.data_in_flight =
send_time_history_.GetOutstandingData(
local_net_id_, remote_net_id_);
return msg;
}
// 如果该数据包不存在,那么久把这个包添加到不在轨队列
} else if (sent_packet.info.included_in_allocation) {
send_time_history_.AddUntracked(
sent_packet.info.packet_size_bytes,
sent_packet.send_time_ms);
}
return absl::nullopt;
}
ProcessTransportFeedback函数会对整体的数据进行整合,反馈给上层
absl::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ProcessTransportFeedback(
const RTC::RTCP::FeedbackRtpTransportPacket& feedback,
Timestamp feedback_receive_time) {
// 获取当前正在飞行的数据
DataSize prior_in_flight = GetOutstandingData();
// 通过回调内容取出最新的数据,计算好接收时间返回
last_packet_feedback_vector_ =
GetPacketFeedbackVector(feedback,
feedback_receive_time);
// 通知观察者,没什么用
{
for (auto* observer : observers_) {
observer->OnPacketFeedbackVector(
last_packet_feedback_vector_);
}
}
// 判断该数据是否为空
std::vector<PacketFeedback> feedback_vector =
last_packet_feedback_vector_;
if (feedback_vector.empty())
return absl::nullopt;
// 整合数据
TransportPacketsFeedback msg;
for (const PacketFeedback& rtp_feedback : feedback_vector) {
// 如果能找到发送包
if (rtp_feedback.send_time_ms !=
PacketFeedback::kNoSendTime) {
// 转换数据到PacketResult类型中
auto feedback =
NetworkPacketFeedbackFromRtpPacketFeedback(
rtp_feedback);
// 将转换好的反馈内容放到结果中
msg.packet_feedbacks.push_back(feedback);
// 如果没有发送记录,那么需要在未知到达内容中加入未初始化的到达时间
} else if (rtp_feedback.arrival_time_ms ==
PacketFeedback::kNotReceived) {
msg.sendless_arrival_times.push_back(
Timestamp::PlusInfinity());
// 如果没有发送记录,但是有接收时间那么把接收时间放入
} else {
msg.sendless_arrival_times.push_back(
Timestamp::ms(rtp_feedback.arrival_time_ms));
}
}
{
// 经过上述统计后,把第一个未确认的时间统计出来,也反馈上层加入估计计算
absl::optional<int64_t> first_unacked_send_time_ms =
send_time_history_.GetFirstUnackedSendTime();
if (first_unacked_send_time_ms)
msg.first_unacked_send_time =
Timestamp::ms(*first_unacked_send_time_ms);
}
msg.feedback_time = feedback_receive_time;
msg.prior_in_flight = prior_in_flight;
msg.data_in_flight = GetOutstandingData();
return msg;
}
2.2.4 时序图
2.3 GoogCcNetworkController
2.3.1 主要成员对象
GoogCcNetworkController类继承了NetworkControllerInterface接口类。该类的成员可以主要包含以下内容:
// 丢包带宽估计器
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
// 限制探测器
std::unique_ptr<AlrDetector> alr_detector_;
// 探测估计器
std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
// 网络状态估计器
std::unique_ptr<NetworkStateEstimator> network_estimator_;
// 网络状态预估器
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
// 延迟带宽估计器
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
// ack数据估计器
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
2.3.2 关键调用函数
该类的主要调用函数与上述类的三个近似,只是多了一个定时检测带宽的函数,分为:
// 发送函数
NetworkControlUpdate OnSentPacket(SentPacket msg) override;
// 反馈数据处理函数
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback msg) override;
// 定时触发带宽检测函数
NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
2.3.3 函数分析
经过反馈适配器后,进入到控制模块只需要在发送、反馈的时候调用两个函数。这两个函数为:OnSentPacket、OnTransportPacketsFeedback。
OnSentPacket函数主要给网络状态相关模块以及拥塞窗口进行发送信息设置。
NetworkControlUpdate GoogCcNetworkController::OnSentPacket(
SentPacket sent_packet) {
// 限制探测器放入数据大小以及发送时间
alr_detector_->OnBytesSent(sent_packet.size.bytes(),
sent_packet.send_time.ms());
// ack数据估计模块设置受限区域的开始时间
acknowledged_bitrate_estimator_->SetAlr(
alr_detector_->
GetApplicationLimitedRegionStartTime().has_value());
// 第一个数据包未发送的话,就记录第一个发送的时间
if (!first_packet_sent_) {
first_packet_sent_ = true;
// Initialize feedback time to send time to allow estimation of RTT until
// first feedback is received.
bandwidth_estimation_->
UpdatePropagationRtt(sent_packet.send_time,
TimeDelta::Zero());
}
// 丢包估计模块设置发送数据
bandwidth_estimation_->OnSentPacket(sent_packet);
bool network_changed = false;
// 拥塞窗口放入已发送数据
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->
UpdateOutstandingData(
sent_packet.data_in_flight.bytes());
network_changed = true;
}
// 一旦数据发生变化则更新当前状态,反馈变化(一般预示着拥塞窗口已满)
// 否则正常反回空的更新结构体
if (network_changed) {
NetworkControlUpdate update;
MaybeTriggerOnNetworkChanged(&update,
sent_packet.send_time);
return update;
} else {
return NetworkControlUpdate();
}
}
OnTransportPacketsFeedback函数则会所有带宽估计模块进行计算。
NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
// 异常检测
if (report.packet_feedbacks.empty()) {
// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
// against building very large network queues.
return NetworkControlUpdate();
}
// 拥塞窗口设置输出码率
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->
UpdateOutstandingData(
report.data_in_flight.bytes());
}
// 初始化时间间隔
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
Timestamp max_recv_time = Timestamp::MinusInfinity();
// 取出发送接收结果
std::vector<PacketResult> feedbacks =
report.ReceivedWithSendInfo();
// 取出最大接收时间
for (const auto& feedback : feedbacks)
max_recv_time = std::max(max_recv_time,
feedback.receive_time);
// 计算rtt
for (const auto& feedback : feedbacks) {
// 计算每个包的rtt
TimeDelta feedback_rtt =
report.feedback_time - feedback.sent_packet.send_time;
// 计算在发送端等待的时间(排队发出,取该队列最大差值就是等待的时间)
TimeDelta min_pending_time = feedback.receive_time -
max_recv_time;
// 计算纯网络rtt,减去排队的时间则是纯网络的rtt
TimeDelta propagation_rtt = feedback_rtt -
min_pending_time;
// 取出最大反馈rtt间隔,使用两个rtt的大值来进行计算
max_feedback_rtt = std::max(max_feedback_rtt,
feedback_rtt);
// 取出排队的最小时间
min_propagation_rtt = std::min(min_propagation_rtt,
propagation_rtt);
}
// rtt存在,则把rtt放入计算队列
if (max_feedback_rtt.IsFinite()) {
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
const size_t kMaxFeedbackRttWindow = 32;
// 队列过大则淘汰最早的内容
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
feedback_max_rtts_.pop_front();
// TODO(srte): Use time since last unacknowledged packet.
// 丢包带宽估计中的最小纯网络传输rtt更新
bandwidth_estimation_->
UpdatePropagationRtt(report.feedback_time,
min_propagation_rtt);
}
// 更新loss和delay estimation的rtt,注意
// loss使用的是feedback_min_rtt,丢包需要时刻检测最低rtt来响应
// delay使用的是feedback_max_rtt,延迟则尽量拿到最差的网络带宽点
if (packet_feedback_only_) {
// 计算平均feed_back_max_rtt
if (!feedback_max_rtts_.empty()) {
// 计算平均feedback_rtt
int64_t sum_rtt_ms =
std::accumulate(feedback_max_rtts_.begin(),
feedback_max_rtts_.end(), 0);
int64_t mean_rtt_ms = sum_rtt_ms /
feedback_max_rtts_.size();
// 更新bwe的rtt
if (delay_based_bwe_)
delay_based_bwe_->
OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
}
// 计算feedback_min_rtt,更新bandwidth_estimation_ rtt,丢包估计
TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
// 这块逻辑和上面计算feedback_max_rtt一样,写了重复代码
for (const auto& packet_feedback : feedbacks) {
TimeDelta pending_time =
packet_feedback.receive_time - max_recv_time;
TimeDelta rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
// Value used for predicting NACK round trip time in FEC controller.
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
}
if (feedback_min_rtt.IsFinite()) {
bandwidth_estimation_->UpdateRtt(feedback_min_rtt,
report.feedback_time);
}
// 更新丢包率
// 上次更新丢包后到现在应该收到的包的总数
expected_packets_since_last_loss_update_ +=
report.PacketsWithFeedback().size();
for (const auto& packet_feedback :
report.PacketsWithFeedback()) {
if (packet_feedback.receive_time.IsInfinite())
lost_packets_since_last_loss_update_ += 1;
}
// 周期内,feedback_time大于丢包更新时间了,更新丢包率
if (report.feedback_time > next_loss_update_) {
next_loss_update_ = report.feedback_time +
kLossUpdateInterval;
// 把期待收到的包和实际ack的包放入
bandwidth_estimation_->UpdatePacketsLost(
lost_packets_since_last_loss_update_,
expected_packets_since_last_loss_update_,
report.feedback_time);
expected_packets_since_last_loss_update_ = 0;
lost_packets_since_last_loss_update_ = 0;
}
}
// 获取当前是否处于alr(是否到达限制区域)
absl::optional<int64_t> alr_start_time =
alr_detector_->GetApplicationLimitedRegionStartTime();
// 告知acknowledge和probe_controller,当前不再处于alr
if (previously_in_alr_ && !alr_start_time.has_value()) {
int64_t now_ms = report.feedback_time.ms();
acknowledged_bitrate_estimator_->
SetAlrEndedTime(report.feedback_time);
probe_controller_->SetAlrEndedTimeMs(now_ms);
}
previously_in_alr_ = alr_start_time.has_value();
// ack处理器,预估接收端吞吐量
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
report.SortedByReceiveTime());
auto acknowledged_bitrate = acknowledged_bitrate_estimator_->
bitrate();
// 将其设置到bandwidth_estimation_中去更新链路容量(link_capacity)
bandwidth_estimation_->
SetAcknowledgedRate(acknowledged_bitrate,
report.feedback_time);
bandwidth_estimation_->IncomingPacketFeedbackVector(report);
for (const auto& feedback : report.SortedByReceiveTime()) {
if (feedback.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe) {
// probe_estimator 根据返回的feedback更新带宽探测的计算
probe_bitrate_estimator_->
HandleProbeAndEstimateBitrate(feedback);
}
}
if (network_estimator_) {
// 这一块暂时还在开发中,目前还未使用,不太清楚干什么
network_estimator_->OnTransportPacketsFeedback(report);
auto prev_estimate = estimate_;
estimate_ = network_estimator_->GetCurrentEstimate();
// TODO(srte): Make OnTransportPacketsFeedback signal whether the state
// changed to avoid the need for this check.
if (estimate_ && (!prev_estimate || estimate_->
last_feed_time != prev_estimate->last_feed_time)) {
event_log_->
Log(std::make_unique<rtceventremoteestimate>(
estimate_->link_capacity_lower, estimate_->
link_capacity_upper));
}
}
// 获取上面循环更新probe_estimator的最终的结果
absl::optional<datarate> probe_bitrate =
probe_bitrate_estimator_->
FetchAndResetLastEstimatedBitrate();
// 如果enable probe < network_estimate时 忽略probe的特性,则忽略probe_bitrate
if (ignore_probes_lower_than_network_estimate_ &&
probe_bitrate && estimate_ && *probe_bitrate <
delay_based_bwe_->last_estimate() &&
*probe_bitrate < estimate_->link_capacity_lower) {
probe_bitrate.reset();
}
// 如果enable
// 将probe略小于throughput_estimate_(预估吞吐量)的特性
// 对probe现在acknowledged_bitrate(链路吞吐量)下
if (limit_probes_lower_than_throughput_estimate_ &&
probe_bitrate && acknowledged_bitrate) {
// Limit the backoff to something slightly below the acknowledged
// bitrate. ("Slightly below" because we want to drain the queues
// if we are actually overusing.)
// The acknowledged bitrate shouldn't normally be higher than the delay
// based estimate, but it could happen e.g. due to packet bursts or
// encoder overshoot. We use std::min to ensure that a probe result
// below the current BWE never causes an increase.
DataRate limit =
std::min(delay_based_bwe_->last_estimate(),
*acknowledged_bitrate *
kProbeDropThroughputFraction);
probe_bitrate = std::max(*probe_bitrate, limit);
}
NetworkControlUpdate update;
bool recovered_from_overuse = false;
bool backoff_in_alr = false;
// 使用feedback进行bwe预测,获得基于延迟的码率估计
DelayBasedBwe::Result result;
result = delay_based_bwe_->IncomingPacketFeedbackVector(
report, acknowledged_bitrate, probe_bitrate, estimate_,
alr_start_time.has_value());
if (result.updated) {
// 预估码率更新了
if (result.probe) {
// bwe使用了探测码率进行重设
// bandwidth_estimation_也进行重设sendbitrate
bandwidth_estimation_->
SetSendBitrate(result.target_bitrate,
report.feedback_time);
}
// Since SetSendBitrate now resets the delay-based estimate, we have to
// call UpdateDelayBasedEstimate after SetSendBitrate.
// 更新bandwidth_estimation_中基于延迟的估计码率
bandwidth_estimation_->
UpdateDelayBasedEstimate(report.feedback_time,
result.target_bitrate);
// Update the estimate in the ProbeController, in case we want to probe.
// 将变化的码率通知到probe_controller, alr_detector, congestion_window等
MaybeTriggerOnNetworkChanged(&update,
report.feedback_time);
}
recovered_from_overuse = result.recovered_from_overuse;
backoff_in_alr = result.backoff_in_alr;
if (recovered_from_overuse) {
// 从overuse中恢复了,重设alr start 时间
probe_controller_->SetAlrStartTimeMs(alr_start_time);
// 获取接下来要做带宽探测的参数,放到update中
auto probes = probe_controller_->
RequestProbe(report.feedback_time.ms());
update.probe_cluster_configs.insert(
update.probe_cluster_configs.end(),
probes.begin(), probes.end());
} else if (backoff_in_alr) {
// 如果在alr中做了码率回退,进行新一轮的探测?
// If we just backed off during ALR, request a new probe.
auto probes = probe_controller_->
RequestProbe(report.feedback_time.ms());
update.probe_cluster_configs.insert(
update.probe_cluster_configs.end(),
probes.begin(), probes.end());
}
// No valid RTT could be because send-side BWE isn't used, in which case
// we don't try to limit the outstanding packets.
if (rate_control_settings_.UseCongestionWindow() &&
max_feedback_rtt.IsFinite()) {
// TODO 这个window需要花时间看一看,直接一直没有看这个东西
UpdateCongestionWindowSize();
}
if (congestion_window_pushback_controller_ &&
current_data_window_) {
// 如果有congestion_window_pushback_controller_,将当前的窗口放在通知器下回推给编码器
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
// 否则,直接放在结果中
update.congestion_window = current_data_window_;
}
// 返回结果
return update;
}
除了依靠反馈函数来触发带宽计算,gcc还会定时触发避免过长时间带宽无变化:
NetworkControlUpdate GoogCcNetworkController::OnProcessInterval(
ProcessInterval msg) {
NetworkControlUpdate update;
if (initial_config_) {
// 重设loss_based和delay_based码率探测器和probe的初始码率
// 获得码率探测簇配置(probe_cluster_config)
update.probe_cluster_configs =
ResetConstraints(initial_config_->constraints);
// 获取当前pacing 的发送码率, padding, time_windows等
update.pacer_config = GetPacingRates(msg.at_time);
// probe探测完成后,允许其因为alr需要快速恢复码率而继续做probe
if (initial_config_->
stream_based_config.requests_alr_probing) {
probe_controller_->EnablePeriodicAlrProbing(
*initial_config_->
stream_based_config.requests_alr_probing);
}
absl::optional<datarate> total_bitrate =
initial_config_->
stream_based_config.max_total_allocated_bitrate;
if (total_bitrate) {
// 为probe设置最大的分配码率(MaxTotalAllocatedBitrate)作为探测的上边界
// 并生成响应的probe_cluster_config去进行探测
auto probes = probe_controller_->
OnMaxTotalAllocatedBitrate(
total_bitrate->bps(), msg.at_time.ms());
update.probe_cluster_configs.insert(
update.probe_cluster_configs.end(),
probes.begin(), probes.end());
max_total_allocated_bitrate_ = *total_bitrate;
}
// 释放initial_config_,下次进来就不通过init_config做初始化了
initial_config_.reset();
}
// 更新拥塞窗口中的pacing数据长度
if (congestion_window_pushback_controller_ && msg.pacer_queue) {
congestion_window_pushback_controller_->
UpdatePacingQueue(msg.pacer_queue->bytes());
}
// 更新码率
bandwidth_estimation_->UpdateEstimate(msg.at_time);
// 检测当前是否处于alr
absl::optional<int64_t> start_time_ms =
alr_detector_->GetApplicationLimitedRegionStartTime();
// 如果处于alr,告诉probe_controller处于alr,可以进行探测,进行快恢复
probe_controller_->SetAlrStartTimeMs(start_time_ms);
// 检测当前是否因alr状态而需要做probe了,获取probe_cluster_config
auto probes = probe_controller_->Process(msg.at_time.ms());
update.probe_cluster_configs.insert(
update.probe_cluster_configs.end(),
probes.begin(), probes.end());
if (rate_control_settings_.UseCongestionWindow() &&
last_packet_received_time_.IsFinite() &&
!feedback_max_rtts_.empty()) {
// 根据rtt和target_rate 更新当前拥塞控制窗口大小
UpdateCongestionWindowSize();
}
if (congestion_window_pushback_controller_ &&
current_data_window_) {
// 重新设置拥塞控制窗口大小
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
update.congestion_window = current_data_window_;
}
// 获取更新后的码率,probe等,同时对alr, probe_controller中的码率进行更新
MaybeTriggerOnNetworkChanged(&update, msg.at_time);
return update;
}