流媒体学习之路(WebRTC)——GCC分析(2)

本文详细解析了WebRTC中的趋势线模块,通过时延梯度判断网络状态,并介绍码率变化模块如何根据网络状态动态调整码率。涉及GCC算法、NADA算法和SCReAM算法,提供了实际代码解读和关键函数剖析。
——
我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost

目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全配置,提供全面的可视化算法观察能力。

欢迎大家使用
——


背景

  事实上,带宽估计的算法有很多,有三种比较经典方案:
  GCC 算法[ https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc-02] 是出自 Google 的一种延时估计和丢包相结合的拥塞控制算法,在 WebRTC 中被默认使用。
  NADA 算法[https://datatracker.ietf.org/doc/html/rfc8698] 是思科公司提出的一种基于延时估计的方法,这种算法带宽利用率高,跟踪带宽变化方面表现很优秀。
  SCReAM 算法[https://www.rfc-editor.org/rfc/rfc8298.html] 是爱立信公司提出的一种基于延时估计的算法,在 OpenWebRTC 中被采用。
  这篇论文[Congestion Control for RTP Media: a Comparison on Simulated Environment] 比较了以上三种算法的效果。
  我们后续可能会对其他算法的原理进行

一、 趋势线模块

1.1 模块简介

  本模块主要针对趋势线滤波模块进行理论分析与解释,并对比旧的卡尔曼滤波器内容进行分析,来得到调整参数的参考。

1.2 到达时间模型

  在https://blog.jianchihu.net/webrtc-research-interarrival.html说到了到达时间模型,主要包含几个包组时间差计算的概念:
  ● 到达时间差:𝑡𝑖−𝑡𝑖−1
  ● 发送时间差:𝑇𝑖−𝑇𝑖−1
  ● 时延变化:𝑑𝑖=𝑡𝑖−𝑡𝑖−1−(𝑇𝑖−𝑇𝑖−1)
  这个时延变化用于评估时延增长趋势,判断网络拥塞状况。在[1]中,这个时延变化叫做单向时延梯度(one way delay gradient)。那这个时延梯度为什么可以判断网络拥塞情况呢?

1.3 时延梯度计算

  首先我们通过一张图看下时延梯度的计算:
请添加图片描述
  对于两个包组:𝑖以及𝑖−1,它们的时延梯度:
𝑑𝑖=𝑡𝑖−𝑡𝑖−1−(𝑇𝑖−𝑇𝑖−1) (1)

1.4 判断依据

  网络无拥塞的正常情况下:
请添加图片描述

  网络拥塞情况下:
请添加图片描述

  第一张图是没有任何拥塞下的网络传输,时延梯度𝑑1:𝑡1−𝑡0−(𝑇1−𝑇0)=0。第二张图是存在拥塞时的网络传输,包在𝑡1时刻到达,因为网络发生拥塞,导致到达时间比原本要晚,原本应在虚线箭头处时刻到达,时延梯度𝑑1:𝑡1−𝑡0−(𝑇1−𝑇0)>0。
  由上可知,正常无拥塞情况下,包组间时延梯度等于0,拥塞时大于0,我们可以通过数学方法估计这个时延梯度的变化情况评估当前网络的拥塞情况,这个就是WebRTC基于时延的带宽估计的理论基础。

1.4 线性回归

  WebRTC用到了线性回归这个数学方法进行时延梯度趋势预测。通过最小二乘法求得拟合直线斜率,根据斜率判断增长趋势。
请添加图片描述

  对于一堆样本点(𝑥,𝑦),拟合直线方程𝑦=𝑏𝑥+𝑎的斜率𝑏按如下公式计:
𝑏=∑𝑛𝑖=1(𝑥𝑖−𝑥¯)(𝑦𝑖−𝑦¯)∑𝑛𝑖=1(𝑥𝑖−𝑥¯)2			(2)

1.5 网络状态

  在WebRTC中,定义了三种网络状态:normal,overuse,underuse,用于表示当前带宽的使用情况,具体含义跟单词本身含义一样。
  例如如果是overuse状态,表示带宽使用过载了,从而判断网络发生拥塞,如果是underuse状态,表示当前带宽未充分利用。后面会介绍如何根据时延梯度增长趋势得到当前的网络状态。

1.6 代码导读

  由TrendlineEstimator类实现。主要接口就一个:UpdateTrendline。传入包组时间差,时间,包大小等参数,判断当前网络状态。

void UpdateTrendline(double recv_delta_ms,
                     double send_delta_ms,
                     int64_t send_time_ms,
                     int64_t arrival_time_ms,
                     size_t packet_size);

  说下输入的各个参数的含义:
  ● recv_delta_ms:包组接收时间差
  ● send_delta_ms:包组发送时间差
  ● send_time_ms:当前处理的RTP的包发送时间
  ● arrival_time_ms:当前处理的RTP的包到达时间
  ● packet_size:当前处理的RTP包的大小
  内部相关函数调用如下:

TrendlineEstimator::UpdateTrendline
                ↓
LinearFitSlope
                ↓
TrendlineEstimator::Detect
                ↓
TrendlineEstimator::UpdateThreshold

  下面结合代码说下UpdateTrendline函数内部计算过程。
  1) 计算时延变化累积值:

// 时延变化:接收时间差 - 发送时间差
const double delta_ms = recv_delta_ms - send_delta_ms;
++num_of_deltas_;
num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax);
if (first_arrival_time_ms_ == -1)
  first_arrival_time_ms_ = arrival_time_ms;
// 累积时延变化
accumulated_delay_ += delta_ms;

  2)根据1)中的时延累积值计算得到平滑后的时延:

smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
                  (1 - smoothing_coef_) * accumulated_delay_;

  3)将从第一个包到达至当前RTP包到达的经历时间,平滑时延值存到双端队列delay_hist_中:

delay_hist_.push_back(std::make_pair(
    static_cast<double>(arrival_time_ms - first_arrival_time_ms_),
    smoothed_delay_));

  4)当队列delay_hist_大小等于设定的窗口大小时,开始进行时延变化趋势计算,得到直线斜率,直线横坐标为经历时间,纵坐标为平滑时延值:

if (delay_hist_.size() == window_size_) {
   
   
  // 0 < trend < 1   ->  时延梯度增加
  //   trend == 0    ->  时延梯度不变
  //   trend < 0     ->  时延梯度减小
  trend = LinearFitSlope(delay_hist_).value_or(trend);
}

  5)通过计算得到的时延变化趋势拟合直线斜率,发送时间差,到达时间判断网络状态:

Detect(trend, send_delta_ms, arrival_time_ms);

1.7 LinearFitSlope函数

  使用最小二乘法求解线性回归,得到时延变化增长趋势的拟合直线斜率。

// 在该函数调用前有这么一段注释,意思为:trend 可以看做(发送码率 - 网络容量)/ 网络容量
// Update trend_ if it is possible to fit a line to the data. The delay
// trend can be seen as an estimate of (send_rate - capacity)/capacity.
// 0 < trend < 1   ->  the delay increases, queues are filling up
//   trend == 0    ->  the delay does not change
//   trend < 0     ->  the delay decreases, queues are being emptied

  看下内部代码实现:

absl::optional<double> LinearFitSlope(
    const std::deque<std::pair<double, double>>& points) {
   
   
  RTC_DCHECK(points.size() >= 2);
  // 所有节点(x,y),x,y值分别求和
  double sum_x = 0;
  double sum_y = 0
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值