首頁  >  文章  >  web前端  >  WebSocket+MSE——HTML5 直播技術解析

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

巴扎黑
巴扎黑原創
2017-06-23 14:45:594303瀏覽

作者 | 劉博(又拍雲多媒體開發工程師)

目前為了滿足比較火熱的行動 Web 端直播需求,一系列的 HTML5 直播技術迅速的發展起來。

常見的可用於 HTML5 的直播技術有 HLS、WebSocket 與 WebRTC。今天我向大家介紹WebSocket 與 MSE 相關的技術要點,並在最後透過一個實例來展示具體用法。

文章大綱

  • WebSocket 協定介紹

  • WebSocket Client/Server API介紹

  • #MSE 介紹

  • fMP4 介紹

  • Demo 展示

##WebSocket

通常的Web 應用程式都是圍繞著HTTP 的請求/回應模型建構的。所有的 HTTP 通訊都透過客戶端來控制,由客戶端向伺服器發出一個請求,伺服器接收和處理完畢後再返回結果給客戶端,客戶端將資料展現出來。由於這種模式無法滿足即時應用需求,於是出現了 SSE、Comet 等 "伺服器推" 的長連線技術。

WebSocket 是基於 TCP 連線之上的通訊協議,可以在單一 TCP 連線上進行全雙工的通訊。 WebSocket 在 2011 年被 IETF 定為標準 RFC 6455,並被 RFC 7936 補充規範,WebSocket API 被 W3C 定為標準。

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 自己的 ,可以很方便的與net/http 一起使用。也可以將 WebSocket 的 handler function 透過 websocket.Handler轉換成 http.Handler,這樣就可以跟 net/http 函式庫一起使用了。

然後透過 websocket.Message.Receive 來接收數據,透過 websocket.Message.Send 來發送數據。

具體程式碼可以看下面的 Demo 部分。

MSE

在介紹 MSE 之前,我們先來看看 HTML5b97864c2e0ef2353a16c4d64c7734e92和39000f942b2545a5315c57fa3276f220 有哪些限制。

HTML5b97864c2e0ef2353a16c4d64c7734e92 和39000f942b2545a5315c57fa3276f220 標籤的限制

  • 不支援流

  • 不支援DRM 和加密

  • 很難自訂控制, 以及保持跨瀏覽器的一致性

  • #編解碼和封裝在不同瀏覽器支援不同

MSE 是解決HTML5 的流程問題。

Media Source Extensions(MSE)是 Chrome、Safari、Edge 等主流瀏覽器支援的一個新的Web API。 MSE 是一個 W3C 標準,允許 JavaScript 動態建構 39000f942b2545a5315c57fa3276f220 和 b97864c2e0ef2353a16c4d64c7734e92 的媒體串流。它定義了對象,允許 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 10、tvOS、macO S的 HLS 中支援 fMP4,可見fMP4 的前景非常的好。

值得一提的是,fMP4、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 播放输出。

详情了解:

推荐阅读:

无连麦,不直播,都在说的直播利器连麦互动到底是啥?
技术干货|移动直播六大关键技术详解
又拍直播云SDK,自带美颜、滤镜、消噪、人声增益等功能
又拍直播云功能处理篇:转码、录制、视频水印、视频截图
又拍直播云功能基础篇:推流和拉流、多协议输出、多访问方式、回源端口自定义
又拍直播云功能高级篇:防盗链、秒级禁播、自动鉴黄、API接口

以上是WebSocket+MSE——HTML5 直播技術解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn