概要
近些年来,互联网行业出现了几波和音视频相关的热潮:VR、短视频、直播等。除了VR因技术成熟度问题,还在蓄势待发,短视频和直播持续热度不减,以各种方式进入新的行业应用领域。视频直播方向,RTMP仍是最流行的上行传输协议,但RTMP的局限性也越来越凸显:
RTMP的容器格式FLV,存在不支持新的codec、不支持多音轨、时间戳精度过低等等缺陷;
RTMP基于TCP做传输,TCP的公平、可靠传输设计并不适用于实时音视频传输。
业界出现了一些新的上行传输方式:SRT、RIST、基于WebRTC的推流、基于fmp4的推流(DASH-IF Live Media Ingest Protocol)等。腾讯云支持SRT协议直播推流,客户反馈相比传统的RTMP,SRT对推流卡顿问题有明显改善[1]。本文重点介绍SRT的功能特性、适用的场景以及后续改进提升的方向,并简要介绍下RIST协议。
SRT协议继承自UDT协议,包括协议设计和代码库。UDT是基于UDP的文件传输协议,最初是针对高带宽、高延迟场景(如远距离光纤传输)设计,用于弥补TCP的不足。关于UDT协议的详细介绍见Experiences in Design and Implementation of a High Performance Transport Protocol, Yunhong Gu, Xinwei Hong, and Robert L. Grossman 2004][2]。Haivision将UDT用于流媒体传输,加入了针对流媒体传输场景的优化特性,如端到端固定延迟等,改造成了SRT协议。SRT协议标准目前还处于草稿阶段[3]。Haivision在2017年开源了libsrt项目[4],并成立SRT联盟,迄今为止已有500多成员。腾讯云是SRT联盟成员之一[5]。
两种传输模式:file文件传输模式和live直播流模式;
这里我们主要关注live模式。live模式既支持loss模式也支持lossless无损模式。
SRT作为传输协议,可以使用任意流媒体封装格式;
但要注意,loss模式要求容器格式必须有错误恢复resync机制,可选范围基本只剩下TS格式或者H.264、annexb之类的裸流。
双向传输;
这一能力使得SRT在一些场景可以替换TCP,比如用SRT做RTMP的传输层。但要注意,SRT设计上是假设双向传输的流彼此无依赖关系,而RTMP存在双向的信令交互,RTMP over SRT模式会出现一些非常隐蔽的缺陷,见后文详述。
支持ARQ和FEC两种丢包恢复机制;
支持connection bonding,目前处于实验阶段。
SRT最突出的特性是端到端固定延迟(Fixed end-to-end latency)。一般的传输协议,从一端send()到另一端receive()所占用的时间是波动的,SRT抹平了网络的抖动,可以保证从srt_sendmsg()到srt_recvmsg()的时间基本恒定。srt_sendmsg()给数据打一个时间戳,在接收端检查时间戳,决定什么时候可以把数据交给上层应用。核心思想并不复杂:buffer抗抖动,根据时间戳保持固定延迟。具体实现还考虑了time drift问题。
端到端延迟约等于配置的SRTO_RECVLATENCY + 1/2 RTT0,RTT0是握手阶段计算得到的RTT。配置的latency要足够补偿RTT的波动,同时考虑丢包重传次数。
固定端到端延迟由TSBPD(Timestamp-based packet delivery)开关控制,是否允许丢包由TLPKTDROP(Too late packet drop)控制。前者受后者影响,只有允许丢包,才能保证固定延迟,否则网络情况较差时,到达的数据包会超过配置的延迟。在同时开启TSBPD和TLPKTDROP时,超时的包不再重传,因为根据固定延迟设计,发送过去也会被接收端丢弃。固定延迟意味着要么准时送给上层应用,要么被动或主动丢弃过期的数据包。
传输层的固定延迟可以简化接收端消费者的缓冲设计。以播放器为例,播放器一般有多级缓冲,IO模块内或IO到demux有缓冲,解码后到渲染也有缓冲队列,而核心的缓冲模块一般放在demux后解码前,原因是可以计算缓冲数据的时长。解码后到渲染前也可以计算时长,但解码后的数据量大,不适合缓冲较长时间数据,不能控制缓冲水位。IO模块做到固定延迟缓冲,播放器可以省去IO到demux、demux到解码的缓冲模块,只需要简单的数据管理。多级缓冲设计延迟大,IO模块的缓冲区是整个播放器缓冲的一小部分,丢包恢复能力弱。当把缓冲区交给传输模块管理后,丢包恢复能力增强。
音视频开发 学习资料、教学视频有需要的可以自行添加学习交流群或者资料领取
弱网抗性
SRT支持ARQ和FEC两种抗弱网机制。另外,Link bonding中的broadcast模式也能提高数据传输的可靠性。
- ARQ -
SRT的ARQ设计同时使用了ACK和NACK两种机制。接收端在两种情况下会发送ACK:
每10 ms发送一次全量ACK(full ACK);
每收到64个包发送一个轻量ACK(light ACK)。
轻量ACK是针对高码率场景考虑的:高码率下,10 ms对应的包太多了,ACK不及时则发送端不能释放已发送的数据。
除了通常的ACK、NACK包之外,SRT还有ACKACK包:
发送端收到ACK后立即发送ACKACK,中间不做delay;接收端把发送ACK到收到ACKACK的时间作为RTT;
接收端收到ACKACK后,停止重发同一个ACK包,否则会多次发送。
用于测量RTT时,可以把ACK看做ping,ACKACK看成pong。
接收端有两种情况会发送NACK:
收到的包sequence number有空洞
周期性检查丢包情况并发送NACK,时间间隔为max( (RTT+4*RTTVar)/2, 20)。
周期性NACK可能导致一个包不只一次被重发,另一方面保证了低延迟的特性。
- 模拟测试 -
SRT抗随机丢包能力强,但高丢包率场景带宽占用比较高。用FFmpeg做SRT client和server,通过NetEm配置多种丢包率:0%、10%、20%、50%、70%,模拟弱网环境。视频码率约5 Mbps,25 fps。
可以看到,丢包70%的情况下,接收端仍然可以获得稳定的帧率。另一方面,丢包率20%时,带宽占用已经是视频码率的两倍。以上实验数据表征带宽充足时高随机丢包的场景;生产环境还需要考虑更恶劣的情况,即可用带宽不足的场景。
除了固定延迟的特性,抗随机丢包也能起到降低延迟的作用。TCP协议因为拥塞控制,在出现丢包时降低发送速度,且重发比较慢,导致数据在发送端累积,延迟增大。下面是对比高RTT加1%随机丢包场景的延迟。因为用来测试的server端不支持SRT下行,所以只在上行模拟1%丢包。
RTT在250 ms ~ 400 ms,波动较大。
Ping:
time=321.291 ms
time=291.814 ms
time=362.499 ms
首先是RTMP上行加RTMP下行的端到端延迟测试:
可以看到,在视频时间戳1分24秒,延迟在2.3秒左右,而到了2分16秒时,延迟达到16秒。而通过RTMP over SRT,延迟可以稳定在0.5秒左右。
SRT file模式有拥塞控制,live模式只有Pacing控制,没有拥塞控制。但libsrt提供了丰富的统计信息,应用层可以根据统计数据调整视频采集和编码,避免拥塞。从直播系统的实时性角度来说,只做传输层的拥塞控制没法保证系统的低延迟,也不能应对传输带宽始终小于视频码率的极限情况。
SRT的Pacing是根据最大发送带宽来计算发包的时间间隔。最大发送带宽由三种策略确定:
手动配置最大发送带宽max_bw;
根据输入码率和overhead,计算max_bw = input_bw * (1 + overhead);
配置overhead,自动估计输入带宽,max_bw = input_bw_estimate * (1+ overhead)。
几种配置有优先级,例如配置了max_bw,则忽略input_bw,overhead等配置,组合关系见下图:
注意:live模式SRT的默认配置是max_bw 1Gbps。当应用往SRT写数据的速度不够均匀时,max_bw过高导致Pacing控制起不到效果,瞬间发送大量数据出现丢包率上升问题。理解并合理配置这三个参数非常重要。
SRT支持现状
SRT协议目前只有libsrt一个代码实现。libsrt功能上既可以做client,又可以拿来做server,既可以做上行推流,也可以做下行播放。由于全局锁、线程过多、CPU占用较高等代码实现方式问题,libsrt不适合做大规模下行播放场景。当前主要用于上行推流,提高上行传输质量。
FFmpeg提供libsrt的封装,option配置方面支持的比较全面。但是FFmpeg avio接口设计上缺少对统计信息的支持,libsrt的统计信息全部丢失。注意FFmpeg libavformat对libsrt的封装没有考虑双向传输的场景,丢失了双向传输的能力。
VLC支持SRT播放和推流。注意已发布的VLC 3.0不支持配置streamid,只有master开发分支支持。
OBS是通过FFmpeg的libavformat来支持SRT。
Gstreamer有libsrt的封装。
腾讯云音视频在SDK侧和服务侧都支持了RTMP over SRT,使得基于RTMP的客户可以无缝切换到该方案。
SRT面向广电领域,需要技术人员手动测试网络条件后再做参数调校。参数配置复杂,缺少自适应特性,难以用一组配置应对多变的网络环境。简化配置 + 自适应策略是SRT的一个优化方向。
例如,当前固定延迟参数latency是以时间单位来配置的。配置latency应当考虑网络的RTT,但RTT是运行时才知道的。如果能以RTT作为单位,配置latency = N * RTT,将大大降低配置难度,适应不同网络条件。
Configure Latency as a Multiple of RTT
Future Improvement
If SRT can estimate RTT from handshakes, then it could configure the effective latency as a multiple of this value, instead of some pre-defined amount of milliseconds. E.g., you could say I want my end-to-end latency be 2×RTT, and after the connection you could find out the actual latency negotiated.
Also something for the future.
详细讨论见:github issue: Negotiated SRT link latency is unknown due to 1/2 RTT addition[6]。
当前缺少拥塞控制,只有简单的配置输入码率、输出带宽上限等方式,在传输带宽小于视频码率、视频码率变化大、网络波动等场景表现不理想。SRT应当具有基本的拥塞控制策略,再与视频编码相结合来应对拥塞。
SRT不限定容器格式,但loss模式依赖容器格式有resync机制,基本只剩下TS一个选项。而TS存在封装层冗余高问题,原因有多方面:固定4字节TS头,padding,固定码率时的空包等。
Lossless模式容器格式有几个选项,除了TS外,还可以使用flv、mkv、fmp4。
flv优点是简单和兼容旧的生态系统,但存在重大缺陷:不支持新的codec,时间戳精度低,音轨限制等等。
mkv除了一个变种webm得到Google支持,主要是开源社区维护的形式,缺少商业生态。
fmp4在支持新codec方面是最完善的,围绕着fmp4,有DASH、HLS、以及CMAF等标准。国内fmp4的应用尚未普及,相信会得到越来越广泛的支持。
RTMP over SRT使得传统基于RTMP的上行SDK 可以无缝迁移。优点是平滑接入现有的RTMP推流系统,但有些地方需要额外注意:
SRT设计上未考虑双向传输数据上存在耦合的情况。RTMP双向的信令数据是存在耦合关系的,一个已知问题是RTMP握手阶段丢包可能导致SRT重发机制不工作,RTMP建连失败。
This is a known issue/behavior. When a receiver has Periodic NAK reports enabled (SRTO_NAKREPORT), then a sender does not apply any RTO, as it expects to receive the loss report from the receiver.
If it is the last packet in transmission, then it will be never retransmitted.
The assumption here is that in live mode you don't normally care about this last packet, and just close a connection on the sender's side when you don't have any further source to send.
见github issue 【LIVE mode transport deadlock】(https://github.com/Haivision/srt/issues/1722)[7].
RTMP代码实现可能会写许多小的数据包。TCP有对数据做聚合的能力,而RTMP over SRT比较难做数据聚合,会发送大量的小IP包。
单向传输时,发送端的RTT信息由接收端通过ACK包带过来。双向传输时,各自都可以计算RTT。但对于RTMP over SRT推流,过了初始阶段,双向传输只剩上行有数据,客户端RTT更新计算机制不再工作。
RIST协议2017年提出,至今(2021年)发布了两个profile,2018年发布simple profile[8],2020年发布main profile[9]。
simple profile继承RTP协议,与RTP协议兼容:在RFC 3550 RTP[10]基础上,RIST新增的内容包括:
NACK
RFC 4585 Extended RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/AVPF)[11]定义的bitmask形式的NACK包;
基于Application defined RTCP,发送range based NACK包。
RTT测量
基于Application defined RTCP来发送Echo request和Echo response,用于测量RTT。
Bonding支持
发送端通过多个网口发送数据,两种模式:
冗余模式,在多个网口发送重复的数据,增加可靠性;
以Round-robin模式在不同网口发送不同的数据,增加带宽。
接收端在一对RTP/RTCP端口接收到同一个客户端不同链路发送的数据,进行排序去重等操作。
RIST保留了RTP的组播模式,但因为组播的特殊性,目前只适用于局域受控的网络环境。
在simple profile基础上,main profile新增如下特性:
多路复用(stream multiplexing)和tunneling:
- 基于GRE-over-UDP RFC 8086 [12]实现;
- RTP和RTCP共用一个传输通道/一个端口;
- 多条流可以共用一个传输通道/一个端口;
- 多条流 + 用户自定义数据流可以共用一个传输通道/一个端口。
加密:
- DTLS加密;
- Pre-Shared Key加密。
TS空包删除;
高比特率、高延迟支持:
- 场景:传输100 Mb/s码率的TS流,RTP sequence number每6.9秒回绕1次。当配置ARQ重发上限为7次时,能够支持的最大RTT为1秒。即高比特率加高延迟的场景,RTP sequence number回绕问题限制了重发次数;
- 方案:利用RTP header extension,把RTP的sequence number扩展到32位。
未来展望
在广电领域,RIST和SRT处于竞争状态;在互联网领域,RIST还有如下问题待解决:
在广Simple profile存在session/streamid和端口分配问题:
-RIST沿用RTP的方式,用IP和端口作为session,导致的问题是:server不能用一个或一对端口支持多个client,每增加一个client,server要新开两个端口。除了受限的特定网络,公网大规模部署面临挑战;
-除了IP和端口作为client和server的映射之外,RIST标准没有定义其他唯一标识client的手段,client不能传递streamid类似标识信息给server。
Main profile不支持向下兼容simple profile:当一端配置为simple profile,一端配置为main profile时,没有一套完整的降级策略,能够让main profile的端自动降级到simple profile。