WebRTC | 网络传输协议 RTP 和 RTCP

news2024/12/23 6:03:49

WebRTC | 网络传输协议 RTP 和 RTCP

  • WebRTC | 网络传输协议 RTP 和 RTCP
    • 如何选择 TCP 与 UDP
    • RTP
      • 概述
      • 工作机制
      • 报文结构
      • RTP 的使用
      • RTP 拓展头
      • RTP 中的填充数据
      • 翻译器和混合器
      • 同步控制
      • 报文大小
      • wireshark 抓取 RTP 报文
    • RTCP
      • 概述
      • 工作机制
      • 分组类型
      • 报文结构
      • WebRTC 的反馈报文
        • RTPFB
        • PSFB
      • wireshark 抓取 RTCP 报文
    • RTP 会话过程
    • RTSP 和 RTP 的关系
    • jrtplib
    • 总结
    • 参考

WebRTC | 网络传输协议 RTP 和 RTCP

流媒体协议栈如下图所示:

在这里插入图片描述

RTP 标准定义了两个子协议,RTP 和 RTCP。

  • 数据传输协议 RTP,用于实时传输数据。该协议提供的信息包括:时间戳(用于同步)、序列号(用于丢包和重排序检测)、以及负载格式(用于说明数据的编码格式)。
  • 控制协议 RTCP,用于 QoS 反馈和同步媒体流。相对于 RTP 来说,RTCP 所占的带宽非常小,通常只有5%。

在这里插入图片描述

在这里插入图片描述

如何选择 TCP 与 UDP

TCP为了实现数据传输的可靠性,采用的是“发送→确认→丢包→重传”这样一套机制。而且为了增加网络的吞吐量,还采用了延迟确认和Nagle算法(Nagle算法,将多个小包组成一个大包发送,组合包的大小不超过网络最大传输单元)。这套机制就是TCP产生延迟的根本原因。

为了增加网络的吞吐量,接收端不必每收到一个包就确认一次,而是对一段时间内收到的所有数据集体确认一次即可。为了实现该功能,TCP通常会在接收端启动一个定时器。定时器的时间间隔一般设置为200ms,即每隔200ms确认一次接收到的数据。这就是延迟确认机制。除此之外,TCP在发送端也启动了一个定时器,不过该定时器的功能不是发送确认消息,而是用来判别是否有丢包的情况。发送端定时器的时长为一个RTO(Retransmission Timeout,重传超时时长。其值约等于RTT的平均值,每次超时后以指数级增长。RTT表示一个数据包从发送端到接收端,然后再回到发送端所用的时长)。如果在定时器超时后仍然没有收到包的确认消息,则认为包丢失了,需要发送端重发丢失的包。这就是TCP的丢包重传机制。

UDP属于不可靠传输协议。在传输数据时,它不保证数据能可靠到达,也不保证数据有序,但它最大的优点就是传输速度“快”。由于UDP没有TCP那一套保证数据可靠、有序的控制逻辑,所以它不会被“人为”地变慢,因此它的实时性是最高的。

针对UDP丢包和抖动的问题,WebRTC给出了一套比较完美的解决方案,通过NACK、FEC、Jitter Bufer以及NetEQ技术既可以解决丢包和抖动问题,又不会产生影响服务质量的时延。通过上面的分析可以知道,由于TCP在极端网络情况下无法控制传输的时延大小,所以在做实时通信传输时,应该首选UDP。

RTP

概述

实时传输协议(Real-time Transport Protocol,简称 RTP)是一个网络传输协议,它是由IETF的多媒体传输工作小组1996年在RFC 1889中公布的。

RTP 是应用层协议,基于 UDP,分组传输,接收端数据包往往有延迟和乱序。在发送端,存在丢包;为降低延迟,往往对传输数据进行预处理(降低质量和高效压缩)。在接收端为了恢复时序,采用了接收缓冲;而为了实现媒体的流畅播放,则采用了播放缓冲。使用接收缓冲,可以将接收到的数据包缓存起来,然后根据数据包的封装信息(如包序号和时戳等),将乱序的包重新排序,最后将重新排序了的数据包放入播放缓冲播放。

