WebSocket+MSE——HTML5 直播技术解析

做者 | 刘博(又拍云多媒体开发工程师)前端

当前为了知足比较火热的移动 Web 端直播需求,一系列的 HTML5 直播技术迅速的发展起来。git

常见的可用于 HTML5 的直播技术有 HLS、WebSocket 与 WebRTC。今天我向你们介绍WebSocket 与 MSE 相关的技术要点,并在最后经过一个实例来展现具体用法。github

文章大纲

  • WebSocket 协议介绍golang

  • WebSocket Client/Server API介绍web

  • MSE 介绍后端

  • fMP4 介绍浏览器

  • Demo 展现缓存

WebSocket

一般的 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,能够绕过大多数防火墙的限制。
图片描述

WebSocket 握手

为了更方便地部署新协议,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 JavaScript API

目前主流的浏览器提供了 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

在介绍 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 上。

Browser Support

经过 caniuse 来检查是否浏览器支持状况。
图片描述

经过 MediaSource.isTypeSupported() 能够进一步地检查 codec MIME 类型是否支持。

fMP4

比较经常使用的视频封装格式有 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 VS non-fragment 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

从高层次上看,MSE 提供了

  • 一套 JavaScript API 来构建 media streams

  • 一个拼接和缓存模型

  • 识别一些 byte 流类型

  • WebM

  • ISO Base Media File Format

  • MPEG-2 Transport Streams

MSE 内部结构

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 TIME

最后阶段,展现两个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 的实现

Refs

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接口

相关文章
相关标签/搜索