摘要:使用了集成了的來(lái)進(jìn)行雙方的數(shù)據(jù)通信。四加入視頻流現(xiàn)在我們已經(jīng)有了一個(gè)可靠的視頻數(shù)據(jù)傳輸通道了,下一步只需要向這個(gè)通道加入數(shù)據(jù)流即可。
歡迎關(guān)注我的知乎專欄:https://zhuanlan.zhihu.com/starkwang
在傳統(tǒng)的 Web 應(yīng)用中,瀏覽器與瀏覽器之間是無(wú)法直接相互通信的,必須借助服務(wù)器的幫助,但是隨著 WebRTC 在各大瀏覽器中的普及,這一現(xiàn)狀得到了改變。
WebRTC(Web Real-Time Communication,Web實(shí)時(shí)通信),是一個(gè)支持網(wǎng)頁(yè)瀏覽器之間進(jìn)行實(shí)時(shí)數(shù)據(jù)傳輸(包括音頻、視頻、數(shù)據(jù)流)的技術(shù),谷歌于2011年5月開(kāi)放了工程的源代碼,目前在各大瀏覽器的最新版本中都得到了不同程度的支持。
這篇文章里我們采用 WebRTC 來(lái)構(gòu)建一個(gè)簡(jiǎn)單的視頻傳輸應(yīng)用。
一、關(guān)于 WebRTC 的一些基本概念傳統(tǒng)的視頻推流的技術(shù)實(shí)現(xiàn)一般是這樣的:客戶端采集視頻數(shù)據(jù),推流到服務(wù)器上,服務(wù)器再根據(jù)具體情況將視頻數(shù)據(jù)推送到其他客戶端上。
但是 WebRTC 卻截然不同,它可以在客戶端之間直接搭建基于 UDP 的數(shù)據(jù)通道,經(jīng)過(guò)簡(jiǎn)單的握手流程之后,可以在不同設(shè)備的兩個(gè)瀏覽器內(nèi)直接傳輸任意數(shù)據(jù)。
這其中的流程包括:
采集視頻流數(shù)據(jù),創(chuàng)建一個(gè) RTCPeerConnection
創(chuàng)建一個(gè) SDP offer 和相應(yīng)的回應(yīng)
為雙方找到 ICE 候選路徑
成功創(chuàng)建一個(gè) WebRTC 連接
下面我們介紹這其中涉及到的一些關(guān)鍵詞:
RTCPeerConnection 對(duì)象是 WebRTC API 的入口,它負(fù)責(zé)創(chuàng)建、維護(hù)一個(gè) WebRTC 連接,以及在這個(gè)連接中的數(shù)據(jù)傳輸。目前新版本的瀏覽器大都支持了這一對(duì)象,但是由于目前 API 還不穩(wěn)定,所以需要加入各個(gè)瀏覽器內(nèi)核的前綴,例如 Chrome 中我們使用 webkitRTCPeerConnection 來(lái)訪問(wèn)它。
為了連接到其他用戶,我們必須要對(duì)其他用戶的設(shè)備情況有所了解,比如音頻視頻的編碼解碼器、使用何種編碼格式、使用何種網(wǎng)絡(luò)、設(shè)備的數(shù)據(jù)處理能力,所以我們需要一張“名片”來(lái)獲得用戶的所有信息,而 SDP 為我們提供了這些功能。
一個(gè) SDP 的握手由一個(gè) offer 和一個(gè) answer 組成。
通信的兩側(cè)可能會(huì)處于不同的網(wǎng)絡(luò)環(huán)境中,有時(shí)會(huì)存在好幾層的訪問(wèn)控制、防火墻、路由跳轉(zhuǎn),所以我們需要一種方法在復(fù)雜的網(wǎng)絡(luò)環(huán)境中找到對(duì)方,并且連接到相應(yīng)的目標(biāo)。WebRTC 使用了集成了 STUN、TURN 的 ICE 來(lái)進(jìn)行雙方的數(shù)據(jù)通信。
二、創(chuàng)建一個(gè) RTCPeerConnection首先我們的目標(biāo)是在同一個(gè)頁(yè)面中創(chuàng)建兩個(gè)實(shí)時(shí)視頻,一個(gè)的數(shù)據(jù)直接來(lái)自你的攝像頭,另一個(gè)的數(shù)據(jù)來(lái)自本地創(chuàng)建的 WebRTC 連接??雌饋?lái)是這樣的:
圖圖圖。。。。。。。
首先我們創(chuàng)建一個(gè)簡(jiǎn)單的 HTML 頁(yè)面,含有兩個(gè) video 標(biāo)簽:
下面我們創(chuàng)建一個(gè) main.js 文件,先封裝一下各瀏覽器的 userMedia 和 RTCPeerConnection 對(duì)象:
function hasUserMedia() { navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; return !!navigator.getUserMedia; } function hasRTCPeerConnection() { window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection; return !!window.RTCPeerConnection; }
然后我們需要瀏覽器調(diào)用系統(tǒng)的攝像頭 API getUserMedia 獲得媒體流,注意要打開(kāi)瀏覽器的攝像頭限制。Chrome由于安全的問(wèn)題,只能在 https 下或者 localhost 下打開(kāi)攝像頭。
var yourVideo = document.getElementById("yours"); var theirVideo = document.getElementById("theirs"); var yourConnection, theirConnection; if (hasUserMedia()) { navigator.getUserMedia({ video: true, audio: false }, stream => { yourVideo.src = window.URL.createObjectURL(stream); if (hasRTCPeerConnection()) { // 稍后我們實(shí)現(xiàn) startPeerConnection startPeerConnection(stream); } else { alert("沒(méi)有RTCPeerConnection API"); } }, err => { console.log(err); } ) }else{ alert("沒(méi)有userMedia API") }
沒(méi)有意外的話,現(xiàn)在應(yīng)該能在頁(yè)面中看到一個(gè)視頻了。
下一步是實(shí)現(xiàn) startPeerConnection 方法,建立傳輸視頻數(shù)據(jù)所需要的 ICE 通信路徑,這里我們以 Chrome 為例:
function startPeerConnection(stream) { //這里使用了幾個(gè)公共的stun協(xié)議服務(wù)器 var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } }
我們使用這個(gè)函數(shù)創(chuàng)建了兩個(gè)連接對(duì)象,在 config 里,你可以任意指定 ICE 服務(wù)器,雖然有些瀏覽器內(nèi)置了默認(rèn)的 ICE 服務(wù)器,可以不用配置,但還是建議加上這些配置。下面,我們進(jìn)行 SDP 的握手。
由于是在同一頁(yè)面中進(jìn)行的通信,所以我們可以直接交換雙方的 candidate 對(duì)象,但在不同頁(yè)面中,可能需要一個(gè)額外的服務(wù)器協(xié)助這個(gè)交換流程。
三、建立 SDP Offer 和 SDP Answer瀏覽器為我們封裝好了相應(yīng)的 Offer 和 Answer 方法,我們可以直接使用。
function startPeerConnection(stream) { var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } //本方產(chǎn)生了一個(gè)offer yourConnection.createOffer().then(offer => { yourConnection.setLocalDescription(offer); //對(duì)方接收到這個(gè)offer theirConnection.setRemoteDescription(offer); //對(duì)方產(chǎn)生一個(gè)answer theirConnection.createAnswer().then(answer => { theirConnection.setLocalDescription(answer); //本方接收到一個(gè)answer yourConnection.setRemoteDescription(answer); }) }); }
和 ICE 的連接一樣,由于我們是在同一個(gè)頁(yè)面中進(jìn)行 SDP 的握手,所以不需要借助任何其他的通信手段來(lái)交換 offer 和 answer,直接賦值即可。如果需要在兩個(gè)不同的頁(yè)面中進(jìn)行交換,則需要借助一個(gè)額外的服務(wù)器來(lái)協(xié)助,可以采用 websocket 或者其它手段進(jìn)行這個(gè)交換過(guò)程。
四、加入視頻流現(xiàn)在我們已經(jīng)有了一個(gè)可靠的視頻數(shù)據(jù)傳輸通道了,下一步只需要向這個(gè)通道加入數(shù)據(jù)流即可。WebRTC 直接為我們封裝好了加入視頻流的接口,當(dāng)視頻流添加時(shí),另一方的瀏覽器會(huì)通過(guò) onaddstream 來(lái)告知用戶,通道中有視頻流加入。
yourConnection.addStream(stream); theirConnection.onaddstream = function(e) { theirVideo.src = window.URL.createObjectURL(e.stream); }
以下是完整的 main.js 代碼:
function hasUserMedia() { navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; return !!navigator.getUserMedia; } function hasRTCPeerConnection() { window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection; return !!window.RTCPeerConnection; } var yourVideo = document.getElementById("yours"); var theirVideo = document.getElementById("theirs"); var yourConnection, theirConnection; if (hasUserMedia()) { navigator.getUserMedia({ video: true, audio: false }, stream => { yourVideo.src = window.URL.createObjectURL(stream); if (hasRTCPeerConnection()) { startPeerConnection(stream); } else { alert("沒(méi)有RTCPeerConnection API"); } }, err => { console.log(err); }) } else { alert("沒(méi)有userMedia API") } function startPeerConnection(stream) { var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onaddstream = function(e) { theirVideo.src = window.URL.createObjectURL(e.stream); } yourConnection.addStream(stream); yourConnection.createOffer().then(offer => { yourConnection.setLocalDescription(offer); theirConnection.setRemoteDescription(offer); theirConnection.createAnswer().then(answer => { theirConnection.setLocalDescription(answer); yourConnection.setRemoteDescription(answer); }) }); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/79888.html
摘要:最后,消息成功抵達(dá)并顯示在頁(yè)面上。在中,所有的數(shù)據(jù)都使用數(shù)據(jù)報(bào)傳輸層安全性。如果應(yīng)用知識(shí)簡(jiǎn)單的一對(duì)一文件傳輸,使用不可靠的數(shù)據(jù)通道將需要設(shè)計(jì)一定的響應(yīng)重傳協(xié)議。目前建議的最大塊大小為。 本文翻譯自WebRTC data channels 在兩個(gè)瀏覽器中,為聊天、游戲、或是文件傳輸?shù)刃枨蟀l(fā)送信息是十分復(fù)雜的。通常情況下,我們需要建立一臺(tái)服務(wù)器來(lái)轉(zhuǎn)發(fā)數(shù)據(jù),當(dāng)然規(guī)模比較大的情況下,會(huì)擴(kuò)展成...
摘要:下面我們就看一下具體如何申請(qǐng)權(quán)限靜態(tài)權(quán)限申請(qǐng)?jiān)陧?xiàng)目中的中增加以下代碼動(dòng)態(tài)權(quán)限申請(qǐng)隨著的發(fā)展,對(duì)安全性要求越來(lái)越高。其定義如下通過(guò)上面的代碼我們就將顯示視頻的定義好了。當(dāng)發(fā)送消息,并收到服務(wù)端的后,其狀態(tài)變?yōu)椤?作者:李超,如遇到相關(guān)問(wèn)題,可以點(diǎn)擊這里與作者直接交流。 前言 在學(xué)習(xí) WebRTC 的過(guò)程中,學(xué)習(xí)的一個(gè)基本步驟是先通過(guò) JS 學(xué)習(xí) WebRTC的整體流程,在熟悉了整體流程之后,...
摘要:而現(xiàn)在我們可以利用多種工具框架進(jìn)行跨平臺(tái)開(kāi)發(fā)。實(shí)現(xiàn)視頻會(huì)議的幾種思路如何利用實(shí)現(xiàn)一個(gè)視頻會(huì)議應(yīng)用這主要取決于使用什么技術(shù)來(lái)實(shí)現(xiàn)作為業(yè)務(wù)核心的部分。通過(guò)與技術(shù)結(jié)合,實(shí)現(xiàn)了網(wǎng)頁(yè)端多方音視頻通訊,可以快速實(shí)現(xiàn)部分的開(kāi)發(fā)。 作者簡(jiǎn)介:張乾澤,聲網(wǎng) Agora Web 研發(fā)工程師 對(duì)于在線教育、醫(yī)療、視頻會(huì)議等場(chǎng)景來(lái)講,開(kāi)發(fā)面向 Windows、Mac 的跨平臺(tái)客戶端是必不可少的一步。在過(guò)去,每...
摘要:為了使連接起作用,對(duì)等方必須獲取元數(shù)據(jù)的本地媒體條件例如,分辨率和編解碼器功能,并收集應(yīng)用程序主機(jī)的可能網(wǎng)絡(luò)地址,用于來(lái)回傳遞這些關(guān)鍵信息的信令機(jī)制并未內(nèi)置到中。所有特定于多媒體的元數(shù)據(jù)都使用協(xié)議傳遞。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 18 篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過(guò)了前面的章節(jié),可以在這里...
閱讀 3317·2023-04-25 15:44
閱讀 1964·2019-08-30 13:11
閱讀 2961·2019-08-30 11:11
閱讀 3177·2019-08-29 17:21
閱讀 1381·2019-08-29 15:38
閱讀 1031·2019-08-29 12:49
閱讀 1890·2019-08-28 18:19
閱讀 3298·2019-08-26 14:01