做者 | 劉博(又拍雲多媒體開發工程師)前端
當前爲了知足比較火熱的移動 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接口