WEBRTC浅析(六)拥塞控制GCC的简介以及WEBRTC中的实现

WEBRTC浅析(六)拥塞控制GCC的简介以及WEBRTC中的实现

导读

本文分为两部分:
第一部分介绍了谷歌的GCC的文档。其中主要介绍了GCC的各个模块,以及具体算法实现。
第二部分,结合了webrtc来看GCC各个模块的具体实现。

一:GCC(Congestion Control Algorithm)文档介绍

原文:Congestion Control Algorithm

  • 两种拥塞控制方法

    1. 基于 RTCWEB的拥塞控制
    2. 一个基于延迟和损失
  • 拥塞控制是对应用程序的所有网络资源的调控。

  • 实时流媒体的拥塞控制策略面临的几大挑战:

    1. 媒体的编码经常无法快速的适应带宽的变化。
    2. 与会人可能会对如何响应有特殊的需求,比如在发现拥塞后不想降低带宽。
    3. 编码对丢包非常敏感,因为对实时性的要求限制了通过重传去修复传输中丢失的媒体包。

反馈和扩展

  1. the controllers are running at the send-side

  2. a delay-based controller at the receive-side

发送Engine

  1. Pace send 模块用来控制实际发送的码率。
    • burst_time 是 5 ms

基于延迟的拥塞控制(The delay-based control)

可以分为四个部分:a pre-filtering, an arrival-time filter, an over-use detector,and a rate controller.
  1. an adaptive filter::持续的更新网络带宽,根据收到包的时间。

    • 内部到达时间(inter-arrival time) : t(i)

      • 内部到达时间之差(the difference inarrival time) : t(i) - t(i-1)
    • 内部离开时间(inter-departure time) : T(i)

      • 内部离开时间之差(the difference in departure-time) : T(i) - T(i-1)
    • 内部延迟(the inter-group delay) : d(i)

      • d(i) = t(i) - t(i-1) - (T(i) - T(i-1))
        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4fs5Alk-1625995097095)(./1.png)]
      • Any packets received out of order are ignored by the arrival-time model
    • 组内延迟公式(inter-group delay):

      • d(i) = w(i)
        1. If weare over-using the channel we expect the mean of w(i) to increase,
        2. if a queue on the network path is being emptied, the mean of w(i)
          will decrease;
        3. otherwise the mean of w(i) will be zero
    • 把w(i)用 m(i) 和 v(i)拆分

      • d(i) = m(i) + v(i)
        • 网络排队延迟 : m(i)
        • 零均值噪声(noise term) : v(i)
          • 代表网络抖动和其他m(i)之外的抖动。(represents network jitter and other delay effects not captured by the model)
  2. 预过滤(The pre-filtering)

    • 预过滤模块的作用是用来处理由信道突变导致的瞬时延迟。
    • 划分到同一个组内的条件:
      1. 在burst_time间隔内,发送的一系列数据包。webrtc里是5ms。
      2. inter-arrival time 小于 burst_time,或者 d(i)(inter-group delay) 小于 0。
  3. 到达时间滤波器(Arrival-time Filter)

    • d(i): 当前测量值。这个可以通过收到的packet来计算出来。

    • m(i): 当前估计值。这个值可以用来检测网络是否过载(over-used)。

    • 下面让我们来计算一下 i 时刻的 估计值 m(i)

        1. m(i+1) = m(i) + u(i)
        
        	
        2. m_hat(i) = m_hat(i-1) + z(i) * k(i)
         
         
        3. z(i) = d(i) - m_hat(i-1)
        	
                    e(i-1) + q(i)
        4. k(i) = ----------------------------------------
        	    var_v_hat(i) + (e(i-1) + q(i))
        	
        5. e(i) = (1 - k(i)) * (e(i-1) + q(i))
      
        6. q(i) = E{u(i)^2}
      
        	q(i) is RECOMMENDED equal to 10^-3
      
  4. 过载检测(over-use detector)

    • 我们可以设置一个阈值(del_var_th)。通过检测这个del_var_th的值来判断是否over-use。这个del_var_th的值应该是动态的,以适应各个环境。

    • 检测逻辑:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zbgAbh9q-1625995097097)(2.png)]

    • del_var_th 的计算公式

        del_var_th(i) = del_var_th(i-1) + (t(i)-t(i-1)) * K(i) * (|m(i)|-del_var_th(i-1))
      

    del_var_th(i) 的值建议在[6, 600]之间,因为太小的值会让检测结果变得太敏感。

  5. 速率控制器(rate control)

    • 速率控制器可以分为两部分:

      • 基于延迟的带宽估计
      • 基于丢包的带宽估计
    • 速率控制系统有三种状态: Increase, Decrease and Hold

        	+----+--------+-----------+------------+--------+
           |     \ State |   Hold    |  Increase  |Decrease|
           |      \      |           |            |        |
           | Signal\     |           |            |        |
           +--------+----+-----------+------------+--------+
           |  Over-use   | Decrease  |  Decrease  |        |
           +-------------+-----------+------------+--------+
           |  Normal     | Increase  |            |  Hold  |
           +-------------+-----------+------------+--------+
           |  Under-use  |           |   Hold     |  Hold  |
           +-------------+-----------+------------+--------+
      

基于丢包的拥塞控制

前面介绍基于时延的控制是有一个假设前提,即传输通道的缓冲足够大。
当传输通道的缓冲很小时,通过时延是观测不到过载状态的,这时需要丢包率来表示过载
  1. As_hat : 基于丢包的计算出的带宽(loss-based controller)

    • rtt: round-trip time
    • 丢包率: packet loss
    • A_hat : 基于延迟计算出的码率(available bandwidth estimates
      received from the delay-based controller)
    • 在信道容量大的时候,基于延迟的计算是有效的。但是小信道容量小的时候,基于丢包的估计更加准确。
  2. 计算 As_hat(i)

     p :丢包率
    
    • 丢包率2-10% , As_hat(i)保持不变。
    • 丢包率大于10% , As_hat(i) = As_hat(i-1)(1-0.5p)
    • 丢白率小于1% , As_hat(i) = 1.05(As_hat(i-1))

最终发送码率:

sending rate = min(As_hat(i), A_hat(i)) (取两者的最小值)

二: WebRTC 代码对照

基于延迟的带宽估计

数据流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5WbpG3hS-1625995097098)(flow.png)]

  • 模块:DelayBasedBwe

  • 作用:计算基于延迟的带宽

    1. 预过滤(The pre-filtering)

      • 函数:InterArrival::ComputeDeltas

      • 作用:过滤

      • 输入:

          uint32_t timestamp,
          int64_t arrival_time_ms,
          size_t packet_size
        
      • 输出:

           uint32_t* timestamp_delta,
           int64_t* arrival_time_delta_ms,
           int* packet_size_delta
        
      • code:

          	// 如果检测到是一个新的group
            if (NewTimestampGroup(arrival_time_ms, timestamp)) {
              // First packet of a later frame, the previous frame sample is ready.
              if (prev_timestamp_group_.complete_time_ms >= 0) {
                *timestamp_delta = current_timestamp_group_.timestamp -
                                   prev_timestamp_group_.timestamp;
                *arrival_time_delta_ms = current_timestamp_group_.complete_time_ms -
                                         prev_timestamp_group_.complete_time_ms;
                
                .......
                
                *packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
                    static_cast<int>(prev_timestamp_group_.size);
                calculated_deltas = true;
          }
        
    2. 到达时间滤波器(Arrival-time Filter)

      • 模块:TrendlineEstimator::Update
      • 作用:在webrtc中,发送端没有使用卡尔曼滤波器,取而代之的是一个简单的过载滤波。
      • 入参:滤波器需要的三个参数:发送时刻差值(delta_timestamp)、到达时刻差值(delta_arrival)和包组数据大小差值(delta_size)
      • 输出:斜率
      1. 首先计算单个包组传输增长的延迟:

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-16W9gSbo-1625995097100)(9.png)]

         const double delta_ms = recv_delta_ms - send_delta_ms;
        
      2. 每个包组的叠加延迟

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dLcL2L3n-1625995097101)(02.png)]

         // 累计延迟
         accumulated_delay_ += delta_ms;
        
      3. 通过累积延迟计算一个均衡平滑延迟值
        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-osN9vg0S-1625995097103)(3.png)]

           smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
                             (1 - smoothing_coef_) * accumulated_delay_;
        
      4. 将第i个包组的传输持续时间

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7Ek3TeC-1625995097104)(5.png)]

           // trans = arrival_time_ms - first_arrival_time_ms
           // Simple linear regression.
           delay_hist_.push_back(std::make_pair(
               static_cast<double>(arrival_time_ms - first_arrival_time_ms),
               smoothed_delay_));
        
      5. 对累计延迟和均衡平滑延迟再求平均

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-luTmwrjq-1625995097105)(4.png)]

         	。。。。
        
        
           for (const auto& point : delay_hist_) {
             sum_x += point.first;
             sum_y += point.second;
           }
           
           // x_avg 是平均累计延迟
           double x_avg = sum_x / points.size();
           
           // y_avg 是均衡平滑延迟值
           double y_avg = sum_y / points.size();
        
      6. 趋势斜率分子值和分母值

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SsQKWXOp-1625995097106)(6.png)]
        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHds2jis-1625995097107)(7.png)]

           for (const auto& point : delay_hist_) {
           
           	 // 分子
             numerator += (point.first - x_avg) * (point.second - y_avg);
        
             // 分母
             denominator += (point.first - x_avg) * (point.first - x_avg);
           }
        
      7. 趋势值为:

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzqFVqW7-1625995097108)(8.png)]

        • Trendline filter通过到达时间差、发送时间差和数据大小来得到一个趋势增长值,如果这个值越大说明网络延迟越来越严重,如果这个值越小,说明延迟逐步下降.
    3. 过载检测(over-use detector)

      • 函数:OveruseDetector::Detect

      • 作用:估计出当前的延迟,计算当前带宽是否过载(over-use)。trendline乘以周期包组个数就是m_i

      • 输入:

          // offset = trendline_ * threshold_gain_
          double offset
          
          // send 平均发送延迟增益
          double timestamp_delta,
          
          // num_of_deltas_
          int num_of_deltas,
        
      • 输出:

          enum class BandwidthUsage {
            kBwNormal = 0,
            kBwUnderusing = 1,
            kBwOverusing = 2,
          };
        
      • 代码:

           // 计算 m(i)
            double T = std::min(num_of_deltas, kMinNumDeltas) * offset;
        
          	// 具体逻辑可以参考上文的 over-user检测逻辑
            if (T >= threshold/* || (loss_rate >= 0.17f && T > threshold_in_loss)*/) {
              if (time_over_using_ == -1) {
                // Initialize the timer. Assume that we've been
                // over-using half of the time since the previous
                // sample.
                time_over_using_ = ts_delta / 2;
              } else {
                // Increment timer
                time_over_using_ += ts_delta;
              }
              overuse_counter_++;
              if ((time_over_using_ > overusing_time_threshold/* ||
                   (loss_rate >= 0.17f && time_over_using_ > overusing_time_threshold_in_loss)*/)
                  /*&& overuse_counter_ > 1*/) {
                if (offset >= prev_offset_) {
                  time_over_using_ = 0;
                  overuse_counter_ = 0;
                  hypothesis_ = BandwidthUsage::kBwOverusing;
                }
              }
            } else if (T < -threshold) {
              time_over_using_ = -1;
              overuse_counter_ = 0;
              hypothesis_ = BandwidthUsage::kBwUnderusing;
            } else {				  	
              time_over_using_ = -1;
              overuse_counter_ = 0;
              hypothesis_ = BandwidthUsage::kBwNormal;
            }
        
      • 函数:OveruseDetector::UpdateThreshold

      • 作用:更新del_var_th(i) 的值,并限制在[6, 600]之间,因为太小的值会让检测结果变得太敏感

            // 更新阈值(del_var_th)
            threshold_ +=
                k * (fabs(modified_offset) - threshold_) * time_delta_ms;
            
            // 把阈值(del_var_th)限制在 [6, 600]之间
            threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
        
    4. 速率控制器(rate control)

      • 函数:AimdRateControl::ChangeBitrate
      • 作用:根据不同的状态,采取不用的码率调节侧率

