WebRTC 入门篇:webrtc中的带宽自适应算法

WebRTC 入门篇:webrtc中的带宽自适应算法

互联网上有一套自己的词语(或者行话)。大多数的词人们都已经熟知:比如谷歌一下(搜索),巨魔(那家伙)等等。

但是有些词汇,特别是那些技术性质的专业术语的意思有的时候可以互换使用。最有代表性的就是网络速度,带宽和比特率了。

首先,让我们抛开速度这个词不看,这会让事情变得更加清楚一点。速度是一个很模糊的术语,它可以包含带宽和比特率的意思。

好了,现在没有字面上的困惑了,我们就可以继续探讨那些可以明确定义的:

比特率

在指定的时间量内(通常为1秒)可以从一点传送或处理到另一个点的比特数(都是0或者1)。基本上可以将其理解为高速公路的限速。

带宽

任何时候可以传输或处理的数据总量(通常也是1秒)。因此,带宽和比特率通常以千比特每秒或者兆比特每秒为单位。你可以把带宽想象成你的车可以容纳的人数。

他们之间的关系

我们用一个日常生活中的例子来类比说明比特率和带宽之间的关系。如果你有一个限搭载40人的客车以每小时60英里的速度行驶,那么你就可以在一个小时之内将40个人运送到60英里以外的地方。但是现在,让我们假设你在一辆双座的兰博基尼跑车上,每小时可以行驶251英里。如果开跑车,你可以更快地行驶出相同的距离,但是人数就会少很多(2个人在一小时内行驶251英里)。

比特率和带宽之间的关系也是如此。如果你有更多的带宽的话,你就可以传输更多的比特量。

总的来说

带宽是容量,而比特率是传输速率。

Webrtc 中的带宽自适应算法分为两种:

1. 发端带宽控制

原理是由 RTCP 中的丢包统计来动态地增加或减少带宽,在减少带宽时使用TFRC 算法来增加平滑度。

2. 收端带宽估算

原理是并由收到 RTP 数据,估出带宽; 用卡尔曼滤波器,对每一帧的发送时间和接收时间进行分析, 从而得出网络带宽利用情况,修正估计出的带宽。

两种算法相辅相成, 收端将估算的带宽发送给发端, 发端结合收到的带宽以及丢包率,调整发送的带宽。

下面具体分析两种算法:

1. 发送端带宽控制

WebRTC 入门篇:webrtc中的带宽自适应算法

2. 接收端带宽估算算法分析

结合文档http://tools.ietf.org/html/draft-alvestrand-rtcweb-congestion-02以及源码webrtc/modules/remote_bitrate_estimator/overuse_detector.cc进行分析。

带宽估算模型: d(i) = dL(i) / c + w(i) d(i)两帧数据的网络传输时间差,dL(i)两帧数据的大小差, c为网络传输能力, w(i)是我们关注的重点, 它主要由三个因素决定:发送速率, 网络路由能力, 以及网络传输能力。w(i)符合高斯分布, 有如下结论:当w(i)增加时,占用过多带宽(over-using);当w(i)减少时,占用较少带宽(under-using);为0时,用到恰好的带宽。所以,只要我们能计算出w(i),即能判断目前的网络使用情况,从而增加或减少发送的速率。

算法原理:即应用kalman-filters(卡尔曼滤波)

theta_hat(i) = [1/C_hat(i) m_hat(i)]^T // i时间点的状态由C, m共同表示,theta_hat(i)即此时的估算值

z(i) = d(i) – h_bar(i)^T * theta_hat(i-1) //d(i)为测试值,可以很容易计算出, 后面的可以认为是d(i-1)的估算值, 因此z(i)就是d(i)的偏差,即residual

theta_hat(i) = theta_hat(i-1) + z(i) * k_bar(i) //好了,这个就是我们要的结果,关键是k值得选取,下面两个公式即是取k值的,具体推导见后继博文

E(i-1) * h_bar(i)

