一、架构总览
PccNetworkControllerFactory (工厂,创建控制器实例)
└── PccNetworkController (主控制器,驱动状态机)
├── PccMonitorInterval (监测区间,采集丢包/时延/吞吐量)
├── RttTracker (RTT 跟踪器,EWMA 平滑)
└── PccBitrateController (码率控制器,梯度上升更新速率)
└── ModifiedVivaceUtilityFunction (效用函数,将性能指标映射为数值)
二、算法原理概述
PCC Vivace 是基于在线凸优化(机器学习)的拥塞控制算法:
- 将时间分割为连续的 Monitor Interval (MI)
- 在每个 MI 中分别以速率
r(1+ε)和r(1-ε)探测发送 - MI 结束后,用丢包率、时延梯度、吞吐量计算效用函数 U
- 通过梯度上升法调整发送速率以最大化 U
状态转移图:
kStartup ──(500ms到期)──> kSlowStart ──(utility下降)──> kOnlineLearning
│ │
└───(超时)───> kOnlineLearning │
│
kDoubleCheck <─┘─(低速率高丢包异常)
│
└──(验证完毕)──> kOnlineLearning
三、RttTracker --- RTT 跟踪器
rtt_tracker.h
cpp
namespace webrtc {
namespace pcc {
class RttTracker {
public:
// initial_rtt: 初始RTT估计值, alpha: EWMA中新样本权重(越大越信任新值)
RttTracker(TimeDelta initial_rtt, double alpha);
// 收到包反馈时更新RTT估计
void OnPacketsFeedback(const std::vector<PacketResult>& packet_feedbacks,
Timestamp feedback_received_time);
// 返回当前平滑后的RTT估计
TimeDelta GetRtt() const;
private:
TimeDelta rtt_estimate_; // 当前EWMA平滑后的RTT估计值
double alpha_; // EWMA权重: 新样本占比(默认0.9, 高度信任最新测量)
};
} // namespace pcc
} // namespace webrtc
rtt_tracker.cc
cpp
namespace webrtc {
namespace pcc {
// 构造函数: 用初始RTT和alpha初始化
RttTracker::RttTracker(TimeDelta initial_rtt, double alpha)
: rtt_estimate_(initial_rtt), // 初始RTT估计, 默认200ms
alpha_(alpha) {} // EWMA权重, 默认0.9
void RttTracker::OnPacketsFeedback(
const std::vector<PacketResult>& packet_feedbacks,
Timestamp feedback_received_time) { // feedback_received_time: 反馈到达本端的时间戳
TimeDelta packet_rtt = TimeDelta::MinusInfinity(); // 初始化为负无穷, 用于取最大值
for (const PacketResult& packet_result : packet_feedbacks) { // 遍历本批反馈中的所有包
if (!packet_result.IsReceived()) // 跳过未收到(丢失)的包
continue;
packet_rtt = std::max<TimeDelta>( // 取本批所有包中的最大RTT
packet_rtt,
feedback_received_time - // RTT = 反馈到达时间 - 包发送时间
packet_result.sent_packet.send_time); // (从发送端视角的端到端往返时延)
}
if (packet_rtt.IsFinite()) // 有有效RTT样本时才更新
// EWMA平滑: rtt = (1-0.9)*旧rtt + 0.9*新rtt
// alpha=0.9 意味着高度信任最新测量, 快速跟踪RTT变化
rtt_estimate_ = (1 - alpha_) * rtt_estimate_ + alpha_ * packet_rtt;
}
TimeDelta RttTracker::GetRtt() const {
return rtt_estimate_; // 返回当前平滑后的RTT
}
} // namespace pcc
} // namespace webrtc
四、PccMonitorInterval --- 监测区间
monitor_interval.h
cpp
namespace webrtc {
namespace pcc {
// PCC将时间分割为连续的监测区间, 用来测试以某个速率发送的后果
class PccMonitorInterval {
public:
// target_sending_rate: 本区间的目标发送速率
// start_time: 区间开始时间(不包含在区间内)
// duration: 区间持续时长
PccMonitorInterval(DataRate target_sending_rate,
Timestamp start_time,
TimeDelta duration);
~PccMonitorInterval();
PccMonitorInterval(const PccMonitorInterval& other);
// 接收包反馈, 分类为已收到/丢失, 并判断区间反馈是否收集完毕
void OnPacketsFeedback(const std::vector<PacketResult>& packets_results);
// 返回true表示已收集到区间内所有包的反馈
// 判断标准: 收到了第一个发送时间超出区间结束时间的包的反馈
bool IsFeedbackCollectionDone() const;
// 返回区间结束时间 = start_time + duration
Timestamp GetEndTime() const;
// 计算本区间丢包率 = 丢失包数 / (丢失+收到)
double GetLossRate() const;
// 用线性回归估算时延梯度(时延随发送时间的变化斜率)
double ComputeDelayGradient(double delay_gradient_threshold) const;
// 返回本区间的目标发送速率
DataRate GetTargetSendingRate() const;
// 计算接收端的实际吞吐量
DataRate GetTransmittedPacketsRate() const;
private:
struct ReceivedPacket {
TimeDelta delay; // 单向时延 = receive_time - send_time
Timestamp sent_time; // 包的发送时间
};
DataRate target_sending_rate_; // 本区间的目标发送速率
Timestamp start_time_; // 区间开始时间(该时刻的包不算在内)
TimeDelta interval_duration_; // 区间持续时长
std::vector<ReceivedPacket> received_packets_; // 已收到的包列表
std::vector<Timestamp> lost_packets_sent_time_; // 丢失包的发送时间列表
DataSize received_packets_size_; // 已收到包的总字节数
bool feedback_collection_done_; // 反馈收集是否完成的标志
};
} // namespace pcc
} // namespace webrtc
monitor_interval.cc
cpp
namespace webrtc {
namespace pcc {
// 构造函数: 初始化区间参数
PccMonitorInterval::PccMonitorInterval(DataRate target_sending_rate,
Timestamp start_time,
TimeDelta duration)
: target_sending_rate_(target_sending_rate), // 本区间的目标发送速率
start_time_(start_time), // 区间起始时间
interval_duration_(duration), // 区间持续时长
received_packets_size_(DataSize::Zero()), // 已接收字节数归零
feedback_collection_done_(false) {} // 反馈收集尚未完成
PccMonitorInterval::~PccMonitorInterval() = default;
PccMonitorInterval::PccMonitorInterval(const PccMonitorInterval& other) =
default;
// 核心: 处理包反馈, 将包归类到本区间
void PccMonitorInterval::OnPacketsFeedback(
const std::vector<PacketResult>& packets_results) {
for (const PacketResult& packet_result : packets_results) { // 遍历所有反馈包
if (packet_result.sent_packet.send_time <= start_time_) {
continue; // 包在本区间开始之前发送, 不属于本区间, 跳过
}
// 如果包的发送时间超过区间结束时间(start + duration)
// 说明区间内所有包都已收到反馈, 标记完成
if (packet_result.sent_packet.send_time >
start_time_ + interval_duration_) {
feedback_collection_done_ = true; // 区间反馈收集完毕
return; // 后续包不属于本区间, 直接返回
}
// 包属于本区间, 根据是否收到分类处理
if (!packet_result.IsReceived()) {
// 未收到 = 丢失, 记录其发送时间
lost_packets_sent_time_.push_back(packet_result.sent_packet.send_time);
} else {
// 已收到, 记录{单向时延, 发送时间}, 累加字节数
received_packets_.push_back(
{packet_result.receive_time - packet_result.sent_packet.send_time,
// 单向时延 = 接收时间 - 发送时间
packet_result.sent_packet.send_time});
// 记录发送时间(用于后续线性回归)
received_packets_size_ += packet_result.sent_packet.size;
// 累加接收字节数(用于计算吞吐量)
}
}
}
// ═══════════════════════════════════════════════════
// 时延梯度计算 --- 最小二乘法线性回归
//
// 数学原理: 给定 (t_i, d_i), t_i=发送时间, d_i=单向时延
// 斜率 = Σ(t̄_i · d_i) / Σ(t̄_i²)
// 其中 t̄_i = t_i - mean(t) (中心化)
//
// 参考:
// https://www.johndcook.com/blog/2008/10/20/comparing-two-ways-to-fit-a-line-to-data/
// ═══════════════════════════════════════════════════
double PccMonitorInterval::ComputeDelayGradient(
double delay_gradient_threshold) const {
// 边界条件: 无包 或 所有包同时发送 → 梯度为0
if (received_packets_.empty() || received_packets_.front().sent_time ==
received_packets_.back().sent_time) {
return 0;
}
// ---- 第一遍遍历: 计算发送时间之和, 用于求均值 ----
double sum_times = 0;
for (const ReceivedPacket& packet : received_packets_) {
double time_delta_us =
(packet.sent_time - received_packets_[0].sent_time).us();
// 相对于首包的发送时间偏移(微秒)
sum_times += time_delta_us; // 累加所有偏移量
}
// ---- 第二遍遍历: 计算回归分子和分母 ----
double sum_squared_scaled_time_deltas = 0; // 分母: Σ(t̄²)
double sum_scaled_time_delta_dot_delay = 0; // 分子: Σ(t̄ · d)
for (const ReceivedPacket& packet : received_packets_) {
double time_delta_us =
(packet.sent_time - received_packets_[0].sent_time).us();
// 相对于首包的发送时间偏移
double delay = packet.delay.us();
// 该包的单向时延(微秒)
double scaled_time_delta_us =
time_delta_us - sum_times / received_packets_.size();
// 中心化: t̄ = t - mean(t), 消除偏移使回归更稳定
sum_squared_scaled_time_deltas +=
scaled_time_delta_us * scaled_time_delta_us;
// 累加 t̄² (分母)
sum_scaled_time_delta_dot_delay += scaled_time_delta_us * delay;
// 累加 t̄ × d (分子)
}
// 斜率 = Σ(t̄·d) / Σ(t̄²) --- 线性回归的标准公式
double rtt_gradient =
sum_scaled_time_delta_dot_delay / sum_squared_scaled_time_deltas;
// 噪声过滤: |gradient| < 阈值(默认0.01)时视为0, 避免噪声干扰决策
if (std::abs(rtt_gradient) < delay_gradient_threshold)
rtt_gradient = 0;
// gradient > 0: 时延在增长(队列积累, 拥塞加重)
// gradient < 0: 时延在减少(队列排空, 拥塞缓解)
// gradient = 0: 时延稳定
return rtt_gradient;
}
// 区间反馈是否收集完毕
bool PccMonitorInterval::IsFeedbackCollectionDone() const {
return feedback_collection_done_;
}
// 区间结束时间 = 开始时间 + 持续时长
Timestamp PccMonitorInterval::GetEndTime() const {
return start_time_ + interval_duration_;
}
// 丢包率 = 丢失包数 / 总包数(丢失+收到)
double PccMonitorInterval::GetLossRate() const {
size_t packets_lost = lost_packets_sent_time_.size(); // 丢失包数量
size_t packets_received = received_packets_.size(); // 收到包数量
if (packets_lost == 0)
return 0; // 无丢包, 直接返回0
return static_cast<double>(packets_lost) / (packets_lost + packets_received);
}
// 返回本区间的目标发送速率
DataRate PccMonitorInterval::GetTargetSendingRate() const {
return target_sending_rate_;
}
// 计算接收端的实际吞吐量(接收侧视角)
DataRate PccMonitorInterval::GetTransmittedPacketsRate() const {
if (received_packets_.empty()) {
return target_sending_rate_; // 无收到包时, 回退到目标速率
}
// 重建接收端时间戳: receive_time = send_time + one_way_delay
Timestamp receive_time_of_first_packet =
received_packets_.front().sent_time + received_packets_.front().delay;
Timestamp receive_time_of_last_packet =
received_packets_.back().sent_time + received_packets_.back().delay;
if (receive_time_of_first_packet == receive_time_of_last_packet) {
RTC_LOG(LS_WARNING) << "区间内所有包在同一时刻接收"; // 无法计算吞吐量
return target_sending_rate_;
}
// 吞吐量 = 总接收字节数 / (末包接收时刻 - 首包接收时刻)
return received_packets_size_ /
(receive_time_of_last_packet - receive_time_of_first_packet);
}
} // namespace pcc
} // namespace webrtc
五、Utility Function --- 效用函数
utility_function.h
cpp
namespace webrtc {
namespace pcc {
// 效用函数接口: 将一个监测区间的性能统计(速率/丢包/时延)映射为数值
// 论文: https://www.usenix.org/conference/nsdi18/presentation/dong
class PccUtilityFunctionInterface {
public:
virtual double Compute(const PccMonitorInterval& monitor_interval) const = 0;
virtual ~PccUtilityFunctionInterface() = default;
};
// 原始Vivace效用函数
// 论文: "PCC Vivace: Online-Learning Congestion Control", Mo Dong et al.
class VivaceUtilityFunction : public PccUtilityFunctionInterface {
public:
// delay_gradient_coefficient: 时延梯度惩罚系数 δ
// loss_coefficient: 丢包惩罚系数 λ
// throughput_coefficient: 吞吐量奖励系数 σ
// throughput_power: 吞吐量指数 t (<1 保证凹性)
// delay_gradient_threshold: 梯度噪声过滤阈值
// delay_gradient_negative_bound: 负梯度截断值(防止过度奖励时延下降)
VivaceUtilityFunction(double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound);
double Compute(const PccMonitorInterval& monitor_interval) const override;
~VivaceUtilityFunction() override;
private:
const double delay_gradient_coefficient_; // δ = 0.005
const double loss_coefficient_; // λ = 10
const double throughput_power_; // t = 0.9
const double throughput_coefficient_; // σ = 0.001
const double delay_gradient_threshold_; // 噪声阈值 = 0.01
const double delay_gradient_negative_bound_; // 负截断 = 0.1
};
// Modified Vivace效用函数(WebRTC默认使用)
// 与原始版本的核心区别: 每一项都额外乘以发送速率r
// 效果: 梯度(dU/dr)与r成正比, 高速率时步长自然更大, 收敛更快
class ModifiedVivaceUtilityFunction : public PccUtilityFunctionInterface {
public:
ModifiedVivaceUtilityFunction(double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power
double delay_gradient_threshold,
double delay_gradient_negative_bound);
double Compute(const PccMonitorInterval& monitor_interval) const override;
~ModifiedVivaceUtilityFunction() override;
private:
const double delay_gradient_coefficient_; // δ = 0.005
const double loss_coefficient_; // λ = 10
const double throughput_power_; // t = 0.9
const double throughput_coefficient_; // σ = 0.001
const double delay_gradient_threshold_; // 噪声阈值 = 0.01
const double delay_gradient_negative_bound_; // 负截断 = 0.1
};
} // namespace pcc
} // namespace webrtc
utility_function.cc
cpp
namespace webrtc {
namespace pcc {
// ═══════════════════════════════════════════════════
// 原始 Vivace 效用函数
//
// 公式: U = σ·r^t - δ·r·g - λ·r·L
//
// σ·r^t = 吞吐量奖励 (t<1 保证凹性, 防止速率无限增长)
// δ·r·g = 时延梯度惩罚 (g>0 = 队列增长 → 扣分)
// λ·r·L = 丢包率惩罚 (L越大扣分越多)
// ═══════════════════════════════════════════════════
VivaceUtilityFunction::VivaceUtilityFunction(
double delay_gradient_coefficient, // δ
double loss_coefficient, // λ
double throughput_coefficient, // σ
double throughput_power, // t
double delay_gradient_threshold, // 噪声过滤阈值
double delay_gradient_negative_bound) // 负梯度截断值
: delay_gradient_coefficient_(delay_gradient_coefficient),
loss_coefficient_(loss_coefficient),
throughput_power_(throughput_power),
throughput_coefficient_(throughput_coefficient),
delay_gradient_threshold_(delay_gradient_threshold),
delay_gradient_negative_bound_(delay_gradient_negative_bound) {
RTC_DCHECK_GE(delay_gradient_negative_bound_, 0);
// 负截断值必须 >= 0
}
double VivaceUtilityFunction::Compute(
const PccMonitorInterval& monitor_interval) const {
RTC_DCHECK(monitor_interval.IsFeedbackCollectionDone());
// 断言: 只有在反馈收集完毕后才能计算效用
double bitrate = monitor_interval.GetTargetSendingRate().bps();
//本区间的目标发送速率 r (bps)
double loss_rate = monitor_interval.GetLossRate();
//本区间丢包率 L
double rtt_gradient =
monitor_interval.ComputeDelayGradient(delay_gradient_threshold_);
//用线性回归计算时延梯度 g (|g|<0.01时视为0)
rtt_gradient = std::max(rtt_gradient, -delay_gradient_negative_bound_);
// 截断负梯度: g = max(g, -0.1)
// 防止时延下降时给予过多奖励(最多奖励到 -0.1 为止)
// U = σ·r^t - δ·r·g - λ·r·L
return (throughput_coefficient_ * std::pow(bitrate, throughput_power_)) -
//第一项: σ·r^t = 0.001 × r^0.9 (吞吐量奖励)
(delay_gradient_coefficient_ * bitrate * rtt_gradient) -
//第二项: δ·r·g = 0.005 × r × g (时延梯度惩罚)
(loss_coefficient_ * bitrate * loss_rate);
//第三项: λ·r·L = 10 × r × L (丢包惩罚)
}
VivaceUtilityFunction::~VivaceUtilityFunction() = default;
// ═══════════════════════════════════════════════════
// Modified Vivace 效用函数 (WebRTC 默认使用)
//
// 公式: U = σ·r^t·r - δ·r²·g - λ·r²·L
// = σ·r^(t+1) - δ·r²·g - λ·r²·L
//
// 与原始版本区别: 每项额外乘以 r
// 效果: dU/dr 与 r 成正比 → 高速率自然用大步长, 低速率用小步长
// ═══════════════════════════════════════════════════
ModifiedVivaceUtilityFunction::ModifiedVivaceUtilityFunction(
double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound)
: delay_gradient_coefficient_(delay_gradient_coefficient),
loss_coefficient_(loss_coefficient),
throughput_power_(throughput_power),
throughput_coefficient_(throughput_coefficient),
delay_gradient_threshold_(delay_gradient_threshold),
delay_gradient_negative_bound_(delay_gradient_negative_bound) {
RTC_DCHECK_GE(delay_gradient_negative_bound_, 0);
}
double ModifiedVivaceUtilityFunction::Compute(
const PccMonitorInterval& monitor_interval) const {
RTC_DCHECK(monitor_interval.IsFeedbackCollectionDone());
double bitrate = monitor_interval.GetTargetSendingRate().bps();
//目标发送速率 r (bps)
double loss_rate = monitor_interval.GetLossRate();
//丢包率 L
double rtt_gradient =
monitor_interval.ComputeDelayGradient(delay_gradient_threshold_);
//时延梯度 g
rtt_gradient = std::max(rtt_gradient, -delay_gradient_negative_bound_);
//截断负梯度: g = max(g, -0.1)
//U = σ·r^t·r - δ·r²·g - λ·r²·L
//带入默认参数: U = 0.001·r^1.9 - 0.005·r²·g - 10·r²·L
return (throughput_coefficient_ * std::pow(bitrate, throughput_power_) *
bitrate) -
//第一项: σ·r^t·r = 0.001 × r^0.9 × r = 0.001 × r^1.9
(delay_gradient_coefficient_ * bitrate * bitrate * rtt_gradient) -
//第二项: δ·r²·g = 0.005 × r² × g
(loss_coefficient_ * bitrate * bitrate * loss_rate);
//第三项: λ·r²·L = 10 × r² × L
}
ModifiedVivaceUtilityFunction::~ModifiedVivaceUtilityFunction() = default;
} // namespace pcc
} // namespace webrtc
六、PccBitrateController --- 码率控制器
bitrate_controller.h
cpp
namespace webrtc {
namespace pcc {
class PccBitrateController {
public:
// 构造函数1: 传入效用函数参数, 内部创建 ModifiedVivaceUtilityFunction
PccBitrateController(
double initial_conversion_factor, // 步长转换因子(默认5)
double initial_dynamic_boundary, // 动态边界初始值(默认0.1=10%)
double dynamic_boundary_increment, // 边界每次增量(默认0.1=10%)
double rtt_gradient_coefficient, // δ
double loss_coefficient, // λ
double throughput_coefficient, // σ
double throughput_power, // t
double rtt_gradient_threshold, // 梯度噪声阈值
double delay_gradient_negative_bound); // 负梯度截断
// 构造函数2: 直接传入自定义效用函数
PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function);
// 慢启动模式: 比较前后两轮效用, 效用增长则采纳新速率, 否则返回nullopt
absl::optional<DataRate> ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval);
// 在线学习模式: 两点梯度计算 + 步长 + 动态边界 → 输出新速率
DataRate ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& block,
DataRate bandwidth_estimate);
~PccBitrateController();
private:
// 将速率变化量限制在动态边界范围内
double ApplyDynamicBoundary(double rate_change, double bitrate);
// 根据梯度方向的连续性计算自适应步长
double ComputeStepSize(double utility_gradient);
int64_t consecutive_boundary_adjustments_number_; // 连续同向边界调整次数
const double initial_dynamic_boundary_; // 边界初始值 0.1
const double dynamic_boundary_increment_; // 边界增量 0.1
const std::unique_ptr<PccUtilityFunctionInterface> utility_function_;
int64_t step_size_adjustments_number_; // 连续同向步长调整计数(正=增, 负=减)
const double initial_conversion_factor_; // 步长基础转换因子 = 5
absl::optional<double> previous_utility_; // 上一轮效用值(慢启动用)
};
} // namespace pcc
} // namespace webrtc
bitrate_controller.cc
cpp
namespace webrtc {
namespace pcc {
// 构造函数1: 内部创建 ModifiedVivace 效用函数, 委托给构造函数2
PccBitrateController::PccBitrateController(double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
double rtt_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double rtt_gradient_threshold,
double delay_gradient_negative_bound)
: PccBitrateController(initial_conversion_factor,
initial_dynamic_boundary,
dynamic_boundary_increment,
std::make_unique<ModifiedVivaceUtilityFunction>(
//默认使用Modified Vivace(非原始Vivace)
rtt_gradient_coefficient,
loss_coefficient,
throughput_coefficient,
throughput_power,
rtt_gradient_threshold,
delay_gradient_negative_bound)) {}
// 构造函数2: 直接接受效用函数对象
PccBitrateController::PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function)
: consecutive_boundary_adjustments_number_(0), // 边界调整计数归零
initial_dynamic_boundary_(initial_dynamic_boundary), // 0.1
dynamic_boundary_increment_(dynamic_boundary_increment), // 0.1
utility_function_(std::move(utility_function)),
step_size_adjustments_number_(0), // 步长调整计数归零
initial_conversion_factor_(initial_conversion_factor) {} // 5
PccBitrateController::~PccBitrateController() = default;
// ═══════════════════════════════════════════════════
// 自适应步长计算
//
// 原理: 连续多次梯度同向 → 远离最优点 → 逐步加大步长
// 方向切换时 → 步长回归最小
//
// 步长放大器规则:
// |n|=1: amp=1, |n|=2: amp=2, |n|=3: amp=3
// |n|>3: amp=2|n|-3 (加速增长)
//
// 最终步长 = amp × initial_conversion_factor(5)
// ═══════════════════════════════════════════════════
double PccBitrateController::ComputeStepSize(double utility_gradient) {
// --- 更新连续同向计数 ---
if (utility_gradient > 0) {
step_size_adjustments_number_ =
std::max<int64_t>(step_size_adjustments_number_ + 1, 1);
// 梯度为正: 计数+1, 若之前为负则重置为+1(至少为1)
} else if (utility_gradient < 0) {
step_size_adjustments_number_ =
std::min<int64_t>(step_size_adjustments_number_ - 1, -1);
//梯度为负: 计数-1, 若之前为正则重置为-1(最大为-1)
} else {
step_size_adjustments_number_ = 0;
//梯度为0: 计数清零
}
// --- 计算步长放大器 ---
int64_t step_size_amplifier = 1; // 默认放大器=1
if (std::abs(step_size_adjustments_number_) <= 3) {
// 连续 ≤3 次: 放大器 = |count| (线性增长: 1,2,3)
step_size_amplifier =
std::max<int64_t>(std::abs(step_size_adjustments_number_), 1);
} else {
// 连续 >3 次: 放大器 = 2|count|-3 (加速增长: 5,7,9,...)
step_size_amplifier = 2 * std::abs(step_size_adjustments_number_) - 3;
}
// 最终步长 = 放大器 × 基础因子(5)
return step_size_amplifier * initial_conversion_factor_;
}
// ═══════════════════════════════════════════════════
// 动态边界 --- 防止速率震荡的安全阀
//
// 初始边界 = 10% × bitrate
// 每次被截断: 边界扩大10% (允许更大的调整)
// 方向切换时: 重置为最小边界
// ═══════════════════════════════════════════════════
double PccBitrateController::ApplyDynamicBoundary(double rate_change,
double bitrate) {
double rate_change_abs = std::abs(rate_change);
//变化量的绝对值
int64_t rate_change_sign = (rate_change > 0) ? 1 : -1;
//变化方向: +1=升速, -1=降速
if (consecutive_boundary_adjustments_number_ * rate_change_sign < 0) {
consecutive_boundary_adjustments_number_ = 0;
//方向切换(上次升速这次降速, 或反之): 重置边界计数
}
// 当前动态边界 = (0.1 + |count|×0.1) × bitrate
// count=0: 边界 = 10% × bitrate
// count=1: 边界 = 20% × bitrate
// count=n: 边界 = (n+1)×10% × bitrate
double dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
double boundary = bitrate * dynamic_change_boundary;
//绝对值边界 (bps)
if (rate_change_abs > boundary) {
// 变化量超出边界 → 截断为边界值
consecutive_boundary_adjustments_number_ += rate_change_sign;
//计数+1, 下次允许更大的变化
return boundary * rate_change_sign;
//返回截断后的值(保持方向)
}
// 变化量未超出边界 → 收缩边界到刚好能容纳此变化量的最小值
while (rate_change_abs <= boundary &&
consecutive_boundary_adjustments_number_ * rate_change_sign > 0) {
//只要变化量仍小于边界 且 还能收缩, 就继续收缩
consecutive_boundary_adjustments_number_ -= rate_change_sign;
// 边界计数-1, 缩小边界
dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
boundary = bitrate * dynamic_change_boundary;
//重新计算缩小后的边界
}
consecutive_boundary_adjustments_number_ += rate_change_sign;
//最后+1确保当前变化量刚好在边界内
return rate_change;
//返回原始变化量(未截断)
}
// ═══════════════════════════════════════════════════
// 慢启动模式更新
//
// 每轮以 1.5x 速率发送, 计算效用:
// 效用增长 → 采纳新速率, 继续慢启动
// 效用不增长 → 返回nullopt, 触发退出慢启动
// ═══════════════════════════════════════════════════
absl::optional<DataRate>
PccBitrateController::ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval) {
double utility_value = utility_function_->Compute(monitor_interval);
//计算本区间的效用值 U
if (previous_utility_.has_value() && utility_value <= previous_utility_) {
return absl::nullopt;
//有前值 且 效用没增长 → 返回空(触发调用方退出慢启动)
}
previous_utility_ = utility_value;
// 记录当前效用作为下一轮的参考
return monitor_interval.GetTargetSendingRate();
//效用在增长, 返回本区间速率作为新的带宽估计
}
// ═══════════════════════════════════════════════════
// 在线学习模式更新 --- 梯度上升法
//
// 输入: intervals[0]的速率为 r(1+ε) 或 r(1-ε)
// intervals[1]的速率为 r(1-ε) 或 r(1+ε)
// 计算: 梯度 = (U₁ - U₂) / (r₁ - r₂)
// Δr = 梯度 × 步长
// Δr = ApplyDynamicBoundary(Δr)
// new_r = max(0, old_r + Δr)
// ═══════════════════════════════════════════════════
DataRate PccBitrateController::ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& intervals,
DataRate bandwith_estimate) {
double first_utility = utility_function_->Compute(intervals[0]);
//计算第一个MI的效用值 U(r₁)
double second_utility = utility_function_->Compute(intervals[1]);
//计算第二个MI的效用值 U(r₂)
double first_bitrate_bps = intervals[0].GetTargetSendingRate().bps();
//第一个MI的目标速率 r₁
double second_bitrate_bps = intervals[1].GetTargetSendingRate().bps();
// 第二个MI的目标速率 r₂
double gradient = (first_utility - second_utility) /
(first_bitrate_bps - second_bitrate_bps);
//核心数值梯度: ∇U = [U(r₁)-U(r₂)] / [r₁-r₂]
// 这就是两点法估算dU/dr的核心公式
double rate_change_bps = gradient * ComputeStepSize(gradient);
// 速率变化量 Δr = 梯度 × 自适应步长
rate_change_bps =
ApplyDynamicBoundary(rate_change_bps, bandwith_estimate.bps());
//应用动态边界约束, 防止变化过大
return DataRate::BitsPerSec(
std::max(0.0, bandwith_estimate.bps() + rate_change_bps));
//新速率 = max(0, 当前估计 + Δr), 保证不为负
}
} // namespace pcc
} // namespace webrtc
七、PccNetworkController --- 主控制器
pcc_network_controller.h
cpp
namespace webrtc {
namespace pcc {
// PCC Vivace: 基于在线凸优化的拥塞控制算法
// 将时间分割为连续MI, 以 r(1+ε) 和 r(1-ε) 测试当前速率r
// 每个MI结束后计算效用函数, 再用梯度上升法更新速率以最大化效用
class PccNetworkController : public NetworkControllerInterface {
public:
enum class Mode {
kStartup, // 启动阶段: 前500ms, 用初始速率探测
kSlowStart, // 慢启动: 每轮速率×1.5, 直到效用不再增长
kOnlineLearning, // 在线学习: 两点梯度探测 + 梯度上升
kDoubleCheck // 双重验证: 低速率高丢包时重新测量
};
enum class MonitorIntervalLengthStrategy {
kAdaptive, // 自适应: MI时长 = max(RTT × 比率, 发包间隔 × 包数)
kFixed // 固定(默认): MI时长 = 发包间隔 × 最小包数(20)
};
explicit PccNetworkController(NetworkControllerConfig config);
~PccNetworkController() override;
// ---- NetworkControllerInterface 回调 ----
NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override;
NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override;
NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
NetworkControlUpdate OnSentPacket(SentPacket msg) override; // 发包回调(核心)
NetworkControlUpdate OnTargetRateConstraints(
TargetRateConstraints msg) override;
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback msg) override; // 反馈回调(核心)
// 以下接口PCC不使用, 返回空更新
NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override;
NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override;
NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override;
NetworkControlUpdate OnReceivedPacket(ReceivedPacket msg) override;
NetworkControlUpdate OnNetworkStateEstimate(
NetworkStateEstimate msg) override;
private:
void UpdateSendingRateAndMode(); // 根据MI结果更新速率和模式
NetworkControlUpdate CreateRateUpdate(
Timestamp at_time) const; // 生成速率更新消息
TimeDelta ComputeMonitorIntervalsDuration() const; // 计算MI持续时长
bool NeedDoubleCheckMeasurments() const; // 是否需要双重验证
bool IsTimeoutExpired(Timestamp current_time) const; // MI是否超时
bool IsFeedbackCollectionDone() const; // 所有MI反馈是否收集完毕
Timestamp start_time_; // 算法启动时间
Timestamp last_sent_packet_time_; // 上一个包的发送时间
TimeDelta smoothed_packets_sending_interval_; // EWMA平滑的发包间隔
Mode mode_; // 当前状态机模式
DataRate default_bandwidth_; // 默认带宽(用于初始化)
DataRate bandwidth_estimate_; // 当前带宽估计 r
RttTracker rtt_tracker_; // RTT跟踪器
TimeDelta monitor_interval_timeout_; // MI超时阈值 = RTT × 2
const MonitorIntervalLengthStrategy
monitor_interval_length_strategy_; // MI长度策略(默认Fixed)
const double monitor_interval_duration_ratio_; // 自适应模式下 MI/RTT 比率
const double sampling_step_; // 采样步长 ε = 0.05
const double monitor_interval_timeout_ratio_; // 超时比率 = 2.0
const int64_t min_packets_number_per_interval_; // MI最少包数 = 20
PccBitrateController bitrate_controller_; // 码率控制器(梯度上升)
std::vector<PccMonitorInterval> monitor_intervals_; // 当前轮的MI列表
std::vector<DataRate> monitor_intervals_bitrates_; // 当前轮各MI的目标速率
TimeDelta monitor_intervals_duration_; // 当前MI持续时长
size_t complete_feedback_monitor_interval_number_; // 已完成反馈的MI数量
webrtc::Random random_generator_; // 随机数生成器(决定探测方向)
std::deque<PacketResult>
last_received_packets_; // 最近收到的20个包(用于超时时估算接收速率)
};
} // namespace pcc
} // namespace webrtc
pcc_network_controller.cc
cpp
namespace webrtc {
namespace pcc {
namespace {
// ═══════════════════════════════════════════════════
// 常量定义
// ═══════════════════════════════════════════════════
constexpr int64_t kInitialRttMs = 200;
// 初始RTT估计 = 200ms
constexpr int64_t kInitialBandwidthKbps = 300;
//初始带宽估计 = 300kbps
constexpr double kMonitorIntervalDurationRatio = 1;
//自适应MI策略下, MI时长 = RTT × 1.0
constexpr double kDefaultSamplingStep = 0.05;
//采样步长 ε = 5%, 探测速率 r(1±0.05)
constexpr double kTimeoutRatio = 2;
//超时阈值 = RTT × 2
constexpr double kAlphaForRtt = 0.9;
//RTT EWMA权重: 新样本占90%, 快速跟踪变化
constexpr double kSlowStartModeIncrease = 1.5;
//慢启动每轮速率翻1.5倍
constexpr double kAlphaForPacketInterval = 0.9;
//包发送间隔EWMA的新样本权重
constexpr int64_t kMinPacketsNumberPerInterval = 20;
//固定MI策略下, 每MI至少包含20个包
const TimeDelta kMinDurationOfMonitorInterval = TimeDelta::Millis(50);
// MI最小持续时间 = 50ms
const TimeDelta kStartupDuration = TimeDelta::Millis(500);
//Startup阶段持续时间 = 500ms
constexpr double kMinRateChangeBps = 4000;
//低速率时最小速率变化量 = 4kbps (加法探测)
constexpr DataRate kMinRateHaveMultiplicativeRateChange = DataRate::BitsPerSec(
static_cast<int64_t>(kMinRateChangeBps / kDefaultSamplingStep));
// = 4000 / 0.05 = 80000 bps = 80kbps
// 超过80kbps使用乘法探测 r(1±ε), 否则用加法探测 r±4kbps
// ---- 码率控制器参数 ----
constexpr double kInitialConversionFactor = 5;
//步长基础转换因子
constexpr double kInitialDynamicBoundary = 0.1;
//动态边界初始值 = 当前速率的10%
constexpr double kDynamicBoundaryIncrement = 0.1;
//每次连续同向截断, 边界扩大10%
// ---- 效用函数参数 ----
constexpr double kRttGradientCoefficientBps = 0.005;
//δ: 时延梯度惩罚系数
constexpr double kLossCoefficientBps = 10;
//λ: 丢包惩罚系数
constexpr double kThroughputCoefficient = 0.001;
//σ: 吞吐量奖励系数
constexpr double kThroughputPower = 0.9;
// t: 吞吐量指数 (<1 保证凹性)
constexpr double kRttGradientThreshold = 0.01;
// 时延梯度噪声过滤阈值, |g|<0.01 视为0
constexpr double kDelayGradientNegativeBound = 0.1;
// 负梯度截断值, g = max(g, -0.1)
constexpr int64_t kNumberOfPacketsToKeep = 20;
// 保留最近20个收到的包(用于超时时估算接收速率)
const uint64_t kRandomSeed = 100;
// 随机数种子(决定探测方向正负)
} // namespace
// ═══════════════════════════════════════════════════
// 构造函数: 初始化所有成员
// ═══════════════════════════════════════════════════
PccNetworkController::PccNetworkController(NetworkControllerConfig config)
: start_time_(Timestamp::PlusInfinity()),
// 正无穷表示尚未启动, 首次发包时会被设为实际时间
last_sent_packet_time_(Timestamp::PlusInfinity()),
// 正无穷表示尚无发包记录
smoothed_packets_sending_interval_(TimeDelta::Zero()),
// 发包间隔EWMA初始为0
mode_(Mode::kStartup),
// 初始模式: Startup
default_bandwidth_(DataRate::KilobitsPerSec(kInitialBandwidthKbps)),
// 默认带宽 = 300kbps
bandwidth_estimate_(default_bandwidth_),
// 当前带宽估计 = 300kbps
rtt_tracker_(TimeDelta::Millis(kInitialRttMs), kAlphaForRtt),
// RTT跟踪器: 初始RTT=200ms, alpha=0.9
monitor_interval_timeout_(TimeDelta::Millis(kInitialRttMs) *
kTimeoutRatio),
// 初始超时 = 200ms × 2 = 400ms
monitor_interval_length_strategy_(MonitorIntervalLengthStrategy::kFixed),
// MI长度策略: 固定(基于包数)
monitor_interval_duration_ratio_(kMonitorIntervalDurationRatio),
// 自适应模式下MI/RTT比率 = 1.0
sampling_step_(kDefaultSamplingStep),
// ε = 0.05
monitor_interval_timeout_ratio_(kTimeoutRatio),
// 超时比率 = 2.0
min_packets_number_per_interval_(kMinPacketsNumberPerInterval),
// MI最少包数 = 20
bitrate_controller_(kInitialConversionFactor,
kInitialDynamicBoundary,
kDynamicBoundaryIncrement,
kRttGradientCoefficientBps,
kLossCoefficientBps,
kThroughputCoefficient,
kThroughputPower,
kRttGradientThreshold,
kDelayGradientNegativeBound),
// 初始化码率控制器(内部创建 ModifiedVivace 效用函数)
monitor_intervals_duration_(TimeDelta::Zero()),
// MI持续时间初始为0
complete_feedback_monitor_interval_number_(0),
// 已完成反馈的MI数量 = 0
random_generator_(kRandomSeed) {
// 随机数生成器, 种子=100
if (config.constraints.starting_rate) {
default_bandwidth_ = *config.constraints.starting_rate;
bandwidth_estimate_ = default_bandwidth_;
// 如果配置中指定了初始速率, 覆盖默认300kbps
}
}
PccNetworkController::~PccNetworkController() {}
// ═══════════════════════════════════════════════════
// 生成速率更新消息 --- 返回给编码器和Pacer
// ═══════════════════════════════════════════════════
NetworkControlUpdate PccNetworkController::CreateRateUpdate(
Timestamp at_time) const {
DataRate sending_rate = DataRate::Zero();
if (monitor_intervals_.empty() ||
(monitor_intervals_.size() >= monitor_intervals_bitrates_.size() &&
at_time >= monitor_intervals_.back().GetEndTime())) {
sending_rate = bandwidth_estimate_;
// 无活跃MI 或 所有MI已结束: 使用当前带宽估计
} else {
sending_rate = monitor_intervals_.back().GetTargetSendingRate();
// 有活跃MI: 使用最新MI的目标发送速率(可能是探测速率 r±ε)
}
NetworkControlUpdate update;
// ---- 设置目标码率(给编码器) ----
TargetTransferRate target_rate_msg;
target_rate_msg.at_time = at_time; // 时间戳
target_rate_msg.network_estimate.at_time = at_time;
target_rate_msg.network_estimate.round_trip_time = rtt_tracker_.GetRtt();
// 附带当前RTT估计
target_rate_msg.network_estimate.loss_rate_ratio = 0;
// TODO: 暂未实现丢包率估计
target_rate_msg.network_estimate.bwe_period =
monitor_interval_duration_ratio_ * rtt_tracker_.GetRtt();
// 带宽估计周期 = 1.0 × RTT
target_rate_msg.target_rate = sending_rate;
// 目标发送速率
update.target_rate = target_rate_msg;
// ---- 设置Pacing配置(给发包器) ----
PacerConfig pacer_config;
pacer_config.at_time = at_time;
pacer_config.time_window = TimeDelta::Millis(1);
// 1ms时间窗口
pacer_config.data_window = sending_rate * pacer_config.time_window;
// 1ms内允许发送的数据量 = rate × 1ms
pacer_config.pad_window = sending_rate * pacer_config.time_window;
// padding也使用相同速率
update.pacer_config = pacer_config;
return update;
}
// ═══════════════════════════════════════════════════
//OnSentPacket --- 发包回调(算法主驱动)
//
//
// 1. 首包初始化
// 2. 更新发包间隔EWMA
// 3. 启动新MI
// 4. 超时检测与处理
// 5. Startup→SlowStart 转换
// 6. 反馈收集完毕后创建下一轮MI
// ═══════════════════════════════════════════════════
NetworkControlUpdate PccNetworkController::OnSentPacket(SentPacket msg) {
// ---- 1. 首包初始化 ----
if (start_time_.IsInfinite()) {
// 正无穷 = 尚未启动, 这是第一个包
start_time_ = msg.send_time;
// 记录算法启动时间
monitor_intervals_duration_ = kStartupDuration;
// Startup阶段MI时长 = 500ms
monitor_intervals_bitrates_ = {bandwidth_estimate_};
// Startup只有1个MI, 速率 = 初始带宽估计(300kbps)
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
// 创建第一个MI: 速率=300kbps, 开始=现在, 时长=500ms
complete_feedback_monitor_interval_number_ = 0;
// 已完成MI数量归零
}
// ---- 2. 更新发包间隔EWMA ----
if (last_sent_packet_time_.IsFinite()) {
//不是第一个包时才能计算间隔
smoothed_packets_sending_interval_ =
(msg.send_time - last_sent_packet_time_) * kAlphaForPacketInterval +
(1 - kAlphaForPacketInterval) * smoothed_packets_sending_interval_;
// EWMA: interval = 0.9×新间隔 + 0.1×旧平滑值
// 用于计算MI时长(固定策略: interval × 20)
}
last_sent_packet_time_ = msg.send_time;
//记录本包发送时间
// ---- 3. 如果当前MI已结束且还有待启动的MI, 启动下一个 ----
if (!monitor_intervals_.empty() &&
msg.send_time >= monitor_intervals_.back().GetEndTime() &&
//当前时间已过最后一个MI的结束时间
monitor_intervals_bitrates_.size() > monitor_intervals_.size()) {
// 还有预定的MI未启动(在线学习模式下有2个MI)
monitor_intervals_.emplace_back(
monitor_intervals_bitrates_[monitor_intervals_.size()],
//使用下一个预定速率
msg.send_time,
// 从当前时间开始
monitor_intervals_duration_);
//使用相同的MI时长
}
// ---- 4. 超时检测与处理 ----
if (IsTimeoutExpired(msg.send_time)) {
//当前时间 - 未完成MI的结束时间 >= RTT×2 → 超时
// 说明长时间没收到反馈, 网络可能严重拥塞
// 用最近收到的20个包估算接收速率
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet.size;
// 从第2个包开始累加(第1个作为时间基准)
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
// 时间跨度 = 最后包接收时间 - 第一包接收时间
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
// 接收速率 = 总字节 / 时间跨度
bandwidth_estimate_ =
std::min<DataRate>(bandwidth_estimate_ * 0.5, receiving_rate);
//超时保守策略: 带宽 = min(当前×0.5, 接收速率)
// 即: 至少减半, 且不超过实际接收速率
if (mode_ == Mode::kSlowStart)
mode_ = Mode::kOnlineLearning;
//慢启动中超时: 直接跳到在线学习(跳过剩余慢启动)
}
// ---- 5. Startup结束 → 转入SlowStart ----
if (mode_ == Mode::kStartup &&
msg.send_time - start_time_ >= kStartupDuration) {
//已过500ms, Startup阶段结束
//用最近20个包估算接收速率(与超时处理逻辑相同)
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet.size;
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
bandwidth_estimate_ = receiving_rate;
//用实测接收速率作为初始带宽估计
monitor_intervals_.clear();
//清除Startup的MI
mode_ = Mode::kSlowStart;
//进入慢启动模式
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
//重新计算MI时长(基于当前发包间隔)
monitor_intervals_bitrates_ = {bandwidth_estimate_};
//慢启动第一轮: 1个MI, 速率=接收速率
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
//创建MI
bandwidth_estimate_ = bandwidth_estimate_ * (1 / kSlowStartModeIncrease);
//关键将估计回退到 1/1.5 = 0.667倍
// 因为下一轮慢启动会乘以1.5x,
// 0.667 × 1.5 = 1.0 → 第一个慢启动MI实际速率 = 接收速率
// 之后每轮: bw×1.5 → 真正的翻倍增长
complete_feedback_monitor_interval_number_ = 0;
return CreateRateUpdate(msg.send_time);
}
// ---- 6. 反馈收集完毕 或 超时 → 创建下一轮MI ----
if (IsFeedbackCollectionDone() || IsTimeoutExpired(msg.send_time)) {
//所有MI反馈都收到 或 超时
monitor_intervals_.clear();
//清除旧MI
monitor_interval_timeout_ =
rtt_tracker_.GetRtt() * monitor_interval_timeout_ratio_;
// 更新超时阈值 = 当前RTT × 2
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
//重新计算MI时长
complete_feedback_monitor_interval_number_ = 0;
//已完成MI计数归零
// ---- 根据模式创建MI ----
if (mode_ == Mode::kSlowStart) {
// 慢启动: 创建1个MI, 速率 = 1.5 × bandwidth_estimate_
monitor_intervals_bitrates_ = {kSlowStartModeIncrease *
bandwidth_estimate_};
monitor_intervals_.emplace_back(
kSlowStartModeIncrease * bandwidth_estimate_,
msg.send_time,
monitor_intervals_duration_);
} else {
// 在线学习 或 DoubleCheck: 创建2个MI
RTC_DCHECK(mode_ == Mode::kOnlineLearning || mode_ == Mode::kDoubleCheck);
monitor_intervals_.clear();
int64_t sign = 2 * (random_generator_.Rand(0, 1) % 2) - 1;
//随机产生 +1 或 -1, 决定哪个MI先用高速率
// Rand(0,1) → 0或1 → ×2-1 → -1或+1
RTC_DCHECK_GE(sign, -1);
RTC_DCHECK_LE(sign, 1);
if (bandwidth_estimate_ >= kMinRateHaveMultiplicativeRateChange) {
//乘法探测: 速率 >= 80kbps 时使用
monitor_intervals_bitrates_ = {
bandwidth_estimate_ * (1 + sign * sampling_step_),
//MI[0]: r × (1 + sign×0.05)
bandwidth_estimate_ * (1 - sign * sampling_step_)};
// MI[1]: r × (1 - sign×0.05)
//
//例: r=1Mbps, sign=+1
// MI[0] = 1.05Mbps, MI[1] = 0.95Mbps
// 例: r=1Mbps, sign=-1
// MI[0] = 0.95Mbps, MI[1] = 1.05Mbps
} else {
//加法探测: 速率 < 80kbps 时使用
monitor_intervals_bitrates_ = {
DataRate::BitsPerSec(std::max<double>(
bandwidth_estimate_.bps() + sign * kMinRateChangeBps, 0)),
//MI[0]: r + sign×4kbps (不低于0)
DataRate::BitsPerSec(std::max<double>(
bandwidth_estimate_.bps() - sign * kMinRateChangeBps, 0))};
//MI[1]: r - sign×4kbps (不低于0)
}
// 立即启动第一个MI
monitor_intervals_.emplace_back(monitor_intervals_bitrates_[0],
msg.send_time,
monitor_intervals_duration_);
//第二个MI在第一个MI结束后自动启动(见前面第3步)
}
}
return CreateRateUpdate(msg.send_time);
//返回速率更新给编码器和Pacer
}
// ═══════════════════════════════════════════════════
// MI持续时间计算
// ═══════════════════════════════════════════════════
TimeDelta PccNetworkController::ComputeMonitorIntervalsDuration() const {
TimeDelta monitor_intervals_duration = TimeDelta::Zero();
if (monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kAdaptive) {
// 自适应: max(RTT × 1.0, 平均发包间隔 × 20)
monitor_intervals_duration = std::max(
rtt_tracker_.GetRtt() * monitor_interval_duration_ratio_,
smoothed_packets_sending_interval_ * min_packets_number_per_interval_);
} else {
RTC_DCHECK(monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kFixed);
// 固定(默认): 平均发包间隔 × 20
monitor_intervals_duration =
smoothed_packets_sending_interval_ * min_packets_number_per_interval_;
//保证每个MI至少包含20个包的测量量
}
monitor_intervals_duration =
std::max(kMinDurationOfMonitorInterval, monitor_intervals_duration);
//下限50ms: 防止MI太短导致统计不可靠
return monitor_intervals_duration;
}
// MI是否超时: 当前时间 - 未完成MI结束时间 >= RTT×2
bool PccNetworkController::IsTimeoutExpired(Timestamp current_time) const {
if (complete_feedback_monitor_interval_number_ >= monitor_intervals_.size()) {
return false;
//所有MI都已完成, 不存在超时
}
return current_time -
monitor_intervals_[complete_feedback_monitor_interval_number_]
.GetEndTime() >=
monitor_interval_timeout_;
//(当前时间 - 第一个未完成MI的结束时间) >= 超时阈值
}
// 所有预定MI的反馈是否全部收集完毕
bool PccNetworkController::IsFeedbackCollectionDone() const {
return complete_feedback_monitor_interval_number_ >=
monitor_intervals_bitrates_.size();
//已完成数量 >= 预定MI数量(慢启动=1, 在线学习=2)
}
// ═══════════════════════════════════════════════════
// OnTransportPacketsFeedback --- 反馈回调
//
//
// 1. 保存最近20包(用于超时估速)
// 2. 更新RTT
// 3. 将反馈路由到对应MI
// 4. 所有MI完成后触发速率更新
// ═══════════════════════════════════════════════════
NetworkControlUpdate PccNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback msg) {
if (msg.packet_feedbacks.empty())
return NetworkControlUpdate();
//无反馈数据, 直接返回
// ---- 1. 保存最近收到的包 ----
for (const PacketResult& packet_result : msg.ReceivedWithSendInfo()) {
last_received_packets_.push_back(packet_result);
//追加到队尾
}
while (last_received_packets_.size() > kNumberOfPacketsToKeep) {
last_received_packets_.pop_front();
//保持最多20个包
}
// ---- 2. 更新RTT估计 ----
rtt_tracker_.OnPacketsFeedback(msg.PacketsWithFeedback(), msg.feedback_time);
// ---- 3. 在线学习刚启动但MI尚未创建时跳过 ----
if (mode_ == Mode::kOnlineLearning &&
monitor_intervals_bitrates_.size() < 2) {
return NetworkControlUpdate();
//在线学习需要2个MI, 但尚未准备好, 跳过
}
// ---- 4. 将反馈路由到MI ----
if (!IsFeedbackCollectionDone() && !monitor_intervals_.empty()) {
while (complete_feedback_monitor_interval_number_ <
monitor_intervals_.size()) {
// 将反馈喂给当前第一个未完成的MI
monitor_intervals_[complete_feedback_monitor_interval_number_]
.OnPacketsFeedback(msg.PacketsWithFeedback());
if (!monitor_intervals_[complete_feedback_monitor_interval_number_]
.IsFeedbackCollectionDone())
break;
//当前MI还没收集完 → 停止(等待更多反馈)
++complete_feedback_monitor_interval_number_;
// 当前MI收集完毕 → 推进到下一个MI
}
}
// ---- 5. 所有MI反馈收集完毕 → 触发决策 ----
if (IsFeedbackCollectionDone()) {
if (mode_ == Mode::kDoubleCheck) {
mode_ = Mode::kOnlineLearning;
//DoubleCheck验证完成 → 回到在线学习
} else if (NeedDoubleCheckMeasurments()) {
mode_ = Mode::kDoubleCheck;
//检测到异常(低速率高丢包) → 进入DoubleCheck模式
// 下一轮会用相同的两个探测速率再测一次
}
if (mode_ != Mode::kDoubleCheck)
UpdateSendingRateAndMode();
//不需要复测时 → 更新发送速率和模式
// DoubleCheck模式下跳过更新(直接用相同速率再测一轮)
}
return NetworkControlUpdate();
}
// ═══════════════════════════════════════════════════
// 双重验证判断: 低速率反而高丢包 → 测量不可靠
// ═══════════════════════════════════════════════════
bool PccNetworkController::NeedDoubleCheckMeasurments() const {
if (mode_ == Mode::kSlowStart) {
return false;
//慢启动只有1个MI, 无法比较, 不需要
}
double first_loss_rate = monitor_intervals_[0].GetLossRate();
//第一个MI的丢包率
double second_loss_rate = monitor_intervals_[1].GetLossRate();
//第二个MI的丢包率
DataRate first_bitrate = monitor_intervals_[0].GetTargetSendingRate();
// 第一个MI的目标速率
DataRate second_bitrate = monitor_intervals_[1].GetTargetSendingRate();
//第二个MI的目标速率
if ((first_bitrate.bps() - second_bitrate.bps()) *
(first_loss_rate - second_loss_rate) <
0) {
return true;
//(速率差) × (丢包差) < 0
// 含义: 速率更高的MI反而丢包更少 → 违反直觉 → 测量不可靠
// 例: r₁=1.05Mbps, L₁=1%, r₂=0.95Mbps, L₂=3%
// (1.05-0.95) × (0.01-0.03) = 0.1 × (-0.02) = -0.002 < 0
// → 需要复测
}
return false;
}
// ═══════════════════════════════════════════════════
// 根据MI结果更新发送速率和工作模式
// ═══════════════════════════════════════════════════
void PccNetworkController::UpdateSendingRateAndMode() {
if (monitor_intervals_.empty() || !IsFeedbackCollectionDone()) {
return;
//无MI或反馈未完成, 跳过
}
if (mode_ == Mode::kSlowStart) {
// ---- 慢启动模式 ----
DataRate old_bandwidth_estimate = bandwidth_estimate_;
//记录旧带宽估计
bandwidth_estimate_ =
bitrate_controller_
.ComputeRateUpdateForSlowStartMode(monitor_intervals_[0])
.value_or(bandwidth_estimate_);
//效用增长 → 返回MI速率作为新估计
// 效用未增长 → 返回nullopt → .value_or保持旧估计不变
if (bandwidth_estimate_ <= old_bandwidth_estimate)
mode_ = Mode::kOnlineLearning;
//带宽没有增长 →退出慢启动, 进入在线学习
} else {
// ---- 在线学习模式 ----
RTC_DCHECK(mode_ == Mode::kOnlineLearning);
bandwidth_estimate_ =
bitrate_controller_.ComputeRateUpdateForOnlineLearningMode(
monitor_intervals_, bandwidth_estimate_);
// 核心 两点梯度 → 步长 → 动态边界 → 新速率
// 内部执行:
// gradient = [U(r₁)-U(r₂)] / [r₁-r₂]
// Δr = gradient × stepSize
// Δr = ApplyDynamicBoundary(Δr)
// new_r = max(0, old_r + Δr)
}
}
// ═══════════════════════════════════════════════════
// 以下接口PCC不使用, 返回空更新
// ═══════════════════════════════════════════════════
NetworkControlUpdate PccNetworkController::OnNetworkAvailability(
NetworkAvailability msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnNetworkRouteChange(
NetworkRouteChange msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnProcessInterval(
ProcessInterval msg) {
return CreateRateUpdate(msg.at_time);
// 定时回调: 返回当前速率(不改变状态)
}
NetworkControlUpdate PccNetworkController::OnTargetRateConstraints(
TargetRateConstraints msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRemoteBitrateReport(
RemoteBitrateReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRoundTripTimeUpdate(
RoundTripTimeUpdate) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnTransportLossReport(
TransportLossReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnStreamsConfig(StreamsConfig msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnReceivedPacket(
ReceivedPacket msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnNetworkStateEstimate(
NetworkStateEstimate msg) {
return NetworkControlUpdate();
}
} // namespace pcc
} // namespace webrtc
八、PccFactory --- 工厂类
pcc_factory.h
cpp
namespace webrtc {
// PCC控制器工厂, 实现 NetworkControllerFactoryInterface 接口
class PccNetworkControllerFactory : public NetworkControllerFactoryInterface {
public:
PccNetworkControllerFactory();
// 创建PCC控制器实例
std::unique_ptr<NetworkControllerInterface> Create(
NetworkControllerConfig config) override;
// 返回定时回调间隔
TimeDelta GetProcessInterval() const override;
};
} // namespace webrtc
pcc_factory.cc
cpp
namespace webrtc {
PccNetworkControllerFactory::PccNetworkControllerFactory() {}
std::unique_ptr<NetworkControllerInterface> PccNetworkControllerFactory::Create(
NetworkControllerConfig config) {
return std::make_unique<pcc::PccNetworkController>(config);
//创建PCC控制器实例
}
TimeDelta PccNetworkControllerFactory::GetProcessInterval() const {
return TimeDelta::PlusInfinity();
// 返回正无穷 → PCC不依赖定时器驱动
// 完全由包事件(OnSentPacket / OnTransportPacketsFeedback)驱动
// 这与GCC/BBR依赖定时器的设计不同
}
} // namespace webrtc
九、核心数学公式汇总

十、算法整体执行流程
cpp
OnSentPacket(发包触发)
│
├─ 首包 ? → 初始化,创建 Startup MI(500ms, 300kbps)
│
├─ 更新发包间隔EWMA
│
├─ 当前MI结束且还有下一个 ? → 启动下一个MI
│
├─ 超时(> RTT×2) ? → bandwidth = min(bw×0 .5, 接收速率)
│
├─ Startup到期(500ms) ? → 估算接收速率 → 进入 SlowStart
│
└─ 所有MI反馈收完 或 超时 ? → 开始新一轮 :
├─ SlowStart : 创建 1个MI, 速率 = 1.5 × bw
└─ OnlineLearning : 创建 2个MI, 速率 = r(1±5 %)
cpp
OnTransportPacketsFeedback(反馈触发)
│
├─ 保存最近20包,
更新RTT
│
├─ 将反馈路由到各MI → 标记完成的MI
│
└─ 所有MI完成 ?
├─ 检查是否需要 DoubleCheck(低速率高丢包异常)
│ ├─ 需要 → 进入DoubleCheck, 用相同速率再测一轮
│ └─ 不需要 → 调用 UpdateSendingRateAndMode() :
│ ├─ SlowStart : 效用增长 ? → 采纳速率, 继续
│ │ 效用不增长 ? → 退出 → OnlineLearning
│ └─ OnlineLearning :
│ gradient = [U(r + ε) - U(r - ε)] / [2ε·r]
│ Δr = gradient × stepSize
│ Δr = ApplyDynamicBoundary(Δr)
│ new_r = max(0, old_r + Δr)
└─ DoubleCheck完成 → 回到OnlineLearning
十一、WebRTC PCC 算法存在哪些不足
| 编号 | 维度 | 不足之处 | 源码 | 实际影响 |
|---|---|---|---|---|
| 1 | 丢包处理 | 不区分拥塞丢包与随机丢包,所有丢包统一惩罚 | utility_function.cc 效用函数中 loss_coefficient_ * bitrate * loss_rate,直接使用总丢包率 L |
在无线/卫星等有随机丢包的链路上,误判为拥塞 → 速率被过度压低,利用率极差 |
| 2 | 收敛速度 | Startup 阶段浪费 500ms 以固定 300kbps 发送,不做任何速率调整 | kStartupDuration = 500ms,kInitialBandwidthKbps = 300,Startup 结束才切换到 SlowStart |
高带宽链路(如 100Mbps)需要极长时间才能探测到实际容量 |
| 3 | 收敛速度 | 慢启动仅 1.5x 增长,且每轮只有 1 个 MI | kSlowStartModeIncrease = 1.5,慢启动只创建 1 个 MI |
从 300kbps 增长到 50Mbps 需要 ≈30 轮慢启动,若每轮 200ms 则需要 6 秒以上 |
| 4 | RTT 测量 | 使用反馈批次中的最大 RTT而非最小 RTT | rtt_tracker.cc: packet_rtt = std::max(packet_rtt, ...) |
排队延迟、处理延迟会拉高估计值,不能准确反映传播时延 |
| 5 | RTT 跟踪 | 无 base RTT(最小 RTT)跟踪,仅维护 EWMA 平滑值 | RttTracker 仅有 rtt_estimate_,无 min_rtt_ 成员 |
无法判断当前 RTT 偏离基线多远,无法做主动排空/延迟预算 |
| 6 | 延迟控制 | 无硬性延迟预算(Delay Budget)机制 | 代码中无任何 base_rtt * multiplier 的限制逻辑 |
RTT 可能无限膨胀,在长肥管道中排队延迟可达数百毫秒 |
| 7 | 紧急响应 | 无紧急回退机制,即使丢包率飙升到 50% 也需要等 MI 结束后才反应 | OnTransportPacketsFeedback 中只在所有 MI 完成后才调用 UpdateSendingRateAndMode |
突发拥塞时响应延迟 = MI 时长 + 反馈延迟,可能数百毫秒到数秒 |
| 8 | 超时处理 | 超时处理过于粗暴:直接减半且无渐进恢复 | bandwidth_estimate_ = std::min(bw * 0.5, receiving_rate) |
超时后速率骤降 50%,无平滑过渡;恢复也依赖慢速梯度上升 |
| 9 | 采样步长 | ε=0.05 硬编码,不自适应 | kDefaultSamplingStep = 0.05 |
低速率时 5% 变化太小(如 100kbps → ±5kbps),高速率时探测幅度可能过大 |
| 10 | 探测方式 | 两个 MI 串行执行,网络条件可能在两个 MI 之间发生变化 | 先运行 MI[0](r+ε),等完成后再运行 MI[1](r-ε) |
如果带宽在两个 MI 之间变化,梯度计算结果可能失真 |
| 11 | DoubleCheck | 异常检测只复测一次,不具备持续鲁棒性 | DoubleCheck 完成后直接 mode_ = kOnlineLearning,不再检查 |
如果网络持续不稳定(如高抖动),单次复测不足以得出可靠结论 |
| 12 | 步长控制 | 步长放大器可无限增长(2n-3),无上限 | step_size_amplifier = 2 * abs(n) - 3,n 可任意大 |
连续多次同向调整后,步长可能极大 → 速率剧烈跳变 → 震荡 |
| 13 | 动态边界 | 方向切换时边界立即重置为最小值(10%) | consecutive_boundary_adjustments_number_ = 0 |
在平衡点附近频繁切换方向时,边界反复收缩 → 调整幅度被限制 → 收敛缓慢 |
| 14 | 吞吐量估算 | 效用函数使用目标发送速率 r 而非实际吞吐量 | bitrate = monitor_interval.GetTargetSendingRate().bps() |
当实际吞吐量远低于发送速率时(如严重拥塞),效用值不能准确反映网络状况 |
| 15 | 噪声过滤 | 时延梯度仅靠阈值 0.01 过滤,无 EWMA 平滑 | if (abs(rtt_gradient) < 0.01) rtt_gradient = 0 |
单个 MI 内的梯度受突发抖动影响大,可能产生虚假的正/负梯度信号 |
| 16 | 丢包率统计 | 每个 MI 独立计算丢包率,无跨 MI 的 EWMA 平滑 | GetLossRate() 仅计算本 MI 内的 lost/(lost+received) |
小样本(20 个包中丢 1 个 = 5%)方差极大,统计不可靠 |
| 17 | 接收速率限制 | 仅在超时时考虑接收速率,正常情况下不做限速 | 超时代码 min(bw*0.5, receiving_rate),但 UpdateSendingRateAndMode 中无此逻辑 |
梯度上升可能将速率推到远超链路容量,造成持续丢包和排队 |
| 18 | 公平性 | 无显式公平性机制,多流竞争时无保障 | 代码中无任何公平性相关逻辑 | 多条 PCC 流共享瓶颈时,可能出现严重不公平 |
| 19 | 随机种子 | 随机种子固定为 100,行为完全确定性 | const uint64_t kRandomSeed = 100 |
多条 PCC 流使用相同种子 → 探测方向同步 → 同时升速或同时降速 → 加剧震荡 |
| 20 | MI 时长策略 | 默认使用固定策略(基于包数),不考虑 RTT | monitor_interval_length_strategy_ = kFixed,时长 = 发包间隔 × 20 |
高 RTT 链路(如 300ms):MI 可能只有几十毫秒,在收到第一个反馈前已结束多个 MI |
| 21 | 抖动处理 | 无任何抖动(Jitter)检测或补偿机制 | 时延梯度直接用原始时延做线性回归,无去抖动处理 | 高抖动环境下(如 WiFi),时延梯度噪声极大,导致频繁误判 |
| 22 | 负梯度截断 | 时延下降(队列排空)时奖励被截断在 -0.1 | rtt_gradient = max(gradient, -0.1) |
当网络从拥塞中快速恢复时,算法不能充分利用时延下降信号来加速升速 |
| 23 | MI 结束判断 | 依赖收到发送时间超出 MI 结束时间的包才标记完成 | if (send_time > start_time_ + duration) { done = true; } |
如果 MI 结束后恰好无包发送(如应用层暂停),则 MI 永远不会完成 → 状态卡死 |
| 24 | 效用函数参数 | 所有效用函数参数硬编码,无自适应调节 | kRttGradientCoefficientBps=0.005, kLossCoefficientBps=10 等均为常量 |
对不同类型网络(有线/无线/卫星)不能自动适配,需要人工调参 |
| 25 | 接口实现 | 多个 NetworkController 接口返回空实现 | OnNetworkRouteChange, OnTargetRateConstraints 等全部返回空 |
不响应路由变更、速率约束等外部信号,无法与 WebRTC 其他模块协同 |
十二、不足分类汇总
| 类别 | 涉及编号 | 核心问题 |
|---|---|---|
| 时延控制薄弱 | 4, 5, 6, 15, 21, 22 | 无 base RTT、无延迟预算、无抖动处理、梯度噪声大 |
| 丢包处理粗糙 | 1, 16 | 不区分丢包类型、小样本统计方差大 |
| 收敛速度慢 | 2, 3, 9, 13, 20 | Startup浪费500ms、慢启动仅1.5x、步长不自适应 |
| 鲁棒性不足 | 7, 8, 10, 11, 23 | 无紧急回退、超时处理粗暴、串行探测易受非平稳干扰 |
| 稳态震荡 | 12, 14, 17 | 步长无上限、效用用目标速率而非实际吞吐、无接收速率限速 |
| 多流/部署 | 18, 19, 24, 25 | 无公平性、固定种子、参数硬编码、接口不完整 |
十三、基于webrtc pcc算法的不足进行改进的pcc++算法
作者对webrtc pcc原生算法针对rtc场景下进行的了改进。直接看效果
PCC++ vs WebRTC PCC (稳态指标)
|----------------------------|----------------|--------|----------------|--------|-------------------------------------|
| 场景 | webrtc pcc | | pcc++ | | 对比结果 |
| | Loss / RTT | Util | Loss / RTT | Util | |
| Stable Wired (50M/20ms) | 0.0%/20.9ms | 15.05% | 0.0%/21.74ms | 91.63% | 丢包持平 rtt+0.84ms 效用值更优+76.58% |
| Wireless (10M/50ms/2%) | 3.57%/52.78ms | 3.11% | 3.34%/54.49ms | 5.56% | 丢包更优-0.23% rtt+1.71ms 效用值更优+2.45% |
| Satellite (20M/300ms/0.5%) | 0.72%/305.33ms | 5.28% | 0.51%/307.24ms | 63.95% | 丢包更优-0.21% rtt+1.91ms 效用值更优+58.67% |
| Dynamic BW (30→10M) | 1.34%/42.05ms | 4.24% | 1.35%/42.82ms | 20.96% | 丢包+0.01% rtt+0.77ms 效用值更优+16.72% |
| Low BW (1.5M/100ms/1%) | 1.99%/108.65ms | 58.40% | 1.58%/104.90ms | 59.65% | 丢包更优-0.41% rtt更优-3.75ms 效用值更优+1.25% |
| High Jitter (5M/30+50ms) | 1.19%/32.07ms | 12.92% | 3.61%/35.04ms | 2.05% | 丢包+2.42% rtt+2.97ms 效用值-10.04 |
| Long Fat Pipe (100M/150ms) | 0.04%/154.20ms | 2.62% | 0.09%/154.28ms | 76.23% | 丢包+0.05% rtt+0.08ms 效用值+73.61 |
| | | | | | |
关键改进成果
丢包率:7 个场景中 4 个优于或持平 WebRTC PCC,其余 3 个差距均 < 2.42%
RTT 控制:5/7 场景与 WebRTC PCC 的 RTT 差距 < 3ms;Low BW 场景 PCC++ 的 RTT 反而更低
带宽利用率:6/7 场景 PCC++ 更高,卫星链路 63.95% vs 5.28%,长肥管道 76.23% vs 2.62%
测试demo:链接: https://pan.baidu.com/s/1CBR1M7-n9ySYWEosM1vABA 提取码: cyqc