建構 WebRTC 應用程式所需的後端服務

什麼是信號?

信號傳輸是協調通訊的程序。如要讓 WebRTC 應用程式設定通話,用戶端必須交換下列資訊:

  • 用於開啟或關閉通訊的會期控制訊息
  • 錯誤訊息
  • 媒體中繼資料,例如轉碼器、轉碼器設定、頻寬和媒體類型
  • 用於建立安全連線的重要資料
  • 網路資料,例如主機的 IP 位址和外部可見的連接埠

這個信號程序需要讓用戶端來回傳遞訊息。WebRTC API 並未實作該機制。您必須自行建構。本文稍後會說明如何建構信號服務。不過,首先需要瞭解一些背景資訊。

為什麼 WebRTC 未定義信號?

為避免多餘的信號傳輸,並盡可能與現有技術相容,WebRTC 標準並未指定信號傳輸方法和通訊協定。JavaScript 工作階段建立通訊協定 (JSEP) 說明瞭這種做法:

此外,JSEP 架構可避免瀏覽器必須儲存狀態,也就是做為信號狀態機器。舉例來說,如果每次重新載入網頁時,信號資料都會遺失,就會造成問題。信號狀態可以改為儲存在伺服器上。

JSEP 架構圖
JSEP 架構

JSEP 需要同層級之間交換提案回覆,也就是上述媒體中繼資料。提議和應答會以會期描述通訊協定 (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 術語的實際意義嗎?請參閱網際網路工程任務組 (IETF) 範例

請注意,WebRTC 的設計允許您先編輯 SDP 文字中的值,再將提案或回覆設為本機或遠端說明。舉例來說,appr.tc 中的 preferAudioCodec() 函式可用於設定預設轉碼器和位元率。以 JavaScript 操作 SDP 有點麻煩,有人討論過 WebRTC 的未來版本是否應改用 JSON,但堅持使用 SDP 也有一些優點

RTCPeerConnection API 和信號:提供、回答和候選項目

RTCPeerConnection 是 WebRTC 應用程式用來建立對等互連連線,以及傳輸音訊和視訊的 API。

如要啟動這項程序,RTCPeerConnection 必須執行兩項工作:

  • 確認當地媒體條件,例如解析度和轉碼器功能。這是用於提議和回覆機制的元資料。
  • 取得應用程式主機的潛在網路位址,又稱為「候選位址」

確認這項本機資料後,必須透過信號機制與遠端對等互連裝置交換資料。

假設愛麗絲想撥打電話給伊芙,以下是完整的提議/回覆機制,詳細說明如下:

  1. Alice 建立 RTCPeerConnection 物件。
  2. Alice 使用 RTCPeerConnection createOffer() 方法建立提案 (SDP 工作階段說明)。
  3. Alice 撥打 setLocalDescription() 的電話,並提供優惠。
  4. Alice 會將提議字串化,並使用信號機制傳送給 Eve。
  5. Eve 會使用 Alice 的提議呼叫 setRemoteDescription(),讓 RTCPeerConnection 瞭解 Alice 的設定。
  6. Eve 會呼叫 createAnswer(),而這個呼叫的成功回呼會傳遞本機工作階段說明 (Eve 的答案)。
  7. Eve 呼叫 setLocalDescription(),將自己的答案設為本機說明。
  8. 接著,Eve 會使用信號機制,將字串化的答案傳送給 Alice。
  9. Alice 使用 setRemoteDescription() 將 Eve 的答案設為遠端工作階段說明。

Alice 和 Eve 也需要交換網路資訊。「尋找候選項目」是指使用 ICE 架構尋找網路介面和通訊埠的程序。

  1. Alice 使用 onicecandidate 處理常式建立 RTCPeerConnection 物件。
  2. 網路候選項目可用時,系統會呼叫處理常式。
  3. 在處理常式中,Alice 會透過信號傳輸管道,將字串化的候選資料傳送給 Eve。
  4. 當 Eve 收到 Alice 的候選訊息時,她會呼叫 addIceCandidate(),將候選項目新增至遠端對等互連說明。

JSEP 支援 ICE 候選項目涓流,可讓來電者在初始提議後,逐步向接聽者提供候選項目,接聽者則可開始處理通話並設定連線,不必等待所有候選項目送達。

為信號傳輸編寫 WebRTC 程式碼

下列程式碼片段是 W3C 程式碼範例,總結了完整的信號程序。程式碼會假設存在某種信號機制 SignalingChannel。我們稍後會詳細說明信號。

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
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);
  }
};

// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    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) {
      // If you get an offer, you need to reply with an 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 RTCPeerConnection,並查看單頁視訊通訊範例的控制台記錄。如要取得更多資訊,請從 Google Chrome 的 about://webrtc-internals 頁面或 Opera 的 opera://webrtc-internals 頁面,下載完整的 WebRTC 信號和統計資料傾印。

同類應用程式探索

這其實是問「如何找到對象聊天?」的另一種說法。

電話號碼和電話簿可供撥打電話。如要進行線上視訊通訊和傳送訊息,您需要身分和狀態管理系統,以及供使用者啟動工作階段的方式。WebRTC 應用程式需要讓用戶端互相發出信號,表示要開始或加入通話。

WebRTC 並未定義對等互連探索機制,因此這裡不會介紹相關選項。只要透過電子郵件或訊息傳送網址即可。如果是 Talkytawk.toBrowser Meeting 等視訊通訊應用程式,只要分享自訂連結,就能邀請他人加入通話。開發人員 Chris Ball 進行了一項有趣的無伺服器 WebRTC 實驗,讓 WebRTC 通話參與者透過任何即時通訊服務 (例如即時通訊、電子郵件或信鴿) 交換中繼資料。

如何建構信號服務?

再次強調,WebRTC 標準並未定義信號傳輸通訊協定和機制。無論選擇哪種方式,您都需要中介伺服器,在用戶端之間交換信號訊息和應用程式資料。遺憾的是,網頁應用程式無法直接在網路上大喊「幫我連線到朋友!」。

幸好信號訊息很小,而且大多會在通話開始時交換。在透過 appr.tc 測試視訊通話時,信號服務處理的訊息總數約為 30 到 45 則,所有訊息的總大小約為 10 KB。

WebRTC 信號服務不僅頻寬需求相對較低,而且只需要轉送訊息並保留少量工作階段狀態資料 (例如連線的用戶端),因此不會耗用太多處理或記憶體資源。

將訊息從伺服器推送至用戶端

信號傳輸訊息服務必須是雙向的,也就是從用戶端到伺服器,以及從伺服器到用戶端。雙向通訊違反 HTTP 用戶端/伺服器要求/回應模型,但多年來已開發出各種變通方法,例如長輪詢,以便將資料從網路伺服器上執行的服務推送至瀏覽器中執行的網路應用程式。

最近,EventSource API廣泛導入。這項功能可啟用伺服器傳送的事件,也就是透過 HTTP 從網頁伺服器傳送至瀏覽器用戶端的資料。EventSource 是專為單向訊息傳輸設計,但可與 XHR 搭配使用,建構用於交換信號訊息的服務。信號服務會透過 XHR 要求傳送呼叫端的訊息,並透過 EventSource 推送至接聽端。

WebSocket 是更自然的解決方案,專為全雙工用戶端/伺服器通訊而設計,可同時雙向傳送訊息。以純 WebSocket 或伺服器傳送事件 (EventSource) 建構信號服務的優點之一,是這些 API 的後端可在各種網路架構上實作,而這些架構常見於大多數網路主機套件,適用於 PHP、Python 和 Ruby 等語言。

除了 Opera Mini 之外,所有新式瀏覽器都支援 WebSocket,更重要的是,支援 WebRTC 的瀏覽器也支援 WebSocket,無論是電腦版或行動版都適用。所有連線都應使用 TLS,確保郵件不會在未加密的情況下遭到攔截,並減少 Proxy 遍歷問題。(如要進一步瞭解 WebSocket 和 Proxy 遍歷,請參閱 Ilya Grigorik 的《High Performance Browser Networking》一書中的 WebRTC 章節)。

您也可以讓 WebRTC 用戶端透過 Ajax 重複輪詢訊息伺服器來處理信號,但這會導致許多多餘的網路要求,對行動裝置來說尤其麻煩。即使工作階段已建立,對等互連仍需輪詢信號訊息,以防其他對等互連變更或終止工作階段。WebRTC Book 應用程式範例會採用這個選項,並針對輪詢頻率進行一些最佳化。