k_bar(i) = ——————————————–

var_v_hat + h_bar(i)^T * E(i-1) * h_bar(i)

E(i) = (I – K_bar(i) * h_bar(i)^T) * E(i-1) + Q(i) // h_bar(i)由帧的数据包大小算出

由此可见,我们只需要知道当前帧的长度,发送时间,接收时间以及前一帧的状态,就可以计算出网络使用情况。

接下来具体看一下代码:

void OveruseDetector::UpdateKalman(int64_t t_delta,
double ts_delta,
                                    uint32_t frame_size,
                                    uint32_t prev_frame_size) {
   const double min_frame_period = UpdateMinFramePeriod(ts_delta);
   const double drift = CurrentDrift();
   // Compensate for drift
   const double t_ts_delta = t_delta - ts_delta / drift;  //即d(i)
   double fs_delta = static_cast(frame_size) - prev_frame_size; 
  // Update the Kalman filter
  const double scale_factor =  min_frame_period / (1000.0 / 30.0);
  E_[0][0] += process_noise_[0] * scale_factor;
  E_[1][1] += process_noise_[1] * scale_factor;
  if ((hypothesis_ == kBwOverusing && offset_ < prev_offset_) ||
      (hypothesis_ == kBwUnderusing && offset_ > prev_offset_)) {
    E_[1][1] += 10 * process_noise_[1] * scale_factor;
  }
  const double h[2] = {fs_delta, 1.0}; //即h_bar
  const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
                        E_[1][0]*h[0] + E_[1][1]*h[1]};
  const double residual = t_ts_delta - slope_*h[0] - offset_; //即z(i), slope为1/C
  const bool stable_state =
      (BWE_MIN(num_of_deltas_, 60) * fabsf(offset_) < threshold_);
  // We try to filter out very late frames. For instance periodic key
  // frames doesn't fit the Gaussian model well.
  if (fabsf(residual) < 3 * sqrt(var_noise_)) {
    UpdateNoiseEstimate(residual, min_frame_period, stable_state);
  } else {
    UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
  }
  const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
  const double K[2] = {Eh[0] / denom,
                       Eh[1] / denom}; //即k_bar
  const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]},
                            {-K[1]*h[0], 1.0 - K[1]*h[1]}};
  const double e00 = E_[0][0];
  const double e01 = E_[0][1];
  // Update state
  E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
  E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
  E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
  E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
  // Covariance matrix, must be positive semi-definite
  assert(E_[0][0] + E_[1][1] >= 0 &&
         E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 &&
         E_[0][0] >= 0);
  slope_ = slope_ + K[0] * residual; //1/C
  prev_offset_ = offset_;
  offset_ = offset_ + K[1] * residual; //theta_hat(i)
  Detect(ts_delta);
}


 BandwidthUsage OveruseDetector::Detect(double ts_delta) {
   if (num_of_deltas_ < 2) {
     return kBwNormal;
   }
   const double T = BWE_MIN(num_of_deltas_, 60) * offset_; //即gamma_1
   if (fabsf(T) > threshold_) {
     if (offset_ > 0) {
       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;
      }
      over_use_counter_++;
      if (time_over_using_ > kOverUsingTimeThreshold  //kOverUsingTimeThreshold是gamma_2, gamama_3=1
          && over_use_counter_ > 1) {
        if (offset_ >= prev_offset_) {
          time_over_using_ = 0;
          over_use_counter_ = 0;
          hypothesis_ = kBwOverusing;
        }
      }
    } else {
      time_over_using_ = -1;
      over_use_counter_ = 0;
 hypothesis_ = kBwUnderusing;
    }
  } else {
    time_over_using_ = -1;
    over_use_counter_ = 0;
    hypothesis_ = kBwNormal;
  }
  return hypothesis_;
}
展开阅读全文

页面更新:2024-02-13

标签:算法   卡尔   带宽   发端   速率   原理   大小   人数   状态   速度   能力   情况   关系   时间   科技   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top