1、引言
随着移动网络网速的提升与资费的降低,视频直播作为一个新的娱乐方式已经被越来越多的用户逐渐接受。特别是最近这几年,视频直播已经不仅仅被运用在传统的秀场、游戏类板块,更是作为电商的一种新模式得到迅速成长。
本文将通过介绍实时视频直播技术体系,包括常用的推拉流架构、传输协议等,让你对现今主流的视频直播技术有一个基本的认知。
2、蘑菇街的直播架构概览
目前蘑菇街直播推拉流主流程依赖于某云直播的服务。
云直播提供的推流方式有两种:
- 1)一是通过集成SDK的方式进行推流(用于手机端开播);
- 2)另一种是通过RTMP协议向远端服务器进行推流(用于PC开播端或专业控台设备开播)。
除去推拉流,该云平台也提供了云通信(IM即时通讯能力)和直播录制等云服务,组成了一套直播所需要的基础服务。
3、推拉流架构1:厂商SDK推拉流
如上题所示,这一种推拉流架构方式需要依赖腾讯这类厂商提供的手机互动直播SDK,通过在主播端APP和用户端APP都集成SDK,使得主播端和用户端都拥有推拉流的功能。
这种推拉流架构的逻辑原理是这样的:
- 1)主播端和用户端分别与云直播的互动直播后台建立长连接;
- 2)主播端通过UDT私有协议向互动直播后台推送音视频流;
- 3)互动直播后台接收到音视频流后做转发,直接下发给与之建立连接的用户端。
这种推拉流方式有几点优势:
- 1)只需要在客户端中集成SDK:通过手机就可以开播,对于主播开播的要求比较低,适合直播业务快速铺开;
- 2)互动直播后台仅做转发:没有转码,上传CDN等额外操作,整体延迟比较低;
- 3)主播端和用户端都可以作为音视频上传的发起方:适合连麦、视频会话等场景。
4、推拉流架构2:旁路推流
之前介绍了通过手机SDK推拉流的直播方式,看起来在手机客户端中观看直播的场景已经解决了。
那么问题来了: 如果我想要在H5、小程序等其他场景下观看直播,没有办法接入SDK,需要怎么处理呢?
这个时候需要引入一个新的概念——旁路推流。
旁路推流指的是: 通过协议转换将音视频流对接到标准的直播 CDN 系统上。
目前云直播开启旁路推流后,会通过互动直播后台将音视频流推送到云直播后台,云直播后台负责将收到音视频流转码成通用的协议格式并且推送到CDN,这样H5、小程序等端就可以通过CDN拉取到通用格式的音视频流进行播放了。
目前蘑菇街直播旁路开启的协议类型有HLS、FLV、RTMP三种,已经可以覆盖到所有的播放场景,在后续章节会对这几种协议做详细的介绍。
5、推拉流架构3:RTMP推流
随着直播业务发展,一些主播逐渐不满足于手机开播的效果,并且电商直播需要高保真地将商品展示在屏幕上,需要通过更加高清专业的设备进行直播,RTMP推流技术应运而生。
我们通过使用OBS等流媒体录影程序,对专业设备录制的多路流进行合并,并且将音视频流上传到指定的推流地址。由于OBS推流使用了RTMP协议,因此我们称这一种推流类型为RTMP推流。
我们首先在云直播后台申请到推流地址和秘钥,将推流地址和秘钥配置到OBS软件当中,调整推流各项参数,点击推流以后,OBS就会通过RTMP协议向对应的推流地址推送音视频流。
这一种推流方式和SDK推流的不同之处在于音视频流是直接被推送到了云直播后台进行转码和上传CDN的,没有直接将直播流转推到用户端的下行方式,因此相比SDK推流延迟会长一些。
总结下来RTMP推流的优势和劣势比较明显。
优势主要是:
- 1)可以接入专业的直播摄像头、麦克风,直播的整体效果明显优于手机开播;
- 2)OBS已经有比较多成熟的插件,比如目前蘑菇街主播常用YY助手做一些美颜的处理,并且OBS本身已经支持滤镜、绿幕、多路视频合成等功能,功能比手机端强大。
劣势主要是:
- 1)OBS本身配置比较复杂,需要专业设备支持,对主播的要求明显更高,通常需要一个固定的场地进行直播;
- 2)RTMP需要云端转码,并且本地上传时也会在OBS中配置GOP和缓冲,延时相对较长。
6、高可用架构方案:云互备
业务发展到一定阶段后,我们对于业务的稳定性也会有更高的要求,比如当云服务商服务出现问题时,我们没有备用方案就会出现业务一直等待服务商修复进度的问题。
因此云互备方案就出现了: 云互备指的是直播业务同时对接多家云服务商,当一家云服务商出现问题时,快速切换到其他服务商的服务节点,保证业务不受影响。
直播业务中经常遇到服务商的CDN节点下行速度较慢,或者是CDN节点存储的直播流有问题,此类问题有地域性,很难排查,因此目前做的互备云方案,主要是备份CDN节点。
目前蘑菇街整体的推流流程已经依赖了原有云平台的服务,因此我们通过在云直播后台中转推一路流到备份云平台上,备份云在接收到了直播流后会对流转码并且上传到备份云自身的CDN系统当中。一旦主平台CDN节点出现问题,我们可以将下发的拉流地址替换成备份云拉流地址,这样就可以保证业务快速修复并且观众无感知。
7、视频直播数据流解封装原理
介绍流协议之前,先要介绍我们从云端拿到一份数据,要经过几个步骤才能解析出最终需要的音视频数据。
如上图所示,总体来说,从获取到数据到最终将音视频播放出来要经历四个步骤。
第一步: 解协议。
协议封装的时候通常会携带一些头部描述信息或者信令数据,这一部分数据对我们音视频播放没有作用,因此我们需要从中提取出具体的音视频封装格式数据,我们在直播中常用的协议有HTTP和RTMP两种。
第二步: 解封装。
获取到封装格式数据以后需要进行解封装操作,从中分别提取音频压缩流数据和视频压缩流数据,封装格式数据我们平时经常见到的如MP4、AVI,在直播中我们接触比较多的封装格式有TS、FLV。
第三步: 解码音视频。
到这里我们已经获取了音视频的压缩编码数据。
我们日常经常听到的视频压缩编码数据有H.26X系列和MPEG系列等,音频编码格式有我们熟悉的MP3、ACC等。
之所以我们能见到如此多的编码格式,是因为各种组织都提出了自己的编码标准,并且会相继推出一些新的议案,但是由于推广和收费问题,目前主流的编码格式也并不多。
获取压缩数据以后接下来需要将音视频压缩数据解码,获取非压缩的颜色数据和非压缩的音频抽样数据。颜色数据有我们平时熟知的RGB,不过在视频的中常用的颜色数据格式是YUV,指的是通过明亮度、色调、饱和度确定一个像素点的色值。音频抽样数据通常使用的有PCM。
第四步: 音视频同步播放。
最后我们需要比对音视频的时间轴,将音视频解码后的数据交给显卡声卡同步播放。
8、视频直播传输协议1:HLS
首先介绍一下HLS协议。HLS是HTTP Live Streaming的简写,是由苹果公司提出的流媒体网络传输协议。
从名字可以明显看出: 这一套协议是基于HTTP协议传输的。
说到HLS协议: 首先需要了解这一种协议是以视频切片的形式分段播放的,协议中使用的切片视频格式是TS,也就是我们前文提到的封装格式。
在我们获取TS文件之前: 协议首先要求请求一个M3U8格式的文件,M3U8是一个描述索引文件,它以一定的格式描述了TS地址的指向,我们根据M3U8文件中描述的内容,就可以获取每一段TS文件的CDN地址,通过加载TS地址分段播放就可以组合出一整段完整的视频。
使用HLS协议播放视频时: 首先会请求一个M3U8文件,如果是点播只需要在初始化时获取一次就可以拿到所有的TS切片指向,但如果是直播的话就需要不停地轮询M3U8文件,获取新的TS切片。
获取到M3U8后: 我们可以看一下里面的内容。首先开头是一些通用描述信息,比如第一个分片序列号、片段最大时长和总时长等,接下来就是具体TS对应的地址列表。如果是直播,那么每次请求M3U8文件里面的TS列表都会随着最新的直播切片更新,从而达到直播流播放的效果。
HLS这种切片播放的格式在点播播放时是比较适用的,一些大的视频网站也都有用这一种协议作为播放方案。
首先:切片播放的特性特别适用于点播播放中视频清晰度、多语种的热切换。比如我们播放一个视频,起初选择的是标清视频播放,当我们看了一半觉得不够清晰,需要换成超清的,这时候只需要将标清的M3U8文件替换成超清的M3U8文件,当我们播放到下一个TS节点时,视频就会自动替换成超清的TS文件,不需要对视频做重新初始化。
其次:切片播放的形式也可以比较容易地在视频中插入广告等内容。
在直播场景下,HLS也是一个比较常用的协议,他最大的优势是苹果大佬的加持,对这一套协议推广的比较好,特别是移动端。将M3U8文件地址喂给video就可以直接播放,PC端用MSE解码后大部分浏览器也都能够支持。但是由于其分片加载的特性,直播的延迟相对较长。比如我们一个M3U8有5个TS文件,每个TS文件播放时长是2秒,那么一个M3U8文件的播放时长就是10秒,也就是说这个M3U8播放的直播进度至少是10秒之前的,这对于直播场景来说是一个比较大的弊端。
HLS中用到的TS封装格式,视频编码格式是通常是H.264或MPEG-4,音频编码格式为AAC或MP3。
一个ts由多个定长的packtet组成,通常是188个字节,每个packtet有head和payload组成,head中包含一些标识符、错误信息、包位置等基础信息。payload可以简单理解为音视频信息,但实际上下层还有还有两层封装,将封装解码后可以获取到音视频流的编码数据。
9、视频直播传输协议2:HTTP-FLV
HTTP-FLV协议,从名字上就可以明显看出是通过HTTP协议来传输FLV封装格式的一种协议。
FLV是Flash Video的简写,是一种文件体积小,适合在网络上传输的封包方式。FlV的视频编码格式通常是H.264,音频编码是ACC或MP3。
HTTP-FLV在直播中是通过走HTTP长连接的方式,通过分块传输向请求端传递FLV封包数据。
在直播中,我们通过HTTP-FLV协议的拉流地址可以拉取到一段chunked数据。
打开文件后可以读取到16进制的文件流,通过和FLV包结构对比,可以发现这些数据就是我们需要的FLV数据。
首先开头是头部信息: 464C56转换ASCII码后是FLV三个字符,01指的是版本号,05转换为2进制后第6位和第8位分别代表是否存在音频和视频,09代表头部长度占了几个字节。
后续就是正式的音视频数据: 是通过一个个的FLV TAG进行封装,每一个TAG也有头部信息,标注这个TAG是音频信息、视频信息还是脚本信息。我们通过解析TAG就可以分别提取音视频的压缩编码信息。
FLV这一种格式在video中并不是原生支持的,我们要播放这一种格式的封包格式需要通过MSE对影视片的压缩编码信息进行解码,因此需要浏览器能够支持MSE这一API。由于HTTP-FLV的传输是通过长连接传输文件流的形式,需要浏览器支持Stream IO或者fetch,对于浏览器的兼容性要求会比较高。
FLV在延迟问题上相比切片播放的HLS会好很多,目前看来FLV的延迟主要是受编码时设置的GOP长度的影响。
这边简单介绍一下GOP: 在H.264视频编码的过程中,会生成三种帧类型:I帧、B帧和P帧。I帧就是我们通常说的关键帧,关键帧内包括了完整的帧内信息,可以直接作为其他帧的参考帧。B帧和P帧为了将数据压缩得更小,需要由其他帧推断出帧内的信息。因此两个I帧之间的时长也可以被视作最小的视频播放片段时长。从视频推送的稳定性考虑,我们也要求主播将关键帧间隔设置为定长,通常是1-3秒,因此除去其他因素,我们的直播在播放时也会产生1-3秒的延时。
10、视频直播传输协议3:RTMP
RTMP协议实际可以与HTTP-FLV协议归做同一种类型。
他们的封包格式都是FlV,但HTTP-FLV使用的传输协议是HTTP,RTMP拉流使用RTMP作为传输协议。
RTMP是Adobe公司基于TCP做的一套实时消息传输协议,经常与Flash播放器匹配使用。
RTMP协议的优缺点非常明显。
RTMP协议的优点主要是:
- 1)首先和HTTP-FLV一样,延迟比较低;
- 2)其次它的稳定性非常好,适合长时间播放(由于播放时借用了Flash player强大的功能,即使开多路流同时播放也能保证页面不出现卡顿,很适合监控等场景)。
但是Flash player目前在web端属于墙倒众人推的境地,主流浏览器渐渐都表示不再支持Flash player插件,在MAC上使用能够立刻将电脑变成烧烤用的铁板,资源消耗很大。在移动端H5基本属于完全不支持的状态,兼容性是它最大的问题。
11、视频直播传输协议4:MPEG-DASH
MPEG-DASH这一协议属于新兴势力,和HLS一样,都是通过切片视频的方式进行播放。
他产生的背景是早期各大公司都自己搞自己的一套协议。比如苹果搞了HLS、微软搞了 MSS、Adobe还搞了HDS,这样使用者需要在多套协议封装的兼容问题上痛苦不堪。
于是大佬们凑到一起,将之前各个公司的流媒体协议方案做了一个整合,搞了一个新的协议。
由于同为切片视频播放的协议,DASH优劣势和HLS类似,可以支持切片之间多视频码率、多音轨的切换,比较适合点播业务,在直播中还是会有延时较长的问题。
12、如何选择最优的视频直播传输协议
视频直播协议选择非常关键的两点,在前文都已经有提到了,即低延时和更优的兼容性。
首先从延时角度考虑: 不考虑云端转码以及上下行的消耗,HLS和MPEG-DASH通过将切片时长减短,延时在10秒左右;RTMP和FLV理论上延时相当,在2-3秒。因此在延时方面HLS ≈ DASH > RTMP ≈ FLV。
从兼容性角度考虑: HLS > FLV > RTMP,DASH由于一些项目历史原因,并且定位和HLS重复了,暂时没有对其兼容性做一个详尽的测试,被推出了选择的考虑范围。
综上所述: 我们可以通过动态判断环境的方式,选择当前环境下可用的最低延迟的协议。大致的策略就是优先使用HTTP-FLV,使用HLS作为兜底,在一些特殊需求场景下通过手动配置的方式切换为RTMP。
对于HLS和HTTP-FLV: 我们可以直接使用 hls.js 和 flv.js 做做解码播放,这两个库内部都是通过MSE做的解码。首先根据视频封装格式提取出对应的音视频chunk数据,在MediaSource中分别对音频和视频创建SourceBuffer,将音视频的编码数据喂给SourceBuffer后SourceBuffer内部会处理完剩下的解码和音视频对齐工作,最后MediaSource将Video标签中的src替换成MediaSource 对象进行播放。
在判断播放环境时我们可以参照flv.js内部的判断方式,通过调用MSE判断方法和模拟请求的方式判断MSE和StreamIO是否可用:
// 判断MediaSource是否被浏览器支持,H.264视频编码和Acc音频编码是否能够被支持解码
window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
如果FLV播放不被支持的情况下: 需要降级到HLS,这时候需要判断浏览器环境是否在移动端,移动端通常不需要 hls.js 通过MSE解码的方式进行播放,直接将M3U8的地址交给video的src即可。如果是PC端则判断MSE是否可用,如果可用就使用hls.js解码播放。
这些判读可以在自己的逻辑里提前判断后去拉取对应解码库的CDN,而不是等待三方库加载完成后使用三方库内部的方法判断,这样在选择解码库时就可以不把所有的库都拉下来,提高加载速度。
13、同层播放如何解决
电商直播需要观众操作和互动的部分比起传统的直播更加多,因此产品设计的时候很多的功能模块会悬浮在直播视频上方减少占用的空间。这个时候就会遇到一个移动端播放器的老大难问题——同层播放。
同层播放问题: 是指在移动端H5页面中,一些浏览器内核为了提升用户体验,将video标签被劫持替换为native播放器,导致其他元素无法覆盖于播放器之上。
比如我们想要在直播间播放器上方增加聊天窗口,将聊天窗口通过绝对定位提升z-index置于播放器上方,在PC中测试完全正常。但在移动端的一些浏览器中,video被替换成了native播放器,native的元素层级高于我们的普通元素,导致聊天窗口实际显示的时候在播放器下方。
要解决这个问题,首先要分多个场景。
首先在iOS系统中: 正常情况下video标签会自动被全屏播放,但iOS10以上已经原生提供了video的同层属性,我们在video标签上增加playsinline/webkit-playsinline可以解决iOS系统中大部分浏览器的同层问题,剩下的低系统版本的浏览器以及一些APP内的webview容器(譬如微博),用上面提的属性并不管用,调用三方库iphone-inline-video可以解决大部分剩余问题。
在Android端: 大部分腾讯系的APP内置的webview容器用的都是X5内核,X5内核会将video替换成原生定制的播放器已便于增强一些功能。X5也提供了一套同层的方案(该方案官方文档链接已无法打开),给video标签写入X5同层属性也可以在X5内核中实现内联播放。不过X5的同层属性在各个X5版本中表现都不太一样(比如低版本X5中需要使用X5全屏播放模式才能保证MSE播放的视频同层生效),需要注意区分版本。
在蘑菇街App中,目前集成的X5内核版本比较老,在使用MSE的情况下会导致X5同层参数不生效。但如果集成新版本的X5内核,需要对大量的线上页面做回归测试,成本比较高,因此提供了一套折中的解决方案。通过在页面URL中增加一个开关参数,容器读取到参数以后会将X5内核降级为系统原生的浏览器内核,这样可以在解决浏览器视频同层问题的同时也将内核变动的影响范围控制在单个页面当中。
作者:JackJiang