大規模信號

雖然每個用戶端的訊號服務消耗的頻寬和 CPU 相對較少,但熱門應用程式的訊號伺服器可能必須處理來自不同位置的大量訊息,且並行程度很高。流量大的 WebRTC 應用程式需要能處理大量負載的信號伺服器。這裡不會詳細說明,但有許多高用量、高效能的訊息傳送選項,包括:

  • 延伸傳訊和狀態通訊協定 (XMPP),原名為 Jabber,是為即時通訊開發的通訊協定,可用於信號傳輸 (伺服器實作包括 ejabberdOpenfire。JavaScript 用戶端 (例如 Strophe.js) 會使用 BOSH 模擬雙向串流,但由於各種原因,BOSH 的效率可能不如 WebSocket,而且基於相同原因,可能無法順利擴充。(離題一下,Jingle 是 XMPP 擴充功能,可啟用語音和視訊。WebRTC 專案使用 libjingle 程式庫的網路和傳輸元件 (這是 Jingle 的 C++ 實作項目)。

  • 開放原始碼程式庫,例如 ZeroMQ (TokBox 的 Rumour 服務使用此程式庫) 和 OpenMQ (NullMQ 使用 WebSocket 透過 STOMP 通訊協定,將 ZeroMQ 概念套用至網路平台)。

  • 使用 WebSocket 的商用雲端訊息平台 (但可能會改用長輪詢),例如 PusherKaazingPubNub (PubNub 也有 WebRTC API)。

  • 商業 WebRTC 平台,例如 vLine

(開發人員 Phil Leggetter 的即時網路技術指南提供訊息傳遞服務和程式庫的完整清單)。

在 Node 上使用 Socket.io 建構信號服務

以下是簡易型網頁應用程式的程式碼,該應用程式使用以 Node 上的 Socket.io 建構的信號服務。Socket.io 的設計可讓您輕鬆建構服務來交換訊息,而且由於內建房間概念,因此特別適合 WebRTC 信號傳輸。這個範例並非設計為可做為生產級信號服務擴充,但對於相對少數的使用者來說,簡單易懂。

Socket.io 使用 WebSocket,並提供 AJAX 長時間輪詢、AJAX 多部分串流、Forever Iframe 和 JSONP 輪詢等備援機制。這個程式庫已移植到各種後端,但最廣為人知的或許是本範例中使用的 Node 版本。

這個範例中沒有 WebRTC。這個範例僅用於說明如何在網頁應用程式中建立信號。請查看控制台記錄,瞭解用戶端加入會議室及交換訊息時發生的情況。這項 WebRTC 程式碼研究室會逐步說明如何將這項功能整合到完整的 WebRTC 視訊通訊應用程式中。

以下是用戶端 index.html

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/https/web.dev/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);
});

以下是完整的伺服器應用程式:

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);

  });

});

(您不需要瞭解 node-static。(這個範例剛好使用這個值)。

如要在 localhost 上執行這個應用程式,您必須安裝 Node、Socket.IO 和 node-static。您可以從 Node.js 下載 Node (安裝程序簡單快速)。如要安裝 Socket.IO 和 node-static,請在應用程式目錄的終端機中執行 Node Package Manager:

npm install socket.io
npm install node-static

如要啟動伺服器,請在應用程式目錄的終端機中執行下列指令:

node server.js

在瀏覽器中開啟 localhost:2013。在任何瀏覽器中開啟新分頁或視窗,然後再次開啟 localhost:2013。如要查看發生了什麼事,請檢查控制台。在 Chrome 和 Opera 中,您可以透過 Google Chrome 開發人員工具存取控制台 (按 Ctrl+Shift+J 鍵,Mac 則按 Command+Option+J 鍵)。

無論選擇哪種信號傳送方式,後端和用戶端應用程式至少需要提供類似這個範例的服務。

訊號常見問題

  • RTCPeerConnection 會等到呼叫 setLocalDescription() 後,才會開始收集候選人。JSEP IETF 草案中規定必須這麼做。
  • 善用 Trickle ICE。候選人抵達後,請立即撥打 addIceCandidate()

現成的訊號伺服器

如果您不想自行推出,可以使用多個 WebRTC 信號伺服器,這些伺服器會使用 Socket.IO (如上例所示),並與 WebRTC 用戶端 JavaScript 程式庫整合:

如果不想編寫任何程式碼,可以向 vLineOpenTokAsterisk 等公司購買完整的商業 WebRTC 平台。

據瞭解,在 WebRTC 早期,Ericsson 曾使用 Apache 上的 PHP 建構信號伺服器。這項技術現在已有些過時,但如果您考慮採用類似做法,不妨看看相關程式碼。

信號安全

「安全性是一門藝術,目標是讓一切安然無恙。」

薩爾曼·魯西迪

所有 WebRTC 元件都必須加密。

不過,WebRTC 標準並未定義信號機制,因此您必須確保信號安全無虞。如果攻擊者成功劫持信號,就能停止工作階段、重新導向連線,以及錄製、變更或插入內容。

確保信號安全最重要的因素是使用安全通訊協定 (例如 TLS),也就是 HTTPS 和 WSS,確保訊息不會以未加密的形式遭到攔截。此外,請小心不要以其他來電者可存取相同信號伺服器的方式,廣播信號訊息。

信號傳輸後:使用 ICE 處理 NAT 和防火牆

WebRTC 應用程式會使用中繼伺服器傳送中繼資料信號,但建立工作階段後,RTCPeerConnection會嘗試直接或對等連線至用戶端,以進行實際的媒體和資料串流。

在較簡單的世界中,每個 WebRTC 端點都會有專屬地址,可與其他對等互連裝置交換地址,以便直接通訊。

簡單的對等互連連線
沒有 NAT 和防火牆的世界

但實際上,大多數裝置都位於一或多層 NAT 後方,有些裝置會透過防毒軟體封鎖特定通訊埠和通訊協定,許多裝置則位於 Proxy 和企業防火牆後方。防火牆和 NAT 實際上可能由同一部裝置 (例如家用 Wi-Fi 路由器) 實作。

NAT 和防火牆後方的對等互連
現實世界

WebRTC 應用程式可以使用 ICE 架構,克服現實世界網路的複雜性。如要啟用這項功能,應用程式必須將 ICE 伺服器網址傳遞至 RTCPeerConnection,如本文所述。

ICE 會嘗試找出連線同層級裝置的最佳路徑。這項功能會同時嘗試所有可能性,並選擇最有效率的選項。ICE 會先嘗試使用從裝置作業系統和網路卡取得的主機位址建立連線。如果失敗 (NAT 後方的裝置會失敗),ICE 會使用 STUN 伺服器取得外部位址,如果失敗,流量會透過 TURN 中繼伺服器傳送。

換句話說,如果直接 (對等互連) 連線失敗,系統會使用 STUN 伺服器取得外部網路位址,並使用 TURN 伺服器轉送流量。

每個 TURN 伺服器都支援 STUN。TURN 伺服器是 STUN 伺服器,內建額外的中繼功能。ICE 也可處理 NAT 設定的複雜性。事實上,NAT 穿洞可能不只需要公開 IP:port 位址。

WebRTC 應用程式會在 iceServers 設定物件中 (選擇性) 指定 STUN 和/或 TURN 伺服器的網址,該物件是 RTCPeerConnection 建構函式的第一個引數。對於 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'
    }
  ]
}

RTCPeerConnection 取得這項資訊後,ICE 就會自動發揮魔法。RTCPeerConnection 會使用 ICE 架構,找出同儕間的最佳路徑,並視需要搭配 STUN 和 TURN 伺服器運作。

STUN

NAT 會為裝置提供 IP 位址,供裝置在私人區域網路中使用,但這個位址無法在外部使用。如果沒有公開位址,WebRTC 對等互連就無法通訊。為解決這個問題,WebRTC 使用 STUN

STUN 伺服器位於公用網際網路上,工作很簡單,就是檢查傳入要求 (來自 NAT 後方執行的應用程式) 的 IP:port 位址,然後將該位址做為回應傳回。換句話說,應用程式會使用 STUN 伺服器,從公開角度探索自己的 IP:port。這個程序可讓 WebRTC 對等互連取得自己的公開存取位址,然後透過信號機制傳遞給另一個對等互連,以便建立直接連結。(實際上,不同的 NAT 運作方式不同,且可能有多個 NAT 層,但原理仍相同)。

