H5 直播技术起航
作者:京东科技 吴磊
音视频基本概念
视频格式就是通常所说的.mp4
,.flv
,.ogv
,.webm
等。简单来说,它其实就是一个盒子,用来将实际的视频流以一定的顺序放入,确保播放的有序和完整性。
视频压缩格式和视频格式具体的区别就是,它是将原始的视频码流变为可用的数字编码。因为,原始的视频流非常大,打个比方就是,你直接使用手机录音,你会发现你几分钟的音频会比市面上出现的 MP3 音频大小大很多,这就是压缩格式起的主要作用。
首先,由原始数码设备提供相关的数字信号流,然后经由视频压缩算法,大幅度的减少流的大小,然后交给视频盒子,打上相应的 dts,pts 字段,最终生成可用的视频文件。
DTS(Decoding Time Stamp):即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据。
PTS(Presentation Time Stamp):即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。
视频编码
视频实际上就是一帧一帧的图片,拼接起来进行播放而已。而图片本身也可以进行相关的压缩,比如去除重复像素,合并像素块等等。不过,还有另外一种压缩方法就是,运动估计和运动补偿压缩,因为相邻图片一定会有一大块是相似的,所以,为了解决这个问题,可以在不同图片之间进行去重。
所以,总的来说,常用的编码方式分为三种:
变换编码:消除图像的帧内冗余
运动估计和运动补偿:消除帧间冗余
熵编码:提高压缩效率
熵编码即编码过程中按熵原理不丢失任何信息的编码。信息熵为信源的平均信息量(不确定性的度量)。常见的熵编码有:香农(Shannon)编码、哈夫曼(Huffman)编码和算术编码(arithmetic coding)。
直播
现在,常用的直播协议有 RTMP,HLS,HTTP-FLV。最常用的还是 HLS 协议,因为支持度高,技术简单,但是延迟非常严重。这对一些对实时性比较高的场景,比如运动赛事直播来说非常的不友好。这里来细分的看一下每个协议。
协议对比
HLS
HLS 全称是 HTTP Live Streaming。这是 Apple 提出的直播流协议。
HLS 由两部分构成,一个是.m3u8
文件,一个是.ts
视频文件(TS 是视频文件格式的一种)。整个过程是,浏览器会首先去请求.m3u8
的索引文件,然后解析m3u8
,找出对应的.ts
文件链接,并开始下载。
他的使用方式为:
直接可以将m3u8
写进src
中,然后交由浏览器自己去解析。当然也可以采取fetch
来手动解析并获取相关文件。HLS 详细版的内容比上面的简版多了一个playlist
,也可以叫做master
。在master
中,会根据网络段实现设置好不同的 m3u8 文件,比如,3G/4G/wifi 网速等。比如,一个 master 文件中为:
大家只要关注BANDWIDTH
(带宽)字段,其他的看一下字段内容大致就清楚了。假如这里选择high.m3u8
文件,那么,里面内容为:
注意,其中以ts
结尾的链接就是在直播中真正需要播放的视频文件。该第二级的m3u8
文件也可以叫做media
文件。该文件,其实有三种类型:
live playlist: 动态列表。顾名思义,该列表是动态变化的,里面的 ts 文件会实时更新,并且过期的 ts 索引会被删除。默认,情况下都是使用动态列表。
2.event playlist: 静态列表。它和动态列表主要区别就是,原来的 ts 文件索引不会被删除,该列表是不断更新,而且文件大小会逐渐增大。它会在文件中,直接添加 #EXT-X-PLAYLIST-TYPE:EVENT 作为标识。
3.VOD playlist: 全量列表。它就是将所有的 ts 文件都列在 list 当中。如果,使用该列表,就和播放一整个视频没有啥区别了。它是使用 #EXT-X-ENDLIST 表示文件结尾。
HLS 缺陷
HLS 缺陷就是延迟性太大了。HLS 中的延时包括:
TCP 握手
m3u8 文件下载
m3u8 文件下所有 ts 文件下载
这里先假设每个 ts 文件播放时长为 5s,每个 m3u8 最多可携带的 ts 文件数为 3~8。那么最大的延迟则为 40s。注意,只有当一个m3u8
文件下所有的 ts 文件下载完后,才能开始播放。这里还不包括 TCP 握手,DNS 解析,m3u8 文件下载。所以,HLS 总的延时是非常令人绝望的。
那解决办法有吗? 有,很简单,要么减少每个 ts 文件播放时长,要么减少m3u8
的中包含 ts 的数量。如果超过平衡点,那么每次请求新的 m3u8 文件时,都会加上一定的延时,所以,这里需要根据业务指定合适的策略。
RTMP
RTMP 全称为:Real-Time Messaging Protocol
。它是基于FLV
格式进行开发的,所以,第一反应就是,又不能用了!!!
是的,在现在设备中,由于 FLV 的不支持,基本上 RTMP 协议在 Web 中,根本用不到。不过,由于MSE
(MediaSource Extensions)的出现,在 Web 上直接接入 RTMP 也不是不可能的。基本思路是根据 WebSocket 直接建立长连接进行数据的交流和监听。RTMP 协议根据不同的套层,也可以分为:
纯 RTMP: 直接通过 TCP 连接,端口为 1935
RTMPS: RTMP + TLS/SSL,用于安全性的交流。
RTMPE: RTMP + encryption。在 RTMP 原始协议上使用,Adobe 自身的加密方法
RTMPT: RTMP + HTTP。使用 HTTP 的方式来包裹 RTMP 流,延迟性比较大。
RTMFP: RMPT + UDP。该协议常常用于 P2P 的场景中,针对延时有变态的要求。
RTMP 内部是借由 TCP 长连接协议传输相关数据,所以,它的延时性非常低。并且,该协议灵活性非常好(所以,也很复杂),它可以根据 message stream ID 传输数据,也可以根据 chunk stream ID 传递数据。两者都可以起到流的划分作用。流的内容也主要分为:视频,音频,相关协议包等。
HTTP-FLV
该协议和 RTMP 比起来其实差别不大,只是落地部分有些不同:
RTMP 是直接将流的传输架在 RTMP 协议之上,而 HTTP-FLV 是在 RTMP 和客户端之间套了一层转码的过程,由于,每个 FLV 文件是通过 HTTP 的方式获取的,所以,它通过抓包得出的协议头需要使用 chunked 编码。
它用起来比较方便,不过后端实现的难度和直接使用 RTMP 来说还是比较大的。
前端音视频流
由于各大浏览器的对 FLV 的围追堵截,导致 FLV 在浏览器的生存状况堪忧,但是,FLV 凭借其格式简单,处理效率高的特点,使各大视频后台的开发者都舍不得弃用,如果一旦更改的话,就需要对现有视频进行转码,比如变为 MP4,这样不仅在播放,而且在流处理来说都有点重的让人无法接受。而 MSE 的出现,彻底解决了这个尴尬点,能够让前端能够自定义来实现一个 Web 播放器,确实完美。(不过,苹果觉得没这必要,所以,在 IOS 上无法实现。)
MSE
MSE 全称就是Media Source Extensions
。它是一套处理视频流技术的简称,里面包括了一系列 API:Media Source
,Source Buffer
等。在没有 MSE 出现之前,前端对 video 的操作,仅仅局限在对视频文件的操作,而并不能对视频流做任何相关的操作。现在 MSE 提供了一系列的接口,使开发者可以直接提供 media stream。
来看一下 MSE 是如何完成基本流的处理的。
上面这个例子可以简单理解为:
第一步,通过异步拉取数据。
第二步,通过 MediaSource 处理数据。
第三步,将数据流交给 audio/video 标签进行播放。
而中间传递的数据都是通过Buffer
的形式来进行传递的。
中间有个需要注意的点,MS 的实例通过URL.createObjectURL()
创建的 url 并不会同步连接到 video.src。换句话说,URL.createObjectURL()
只是将底层的流(MS)和 video.src 连接中间者,一旦两者连接到一起之后,该对象就没用了。
MediaSource
MediaSource 是 Media Source Extensions API 表示媒体资源 HTMLMediaElement 对象的接口。MediaSource 对象可以附着在 HTMLMediaElement 在客户端进行播放。
MS(MediaSource) 只是一系列视频流的管理工具,它可以将音视频流完整的暴露给 Web 开发者来进行相关的操作和处理。所以,它本身不会造成过度的复杂性。
MS 整个只挂载了 4 个属性,3 个方法和 1 个静态测试方法。
4 个属性:
sourceBuffers: 获得当前创建出来的 SourceBuffer
activeSourceBuffers: 获得当前正处于激活状态的 SourceBuffer
readyState: 返回当前 MS 的状态,比如:
closed
,open
,ended
.
duration: 设置当前 MS 的播放时长。
3 个方法:
addSourceBuffer(): 根据给定的 MIME 创建指定类型的 SourceBuffer
removeSourceBuffer(): 将 MS 上指定的 SourceBuffer 移除。
endOfStream(): 直接终止该流
1 个静态测试方法:
isTypeSupported(): 主要用来判断指定的音频的 MIME 是否支持。
最基本的就是使用addSourceBuffer
该方法来获得指定的 SourceBuffer。
资料:https://developer.mozilla.org/zh-CN/docs/Web/API/MediaSource
SourceBuffer
SourceBuffer 接口表示通过 MediaSource 对象传递到 HTMLMediaElement 并播放的媒体分块。它可以由一个或者多个媒体片段组成。
一旦利用 MS 创建好 SourceBuffer 之后,后续的工作就是将额外获得的流放进 Buffer 里面进行播放即可。所以,SourceBuffer 提供两个最基本的操作appendBuffer
,remove
。之后,就可以通过appendBuffer
直接将 ArrayBuffer 放进去即可。
其中,SourceBuffer 还提供了一个应急的方法abort()
如果该流发生问题的话可以直接将指定的流给废弃掉。
音视频的 ArrayBuffer 通过 MediaSource 和 SourceBuffer 的处理直接将<audio>
&&<video>
接入。然后,就可以实现正常播放的效果。
资料:https://developer.mozilla.org/zh-CN/docs/Web/API/SourceBuffer
基于 flv.js 实现 H5 直播
flv.js 简介
flv.js 是来自 Bilibli 的开源项目。它解析 FLV 文件传给原生 HTML5 Video 标签播放音视频数据,使浏览器在不借助 Flash 的情况下播放 FLV 成为可能。
flv.js 优势
由于浏览器对原生 Video 标签采用了硬件加速,性能很好,支持高清。
同时支持录播和直播
去掉对 Flash 的依赖
flv.js 限制
FLV 里所包含的视频编码必须是 H.264,音频编码必须是 AAC 或 MP3, IE11 和 Edge 浏览器不支持 MP3 音频编码,所以 FLV 里采用的编码最好是 H.264+AAC,这个让音视频服务兼容不是问题。
对于录播,依赖 原生 HTML5 Video 标签 和 Media Source Extensions API
对于直播,依赖录播所需要的播放技术,同时依赖 HTTP FLV 或者 WebSocket 中的一种协议来传输 FLV。其中 HTTP FLV 需通过流式 IO 去拉取数据,支持流式 IO 的有 fetch 或者 stream
由于依赖 Media Source Extensions,目前所有 iOS 和 Android4.4.4 以下里的浏览器都不支持,也就是说目前对于移动端 flv.js 是有局限性的。
flv.js 原理
flv.js 只做了一件事,在获取到 FLV 格式的音视频数据后通过原生的 JS 去解码 FLV 数据,再通过 Media Source Extensions API 传递给原生 HTML5 Video 标签。(HTML5 原生仅支持播放 mp4/webm 格式,不支持 FLV)
vue + flv.js
flv.js 资料:https://www.npmjs.com/package/flv.js
参考资料:
https://segmentfault.com/a/1190000008916399
https://segmentfault.com/a/1190000010440054
https://blog.csdn.net/An1090239782/article/details/108972491
https://zhuanlan.zhihu.com/p/47773064
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/96269b3036857daea6af06bb9】。文章转载请联系作者。
评论