作者:王宇(腾讯音视频高级架构师)
自我介绍下,毕业以来加入腾讯,一直从事客户端研发,身处互联网公司,踏着互联网的浪潮,一直在浪尖行走,从最早的PC QQ,到移动时代的手Q,再到腾讯物联的嵌入式,以及最近这两年直播领域风起云涌,接下来主要给大家介绍下QQ视频直播的架构及原理。
今天主要给大家介绍三个方面的内容,视频直播的框架及基础介绍;
直播中的一些关键技术:主要包括播放控制问题,性能与质量等;直播APP的整体解决方案。
首先来看下直播前后台的大致框架,通过推流App或者第三方推流器将数据推送给后台服务器;
服务器将收到的数据进行转码发送给DC;
观看者从OC上拉取对应的直播流进行观看;
目前主要采用RMTP、HLS、FLV三种直播格式。接下来我们重点看下直播过程中客户端架构。
这是直播客户端的大体框。
左侧是推流端:音视频采集;预处理;音视频编解码;数据封装网络发送;
右侧是播放端:网络收数据;音视频解码;视频渲染与音频播放。
整个过程就是一个线性的流水化过程,流程看起来还是比较简洁的。
视频编码,就是一个压缩的过程,如何将视频数据压缩后便于网络传输,这里有几个纬度:
1.每一帧数据的帧内压缩,也就是这里的I帧,能够独立还原出图像数据,一般比较大;
2.连续的两帧画面之间存在相似性,参考前面一帧数据,只记录差异部分,这样数据量就比较小,也就是这里的P帧,采用的是前向预测编码;
3.更进一步,如果参考前后两帧数据记录差异部分,这样数据量会更小压缩率最高,也就是这里的B帧,采用双向预测内插编码。
麦克风采集到的原始PCM数据一般比较大,以44.1k,双声道为例,码率大概有1M多,不做压缩的话很难传输,音频编码,就是将采集到的PCM数据,根据不同的音频格式进行压缩,当然目的也是为了在保证音质的前提下尽量压缩音频数据,目前采用的是AAC来压缩。
流媒体音视频数据封装格式,原始的编码之后的音视频数据不太利于网络传输,目前有多种封装格式:
1.RTMP协议,需要做拆包与组包,将每个数据帧拆成小分片加上协议头发送,一般用于推流;
2.FLV格式,比较简单每个数据帧加协议头,一般用于播放;
3.HLS格式,将一组音视频数据组合在一起,通过m3u8文体来关联,相当于一个个的小文件,适用于网页播放。
直播的框架及基本原理就这么简洁,但是要做到好的体验却不是一件很简单的事情,基本上几个人花一两个月就能做出来,但是要优化出一个好的效果需要长期的积累与打磨。
接下来主要看下直播过程中的一些关键问题,主要包括:首屏秒开、流畅与延迟控制、实时音画同步等等。
当我们观看一个直播的时候不能等半天才出画面,体验显然很差,我们期望的是能够立马看到画面,即首屏秒开。
首先来看下解码过程:推流端上传P帧,播放端拉到一个P帧,如果帧连续,能立马解出来正常显示;如果没有之前的参考帧,而仅仅只收到了一个P帧,解析不出来不能显示,直到遇到一个关键帧;如果要做到数据来了就能立马显示,对于首次打开就必须要能收到一个I帧,那么问题来了,服务端如何保证返回给播放端的第一帧为I帧?
假设有一个人正在观看,推流端上传一个P帧,服务器将其发送给播放端,播放端正常播放,同时服务器将其放到一个缓存队列里面;有了这样一个临近数据的缓存队列,当有一个新的观看者来播放的时候,就可以直接从缓存队列里面取最新的一个GOP一次性返回给观看端,此时播放端就可以立马解析出I帧渲染,这样就能做到秒开。
流畅与时延控制,对播放而言,来数据就播放,这个问题应该就这么简单,但是..
播放有两类需求:
1.游戏场景下更关注流畅性;
2.主播场景下更关注低延迟;
于是就有了流畅与极速两种模式,流畅就是缓冲播放,极速就是来数据就播放,缓存大的时候就做速播;看起来还不错,但是..
实际使用发现流畅模式会带来延迟大的问题,而极速模式卡顿的概率又比较高;有没有一种更好的方案?这里问题的根源在于网络是抖动的,要保证低延迟的同时又能流畅播放,其实就是在流畅与低延迟之间做平衡;应该怎么设计?
对于播放端而言,网络来数据,自己播放,这就是一个生产消费模型,正常情况是生产多少,消费多少,但网络抖动引发卡顿与数据堆积从而造成时延。
这里统计过去一段时间内的数据,有两个指标:
1.待播放数据的大小反应时延,即这里的过去一段时间内的AvgCache;
2.过去一段时间内的网络抖动大小决定了待播放数据抖动的大小,也即决定了卡顿,即这里的Jitter,Jitter随时间推移而修正。
平均cache太大即延迟大,为了消除延迟就需要快速消耗掉一部分数据,这里要消耗掉多少数据呢?
理想情况下是要调整到红色曲线所示的,加速MinCache的数据,这样既能保证不触发卡顿又能做到最小延迟,但是这太理想化了,这里需要更谨慎一些,于是做了以下调整:
1.引入最小阈值,如图所示最多加速到这个程度;
2.不是一次加速到最佳状态,设定最大加速时长。
总结下,统计数据决策加速;计算调整时长,恒定加速;如此循环,就能调整到一个最佳状态,这就是自适应播放控制策略。
这里加速有三种策略:
1.直接丢弃部分数据;
2.短时的快速播放;
3.长时的较快速的播放,让人察觉不到在速播。
这里问题的根本就是要多消耗一部分数据,毫无疑问不论是丢弃、快速加速、慢速加速都是有损的,三种方案各有优劣,瞬时的大损伤好,还是长时的低损伤好?这里还是看个人喜好,萝卜白菜各有所爱,最终还是要看用户反馈来选择。既然做了速播,那不可避免会遇到音画不同步的问题,如何解决?
音画如何做同步?
经典的音画同步有三种方案:音频同步到视频;音视频同步到外部时钟;视频同步到音频;
方案一音频很难去频繁的做调速,所以直接pass了;
方案二在卡顿的时候需要做时间修正,做加减速的时候保持步调一致不是很好处理;
方案三视频同步到音频比较自然,视频能够很方便做调速处理,目前大多推荐的也是方案三,实现起来也比较简单,接下来看下整个播放控制方案。
音视频jitter队列分开方便取数据,控制器根据jitter大小及当前策略来决定加速还是正常播放;
音频按需播放,在最末端,音频将PTS同步给视频,视频据此修正当前帧的播放时机,解决音画不同步问题;
引入一个负反馈机制来保证解码后的数据不会太大;
声音如何加速,声音处理有变调变速,变调不变速,变速不变调,这里采用了变速不变调算法来对声音进行加速。
来看下实际效果,这分别是两种损伤模型下的效果,左边的是瞬时高损伤,右边的是长时低损伤;可以感受下两种的不同效果。
高分辨率下性能如何保证,360p一般都能达到比较好的性能,那么问题来了,720P推流与播放、1080P播放性能如何保证?主要包括以下几个方面:
1.整个系统采用多线程处理,推流与播放都是流水化的过程,每个处理流程都采用独立线程,每秒帧率完全由单一处理过程的最大耗时决定,而不是整个流程的处理时间。
2.多核手机已经普及,采用软编软解能够在手机性能达标的情况下完成高帧率编解码。
3.目前手机基本都支持硬编硬解,对于高分辨率采用硬件编解码,可以显著降低CPU占用,提升性能。
4.能用GPU处理的尽量用GPU来做,如本地渲染与镜像处理、格式转换等。
5.采用ARM指令集优化来提升性能,比如视频裁剪、旋转等算法。
既然做直播SDK,那么有问题如何定位?直播质量如何监控?
这里问题主要包括两大类:
1.开发测试过程中遇到的问题;
2.用户现网问题;这里直播质量主要包括CPU占用率、卡顿率、播放延迟、上下行带宽等等。
用户遇到的问题如何怎么定位,LOG肯定是第一手资料,如何方便快捷的获取到LOG?
这里有一套LOG提取系统,三个步骤:获取地址软色、问题重现、提取log分析;这个就是我们的LOG提取系统。
为了监控直播质量,这里做了完备的数据上报及分析系统,基本上涵盖了各种关键性指标,既能反应直播的各种性能方便优化,同时也能辅助定位各种问题;这个是播放端的整体统计数据,下行带宽、卡顿率、缓冲大小、CPU占用率,这是一个宏观统计数据,反应了当前直播观看端的一个质量。
有了直播质量监控,更精细一些我们可以做一些运营分析,比如说,最近我们内部对一个游戏直播的流畅性做了一个运营统计分析,把外网用户分成了三组,通过配置不同的播放端参数来统计质量,最后我们得出了几个比较有意思的结论:
这是对直播推流端做的一个质量评估,根据一些指标对每项进行打分,然后给出一个综合得分来评估当前直播推流端的质量。
这个是直播后台的整套大体框架。
上面主要是完成视频的推流与播放,包含了接入集群,计算集群及CDN加速网络;
下面主要是帐号、消息等基础服务,及社交互动系统,我们使用这些后台服务构建了小直播的App。
小直播客户端App及业务后台源码对客户开放,提供了直播App的整套集成解决方案,基于此可以很方便的完成一款直播App的开发。