H5 的直播協議和視頻監控方案

知乎:chapin:基於 H5 的直播協議和視頻監控方案

一、流媒體主要實現方式


對上圖的說明如下:

  1. 在設備層,需要以某種方式獲得碼流,以流協議的方式發送出去。最常用的方式是RTSP/RTP。流的可能獲取路徑爲:
    1). 設備直接暴露 RTSP 協議端點,並且發送標準碼流
    2). 設備 SDK 允許獲取標準碼流,需要自己以 RTSP 協議發送
    3). 設備 SDK 允許獲得解碼後的逐幀,需要直接編碼爲 H264,然後以 RTSP 發送
  2. 流媒體層通常需要引入專門的流媒體服務器,這類服務器能夠在內部進行各種流協議的轉換,可以解除客戶端對特定流協議的依賴
  3. 客戶端和服務器端的傳輸方式,可以有 TCP、HTTP、P2P(WebRTC)、WebSocket 等多種。其中
    1). 直接的 TCP 協議瀏覽器是不支持的,這意味着 RTSP/RTMP 等協議,在瀏覽器端必須要有插件纔可以使用
    2). WebSocket 通常配合 JSMpeg 或者 MSE 使用,由程序向 JSMpeg/MSE 不斷 Feed 視頻幀
  4. 客戶端解碼展示的技術主要有三類:
    1). 瀏覽器內置的解碼能力,主要通過 video 標籤,MSE 屬於此類
    2). JavaScript 軟解碼,主要是 JSMpeg、Broadway
    3). 插件機制,例如 Chrome 的 NaCl

二、流媒體技術

2.1 流媒體

所謂多媒體(Multimedia)是指多種內容形式 —— 文本、音頻、視頻、圖片、動畫等的組合。

所謂流媒體,就是指源源不斷的由提供者產生,並持續的被終端用戶接收、展示的多媒體,就像水流一樣。現實世界中的媒體,有些天生就是流式的,例如電視、廣播,另外一些則不是,例如書籍、CD。

流媒體技術(從傳遞媒體角度來看)可以作爲文件下載的替代品。

流媒體技術關注的是如何傳遞媒體,而不是如何編碼媒體,具體的實現就是各種流媒體協議。封裝後的媒體比特流(容器格式)由流媒體服務器遞送到流媒體客戶端。流媒體協議可能對底層容器格式、編碼格式有要求,也可能沒有任何要求。

2.2 直播

直播流(Live streaming)和靜態文件播放的關鍵差異:

  1. 點播的目標文件通常位於服務器上,具有一定的播放時長、文件大小。瀏覽器可以使用漸進式下載,一邊下載一邊播放
  2. 直播不存在播放起點、終點。它表現爲一種流的形式,源源不斷的從視頻採集源通過服務器,傳遞到客戶端
  3. 直播流通常是自適應的(adaptive),其碼率隨着客戶端可用帶寬的變化,可能變大、變小,以儘可能消除延遲

流媒體技術不但可以用於監控畫面預覽,也可以改善錄像播放的用戶體驗,比起簡單的靜態文件回放,流式回放具有以下優勢:

  1. 延遲相對較低,播放能夠儘快開始
  2. 自適應流可以避免卡頓

2.3 流協議

主流的用於承載視頻流的流媒體協議包括:

2.3.1 HLS 協議

HTTP 實時流(HTTP Live Streaming),由蘋果開發,基於 HTTP 協議

HLS 的工作原理是,把整個流劃分成一個個較小的文件,客戶端在建立流媒體會話後,基於HTTP 協議下載流片段並播放。客戶端可以從多個服務器(源)下載流。

在建立會話時,客戶端需要下載 Extended M3U (m3u8) 播放列表文件,其中包含了 MPEG-2 TS(Transport Stream)容器格式的視頻的列表。在播放完列表中的文件後,需要再次下載m3u8,如此循環

此協議在移動平臺上支持較好,目前的 Android、iOS 版本都支持

此協議的重要缺點是高延遲(5s以上通常),要做到低延遲會導致頻繁的緩衝(下載新片段)並對服務器造成壓力,不適合視頻監控

播放 HLS 流的 HTML 代碼片段:

<video src="http://movie.m3u8" height="329" width="480"></video>

2.3.2 RTMP 協議

實時消息協議(Real Time Messaging Protocol),由 Macromedia(Adobe)開發。此協議實時性很好,需要 Flash 插件才能在客戶端使用,但是Adobe已經打算在不久的將來放棄對Flash的支持了

