Translated from WebRTC in the real world: STUN, TURN and signaling.
最近剛接觸到WebRTC,網上看到這篇介紹WebRTC的文章不錯,仔細讀了讀還算有用,分享出來能幫到一些剛入門的人也挺好的,翻譯不好的地方可以直接看原文。
WebRTC可以進行P2P點對點通信,但是WebRTC仍然需要服務器:
- 客戶端需要服務器交換一些數據來協調通信,這稱之爲信令。
- 使用服務器來應對NAT網絡地址轉換和防火牆。
在本文中,將介紹如何構建信令服務,以及如何使用STUN和TURN服務器來處理WebRTC在實際使用過程中的連接問題。本文還將解釋WebRTC應用程序如何處理多方通話,並與諸如VoIP和PSTN(AKA電話)之類的服務進行交互。
如果您不熟悉WebRTC的基本知識,我們強烈建議您在閱讀本文之前先看一下如何開始使用WebRTC。
什麼是信令?
信令用於協調通信,WebRTC應用開始通話之前,客戶端需要交換一些信息(信令):
- 用於打開或關閉通信的會話控制消息。
- 錯誤信息。
- 媒體元數據,例如編解碼器和編解碼器設置,帶寬和媒體類型。
- 用於建立安全連接的的祕鑰信息。
- 主機的IP和端口等網絡信息。
客戶端之間來回傳遞這些消息需要實現一種信令通信方式,但是WebRTC的API並沒有實現信令通信機制,所以使用者需要自己去實現。下面會介紹一些構建信令服務的方法,但是這裏可以先了解一下這些背景。
WebRTC爲什麼不規定信令標準?
爲了避免冗餘並提高與已有技術的兼容性,WebRTC標準未規定信令方法和協議。JavaScript會話建立協議(JSEP)描述了一種大致的方法:
WebRTC設計思想是完全指定和控制媒體層面,把信令層面儘可能的交給應用去實現。這是因爲不同的應用程序可能更喜歡使用不同的信令協議,比如已經存在的SIP或者Jingle信令協議,抑或一些針對應用定製的協議。在這種方法中,需要交換的關鍵信息是多媒體會話描述,它指定了建立媒體連接所必需的傳輸和媒體配置信息。
JSEP的體系結構使瀏覽器不必保存狀態:也就是說,作爲一個信令狀態機,如果在每次重新加載頁面時丟失信令數據,這將是有問題的。相反,可以在服務器上保存信令狀態。
JSEP 架構
JSEP需要在 offer / 提議 和 answer / 應答 的點與點之間交換上文提到的媒體元數據信息。交換信息的兩個點之間使用SDP會話描述協議進行通信。SDP協議消息格式大概是這個樣子:
v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
…
想查看SDP官方文檔,點這裏 SDP for the WebRTC。
WebRTC被設計成可以通過修改一些SDP文本中的值來調整會話,使用JavaScript操作SDP有點麻煩,也有討論WebRTC的未來版本是否應該使用JSON代替SDP,但目前因爲使用這個方法還有一些優點所以堅持使用SDP。
RTCPeerConnection + 信令:offer(提議)、answer(應答)和candidate(候選地址)
這幾個詞翻譯過來也不好理解,算了不翻譯了。還有那個P2P的peer就先翻譯爲端點吧,總不能直接說是個P。
RTCPeerConnection是WebRTC應用程序在點對點之間創建連接並傳送音頻和視頻的API。
要想創建音視頻通信連接,RTCPeerConnection有兩個任務:
- 確定本地媒體信息,例如分辨率和編解碼器信息。這是用於offer和answer機制的元數據。
- 獲取應用程序主機的網絡地址,稱爲candidate。
一旦確定了本地數據,就必須通過信令機制與遠程端點的進行交換。
假如有這麼一個場景,Alice嘗試與Eve進行通話,下面是完整的 offer / answer 機制的細節:
- Alice創建了一個
RTCPeerConnection
對象。 - Alice使用
RTCPeerConnection
的createOffer()
方法創建了一個offer
(一個SDP會話描述文本)。 - Alice調用
setLocalDescription()
將她的offer
設置爲本地描述。 - Alice把
offer
轉換爲字符串,並使用信令機制將其發送給Eve。 - Eve對Alice的
offer
調用setRemoteDescription()
函數,爲了讓他的RTCPeerConnection
知道Alice的設置。 - Eve調用
createAnswer()
函數創建answer
。 - Eve通過調用
setLocalDescription()
將她的answer
設置爲本地描述。 - Eve使用信令機制把她字符串化的的
answer
傳給Alice。 - Alice使用
setRemoteDescription()
函數將Eve的answer
設置爲遠程會話描述。
Alice和Eve也需要去交換網絡信息。“查找候選地址candidate”一詞是指使用ICE框架查找網絡接口和端口的過程。
- Alice創建
RTCPeerConnection
對象的時候會生成一個onicecandidate
句柄。 - 這個句柄在網絡
candidate
生效時會被調用。 - Alice通過信令通道將字符串化的
candidate
數據發送給Eve。 - 當Eve從Alice獲取
candidate
消息時,她調用addIceCandidate()
,將candidate
添加到遠程對等描述中。
JSEP支持ICE Candidate Trickling,它允許調用方在初始化 offer 之後遞增地向被調用方提供候選地址candidate,並且允許被調用方在沒有等待所有候選地址candidate到達的情況下開始進行操作並建立連接。
關於信令的WebRTC代碼
下面這段代碼總結了信令的完整過程,這段代碼假定存在SignalingChannel信令機制。後面會詳細討論信令。
// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stuns:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
// 給另外一個端點發送candidate
pc.onicecandidate = ({candidate}) => signaling.send({candidate});
// 讓"negotiationneeded"事件觸發生成offer
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
// send the offer to the other peer
signaling.send({desc: pc.localDescription});
} catch (err) {
console.error(err);
}
};
// 一旦遠程媒體到達,就把它放在遠程視頻元素結構中
pc.ontrack = (event) => {
// don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
// 調用 start() 進行初始化
async function start() {
try {
// 獲取本地流,把它顯示在本地視頻窗口中併發送出去
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
signaling.onmessage = async ({desc, candidate}) => {
try {
if (desc) {
// 如果收到一個offer,就需要響應一個answer
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({desc: pc.localDescription});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc);
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
這個網站simpl.info/pc提供一個WebRTC視頻聊天的示例程序,可以在這頁面直觀感受一下視頻聊天的過程(電腦需要有攝像頭並且允許瀏覽器使用)。如果你想查看視頻對話的過程中offer/answer
和candidate
的交互過程log,可以從下面的頁面查看或者下載一個完整的WebRTC信令和統計表格:Chrome瀏覽器進入這個頁面chrome://webrtc-internals
,Opera瀏覽器進入這個頁面opera://webrtc-internals
。(先打開前面的視頻對話的網頁開啓視頻對話,然後打開後面的地址可以查看詳細交互信息)。
Peer Discovery / 對點發現機制
這是一種奇特的說法 - 我如何找人交談?
對於打電話,我們有電話號碼或者查詢號碼簿。對於在線視頻聊天和消息傳遞,我們需要身份和狀態管理系統,以及用戶啓動會話的方法。WebRTC應用程序需要一種方法讓客戶向他們想要發起或加入會議的其他人發送信號。
WebRTC沒有規定對點發現機制,該過程可以像通過電子郵件發送URL一樣簡單。視頻聊天應用可以把每個會議用一個URL進行表示,參加會議的人通過點擊這個URL就可以進行視頻會議了。開發人員Chris Ball構建了一個有趣的無服務器WebRTC測試,使WebRTC參會者能夠通過他們喜歡的任何消息服務交換元數據,例如IM,電子郵件等。
如何建立信令服務?
注意!WebRTC標準沒有定義信令協議和機制。
無論您選擇哪種實現方式,您都需要一箇中間服務器來在客戶端之間交換信令消息和應用程序數據。因爲在一個網絡應用程序不能簡單地向互聯網喊“把我連接到我的朋友”就可以連接的。(歪果仁的腦回路確實清奇)
值得慶幸的是,信令消息通常很小,並且主要在呼叫開始時進行交換。在使用appr.tc進行測試時發現,對於視頻聊天會話,信令服務總共處理了大約30-45條消息,所有消息的總大小也就10kB左右。
WebRTC信令服務不僅帶寬佔用得少,而且使用的內存資源等也都非常少,因爲他只需要中繼消息並保留少量的會話狀態數據(例如連接的客戶端)。
服務器將消息推送到客戶端
用於信令的消息服務應該是雙向的:客戶端到服務器和服務器到客戶端。這種雙向通信違背了HTTP C/S 請求/響應模型,但是爲了將數據從Web服務器推送到瀏覽器應用上,多年來已經開發了諸如長輪詢之類的技術。
最近, EventSource API已經得到廣泛應用。這這個API啓用了“server-sent events”:通過HTTP從Web服務器連續向瀏覽器客戶端發送數據。EventSource是爲單向消息傳遞而設計的,但是它可以與XHR結合使用,以構建用於交換信令消息的服務:信令服務通過將消息通過EventSource推送到被調用方,從調用方傳遞由XHR請求傳遞的消息。
WebSocket是一種更自然的解決方案,就是爲了全雙工的客戶端-服務器通信(消息可以同時雙向流動)而設計的。使用純WebSocket或Server-Sent Events(EventSource)構建的信號服務的一個優點是,這些API的後端可以使用PHP、Python和Ruby等語言,可以在大多數常用的Web框架上實現。
目前,大約四分之三的瀏覽器支持WebSocket,更重要的是,無論是在桌面還是移動設備上,支持WebRTC的所有瀏覽器也支持WebSocket。所有的鏈接都應該使用TLS以確保不被攔截到未加密的消息,還可以減少代理的遍歷問題。
WebRTC視頻聊天應用程序 “appR.TC”的信令是通過Google App Engine Channel API實現的,該API使用Comet技術(長輪詢)在App Engine後端和Web客戶端之間進行推送信令。
也可以通過WebRTC客戶端多次使用AJAX輪詢消息服務器來處理信令,但這會導致大量冗餘的網絡請求,特別是對於移動設備而言更嚴重。即使在一個會話已經建立,節點也需要在其他節點發生變化或終止會話的情況下輪詢信令消息。
擴展信令
雖然信令服務每個客戶端消耗相對較少的帶寬和CPU資源,但是流行應用程序的信令服務器可能必須處理來自不同位置的大量消息,並且具有高併發性。獲得大量流量的WebRTC應用程序需要能夠處理相當大負載的信令服務器。
這裏不會詳細介紹針對高容量高性能的消息傳遞處理方法,僅僅列出如下幾種選擇:
- XMPP(可擴展消息傳遞和呈現協議):爲即時消息傳遞開發的可用於信令的協議。
- 開源庫,如ZeroMQ和OpenMQ。NullMQ使用基於WebSocket的STOMP協議將ZeroMQ概念應用於Web平臺。
- 使用WebSocket的商業雲消息傳遞平臺,例如Pusher,Kaazing和PubNub。
- 商業WebRTC平臺,如vLine。
(開發者Phil Leggetter的實時Web技術指南提供了消息服務和庫的綜合列表。)
在Node上使用Socket.io構建信令服務
下面是一個簡單的Web應用程序的代碼,它使用在Node上使用Socket.io構建的信令服務。Socket.io的設計使構建交換消息的服務變得簡單,而Socket.io特別適合WebRTC信令,因爲它內置了“房間”的概念。此示例不是爲生產級信令服務而設計的,但對於相對少量的用戶來說很容易理解。
Socket.io使用帶有AJAX長輪詢、AJAX多部分流、Forever Iframe和JSONP輪詢機制的WebSocket。它已被移植到各種後端,但可能其Node版本是最有名的,我們在下面的示例中使用它。
在這個例子中沒有WebRTC:它的設計只是爲了展示如何在Web應用程序中構建信令。查看控制檯日誌以查看客戶端加入會議室並交換消息時發生了什麼。我們的WebRTC代碼庫提供瞭如何將其集成到完整的WebRTC視頻聊天應用程序中的詳細說明。
下面是客戶端index.html代碼。
<!DOCTYPE html>
<html>
<head>
<title>WebRTC client</title>
</head>
<body>
<script src='/socket.io/socket.io.js'></script>
<script src='js/main.js'></script>
</body>
</html>
下面是客戶端引用的JavaScript文件main.js
const isInitiator;
room = prompt('Enter room name:');
const socket = io.connect();
if (room !== '') {
console.log('Joining room ' + room);
socket.emit('create or join', room);
}
socket.on('full', (room) => {
console.log('Room ' + room + ' is full');
});
socket.on('empty', (room) => {
isInitiator = true;
console.log('Room ' + room + ' is empty');
});
socket.on('join', (room) => {
console.log('Making request to join room ' + room);
console.log('You are the initiator!');
});
socket.on('log', (array) => {
console.log.apply(console, array);
});
完整的服務器APP
const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);
const io = require('socket.io').listen(app);
io.sockets.on('connection', (socket) => {
// convenience function to log server messages to the client
function log(){
const array = ['>>> Message from server: '];
for (const i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', (message) => {
log('Got message:', message);
// for a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', (room) => {
const numClients = io.sockets.clients(room).length;
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);
if (numClients === 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id +
' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id +
' joined room ' + room);
});
});
要想運行這個app,你需要先安裝Node、socket.io和node-static。從Node.js網站下載相應版本Node進行安裝,然後使用一下命令安裝另外兩個庫。
npm install socket.io
npm install node-static
運行node server.js
命令來啓動服務器。這時打開瀏覽器訪問localhost:2013,然後再打開一個頁面訪問此地址,模擬兩個獨立的客戶端。可以使用Command-Option-J(Mac)或Ctrl-Shift-J(Win)命令來查看瀏覽器此時處理過程。
無論選擇何種方式發送信令,你的服務器後端和客戶端應用程序至少都需要提供類似於此示例的服務。
信令陷阱
- 在調用
setLocalDescription()
之前,RTCPeerConnection
不會收集candidates
信息。 - 利用Trickle ICE機制(見上文):
candidates
到達後立即調用addIceCandidate()
。
現成的信令服務器
如果你不想自己動手實現信令服務器,這有幾個使用了Socket.io的、與客戶端JavaScript庫集成WebRTC信令服務器可以使用:
- webRTC.io:WebRTC的最早的抽象庫之一。
- easyRTC:全棧WebRTC包。
- Signalmaster:爲與SimpleWebRTC JavaScript客戶端庫一起使用而創建的信令服務器。
如果您根本不想編寫任何代碼,可以從vLine,OpenTok和Asterisk等公司獲得完整的商業WebRTC平臺解決方案。
信令安全
所有WebRTC組件都必須加密。但是,WebRTC標準並未定義信令機制,因此你需要想辦法確保信令安全。如果攻擊者設法劫持信令,他們可以停止會話,重定向連接並記錄,更改或注入內容。
確保信令的最重要因素是使用安全協議、HTTPS和WSS(例如TLS),確保不能被攔截到未加密的消息。也要注意,不要以相同的信令服務器訪問其他信令者的方式來廣播信令消息。
事實上,爲了保護WebRTC應用程序,信令使用TLS絕對是必要的。
使用ICE處理NAT和防火牆
對於元數據信令,WebRTC應用程序使用中間服務器,但是對於實際的媒體和數據流,一旦建立會話,RTCPeerConnection就會嘗試點對點直接連接客戶端。
簡單網絡結構中,每個WebRTC端點都有一個唯一的地址,可以直接與其他端點交換信息直接通信。
沒有NAT和防火牆的應用場景如下圖
實際上,大多數設備都處於一層或多層NAT網絡結構中,有些設備具有阻止某些端口和協議的防病毒軟件,而且許多設備都支持代理和企業防火牆。防火牆和NAT也可以由相同的設備實現,例如家庭wifi路由器。
真實的使用場景
WebRTC應用程序可以使用ICE框架來克服現實網絡的複雜性。要實現此目的,您的應用程序必須將ICE服務器URL傳遞給RTCPeerConnection,如下所述。
ICE會嘗試遍歷兩個端點之間的所有路徑並查找最佳路徑。ICE首先嚐試使用從設備的操作系統和網卡獲得的主機地址建立連接。如果這個方法失敗(表示此時設備處於NAT環境下),ICE使用STUN服務器獲取外部地址。如果使用STUN也無法連接,則通過TURN中繼服務器進行路由。
換句話說:
- STUN服務器用於獲取外部網絡地址。
- 如果直連失敗,TURN服務器將用於中繼流量。
每個TURN服務器都支持STUN:TURN服務器是內置了中繼功能的STUN服務器。ICE還可以應對複雜的NAT設置,實際上,NAT打洞可能不僅僅需要共有IP和端口。
WebRTC應用的 RTCPeerConnection 構造函數的第一個參數 iceServers 中會指定STUN或TURN服務器的URL。對於appr.tc,該值看起來像這樣:
{
'iceServers': [
{
'urls': 'stun:stun.l.google.com:19302'
},
{
'urls': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'urls': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}
注意:上面顯示的TURN證書示例是有時間限制的,並於2013年9月到期。要測試憑據,您可以使用candidate採集示例程序並檢查您是否獲得了中繼類型的candidate。
一旦RTCPeerConnection具有該信息,RTCPeerConnection就可以使用ICE框架計算出端點之間的最佳路徑,必要時會使用STUN和TURN服務器。
STUN
NAT爲設備提供內網IP地址,以便在專用本地網絡中使用,但是這個地址不能在外部使用。對於WebRTC而言,沒有公共地址,點與點之間就無法直接進行通信。爲了解決這個問題,WebRTC採用STUN技術。
STUN服務器位於公網上並且有一個簡單的任務:檢查傳入請求的IP和端口地址(來自在NAT網絡中運行的應用程序)並將該地址作爲響應發回。換句話說,應用程序使用STUN服務器查詢其位於公網上的IP和端口。此過程使WebRTC端點能夠查詢到自己公開訪問的地址,然後通過信令機制將其傳遞給另一個端點,以便建立直接鏈接。(事實上,不同的NAT以不同的方式工作,並且可能存在多個NAT層,但原理仍然是相同的)。
大白話的說就是,很多內網的設備可以給公網地址發送數據,並不知道公網是用什麼的地址來識別自己的,STUN服務器在收到查詢請求的時候會告訴這個設備它的公網地址是啥樣子的。設備拿到這個地址把這個地址發送給需要建立直接聯繫的其他設備
STUN服務器對計算性能和存儲要求都不太高,因此相對低規格的STUN服務器可以處理大量請求。
根據webrtcstats.com的統計,有86%的WebRTC應用使用STUN成功建立連接,在內網端點之間的呼叫可能會更少,因爲不用考慮防火牆和NAT地址轉換。
使用STUN服務器去獲取本設備公共的地址
TURN
RTCPeerConnection嘗試通過UDP建立點與點之間的直接通信。如果失敗,RTCPeerConnection將轉向TCP。如果TCP連接失敗,可以將TURN服務器用作回退,在端點之間中繼數據。
注意:TURN用於在端點之間中繼音頻/視頻/數據流,而不是信令數據!
TURN服務器具有公共地址,因此即使端點位於防火牆或代理之後,也可以與其他端點進行通信。TURN服務器雖然只有這麼一個簡單的任務 —— 中繼流, 但與STUN服務器不同,它們本身就消耗了大量帶寬。換句話說,TURN服務器需要更強大。
完整的交互過程: STUN, TURN 和信令圖
此圖顯示TURN正在運行:單純使用STUN未成功連接,因此每個端點都使用TURN服務器進行中繼。
部署STUN和TURN服務器
爲了進行測試,Google運行appr.tc使用的是公共STUN服務器stun.l.google.com:19302。對於生產STUN / TURN服務,我們建議使用rfc5766-turn-server。
可以從code.google.com/p/rfc5766-turn-server獲取STUN和TURN服務器的源代碼,該代碼還提供了有關服務器安裝的多個信息源的鏈接。還提供Amazon Web Services的VM映像。
restund是一個可替代的TURN服務器,這個代碼也是開源的,也可用於AWS。以下是如何在Google Compute Engine上設置restund的介紹:
- 根據需要打開防火牆相應端口,tcp=443,udp/tcp=3478。
- 創建具有公網IP的四個實例,標準Ubuntu 12.06映像。
- 設置本地防火牆配置 (allow ANY from ANY)。
- 安裝必要的工具
make
和gcc
。 - 安裝庫creytiv.com/re.html.
- 下載restund並解壓, creytiv.com/restund.html。
- 執行
wget hancke.name/restund-auth.patch
和patch -p1 < restund-auth.patch
。 - 執行
make
,sudo make install
安裝libre和restund。 - 根據需要調整restund.conf(替換IP地址並確保它包含相同的共享密鑰)並複製到
/etc
。 - 將
restund/etc/restund
複製到/etc/init.d/
。 - 配置restund:
- Set LD_LIBRARY_PATH
- Copy
restund.conf
to/etc/restund.conf
- Set
restund.conf
to use the right 10. IP address
- 運行restund。
- 從遠程機器測試這個stund:
./client IP:port
。
具有多個端點的WebRTC
上面討論的都是一對一的呼叫,很容易想象,媒體流的用例不僅僅是簡單的一對一呼叫。比如一羣同事一起組織一個會議或者需要衆多人觀看的會議都是多個端點同時在線的。
WebRTC應用程序可以使用多個RTCPeerConnections,以便每個端點連接到網狀配置中的每個其他端點。這是talky.io等應用程序採用的方法,這種每個端點都直接連接的方式對於少數幾個參會者系統來說的話效果非常好。但是這種方式處理和帶寬消耗變得過大,尤其是對於移動客戶端。
Mesh拓撲結構: 每個端點都直接連接
除此之外,WebRTC應用程序可以選擇一個端點,以星形網絡配置將流分發給所有其他端點。也可以直接在服務器上運行一個WebRTC端點(虛擬參會者)並構建自己的重新分發機制。
從Chrome 31和Opera 18開始,一個RTCPeerConnection的MediaStream可以作爲另一個RTCPeerConnection的輸入。這樣可以實現更靈活的架構,因爲它允許Web應用程序通過選擇要連接的其他端點來處理呼叫路由。
MCU / 多點控制單元
對於擁有大量端點而言,更好的選擇是使用多點控制單元(MCU),這是一個可以作爲在大量參與者之間分發媒體數據的類似於橋樑的服務器。MCU可以調整視頻會議不同分辨率,編解碼器和幀速率,處理轉碼,進行選擇性流轉發以及混合或記錄音頻和視頻。對於多方通話,需要考慮許多問題:特別是如何顯示多個視頻輸入並混合來自多個來源的音頻。vLine等雲平臺也會嘗試優化流量路由。
可以購買完整的MCU硬件,也可以自己構建。
有幾種開源MCU軟件可供選擇。例如,Licode爲WebRTC生產開源MCU; 或者OpenTok的Mantis。
瀏覽器之外的VoIP,電話和消息
瀏覽器中運行的WebRTC應用程序可能需要與在另一通信平臺(例如電話或視頻會議系統)上運行的設備或平臺之間建立通信,WebRTC的標準化特性使這種情況成爲可能。
SIP協議是VoIP和視頻會議系統使用的信令協議。爲了實現WebRTC Web應用程序與SIP客戶端(如視頻會議系統)之間的通信,WebRTC需要一個代理服務器來調解信令。信令必須通過網關,但是一旦建立了通信,SRTP流量(視頻和音頻)就可以在端點之間直連了。
PSTN,公共交換電話網,是老式模擬電話的電路交換網絡。對於WebRTC Web應用程序和電話之間的呼叫,流量必須通過PSTN網關。同樣,WebRTC Web應用程序需要中間XMPP服務器與Jingle端點(如IM客戶端)進行通信。Jingle是由Google開發的XMPP擴展,目的是爲語音和視頻提供消息傳遞服務:當前的WebRTC實現是基於C++ libjingle庫的,這是最初爲Google Talk開發的Jingle實現版本。
許多應用程序、庫和平臺利用WebRTC特性,比如: