每日一句
人生的挑戰(zhàn),無(wú)處不在,滿懷信心,輕裝上路,明天永遠(yuǎn)是充滿希望的戰(zhàn)場(chǎng)。
承接上文
承接上文的內(nèi)容介紹完相關(guān)WebRTC技術(shù)的概念和發(fā)展歷程后,開(kāi)始初步摸索一下相關(guān)WebRTC技術(shù)的功能和原理。??【W(wǎng)ebRTC原理探索】未來(lái)可期,WebRTC的誕生發(fā)展
技術(shù)回顧
WebRTC概念定義
WebRTC名稱源自網(wǎng)頁(yè)實(shí)時(shí)通信(Web Real-Time Communication)的縮寫,是一個(gè)支持網(wǎng)頁(yè)瀏覽器進(jìn)行實(shí)時(shí)語(yǔ)音對(duì)話或視頻對(duì)話的技術(shù),是谷歌2010年以6820萬(wàn)美元收購(gòu)Global IP Solutions公司而獲得的一項(xiàng)技術(shù)。
WebRTC功能范疇
WebRTC是一個(gè)開(kāi)源項(xiàng)目,旨在使得瀏覽器能為實(shí)時(shí)通信(RTC)提供簡(jiǎn)單的 JavaScript接口。
WebRTC不僅可傳輸視頻,也可以傳輸其他數(shù)據(jù)例如文本、圖片等。需要注意的是,WebRTC并不是瀏覽器的一個(gè)子集,瀏覽器只是根據(jù) WebRTC 的標(biāo)準(zhǔn)協(xié)議實(shí)現(xiàn)了 WebRTC的原生接口。Android 和 IOS 系統(tǒng)也支持 WebRTC 。
WebRTC應(yīng)用包括下面四個(gè)主要的概念
信令服務(wù)器(Signalling servers)
ICE服務(wù)器(ICE servers)
媒體服務(wù)器(Media servers)
JavaScript接口 (JavaScript API)
WebRTC已經(jīng)納入HTML5標(biāo)準(zhǔn)
目前支持WebRTC協(xié)議的瀏覽器有:Chrome、Firefox Opera,IE不支持~
WebRTC沒(méi)有指定具體的信令協(xié)議,具體的信令協(xié)議留給應(yīng)用程序?qū)崿F(xiàn)。
WebRTC使用JSEP協(xié)議建立會(huì)話,什么是JSEP后面說(shuō)
WebRTC采用ICE實(shí)現(xiàn)NAT穿越。
WebRTC客戶端之間可以進(jìn)行點(diǎn)對(duì)點(diǎn)的媒體傳輸。
WebRTC的核心組件
音視頻引擎:OPUS、VP8/VP9、H264
傳輸層協(xié)議:底層傳輸協(xié)議為UDP
媒體協(xié)議:SRTP/SRTCP
數(shù)據(jù)協(xié)議:DTLS/SCTP
P2P內(nèi)網(wǎng)穿透:STUN / TURN / ICE / Trickle ICE
信令與SDP協(xié)商:HTTP / WebSocket / SIP、 Offer Answer 模型
下圖為WebRTC內(nèi)部結(jié)構(gòu)簡(jiǎn)化圖:
最底層是硬件設(shè)備。
上面是音頻捕獲模塊和視頻捕獲模塊。
-
中間部分為音視頻引擎:
音頻引擎負(fù)責(zé)音頻采集和傳輸,具有降噪、回聲消除等功能。
視頻引擎負(fù)責(zé)網(wǎng)絡(luò)抖動(dòng)優(yōu)化,互聯(lián)網(wǎng)傳輸編解碼優(yōu)化。
在音視頻引擎之上是一套C++ API,在C++ 的API之上是提供給瀏覽器的Javascript API。
JSEP
JSEP(JavaScript Session Establishment Protocol,JavaScript會(huì)話建立協(xié)議)是一個(gè)信令A(yù)PI,允許開(kāi)發(fā)者構(gòu)建更強(qiáng)大的應(yīng)用程序以及增加在信令協(xié)議選擇上的靈活性。
-
JSEP是干什么的呢,一方面提供接口如createOffer()供web應(yīng)用程序調(diào)用生成SDP,另一方面提供ICE功能接口。這些功能都由瀏覽器實(shí)現(xiàn),瀏覽器WebRTC傳輸信令(offer/answer)采用Websocket。
建立會(huì)話最關(guān)鍵的就是媒體的協(xié)商,WebRTC雖然沒(méi)有指定具體的信令協(xié)議,但是媒體協(xié)商采用了SDP協(xié)議。
如果web應(yīng)用程序不使用額外的信令協(xié)議,僅使用JSEP,兩個(gè)WebRTC client(同一個(gè)WebRTC client程序,兩處登陸)之間也是可以建立鏈接的,即只要應(yīng)用程序能解析用WS傳遞過(guò)來(lái)的Offer/Answer消息,提取出其中的SDP和ICE信息就可以了。
github上codelabdemo就是不用其他信令協(xié)議,直接使用JSEP生成offer/answer信令,然后采用ws協(xié)議傳輸實(shí)現(xiàn)的。
JSEP并不是信令協(xié)議,可以在JSEP的基礎(chǔ)上引入SIP等信令協(xié)議,使WebRTC應(yīng)用功能更加完備。
信令服務(wù)器
- 信令服務(wù)器主要用于在兩個(gè)用戶之間交換信息。雖然WebRTC是點(diǎn)對(duì)點(diǎn)通信,但還是需要服務(wù)器來(lái)初始化連接,并傳遞一些信息。
WebRTC沒(méi)有定義用于建立信道的信令的協(xié)議,因此可以使用任意的傳輸方式,例如 WebSocket, XMPP, SIP, AJAX。
- 可以使用實(shí)時(shí)的傳輸協(xié)議比如WebSocket來(lái)交換數(shù)據(jù),也可以使用簡(jiǎn)單的 GET/POST方式輪詢服務(wù)器來(lái)獲取數(shù)據(jù)。
信令服務(wù)器傳送的數(shù)據(jù)有
- 協(xié)商媒體功能和設(shè)置
- 標(biāo)識(shí)和驗(yàn)證會(huì)話參與者的身份
- 控制媒體會(huì)話、指示進(jìn)度、更改會(huì)話和終止會(huì)話
其中只有第一項(xiàng)的必備功能。其他都可以根據(jù)業(yè)務(wù)需求自由調(diào)整。
SDP協(xié)議
媒體協(xié)商最重要的功能在于,為參與點(diǎn)對(duì)點(diǎn)通信的兩個(gè)瀏覽器之間交換會(huì)話描述協(xié)議「SDP」。
「SDP」包含瀏覽器的RTP媒體棧配置所需的全部信息,包括媒體類型(音頻、視頻、數(shù)據(jù))、所需的編解碼器,用于編解碼器參數(shù)或設(shè)置,以及有關(guān)帶寬的信息。
此外,信令通道還用于交換候選地址,以便進(jìn)行ICE打洞。
信令互通方案
WebRTC與SIP互通
要想讓W(xué)ebRTC與SIP互通,要解決兩個(gè)層面的問(wèn)題:信令層和媒體層。
兩個(gè)網(wǎng)絡(luò)使用的信令機(jī)制不同,所以要進(jìn)行信令的轉(zhuǎn)換,才能完成媒體的協(xié)商,建立會(huì)話。媒體層要完成編碼的轉(zhuǎn)換,以及RTP/SRTP轉(zhuǎn)換等功能。
這里主要說(shuō)項(xiàng)信令層面的互通。
目前SIP和WebRTC信令上互通有兩種解決方案:
-
JavaScript實(shí)現(xiàn)SIP協(xié)議棧,WebRTC應(yīng)用程序基于這個(gè)協(xié)議棧開(kāi)發(fā)。WebRTC Client發(fā)出的信令就是SIP信令,但一般采用websocket為信令傳輸協(xié)議。
- 這樣WebRTC client就可以直接注冊(cè)到支持WS的SIP Server上了。jssip 、sipml5都是這種解決方案。
-
通過(guò)轉(zhuǎn)換網(wǎng)關(guān)實(shí)現(xiàn)協(xié)議的轉(zhuǎn)換,從而互通。一個(gè)開(kāi)源的網(wǎng)關(guān)項(xiàng)目就是 WebRTC2SIP。
- WebRTC2SIP是一個(gè)功能很完善的網(wǎng)關(guān),既實(shí)現(xiàn)了信令層,也實(shí)現(xiàn)了媒體層,編碼轉(zhuǎn)換功能很強(qiáng)大,也可以直接當(dāng)做媒體網(wǎng)關(guān),用于編解碼,溝通兩端的媒體。
ICE服務(wù)器
實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)通信的關(guān)鍵在于兩個(gè)瀏覽器之間能直接發(fā)送和接收數(shù)據(jù)包,但一般情況下,瀏覽器或手機(jī)都是通過(guò)路由器訪問(wèn)Internet,所以存在網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)。
NAT之內(nèi)的IP地址是私有地址,外部無(wú)法訪問(wèn)。分配給NAT的IP地址才是公共地址。NAT每次從內(nèi)部到外部轉(zhuǎn)發(fā)數(shù)據(jù)包時(shí)都使用公共地址。
交互式建立連接(ICE)是一種標(biāo)準(zhǔn)穿透協(xié)議,它利用STUN和TURN服務(wù)器來(lái)建立連接。
STUN服務(wù)器可以遍歷NAT,獲取瀏覽器的候選地址,包括私有地址、外層NAT的公共 IP地址等。
通信信令通道可以交換候選地址,瀏覽器一旦發(fā)送并收到了候選地址,就會(huì)開(kāi)始進(jìn)行連接檢查,若檢查成功,便使用該候選項(xiàng)發(fā)送媒體。
在大多情況下,通過(guò)穿透可以建立直接對(duì)等連接。但是,若NAT或防火墻限制非常嚴(yán)格,無(wú)法建立連接,就只能通過(guò)TURN服務(wù)器中繼媒體。
媒體服務(wù)器
媒體服務(wù)器不是必須的,但在多方會(huì)話或需要對(duì)媒體做額外處理的情況下可以考慮加入。對(duì)于有多個(gè)瀏覽器參與的會(huì)議,可以采用一個(gè)集中式媒體服務(wù)器。在這種情況下,美國(guó)瀏覽器都只需與媒體服務(wù)器建立單個(gè)連接即可,這種結(jié)構(gòu)的優(yōu)勢(shì)是額能夠擴(kuò)展非常大的會(huì)話,同時(shí)可以在最大限度上減少當(dāng)有新加入者加入會(huì)話事美國(guó)瀏覽器所需的處理工作量。同時(shí),媒體服務(wù)器也可對(duì)媒體進(jìn)行分析、處理、保存等工作。
JavaScript接口
getUserMedia
通過(guò)調(diào)用navigator.getUserMedia()可以獲取視頻或音頻的數(shù)據(jù),constraints 參數(shù)可以選擇是否獲取視頻音頻。下面是一個(gè)簡(jiǎn)單的示例
var constraints = {
audio: false,
video: true
};
var video = document.querySelector('video');
function successCallback(stream) {
if (window.URL) {
video.src = window.URL.createObjectURL(stream);
} else {
video.src = stream;
}
}
function errorCallback(error) {
console.log('navigator.getUserMedia error: ', error);
}
navigator.getUserMedia(constraints, successCallback, errorCallback);
RTCPeerConnection
RTCPeerConnection是WebRTC中最重要的一個(gè)接口,用于確定ICE服務(wù)器、交換 SDP。連接過(guò)程如下:
創(chuàng)建RTCPeerConnection對(duì)象
- RTCPeerConnection的參數(shù)用于確定ICE服務(wù)器,下面是使用了 google 開(kāi)放的 STUN 服務(wù)器
let iceServer = {
"iceServers": [{
"url": "stun:stun.l.google.com:19302"
}]
};
let pc = new RTCPeerConnection(servers);
- 將媒體流放入 RTCPeerConnection 對(duì)象中
pc.addStream(localStream);
通過(guò)offer和answer交換SDP描述符
-
甲和乙各自建立一個(gè)PC實(shí)例
甲通過(guò)PC所提供的createOffer()方法建立一個(gè)包含甲的SDP描述符的offer信令。
甲通過(guò)PC所提供的setLocalDescription()方法,將甲的SDP描述符交給甲的PC實(shí)例。
-
甲將offer信令通過(guò)服務(wù)器發(fā)送給乙
乙將甲的offer信令中所包含的的SDP描述符提取出來(lái),通過(guò)PC所提供的 setRemoteDescription()方法交給乙的PC實(shí)例
乙通過(guò)PC所提供的createAnswer()方法建立一個(gè)包含乙的SDP描述符 answer信令
乙通過(guò)PC所提供的setLocalDescription()方法,將乙的SDP描述符交給乙的PC實(shí)例
乙將answer信令通過(guò)服務(wù)器發(fā)送給甲
甲接收到乙的answer信令后,將其中乙的SDP描述符提取出來(lái),調(diào)用setRemoteDescripttion()方法交給甲自己的PC實(shí)例。
ICE打洞
- 當(dāng)網(wǎng)絡(luò)候選可用時(shí),通過(guò)信令服務(wù)器將其發(fā)送到對(duì)方瀏覽器
pc.onicecandidate = function(event) {
if (event.candidate) {
sendToServer(event.candidate)
}
};
- 當(dāng)接受到對(duì)方網(wǎng)絡(luò)候選時(shí),將其加入
let candidate = new RTCIceCandidate(candidate);
pc.addIceCandidate(candidate);
- 監(jiān)聽(tīng)對(duì)方發(fā)送的媒體是否可用,并播放媒體
pc.onaddstream = event => {
remoteVideo.src = window.URL.createObjectURL(event.stream);
}
RTCDataChannel
RTCDataChannel是RTCPeerConnectionAPI的一部分,只有在創(chuàng)建了 RTCPeerConnection實(shí)例后才能創(chuàng)建數(shù)據(jù)通道。
數(shù)據(jù)通道可以用于發(fā)送文本或是文件。
pc = new RTCPeerConnection();
dc = pc.createDataChannel('dc');
dc.onmessage = event => console.log(event.data);
dc.send('text');
dc.sed(new arraybuffer(32))
在另一端可以使用 ondatachannel 獲得 RTCDataChannel 對(duì)象
pc.ondatachannel = event => dc = event.channel;