WebRTC入門篇

來源:互聯網
上載者:User
什麼是WebRTC。
眾所周知,瀏覽器本身不支援相互之間直接建立通道進行通訊,都是通過伺服器進行中轉。比如現在有兩個用戶端,甲和乙,他們倆想要通訊,首先需要甲和伺服器、乙和伺服器之間建立通道。甲給乙發送訊息時,甲先將訊息發送到伺服器上,伺服器對甲的訊息進行中轉,發送到乙處,反過來也是一樣。這樣甲與乙之間的一次訊息要通過兩段通道,通訊的效率同時受制於這兩段通道的頻寬。同時這樣的通道並不適合資料流的傳輸,如何建立瀏覽器之間的點對點傳輸,一直困擾著開發人員。WebRTC應運而生。

WebRTC是一個開源項目,旨在使得瀏覽器能為即時通訊(RTC)提供簡單的JavaScript介面。說的簡單明了一點就是讓瀏覽器提供JS的即時通訊介面。這個介面所創立的通道並不是像WebSocket一樣,打通一個瀏覽器與WebSocket伺服器之間的通訊,而是通過一系列的信令,建立一個瀏覽器與瀏覽器之間(peer-to-peer)的通道,這個通道可以發送任何資料,而不需要經過伺服器。並且WebRTC通過實現MediaStream,通過瀏覽器調用裝置的網路攝影機、話筒,使得瀏覽器之間可以傳遞音頻和視頻。

三個介面
WebRTC實現了三個API,分別是:
* MediaStream:通過MediaStream的API能夠通過裝置的網路攝影機及話筒獲得視頻、音訊同步流
* RTCPeerConnection:RTCPeerConnection是WebRTC用於構建點對點之間穩定、高效的流傳輸的組件
* RTCDataChannel:RTCDataChannel使得瀏覽器之間(點對點)建立一個高輸送量、低延時的通道,用於傳輸任意資料
這裡大致上介紹一下這三個API:

MediaStream(getUserMedia)
MediaStream API為WebRTC提供了從裝置的網路攝影機、話筒擷取視頻、音頻流資料的功能。

如何調用:
同門可以通過調用navigator.getUserMedia(),這個方法接受三個參數:
1. 一個約束對象(constraints object),這個後面會單獨講
2. 一個調用成功的回呼函數,如果調用成功,傳遞給它一個流對象
3. 一個調用失敗的回呼函數,如果調用失敗,傳遞給它一個錯誤對象

瀏覽器安全色性:

由於瀏覽器實現不同,他們經常會在實現標準版本之前,在方法前面加上首碼,所以一個相容版本就像這樣:
var getUserMedia = (navigator.getUserMedia || 
                    navigator.webkitGetUserMedia || 
                    navigator.mozGetUserMedia || 
                    navigator.msGetUserMedia);

一個超級簡單的例子

這裡寫一個超級簡單的例子,用來展現getUserMedia的效果:

<!doctype html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <title>GetUserMedia執行個體</title></head><body>    <video id="video" autoplay></video></body><script type="text/javascript">    var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);    getUserMedia.call(navigator, {        video: true,        audio: true    }, function(localMediaStream) {        var video = document.getElementById('video');        video.src = window.URL.createObjectURL(localMediaStream);        video.onloadedmetadata = function(e) {            console.log("Label: " + localMediaStream.label);            console.log("AudioTracks" , localMediaStream.getAudioTracks());            console.log("VideoTracks" , localMediaStream.getVideoTracks());        };    }, function(e) {        console.log('Reeeejected!', e);    });</script></html>

將這段內容儲存在一個HTML檔案中,放在伺服器上。用較新版本的Opera、Firefox、Chrome開啟,在瀏覽器彈出詢問是否允許訪問網路攝影機和話筒,選同意,瀏覽器上就會出現網路攝影機所拍攝到的畫面了

注意,HTML檔案要放在伺服器上,否則會得到一個NavigatorUserMediaError的錯誤,顯示PermissionDeniedError,最簡單方法就是cd到HTML檔案所在目錄下,然後python -m SimpleHTTPServer(裝了python的話),然後在瀏覽器中輸入http://localhost:8000/{檔案名稱}.html

這裡使用getUserMedia獲得流之後,需要將其輸出,一般是綁定到video標籤上輸出,需要使用window.URL.createObjectURL(localMediaStream)來創造能在video中使用src屬性播放的Blob URL,注意在video上加入autoplay屬性,否則只能捕獲到一張圖片

