本文为
《程序员》
原创文章,未经允许不得转载,更多精彩文章请订阅
2017年《程序员》
作者简介:
张亚伟,齐聚科技技术研究院技术总监,拥有多年跨平台直播开发经验与技术积累。
责编:
屠敏,欢迎技术投稿、约稿、给文章纠错,请发送邮件至
[email protected]
。
导语:
本内容包括A主播音视频数据合成,B主播音视频数据合成,UpServer转发音视频数据、传输协议介绍等小节。其中大量细节都是在第二篇
《移动直播连麦实现——Server端合成》
中定义的,如视频基本参数定义、显示位置定义、音频合成算法、时间戳定义和使用说明等,为避免重复说明,此处直接使用。更多内容详情见
《移动直播连麦实现思路:整体篇》
。
当A端合成连麦的音视频数据流后,会把该数据流推送给UpServer,再由UpServer推送给DeliveryServer集群,然后由DeliveryServer分发给移动直播的所有观众。DeliveryServer集群及后续的推送工作不涉及音视频合成,与连麦无关,不属于本文关注的范围,下文不再描述,结构图也仅绘制到DeliveryServer,望大家了解。
A主播视频
A主播端视频合成处理较为复杂,分为三步,第一步是A主播接收所有B主播视频,并进行解码和缓存;第二步是A主播本地视频的采集,与远程视频进行合成、显示等;第三步是编码、打包、发送合成好的视频图像给UpServer服务器。
下面分别对这三个步骤进行详细说明。
接收B主播视频
A主播负责视频合成时,需要先拿到所有B主播的视频数据,而B主播的视频都是由UpServer服务器转发给A端的,如图1所示。此时,所有B主播的视频数据都发给UpServer,再由UpServer转发给A主播;针对B主播的视频数据,UpServer不需要额外处理,按独立数据流发给A主播即可。
A主播需要创建不同的逻辑对象,分别用于接收B1、B2、B3主播的视频数据,并在接收排序后按时间戳拼帧、解码,得到并保存每个B主播的视频图像。
合成本地和远程视频
在直播中,A主播自身也要持续不断地采集视频,按照预定的时间间隔得到每帧本地视频后,与上一步得到的B主播视频进行合成;A主播本地视频采集帧率是十分稳定的,无需考虑丢包、延迟等造成帧率不稳定的问题,故视频合成后的帧率就是A主播本地视频采集的帧率。
图1 A主播接收视频数据流结构图
视频参数、显示位置预定义、视频尺寸的缩放、颜色空间的转换、视频图像数据的存储格式等都与第二篇Server端合成相同,这里不再说明。下面的A主播视频合成流程介绍中继续复用角色定义,B1、B2、B3代表各B主播的图像缓存逻辑对象,A代表A主播本地采集到的视频,合成后的视频图像仍然存储在VideoMixer中。
-
首先读取A主播的视频数据作为合成底图,由于是本地采集,无需考虑读取失败情况。
-
拷贝A图像上部的58行到VideoMixer中,行数计算公式是640-160*3-1*2-100=58。
-
接着读取B3主播的视频数据进行合成,有成功和失败两种情况,如读取成功接第5步,如读取失败也分为两种情况,一是有上一帧,二是没有上一帧。如有上一帧也是执行第5步,如没有上一帧或不存在B3主播则见下一步。
-
如读取B3视频失败且上一帧不存在,或者不存在该主播,则直接拷贝A的视频图像161(160+1)行到VideoMixer中。
-
如读取B3视频数据成功或有上一帧,则把B3和A主播视频图像进行合成,这里需要逐行合成,每行要分成2段进行拷贝,第一段拷贝A视频数据到VideoMixer中,拷贝列数360-120=240;第二段拷贝B3视频数据到VideoMixer中,120列。
-
如读取B3视频数据成功,合成之后还要备份B3的视频数据到指定位置,用于下次读取失败时复用上一帧;当B3主播停止连麦或较长时间卡顿后,还需要清除该数据缓存,避免引起歧义。
-
接下来拷贝B3、B2之间间隔部分,即A的视频数据到VideoMixer中,1行。
-
循环3-7步过程,把B2、B2与B1之间间隔(A的1行)的视频数据拷贝到VideoMixer中;再循环一次,把B1、B1下部(A的100行)的视频数据拷贝到VideoMixer中。
-
使用上述流程,就完成了“A+B1+B2+B3”视频图像合成,合成结果已存储在VideoMixer中。
A主播本地的视频显示也要包括各B主播视频(A+B1+B2+B3),故使用合成好的VideoMixer绘制即可。
发送合成视频
A主播生成“A+B1+B2+B3”视频图像后,需要把该视频帧编码、打包,并发送给UpServer服务器,再由UpServer服务器转发该视频流给各B主播和DeliveryServer,如图2所示。
图2 A主播发送视频数据流结构图
DeliveryServer接收并转发“A+B1+B2+B3”视频流,用于移动直播用户观看连麦视频,后续的发送流程与移动直播相同,不再赘述。各B主播接收“A+B1+B2+B3”视频流,用于显示A主播及其他B主播的视频,对于B主播视频合成和显示的细节,将在B主播视频合成小节中讲述。
总结,在移动直播连麦A端合成视频模式下,A端需要接收和发送大量媒体数据,执行的任务繁重、实现的业务功能复杂,需要消耗更多的CPU资源,所以A主播的手机网络状态和CPU处理性能是最大瓶颈,这点一定要注意。如果可以使用硬件编码和解码,就尽量使用,毕竟就算是最好的手机设备,若手机使用过程中发热量太大,也对业务应用有一定影响。
B主播视频
B主播视频处理过程与A主播相比要简单一些,主要分为发送、接收显示两个部分。发送部分也是采集、编码、打包、发送等,与其他合成模式下的流程一致,仅需注意下视频尺寸为160*120,毕竟视频尺寸小消耗的处理资源也少一些。B主播视频数据流发送的结构图,可参考上一节的图1,这里不再重新绘制。
接下来说B主播接收视频及后续的合成流程,有两种方式,以B1主播为例说明如下:
-
第一种,UpServer推送合成好的视频“A+B1+B2+B3”给B1,与推送给普通观众的视频流相同,如上一节的图2所示。此时B1需要把本地采集到的视频合成到“A+B1+B2+B3”顶层上,替换掉原有的B1视频图像即可,从本地视频显示的实时性要求来看,这步替换是必须要做的。
-
第二种,UpServer推送合成好的视频“A+B1+B2+B3”给B1,同时UpServer也把B2、B3的视频数据独立推送给B1,如图3所示。此时B1主播收到独立的3路视频数据流,需要把本地采集到的视频及B2、B3主播视频,合成到“A+B1+B2+B3”顶层上,替换掉A主播已经合成好的B1、B2、B3视频图像。
图3 B1主播接收视频数据流结构图
先描述下以上两种视频合成方式的细节。第一种方式与UpServer合成模式下的B主播视频合成流程完全相同,细节不再复述。而第二种方式B1的处理流程更为复杂一些,需要把“A+B1+B2+B3”、B2、B3的视频数据都解码,然后按照音视频同步的顺序读取和合成,得到的结果是新的合成图像“A+B1(新)+B2(新)+B3(新)”,视频合成流程与A主播视频合成类似。
接着比较下两种视频合成方式的优缺点。第一种方式以前就建议过,从功能和业务角度来说,可以满足需要。而第二种方式的优缺点更明显一些,缺点是需要处理的内容更多了,包括B2、B3主播视频的解码、同步和合成等,需要消耗更多的系统资源;优点也有以下两点:
-
与第一种方式相比,B2、B3主播视频的实时性更好一些,毕竟使用UpServer直接发送的B2、B3视频数据进行合成,比使用A合成好的B2、B3视频数据,处理延迟和网络延迟占用的时间可以明显降低。
-
B2、B3主播的音视频同步效果会更好一些。一般情况下B1需要独立接收A、B2、B3主播的音频数据,当B1播放B2的音频、视频时,由于两路数据流都有,所以同步播出的效果会比较理想;而使用方式一,B2视频是由A合成好的,当B1主播播放B2的音频时,无法和B2视频进行同步。
处理延迟时间是指A合成B2、B3视频消耗的时间,包括解码、合成、编码,打包等;网络延迟时间是指B2、B3的视频数据,要先由UpServer要发给A,A完成视频合成后再发送回UpServer网络传输使用的时间。这两个时间加在一起,在处理比较好的情况下,估计也需要1秒以上,此处还不考虑网络比较差导致的丢包、网络中断导致的卡顿情况,毕竟网络质量差,连麦的体验效果也必然非常差。
使用方式一,A主播合成的“A+B1+B2+B3”视频时间戳必然是A主播的,可以很好的和A主播音频进行同步,但不能和独立的B2、B3主播音频数据时间戳同步。由于延迟占用时间,A主播合成的B主播视频必然比独立发送的B2、B3主播音频更晚到达B1主播,导致B1主播播放B2、B3音频时,与B2、B3视频出现不同步的可能性很大。
总结,以上两种方式,方式一的处理流程简单一些,消耗的资源也少,而方式二处理流程更为复杂,且消耗的资源也多;但在用户体验效果方面,方式二会更好一些,结论是如果非常重视用户体验,且消耗的资源可以承受则使用方式二,否则使用方式一。如果是第一次实现,建议先使用方式一实现,待收到更多效果反馈后,再确定是否改为方式二,毕竟方式二仅对B主播的用户体验改善有效,而与移动直播的众多接收用户体验效果无关。