STUN 伺服器不需要執行太多作業或記住太多資訊,因此規格相對較低的 STUN 伺服器也能處理大量要求。

根據 Webrtcstats.com 的資料,大多數 WebRTC 通話都能順利透過 STUN 建立連線 (86%),但如果對等互連裝置位於防火牆後方,且 NAT 設定複雜,連線成功率可能會較低。

使用 STUN 伺服器的對等互連連線
使用 STUN 伺服器取得公開 IP:port 位址

TURN

RTCPeerConnection 會嘗試透過 UDP 在對等互連裝置之間建立直接通訊。如果失敗,RTCPeerConnection 會改用 TCP。如果失敗,則可使用 TURN 伺服器做為備援,在端點之間轉送資料。

再次強調,TURN 用於在對等互連裝置之間轉送音訊、視訊和資料串流,而非信號資料!

TURN 伺服器具有公開位址,因此即使對等互連裝置位於防火牆或 Proxy 後方,也能與 TURN 伺服器通訊。TURN 伺服器的工作概念很簡單,就是轉送串流。不過,與 STUN 伺服器不同的是,TURN 伺服器本質上會消耗大量頻寬。換句話說,TURN 伺服器需要更強大的效能。

使用 STUN 伺服器的對等互連連線
完整蒙提:STUN、TURN 和信號

下圖顯示 TURN 的運作方式。純 STUN 失敗,因此每個對等互連裝置都改用 TURN 伺服器。

部署 STUN 和 TURN 伺服器

為進行測試,Google 會執行公開 STUN 伺服器 stun.l.google.com:19302,appr.tc 也是使用這個伺服器。如要使用正式版 STUN/TURN 服務,請使用 rfc5766-turn-server。STUN 和 TURN 伺服器的原始碼可在 GitHub 上取得,您也可以在該處找到伺服器安裝相關資訊的連結。您也可以使用 Amazon Web Services 的 VM 映像檔

您也可以使用 restund 做為替代 TURN 伺服器,這個伺服器提供原始碼,也適用於 AWS。以下說明如何在 Compute Engine 上設定 restund。

  1. 視需要開啟防火牆,允許 tcp=443、udp/tcp=3478。
  2. 建立四個執行個體,每個執行個體對應一個公開 IP,並使用 Ubuntu 12.06 標準映像檔。
  3. 設定本機防火牆設定 (允許來自任何來源的任何流量)。
  4. 安裝工具: shell sudo apt-get install make sudo apt-get install gcc
  5. creytiv.com/re.html 安裝 libre。
  6. creytiv.com/restund.html 擷取 restund,然後解壓縮至./。
  7. wget hancke.name/restund-auth.patch,然後使用 patch -p1 < restund-auth.patch 套用。
  8. 執行 makesudo make install,以取得 libre 和 restund。
  9. 根據需求調整 restund.conf (替換 IP 位址並確保包含相同的共用密鑰),然後複製到 /etc
  10. restund/etc/restund 複製到 /etc/init.d/
  11. 設定 restund:
    1. 設定 LD_LIBRARY_PATH
    2. restund.conf 複製到 /etc/restund.conf
    3. restund.conf 設為使用正確的 10。IP 位址。
  12. 執行 restund
  13. 使用遠端電腦的 stund 用戶端進行測試:./client IP:port

超越一對一:多方 WebRTC

您也可以參考 Justin Uberti 提出的 REST API for access to TURN Services IETF 標準。

不難想像,媒體串流的用途不只是一對一通話。例如,一群同事進行視訊會議,或一場有數百萬觀眾的公開活動,只有一位講者。

WebRTC 應用程式可以使用多個 RTCPeerConnection,讓每個端點都連線至網格設定中的每個其他端點。talky.io 等應用程式就是採用這種方法,而且對於少數同儕來說,效果相當不錯。此外,處理作業和頻寬消耗量會過高,行動用戶端尤其如此。

網格:小型 N 方通話
完整網狀拓撲:所有人都與所有人連線

或者,WebRTC 應用程式也可以選擇一個端點,將串流分配給星狀設定中的所有其他端點。您也可以在伺服器上執行 WebRTC 端點,並建構自己的重新發布機制 (webrtc.org 提供範例用戶端應用程式)。

自 Chrome 31 和 Opera 18 起,一個 MediaStream 的輸出可做為另一個 RTCPeerConnection 的輸入。這項功能可讓網頁應用程式選擇要連線的其他對等互連裝置,藉此處理通話轉送作業,因此能實現更彈性的架構。如要查看實際運作情形,請參閱 WebRTC 範例:對等互連連線中繼WebRTC 範例:多個對等互連連線

多點控制單元

如果端點數量眾多,建議使用多點控制單元 (MCU)。這類伺服器可做為橋樑,在大量參與者之間分配媒體。MCU 可處理視訊會議中的不同解析度、轉碼器和影格速率;處理轉碼;選擇性轉送串流;以及混合或錄製音訊和視訊。如果是多人通話,則須考量多項問題,特別是如何顯示多個視訊輸入內容,以及如何混合多個來源的音訊。vLine 等雲端平台也會嘗試最佳化流量路徑。

你可以購買完整的 MCU 硬體套件,也可以自行建構。

Cisco MCU5300 的後視圖
Cisco MCU背面

我們提供多種開放原始碼 MCU 軟體選項,舉例來說,Licode (舊稱 Lynckia) 會為 WebRTC 產生開放原始碼 MCU。OpenTok 採用 Mantis

瀏覽器以外的裝置:VoIP、電話和訊息

WebRTC 的標準化特性,可讓在瀏覽器中執行的 WebRTC 應用程式,與在其他通訊平台 (例如電話或視訊會議系統) 上執行的裝置或平台建立通訊。

SIP 是 VoIP 和視訊會議系統使用的信號協定。如要啟用 WebRTC 網頁應用程式與 SIP 用戶端 (例如視訊會議系統) 之間的通訊,WebRTC 需要 Proxy 伺服器來調解信號。信號必須通過閘道,但建立通訊後,SRTP 流量 (視訊和音訊) 即可直接在對等互連之間流動。

公用交換電話網路 (PSTN) 是所有「普通舊式」類比電話的電路交換網路。WebRTC 網路應用程式與電話之間的通話必須透過 PSTN 閘道傳輸流量。同樣地,WebRTC 網頁應用程式也需要中介 XMPP 伺服器,才能與 IM 用戶端等 Jingle 端點通訊。Jingle 是 Google 開發的 XMPP 擴充功能,可為訊息服務啟用語音和視訊功能。目前的 WebRTC 實作是以 C++ libjingle 程式庫為基礎,這是最初為 Talk 開發的 Jingle 實作。

許多應用程式、程式庫和平台都會利用 WebRTC 與外部世界通訊:

  • sipML5:開放原始碼的 JavaScript SIP 用戶端
  • jsSIP:JavaScript SIP 程式庫
  • Phono:以外掛程式形式建構的開放原始碼 JavaScript 電話 API
  • Zingaya:可嵌入的電話小工具
  • Twilio:語音和訊息
  • Uberconference:會議通訊

sipML5 開發人員也建立了 webrtc2sip 閘道。Tethr 和 Tropo 運用 OpenBTS 基地台,透過 WebRTC 讓功能手機和電腦通訊,展現「公事包中的災害通訊」架構。這就是不透過電信業者進行電話通訊!

瞭解詳情

WebRTC 程式碼研究室提供逐步操作說明,教您如何使用在 Node 上執行的 Socket.io 信號服務,建構視訊和文字即時通訊應用程式。

2013 年 Google I/O 大會的 WebRTC 簡報,由 WebRTC 技術主管 Justin Uberti 進行

Chris Wilson 的 SFHTML5 簡報 - WebRTC 應用程式簡介

這本 350 頁的書籍「WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web」詳細介紹了資料和信號路徑,並包含多張詳細的網路拓撲圖。

WebRTC and Signaling: What Two Years Has Taught Us - TokBox 網誌文章,說明為何將信號排除在規格之外是個好主意

Ben Strong 的「A Practical Guide to Building WebRTC Apps」一文,提供了許多關於 WebRTC 拓撲和基礎架構的資訊。

Ilya Grigorik 的《High Performance Browser Networking》一書的 WebRTC 章節,深入探討了 WebRTC 架構、用途和效能。