基于丢包的带宽估计

  • 模块:SendSideBandwidthEstimation::UpdateEstimate
  • 作用:根据丢包率,计算基于丢包的可用带宽。
    • 丢包率2-10% , As_hat(i)保持不变。

    • 丢包率大于10% , As_hat(i) = As_hat(i-1)(1-0.5p)

        new_bitrate = static_cast<uint32_t>(
        	(current_bitrate_bps_ * 
        	static_cast<double>(512 - last_fraction_loss_)) /512.0);
      
    • 丢白率小于1% , As_hat(i) = 1.05(As_hat(i-1))

          // webrtc中丢包的实现和Gcc文档里描述的机制还会略有出入
        float beta = 1.08f;
                     uint32_t increase_bps = std::max<uint32_t>(min_bitrate_history_.front().second*(beta - 1), kMinIncreaseBps);
        new_bitrate = static_cast<uint32_t>(min_bitrate_history_.front().second) + increase_bps + 0.5;
        // Add 1 kbps extra, just to make sure that we do not get stuck
        // (gives a little extra increase at low rates, negligible at higher
        // rates).
        new_bitrate += 1000;
      

最终发送码率的计算:

  • 函数:SendSideBandwidthEstimation::CapBitrateToThresholds

  • 公式:sending rate = min(As_hat(i), A_hat(i)) (取两者的最小值)

        //如果当前bitrate(基于丢包的带宽)大于delay_based_bitrate_bps_(基于延时的带宽)时
        if (delay_based_bitrate_bps_ > 0 && bitrate_bps > delay_based_bitrate_bps_) {
      		bitrate_bps = delay_based_bitrate_bps_;
    	}
    		。。。
    	// 把比较后的码率赋值给当前码率
    	current_bitrate_bps_ = bitrate_bps;
    

上一篇:rowsBetween + over窗口函数实际应用


下一篇:【AI学习总结】均方误差(Mean Square Error,MSE)与交叉熵(Cross Entropy,CE)损失函数