HTML5 直播協議之 WebSocket 和 MSE

當前爲了知足比較火熱的移動 Web 端直播需求, 一系列的 HTML5 直播技術迅速的發展了起來.前端

常見的可用於 HTML5 的直播技術有 HLS, WebSocket 與 WebRTC. 今天我要向你們介紹一下 WebSocket 與 MSE 相關的內容, 並在最後經過一個實際的例子, 來展現其具體的用法.golang

大綱

  • WebSocket 協議介紹.
  • WebSocket Client/Server API 介紹.
  • MSE 介紹.
  • fMP4 介紹.
  • Demo 展現.

WebSocket

一般的 Web 應用都是圍繞着 HTTP 的請求/響應模型而構建的. 全部的 HTTP 通訊都是經過客戶端來控制的, 都是由客戶端向服務器發出一個請求, 服務器接收和處理完畢後再返回結果給客戶端, 客戶端再將數據展示出來. 這種模式不能知足實時應用的需求, 因而出現了 SSE, Comet 等 「服務器推」 的長鏈接技術.web

WebSocket 是直接基於 TCP 鏈接之上的通訊協議, 能夠在單個 TCP 鏈接上進行全雙工的通訊. WebSocket 在 2011 年被 IETF 定爲標準 RFC 6455, 並被 RFC 7936 所補充規範, WebSocket API 被 W3C 定爲標準.瀏覽器

WebSocket 是獨立的建立在 TCP 上的協議, HTTP 協議中的那些概念都不復存在, 和 HTTP 的惟一關聯是使用 HTTP 協議的 101 狀態碼進行協議切換, 使用的 TCP 端口是 80, 能夠用於繞過大多數防火牆的限制.緩存

WebSocket 握手

爲了更方便地部署新協議,HTTP/1.1 引入了 Upgrade 機制, 它使得客戶端和服務端之間能夠藉助已有的 HTTP 語法升級到其它協議. 這個機制在 RFC7230 的 6.7 Upgrade ) 一節中有詳細描述.服務器

要發起 HTTP/1.1 協議升級,客戶端必須在請求頭部中指定這兩個字段:websocket

Connection: Upgrade
Upgrade: protocol-name[/protocol-version]

若是服務端贊成升級, 那麼須要這樣響應:app

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 上完成.socket

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 本身的 golang.org/x/net/websocket , 能夠很是方便的與 net/http 一塊兒使用.

能夠將 websocket 的 handler function 經過 websocket.Handler 轉換成 http.Handler , 這樣就能夠跟 net/http 庫一塊兒使用了.

而後經過 websocket.Message.Receive 來接收數據, 經過 websocket.Message.Send 來發送數據.

具體代碼能夠看下面的 Demo 部分.

MSE

在介紹 MSE 以前, 咱們先看看 HTML5 <audio> 和 <video> 有哪些限制.

HTML5

標籤的限制

  • 不支持流.
  • 不支持 DRM 和加密.
  • 很難自定義控制, 以及保持跨瀏覽器的一致性.
  • 編解碼和封裝在不一樣瀏覽器支持不一樣.

MSE 是解決 HTML5 的流問題.

Media Source Extensions (MSE) 是一個主流瀏覽器支持的新的 Web API. MSE 是一個 W3C 標準, 容許 JavaScript 動態的構建 <video> 和 <audio> 的媒體流. 他定義了對象, 容許 JavaScript 傳輸媒體流片斷到一個 HTMLMediaElement.

經過使用 MSE, 你能夠動態地修改媒體流而不須要任何的插件. 這讓前端 JavaScript 能夠作更多的事情, 咱們能夠在 JavaScript 進行轉封裝, 處理, 甚至轉碼.

雖然 MSE 不能讓流直接傳輸到 media tags 上, 可是 MSE 提供了構建跨瀏覽器播放器的核心技術, 讓瀏覽器經過 JavaScript API 來推音視頻到 media tags 上.

如今每一個客戶端平臺都開始逐步開放流媒體相關的 API: Flash 平臺有 Netstream, Android 平臺有 Media Codec API, 而 Web 上對應的就是標準的 MSE. 由此能夠看出, 將來的趨勢是在客戶端能夠作愈來愈多的事情.

Browser Support

經過 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 vs non-fragment mp4

下面一個 fragment mp4 文件經過 mp4parser 分析後的截圖

下面一個 non-fragment mp4 文件經過 mp4parser 分析後的截圖

Apple 在今年的 WWDC 大會上宣佈會在 iOS 10, tvOS, macOS 的 HLS 中支持 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

  • MSE Vod Demo 展現利用 MSE 和 WebSocket 實現一個點播服務.
  • MSE Live Demo 展現利用 MSE 和 WebSocket 實現一個直播服務.

MSE VOD Demo

Your browser does not support the video tag.

MSE Live Demo

Your browser does not support the video tag.

Refs

WebSocket

 

來自:http://akagi201.org/post/websocket-mse/

相關文章
相關標籤/搜索