JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
JCHub
Code as My Sword, Lost in Obsession
  1. Main page
  2. WebRTC
  3. Main content

WebRTC研究:包组时间差计算-InterArrival

2019年11月12日 11724hotness 6likes 21comments

在新版GCC(Google Congestion Control)也就是Sendside BWE中,包含两种拥塞控制模型。一种是基于丢包的,一种是基于时延的,全部在发送端计算。Sendside BWE最后综合这两种估计值取小的那个作为目标码率。

基于时延的拥塞控制算法主要由四部分组成:预处理(pre-filtering), 到达时间滤波器(arrival-time filter), 过载检测器(over-use detector),码率控制器(and a rate controller)。本文主要介绍其中的到达时间滤波器。

到达时间模型

在参考[1]中定义了一种到达时间模型(Arrival-time model),在这里我们先介绍几个概念。

两个包组(包组概念下一小节介绍,包组属于预处理部分的内容)的到达时间差:inter-arrival时间:

C++
1
t(i) - t(i-1)

两个包组离开(发送)时间差:inter-departure时间:

C++
1
T(i) - T(i-1)

包组i与包组i-1的时延变化:

C++
1
d(i) = t(i) - t(i-1) - (T(i) - T(i-1))

这个时延变化将在后续评估网络时延增长趋势的滤波器中用到,用于判断网络拥塞状况,这里就先不展开讨论,本文主要介绍这些时间差如何计算。

包组

在WebRTC中计算时延不是一个个包计算的,而是通过将包分组,然后计算这些包组间的时延,这样做目的主要是为了提高带宽估计准确性,避免无线网络环境下突发数据的影响,具体影响后面写篇专门文章分析。WebRTC中这些包组称作TimestampGroup。这里我们看下WebRTC中TimestampGroup的结构:

