直播在2016年是一个非常火热的领域,我也有幸在今年参与了新浪微博直播客户端的开发,在此分享一下关于直播开发的一些经验。
1.视频直播的基本原理
视频直播的过程大概由这么几个部分构成:
- 推流端
- 源服务器
- CDN边缘节点
- 播放端
整个直播流转的过程是:推流端将视频流推向源服务器,源服务器对视频流进行编码或者转存,CDN负责负载均衡与缓存,CDN节点从源服务器获取视频流,播放端再从CDN上把视频流拉下来。
2.不同的直播协议
- HLS
HLS全称是Http live stream,是苹果公司主导的一种直播协议,完全符合http协议标准,Html对其原生进行支持,所以这种协议的优势就在于无论是在web端,还是iOS端都可以方便快捷的播放HLS的视频流。HLS协议不仅支持直播还支持点播,广泛的应用于H5产品当中。
HLS本身请求的是一个m3u8格式的文件:
#EXTM3U m3u文件头,必须放在第一行
#EXT-X-MEDIA-SEQUENCE 第一个TS分片的序列号
#EXT-X-TARGETDURATION 每个分片TS的最大的时长
#EXT-X-ALLOW-CACHE 是否允许cache
#EXT-X-ENDLIST m3u8文件结束符
#EXTINF extra info,分片TS的信息,如时长,带宽等
里面保存了一个一个.ts格式的视频分片文件路径
直播流会首先下载当前生成好的m3u8文件,再去一个一个下载里面的分片视频,所以对于源服务器来讲,生成好了一个m3u8文件之后,客户端才可能会拉到这个视频流,这也造成了HLS协议高延迟的问题。倘若一个m3u8文件里面所有的分片长度为10s,那么客户端拉到这个视频流的延迟至少为10s。
rtmp
rtmp全称Real Time Messaging Protocol,是一种基于TCP的数据通信协议,广泛的被应用于流媒体的传输中来,此协议是实时传输数据流,所以延迟比HLS要低,由于具备TCP协议的拥塞控制,对数据的完整性有一定保证。与此同时,Adoube对rtmp协议的支持也做的很好,Flash可以完美的支持rtmp协议的视频流播放,大多数推流端都使用rtmp协议进行推流。http-flv
http-flv是通过http协议传输flv的视频流,HTTP协议中有个content-length字段,规定了请求http的body部分的长度,如果请求的时候不加content-length字段,那么客户端会一直受到数据。基于传输包的http-flv协议可以将数据包做的比rtmp做的更小,在流量上有比较大的优势,而延迟几乎和rtmp相同。UDP
在过去传统的视频通话广泛的基于UDP协议,由于不像以上几种协议都基于TCP这种带拥塞控制的协议,UDP可以做到1s以内的超低延迟,在直播中UDP没有广泛采用的原因是需要对服务端的架构进行改造,所以对于之前做过视频通话的厂商来讲,他们拥有着得天独厚的优势,目前很多互联网教育厂商由于对超低延迟的需求,基本都采用了UDP协议
3.直播延迟问题
随着视频直播领域变得越来越火,直播的延迟问题也日趋收到开发者的关注,根据不同也业务需求,厂商对延迟的要求也有所不同,美女秀场直播2~5s之间可以忍受,对于在线教育来讲1s左右才能保证正常的教学需求。总体来看,目前影响直播延迟基本有这么几种原因:
- 网络速度
在整个视频直播流转的过程中,无论是推流端到源服务器之间的网速,还是播放端从CDN拉流的速度都影响着整个视频直播过程的体验。所以网速往往是直播延迟最至关重要的原因。 - 传输协议
在上一节中讲到了不同的传输协议对延迟的影响,这里不再赘述。 - 编解码速度
对于视频直播来讲编解码的速度,好的编解码策略也对直播卡顿延迟问题有的很大的影响,下一节我们来从了解H.264编码的原理上思考如何做一个好的编解码策略。
4.H.264编码
我们知道视频本身是由一张一张的图片构成,快速播放起来欺骗我们的眼睛,让我们觉得图片里面的场景是动态的。但是如果一个1080p的视频,以60帧的帧率来播放,在视频完全不压缩的情况下,将是一个庞然大物,单单其中的一帧图片就要好几M。所以大家发明了很多视频编码的来将视频进行压缩。压缩的基本思路就是将视频中一张一张完整的图片,有变化的部分存下来,一样的部分就只存一张就好了,去除了冗余的部分视频就会小得多。
我们让一部分帧保存了完整的图片,对于这种帧我们叫做I帧,如果I帧可以seek,我们将它叫做关键帧。两个I帧之间叫做一个GOP。
只保存了变化部分的帧,我们叫做参考帧,其中又分为前向参考帧(P帧)和双向参考帧(B帧)。参考帧的意思就是根据I帧或者P帧进行参考,决定自己保留图片中的哪部分内容。参考帧越多,尤其是B帧越多,这个视频就越小,因为图片保留的部分就越少。
可以看到,B帧需要前后都要参考,虽然更多的B帧可以更好的压缩视频的体积,但是对于编解码也变得更加的困难,因为当我收到一个B帧之后,我必须等到后面有一个P帧或者I帧出现我才能进行解码,这对于直播的流畅性来讲是有影响的。
5.对于直播首帧的优化
- GOP缓存
对于播放端的解码器而言,只有当遇到一个I帧的时候,视频才可以播放,假设播放端拉流的时候,刚好需要等2s才会遇到下一个I帧,这个播放器就可能会黑2s。所以为了能够秒开,快速的播放到第一帧视频,厂商往往会将最近的一个GOP缓存在CDN中,当播放端去拉流的时候,直接把上一个I帧传下来,保证播放器拉到流直接就可以播放。 - 更小的缓冲buffer
直播视频的buffer一般为百毫秒级,降低buffer可能帮助首帧快速的被渲染出来,但缺点是,在网络状态不好的条件下有可能出现频繁caching的情况。所以建议只策略的降低第一个缓冲buffer的大小,来帮助首帧可以快速呈现。 - CDN最近策略
由于CDN负载均衡的策略,CDN会根据当前各个边缘节点的负载情况来指定客户端从哪个边缘节点来拉流,但这样有可能会导致播放端走了一个比较远的路由,从而增加延迟。有的厂商会在播放端密集的地区增加CDN的负载容纳量,并且指派就近原则,选取最近而不是根据负载来判断拉流的路径。 - UDP策略
将基于TCP的视频流协议换成无拥塞控制的UDP。 - local DNS
由于拉流时的请求地址往往是带域名的,请求过程中走DNS服务器有时会耗费一定的时间,可以选用local DNS,在客户端本地拿到缓存的ip地址直接请求服务器。