有一個開源項目 HTML5 FLV Player ,它支持在沒有Flash插件的情況下,播放 Flash 的視頻格式 FLV。此項目依賴於 MSE,支持以下特性:

  1. 支持 H.264 + AAC/MP3 編碼的FLV容器格式的播放
  2. 分段(segmented)視頻播放
  3. 基於 HTTP 的 FLV 低延遲實時流播放
  4. 兼容主流瀏覽器
  5. 資源佔用低,可以使用客戶端的硬件加速

2.3.3 RTSP 協議

RTSP是一個實時傳輸流協議,是一個應用層的協議

通常說的RTSP包括RTSP協議、RTP協議、RTCP協議

對於這些協議的作用簡單的理解如下

  • RTSP協議:負責服務器與客戶端之間的請求與響應
  • RTP協議:負責傳輸媒體數據
  • RTCP協議:在RTP傳輸過程中提供傳輸信息

rtsp承載與rtp和rtcp之上,rtsp並不會發送媒體數據,而是使用rtp協議傳輸
rtp並沒有規定發送方式,可以選擇udp發送或者tcp發送

實時流協議(Real Time Streaming Protocol),由 RealNetworks 等公司開發。此協議負責控制通信端點(Endpoint)之間的媒體會話(media sessions) —— 例如播放、暫停、錄製。通常需要結合:實時傳輸協議(Real-time Transport Protocol)、實時控制協議(Real-time Control Protocol)來實現視頻流本身的傳遞

大部分瀏覽器沒有對 RTSP 提供原生的支持

2.3.4 MPEG-DASH

基於HTTP的動態自適應流(Dynamic Adaptive Streaming over HTTP),它類似於 HLS,也是把流切分爲很小的片段。DASH 爲支持爲每個片段提供多種碼率的版本,以滿足不同客戶帶寬

協議的客戶端根據自己的可用帶寬,選擇儘可能高(避免卡頓、重新緩衝)的碼率進行播放,並根據網絡狀況實時調整碼率

DASH 不限制編碼方式,你可以使用 H.265, H.264, VP9 等視頻編碼算法

Chrome 24+、Firefox 32+、Chrome for Android、IE 10+支持此格式

類似於 HLS 的高延遲問題也存在。

2.3.5 WebRTC 協議

WebRTC 是一整套 API,爲瀏覽器、移動應用提供實時通信(RealTime Communications)能力。它包含了流媒體協議的功能,但是不是以協議的方式暴露給開發者的

WebRTC 支持 Chrome 23+、Firefox 22+、Chrome for Android,提供 Java / Objective-C 綁定

WebRTC 主要有三個職責:

  1. 捕獲客戶端音視頻,對應接口 MediaStream(也就是 getUserMedia)
  2. 音視頻傳輸,對應接口 RTCPeerConnection
  3. 任意數據傳輸,對應接口 RTCDataChannel

WebRTC 內置了點對點的支持,也就是說流不一定需要經過服務器中轉

2.4 服務器端技術

視頻監控通常都是 CS 模式(而非P2P),在服務器端,你需要部署流媒體服務。

2.4.1 GStreamer

這是一個開源的跨平臺多媒體框架。通過它你可以構建各種各樣的媒體處理組件,包括流媒體組件。通過插件機制,GStreamer 支持上百種編碼格式,包括 MPEG-1, MPEG-2, MPEG-4, H.261, H.263, H.264, RealVideo, MP3, WMV, FLV

Kurento、Flumotion 都是基於 GStreamer 構建的流媒體服務器軟件。

2.4.2 Live555

Live555 是流媒體服務開發的基礎庫,支持 RTP/RTCP/RTSP/SIP 等協議,適合在硬件資源受限的情況下使用(例如嵌入式設備)。

基於 Live555 的軟件包括:

  1. Live555媒體服務器,完整的RTSP服務器
  2. openRTSP,一個命令行程序,支持提供RTSP流、接收RTSP流、把RTSP流中的媒體錄像到磁盤
  3. playSIP,可以進行V oIP 通話
  4. liveCaster,支持組播的 MP3 流媒體服務

2.4.3 其它

流媒體服務實現有很多,它們中的一些在最初針對特定的流協議,大部分都走向多元化。例如,Red5是一個RTMP流媒體服務器,Wowza是一個綜合的流媒體服務器,WebRTC的流媒體服務。

三、HTML5媒體標籤

HTML5支持<audio>和 <video>標籤(兩者都對應了HTMLMediaElement的子類型)以實現視頻、音頻的播放。

3.1 <audio>

此標籤用於在瀏覽器中創建一個純音頻播放器。播放靜態文件的示例:

