即時傳輸協議 (RTP) 簡介

即時傳輸協議 (RTP),在 RFC 3550 中定義,是一個 IETF 標準協議,旨在實現即時連線,以交換需要即時優先順序的資料。本文概述了 RTP 是什麼以及它在 WebRTC 中的功能。

注意:WebRTC 實際上使用 SRTP (安全即時傳輸協議) 來確保交換資料的安全性和身份驗證。

將延遲降至最低對於 WebRTC 尤其重要,因為面對面的通訊需要儘可能低的 延遲。一個使用者說話與另一個使用者聽到之間的時間延遲越大,就越容易出現交叉通話和其他形式的混亂。

RTP 的關鍵特性

在檢查 RTP 在 WebRTC 中的使用之前,瞭解 RTP 的功能和不足之處很有幫助。RTP 是一種資料傳輸協議,其使命是在當前條件下儘可能高效地在兩個端點之間傳輸資料。這些條件可能受到從網路堆疊的底層到物理網路連線、中間網路、遠端端點的效能、噪聲水平、流量水平等的各種因素的影響。

由於 RTP 是一個數據傳輸協議,它得到了密切相關的 RTP 控制協議 (RTCP) 的補充,RTCP 定義在 RFC 3550,第 6 部分。RTCP 增加了包括服務質量 (QoS) 監控、參與者資訊共享等功能。它不足以完全管理使用者、成員、許可權等,但為不受限制的多使用者通訊會話提供了基本需求。

RTCP 與 RTP 在同一 RFC 中定義的事實,暗示了這兩個協議之間緊密相關的程度。

RTP 的能力

RTP 在 WebRTC 方面的主要優勢包括

  • 通常較低的延遲。
  • 資料包已進行序號和時間戳標記,以便在亂序到達時重新組裝。這使得使用 RTP 傳送的資料可以在不保證順序甚至不保證交付的傳輸上進行傳遞。
  • 這意味著 RTP 可以(但並非必須)在 UDP 之上使用,以獲得其效能以及其多路複用和校驗和特性。
  • RTP 支援組播;雖然這對於 WebRTC 來說尚不重要,但隨著 WebRTC 未來(希望)得到增強以支援多使用者對話,它可能會變得重要。
  • RTP 不僅限於視聽通訊。它可以用於任何形式的連續或活動資料傳輸,包括資料流、活動徽章或狀態顯示更新,或控制和測量資訊傳輸。

RTP 的不足之處

RTP 本身並未提供所有可能的功能,這就是為什麼 WebRTC 還使用其他協議。RTP 不包含的一些更值得注意的方面包括

  • RTP 保證服務質量 (QoS)。
  • 儘管 RTP 旨在用於對延遲敏感的場景,但它本身並不提供任何確保 QoS 的功能。相反,它只提供允許在堆疊的其他地方實現 QoS 所需的資訊。
  • RTP 不處理可能需要的資源分配或預留。

在 WebRTC 的相關方面,這些問題在 WebRTC 基礎架構的各個地方都有處理。例如,RTCP 處理 QoS 監控。

RTCPeerConnection 和 RTP

每個 RTCPeerConnection 都有方法可以訪問服務於對等連線的 RTP 傳輸列表。這些對應於 RTCPeerConnection 支援的三種傳輸型別

RTCRtpSender

RTCRtpSender 負責將 MediaStreamTrack 資料編碼並傳輸到遠端對等節點。可以透過呼叫 RTCPeerConnection.getSenders() 來獲取給定連線的傳送器。

RTCRtpReceiver

RTCRtpReceiver 提供檢查和獲取有關傳入 MediaStreamTrack 資料的資訊的能力。可以透過呼叫 RTCPeerConnection.getReceivers() 來獲取連線的接收器。

RTCRtpTransceiver

RTCRtpTransceiver 是一個 RTP 傳送器和一個 RTP 接收器的組合,它們共享一個 SDP mid 屬性,這意味著它們共享相同的 SDP 媒體 m-line(代表雙向 SRTP 流)。這些由 RTCPeerConnection.getTransceivers() 方法返回,每個 mid 和收發器共享一對一的關係,mid 對於每個 RTCPeerConnection 都是唯一的。

利用 RTP 實現“保持”功能

由於 RTCPeerConnection 的流是透過 RTP 和上述介面實現的,因此您可以利用這些介面對流的內部進行訪問和調整。您可以做的最簡單的事情之一就是實現“保持”功能,即通話參與者可以點選一個按鈕,關閉他們的麥克風,而是向另一個對等方傳送音樂,並停止接收傳入的音訊。

注意:本示例使用了現代 JavaScript 功能,包括 async 函式await 表示式。這極大地簡化了處理 WebRTC 方法返回的 Promise 的程式碼,使其更具可讀性。

在下面的示例中,我們將把啟用和停用“保持”模式的對等方稱為本地對等方,而被保持的使用者稱為遠端對等方。

啟用保持模式

本地對等方

當本地使用者決定啟用保持模式時,將呼叫下面的 enableHold() 方法。它接受一個包含保持期間播放的音訊的 MediaStream 作為輸入。

js
async function enableHold(audioStream) {
  try {
    await audioTransceiver.sender.replaceTrack(audioStream.getAudioTracks()[0]);
    audioTransceiver.receiver.track.enabled = false;
    audioTransceiver.direction = "sendonly";
  } catch (err) {
    /* handle the error */
  }
}

try 塊中的三行程式碼執行以下步驟

  1. 用包含保持音樂的 MediaStreamTrack 替換其傳出音訊軌道。
  2. 停用傳入音訊軌道。
  3. 將音訊收發器切換到僅傳送模式。

這會透過傳送 negotiationneeded 事件來觸發 RTCPeerConnection 的重新協商,您的程式碼會透過使用 RTCPeerConnection.createOffer 生成 SDP 提議並將其透過信令伺服器傳送到遠端對等方來響應。

audioStream(包含要播放的音訊,而不是本地對等方的麥克風音訊)可以來自任何地方。一種可能性是有一個隱藏的 <audio> 元素,並使用 HTMLAudioElement.captureStream() 來獲取其音訊流。

遠端對等方

在遠端對等方,當我們收到方向設定為 "sendonly" 的 SDP 提議時,我們使用 holdRequested() 方法來處理它,該方法接受 SDP 提議字串作為輸入。

js
async function holdRequested(offer) {
  try {
    await peerConnection.setRemoteDescription(offer);
    await audioTransceiver.sender.replaceTrack(null);
    audioTransceiver.direction = "recvonly";
    await sendAnswer();
  } catch (err) {
    /* handle the error */
  }
}

這裡執行的步驟是

  1. 透過呼叫 RTCPeerConnection.setRemoteDescription() 將遠端描述設定為指定的 offer
  2. 將音訊收發器的 RTCRtpSender 的軌道替換為 null,表示沒有軌道。這會停止在收發器上傳送音訊。
  3. 將音訊收發器的 direction 屬性設定為 "recvonly",指示收發器僅接收音訊而不傳送任何音訊。
  4. SDP 答案透過呼叫 sendAnswer() 方法生成併發送,該方法使用 createAnswer() 生成答案,然後透過信令服務將生成的 SDP 傳送到另一個對等方。

停用保持模式

本地對等方

當本地使用者點選介面控制元件停用保持模式時,將呼叫 disableHold() 方法來開始恢復正常功能的流程。

js
async function disableHold(micStream) {
  await audioTransceiver.sender.replaceTrack(micStream.getAudioTracks()[0]);
  audioTransceiver.receiver.track.enabled = true;
  audioTransceiver.direction = "sendrecv";
}

這會按如下方式逆轉 enableHold() 中執行的步驟

  1. 音訊收發器的 RTCRtpSender 軌道被替換為指定流的第一個音訊軌道。
  2. 收發器的傳入音訊軌道被重新啟用。
  3. 音訊收發器的方向設定為 "sendrecv",表示它應恢復傳送和接收流式音訊,而不是僅傳送。

就像在啟用保持時一樣,這會再次觸發協商,導致您的程式碼向遠端對等方傳送新的提議。

遠端對等方

當遠端對等方收到 "sendrecv" 提議時,它會呼叫其 holdEnded() 方法

js
async function holdEnded(offer, micStream) {
  try {
    await peerConnection.setRemoteDescription(offer);
    await audioTransceiver.sender.replaceTrack(micStream.getAudioTracks()[0]);
    audioTransceiver.direction = "sendrecv";
    await sendAnswer();
  } catch (err) {
    /* handle the error */
  }
}

此處 try 塊內執行的步驟是

  1. 透過呼叫 setRemoteDescription() 將收到的提議儲存為遠端描述。
  2. 音訊收發器的 RTCRtpSenderreplaceTrack() 方法用於將傳出音訊軌道設定為麥克風音訊流的第一個軌道。
  3. 收發器的方向設定為 "sendrecv",表示它應恢復傳送和接收音訊。

從現在開始,麥克風重新啟用,遠端使用者可以再次聽到本地使用者,並與他們交談。

另見