webrtc弱网-ReceiveSideCongestionController类源码分析及算法原理

ReceiveSideCongestionController是WebRTC接收端拥塞控制的核心枢纽,承担双模式带宽估计的关键职责。它通过智能算法切换机制,动态选择发送端BWE(基于TransportSequenceNumber的延迟反馈)或接收端BWE(基于AbsoluteSendTime/TOF的本地估计)模式。针对音频流强制使用发送端BWE确保低延迟,视频流则自适应选择最优路径。该组件集成RembThrottler进行码率限制,通过RemoteEstimatorProxy生成TransportFeedback,并采用精细的线程安全设计保护共享状态。最终通过REMB和TransportFeedback RTCP消息将网络状态反馈给发送端,实现端到端的拥塞控制闭环,确保实时通信的质量和稳定性。

一. 核心功能

接收端拥塞控制器是WebRTC中负责接收方带宽估计的关键组件,主要功能包括:

  • 双模式带宽估计:支持发送端BWE和接收端BWE两种模式

  • 反馈管理:通过TransportFeedback和REMB消息向发送端反馈网络状态

  • 动态算法选择:根据RTP扩展头自动选择最优的带宽估计算法

  • 码率控制:限制最大接收码率,避免网络拥塞

二. 核心算法原理

发送端BWE (Sender-side BWE)

复制代码
详细见:https://blog.csdn.net/talkRTC/article/details/150635619?spm=1001.2014.3001.5501
// 基于TransportSequenceNumber的带宽估计
// 接收端记录包到达时间,通过TransportFeedback消息反馈给发送端
// 发送端基于这些信息进行带宽估计

接收端BWE (Receiver-side BWE)

复制代码
// 两种估计算法:
// 1. AbsoluteSendTime算法:基于绝对发送时间扩展头
// 2. SingleStream算法:基于传输时间偏移
// 算法根据数据包中的扩展头动态切换

算法原理图

  1. 两种估计算法对比
特性 AbsoluteSendTime算法 SingleStream(TOF)算法
时间基准 绝对发送时间戳 传输时间偏移
精度 高(24位, 约15μs) 中等
同步要求 需要时钟同步 相对时间,不要求严格同步
适用场景 视频会议、实时通信 传统RTP流
  1. 包分组处理
复制代码
// 包组划分原则:
// - 发送时间间隔 > 5ms 作为组边界
// - 每组包含多个连续包
// 计算组间延迟变化:
组间延迟梯度 = (到达时间差 - 发送时间差) / 发送时间差
  1. 过载检测机制
复制代码
基于Kalman滤波的延迟变化检测:
状态变量: [延迟梯度, 延迟变化率]
观测值: 实际测量的延迟梯度
检测逻辑:
  - 延迟梯度 > 阈值 + 方差: 过载
  - 延迟梯度 < -阈值: 欠载  
  - 其他: 正常

工作时序图

AST算法核心流程

复制代码
// 1. 时间戳转换
absolute_send_time = 从RTP扩展头提取
send_time_24bits = (absolute_send_time * 1000) >> 18

// 2. 包组检测
if (current_send_time - prev_send_time > 5ms) {
    // 新包组开始
    form_new_packet_group();
}

// 3. 延迟计算
inter_arrival = current_arrival - prev_arrival
inter_send = current_send - prev_send  
delay_gradient = inter_arrival - inter_send

// 4. 状态判断
if (delay_gradient > threshold + noise_var) {
    state = OVERUSE;
} else if (delay_gradient < -threshold) {
    state = UNDERUSE;
} else {
    state = NORMAL;
}

三. 关键数据结构

复制代码
class ReceiveSideCongestionController {
private:
    Clock& clock_;                          // 系统时钟
    RembThrottler remb_throttler_;          // REMB码率限制器
    RemoteEstimatorProxy remote_estimator_proxy_; // 远程估计代理
    
    mutable Mutex mutex_;                   // 线程安全锁
    std::unique_ptr<RemoteBitrateEstimator> rbe_; // 远程带宽估计器
    bool using_absolute_send_time_;         // 当前使用的算法标志
    uint32_t packets_since_absolute_send_time_; // AST算法包计数器
};

四. 核心方法详解

4.1 构造函数

复制代码
ReceiveSideCongestionController::ReceiveSideCongestionController(
    Clock* clock,
    RemoteEstimatorProxy::TransportFeedbackSender feedback_sender,
    RembThrottler::RembSender remb_sender,
    NetworkStateEstimator* network_state_estimator)
    : clock_(*clock),
      remb_throttler_(std::move(remb_sender), clock),
      remote_estimator_proxy_(std::move(feedback_sender), network_state_estimator),
      rbe_(new RemoteBitrateEstimatorSingleStream(&remb_throttler_, clock)),
      using_absolute_send_time_(false),
      packets_since_absolute_send_time_(0) {}