<audio controls preload="auto">
 <source src="song.mp3" type="audio/mpeg">
 <!-- 備選格式,如果瀏覽器不支持mp3 -->
 <source src="song.ogg" type="audio/ogg">
 <!-- 如果瀏覽器不支持audio標籤,顯示下面的連接 -->
 <a href="audiofile.mp3">download audio</a>
</audio>

3.2 <video>

此標籤用於在瀏覽器中創建一個視頻播放器。播放靜態文件的示例:

<!-- poster指定預覽圖,autoplay自動播放,muted靜音 -->
<video controls width="640" height="480" poster="movie.png" autoplay muted>
 <source src="movie.mp4" type="video/mp4">
 <!-- 備選格式,如果瀏覽器不支持mp4 -->
 <source src="movie.webm" type="video/webm">
 <!-- 可以附帶字幕 -->
 <track src="subtitles_en.vtt" kind="subtitles" srclang="en" label="English">
 <!-- 如果瀏覽器不支持video標籤,顯示下面的連接 -->
 <a href="videofile.mp4">download video</a>
</video>

3.3 <canvas>

在畫布中,你可以進行任意的圖形繪製,當然可以去逐幀渲染視頻內容。

編程方式創建

音頻、視頻播放器標籤也可以利用JavaScript編程式的創建,示例代碼:

var video = document.createElement('video');
if (video.canPlayType('video/mp4')) {
 video.setAttribute('src', 'movie.mp4' );
} else if (video.canPlayType('video/webm')) {
 video.setAttribute('src', 'movie.webm');
}
video.width = 640;
video.height = 480;

四、MSE

媒體源擴展(Media Source Extensions,MSE)是一個 W3C 草案,桌面瀏覽器對MSE的支持較好。MSE擴展流 video/audio 元素的能力,允許你通過 JavaScript 來生成(例如從服務器抓取)媒體流供 video/audio 元素播放。使用MSE你可以:

  1. 通過JavaScript 來構建媒體流,不管媒體是如何捕獲的
  2. 處理自適應碼流、廣告插入、時間平移(time-shifting,回看)、視頻編輯等應用場景
  3. 最小化 JavaScript 中處理媒體解析的代碼

MSE 定義支持的(你生成的)媒體格式,只有符合要求的容器格式、編碼格式才能被 MSE 處理。通常容器格式是 ISO BMFF(MP4),也就是說你需要生成 MP4 的片斷,然後 Feed 給MSE 進行播放。

MediaSource 對象作爲 video/audio 元素的媒體來源,它可以具有多個 SourceBuffer 對象。應用程序把數據片段(segment)附加到 SourceBuffer 中,並可以根據系統性能對數據片段的質量進行適配。SourceBuffer 中包含多個 track buffer —— 分別對應音頻、視頻、文本等可播放數據。這些數據被音頻、視頻解碼器解碼,然後在屏幕上顯示、在揚聲器中播放:

要把 MediaSource 提供給 video/audio 播放,調用:video.src = URL.createObjectURL(mediaSource);

4.1基於 MSE 的框架

4.1.1 wfs

wfs 是一個播放原始 H.264 幀的 HTML5 播放器,它的工作方式是把 H.264 NAL 單元封裝爲 ISO BMFF(MP4)片,然後Feed給 MSE 處理。

4.1.2 flv.js

flv.js是一個 HTML5 Flash 視頻播放器,基於純 JS,不需要 Flash 插件的支持。此播放器將FLV 流轉換爲 ISO BMFF(MP4)片斷,然後把 MP4 片斷提供給 video 元素使用。

flv.js 支持 Chrome 43+, FireFox 42+, Edge 15.15048+ 以上版本的直播流 。

4.1.3 Streamedian

Streamedian 是一個 HTML5 的 RTSP 播放器。實現了 RTSP 客戶端功能,你可以利用此框架直接播放 RTSP 直播流。此播放器把RTP協議下的 H264/AAC 在轉換爲 ISO BMFF 供 video 元素使用。Streamedian 支持 Chrome 23+, FireFox 42+, Edge 13+,以及 Android 5.0+。不支持 iOS 和 IE。

在服務器端,你需要安裝 Streamedian 提供的代理(此代理收費),此代理將 RTSP轉換爲WebSocket。Streamedian 處理視頻流的流程如下:

五、WebRTC

WebRTC 是一整套 API,其中一部分供 Web 開發者使用,另外一部分屬於要求瀏覽器廠商實現的接口規範。WebRTC 解決諸如客戶端流媒體發送、點對點通信、視頻編碼等問題。桌面瀏覽器對WebRTC的支持較好,WebRTC 也很容易和 Native 應用集成。