RTP 为数据提供了具有实时特征的端对端传送服务,如在组播或单播网络服务下的交互式视频音频或模拟数据。RTP 不像 HTTP 和 FTP 可完整的下载整个影视文件,它是以固定的数据率在网络上发送数据,客户端也是按照这种速度观看影视文件,当影视画面播放过后,就不可以再重复播放,除非重新向服务器端要求数据。

RTP 本身只保证实时数据的传输,并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 RTCP 提供这些服务。RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置。

工作机制

RTP 协议就是提供了时间标签,序列号以及其它的结构用于控制适时数据的流放。在流的概念中,时间标签是最重要的信息。发送端依照即时的采样在数据包里隐蔽的设置了时间标签。在接受端收到数据包后,就依照时间标签按照正确的速率恢复成原始的适时的数据。不同的媒体格式调时属性是不一样的。

但是 RTP 本身并不负责同步,不提供任何机制来保证实时地传输数据,不支持资源预留,也不保证服务质量。RTP 报文甚至不包括长度和报文边界的描述。同时 RTP 协议和 RTCP 协议使用相邻的不同端口,这样大大提高了协议的灵活性和处理的简单性。

RTP 协议基于 UDP 实现。UDP 协议只是传输数据包,不管数据包传输的时间顺序。RTP 的协议数据单元是用 UDP 分组来承载的。在承载 RTP 数据包的时候,有时候一帧数据被分割成几个包具有相同的时间标签,则可以知道时间标签并不是必须的。而 UDP 的多路复用让 RTP 协议利用支持显式的多点投递,可以满足多媒体会话的需求。

报文结构

每一个 RTP 数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。

在这里插入图片描述

RTP报头格式为:

  • 版本号(V):2 比特,用来标志使用的 RTP 版本。现在使用的都是第 2 个版本,所以该字段固定为 2。
  • 填充位(P):1 比特,如果 P=1,则该 RTP 包的尾部就附加一个或多个额外的八位组,它们不是有效载荷的一部分。
  • 扩展位(X):1 比特,如果 X=1,拓展头部会放在 CSRC 之后,携带一些附加信息。
  • CC:4 比特,CSRC 计数器,指示 CSRC 标识符的个数。
  • 标记位(M):1 比特,该位的解释由配置文档(Profile)决定,一般情况下用于标识边界(对于视频,标记一帧的结束;对于音频,标记会话的开始)。
  • 载荷类型(PT):7 比特,标识了 RTP 载荷的类型。具体值和对应参见:RFC3551。
  • 序列号(Sequence Number):16 比特,发送方在每发送完一个 RTP 包后就将该域的值增加 1,接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。序列号的初始值是随机的。
  • 时间戳(Timestamp):32 比特,时间戳反映了该 RTP 报文数据的第一个八位组的采样时刻。在一次会话开始时,时间戳初始化成一个初始值。即使在没有信号发送时,时间戳的数值也要随时间而不断地增加。接收者使用时间戳来计算延迟和延迟抖动,并进行同步控制。
  • 同步源标识符(SSRC):32 比特,同步源就是指 RTP 包流的来源。RTP 要求不同源的数据流之间用 SSRC 字段区分,在同一个 RTP 会话中不能有两个相同的 SSRC 值。该标识符由 MD5 随机算法生成。
  • 贡献源列表(CSRC List):0~15 项,每项 32 比特,用来标志对一个 RTP 混合器产生的新包有贡献的所有 RTP 包的源。由混合器将这些有贡献的 SSRC 标识符插入表中。SSRC 标识符都被列出来,以便接收端能正确指出交谈双方的身份。

RTP 的使用

