2016 年 5 月 28 日,又拍云 Open Talk  NO.22 期活动来到北京,红点直播 CTO 王宇航在活动上作了《浅析低延迟直播协议设计:rtp/rtcp》,以下是分享实录:

我是红点的王宇航,感谢主办方给我提供这个机会跟大家交流一下协议设计方面的一些内容。

我们团队在两年前开始从事直播产品的研发工作。我们可以看到如今的直播市场非常火爆,有很多直播云服务的提供商可供产品选择。但是两年前的情况并非如此,当时市场上这样的提供商很少,我们也因此抓住这一商机,并有幸在这方面做了一些研究。今天的内容也是这方面的内容。

image.png

现在视频直播产品有很多,比如大家耳熟能详的映客、YY,还有近期特别火爆的一直播。它们在不同场景下可能有不同的直播技术选择,从图 1 中,我们可以看到左边的图片是微信群视频会议,这里面是四个参与方,这四个参与方互相之间可以实现实时通讯。右边的图片是 ME ,是 YY 旗下一个产品,我截的这个图展示的是里面联线功能。主播在表演的同时,可以在现场接入一个场外观众跟他一起做实时互动,同时呈现给其他观众。

我们可以看到,图 1 中展示的情况实际上不完全是一个一对多的直播,还包含了一个双向的直播,换而言之我们可以把它称之为通讯,在专业领域我们更多地把这个场景称之为会议的场景。我们认为其实是这两个人在开会,同时也让其他的观众能够看到他们开会的过程。

类似的会议场景可能还有一个我没在图里列出来,这种模式出现在我近期注意到的一个国外网站上。这个网站有一个功能让我特别惊奇:场外的观众可以通过打赏或送礼的形式,改变主播画面内物理世界的东西。比如说主播物理世界有一个玩具,我的打赏可以让这个小鸭子动一动。实际上观众这里的要求是希望观众的实时动作能够反映在一个画面里,这种场景对延迟的要求非常高。可能 1 秒的延迟是我们忍耐的上限。

大家都知道通用 CDN 大部分提供的是 RTMP 的方案以及 HLS 的方案。HLS 在手机 H5 里面的兼容性非常好,而 RTMP 是 Adobe 的协议,它在延迟、稳定性和分发质量方面平衡得很不错。但是当我们涉及会议场景时,基于 TCP 的协议就不能完全满足我们的要求了。

image.png

假设在没有丢包的时候,我们正常假设这个传输是一个流媒体,同样数据是有时效性的。在有时效性的情况下,我们可能在单位时间内产生的数据有一个固定的量。固定量的数据在 TCP 协议站上有什么问题?TCP 协议设计的目标是为了j更大化带宽的利用率。它会在传输的过程中衡量信道的宽度,认为它所要达到的效果应该是使我们尽可能的高效使用网络。那么丢包的时候,协议栈做出非常严厉的惩罚,它会降低它认为信道的宽度,它认为我已经更大化的利用了这个网络,会认为如果有丢包是我发多了,或者是网络出现了拥塞。我通过我发送数据的数量来减缓拥塞情况,让它再回归正常水平。比如说丢下一个数据,TCP 做了一次惩罚,使后面的数据只能向后推,这个时间就是延迟的起点。(如图 2 所示)

由此我们就能知道对丢包的处理,是网络协议对延迟影响大的一个因素。可能有的协议或者有一些网络对丢包不在乎,有一个包能够到达目标地点就足够了,但并不是所有的协议都能这样。

image.png

RTP 协议这个名字是 Real-Time  Protocol。这个名字涵盖所有实时相关的东西。我们看看这个协议支持的特性,它声称支持数据端到端传输,支持我们能定义数据类型,能给数据打上序号,时间戳还有分发监控。它不支持及时发送,假如让协议给你发送一个数据,它不保证什么时候给你发。你发 10 个,它真的可能只有一个能到。它也不保证送达,也没有时序,我发的顺序是 12345 ,收到的顺序可以是 54321 。这个协议听上去几乎不能用。但是这样一个没支持太多东西的协议实际上也给了我们一个空间,我们有能力在这个基础上为它增加很多策略,如拥塞控制算法就可以增加包括对于时序的处理和对于质量的处理。(如图 3)

image.png

图 4 展示的是 RTP 协议的头,其实很简单,左上第一行是版本号,表示的是协议标准。第二行代表协议后面有没有追加空白,然后 X 表示这个头是不是有扩展。CC 是一个数量,M  和 PT 其中有 8 个比特,表示数据类型,可以自定义。然后是一个序号,一个时间戳。这个里面的 SSRC 和 CSRC 有什么区别?SSRC 是同步源的标识。它支持转发和混合器的结构,混合器结构的功能是我可以把参与会议的多媒体混成一路,再转给其它的听众。

image.png

