通過WebRTC進行實時通信-通過RTCPeerConnection傳輸視頻

我們將要學習的知識點

在這一步,我們將知道下面的事情怎麼做:

  • 簡單介紹瀏覽器與WebRTC的不同。
  • 使用 RTCPeerConnection API 傳輸視頻。
  • 控制媒體的捕獲與傳輸。

本步完整的版本在 step-02目錄下。

什麼是RTCPeerConnection

RTCPeerConnection 是調用WebRTC傳輸音視頻和交換數據的API。這個例子是在同一個頁面中兩個RTCPeerConnection對象之間建立連接。沒有什麼實際價值,但去能很好的證明RTCPeerConnection是如何工作的。

添加視頻元素和控制按鈕

在index.html裏將一個video元素替換爲兩個video元素和三個按鈕。

<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>

<div>
  <button id="startButton" >start</button>
  <button id="callButton">call</button>
  <button id="hangupButton">hang up</button>
</div>

一個視頻元素用於顯示從getUserMedia()上獲取的視頻流,另一個通過RTCPeerConnection顯示同樣的視頻流。在真實的應用中,一個視頻元素顯示本地流,另一個顯示遠端流。

添加 adapter.js 片段

在main.js鏈接之上,添加一個到當前 adapter.js版本的連接。

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

adapter.js是一段將app與具體的變化和不同的前輟隔離開的片段。(雖然實際上,爲WebRTC的實現使用的標準和協議是非常穩定的,只有很少的前輟名)。

在這一步,我們已經鏈接了最新版的 adapter.js,它對於 codelab很合適,但可以對於產品APP不一定合適。adapter.js GitHub repo 展示了確保你的APP總能訪問最新版本的技術。

對於WebRTC interop的完整信息,參見webrtc.org/web-apis/interop.

index.html應該看起來像下面這樣子:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>

  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

安裝 RTCPeerConnection 代碼

用 step-02目錄中的版本替換main.js

在 codelab裏對大段代碼做剪切複製不是很好的做法。但是爲了得到 RTCPeerConnection 並使它運行,沒有別的辦法,只能全力以赴。

很快你就會學會如何進行編碼工作。

呼叫

打開 index.html, 點擊Start button 從webcam 獲取視頻, 點擊 Call 建軍一個對等連接 。 你將看到在兩個video元素上顯示同樣的來自於webcam的視頻。看瀏覽器的console ,可以看到 WebRTC的日誌。

它是如何工作的?

這部分有很多內容...

如果你想跳過下面的解釋,也沒問題。

你仍然可以繼續 codelab!
WebRTC使用 RTCPeerConnection API在 WebRTC客戶端之間建立連接傳輸視頻,稱之爲 peers。

在這個例子中,兩上 RTCPeerConnection 對象是在同一頁上,pc1 和pc2。沒什麼實際價值,但很好的證明了 API 的工作。

在 WebRTC peer之間建立一個呼叫,包括三個任務:

  • 爲呼叫的每個端創建一個RTCPeerConnection,並且在每端都添加一個從getUserMedia()獲取的本地流。
  • 獲得並共享的網絡信息:潛在的連接端點稱爲ICE](http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment) 候選者。
  • 獲得並共享本地與遠端描述:本地多媒體的元數據用SDP格式。

想像一下Alice和 Bob想使用 RTCPeerConnection建立視頻聊天。

首先,Alice和 Bob交換網絡信息,“查找候選者”一詞是指使用[ICE]框架(http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment)查找網絡接口和端口的過程。

  1. Alice 創建帶有icecandidate handler(addEventListener('icecandidate'))的 RTCPeerConnection 對象。 這對應下面 main.js中的代碼:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);

在這個例子中,對於 RTCPeerConnection 的 servers參數沒什麼用處。

這裏可以指定 STUN 和 TURN 服務的地址。

WebRTC 被設計成點對點的工作方式,所以用戶之間儘可能直接通過路由進行連接 WebRTC旨在應對現實世界的網絡: 客戶端應用穿越 NAT gateways 和防火牆, 並且點對點網絡在直連失敗時需要回退。

作爲這個過程的一部分, 在點對點連接失敗的情況下,WebRTC APIs 使用 STUN 服務得到你的計算機的IP 地址,並且使用 TURN 服務作爲 relay 服務。 WebRTC in the real world 闡明瞭更多細節。

  1. Alice 調用 getUserMedia() 並且添加傳遞給它的流:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
  then(gotLocalMediaStream).
  catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
  localStream = mediaStream;
  trace('Received local stream.');
  callButton.disabled = false;  // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
  1. 當網絡候選者變爲有效時,在第一步中的 onicecandidate handler將被調用。
  2. Alice 將序列化後的候選者數據發給 Bob,在真實的實用中,這個過程(稱爲信令)通過一個消息服務進行- 在後面的步驟中,你將學到如何處理它。當然,在本步驟中,在同一頁中的兩個RTCPeerConnection對象通直接通信不需要額外的消息。
  3. 當Bob從Alice得到候選者消息後,他調用 addIceCandidate()添加候選者到遠端描述:
function handleConnection(event) {
  const peerConnection = event.target;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer.addIceCandidate(newIceCandidate)
      .then(() => {
        handleConnectionSuccess(peerConnection);
      }).catch((error) => {
        handleConnectionFailure(peerConnection, error);
      });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
          `${event.candidate.candidate}.`);
  }
}

WebRTC端點之間還需要找出並交換本地和遠程音頻和視頻的媒體信息,例如 分辨率和編碼能力。Signaling交換媒體配置信息通過交換元數據 Bob來進行,稱爲 offer 和 answer,使用 Session Description Protocol 格式,稱爲 SDP:

  1. Alice 運行 RTCPeerConnection 的 createOffer() 方法. 返回值提供了一個RTCSessionDescription: Alice的本地 session description:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. 如果成功,Alice 使用 setLocalDescription()設置本地描述,然後通過消息通道發送這個 session 描述給 Bob。
  2. Bob 使用setRemoteDescription設置 Alice 發送給它的描述作爲遠端描述。
  3. Bob 運行RTCPeerConnection的 createAnswer () 方法,將他從Alice哪兒得到的遠端描術傳遞給它,這樣就可以生成一個與她兼容的本地會話。必須傳遞給 createAnswer() 一個 RTCSessionDescription,併發送給 Alice。
  4. 當 Alice 得到 Bob的描述會話時,她給setRemoteDescription設置一個遠程會話。
// Logs offer creation and sets peer connection session descriptions.
function createdOffer(description) {
  trace(`Offer from localPeerConnection:\n${description.sdp}`);

  trace('localPeerConnection setLocalDescription start.');
  localPeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection setRemoteDescription start.');
  remotePeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection createAnswer start.');
  remotePeerConnection.createAnswer()
    .then(createdAnswer)
    .catch(setSessionDescriptionError);
}

// Logs answer to offer creation and sets peer connection session descriptions.
function createdAnswer(description) {
  trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

  trace('remotePeerConnection setLocalDescription start.');
  remotePeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('localPeerConnection setRemoteDescription start.');
  localPeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);
}

  1. Ping!

點滴

  1. 看一下chrome://webrtc-internals,這個提供了WebRTC的狀態和調試數據。(Chrom URLs的完整列表是在 chrome://about
  2. 這頁的CSS風格:
  • 將視頻並排放置
  • 將Button設置成相同寬度和文本大小。
  • 確何佈局適用於移動端
  1. 從 Chrome Dev Tool 控制檯,看localSteam, localPeerConnection 以及 remotePeerConnection。
  2. 從控制檯看localPeerConnectionpc1.localDescription SDP格式看起來是什麼樣兒?

我們學到了什麼

在這一步你學會了如何去:

  • 摘要瀏覽器與WebRTC的差異,adapter.js
  • 使用RTCPeerConnection API傳輸視頻。
  • 控制媒體的捕獲和傳輸
  • 在端點之間共享媒體和網絡信息開啓WebRTC呼叫。

本步驟完整的版本在 step-2目錄中。

提示

在這一步學習了很多內容,另一個詳細的解釋了RTCPeerConnection的資源是webrtc.org/start.這頁包括了JavaScript架構的建議 - 如果你喜歡 WebRTC,而且不想因API扯皮。

  • 可以從adapter.js GitHub repo找到更多的關於 adapter.js的片段。
  • 想看看世界上最好的視頻聊天應用程序是什麼樣的?看看AppRTC,這是WebRTC項目的WebRTC調用的規範應用程序:app, code。呼叫建立時間小於500毫秒。

最佳實踐

爲了使您的代碼能夠面向未來,請使用新的基於Promise的API,並通過使用 adapter.js實現與不支持它們的瀏覽器的兼容性。

接下來

此步驟顯示如何使用WebRTC在端點之間傳輸視頻 - 但此codelab與數據無關!

在下一步中,瞭解如何使用RTCDataChannel流式傳輸任意數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章