使用 MSE 時,你需要自己構建視頻流。使用 WebRTC 時則可以直接捕獲客戶端視頻流。

使用 WebRTC 時,大部分情況下流量不需要依賴於服務器中轉,服務器的作用主要是:

  1. 在信號處理時,轉發客戶端的數據
  2. 配合實現 NAT/防火牆 穿透
  3. 在點對點通信失敗時,作爲中繼器使用

5.1 架構圖

5.2 流捕獲

5.2.1 捕獲視頻

主要是捕獲客戶端攝像頭、麥克風。在視頻監控領域用處不大,這裏大概瞭解一下。流捕獲通過 navigator.getUserMedia 調用實現:

<script type="text/javascript">
 navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.getUserMedia;
 var success = function (stream) {
   var video = document.getElementById('camrea');
   // 把MediaStream對象轉換爲Blob URL,提供給video播放
   video.src = URL.createObjectURL( stream );
   video.play();
 }
 var error = function ( err ) {
   console.log( err )
 }

 // 調用成功後,得到MediaStream對象
 navigator.getUserMedia( { video: true, audio: true }, success, error );
</script>
<video id="camrea" width="640" height="480"/>

三個調用參數分別是:

  1. 約束條件,你可以指定媒體類型、分辨率、幀率
  2. 成功後的回調,你可以在回調中解析出 URL 提供給 video 元素播放
  3. 失敗後的回調

5.2.2 捕獲音頻

捕獲音頻類似:

navigator.getUserMedia( { audio: true }, function ( stream ) {
    var audioContext = new AudioContext();

    // 從捕獲的音頻流創建一個媒體源管理
    var streamSource = audioContext.createMediaStreamSource( stream );

    // 把媒體源連接到目標(默認是揚聲器)
    streamSource.connect( audioContext.destination );
}, error );

5.2.3 MediaStream

MediaStream對象提供以下方法:

  1. getAudioTracks(),音軌列表
  2. getVideoTracks(),視軌列表

每個音軌、視軌都有個label屬性,對應其設備名稱。

5.2.4 Camera.js

Camera.js 是對 getUserMedia 的簡單封裝,簡化了API 並提供了跨瀏覽器支持:

camera.init( {
    width: 640,
    height: 480,
    fps: 30, // 幀率
    mirror: false,  // 是否顯示爲鏡像
    targetCanvas: document.getElementById( 'webcam' ), // 默認null,如果設置了則在畫布中渲染

    onFrame: function ( canvas ) {
        // 每當新的幀被捕獲,調用此回調
    },

    onSuccess: function () {
        // 流成功獲取後
    },

    onError: function ( error ) {
        // 如果初始化失敗
    },

    onNotSupported: function () {
        // 當瀏覽器不支持camera.js時
    }
} );
// 暫停
camera.pause();
// 恢復
camera.start();

5.3 信號處理

在端點之間(Peer)發送流之前,需要進行通信協調、發送控制消息,即所謂信號處理(Signaling),信號處理牽涉到三類信息:

會話控制信息:初始化、關閉通信,報告錯誤
網絡配置:對於其它端點來說,本機的 IP 和 port 是什麼
媒體特性:本機能夠處理什麼音視頻編碼、多高的分辨率。本機發送什麼樣的音視頻編碼
WebRTC 沒有對信號處理規定太多,我們可以通過 Ajax/WebSocket 通信,以 SIP、Jingle、ISUP 等協議完成信號處理。點對點連接設立後,流的傳輸並不需要服務器介入。信號處理的示意圖如下:

5.3.1 示例代碼

下面的代表片段包含了一個視頻電話的信號處理過程:

// 信號處理通道,底層傳輸方式和協議自定義
var signalingChannel = createSignalingChannel();
var conn;

// 信號通過此回調送達本地,可能分多次送達
signalingChannel.onmessage = function ( evt ) {
    if ( !conn ) start( false );

    var signal = JSON.parse( evt.data );
    // 會話描述協議(Session Description Protocol),用於交換媒體配置信息(分辨率、編解碼能力)
    if ( signal.sdp )
    // 設置Peer的RTCSessionDescription
        conn.setRemoteDescription( new RTCSessionDescription( signal.sdp ) );
    else
    // 添加Peer的Candidate信息
        conn.addIceCandidate( new RTCIceCandidate( signal.candidate ) );
};