图 5 是 RTP 组织网络的一个样例,有三个角色:一个是终端,我们理解为每一个参与者,这个参与者可以是参与会议的人,我既可以说话,我也可以听到其他与会者的内容;第二个是混合器,这个混合器是刚才说的可以把多路的多媒体的数据混成一路,混合器直观的体现就是在音频领域,我们可以把多人说的话混成一路,首先它的带宽减少了,同时时序自然对上,保持一致;第三个角色是一个转发器,理解起来也比较简单,就是把以上这些流转出去。

这里我们能够看到比如说 E1 和 E2,它们经过第一路混合器之后,转出来其实就是刚才说的 SSRC 值,它的值变了。但是原来比如说 E1:17,CSRC 会体现在后面。通过这样的网络,能够组建一个支持会议场景的内容分发,尤其是流媒体的实时传输。

刚刚介绍是 RTP,它正如表现的那样特别简单粗暴,几乎不提供什么支持。但是配合着它,我们这个组织还额外设置了一个协议,就是 RTCP,主要作用为了弥补 RTP 的不足,或者是 RTP 没有保证的东西,RTCP 进行额外的补充。

image.png

RTCP 有 5 个类型数据包:发送方报告,接收方报告,源描述,结束,远程调用方法。在发送方报告中,重要的是什么呢?发送者真正关心是发了多少数据,发的数据丢了多少,可能还包括发的数据到底过了多长时间我才能够收到,以及网络过程中的抖动。这里面我们看到它包含了好几个时间戳,包含好几个包的数量。它也列出了到底是哪一路流的发送方报告,还有丢失情况。(如图 6 所示)

image.png

图 7 展示的是接收方的报告,接收方的报告比较明确。它主要反应发送方数据质量的信息,RTP 里面是有序号的,可以体现多少序号断的、没有收到。这里有一个抖动概念和  RTT 的概念,抖动概念指的是发送方发了两个数据, A 和 B ,第一秒发 A ,第二秒发 B 。对于接收方来说,比如接收方第三秒收到 A ,不一定第四秒收到 B ,可能第五秒才收到 B 。发送方隔 1 秒发送数据,但是接收方那边差 2 秒,这 2 秒和 1 秒就是抖动。通过上面的事例,我们可以看到时延有不确定性,有时候多,有时候少。这在我们日常生活中感受很明显,比如我们用迅雷下载东西,有时候跑好几兆,有时候基本走不动。图 8 的公式是对抖动的平滑处理。

image.png

image.png

图 9 展示的是另外一个指标——RTT,RRT (Round-Trip  Time) 这个指标描述的是一个数据包从发送方发送到接收者,接收者给出一个反馈,这个反馈再回到发送方,这时候发送方识别到的时间差就是往返时间。这里计算用到的量包括 DLSR 和 LSR,DLSR 是距离上一个发送报告的时间。接收者可以使用 DLSR,帮助发送者利用返回之后的报告算出来往返时间。RTT 更像我们工程师日常使用网络,经常的 Ping 一下,跟它很接近的。

image.png

流媒体丢包一般有以下 3 种处理方案(图 10),会议场景可能会同时使用这 3 个策略。第一个策略是重传,很明确,丢了什么数据重新传什么数据,这个很有效。你真的丢了我才传给你,不会有什么浪费。第二个策略是前向纠错,分成两个类型,一个部分是多媒体无关的前向纠错,一部分是多媒体相关的。其中多媒体无关的前向纠错是我们今天主要介绍的,它更多会用在网络上,同时这个技术在存储领域也有应用。 一个策略是交叉传输,我们日常看到多媒体可能是按照时序的,一个多媒体片断是由 1 到 10 组成的。我们传输也是 1 到 10。如果这个过程当中有丢包,比如说 3456 连续丢失丢。这次丢包的影响可能表现在视频播放出现停顿。我们处理过连续丢包的视频的朋友可能知道,丢的如果是关键帧影响非常大,导致后面一大片的花屏。所以当连续丢包对流媒体伤害特别大的情况下,我们采用交叉传输策略。1 到 10,原来是 3 个 3 个传,123 传一次,456 传一次,789  传一次,我们可以改变传输策略,采用 147、 280 和 369 的传输策略,这样一组数据丢掉,实际丢失在流媒体中间穿插的数据,播放程序可以在几乎不失真的状态下把视频恢复出来。

image.png

然后我想重点强调一下前向纠错的策略(图 11)。所谓前向纠错,其实是数据冗余。它是解决丢包问题的主要方案。我们观看实时场景时,正常情况下如果出现丢包,比如在刚才我们这种策略里面,有一个是重传,重传的前提是什么呢?如果发送方想知道这个东西是否需要重传,需要依赖往返时间。重传非常依赖这个值,RTT 比较大,重传策略很难设计,因为不知道它到底是丢了,还是收到了但是没有来得及反馈。同样的情况我们利用前向纠错的技术,可以很大程度上不依赖重传,尤其是在会议的状态下。因为它的延迟一般是在 250 毫秒的量级,这个量级下,重传的效果并不会很理想。

