初識(shí)WebRTC

WebRTC

1,WebRTC入門基礎(chǔ)

什么是WebRTC

WebRtc(Web Real-Time Communication)支持網(wǎng)頁(yè)瀏覽器進(jìn)行實(shí)時(shí)語(yǔ)音對(duì)話或視頻對(duì)話的技術(shù);

瀏覽器本身不支持互相之間直接建立信道進(jìn)行通信,都是通過(guò)服務(wù)器進(jìn)行中轉(zhuǎn).比如現(xiàn)在有兩個(gè)客戶端,甲和乙,他們倆想要通信,首先需要甲和服務(wù)器.乙和服務(wù)器之間建立信道.甲給乙發(fā)送消息時(shí),甲先將消息發(fā)送到服務(wù)器上,服務(wù)器對(duì)家的消息進(jìn)行中轉(zhuǎn),發(fā)送到乙處,反過(guò)來(lái)也是一樣.這樣甲和乙之間的依次消息要通過(guò)兩段信道,通信的效率同時(shí)受制于這兩端信道的帶寬.同時(shí)這樣的信道并不適合數(shù)據(jù)流的傳輸

WebRTC是一個(gè)開源項(xiàng)目,旨在使得瀏覽器能為實(shí)時(shí)通信(RTC)提供簡(jiǎn)單的JavaScript接口;讓瀏覽器提供JS的即時(shí)通信接口。這個(gè)接口所創(chuàng)立的信道并不是像WebSocket一樣,打通一個(gè)瀏覽器與WebSocket服務(wù)器之間的通信,而是通過(guò)一系列的信令,建立一個(gè)瀏覽器與瀏覽器之間(peer-to-peer)的信道,這個(gè)信道可以發(fā)送任何數(shù)據(jù),而不需要經(jīng)過(guò)服務(wù)器。并且WebRTC通過(guò)實(shí)現(xiàn)MediaStream,通過(guò)瀏覽器調(diào)用設(shè)備的攝像頭、話筒,使得瀏覽器之間可以傳遞音頻和視頻

三個(gè)接口

WebRTC實(shí)現(xiàn)了三個(gè)API,分別是:

  • MediaStream: 通過(guò)MediaStream的API能夠通過(guò)設(shè)備的攝像頭及話筒獲得視頻,音頻的同步流
  • RTCPeerConnection: RTCPeerConnection是WebRTC用于構(gòu)建點(diǎn)對(duì)點(diǎn)之間穩(wěn)定高效的流傳輸?shù)慕M件
  • RTCDataChannel: RTCDataChannel使得瀏覽器之間(點(diǎn)對(duì)點(diǎn))建立一個(gè)高吞吐量.低延時(shí)的信道,用于傳輸任意數(shù)據(jù)

MediaStream(getUserMedia)

通過(guò)MediaStream的API能夠通過(guò)設(shè)備的攝像頭及話筒獲得視頻,音頻的同步流

如果調(diào)用

可以通過(guò)調(diào)用navigator.getUserMedia(),這個(gè)方法接受三個(gè)參數(shù):
1,一個(gè)約束對(duì)象(constrains object)
2,一個(gè)調(diào)用成功的回調(diào)函數(shù),如果調(diào)用成功,傳遞給他一個(gè)流對(duì)象
3,一個(gè)調(diào)用失敗的額回調(diào)函數(shù),如果調(diào)用失敗,傳遞給他一個(gè)錯(cuò)誤對(duì)象

瀏覽器的兼容性

const getUserMedia = (navigator.getUserMedia || 
                    navigator.webkitGetUserMedia || 
                    navigator.mozGetUserMedia || 
                    navigator.msGetUserMedia);

約束對(duì)象

可以被設(shè)置在getUserMedia()RTCPeerConnectionaddStream方法中,這個(gè)約束對(duì)象是WebRTC用來(lái)指定接受什么樣的流,其中可以定義如下屬性

  • video: 是否接受視頻流
  • audio: 是否接受音頻流
  • MinWidth: 視頻流最小寬度
  • MaxWidth: 視頻流最大寬度
  • MinHeight: 視頻流最小高度
  • MaxHeight: 視頻流最大高度
  • MinAspectRatio: 視頻流最小寬高比
  • MaxAspectRatio: 視頻流最大寬高比
  • MinFramerate: 視頻流最小幀速率
  • MaxFramerate: 視頻流最大幀速率

例子

<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>GetUserMedia實(shí)例</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>

這里使用getUserMedia獲得流之后,需要將其輸出,一般綁定到video標(biāo)簽上輸出,需要使用window.URL.createObjectURL(localMediaStream)來(lái)創(chuàng)造能在video中使用 src屬性播放的Blob URL,注意在video上加入autoplay屬性,否則只能撲火到一張圖片
流創(chuàng)建完畢后可以通過(guò)label屬性來(lái)獲得其唯一的標(biāo)識(shí),還可以通過(guò)getAudioTracks()和getVideoTracks()方法來(lái)獲得流的追蹤對(duì)象數(shù)組(如果沒(méi)有開啟某種流,它的追蹤對(duì)象數(shù)組將是一個(gè)空數(shù)組)

PTCPeerConnection

WebRTC使用RTCPeerConnection來(lái)在瀏覽器之間傳遞流數(shù)據(jù),這個(gè)流數(shù)據(jù)通道是點(diǎn)對(duì)點(diǎn)的,不需要經(jīng)過(guò)服務(wù)器進(jìn)行中轉(zhuǎn).但是這并不意味著我們能拋棄服務(wù)器.我們?nèi)匀恍枰鼇?lái)為我們傳遞信令來(lái)建立這個(gè)信道.WebRTC沒(méi)有定義用于建立信道的信令的協(xié)議:信令并不是RTCPeerConnection API的一部分

信令

既然沒(méi)有定義具體的信令的協(xié)議,我們就可以選擇任意方式(AJAX、WebSocket),采用任意的協(xié)議(SIP、XMPP)來(lái)傳遞信令,建立信道.比如可以用node的ws模塊,在在WebSocket上傳遞信令
需要信令來(lái)交換的信息有三種:

  • session的信息:用來(lái)初始化通信還有報(bào)錯(cuò)
  • 網(wǎng)絡(luò)配置: 比如IP地址和端口啥的
  • 媒體適配: 發(fā)送方和接受方的瀏覽器能接受什么樣的編碼器和分辨率

通過(guò)服務(wù)器建立信道

就算WebRTC提供瀏覽器之間點(diǎn)對(duì)點(diǎn)信道的數(shù)據(jù)傳輸,但是建立這個(gè)信道,必須有服務(wù)器的參與,WebRTC需要服務(wù)器對(duì)其進(jìn)行4方面的功能支持

  • 1,用戶發(fā)現(xiàn)以及通信
  • 2,信令傳遞
  • 3,NAT/防火墻穿越
  • 4,如果點(diǎn)對(duì)點(diǎn)通信建立失敗,可以作為中轉(zhuǎn)服務(wù)器

NAT/防火墻穿越技術(shù)

建立點(diǎn)對(duì)點(diǎn)信道的一個(gè)常見問(wèn)題,就是NAT穿越技術(shù)。在處于使用了NAT設(shè)備的私有TCP/IP網(wǎng)絡(luò)中的主機(jī)之間需要建立連接時(shí)需要使用NAT穿越技術(shù)。以往在VoIP領(lǐng)域經(jīng)常會(huì)遇到這個(gè)問(wèn)題。目前已經(jīng)有很多NAT穿越技術(shù),但沒(méi)有一項(xiàng)是完美的,因?yàn)镹AT的行為是非標(biāo)準(zhǔn)化的。這些技術(shù)中大多使用了一個(gè)公共服務(wù)器,這個(gè)服務(wù)使用了一個(gè)從全球任何地方都能訪問(wèn)得到的IP地址。在RTCPeeConnection中,使用ICE框架來(lái)保證RTCPeerConnection能實(shí)現(xiàn)NAT穿越

ICE,全名叫交互式連接建立(Interactive Connectivity Establishment),一種綜合性的NAT穿越技術(shù),它是一種框架,可以整合各種NAT穿越技術(shù)如STUN、TURN(Traversal Using Relay NAT 中繼NAT實(shí)現(xiàn)的穿透)。ICE會(huì)先使用STUN,嘗試建立一個(gè)基于UDP的連接,如果失敗了,就會(huì)去TCP(先嘗試HTTP,然后嘗試HTTPS),如果依舊失敗ICE就會(huì)使用一個(gè)中繼的TURN服務(wù)器。

RTCDataChannel

既然能建立點(diǎn)對(duì)點(diǎn)的信道來(lái)傳遞實(shí)時(shí)的視頻,音頻數(shù)據(jù)流.為什么不你能用這個(gè)信道傳一點(diǎn)其他數(shù)據(jù)呢?RTCDataChannel就是用來(lái)干這個(gè)的,基于它我們可以再瀏覽器之間傳輸任意數(shù)據(jù),DataChannel是建立在PeerConnection上的,不能單獨(dú)使用

我們可以使用channel = pc.createDataCHannel("someLabel");來(lái)在PeerConnection的實(shí)例上創(chuàng)建Data Channel,并給與它一個(gè)標(biāo)簽
DataChannel使用方式幾乎和WebSocket一樣,有幾個(gè)事件:

  • onopen
  • onclose
  • onmessage
  • onerror

同時(shí)它有幾個(gè)狀態(tài),可以通過(guò)readyState獲取:

  • 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ù)

通過(guò)Data Channel發(fā)送文件大致思路

JavaScript已經(jīng)提供了File API從input[type='file']的元素中提取文件,并通過(guò)FileReader來(lái)將文件的轉(zhuǎn)換成DataURL,這也意味著我們可以將DataURL分成多個(gè)碎片來(lái)通過(guò)Channel來(lái)進(jìn)行文件傳輸

2,信令

WebRTC的服務(wù)器

WebRTC提供了瀏覽器到瀏覽器(點(diǎn)對(duì)點(diǎn))之間的通信,但并不意味著WebRTC不需要服務(wù)器。暫且不說(shuō)基于服務(wù)器的一些擴(kuò)展業(yè)務(wù),WebRTC至少有兩件事必須要用到服務(wù)器:

  1. 瀏覽器之間交換建立通信的元數(shù)據(jù)(信令)必須通過(guò)服務(wù)器
  2. 為了穿越NAT和防火墻

為什么需要信令

我們需要通過(guò)一系列的信令來(lái)建立瀏覽器之間的通信.而具體需要通過(guò)信令交換哪些內(nèi)容,下面簡(jiǎn)單的列了一下
1,用來(lái)控制通信開啟或者關(guān)閉的連接控制信息
2,發(fā)生錯(cuò)誤時(shí)用來(lái)彼此告知的信息
3,媒體流元數(shù)據(jù),比如像解碼器,解碼器的配置,帶寬,媒體類型
4,用來(lái)建立安全連接的關(guān)鍵數(shù)據(jù)
5,外界所看到的網(wǎng)絡(luò)上的數(shù)據(jù),比如IP地址,端口等
在建立連接之前,瀏覽器之間顯然沒(méi)有辦法傳遞數(shù)據(jù)。所以我們需要通過(guò)服務(wù)器的中轉(zhuǎn),在瀏覽器之間傳遞這些數(shù)據(jù),然后建立瀏覽器之間的點(diǎn)對(duì)點(diǎn)連接。但是WebRTC API中并沒(méi)有實(shí)現(xiàn)這些。

會(huì)話描述協(xié)議(Session Description Protocol)

JSEP將客戶端之前傳遞的信令分為兩種:offer信令和answer信令.他們主要內(nèi)容的格式都遵循會(huì)話描述協(xié)議(Session Description Protocol,簡(jiǎn)稱SDP)
是一個(gè)在點(diǎn)對(duì)點(diǎn)連接中描述自己的字符串.我們可以將其封裝在JSON中進(jìn)行傳輸,在PeerConnection建立后將通過(guò)服務(wù)器中轉(zhuǎn)后,將自己的SDP描述符和對(duì)方的SDP描述符交給PeerConnection就行了

信令與PTCPeerConnection建立

1,通過(guò)offer和answer交換SDP描述符

大致上在兩個(gè)用戶(甲和乙)之間建立點(diǎn)對(duì)點(diǎn)連接流程應(yīng)該是這個(gè)樣子(這里不考慮錯(cuò)誤的情況,RTCPeerConnection簡(jiǎn)稱PC):

  1. 甲和乙各自建立一個(gè)PC實(shí)例
  2. 甲通過(guò)PC所提供的createOffer()方法建立一個(gè)包含甲的SDP描述符的offer信令
  3. 甲通過(guò)PC所提供的setLocalDescription()方法,將甲的SDP描述符交給甲的PC實(shí)例
  4. 甲將offer信令通過(guò)服務(wù)器發(fā)送給乙
  5. 乙將甲的offer信令中所包含的的SDP描述符提取出來(lái),通過(guò)PC所提供的setRemoteDescription()方法交給乙的PC實(shí)例
  6. 乙通過(guò)PC所提供的createAnswer()方法建立一個(gè)包含乙的SDP描述符answer信令
  7. 乙通過(guò)PC所提供的setLocalDescription()方法,將乙的SDP描述符交給乙的PC實(shí)例
  8. 乙將answer信令通過(guò)服務(wù)器發(fā)送給甲
  9. 甲接收到乙的answer信令后,將其中乙的SDP描述符提取出來(lái),調(diào)用setRemoteDescripttion()方法交給甲自己的PC實(shí)例

甲和乙所創(chuàng)建的PC實(shí)例都包含了甲和乙的SDP描述符,接下來(lái)--獲取連接兩端主機(jī)的網(wǎng)絡(luò)地址

2,通過(guò)ICE框架建立NAT/防火墻穿越的連接

var iceServer = {
    "iceServers": [{
        "url": "stun:stun.l.google.com:19302"
    }]
};
var pc = new RTCPeerConnection(iceServer);

當(dāng)然這兩個(gè)地址也需要交換,交換流程如下(RTCPeerConnection簡(jiǎn)稱PC);
1,甲,乙各創(chuàng)建配置了ICE服務(wù)器的PC實(shí)例,并為其添加onicecandidate事件回調(diào)
2,當(dāng)網(wǎng)絡(luò)候選可用時(shí),將會(huì)調(diào)用onicecandidate函數(shù)
3,在回調(diào)函數(shù)內(nèi)部,甲或乙將網(wǎng)絡(luò)候選色消息封裝在ICE Candidate命令中,通過(guò)服務(wù)器中轉(zhuǎn),傳遞給對(duì)方
4,甲或乙接收到對(duì)方通過(guò)服務(wù)器中轉(zhuǎn)所發(fā)送過(guò)來(lái)ICE Candidate信令時(shí),將其解析并并獲得網(wǎng)絡(luò)候選,將其通過(guò)PC實(shí)例的addIceCandidate()添加到PC實(shí)例中

這樣連接就創(chuàng)建完成了,可以向RTCPeerConnection中通過(guò)addStream()加入流來(lái)傳輸媒體流出具.將流加入到RTCPeerConnection實(shí)例中,對(duì)方就可以通過(guò)onaddstream所綁定的回調(diào)函數(shù)監(jiān)聽到了,調(diào)用addStream()可以再連接完成之前,在連接建立之后,對(duì)方一樣能監(jiān)聽到媒體流

參考

github
blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容