流建立完畢後可以通過label屬性來獲得其唯一的標識,還可以通過getAudioTracks()和getVideoTracks()方法來獲得流的追蹤對象數組(如果沒有開啟某種流,它的追蹤對象數組將是一個空數組) 約束對象(Constraints)

約束對象可以被設定在getUserMedia()和RTCPeerConnection的addStream方法中,這個約束對象是WebRTC用來指定接受什麼樣的流的,其中可以定義如下屬性:
* video: 是否接受視頻流
* audio:是否接受音頻流
* MinWidth: 視頻流的最小寬度
* MaxWidth:視頻流的最大寬度
* MinHeight:視頻流的最小高度
* MaxHiehgt:視頻流的最大高度
* MinAspectRatio:視頻流的最小寬高比
* MaxAspectRatio:視頻流的最大寬高比
* MinFramerate:視頻流的最小畫面播放速率
* MaxFramerate:視頻流的最大畫面播放速率 RTCPeerConnection

WebRTC使用RTCPeerConnection來在瀏覽器之間傳遞流資料,這個流資料通道是點對點的,不需要經過伺服器進行中轉。但是這並不意味著我們能拋棄伺服器,我們仍然需要它來為我們傳遞信令(signaling)來建立這個通道。WebRTC沒有定義用於建立通道的信令的協議:信令並不是RTCPeerConnection API的一部分。

信令

既然沒有定義具體的信令的協議,我們就可以選擇任意方式(AJAX、WebSocket),採用任意的協議(SIP、XMPP)來傳遞信令,建立通道,比如我寫的demo,就是用的node的ws模組,在WebSocket上傳遞信令

需要信令來交換的資訊有三種:
* session的資訊:用來初始化通訊還有報錯
* 網路設定:比如IP地址和連接埠啥的
* 媒體適配:發送方和接收方的瀏覽器能夠接受什麼樣的編碼器和解析度

這些資訊的交換應該在點對點的流傳輸之前就全部完成,一個大致的架構圖如下:

通過伺服器建立通道

這裡再次重申,就算WebRTC提供瀏覽器之間的點對點通道進行資料轉送,但是建立這個通道,必須有伺服器的參與。WebRTC需要伺服器對其進行四方面的功能支援:
1. 使用者發現以及通訊
2. 信令傳輸
3. NAT/防火牆穿越
4. 如果點對點通訊建立失敗,可以作為代理服務器 NAT/防火牆穿越技術

建立點對點通道的一個常見問題,就是NAT穿越技術。在處於使用了NAT裝置的私人TCP/IP網路中的主機之間需要建立串連時需要使用NAT穿越技術。以往在VoIP領域經常會遇到這個問題。目前已經有很多NAT穿越技術,但沒有一項是完美的,因為NAT的行為是非標準化的。這些技術中大多使用了一個公用伺服器,這個服務使用了一個從全球任何地方都能訪問得到的IP地址。在RTCPeeConnection中,使用ICE架構來保證RTCPeerConnection能實現NAT穿越

ICE,全名叫互動式串連建立(Interactive Connectivity Establishment),一種綜合性的NAT穿越技術,它是一種架構,可以整合各種NAT穿越技術如STUN、TURN(Traversal Using Relay NAT 中繼NAT實現的穿透)。ICE會先使用STUN,嘗試建立一個基於UDP的串連,如果失敗了,就會去TCP(先嘗試HTTP,然後嘗試HTTPS),如果依舊失敗ICE就會使用一個中繼的TURN伺服器。

我們可以使用Google的STUN伺服器:stun:stun.l.google.com:19302,於是乎,一個整合了ICE架構的架構應該長這個樣子

瀏覽器安全色

還是首碼不同的問題,採用和上面類似的方法:

var PeerConnection = (window.PeerConnection ||                    window.webkitPeerConnection00 ||                     window.webkitRTCPeerConnection ||                     window.mozRTCPeerConnection);
建立和使用:

//使用Google的stun伺服器var iceServer = {    "iceServers": [{        "url": "stun:stun.l.google.com:19302"    }]};//相容瀏覽器的getUserMedia寫法var getUserMedia = (navigator.getUserMedia ||                    navigator.webkitGetUserMedia ||                     navigator.mozGetUserMedia ||                     navigator.msGetUserMedia);//相容瀏覽器的PeerConnection寫法var PeerConnection = (window.PeerConnection ||                    window.webkitPeerConnection00 ||                     window.webkitRTCPeerConnection ||                     window.mozRTCPeerConnection);//與後台伺服器的WebSocket串連var socket = __createWebSocketChannel();//建立PeerConnection執行個體var pc = new PeerConnection(iceServer);//發送ICE候選到其他用戶端pc.onicecandidate = function(event){    socket.send(JSON.stringify({        "event": "__ice_candidate",        "data": {            "candidate": event.candidate        }    }));};//如果檢測到媒體流串連到本地,將其綁定到一個video標籤上輸出pc.onaddstream = function(event){    someVideoElement.src = URL.createObjectURL(event.stream);};//擷取本地的媒體流,並綁定到一個video標籤上輸出,並且發送這個媒體流給其他用戶端getUserMedia.call(navigator, {    "audio": true,    "video": true}, function(stream){    //發送offer和answer的函數,發送本地session描述    var sendOfferFn = function(desc){            pc.setLocalDescription(desc);            socket.send(JSON.stringify({                 "event": "__offer",                "data": {                    "sdp": desc                }            }));        },        sendAnswerFn = function(desc){            pc.setLocalDescription(desc);            socket.send(JSON.stringify({                 "event": "__answer",                "data": {                    "sdp": desc                }            }));        };    //綁定本地媒體流到video標籤用於輸出    myselfVideoElement.src = URL.createObjectURL(stream);    //向PeerConnection中加入需要發送的流    pc.addStream(stream);    //如果是發送方則發送一個offer信令,否則發送一個answer信令    if(isCaller){        pc.createOffer(sendOfferFn);    } else {        pc.createAnswer(sendAnswerFn);    }}, function(error){    //處理媒體流建立失敗錯誤});//處理到來的信令socket.onmessage = function(event){    var json = JSON.parse(event.data);    //如果是一個ICE的候選,則將其加入到PeerConnection中,否則設定對方的session描述為傳遞過來的描述    if( json.event === "__ice_candidate" ){        pc.addIceCandidate(new RTCIceCandidate(json.data.candidate));    } else {         pc.setRemoteDescription(new RTCSessionDescription(json.data.sdp));    }};
執行個體

由於涉及較為複雜靈活的信令傳輸,故這裡不做簡短的執行個體,可以直接移步到最後

RTCDataChannel
既然能建立點對點的通道來傳遞即時的視頻、音頻資料流,為什麼不能用這個通道傳一點其他資料呢。RTCDataChannel API就是用來幹這個的,基於它我們可以在瀏覽器之間傳輸任意資料。DataChannel是建立在PeerConnection上的,不能單獨使用。

使用DataChannel

我們可以使用channel = pc.createDataCHannel("someLabel");來在PeerConnection的執行個體上建立Data Channel,並給與它一個標籤

DataChannel使用方式幾乎和WebSocket一樣,有幾個事件:
* onopen
* onclose
* onmessage
* onerror

同時它有幾個狀態,可以通過readyState擷取:
* connecting: 瀏覽器之間正在試圖建立channel
* open:建立成功,可以使用send方法發送資料了
* closing:瀏覽器正在關閉channel
* closed:channel已經被關閉了

兩個暴露的方法:
* close(): 用於關閉channel
* send():用於通過channel向對方發送資料 通過Data Channel傳送檔案大致思路

JavaScript已經提供了File API從input[type='file']的元素中提取檔案,並通過FileReader來將檔案的轉換成DataURL,這也意味著我們可以將DataURL分成多個片段來通過Channel來進行檔案傳輸 一個綜合的Demo

SkyRTC-demo,這是我寫的一個Demo。建立一個視訊交談室,並能夠廣播檔案,當然也支援單對單檔案傳輸,寫得還很粗糙,後期會繼續完善 使用方式 下載解壓並cd到目錄下 運行npm install安裝依賴的庫(express, ws, node-uuid) 運行node server.js,訪問localhost:3000,允許網路攝影機訪問 開啟另一台電腦,在瀏覽器(Chrome和Opera,還未相容Firefox)開啟{server所在IP}:3000,允許網路攝影機和話筒訪問 廣播檔案:在左下角選定一個檔案,點擊“傳送檔案”按鈕 廣播資訊:左下角input框輸入資訊,點擊發送 可能會出錯,注意F12對話方塊,一般F5能解決 功能

視頻音頻聊天(串連了網路攝影機和話筒,至少要有網路攝影機),廣播檔案(可單獨傳播,提供API,廣播就是基於單獨傳播實現的,可同時傳播多個,小檔案還好說,大檔案坐等記憶體吃光),廣播聊天資訊 轉自:http://segmentfault.com/a/1190000000436544#articleHeader19

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.