// 調用此方法啓動WebRTC,獲取本地流並顯示,偵聽連接上的事件並處理
function start( isCaller ) {
    conn = new RTCPeerConnection( { /**/ } );

    // 把地址/端口信息發送給其它Peer。所謂Candidate就是基於ICE框架獲得的本機可用地址/端口
    conn.onicecandidate = function ( evt ) {
        signalingChannel.send( JSON.stringify( { "candidate": evt.candidate } ) );
    };

    // 當遠程流到達後,在remoteView元素中顯示
    conn.onaddstream = function ( evt ) {
        remoteView.src = URL.createObjectURL( evt.stream );
    };

    // 獲得本地流
    navigator.getUserMedia( { "audio": true, "video": true }, function ( stream ) {
        // 在remoteView元素中顯示
        localView.src = URL.createObjectURL( stream );
        // 添加本地流,Peer將接收到onaddstream事件
        conn.addStream( stream );

        if ( isCaller )
        // 獲得本地的RTCSessionDescription
            conn.createOffer( gotDescription );
        else
        // 針對Peer的RTCSessionDescription生成兼容的本地SDP
            conn.createAnswer( conn.remoteDescription, gotDescription );

        function gotDescription( desc ) {
            // 設置自己的RTCSessionDescription
            conn.setLocalDescription( desc );
            // 把自己的RTCSessionDescription發送給Peer
            signalingChannel.send( JSON.stringify( { "sdp": desc } ) );
        }
    } );
}

// 通信發起方調用:
start( true );

5.4 流轉發

主要牽涉到的接口是RTCPeerConnection,上面的例子中已經包含了此接口的用法。WebRTC在底層做很多複雜的工作,這些工作對於JavaScript來說是透明的:

  1. 執行解碼
  2. 屏蔽丟包的影響
  3. 點對點通信:WebRTC 引入流交互式連接建立(Interactive Connectivity Establishment,ICE)框架。ICE 負責建立點對點鏈路的建立:
    • 首先嚐試直接
    • 不行的話嘗試 STUN(Session Traversal Utilities for NAT)協議。此協議通過一個簡單的保活機制確保NAT端口映射在會話期間有效
    • 仍然不行嘗試 TURN(Traversal Using Relays around NAT)協議。此協議依賴於部署在公網上的中繼服務器。只要端點可以訪問TURN服務器就可以建立連接
  4. 通信安全
  5. 帶寬適配
  6. 噪聲抑制
  7. 動態抖動緩衝(Dynamic jitter buffering),抖動是由於網絡狀況的變化,緩衝用於收集、存儲數據,定期發送

5.5 任意數據交換

通過 RTCDataChannel 完成,允許點對點之間任意的數據交換。RTCPeerConnection 連接創建後,不但可以傳輸音視頻流,還可以打開多個信道(RTCDataChannel)進行任意數據的交換。RTCDataChanel 的特點是:

  1. 類似於 WebSocket 的API
  2. 支持帶優先級的多通道
  3. 超低延遲,因爲不需要通過服務器中轉
  4. 支持可靠/不可靠傳輸語義。支持 SCTP、DTLS、UDP 幾種傳輸協議
  5. 內置安全傳輸(DTLS)
  6. 內置擁塞控制
    使用 RTCDataChannel 可以很好的支持遊戲、遠程桌面、實時文本聊天、文件傳輸、去中心化網絡等業務場景。

5.6 WebRTC框架

  1. PeerJS :簡化 WebRTC 的點對點通信、視頻、音頻調用,提供雲端的 PeerServer,你也可以自己搭建服務器
  2. Sharefest:基於 Web 的 P2P 文件共享
  3. webRTC.io:WebRTC 的一個抽象層,同時提供了客戶端、服務器端 Node.js 組件。服務器端組件抽象了 STUN。類似的框架還有 SimpleWebRTC、easyrtc
  4. OpenWebRTC:允許你構建能夠和遵循 WebRTC 標準的瀏覽器進行通信的 Native 應用程序,支持Java綁定
  5. NextRTC:基於 Java 實現的 WebRTC 信號處理服務器
  6. Janus:這是一個 WebRTC 網關,純服務器端組件,目前僅僅支持 Linux 環境下安裝。Janus本身實現了到瀏覽器的 WebRTC 連接機制,支持以JSON格式交換數據,支持在服務器端應用邏輯 - 瀏覽器之間中繼 RTP/RTCP 和消息。特殊化的功能有服務器端插件完成。官網地址:https://janus.conf.meetecho.com
  7. Kurento:這是一個開源的 WebRTC 媒體服務器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章