HTML5 直播協議之 WebSocket 和 MSE fmp4


當前為了滿足比較火熱的移動 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 協議中的那些概念都不復存在, 和 HTTP 的唯一關聯是使用 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 自己的 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM