这里简单插述一下视频编解码过程中的一种约定:GOP( Group of Pictures )
为了便于视频内容的存储和传输,通常需要减少视频内容的体积,也就是需要将原始的内容元素(图像和音频)经过压缩,压缩算法也简称编码格式。例如视频里边的原始图像数据会采用 H.264 编码格式进行压缩,音频采样数据会采用 AAC 编码格式进行压缩。 视频内容经过编码压缩后,确实有利于存储和传输; 不过当要观看播放时,相应地也需要解码过程。因此编码和解码之间,显然需要约定一种编码器和解码器都可以理解的约定。就视频图像编码和解码而言,这种约定很简单: 编码器将多张图像进行编码后生产成一段一段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取一段一段的 GOP 进行解码后读取画面再渲染显示。 GOP ( Group of Pictures ) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位,它的排列顺序将会一直重复到影像结束。
在互动直播SDK中,将帧类型扩展到五种:
I帧不需要参考帧;
P帧只参考上一帧;
P_WITHSP帧可参考上一帧、I帧、GF帧、SP帧,自己不可以被参考;
SP帧可参考I帧、GF帧、SP帧;
GF帧可参考I帧、GF帧 。
标准的H264编码的参照关系,每一个GOP的第一针是I帧,P帧依次参考上一帧,抗丢包性不强,如果中间有I帧或P帧丢失,则该GOP内后续P帧就会解码失败。
在实时直播的场景,为保证流畅性,重写编码器逻辑,首个GOP包开头为I帧,后面GOP包开头为GF帧,这是利用GF帧的传递参考关系是跨GOP,每个GF帧参考上一个GOP的I帧或GF帧。GF帧体积对比I帧要小,后续GOP的下载解码更快速。
对GOP内部的帧组织,也使用P_WITHSP帧来代替P帧,主要是因为P_WITHSP帧(粉红色表示)的解析可参考上一帧、I帧、GF帧、SP帧,自己不可以被参考。就算上一帧P_WITHSP未解码出来,后一帧P_WITHSP的解码也不受影响,增强了抗丢包性。
那具体到业务上,通过WireShark抓包我们发现。过程主要耗时在首个GOP包下行比较慢,需要等待I帧(FT是0代表I帧)下载完毕才开始解码,如果I帧不完整无法解码,则需要等待第二个GOP包,等待时间加长。
那通过这个现象,为了让整个过程加快,我们做了以下工作:
减小首个GOP包的分片大小:将GOP的分片由5s改为3s,并且首个GOP包只缓存必要的I帧,减少首个GOP包的体积;(PS:GOP包的长度和主播端编码性能也是强相关,GOP分片太小,编码性能不高,分片时长的确定需要综合考虑)
首个GOP包需要走网络下载,同样网络条件下这部分路径越短下载越快。GOP包之前是存在流控服务器上,GOP包要到达客户端连接的接口机,还需要链接传输的耗时。新的版本直接在接口机上缓存当前直播中房间的GOP数据,保证在客户端连上接口机之后,就可以直接从本机缓存中推流首帧数据,省掉之前的链接传输耗时;
大部分播放器都是拿到一个完整的 GOP 后才能解码播放, 改写播放器逻辑让播放器拿到第一个关键帧(I帧)后就给予显示。不需要等待全部的GOP下载完毕才开始解码; 以上三点做好了之后,效果明显,整个的拉取首帧的时间由之前的2140ms降到平均300ms,当然完成这些工作并不是上面叙述的三点那么简单,中间过程我们也发现一些棘手问题,并推动解决:
主播上行网络丢包导致的GOP乱序、多台接口机之间缓存的管理、GOP分片时长的确定。
4)持续优化
我们一直没有放弃“更快更爽”的体验追求,在后续的迭代中也持续优化直播的体验:
接口机IP竞速;
合并请求;
多码率。