WebRTC is a new front in the long war for an open and unencumbered web. --Brendan Eich
WebRTC 是爭(zhēng)取開(kāi)放和無(wú)阻礙Web 的漫長(zhǎng)戰(zhàn)爭(zhēng)中一條新戰(zhàn)線。JavaScript 之父 Brendan Eich
WebRTC前世今生
讓人類(lèi)通過(guò)網(wǎng)絡(luò)進(jìn)行音視頻通信是網(wǎng)絡(luò)最后的巨大挑戰(zhàn):實(shí)時(shí)通信(RTC),實(shí)時(shí)通信就像網(wǎng)絡(luò)上在文本框中輸入文本一樣自然,沒(méi)有它,就限制了我們新的方式使人們互動(dòng)交流起來(lái)。
從歷史上看,RTC 變化很大很復(fù)雜,需要昂貴的音視頻技術(shù)授權(quán)或話費(fèi)巨大代價(jià)去開(kāi)發(fā),RTC 技術(shù)與現(xiàn)有的內(nèi)容、數(shù)據(jù)、服務(wù)整合一直都很困難和耗時(shí),在網(wǎng)絡(luò)上尤其如此。
Gmail 視頻聊天在2008年開(kāi)始流行,在2011年 Google 推出視頻群聊,它使用 GoogleTalk 服務(wù)就像 Gmail 一樣。Google 收購(gòu)了 GIPS,它是一個(gè)為 RTC 開(kāi)發(fā)出許多組件的一個(gè)公司,例如編解碼回聲消除技術(shù)。Google 開(kāi)源了 GIPS 開(kāi)發(fā)的技術(shù),與相關(guān)機(jī)構(gòu) IET 和 W3C 制定行業(yè)標(biāo)準(zhǔn)。在2011年5月,愛(ài)立信實(shí)現(xiàn)了第一個(gè) WebRTC應(yīng)用。
WebRTC已經(jīng)實(shí)現(xiàn)了對(duì)于實(shí)時(shí)通信,免插件音頻數(shù)據(jù)傳輸?shù)臉?biāo)準(zhǔn)制定。
- 許多網(wǎng)路服務(wù)已經(jīng)使用了 RTC 但需下載,本地應(yīng)用或是插件,包括 Skype、Facebook、Google Hangouts。
- 下載安裝升級(jí)插件是復(fù)雜的可能出錯(cuò)的,令人厭煩的。
- 插件可能很難部署、調(diào)試、故障排除等
WebRTC指導(dǎo)原則是 APIs 應(yīng)該是開(kāi)源的、免費(fèi)的、標(biāo)準(zhǔn)化的,瀏覽器內(nèi)置的,比現(xiàn)有技術(shù)更高效的。
WebRTC現(xiàn)狀
隨著移動(dòng)互聯(lián)網(wǎng)和智能硬件的快速發(fā)展,音視頻技術(shù)從獨(dú)立應(yīng)用普及到嵌入式應(yīng)用中,不管是智能硬件、手機(jī)應(yīng)用或是 web 程序中許多模塊越來(lái)越依賴音視頻技術(shù)。
2011年 Google 將 WebRTC 項(xiàng)目開(kāi)源,WebRTC 在被 Google 開(kāi)源之前,價(jià)值已得到充分認(rèn)可,比如 QQ 中使用 WebRTC 部分技術(shù)。
WebRTC 發(fā)展情況可從標(biāo)準(zhǔn)規(guī)范和瀏覽器支持兩個(gè)方面看,WebRTC 標(biāo)準(zhǔn)是由 W3C 和 IETF 聯(lián)合制定了,2016年1月28日,W3C 公布最新的 WebRTC 標(biāo)準(zhǔn),標(biāo)準(zhǔn)中定義了 WebIDL 中一系列的 ECMAScript API 來(lái)允許使用合適的 RTP 的瀏覽器或設(shè)備來(lái)接收/發(fā)送媒體。
WebRTC 很多參數(shù)都由 GIPS 工程師依靠經(jīng)驗(yàn)所設(shè)定的值,這會(huì)出現(xiàn)卡頓、延時(shí)、回聲、丟包、多人視頻不穩(wěn)定等問(wèn)題,由于公網(wǎng)的穩(wěn)定性或機(jī)型適配等外在因素,以上問(wèn)題在項(xiàng)目上線后更為嚴(yán)重。
總之 WebRTC 雖然提供了一套音視頻實(shí)時(shí)通訊的解決方案,但是實(shí)際應(yīng)用中,由于網(wǎng)絡(luò)傳輸、設(shè)備適配以及多方通話都存在問(wèn)題,效果并不理想。
由于 WebRTC 復(fù)雜性和尚未完善,建議結(jié)合自己的實(shí)際參考:
- 音視頻不是公司核心方向,建議使用第三方。
- 項(xiàng)目時(shí)間緊、有多人視頻場(chǎng)景,使用場(chǎng)景依賴于手機(jī)端建議使用第三方。
- 公司沒(méi)音視頻人才建議使用第三方 SDK 或技術(shù)外包。
- 若公司實(shí)力、財(cái)力、人力雄厚,時(shí)間也不緊急,可考慮 WebRTC 繼承開(kāi)發(fā)。
- 若音視頻技術(shù)是公司核心放心,若不想花太多時(shí)間研究 WebRTC 可尋找熟悉 WebRTC 的人來(lái)培訓(xùn)。
- 項(xiàng)目時(shí)間不緊急,無(wú)人視頻需求且音頻質(zhì)量要求不高可考慮 WebRTC 集成開(kāi)發(fā)。
即時(shí)通信和實(shí)時(shí)通信的區(qū)別
即時(shí)通信和實(shí)時(shí)通信都是一套網(wǎng)絡(luò)通信系統(tǒng),其本質(zhì)都是對(duì)信息進(jìn)行轉(zhuǎn)發(fā)。最大不同點(diǎn)是對(duì)信息傳遞的時(shí)間規(guī)定。二者的區(qū)別為
1.應(yīng)用場(chǎng)景
- 即時(shí)通信:文字聊天、語(yǔ)音消息發(fā)送、文件傳輸、音視頻播放等。通俗的說(shuō)就是發(fā)短信。
- 實(shí)時(shí)通信:語(yǔ)音、視頻電話會(huì)議、網(wǎng)絡(luò)電話等。通俗的說(shuō)就是打電話。
2.產(chǎn)品需求
- 即使通信:要求可靠考核送達(dá)率
- 實(shí)時(shí)通信:要求低延時(shí)和接通率
3.技術(shù)環(huán)節(jié)
- 即時(shí)通信:消息接收和確認(rèn)
- 實(shí)時(shí)通信:采集、前處理、編碼、解碼、播放渲染
4.傳輸協(xié)議
- 即使通信:為保證連接的可靠性常用的 TCP ,此類(lèi)協(xié)議的特點(diǎn)是追求連接的可靠性,而造成了延遲的不可控,超過(guò)2秒的延遲響應(yīng)是常態(tài),甚至幾十分鐘的延遲響應(yīng),而電信級(jí)的實(shí)時(shí)通信標(biāo)準(zhǔn)是400ms,而基于互聯(lián)網(wǎng)的實(shí)時(shí)通信需另辟蹊徑,開(kāi)創(chuàng)出新的傳輸解決方案。
- 實(shí)時(shí)通信:在設(shè)計(jì)延時(shí)的實(shí)時(shí)通信服務(wù)時(shí),UDP 表現(xiàn)要比 TCP 好得多,實(shí)時(shí)通信中低延時(shí)的效果,特別是在互聯(lián)網(wǎng)中超過(guò)30%丟包時(shí),TCP 延時(shí)可到幾十分鐘,超過(guò)50%丟包時(shí)很容易斷開(kāi)。在同樣丟包30%的鏈路上,UDP 還可傳輸數(shù)據(jù),TCP 就無(wú)法進(jìn)行實(shí)時(shí)通信了。
搭建 WebRTC 數(shù)據(jù)通道服務(wù)
WebRTC可讓數(shù)據(jù)、視頻、屏幕共享通過(guò) P2P 連接形式進(jìn)行傳播的技術(shù),這意味著數(shù)據(jù)無(wú)需通過(guò)服務(wù)器進(jìn)行傳輸。
WebRTC API
WebRTC 是一系列協(xié)議和 API 的大集合,WebRTC 協(xié)議被內(nèi)置在 Chrome 和 Firefox 瀏覽器中,包括三部分:
- getUserMedia 允許網(wǎng)頁(yè)瀏覽器獲取攝像頭和麥克風(fēng)的使用權(quán)限,并且捕獲媒體。通過(guò) MediaStream 的 API 能夠通過(guò)設(shè)備的攝像頭及話筒獲得音視頻的同步流。
- RTCPeerConnection 負(fù)責(zé)管理端到端連接,RTCPeerConnection 是 WebRTC 用于構(gòu)建點(diǎn)對(duì)點(diǎn)之間穩(wěn)定、高效的流傳輸?shù)慕M件。
- RTCDataChannel 允許瀏覽器分享任意數(shù)據(jù),RTCDataChannel 使得瀏覽器之間(點(diǎn)對(duì)點(diǎn))建立一個(gè)高吞吐量、低延時(shí)的信道,用于傳輸任意數(shù)據(jù)。
MediaStream(getUserMedia)
MediaStream API為 WebRTC 提供了從設(shè)備的攝像頭、話筒獲取視音頻數(shù)據(jù)的功能。
getUserMedia瀏覽器兼容性
navigator.getUserMedia || (navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
if(!navigator.getUserMedia){
return;
}
Chrome21、Firefox17、Opera18支持,IE 尚不支持。
getUserMedia(streams, success, error)
//獲取攝像頭和麥克風(fēng)的實(shí)時(shí)信息
var streams = {video:true, audio:true};
navigator.getUserMedia(streams, onSuccessFn, onErrorFn);
streams 表示包括哪些多媒體設(shè)備對(duì)象
約束對(duì)象可被設(shè)置在 getUserMedia()和 RTCPeerConnection 的 addStream()中,這個(gè)約束是 WebRTC用來(lái)指定接受什么樣的流的,可定義如下屬性:
- video 是否接受視頻流
- audio 是否接受音頻流
- MinWidth 視頻流的最小寬度
- MaxWidith 視頻流的最大寬度
- MinHeight 視頻流的最小高度
- MaxHeight 視頻流的最大高度
- MinAspectRatio 視頻流的最小寬高比
- MaxAspectRatio 視頻流的最大寬高比
- MinFramerate 視頻流的最小幀速率
- MaxFramerate 視頻流的最大幀速率
success 成功回調(diào)函數(shù),獲取多媒體設(shè)備成功調(diào)用,傳遞給它一個(gè)流對(duì)象。
網(wǎng)頁(yè)使用 getUserMedia,瀏覽器會(huì)詢問(wèn)用戶,是否許可提供信息。若用戶拒絕就會(huì)調(diào)用 onErrorFn。發(fā)生錯(cuò)誤時(shí)回調(diào)函數(shù)的參數(shù)是一個(gè) Error 對(duì)象,它有 code 參數(shù),取值如下:
- PERMISSION_DENIED 用戶拒絕提供信息
- NOT_SUPPORTED_ERROR 瀏覽器不支持指定的媒體類(lèi)型
- MANDATORY_UNSATISHIED_ERROR 指定的媒體類(lèi)型未收到媒體流
error 失敗回調(diào)函數(shù),獲取多媒體設(shè)備失敗時(shí)調(diào)用。
function onSuccessFn(stream){
var video = document.getElementById('video');
if(window.URL){
video.src = window.URL.createObjectURL(stream);
}else{
video.src = stream;
}
video.autoplay = true;
}
實(shí)時(shí)數(shù)據(jù)交換
WebRTC另外兩個(gè) API, RTCPeerConnection 用于瀏覽器之間點(diǎn)對(duì)點(diǎn)的連接,RTCDataChannel 用于點(diǎn)對(duì)點(diǎn)的手通道。
RTCPeerConnection
WebRTC使用 RTCPeerConnection 在瀏覽器之間傳遞流數(shù)據(jù),這個(gè)流數(shù)據(jù)通道是點(diǎn)對(duì)點(diǎn)的,無(wú)需經(jīng)過(guò)服務(wù)器進(jìn)行中轉(zhuǎn)。但并不意味著能拋棄服務(wù)器,仍需它為傳遞信令(signaling)來(lái)建立信道。WebRTC 沒(méi)有定義用來(lái)建立信道的指令協(xié)議,信令并不是 RTCPeerConnection API的一部分。
信令
即使沒(méi)有定義具體的信令協(xié)議,可選擇任意方式(AJAX、WebSocket),采用任意的協(xié)議(SIP、XMPP)來(lái)傳遞指令,建立信道。
信令交換信息應(yīng)用場(chǎng)景
- session:用來(lái)初始化通信還有報(bào)錯(cuò)
- 網(wǎng)絡(luò)配置:比如 IP 地址和端口
- 媒體適配:發(fā)送方和接收方的瀏覽器能接受什么樣的編碼器和分辨率
通過(guò)服務(wù)器建立信道
雖然 WebRTC 提供瀏覽器之間的點(diǎn)對(duì)點(diǎn)信道進(jìn)行數(shù)據(jù)船速,但建立這個(gè)信道必須有服務(wù)器參與。WebRTC 需服務(wù)器對(duì)其進(jìn)行四方面的功能支持:
- 用戶發(fā)現(xiàn)以及通信
- 信令傳輸
- NAT/防火墻穿越
- 若點(diǎn)對(duì)點(diǎn)通信建立失敗可作為中轉(zhuǎn)服務(wù)器
NAT/防火墻穿越技術(shù)
建立點(diǎn)對(duì)點(diǎn)信道的問(wèn)題是 NAT 穿越技術(shù),在處于使用了 NAT 設(shè)備的私有 TCP/IP 網(wǎng)絡(luò)中的主機(jī)之間需建立連接時(shí)需使用 NAT 穿越技術(shù)。
由于 NAT 的行為是非標(biāo)準(zhǔn)化的,在 RTCPeerConnection 中使用 ICE 框架來(lái)保證 RTCPeerConnection 能實(shí)現(xiàn) NAT 穿越。
ICE
ICE(Interactive Connectivity Establishment)交互式連接建立,一種綜合性的 NAT 穿越技術(shù),是一種框架,可整合各種 NAT 穿越技術(shù)如 STUN、TURN(Traversal Using Relaying NAT,中繼NAT實(shí)現(xiàn)的穿透)。ICE 會(huì)先使用 STUN,嘗試建立一個(gè)基于 UDP 的連接,如失敗了就會(huì)去 TCP(先嘗試 HTTP,然后嘗試 HTTPS),若依舊失敗 ICE 就會(huì)使用一個(gè)中繼的 TURN 服務(wù)器。
在 P2P 連接被創(chuàng)建前,首先必須進(jìn)行信令處理。為了完成讓用戶都具有另一端用戶的 IP 以及其他任何用戶想要共享的涉及數(shù)據(jù)/視頻的信息,信令會(huì)在用戶和信令服務(wù)器之間進(jìn)行多次往返。
RTCDataChannel
既然能建立點(diǎn)對(duì)點(diǎn)的信道來(lái)傳遞實(shí)時(shí)的視頻、音頻數(shù)據(jù)流,為什么不能用這個(gè)信道傳一點(diǎn)其他數(shù)據(jù)呢?
RTCDataChannel API就是用來(lái)干這個(gè)的,基于它我們可在瀏覽器之間傳遞任意數(shù)據(jù)。DataChannel 是建立在 PeerConnection 上,不能單獨(dú)使用。
使用 DataChannel
可使用channel=pc.createDataChannel("someLabel")
來(lái)在 PeerConnection 的實(shí)例上創(chuàng)建 Data Channel,并給與它一個(gè)標(biāo)簽。
DataChannel 使用方式幾乎與 WebSocket 一樣,有如下事件:
- onopen
- onclose
- onmessage
- onerror
DataChannel 擁有幾個(gè)狀態(tài),可通過(guò) readState 獲取:
- connecting 瀏覽器之間正在試圖建立 channel
- open 建立成功可使用 send()發(fā)送數(shù)據(jù)
- closing 瀏覽器正在關(guān)閉 channel
- closed channel 已經(jīng)被關(guān)閉了
兩個(gè)暴露的方法
- close() 用于關(guān)閉 channel
- send() 用于通過(guò) channel 向?qū)Ψ桨l(fā)送數(shù)據(jù)
端到端連接流程
- 對(duì)等端 A 向?qū)Φ榷?B 發(fā)送“連接請(qǐng)求”(offer)
- 對(duì)等端 B 向?qū)Φ榷?A 發(fā)送“應(yīng)答響應(yīng)”(answer)
- 對(duì)等端 A 向?qū)Φ榷?B 發(fā)出“候選”(candidate)
候選告知另一端用戶如何將信息傳遞到本地瀏覽器中,它是在幕后完成的。
注意事項(xiàng)
- 信令是不對(duì)稱(chēng)的,其中一個(gè)用戶必須發(fā)起它,另一端必須接收。
- STUN 和 TURN 服務(wù)器在不同本地網(wǎng)絡(luò)的兩用戶之間連接建立起到至關(guān)重要的作用。