image.png

图 12 是一个前向纠错对于分层的方案,分层指的是数据包里面有不同重要程度的数据,对于不同程度的数据分段对它进行保护。

image.png

图 13 是前向纠错的数据包,我们能够看到它这个协议是设计在 RTP 标准上的。前面是 RTP 包头,后面就是前向纠错的数据包的格式。

image.png

图 14 介绍了两个比较通用的前向纠错的算法,第一个算法是异或。异或操作有 1 个特点,那就是假如我们有 4 个数据,那么它们可以取 4 个异或值,其中每 1 个数据都可以由另外 4 个异或计算出来。我们把ABCD和E想象成一个数据包,如果我们传输 ABCD 4 个数据包,第五个数据包传输的是E,这五个数据包可以丢失任何 1 个数据包。接收方收到数据之后,能够把它丢的数据恢复出来。前向纠错算法能处理的是连续数据里只丢1个包。同时丢失 A 和 B,这个算法不能解决,这种情况下你的 A 和 B 是真的丢了。这个其实就是一个方程,初中或高中的课程就讲过 2 个方程可以解出 2 个未知数,3 个方程可以解出 3 个未知数。如果 2 个未知数,4 个方程,其实也是能解出来的,只不过这 4 个方程当中有 2 个方程不是必需的。

image.png

因此这给了我们更多的操作空间。我们把这个参数想成数据包,里面有 5 个参数,5 个数据包。左边给它 8 个方程,8 个方程可以解出来这 5 个数据包的值,通过 8 个方程可以解出右边的一个结果。在传输数据的时候,额外的几个方程组,其实就是我们冗余的数据。也就是说我们原来的数据传的是 12345 这 5 个数据。然后我们额外传了C。假如这 8 个数据里面任意丢了三个数据 C1、C2 和 C3,程序可以利用其他内容额外把它们恢复出来,这个逻辑就是纠错过程冗余,以及为什么冗余可以在任意位置恢复出来的原因。这个算法的一个好处是可以连续的丢数据,比如网络传输的时候,传 1 到 10 这样数据,这个时候我们使用冗余度是 5,10 个数据有 5 个是冗余的,既 50% 的冗余度。这 15 个数据当中我们任意丢失 5 个数据,接收方认为这个数据包是完成的,没有丢任何数据,不需要重传,也不需要多媒体相关的纠错法。网络传输过程当中,每次发出去的数据不需要等待重传的延迟,可以把冗余数据发送出去。对于接方来讲,在带宽可以接受的情况下,延迟仍然是很低的。(如图 15 所示)

这里跟大家分享一下红点产品运营过程当中遇到的一些情况,我们都知道移动时代手机连接 WiFi,或使用 3G、4G 网络的时候,很多时候传输的数据走的是无线信道。这会遇到什么问题呢?那就是丢包不发生在骨干网,而是发生在客户手机和 WiFi 之间。比如大部分用户不知道怎么去设置路由器,大部分家庭路由器距离手机位置也很远,网络质量非常差,经常发生丢包。实际上它的带宽够了,但是网络质量不太好,在这种情况下,你通过冗余策略可以很好的提高它播放的质量。

image.png

给大家介绍一下数据包拥塞控制协议(图 16)。前面我们说到的 RTP 协议不仅可以基于 UDP 协议,也可以基于 TCP 协议。只是大部分利用RTP协议的场景实际上是基于UDP 协议的。DCCP 是一个拥塞控制的策略,里面包含 4 项内容:首先是建立会话,像 TCP 的握手;第二是数据窗口,可能很多时候要处理一个数据序号的顺序和整合一些数据碎片;第三是反馈,ACK 就是关于数据到达反馈;last是拥塞控制。

其中数据窗口、反馈还有拥塞控制是影响传输质量的关键,其中我们都知道关于 TCP 协议相关内容,它有很严格的拥塞控制措施,使用很大的带宽下 TCP 传输超延迟内容不是很友好。DCCP 则给我们一个方案,让我们自己控制拥塞控制,传输延迟和数据质量,对此我们可以有一个很强的掌控力。这在 Ack 里面,一般会有两个策略:一种是直接告诉发送方我到底哪个数据没有收到;还有一种是有一个选择性的来直接告诉发送方收到的数据片断到底是一个什么样的碎片状态,让发送方可以根据自己的情况,有选择地重发一些数据,避免一些不必要的负担。

数据窗口这块跟数据的时效性关系很大,其实我们用 TCP 协议的时候可能大部分数据长度跟时间关系不是那么强。但是会议场景对时效性要求较高。数据窗口里面时间很难超过 1 秒,1 秒以后的数据其实就已经不再有任何用处了。

建立会话这部分,在这个标准里面之只有建议性的方案,就不多做介绍了。

以上就是我今天想要跟大家分享的内容,我分享这些内容可以在图 17 展示的参考文献中查到。

image.png