关于RTP的使用主要包括以下两个方面:一是创建/解析RTP包;二是根据RTP包进行逻辑处理。

WebRTC 提供了一个 RTP 处理类:RtpPacket,定义一个该类的对象,就可以设置/提取 RTP 协议头各个字段的内容。

接下来讲一下逻辑处理,比如创建一个接收队列来消除包抖动。某个包找不到有两种情况:包丢失或者包乱序。如果缓冲队列满了,说明包丢失;否则,该包还处于待定状态。

当接收到一个包的标记位(M)为 1,可以与前面的 RTP 包组成一个完整的帧。将这些包从接收队列弹出,交给组帧模块处理。

WebRTC中解决RTP包抖动的缓冲队列就是我们通常所说的JitterBuffer。上面的例子就是JitterBuffer的基本原理。

RTP 拓展头

RTP 头部的扩展位(X)占 1 比特,如果 X=1,拓展头部会放在 CSRC 之后,携带一些附加信息。

在这里插入图片描述

RTP扩展头由三部分组成,分别为profile、length以及header extension。

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      defined by profile       |           length              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        header extension                       |
   |                             ....                              |

其中:

  • profile字段占2字节,用于区分不同的配置。在RFC5285中定义了两种profile,分别是 {0xBE,0xDE} 和 {0x10,0x0X}。接收端解析RTP扩展头时,通过profile来区分header extension中的内容该如何解析。
  • length字段占2字节,表示扩展头所携带的header extension的个数。如果length为4,表示有4个header extension;header extension字段是扩展头信息,以4字节为单位,其具体含义由profile决定。

扩展头中的两个profile值 {0xBE,0xDE} 和 {0x10,0x0X} 分别代表存放在header extension中的两种不同的数据格式,即one-byte-header和two-byte-header。

one-byte-header 的含义是单个header extension由1字节的Header和N字节的Body组成。而Header由前4位的ID和后4位的len组成。

  • ID:拓展头的值,见于 《WebRTC 中的 SDP》 中 [网络描述:RTP 拓展头] 一章。
  • len:跟在Header后面的数据(以字节为单位)长度减 1。

one-byte-header 示例:

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       0xBE    |    0xDE       |           length=3           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  ID   | L=0   |     data      |  ID   |  L=1  |   data...
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            ...data   |  ID   |  L=3  |              data             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |              data             |     0(pad)    |     0(pad)    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

RTP拓展头后的第一个16为固定为0XBEDE标志,意味着这是一个one-byte扩展,length = 3 说明后面有三个扩展头,每个扩展头首先以一个byte开始,前4位是这个扩展头的ID, 后四位是data的长度-1,譬如说L=0意味着后面有1个byte的data,同理第二个扩展头的L=1说明后面还有2个byte的data。body结束后,为了让整个 RTP 拓展头是 4 字节的整数倍,后面填充了 2 字节的 0。

two-byte-header 的含义是单个header extension由2字节的Header和N字节的Body组成。而Header的第一个字节是ID,第二个字节 len 表示存放的实际长度。

当扩展头中的profile值为 {0x10,0x0X} 时,解析 RTP 拓展头会按照two-byte-header 的格式进行。其中的 X 占 4 bit,代表任意值由应用层自己定义它的含义。

two-byte-header 示例:

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       0x10    |    0x00       |           length=3            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |      ID       |      L=0      |     ID        |     L=1       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |      data     |       ID      |      L=4      |    data...
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                              ...data                 |     0 (pad)   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

可以看到开头为 0x100 + 0x0, 接下来的为length=3表示接下来有3个头,接下来的就是扩展头和数据,扩展头除了ID和L相对于one-byte header从4bits变成了8bits之后,其余都一样。

RTP 中的填充数据

与RTP扩展头类似,RTP头中的P位用于标识RTP包中是否有填充数据。如果P位为1,说明RTP包中含有填充数据。

当RTP包中包含有填充数据时,其数据包的最后一个字节记录着包中填充字节的个数,即图中的Padding Size部分。如果Padding Size为5,说明RTP包中共有5个填充字节,其中包括它自己。这些填充数据不属于RTP Payload的部分,因此在解析RTP Payload部分之前,应将填充部分去掉。

去除填充部分的算法:读取 RTP 包的最后一字节,即 Padding Size。从最后一个字节算起,将 RTP 包末尾 Padding Size 个字节丢弃。

翻译器和混合器

翻译器和混合器都是 RTP 协议的中继系统。翻译器用在通过IP多播不能直接到达的用户区,例如发送者和接收者之间存在防火墙。当与会者能接收的音频编码格式不一样,比如有一个与会者通过一条低速链路接入到高速会议,这时就要使用混合器。在进入音频数据格式需要变化的网络前,混合器将来自一个源或多个源的音频包进行重构,并把重构后的多个音频合并,采用另一种音频编码进行编码后,再转发这个新的RTP包。从一个混合器出来的所有数据包要用混合器作为它们的同步源(SSRC,见RTP的封装)来识别,可以通过贡献源列表(CSRC表,见RTP的封装)可以确认谈话者。

同步控制

在这里插入图片描述

由上图可知,如果只有序列号,并不能完整按照顺序的将 data 播放出来,因为如果 data 中间有一段是没有资料的,只有序列号的话会造成错误,需搭配上让它知道在哪个时间将 data 正确播放出来,如此我们才能播放出正确无误的信息。

报文大小

RTP 载荷封装设计本文的网络传输是基于 IP 协议,所以最大传输单元(MTU)最大为 1500 字节,在使用 IP/UDP/RTP 的协议层次结构的时候,这其中包括至少 20 字节的 IP 头,8 字节的 UDP 头,以及 12 字节的 RTP 头。这样,头信息至少要占用 40 个字节,那么 RTP 载荷的最大尺寸为 1460 字节。如果一帧数据大于 1460 字节,则需要分片打包,然后到接收端再拆包,组合成一帧数据,进行解码播放。

wireshark 抓取 RTP 报文

抓取方法参考:《RTSP协议抓包及讲解》。

我们可以参考上面的 RTP 协议的报文结构对下面一条 RTP 报文进行分析:

在这里插入图片描述

RTCP

概述

实时传输控制协议(Real-time Transport Control Protocol,RTCP)是 RTP 的一个姐妹协议,它是应用层协议,基于 UDP 实现。

RTCP 的主要功能是:服务质量的监视与反馈、媒体间的同步,以及多播组中成员的标识。

工作机制

当应用程序开始一个 RTP 会话时将使用两个端口:一个给 RTP,一个给 RTCP。RTP 本身并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 RTCP 提供这些服务。

RTP和RTCP使用UDP端口1024 - 65535。RTP 使用偶数端口号接收发送数据,相应的RTCP则使用相邻的下一位奇数端口号。

RTCP 负责管理传输质量在当前应用进程之间交换控制信息。

在 RTP 会话期间,各参与者周期性地传送 RTCP 包,其中含有已发送的数据包的数量、丢失的数据包的数量等统计资料。因此,各参与者可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。

RTP 和 RTCP 配合使用,能以有效的反馈和最小的开销使传输效率最佳化,故特别适合传送网上的实时数据。根据用户间的数据传输反馈信息,可以制定流量控制的策略,而会话用户信息的交互,可以制定会话控制的策略。

RTCP 封装的仅仅是一些控制信息,因而分组很短,所以可以将多个RTCP分组封装在一个UDP包中。

分组类型

在 RTCP 通信控制中,RTCP 协议的功能是通过不同的 RTCP 数据报来实现的,主要有如下几种类型:

  • SR:发送端报告,所谓发送端是指发出 RTP 数据报的应用程序或者终端,发送端同时也可以是接收端。
  • RR:接收端报告,所谓接收端是指仅接收但不发送 RTP 数据报的应用程序或者终端。
  • SDES:源描述,主要功能是作为会话成员有关标识信息的载体,如用户名、邮件地址、电话号码等,此外还具有向会话成员传达会话控制信息的功能。其中有价值的是 CNAME,其作用是将不同的源(SSEC)绑定到同一个 CNAME 上。当重传流时,SSRC 会冲突,可以通过 CNAME 将旧的 SSRC 更换成新的 SSRC,从而保证通信的每一个 SSRC 都是唯一的。
  • BYE:通知离开,主要功能是指示某一个或者几个源不再有效,即通知会话中的其他成员自己将退出会话。当 WebRTC 收到该报文时,就会删除该 SSRC 对应的通道。
  • APP:由应用程序自己定义,解决了 RTCP 的扩展性问题,并且为协议的实现者提供了很大的灵活性。
  • RTPFB:RTP 反馈报文,是指 RTP 传输层面的报文。该报文可以装入不同类型的子报文。
  • PSFB:RTP 中与负载相关的反馈报文。该报文可以装入不同类型的子报文。

报文结构

每一个 RTCP 数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 1 个字节的含义是固定的。

在这里插入图片描述

RTCP 报头格式为:

  • 版本号(V):2 比特,用来标志使用的 RTCP 版本。现在使用的都是第 2 个版本,所以该字段固定为 2。
  • 填充位(P):1 比特,填充位标识。如果 P=1,则该 RTCP 包的尾部就附加一个或多个额外的八位组,它们不是有效载荷的一部分。
  • 接收报告计数器(RC):5 比特,其含义取决于报文类型。对于 RR/SR,它表示所携带接收报告的个数;对于 SDES,它表示报文中 item 的个数;对于 BYE,它表示 SSRC/CSRC 的个数。
  • 包类型(PT):8 比特,标识 RTCP 的分组类型。在这里插入图片描述
  • 长度域(Length):16 比特,表示整个 RTCP 包的大小(包括 RTCP 头、负载、填充字节),以 4 字节为单位。
  • Data:负载。不同类型的 RTCP 报文其负载的内容千差万别,相关内容请自行查阅 RFC3550。

WebRTC 的反馈报文

RTPFB

PT = 205 表示 RTPFB 报文,是 RTP 传输层面的反馈报文。该报文可以装入不同类型的子报文。该报文中可以包含多个子报文,其中WebRTC使用到的报文只有4项。

在这里插入图片描述

  • NACK:接收端用于通知发送方在上次包发送周期内有哪些包丢失了。在NACK报文中包含两个字段:PID和BLP。PID(Package ID)字段用于标识从哪个包开始统计丢包;而BLP(16位)字段表示从PID包开始,接下来的16个RTP包的丢失情况。
  • TMMBR和TMMBN是一对报文,TMMBR表示临时最大码流请求报文,TMMBN是对临时最大码流请求的应答报文。这两个报文虽然在WebRTC中实现了,但已被WebRTC废弃,其功能由TFB和REMB报文所代替。
  • TFB是WebRTC中TCC算法的反馈报文,该报文会记录包的延迟情况,然后交由发送端的TCC算法计算下行带宽。
PSFB

PT = 206 表示 PSFB 报文,是 RTP 中与负载相关的反馈报文。该报文可以装入不同类型的子报文。

在这里插入图片描述

PLI报文与FIR报文很类似,当发送端收到这两个报文时,都会触发生成关键帧(IDR帧),但两者还是有一些区别的。PLI报文是在接收端解码器无法解码时发送的报文。FIR报文主要应用于多方通信时后加入房间的参与者向已加入房间的共享者申请关键帧。通过这种方式,可以保障后加入房间的参与者不会因收到的第一帧不是关键帧而引起花屏或黑屏的问题。

REMB报文是WebRTC增加的反馈报文,用于将接收端评估出的带宽值发给发送端。不过,由于最新的WebRTC已全面启用基于发送端的带宽估算方法,即TCC,因此目前REMB仅用于向后兼容,不再做进一步更新。

wireshark 抓取 RTCP 报文

我们可以参考上面的 RTCP 数据报对下面一包 RTP 报文进行分析:

在这里插入图片描述

设置过滤 RTCP 报文,可以看到下面第 397、401 报文为接收端报告+源描述,第 473、475 包报文为发送端报告 + 源描述报文:

在这里插入图片描述

RTP 会话过程

当应用程序建立一个RTP会话时,应用程序将确定一对目的传输地址。目的传输地址由一个网络地址和一对端口组成,有两个端口:一个给RTP,一个给RTCP,使得RTP/RTCP数据能够正确发送。RTP和RTCP使用UDP端口(范围 1024 - 65535)。RTP 使用偶数端口号接收发送数据,相应的RTCP则使用相邻的下一位奇数端口号,这样就构成一个UDP端口对。

发送过程如下:

  1. RTP 协议从接收流媒体信息码流(如H.264),封装成RTP数据包;RTCP接收控制信息,封装成 RTCP 控制包。
  2. RTP 将 RTP 数据包发往UDP端口对中偶数端口;RTCP 将 RTCP 控制包发往UDP端口对中的奇数端口。

接收者:

  1. 根据RTP包的序列号来排序。
  2. 根据声音流和图像流的相对时间(即RTP包的时间戳),以及它们的绝对时间(即对应的RTCP包中的RTCP),可以实现声音和图像的同步。
  3. 接收缓冲用来排序乱序了的数据包。
  4. 播放缓冲用来消除播放的抖动,实现等时播放。

RTSP 和 RTP 的关系

RTSP 与 RTP 最大的区别在于:RTSP 是一种双向实时数据传输协议,它允许客户端向服务器端发送请求,如回放、快进、倒退等操作。当然,RTSP 可基于 RTP 来传送数据,还可以选择 TCP、UDP、组播 UDP 等通道来发送数据,具有很好的扩展性,它是一种类似于 HTTP 协议的网络应用层协议。作为一个应用层协议, RTSP 提供了一个可供扩展的框架,它的意义在于使得实时流媒体数据的受控和点播变得可能。

jrtplib

jrtplib 是实现 RTP 的 C++ 库。

初始化:

RTPSession sess;

sess.Create(5000);

// 3.11版jrtplib库的Create方法被修改为Create(sessparams,&transparams)
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
sessparams.SetOwnTimestampUnit(1.0 / 8000.0); /*设置时间戳,1/8000表示1秒钟采样8000次,即录音时的8KHz*/
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase); /*本地通讯端口*/

数据发送:

// RTP 协议允许同一会话存在多个目标地址

unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);

sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);

sess.SendPacket((void *)buffer, sizeof(buffer), 0, false, 8000);

数据接收:

sess_client.Poll(); // 接收发送过来的 RTP 或者RTCP 数据报

sess_client.BeginDataAccess();

if (sess.GotoFirstSourceWithData())
{ // 遍历那些携带有数据的源
	do
	{
		sess.AddToAcceptList(remoteIP, allports, portbase);
		sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);

		RTPPacket *pack;
		pack = sess.GetNextPacket(); // 处理接收到的数据
		delete pack;
	} while (sess.GotoNextSourceWithData());
}

sess_client.EndDataAccess();

总结

RTP是一个非常轻量的传输协议,特别适合传输音视频数据,或者说它就是专门为传输音视频数据而开发的。RTP控制协议RTCP对于传输服务质量起着关键的作用,WebRTC的服务质量系统中的大量控制参数都是通过RTCP获取的。

参考

  1. https://blog.csdn.net/weixin_39766005/article/details/132301075
  2. https://baike.baidu.com/item/%E5%AE%9E%E6%97%B6%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE
  3. https://baike.baidu.com/item/rtcp
  4. https://www.rfc-editor.org/rfc/rfc3551.html
  5. https://blog.csdn.net/leixiaohua1020/article/details/50535230
  6. https://blog.csdn.net/wangbuji/article/details/130781275
  7. https://blog.csdn.net/wangbuji/article/details/121285741
  8. https://blog.csdn.net/qq_41839588/article/details/133465671
  9. https://www.cnblogs.com/ishen/p/12050077.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1690435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口响应断言

目录 接口断言介绍接口断言方式介绍响应状态码断言 课程目标 掌握什么是接口断言。了解接口断言的多种方式。掌握如何对响应状态码完成断言。 思考 这两段代码是完整的接口自动化测试代码吗? …省略… when().get(“https://httpbin.ceshiren.com/get?namead&…

白鹭群优化算法,原理详解,MATLAB代码免费获取

白鹭群优化算法(Egret Swarm Optimization Algorithm,ESOA)是一种受自然启发的群智能优化算法。该算法从白鹭和白鹭的捕食行为出发,由三个主要部分组成:坐等策略、主动策略和判别条件。将ESOA算法与粒子群算法(PSO)、遗传算法(GA)…

5.24学习记录

[FSCTF 2023]ez_php2 比较简单的pop链 <?php highlight_file(__file__); Class Rd{public $ending;public $cl;public $poc;public function __destruct(){echo "All matters have concluded";die($this->ending);}public function __call($name, $arg){for…

揭秘Python的魔法:装饰器的超能力大揭秘 ‍♂️✨

文章目录 Python进阶之装饰器详解1. 引言装饰器的概念与意义装饰器在Python编程中的作用 2. 背景介绍2.1 函数作为对象2.2 高阶函数 3. 装饰器基础3.1 理解装饰器3.2 装饰器的工作原理 4. 带参数的装饰器4.1 为什么需要带参数4.2 实现带参数的装饰器使用函数包裹装饰器使用类实…

【ZYNQ】AXI-Quad-SPI SDK 开发记录 测试

前人工作 如前人工作&#xff0c;在Navigate to BSP Settings中找到历例程 file:///F:/Xilinx/Vitis/2019.2/data/embeddedsw/XilinxProcessorIPLib/drivers/spi_v4_5/doc/html/api/example.html使用XSpi_LowLevelExample例子&#xff0c;源代码的AI解析 int XSpi_LowLeve…

蓝桥杯Web开发【模拟题一】15届

1.动态的Tab栏 日常在使用移动端 APP 或访问 PC 端网站的时候&#xff0c;常常发现在一些有工具栏或者 Tab 栏的页面会有顶栏固定的效果。简单来说&#xff0c;在页面未开始滚动时顶栏处在其原有的位置上&#xff0c;当页面向下滚动一定区域后&#xff0c;顶栏会跟随滚动固定在…

python-数据分析与可视化基础

1、data1.csv中的B、C、D和E列数据分别是日期、权重、A企业的销售额、B企业的销售额。读取C、D、E列数据,并统计E列数据的算术平均数、加权平均值(权值为C列数据)、方差、中位数、最小值、最大值。并绘制E列数据的直方图。 &#xff08;1&#xff09;源代码&#xff1a; impo…

vue3的api风格

Vue的组件有两种不同的风格&#xff1a;组合式API 和 选项式API 选项式api 选项式API&#xff0c;可以用包含多个选项的对象来描述组件的逻辑&#xff0c;如&#xff1a;data&#xff0c;methods&#xff0c;mounted等。 组合式api setup&#xff1a;是一个标识&#xff0c;告…

ST-SLAS Technology 实验室自动化与筛查学会技术

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、出版支持 一、期刊简介 SLAS Technology ——SLAS技术强调促进和改进生命科学研发的科学和技术进步;药物递送;诊断;生物医学和分子成像&#xff1b;以及个性化和精准医疗。这包括高通量和其他实验室自动化技术;…

Springboot项目打包:将依赖的jar包输出到指定目录

场景 公司要对springboot项目依赖的jar包进行升级&#xff0c;但是遇到一个问题&#xff0c;项目打包之后&#xff0c;没办法看到他里面依赖的jar包&#xff0c;版本到底是不是升上去了&#xff0c;没办法看到。 下面是项目打的jar包 我们通过反编译工具jdgui&#xff0c;来…

云计算架构最全方案详解

云计算架构最全详解(图文全面总结) 一、引言云计算已经成为现代企业和科技发展的重要支柱。本文将详细介绍云计算架构的组成部分及其工作原理&#xff0c;帮助读者深入理解这一复杂而强大的系统。二、云计算架构组成部分云计算架构主要包括以下几个关键组件&#xff1a;基础设施…

【软件工程】【23.10】p3

关键字&#xff1a; 软件工程定义及目的、需求规约定义及性质、模块的控制域及作用域、类和类图、调试特征、瀑布模型

LINUX系统编程:命名管道

匿名管道的通信只能在&#xff0c;有血缘关系的进程中&#xff0c;本质就是&#xff0c;子进程会拷贝一份父进程的文件描述符表&#xff0c;父子进程就可以看到操作系统的同一块资源&#xff08;文件&#xff09;&#xff0c;以这块资源为媒介进行通信。 命名管道&#xff0c;…

shell文本三剑客——awk命令【☆】

目录 一、akw原理 二、命令格式 三、常用变量 四、awk的用法 1.输出整行内容 2.按字段输出文本内容 3.按列输出文件内容 FS变量为列分隔符 4.awk的三个模式 5. awk ‘控制语句条件 {操作}’ 文件 6.awk的数组 7.awk的应用 一、akw原理 逐行读取文本&#xff0c;默认…

SpringFramework实战指南

二、SpringFramework实战指南 目录 一、技术体系结构 1.1 总体技术体系1.2 框架概念和理解 二、SpringFramework介绍 2.1 Spring 和 SpringFramework概念2.2 SpringFramework主要功能模块2.3 SpringFramework 主要优势 三、Spring IoC容器和核心概念 3.1 组件和组件管理概念3…

基于灰狼优化算法优化RBF(GWO-RBF)的数据回归预测(多输入多输出)

代码原理及流程 基于灰狼优化算法优化多输入多输出&#xff08;MIMO&#xff09;的RBF神经网络的数据回归预测&#xff0c;可以采取以下步骤&#xff1a; 1. 数据准备&#xff1a;准备包含多个输入特征和多个输出目标的数据集&#xff0c;确保数据已经经过预处理和归一化。 …

探寻最强性能云电脑:ToDesk云电脑、无影云、网易云游戏、易腾云横测大比拼

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

massCode打造个人代码知识库的利器

缘起 作为一名软件工程师,我在编写代码时经常遇到这样的情况:我记得之前在某个应用程序中使用过某种语法,但我却不记得是在什么时候或哪个项目中使用的。通过浏览所有代码来找到这个语法是非常耗时的,尤其是当你不记得可以简化搜索的关键字时。此外,你可能有代码片段,但…

本地连不上远程阿里云MySQL数据库,密码对就是连不上

三步解决 设置安全组&#xff1a; 设置防火墙&#xff1a; iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT设置root用户连接host&#xff1a; 终端登录mysql&#xff0c;然后&#xff1a; use mysql; select user,host from user where use…

【数据结构】二叉树的功能实现

文章目录 关于二叉树的创建如何创建二叉树实现二叉树的前、中、后序遍历层序遍历 关于二叉树的创建 在笔者的上一篇文章中堆进行了一个详细介绍&#xff0c;而二叉树是以堆为基础进行创建&#xff0c;它与堆的显著不同是 堆像是一个线性结构&#xff0c;堆的结构往往是一个数…