1
2
3
4
5
6
7
  struct TimestampGroup {
    TimestampGroup()
        : size(0), // 包组内包含的各个RTP包大小之和
          first_timestamp(0),
          timestamp(0),
          first_arrival_ms(-1),
          complete_time_ms(-1) {}

那么如何对包进行分组呢?

WebRTC是通过计算发送时间差值来分组。在包组中,除了第一个包外,后面的包距离包组第一个包的发送时间差小于5ms。假设每个包都有一个发送时间t,第一个包的发送时间为t0,如果后续的包发送时间与t0时间差△t = t - to <= 5ms,那么这些包都可以与t0发送时间的包归为一组,如果某个包得到的△t > 5ms,那么该包就作为下一个包组的第一个包,接着后面的包就跟该包继续比较时间差,判断能否归为一组。下面我们举个例子说明下。

上图中有两个包组TG1和TG2,其中序列号为1的包与4的时间差小于5毫秒,那么序列号1~4的包被划到一个包组TG1。序列号为5的包与1之间的时间间隔超过5毫秒,那么5就是TG2的第一个包,它与序列号6、7、8划到一个包组TG2。

在后续评估时延增长趋势的滤波器需要三个主要参数:发送时刻差值(timestamp_delta)、到达时刻差值(arrival_time_delta)和包组数据大小差值(packet_size_delta)。由上图可知:

C++
1
2
3
timestamp_delta = TG2:timestamp - TG1:timestamp;
arrival_time_delta = TG2:complete_time_ms - TG1:complete_time_ms;
packet_size_delta = TG2:size - TG1:size;

代码导读

ComputeDeltas函数

前面说到的时间差值计算以及包组判断代码主要由InterArrival类实现。InterArrival类主要就一个接口:ComputeDeltas。传入每个RTP包时间、大小等参数,计算包组时间间隔,包组大小差值,得到新的包组相关差值后返回true。

C++
1
2
3
4
5
6
7
bool InterArrival::ComputeDeltas(uint32_t timestamp,
                                 int64_t arrival_time_ms,
                                 int64_t system_time_ms,
                                 size_t packet_size,
                                 uint32_t* timestamp_delta,
                                 int64_t* arrival_time_delta_ms,
                                 int* packet_size_delta)

下面解释下各个参数:

  • timestamp:RTP包发送时间
  • arrival_time_ms:RTP包到达时间
  • system_time_ms:当前时间
  • timestamp_delta(output):包组发送时间差
  • arrival_time_delta_ms(output): 包组到达时间差
  • packet_size_delta(output) :包组大小差值

接下来看下相关代码流程,看timestamp_delta、arrival_time_delta_ms如何计算:

C++
1
2
3
4
5
6
7
8
9
10
11
12
// 若当前包属于新的包组
if (NewTimestampGroup(arrival_time_ms, timestamp)) {
  // 前一个包组有数据
  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计算如下:

C++
1
2
3
// 包组大小差值:当前包组大小减去前一个包组大小
*packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
                           static_cast<int>(prev_timestamp_group_.size);

NewTimestampGroup函数

WebRTC新包组判断代码:

C++
1
2
3
4
5
6
7
8
9
10
11
12
bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms,
                                     uint32_t timestamp) const {
  if (current_timestamp_group_.IsFirstPacket()) {
    return false;
  } else if (BelongsToBurst(arrival_time_ms, timestamp)) {
    return false;
  } else {
    uint32_t timestamp_diff =
        timestamp - current_timestamp_group_.first_timestamp;
    return timestamp_diff > kTimestampGroupLengthTicks;
  }
}

  • 若是第一个包,得作为后续包对比基准,不认为是新包组
  • 若是突发数据(burst),不认为是新包组
  • 与第一个包发送时间间隔大于kMaxBurstDurationMs(值为5),认为该包属于新包组

BelongsToBurst函数

这里我们也介绍下上一小节说到的突发数据。有些客户端发送数据时,没用使用pacing平滑发送模块,没有控制发送速率,这样容易导致突发流量,尤其是发送关键帧数据时,或者在无线网络环境下,也容易出现突发流量。我们看下WebRTC中如何判断突发数据的。

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool InterArrival::BelongsToBurst(int64_t arrival_time_ms,
                                  uint32_t timestamp) const {
  if (!burst_grouping_) {
    return false;
  }
  assert(current_timestamp_group_.complete_time_ms >= 0);
  // 与当前包组最后一个包的到达时间差
  int64_t arrival_time_delta_ms =
      arrival_time_ms - current_timestamp_group_.complete_time_ms;
  // 与当前包组最后一个包的发送时间差
  uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp;
  int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5;
  if (ts_delta_ms == 0)
    return true;
  // 传输时延
  int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms;
  // 传输时延变化小于0
  // 与当前包组最后一个包的到达时间差不大于kBurstDeltaThresholdMs(5ms)
  // 与当前包组第一个包的到达时间差小于kMaxBurstDurationMs(100ms)
  if (propagation_delta_ms < 0 &&
      arrival_time_delta_ms <= kBurstDeltaThresholdMs &&
      arrival_time_ms - current_timestamp_group_.first_arrival_ms <
          kMaxBurstDurationMs)
    return true;
  return false;
}

总结

本篇文章主要讲了到达时间模型以及在WebRTC中的源码实现:InterArrival。后面文章我们将研究下trendline滤波器。前面计算得到的相关差值我们将传递给trendline滤波器,进行网络拥塞情况判断。

WebRTC研究:Trendline滤波器-TrendlineEstimator

参考

[1] A Google Congestion Control Algorithm for Real-Time Communication.https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02.

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: WebRTC
Last updated:2022年3月27日

Jeff

管理员——代码为剑,如痴如醉

Tip the author Like
< Last article
Next article >

Comments

  • holden

    请问这个值是怎么得出的,为什么不是直接写5ms,而是用了下面这个公式
    (kTimestampGroupLengthMs << 26) / 1000

    2019年12月12日
    Reply
    • Jeff

      @holden 这个是内部不同时间单位的转换,kTimestampGroupLengthMs经过这个公式转换后就变为kTimestampGroupLengthTicks,你看下Ms与Ticks后缀,就明白具体单位含义了,一个是以毫秒为单位,一个是以采样频率为单位。

      2019年12月16日
      Reply
      • Holden

        @Jeff 再请教一下,我再win10上编译好了webRTC的文件,该怎么调式goog_cc的那个模块,我试着启了example中的peerconnection.exe,但是好像没调到congestion controller 模块

        2019年12月20日
        Reply
        • Jeff

          @Holden WebRTC本身是P2P传输,所以你还得跑P2P服务器,example也带有stun,turn服务器,因为是P2P,所以得跑两个peerconnection.exe。嫌麻烦,可以调试自带的gcc单元测试代码。

          2019年12月20日
          Reply
          • Holden

            @Jeff 请问怎么才能调试自带的gcc测试代码。。。我现在只能调试example里面的代码,其他路径下的文件都没有设为启动项这个选项

            2019年12月23日
            Reply
            • Jeff

              @Holden 我是自己在WebRTC里新建一个工程,把单元测试代码拷贝过来,然后调试

              2019年12月23日
              Reply
  • Holden

    大佬可不可以解释下这个公式:
    uint32_t send_time_24bits =
    static_cast(
    ((static_cast(info.send_time_ms) << kAbsSendTimeFraction) +
    500) /
    1000) &
    0x00FFFFFF;
    --------------------------------
    我看文档上NTP转ABS的公式是:abs_send_time_24 = (ntp_timestamp_64 » 14) & 0x00ffffff
    那开头的那个公式是怎么得出来的?
    而且后18位是数据为,左移18是做什么用的?
    +500/1000是在做向上圆整吗?
    最新的算法不是不适用abs时间了吗,为啥代码中还有使用?

    问题比较多,望大佬答疑解惑!

    2019年12月25日
    Reply
    • Jeff

      @Holden 抱歉,这个新转换公式我也无法解释,查了下,没看到相关文档说明。

      2019年12月30日
      Reply
    • Jeff

      @Holden 参考这个:https://blog.jianchihu.net/webrtc-research-dbb-abstime-trans.html

      2020年5月3日
      Reply
  • nativertc

    根据发送时间来分组,如果没有remb
    不是就没有abs time吗?那怎么根据发送时间来分组

    2020年11月7日
    Reply
    • Jeff

      @nativertc 建议先好好了解下WebRTC现在代码在哪一端计算这个。

      2020年11月8日
      Reply
      • nativertc

        @Jeff 我应该找到了,rfc5450, toffset,我想要的应该是这个。虽然还没有看的太明白
        不是接收端计算到达包组的到达时间差?

        2020年11月9日
        Reply
        • Jeff

          @nativertc 计算都在发送端

          2020年11月9日
          Reply
  • nativertc

    终于搞懂了,看了下源码,和toffset这个没有关系。是通过rtcp反馈然后在发送端计算的,abs-send-time是记录在history里面的

    2020年11月24日
    Reply
  • Jeffrey

    另外请教一下
    看了一下该部分的代码,arrival_time_ms在发送端计算的,而且是以第一次收到的transport feedback的包的接收时间做为基准,所以这里的arrival_time_ms是不是代表的是transport feedback的接收时间而并非rtp包接收端的接收时间呀

    2021年6月30日
    Reply
    • Jeff

      @Jeffrey 这里RTP包接收时间计数方式按前面文章说的是以reference time为基准,WebRTC里这个reference time是以第一个收到的feedback包的接收时间为基准,后面其他feecback包reference time会基于这个时间加上delta(各个feedback包reference time间隔)。所以你看到的RTP包接收时间是以第一次收到的transport feedback的包接收时间为基准。无论以哪个为基准都不影响计算,我们关注的是各个包的接收时间间隔。

      2021年7月5日
      Reply
  • iubrutally

    请教一下,在判断突发流量时候,不是应该取发送时间的绝对值么,这地方怎么处理乱序到达的突发包呢?

    2021年8月16日
    Reply
  • Jeffrey

    double send_delta_ms = (1000.0 * timestamp_delta) / (1 << kInterArrivalShift);
    这句有是啥意思呀

    2021年9月14日
    Reply
    • Jeffrey

      @Jeffrey 前面这个乘以1000 是转换成ms ,后面除以这个是啥来着?

      2021年9月14日
      Reply
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    Cancel

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    文章目录
    • 到达时间模型
    • 包组
    • 代码导读
      • ComputeDeltas函数
      • NewTimestampGroup函数
      • BelongsToBurst函数
    • 总结
    • 参考
    Related Posts
    • 浅谈基于SFU实现一对一效果
    • WebRTC资讯:H265支持进展
    • Protected: WebRTC硬件编解码器出错无缝切换软编软解
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    Categories

    COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang