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研究:Transport-cc之RTP及RTCP

2020年4月25日 12661hotness 26likes 17comments

Transport-cc指的是Transport-wide Congestion Control。WebRTC最新的拥塞控制算法(Sendside BWE)基于Transport-cc,接收端记录数据包到达时间,构造相关RTCP包,然后反馈给发送端,在发送端做带宽估计,从而进行拥塞控制。之所以基于Transport-cc,放到发送端进行带宽估计,除了方便维护,也增加了相关算法的灵活性,因为大多数处理逻辑都放到了发送端。WebRTC中为了使用Transport-cc,需要RTP报头扩展以及相关RTCP的配套支持。这里我们介绍下支撑Transport-cc的RTP以及RTCP。

RTP Header扩展

Transport sequence number

首先我们先来复习下RTP固定报头结构:

1
2
3
4
5
6
7
8
9
10
11
12
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           synchronization source (SSRC) identifier            |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
   |            contributing source (CSRC) identifiers             |
   |                             ....                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

可以看到有一个sequence number字段,用于记录RTP包的序列号。一般情况下我们一个传输通道(PeerConnection)只包含一路视频流,这个sequence number能满足大多数需求。但是在一些情况下,我们一个连接可能传输多个视频流,这些视频流复用一个传输通道,例如simulcast或者single PC场景,一个PeerConnection可能包含多个不同的视频流。在这些视频流中,RTP报头的sequence number是单独计数的。

这里举个例子,假设同一个PeerConnection下,我们传输两个视频流A与B,它们的RTP包记为Ra(n),Rb(n),n表示sequence number,这样我们观察同一个PeerConnection下,视频流按如下形式传输:
Ra(1),Ra(2),Rb(1),Rb(2),Ra(3),Ra(4),Rb(3),Rb(4)

在对某条PeerConnection进行带宽估计时,我们需要估计整条PeerConnection下所有视频流,而不是单独某个流。这样为了做一个RTP session(传输层)级别的带宽估计,原有各个流的sequence number就不能满足我们需要了。

为此Transport-cc中,使用了RTP报头扩展,用于记录transport sequence number,同一个PeerConnection连接下的所有流的transport sequence number,使用统一的计数器进行计数,方便进行同一个PeerConnection下的带宽估计。

这里我们使用前面的例子,视频流A与B,它们的RTP包记为Ra(n,m),Rb(n,m),n表示sequence number,m表示transport sequence number,这样同一个PeerConnection下,视频流按如下形式传输:
Ra(1,1),Ra(2,2),Rb(1,3),Rb(2,4),Ra(3,5),Ra(4,6),Rb(3,7),Rb(4,8)

这样进行带宽估计时,通过transport sequence number我们就能关心到这条传输通道下所有数据包的情况了。

RTP transport sequence number报头定义如下:

1
2
3
4
5
6
7
     0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       0xBE    |    0xDE       |           length=1            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  ID   | L=1   |transport-wide sequence number | zero padding  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

由于属于RTP报头扩展,所以可以看到以0xBEDE固定字段开头,表示One-Byte Header类型的扩展。

One-Byte Header相关知识请参考:WebRTC研究:RTP报头扩展

transport sequence number占两个字节,存储在One-Byte Header的Extension data字段。由于按4字节对齐,所以还有值为0的填充数据。

对于同一个PeerConnection下的每个包,这个transport sequence number是从1开始递增的。这里我们看下Wireshark中对带transport sequence numberRTP报头扩展的解析:

One-Byte Header中Extension data字段为0x0028,可知该RTP包的transport sequence number为0x0028。

代码导读

WebRTC中,要发送的数据都会经过Pacing模块,用于平滑发送处理,要发送数据会送到pacer thread,在pacer thread中的PacketRouter::SendPacket,对要发送的RTP数据包打上统一计数的TransportSequenceNumber扩展。

C++
1
2
3
4
5
6
7
8
9
10
void PacketRouter::SendPacket(std::unique_ptr<RtpPacketToSend> packet,
                              const PacedPacketInfo& cluster_info) {
  rtc::CritScope cs(&modules_crit_);
  // With the new pacer code path, transport sequence numbers are only set here,
  // on the pacer thread. Therefore we don't need atomics/synchronization.
  // 如果当前RTP包注册了TransportSequenceNumber扩展
  if (packet->HasExtension<TransportSequenceNumber>()) {
    packet->SetExtension<TransportSequenceNumber>((++transport_seq_) & 0xFFFF);
  }
}

TransportFeedback RTCP

允许接收端向发送端传递有关媒体流传输质量的信息,包括到达时间,丢包信息。

报文格式

Transport-cc中,收流客户端通过TransportFeedback RTCP向发送端反馈收到的各个RTP包的到达时间,丢包信息。首先我们看下TransportFeedback包格式定义:

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
     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |V=2|P|  FMT=15 |    PT=205     |           length              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  0 |                     SSRC of packet sender                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  4 |                      SSRC of media source                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  8 |      base sequence number     |      packet status count      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 |                 reference time                | fb pkt. count |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 |          packet chunk         |         packet chunk          |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    .                                                               .
    .                                                               .
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |         packet chunk          |  recv delta   |  recv delta   |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    .                                                               .
    .                                                               .
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |           recv delta          |  recv delta   | zero padding  |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • FMT:5bits。Feedback message type(FMT)固定为15
  • PT:8bits。由于属于传输层的Feedback Messages,所以payload type(PT)为205
  • base sequence number:2字节,TransportFeedback包中记录的第一个RTP包的transport sequence number,在反馈的各个TransportFeedback RTCP包中,这个字段不一定是递增的,也有可能比之前的RTCP包小
  • packet status count:2字节,表示这个TransportFeedback包记录了多少个RTP包信息,这些RTP的transport sequence number以base sequence number为基准
  • ,比如记录的第一个RTP包的transport sequence number为base sequence number,那么记录的第二个RTP包transport sequence number为base sequence number+1

  • reference time:3字节,表示参考时间,以64ms为单位,RTCP包记录的RTP包到达时间信息以这个reference time为基准进行计算
  • feedback packet count:1字节,用于计数发送的每个TransportFeedback包,相当于RTCP包的序列号。可用于检测TransportFeedback包的丢包情况
  • packet chunk:2字节,记录RTP包的到达状态,记录的这些RTP包transport sequence number通过base sequence number计算得到
  • recv delta: 8bits,对于"packet received"状态的包,也就是收到的RTP包,在recv delta列表中添加对应的的到达时间间隔信息,用于记录RTP包到达时间信息。通过前面的reference time以及recv delta信息,我们就可以得到RTP包到达时间

packet chunk

首先先了解下RTP包状态,目前定义了如下四种状态,每个状态值2bits,用来标识RTP包的到达状态,以及与前面RTP包的时间间隔大小信息:

  • 00-Packet not received
  • 01-Packet received, small delta
  • 10-Packet received, large or negative delta
  • 11-[Reserved]

packet chunk有两种类型,Run length chunk(行程长度编码数据块)与Status vector chunk(状态矢量编码数据块),对应packet chunk结构的两种编码方式。packet chunk的第一bit标识chunk类型。

Run length chunk

这里先来了解下Run length(行程长度)编码。Run length编码是一种简单的数据压缩算法,其基本思想是将重复且连续出现多次的字符使用“连续出现次数+字符”来描述,例如:aaabbbcdddd通过Run length编码就可以压缩为3a3bc4d。Run length chunk中就使用了Run length编码标识连续多个相同状态的包。

Run length chunk第一bit为0,后面跟着packet status以及run length。格式如下:

1
2
3
4
5
       0                   1
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |T| S |       Run Length        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

chunk type (T):1 bit,值为0
packet status symbol (S):2 bits,标识包状态
run length (L):13 bits,行程长度,标识有多少个连续包为相同状态

下面举例子说明下。

1
2
3
4
5
       0                   1
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |0|0 0|0 0 0 0 0 1 1 0 1 1 1 0 1|
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

packet status为00,由前面包状态可知为"Packet not received"状态,run lengh为221(11011101),说明连续有221个包为"Packet not received"状态。

Status Vector Chunk

第一bit为1,后面跟着symbol size以及symbol list。格式如下:

1
2
3
4
5
        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |T|S|       symbol list         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • chunk type (T):1 bit,值为1
  • symbol size(S):1 bit,为0表示只包含"packet not received" (0)以及"packet received"(1)状态,每个状态使用1bit表示,这样后面14bits的symbol list能标识14个包的状态。为1表示使用2bits来标识包状态,这样symbol list中我们只能标识7个包的状态
  • symbol list:14 bits,标识一系列包的状态, 总共能标识7或14个包的状态

下面举例子说明下。

例子1:

1
2
3
4
5
        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |1|0|0 1 1 1 1 1 0 0 0 1 1 1 0 0|
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

symbol size为0,这样能标识14个包的状态。第一个包状态为"packet not received"(0),接着后面5个包状态为"packet received"(1),再接着三个包状态为"packet not received",再接着三个包状态为"packet received",最后两个包状态为"packet not received"。

例子2:

1
2
3
4
5
        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |1|1|0 0 1 1 0 1 0 1 0 1 0 0 0 0|
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

symbol size为1,这样只能标识7个包的状态。第一个包为"packet not received"(00)状态,第二个包为 "packet received, w/o timestamp"(11)状态,再接着三个包为"packet received"(01)状态,最后两个包为"packet not received"(00)状态。

Receive Delta

以250us(0.25ms)为单位,表示RTP包到达时间与前面一个RTP包到达时间的间隔,对于记录的第一个RTP包,该包的时间间隔是相对reference time的。

  • 如果在packet chunk记录了一个"Packet received, small delta"状态的包,那么就会在receive delta列表中添加一个无符号1字节长度receive delta,无符号1字节取值范围[0,255],由于Receive Delta以0.25ms为单位,故此时Receive Delta取值范围[0, 63.75]ms
  • 如果在packet chunk记录了一个"Packet received, large or negative delta"状态的包,那么就会在receive delta列表中添加一个有符号2字节长度的receive delta,范围[-8192.0, 8191.75] ms
  • 如果时间间隔超过了最大限制,那么就会构建一个新的TransportFeedback RTCP包,由于reference time长度为3字节,所以目前的包中3字节长度能够覆盖很大范围了

以上说明总结起来就是:对于收到的RTP包在TransportFeedback RTCP receive delta列表中通过时间间隔记录到达时间,如果与前面包时间间隔小,那么使用1字节表示,否则2字节,超过最大取值范围,就另起新RTCP包了。

对于"Packet received, small delta"状态的包来说,receive delta最大值63.75ms,那么一秒时间跨度最少能标识1000/63.75~=16个包。由于receive delta为250us的倍数,所以一秒时间跨度最多能标识4000个包。

packet chunk以及receive delta的使用是为了尽可能减小RTCP包大小。packet chunk用到了不同编码方式,对于收到的RTP包才添加到达时间信息,而且是通过时间间隔的方式记录到达时间。

代码导读

在RemoteEstimatorProxy中处理RTP包的到达时间,构造Transport-cc报文,反馈给发送端。大概函数调用流程如下:

1
2
3
4
5
6
7
RemoteEstimatorProxy::IncomingPacket
             ↓
RemoteEstimatorProxy::Process
             ↓
RemoteEstimatorProxy::SendPeriodicFeedbacks
             ↓
RemoteEstimatorProxy::BuildFeedbackPacket

RemoteEstimatorProxy::IncomingPacket中,如果RTP包带有TransportSequenceNumber扩展,会记录该RTP包的到达时间,然后添加到构造的Transport-cc报文中。我们看下主要处理代码:

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
  if (header.extension.hasTransportSequenceNumber) {
    seq = unwrapper_.Unwrap(header.extension.transportSequenceNumber);
 
    if (send_periodic_feedback_) {
      if (periodic_window_start_seq_ &&
          packet_arrival_times_.lower_bound(*periodic_window_start_seq_) ==
              packet_arrival_times_.end()) {
        // Start new feedback packet, cull old packets.
        for (auto it = packet_arrival_times_.begin();
             it != packet_arrival_times_.end() && it->first < seq &&
             arrival_time_ms - it->second >= send_config_.back_window->ms();) {
          it = packet_arrival_times_.erase(it);
        }
      }
      if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
        periodic_window_start_seq_ = seq;
      }
    }
 
    // We are only interested in the first time a packet is received.
    if (packet_arrival_times_.find(seq) != packet_arrival_times_.end())
      return;
 
    packet_arrival_times_[seq] = arrival_time_ms;
  }

在RTCPReceiver中处理收到的Transport-cc报文,然后送给TransportFeedbackObserver处理。大概函数调用流程如下

1
2
3
4
5
6
7
RTCPReceiver::IncomingPacket
              ↓
RTCPReceiver::TriggerCallbacksFromRtcpPacket
              ↓
TransportFeedbackObserver::OnTransportFeedback
              ↓
RtpTransportControllerSend::OnTransportFeedback

在webrtc::rtcp::TransportFeedback中负责TransportFeedback包的解析以及构造。

总结

本文介绍了WebRTC带宽估计用到的TransportSequenceNumber RTP报头扩展以及TransportFeedback RTCP,并分析了相关代码调用,熟悉这些有助于更好理解WebRTC中的带宽估计机制。

参考

[1] RTP Extensions for Transport-wide Congestion Control draft-holmer-rmcat-transport-wide-cc-extensions-01.https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01.

本文已收录到大话WebRTC专栏,更多精彩请访问《大话WebRTC》。

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: WebRTC 网络 音视频
Last updated:2024年4月17日

Jeff

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

Tip the author Like
< Last article
Next article >

Comments

  • 于吉太

    痴乎兄写得很透彻!

    2020年4月29日
    Reply
  • Jeffrey

    兄弟请教个问题
    void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet,
    MediaType media_type) {
    auto it = receive_rtp_config_.find(packet.Ssrc());
    bool use_send_side_bwe =
    (it != receive_rtp_config_.end()) && it->second.use_send_side_bwe;
    我的这段代码的use_send_side_bwe 为啥一直返回false,明明双方的sdp有扩展twcc的
    这个为false导致如下:
    if (!use_send_side_bwe && header.extension.hasTransportSequenceNumber) {
    // Inconsistent configuration of send side BWE. Do nothing.
    // TODO(nisse): Without this check, we may produce RTCP feedback
    // packets even when not negotiated. But it would be cleaner to
    // move the check down to RTCPSender::SendFeedbackPacket, which
    // would also help the PacketRouter to select an appropriate rtp
    // module in the case that some, but not all, have RTCP feedback
    // enabled.
    return;
    }
    这样我的接收端收到twcc的包后就不能进行feedback回复了。。。不知道是啥原因

    2020年8月28日
    Reply
  • Jeffrey

    具体是在call模块,处理rtp数据的时候

    2020年8月28日
    Reply
    • Jeff

      @Jeffrey receive_rtp_config是在Call::CreateVideoReceiveStream中配置的,通过ReceiveRtpConfig(config)获取配置,通过如下判断use_send_side_bwe:
      bool UseSendSideBwe(const std::vector<RtpExtension>& extensions,
      bool transport_cc) {
      if (!transport_cc)
      return false;
      for (const auto& extension : extensions) {
      if (extension.uri == RtpExtension::kTransportSequenceNumberUri ||
      extension.uri == RtpExtension::kTransportSequenceNumberV2Uri)
      return true;
      }
      return false;
      }

      你可以看下这里哪里设置不对,再排查原因。

      2020年8月30日
      Reply
  • deng

    写得挺好,走心了。。。

    2021年1月4日
    Reply
  • deng

    你可以关注一下mediasoup,用mediasoup做服务器端转发模式更合适。
    webrtc peerconnection适合终端。

    2021年1月4日
    Reply
  • Random

    感谢分享,写得很好,学习了

    2021年5月17日
    Reply
  • Jeffrey

    再请教两个问题
    1.
    constexpr int kBaseScaleFactor =
    TransportFeedback::kDeltaScaleFactor * (1 << 8);
    constexpr int64_t kTimeWrapPeriodUs = (111 << 24) * kBaseScaleFactor;
    这个111是什么意思
    该段代码,transport_feedback.cc文件中
    2.
    packet_feedback.receive_time =
    current_offset_ + packet_offset.RoundDownTo(TimeDelta::Millis(1));
    twcc feed back的计算包的接收时间在发送端计算,这个
    current_offset_ 该值的取值是以第一次接收到的feedback包作为基准,那岂不是变成这里计算的rtp到达时间多算了rtt/2 ?
    上述代码在transport_feedback_adapter.cc的ProcessTransportFeedbackInner中

    2021年6月30日
    Reply
    • Jeff

      @Jeffrey RTP时间问题已在另一个相同问题下答复

      2021年7月6日
      Reply
  • 无知之徒

    第一个问题中111是1LL,不是三个1,这个地方主要是处理24位reference time回绕的问题

    2021年7月6日
    Reply
  • 小胖子

    请教一个问题,在tcc的包里面的base sequence numbe应该是前面码流rtp包的扩展头里面的一系列transport sequence number。但是我用wireshark抓包的时候发现两个值对不上,我可以看到rtp包里面的扩展头在不断增加,但是tcc的包反馈的base sequence numbe和码流里面transport sequence number差距很大。举个例子:序号15013的包是码流包,他的rtp扩展头里面序号是0x028db。序号15014的包是tcc的包,但是base sequence number是0x5e60。我理解tcc的这个包有点延时性,但这个差距真的是太大了。不解~~

    2022年4月1日
    Reply
  • sola

    如果 twcc 的rtcp反馈包丢了不少,发送端怎么靠这个估算接收端的码率.

    2023年8月9日
    Reply
    • Jeff

      @sola 有个RobustThroughputEstimator类可以参考,反馈包丢失,会造成两个包间隔时间变大,也就是所谓的receive time gap,RobustThroughputEstimator会尽可能消除这种影响。

      2023年9月28日
      Reply
  • 啊非

    reference time:3字节,表示参考时间,以64ms为单位,但是
    代码里面是 TransportFeedback::kDeltaScaleFactor * (1 << 8); 怎么理解?

    2024年12月30日
    Reply
  • 啊非

    大神,请教一个问题:
    constexpr int kBaseScaleFactor =
    TransportFeedback::kDeltaScaleFactor * (1 << 8);
    constexpr int64_t kTimeWrapPeriodUs = (111 << 24) * kBaseScaleFactor;

    就是这里的代码里面是 TransportFeedback::kDeltaScaleFactor * (1 << 8); 怎么理解?

    2024年12月30日
    Reply
    • 邱国禄

      @啊非 Receive Delta以0.25ms为单位,reference time以64ms为单位,kDeltaScaleFactor=0.25ms,kDeltaScaleFactor*(1<<8) = 0.25ms * 256 = 64ms。

      2025年2月17日
      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.

    文章目录
    • RTP Header扩展
      • Transport sequence number
      • 代码导读
    • TransportFeedback RTCP
      • 报文格式
      • 代码导读
    • 总结
    • 参考
    Related Posts
    • WebRTC音视频传输基础:NAT穿透
    • WebRTC研究:丢包判断
    • WebRTC研究:RTP中的序列号以及时间戳比较
    • 浅谈基于SFU实现一对一效果
    • WebRTC研究:统计参数之往返时延
    Categories

    COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang