做者 | 刘博(又拍云多媒体开发工程师)前端
当前为了知足比较火热的移动 Web 端直播需求,一系列的 HTML5 直播技术迅速的发展起来。git
常见的可用于 HTML5 的直播技术有 HLS、WebSocket 与 WebRTC。今天我向你们介绍WebSocket 与 MSE 相关的技术要点,并在最后经过一个实例来展现具体用法。github
WebSocket 协议介绍golang
WebSocket Client/Server API介绍web
MSE 介绍后端
fMP4 介绍浏览器
Demo 展现缓存
一般的 Web 应用都是围绕着 HTTP 的请求/响应模型构建的。全部的 HTTP 通讯都经过客户端来控制,由客户端向服务器发出一个请求,服务器接收和处理完毕后再返回结果给客户端,客户端将数据展示出来。因为这种模式不能知足实时应用需求,因而出现了 SSE、Comet 等 "服务器推" 的长链接技术。服务器
WebSocket 是基于 TCP 链接之上的通讯协议,能够在单个 TCP 链接上进行全双工的通讯。WebSocket 在 2011 年被 IETF 定为标准 RFC 6455,并被 RFC 7936 补充规范,WebSocket API 被 W3C 定为标准。websocket
WebSocket 是独立地建立在 TCP 上的协议,HTTP 协议中的那些概念都和 WebSocket 没有关联,惟一关联的是使用 HTTP 协议的 101 状态码进行协议切换时,使用的 TCP 端口是 80,能够绕过大多数防火墙的限制。
为了更方便地部署新协议,HTTP/1.1 引入了 Upgrade 机制,使得客户端和服务端之间能够借助已有的HTTP语法升级到其它协议。这个机制在 RFC7230 的 6.7 Upgrade 一节中有详细描述。
要发起 HTTP/1.1 协议升级,客户端必须在请求头部中指定这两个字段 ▽
> Connection: Upgrade Upgrade: protocol-name[/protocol-version]
若是服务端赞成升级,那么须要这样响应 ▽
> HTTP/1.1 101 Switching Protocols Connection: upgrade Upgrade: protocol-name[/protocol-version] [... data defined by new protocol ...]
能够看到,HTTP Upgrade 响应的状态码是 101,而且响应正文可使用新协议定义的数据格式。
WebSocket 握手就利用了这种 HTTP Upgrade 机制。一旦握手完成,后续数据传输直接在 TCP 上完成。
目前主流的浏览器提供了 WebSocket 的 API 接口,能够发送消息(文本或者二进制)给服务器,而且接收事件驱动的响应数据。
Step1. 检查浏览器是否支持 WebSocket
> if(window.WebSocket) { // WebSocket代码 }
Step2. 创建链接
> var ws = new WebSocket('ws://localhost:8327');
Step3. 注册回调函数以及收发数据
分别注册 WebSocket 对象的 onopen、onclose、onerror 以及 onmessage 回调函数。
经过ws.send()来进行发送数据,这里不只能够发送字符串,也能够发送 Blob 或 ArrayBuffer 类型的数据。
若是接收的是二进制数据,须要将链接对象的格式设为 blob 或 arraybuffer。
ws.binaryType = 'arraybuffer'; WebSocket Golang API
服务器端 WebSocket 库我推荐使用 Google 本身的 http://golang.org/x/net/webso...,能够很是方便的与 net/http 一块儿使用。也能够将 WebSocket 的 handler function 经过 websocket.Handler转换成 http.Handler,这样就能够跟 net/http 库一块儿使用了。
而后经过 websocket.Message.Receive 来接收数据,经过 websocket.Message.Send 来发送数据。
具体代码能够看下面的 Demo 部分。
在介绍 MSE 以前,咱们先看看 HTML5<audio>和<video> 有哪些限制。
HTML5<audio> 和 <video> 标签的限制
不支持流
不支持 DRM 和加密
很难自定义控制, 以及保持跨浏览器的一致性
编解码和封装在不一样浏览器支持不一样
MSE 是解决 HTML5 的流问题。
Media Source Extensions(MSE)是 Chrome、Safari、Edge 等主流浏览器支持的一个新的Web API。MSE 是一个 W3C 标准,容许 JavaScript 动态构建 <video> 和 <audio> 的媒体流。它定义了对象,容许 JavaScript 传输媒体流片断到一个 HTMLMediaElement。
经过使用 MSE,你能够动态地修改媒体流而不须要任何插件。这让前端JavaScript能够作更多的事情—— 在 JavaScript 进行转封装、处理,甚至转码。
虽然 MSE 不能让流直接传输到 media tags 上,可是 MSE 提供了构建跨浏览器播放器的核心技术,让浏览器经过JavaScript API来推音视频到 media tags 上。
经过 caniuse 来检查是否浏览器支持状况。
经过 MediaSource.isTypeSupported() 能够进一步地检查 codec MIME 类型是否支持。
比较经常使用的视频封装格式有 WebM 和 fMP4。
WebM 和 WebP 是两个姊妹项目,都是由 Google 赞助的。因为 WebM 是基于 Matroska 的容器格式,天生是流式的,很适合用在流媒体领域里。
下面着重介绍一下 fMP4 格式。
咱们都知道 MP4 是由一系列的 Boxes 组成的。普通的 MP4 的是嵌套结构的,客户端必需要从头加载一个 MP4 文件,才可以完整播放,不能从中间一段开始播放。
而 fMP4 由一系列的片断组成,若是服务器支持 byte-range 请求,那么,这些片断能够独立的进行请求到客户端进行播放,而不须要加载整个文件。
为了更加形象的说明这一点,下面我介绍几个经常使用的分析 MP4 文件的工具。
gpac,原名 mp4box,是一个媒体开发框架,在其源码下有大量的媒体分析工具,可使用testapps;
mp4box.js,是 mp4box 的 Javascript 版本;
bento4,一个专门用于 MP4 的分析工具;
mp4parser,在线 MP4 文件分析工具。
下面是一个 fragment mp4 文件经过 mp4parser(Online MPEG4 Parser )分析后的截图 ▽
下面是一个 non-fragment mp4 文件经过 mp4parser 分析后的截图 ▽
咱们能够看到 non-fragment mp4 的最顶层 box 类型很是少,而 fragment mp4 是由一段一段的 moof+mdat 组成的,它们已经包含了足够的 metadata 信息与数据, 能够直接 seek 到这个位置开始播放。也就是说 fMP4 是一个流式的封装格式,这样更适合在网络中进行流式传输,而不须要依赖文件头的metadata。
Apple在WWDC 2016 大会上宣布会在 iOS 十、tvOS、macO S的 HLS 中支持 fMP4,可见fMP4 的前景很是的好。
值得一提的是,fMP四、CMAF、ISOBMFF 其实都是相似的东西。
从高层次上看,MSE 提供了
一套 JavaScript API 来构建 media streams
一个拼接和缓存模型
识别一些 byte 流类型
WebM
ISO Base Media File Format
MPEG-2 Transport Streams
MSE 自己的设计是不依赖任务特定的编解码和容器格式的,可是不一样的浏览器支持程度是不同的。
能够经过传递一个 MIME 类型的字符串到静态方法:
`> MediaSource.isTypeSupported`
来检查。好比 ▽
MediaSource.isTypeSupported('audio/mp3'); // false MediaSource.isTypeSupported('video/mp4'); // true MediaSource.isTypeSupported('video/mp4; codecs="avc1.4D4028, mp4a.40.2"'); // true
获取 Codec MIME string 的方法能够经过在线的 mp4info,或者使用命令行 mp4info test.mp4 | grep Codecs,能够获得相似以下结果 ▽
> mp4info fmp4.mp4| grep Codec Codecs String: mp4a.40.2 Codecs String: avc1.42E01E
当前,H.264 + AAC 的 MP4 容器在全部的浏览器都支持。
普通的 MP4 文件是不能和 MSE 一块儿使用的, 须要将 MP4 进行 fragment 化。
检查一个 MP4 是否已经 fragment 的方法 ▽
> mp4dump test.mp4 | grep "\[m"
若是是non-fragment会显示以下信息 ▽
> mp4dump nfmp4.mp4 | grep "\[m" [mdat] size=8+50873 [moov] size=8+7804 [mvhd] size=12+96 [mdia] size=8+3335 [mdhd] size=12+20 [minf] size=8+3250 [mdia] size=8+3975 [mdhd] size=12+20 [minf] size=8+3890 [mp4a] size=8+82 [meta] size=12+78
若是已经 fragment,会显示以下的相似信息 ▽
> mp4dump fmp4.mp4 | grep "\[m" | head -n 30 [moov] size=8+1871 [mvhd] size=12+96 [mdia] size=8+312 [mdhd] size=12+20 [minf] size=8+219 [mp4a] size=8+67 [mdia] size=8+371 [mdhd] size=12+20 [minf] size=8+278 [mdia] size=8+248 [mdhd] size=12+20 [minf] size=8+156 [mdia] size=8+248 [mdhd] size=12+20 [minf] size=8+156 [mvex] size=8+144 [mehd] size=12+4 [moof] size=8+600 [mfhd] size=12+4 [mdat] size=8+138679 [moof] size=8+536 [mfhd] size=12+4 [mdat] size=8+24490 [moof] size=8+592 [mfhd] size=12+4 [mdat] size=8+14444 [moof] size=8+312 [mfhd] size=12+4 [mdat] size=8+1840 [moof] size=8+600
把一个 non-fragment MP4 转换成 fragment MP4。
可使用 FFmpeg 的 -movflags 来转换。
对于原始文件为非 MP4 文件 ▽
> ffmpeg -i trailer_1080p.mov -c:v copy -c:a copy -movflags frag_keyframe+empty_moov bunny_fragmented.mp4
对于原始文件已是 MP4 文件 ▽
> ffmpeg -i non_fragmented.mp4 -movflags frag_keyframe+empty_moov fragmented.mp4
或者使用 mp4fragment ▽
> mp4fragment input.mp4 output.mp4
最后阶段,展现两个demo,分别是 MSE Vod Demo、MSE Live Demo
MSE Vod Demo
展现利用 MSE 和 WebSocket 实现一个点播服务
后端读取一个 fMP4 文件,经过 WebSocket 发送给 MSE,进行播放
展现利用 MSE 和 WebSocket 实现一个直播服务
后端代理一条 HTTP-FLV 直播流,经过 WebSocket 发送给 MSE,进行播放
前端 MSE 部分作了不少工做, 包括将 flv 实时转封装成了 fMP4,这里引用了 videojs-flow 的实现
WebSocket
rfc6455
HTTP Upgrade
WebSocket API
MDN WebSocket
videojs-flow
MSE
W3C
MDN MSE
HTML5 Codec MIME
又拍直播云是基于又拍云内容分发网络为直播应用提供超低延迟、高码率、高并发的整套从推流端到播放端的一站式解决方案。包括实时转码,实时录制,分发加速,水印,截图,秒级禁播,延时直播等功能。直播源站支持自主源站或又拍云源,为支持用户在不一样终端播放,支持 RTMP、HLS、HTTP-flv 播放输出。
详情了解:https://www.upyun.com/product...
推荐阅读:
无连麦,不直播,都在说的直播利器连麦互动究竟是啥?
技术干货|移动直播六大关键技术详解
又拍直播云SDK,自带美颜、滤镜、消噪、人声增益等功能
又拍直播云功能处理篇:转码、录制、视频水印、视频截图
又拍直播云功能基础篇:推流和拉流、多协议输出、多访问方式、回源端口自定义
又拍直播云功能高级篇:防盗链、秒级禁播、自动鉴黄、API接口