功能:初始化所有组件,默认使用SingleStream算法

4.2 包处理入口

复制代码
void ReceiveSideCongestionController::OnReceivedPacket(
    const RtpPacketReceived& packet, MediaType media_type) {
    bool has_transport_sequence_number = 
        packet.HasExtension<TransportSequenceNumber>() ||
        packet.HasExtension<TransportSequenceNumberV2>();
        
    if (media_type == MediaType::AUDIO && !has_transport_sequence_number) {
        // 音频流且无传输序列号,不支持接收端BWE
        return;
    }

    if (has_transport_sequence_number) {
        // 发送端BWE模式:转发包信息给远程估计代理
        remote_estimator_proxy_.IncomingPacket(packet);
    } else {
        // 接收端BWE模式:本地进行带宽估计
        MutexLock lock(&mutex_);
        PickEstimator(packet.HasExtension<AbsoluteSendTime>());
        rbe_->IncomingPacket(packet);
    }
}

4.3 算法选择器

复制代码
void ReceiveSideCongestionController::PickEstimator(bool has_absolute_send_time) {
    if (has_absolute_send_time) {
        // 发现AST扩展头,立即切换到AST算法
        if (!using_absolute_send_time_) {
            RTC_LOG(LS_INFO) << "切换到绝对发送时间RBE算法";
            using_absolute_send_time_ = true;
            rbe_ = std::make_unique<RemoteBitrateEstimatorAbsSendTime>(
                &remb_throttler_, &clock_);
        }
        packets_since_absolute_send_time_ = 0;
    } else {
        // 没有AST扩展头,等待一定包数后切回TOF算法
        if (using_absolute_send_time_) {
            ++packets_since_absolute_send_time_;
            if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) {
                RTC_LOG(LS_INFO) << "切换到传输时间偏移RBE算法";
                using_absolute_send_time_ = false;
                rbe_ = std::make_unique<RemoteBitrateEstimatorSingleStream>(
                    &remb_throttler_, &clock_);
            }
        }
    }
}

4.4 周期处理

复制代码
TimeDelta ReceiveSideCongestionController::MaybeProcess() {
    Timestamp now = clock_.CurrentTime();
    mutex_.Lock();
    TimeDelta time_until_rbe = rbe_->Process();           // 处理接收端BWE
    mutex_.Unlock();
    TimeDelta time_until_rep = remote_estimator_proxy_.Process(now); // 处理发送端BWE反馈
    TimeDelta time_until = std::min(time_until_rbe, time_until_rep);
    return std::max(time_until, TimeDelta::Zero());       // 返回下次处理时间
}

五. 设计亮点

5.1 智能算法切换

复制代码
// 基于数据包特征自动选择最优算法
// AST算法优先,在没有AST时回退到TOF算法
// 切换阈值kTimeOffsetSwitchThreshold=30包,避免频繁切换

算法原理图:

5.2 双模式支持

复制代码
// 统一处理发送端BWE和接收端BWE
// 根据TransportSequenceNumber存在性自动路由
// 音频流特殊处理:仅支持发送端BWE

算法原理图

5.3 线程安全设计

复制代码
// 使用Mutex保护共享状态
// RTC_GUARDED_BY(mutex_)注解明确线程约束
// 细粒度锁:仅保护接收端BWE相关状态

六. 典型工作流程

6.1 视频流处理(接收端BWE)

复制代码
1. 收到视频RTP包 → 检查扩展头
2. 有AbsoluteSendTime → 选择AST算法
3. 调用rbe_->IncomingPacket()进行本地估计
4. 定期MaybeProcess()生成REMB反馈
5. 通过remb_throttler_发送码率限制

6.2 音频流处理

复制代码
1. 收到音频RTP包
2. 必须有TransportSequenceNumber,否则跳过
3. 仅支持发送端BWE模式

这个设计体现了WebRTC拥塞控制的核心理念:灵活性自适应性,能够根据网络条件和流类型自动选择最优的带宽估计策略。

相关推荐
会员源码网12 小时前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing13 小时前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
HelloReader13 小时前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人
算法
颜酱16 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
qianpeng89717 小时前
水声匹配场定位原理及实验
算法
董董灿是个攻城狮1 天前
AI视觉连载8:传统 CV 之边缘检测
算法
AI软著研究员2 天前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish2 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱2 天前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者2 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