2021/09/27

WebRTC API

Web Real-Time Communication API 是提供網頁進行視訊或語音串流通訊的技術,可以直接使用瀏覽器進行資料交換。WebRTC 可提供瀏覽器在不需要安裝外掛程式或第三方軟體下,分享應用程式的資料和進行電話會議。

因為 WebRTC 還在發展當中,各 browser 有不同程度的 codec 支援以及 WebRTC 功能。故建議使用 adapter ,這是 Google 提供,減少跨瀏覽器差異的 library。

browser compatability matrix 是不同瀏覽器的相容表。

WebRTC 有多種功能,包含 audio, video conferencing,檔案交換,screen sharing,identity management,支援傳統交換機的 DTMF signals,兩端點之間的連線不需要特殊的 drivers/plugins,也可不需要中間 server。

兩個 peers 之間是以 RTCPeerConnection 產生連線,當連線建立後,就能夠使用 MediaStream 或 RTCDataChannel。

MediaStream 包含了數個 media information tracks,是由 MediaStreamTrack 介面表示的物件,可包含多種 media data: audio, video, text,大部分的 streams 包含至少一條 audio track,一條 video track,可用來傳送 live media,或將 media 存檔。

也可以用 RTCDataChannel 介面在兩端之間,傳遞任意的 binray data,這邊可用來傳送 channel-information, 交換 metadata,game status, file transfer。

WebRTC References

Connection setup and management

Interfaces

Interfaces Description
RTCPeerConnection local 與 remote peer 之間的 WebRTC connection
RTCDataChannel connection 的兩個 peers 之間的 bi-directional data channel
RTCDataChannelEvent 將 RTCDataChannel 接到 RTCPerrConnection 的 event
RTCSessionDescription session 的參數,裡面包含了 description type,代表它所描述的部分 offer/answer negotiation process 以及該 seesion的 SDP descriptor
RTCStatsReport connection 或是 tack on connection 的統計資訊。以 RTCPeerConnection.getStats() 取得。使用 WebRTC 的統計資料有另外的 WebRTC Statistics API
RTCIceCandidate 代表一個可用來建立 RTCPeerConnection 的 candidate ICE server
RTCIceTransport ICE transport 的資訊
RTCPeerConnectionIceEvent 在 RTCPeerConnection 中,有關 ICE candidates 的相關 events。event 是使用 type: icecandidate
RTCRtpSender 在 RTCPeerConnection 裡面的 MediaStreamTrack,用來encoding 傳輸資料
RTCRtpReceiver 在 RTCPeerConnection 裡面的 MediaStreamTrack,用來decoding 傳輸資料
RTCTrackEvent track event 介面。通常是 RTCRtpReceiver 接到 RTCPeerConnection 時發生,代表 RTCPeerConenction 產生了新的 MediaStreamTrack
RTCSctpTransport SCTP transport 的資訊,可在 RTCPeerConnection 的 data channel 收送資料時,透過 SCTP packets,存取Datagram Transport Layer Security(DTLS)

Dictionaries

Dictionaries Description
RTCConfiguration RTCPeerConnection 的 conifguration options
RTCIceServer 定義如何連接 ICE Sever,例如 Stun or Turn server
RTCRtpContributingSource 有關 contributing source (SCRC) 的資訊,包含該 source 被播放了一個最新的 packet 的 most recent time

ex:

var configuration = { iceServers: [{
                          urls: "stun:stun.services.mozilla.com",
                          username: "louis@mozilla.com",
                          credential: "webrtcdemo"
                      }, {
                          urls: ["stun:stun.example.com", "stun:stun-1.example.com"]
                      }]
};

var pc = new RTCPeerConnection(configuration);

Events

Events Description
bufferedamountlow data channel 目前 buffered 的資料量,也就是 bufferedAmount property 已經低於 bufferedAmountLowThreshold 這個 minimum buffered data size
close data channel 已結束 process,進入 closed state,data transport 已經完全 closed,在沒有完全 closed 的時候,會發送 closing event
closing RTCDataChannel 已轉換到 closing state,如果完全停止則是 close event
connectionstatechange 連線狀態(connectionState) 改變
datachannel 有新的 RTCDataChannel 可以使用,事件類別為 RTCDataChannelEvent
error data channel 發生錯誤 RTCErrorEvent
error RTCDtlsTransport 發生錯誤 RTCErrorEvent,可能是 dtls-failure 或是 fingerprint-failure
gatheringstatechange RTCIceTransport 的 gathering state 已改變
icecandidate RTCPeerConnectonIceEvent
當 local device 識別到一個新的 ICE candidate,並需要 local peer 去呼叫 setLocalDescription() 時,會產生此 event
icecandidateerror RTCPeerConnectionIceErrorEvent
在 gathering ICE candidates 發生錯誤時
iceconnectionstatechange 當 ICE gathering state (屬性 icegatheringstate) 改變時,會發送給 RTCPeerConnection
message data channel 收到一個訊息,event type 為 MessageEvent
negotiationneeded 通知 RTCPeerconnection 需要處理 session negotiation,可在 setLocalDescription() 後,呼叫 createOffer()
open RTCDataChannel 的 data transport 已經 opened 或 re-opened
selectedcandidatepairchane RTCIceTransport 目前選用的 ICE candidates 改變
track 當新的 track 在成功協調 media streaming 後,會產生 RTCTrackevent 發送給 RTCPeerConnection
signalingstatechange 當 signalingstate 改變時,送給 peer connection。這會發生在呼叫 setLocalDescription() 或 setRemoteDescription() 之後
statechange RTCDtlsTransport 的狀態改變
statechange RTCIceTransport 的狀態改變
statechange RTCSctpTransport 的狀態改變

Types

Types Description
RTCSctpTransport.state 代表 RTCSctpTrnasport instance 的 state
RTCSessionDescriptionCallback 當要求 create offers 或 answers 時,RTCSessionDescriptionCallback 會傳給 RTCPeerConnection

Identity and security

管理 identity and security,用在連線的認證

identity Description
RTCIdentityProvider user agent,可要求處理 identity assertion
RTCIdentityAssertion 代表 current connection 的 identity of the remote peer。如果目前沒有 peer,就會回傳 null,一但被設定,就不能被修改
RTCIdentityProviderRegistrar Registers and identity provider (idP)
RTCIdentityEvent 代表 identity provider (idP) 產生了 identity assertion。event type 為 identityresult
RTCIdentityErrorEvent 代表 identity provider (idP) 產生了 error。event type 為 idpassertionerror 或 idpvalidationerror
RTCertificate RTCPeerConnection 用來認證的 certificate

Telephony

PSTN 使用的 dialing tone

Interfaces

Interfaces Description
RTCDTMFSender 發送 DTMF signaling
RTCDTMFToneChangeEvent tonechange event 用來表示 DTMF 開始或結束

Events

Events Description
tonechange 當connection 開始播放 DTMF tone,或是 RTCDTMFSender 的 toneBuffer 已經送完時,會產生 event type: RTCDTMFToneChangeEvent

WebRTC Connectivity

Signaling

WebRTC 必須搭配 signaling service 才能使用。用來交換 SDP 資訊,也就是 offer/answer 資料

  • Peer A init connection 產生 Offer
  • 透過 signal channel 發送 Offer 給 Peer B
  • Peer B 接收 Offer,產生 Answer
  • 透過 signal channel 回送 Answer

Session Descriptions

configuration of an endpoint on a WebRTC connection 就稱為 session description

以 SDP 描述,裡面包含 media type, format, transfer protocol, endpoint's IP/Port, 其他資訊

media data exchange 是以 Interactive Connectivity Establishment (ICE) 處理,該協定可讓兩個在 NAT 下的 devices 使用 intermediary 交換 offers, answers

每個 peer 都留存兩個 description:localdescription 及 remote description

當建立 call 或是 某一端要修改 configuration 時,就會透過 offer/answer process 處理,以下是基本的 steps

  1. caller 透過 MediaDevices.getUserMedia 取得 local Media
  2. caller 產生 RTCPeerConnection,呼叫 RTCPeerConnection.addTrack()
  3. caller 呼叫 RTCPeerConnection.createOffer() 產生 offer
  4. caller 呼叫 RTCPeerConnection.setLocalDescription() 發送 offer 到 local description
  5. caller 詢問 STUN servers 用以產生 ice candidates
  6. caller 使用 signaling server 傳送 offer 給 receiver
  7. receiver 接收 offer,呼叫 RTCPeerConnection.setRemoteDescription() 作為 remote description
  8. receiver 設定 call
    • capture local media
    • 呼叫 RTCPeerConnection.addTrack()
  9. receiver 透過 RTCPeerConnection.createAnswer() 產生 answer
  10. receiver 呼叫 RTCPeerConnection.setLocalDescription(),傳入 answer,也就是將 answer 設定為 local description
  11. receiver 使用 signaling server 發送 answer 給 caller
  12. caller 收到 answer
  13. caller 呼叫 RTCPeerConnection.setRemoteDescription() 將 answer 設定為 remote description
  14. 然後就能開始互傳 media

Pending and Current Descriptions

在處理 localDescription 與 remoteDescription 時,可能會因為格式不相容而被 reject,因此需要一個機制能夠 propose a new format,直到另一端接受

current description:由 RTCPeerConnection.currentLocalDescription 及 RTCPeerConenction.currentRemoteDescription 回傳,這也是目前雙方同意使用的 description。

pending description:由 RTCPeerConnection.pendingLocalDescription 及 RTCPeerConnection.pendingRemoteDescription 回傳,代表 setLocalDesciption() 或 setRemoteDescription() 目前考慮中的 description

RTCPeerConnection.localDescription 與 RTCPeerConnection.remoteDescription 回傳的 description,如果在 pending description 狀態,就是回傳 pending description,否則就回傳 current description

呼叫 setLocalDescription() 或 setRemoteDescription() 時,description 會先設定為 pending description,然後 WebRTC layer 開始評估要不要接受,一但 proposed description 被接受,current description 就會改成 pending description 的值,pending description 被改為 null

pendingLocalDescription 包含 offer or answer,還有 local ICE candidates

pendingRemoteDescription 包含 remote ICE candidates,可呼叫 RTCPeerconnection.addIceCandidate() 提供資訊

ICE Candidates

除了交換 media information,還需要交換 network connection 的 informations,也就是 ICE candidate 以及 peer 能夠通訊的方法 (directly or through a TURN server)

通常每個 peer 會先 propose 最佳的 candidates,理想狀態要使用 UDP (速度快,且容易 recover 中斷),但 ICE 標準也允許使用 TCP (當無法使用 UDP 時),另外不是所有瀏覽器都支援 ICE over TCP

UDP candidate types

  • host:實體 IP,可直接連線

  • srflx:Server Reflexive and Relayed Candidates

    host 因為經過 NAT,透過 Stun/Turn server 產生的 candidate。connection 最初要求來自 STUN server 的 candidate,並將 request 傳給 remote peer

  • prflx: Peer Reflexive Candidates

    來自 symmetric NAT,通常是 trickle ICE (也就是 additional candidate 交換,發生在 primary signaling 之後,在 verification 結束以前) 的 additional candidate

    是直接發送給 peer host 產生的 candidate

  • relay

    類似 srflx,但是由 Turn Server 產生的中繼 candidate IP:Port

tcp 有三種:active, passive, so

  • active

    會打開 outbound connection,且不接受 incoming connection request。這是最常見的類型

  • passive

    只會接受 incoming connection

  • so

    會嘗試同時在兩個終端直接打開連線

Choosing a candidate pair

ICE layer 會選擇兩個 peers 中的某一個當作 controlling agent。ICE Agent 會決定 connection 最後使用的 candidate pair,另一個 peer 就稱為 controlled agent。可透過 RTCIceCandidate.transport.role 查詢

controlling agent 除了決定 candidate pair 以外,還會透過 STUN,signaling 選用的 controlled agent,更新 offer。controlled agent 只會等待通知使用哪一個 candidate pair

ICE session 可能會讓 controlling agent 選擇多個 candidate pair,每次分享資訊給 controlled agent 時,兩端會重新設定 connection 使用的 candidate pair

一但 ICE session 完成,configuration 就固定了,除非發生 ICE reset

在每個 candidate generation 結束時,會以 RTCIceCandidate 格式發送 end-of-candidates notification,也就是 candidate 屬性為空字串。這個 candidate 還是要呼叫 addIceCandidate() 加入 connection,以便通知 remote peer

如果在 negotiation 時,沒有其他的 candidates,就會發送 end-of-candidates notification,發送 candidate 屬性為 null 的 RTCIceCandidate。這個訊息不需要傳給 remote peer。這是 legacy notification of a state,可用 icegatheringstatechange event 偵測得知

ICE rollbacks

如果不像掛斷既有的 call,可作 renegotiation

ICE rollback 會 restore 上次連線中 signalingState 為 stable 的 SDP offer

發送 description 其 type 為 rollback,就可以 init rollback,該 description 的其他 properties 都會被忽略

當 peer 已經有先前產生的 offer 時,ICE agent 會自動產生 rollback。如果 local peer 的狀態為 have-local-offer,也就是 local pper 有發送過 offer

呼叫 setRemoteDescription() 可收到 received offer triggers rollback

完整的 exchange


Signaling and video calling

Signaling and video calling

discovery 與 negotiation process 用以產生 conenction,稱為 signaling

WebRTC 需要兩個不同網路的端點,能夠互相找到對方,並協調 media format,需要透過第三方 server 處理 signaling

signaling server

兩個 devices 之間的 WebRTC 連線需要先透過 signaling server 協調兩端

WebRTC 不指定 transport mechanism,可使用 WebSocket 或是 XMLHttpRequest

server 不需要了解或是處理 signaling data content (SDP),只需要傳遞。ICE subsystem 要如何傳送 singaling data 給另一個 peer 是比較重要的問題。

準備給 signaling 使用的 chat server

chat server 透過 WebSocket 傳送 JSON string

可用類似方法,做 signaling 與 ICE negotiation

# 將訊息傳送給特定 target 的 function
function sendToOneUser(target, msgString) {
  var isUnique = true;
  var i;

  for (i=0; i < connectionArray.length; i++) {
    if (connectionArray[i].username === target) {
      connectionArray[i].send(msgString);
      break;
    }
  }
}

Designing the signaling protocol

需要一個 protocol 交換 message,以下利用 JSON object 作為範例

Exchange session descriptions

在開始 singaling process 時,user init call 並產生 offer,裡面包含 session description (SDP 格式),該資料要傳給 callee,callee 會以 answer 回應

設計用 video-offer video-answer 分別代表 offer, answer

JSON 欄位 說明
type message type, video-offer or video-answer
name sender's name
target callee's name
sdp SDP string

兩端可知道 codecs, codec parameters,但還不知道如何傳遞 media data。透過 ICE candidates 處理

Exchange ICE candidates

兩端需要交換 ICE candidates 協調真正的 connection,每個 ICE candidate 都會描述一個可使用的溝通管道。peer 會持續發送找到的 candidates,不管 streaming 是否已經開始了。

icecandidate event 會送給 RTCPeerConenction,並以 pc.setLocalDescription(offer) 完成 process

如果有更好的 candidate,stream 可修改 formats

欄位 說明
type new-ice-candidate
target callee's name
candidate SDP candidate string
不需要處理訊息內容,只需要傳給 remote peer

每個 ICE message 會提供 TCP/UDP, IP address, port, connection type (說明是 peer IP 或是 relay server),包含 NAT 資訊

note: 當收到 onicecandidate 時,要將 candidate 透過 signaling connection 傳給另一個 peer。收到 ICE candidate message 時,就呼叫 RTCPeerConnection.addIceCandidate()。建議不要嘗試修改 SDP

note: onicecandidate event 與 createAnswer() 都是 async call,注意 signaling 不要修改處理順序。setRemoteDescription() 後,才能呼叫 addIceCandidate()

Signaling transaction flow

  • Each user's client running within a web browser
  • Each user's web browser
  • The signaling server
  • The web server hosting the chat service

ICE candidate exchange process

在 local ICE layer 收到的 candidate,各自發送給另一端,當兩端同意使用某個 candidate,就會開始傳遞 media

當網路狀況有異動,peer 可能會建議切換到不同 media resolution 或 不同 codec,就會啟動新的 exchange of candidates

client application

兩個 video elements,一個 hangup 按鈕

<div class="flexChild" id="camera-container">
  <div class="camera-box">
    <video id="received_video" autoplay></video>
    <video id="local_video" autoplay muted></video>
    <button id="hangup-button" onclick="hangUpCall();" disabled>
      Hang Up
    </button>
  </div>
</div>
// 透過 WebSocket 發送訊息給 signaling server
function sendToServer(msg) {
  var msgJSON = JSON.stringify(msg);

  connection.send(msgJSON);
}
// users: 每個連線的 user 的 usernames
function handleUserlistMsg(msg) {
  var i;
  var listElem = document.querySelector(".userlistbox");

  while (listElem.firstChild) {
    listElem.removeChild(listElem.firstChild);
  }

  msg.users.forEach(function(username) {
    var item = document.createElement("li");
    item.appendChild(document.createTextNode(username));
    item.addEventListener("click", invite, false);

    listElem.appendChild(item);
  });
}
// 當 user click 某個想要呼叫的 username,就呼叫 inivte
var mediaConstraints = {
  audio: true, // We want an audio track
  video: true // ...and we want a video track
};

function invite(evt) {
  if (myPeerConnection) {
    alert("You can't start a call because you already have one open!");
  } else {
    var clickedUsername = evt.target.textContent;

    if (clickedUsername === myUsername) {
      alert("I'm afraid I can't let you talk to yourself. That would be weird.");
      return;
    }

    targetUsername = clickedUsername;
    createPeerConnection();

    navigator.mediaDevices.getUserMedia(mediaConstraints)
    .then(function(localStream) {
      document.getElementById("local_video").srcObject = localStream;
      localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
    })
    .catch(handleGetUserMediaError);
  }
}
// 如果 getUserMedia 發生 error 的錯誤處理
function handleGetUserMediaError(e) {
  switch(e.name) {
    case "NotFoundError":
      alert("Unable to open your call because no camera and/or microphone" +
            "were found.");
      break;
    case "SecurityError":
    case "PermissionDeniedError":
      // Do nothing; this is the same as the user canceling the call.
      break;
    default:
      alert("Error opening your camera and/or microphone: " + e.message);
      break;
  }

  closeVideoCall();
}
// 建立 PeerConnection
function createPeerConnection() {
  myPeerConnection = new RTCPeerConnection({
      iceServers: [     // Information about ICE servers - Use your own!
        {
          urls: "stun:stun.stunprotocol.org"
        }
      ]
  });

  myPeerConnection.onicecandidate = handleICECandidateEvent;
  myPeerConnection.ontrack = handleTrackEvent;
  myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
  myPeerConnection.onremovetrack = handleRemoveTrackEvent;
  myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
  myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
  myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}
  • Starting negotiation
// 處理 negotiationneeded event
function handleNegotiationNeededEvent() {
  myPeerConnection.createOffer().then(function(offer) {
    return myPeerConnection.setLocalDescription(offer);
  })
  .then(function() {
    sendToServer({
      name: myUsername,
      target: targetUsername,
      type: "video-offer",
      sdp: myPeerConnection.localDescription
    });
  })
  .catch(reportError);
}
  • session negotiation
    • Handling the invitation
function handleVideoOfferMsg(msg) {
  var localStream = null;

  targetUsername = msg.name;
  createPeerConnection();

  var desc = new RTCSessionDescription(msg.sdp);

  myPeerConnection.setRemoteDescription(desc).then(function () {
    return navigator.mediaDevices.getUserMedia(mediaConstraints);
  })
  .then(function(stream) {
    localStream = stream;
    document.getElementById("local_video").srcObject = localStream;

    localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
  })
  .then(function() {
    return myPeerConnection.createAnswer();
  })
  .then(function(answer) {
    return myPeerConnection.setLocalDescription(answer);
  })
  .then(function() {
    var msg = {
      name: myUsername,
      target: targetUsername,
      type: "video-answer",
      sdp: myPeerConnection.localDescription
    };

    sendToServer(msg);
  })
  .catch(handleGetUserMediaError);
}
  • sending ICE candidates
function handleICECandidateEvent(event) {
  if (event.candidate) {
    sendToServer({
      type: "new-ice-candidate",
      target: targetUsername,
      candidate: event.candidate
    });
  }
}
  • receiving ICE candidates
function handleNewICECandidateMsg(msg) {
  var candidate = new RTCIceCandidate(msg.candidate);

  myPeerConnection.addIceCandidate(candidate)
    .catch(reportError);
}
  • receiving new streams
function handleTrackEvent(event) {
  document.getElementById("received_video").srcObject = event.streams[0];
  document.getElementById("hangup-button").disabled = false;
}
  • handling the removal of tracks
function handleRemoveTrackEvent(event) {
  var stream = document.getElementById("received_video").srcObject;
  var trackList = stream.getTracks();

  if (trackList.length == 0) {
    closeVideoCall();
  }
}
  • hangup
function hangUpCall() {
  closeVideoCall();
  sendToServer({
    name: myUsername,
    target: targetUsername,
    type: "hang-up"
  });
}
  • end call
function closeVideoCall() {
  var remoteVideo = document.getElementById("received_video");
  var localVideo = document.getElementById("local_video");

  if (myPeerConnection) {
    myPeerConnection.ontrack = null;
    myPeerConnection.onremovetrack = null;
    myPeerConnection.onremovestream = null;
    myPeerConnection.onicecandidate = null;
    myPeerConnection.oniceconnectionstatechange = null;
    myPeerConnection.onsignalingstatechange = null;
    myPeerConnection.onicegatheringstatechange = null;
    myPeerConnection.onnegotiationneeded = null;

    if (remoteVideo.srcObject) {
      remoteVideo.srcObject.getTracks().forEach(track => track.stop());
    }

    if (localVideo.srcObject) {
      localVideo.srcObject.getTracks().forEach(track => track.stop());
    }

    myPeerConnection.close();
    myPeerConnection = null;
  }

  remoteVideo.removeAttribute("src");
  remoteVideo.removeAttribute("srcObject");
  localVideo.removeAttribute("src");
  remoteVideo.removeAttribute("srcObject");

  document.getElementById("hangup-button").disabled = true;
  targetUsername = null;
}

ICE connection state

function handleICEConnectionStateChangeEvent(event) {
  switch(myPeerConnection.iceConnectionState) {
    case "closed":
    case "failed":
      closeVideoCall();
      break;
  }
}

ICE signaling state

function handleSignalingStateChangeEvent(event) {
  switch(myPeerConnection.signalingState) {
    case "closed":
      closeVideoCall();
      break;
  }
};

ICE gathering state

function handleICEGatheringStateChangeEvent(event) {
  // Our sample just logs information to console here,
  // but you can do whatever you need.
}

WebRTC Data Channel

Using WebRTC Data Cahnnel

建立 RTCPeerConnection 後,就能收送 media data,也可以使用 data channel,可安全地交換任何資料

因所有 WebRTC components 都要求加密,RTCDataChannel 會自動使用 Datagram Transport Layer Security (DTLS)

產生 data channel

RTCDataChannel 使用的 data transport 可用兩種方法產生

  • 以 WebRTC 產生 transport,並宣告為 remote peer (會收到 datachannel event),這種方法比較簡單且常用,但比較沒有彈性
  • 自己寫 code 協調 data transport,自己 signal 到另一端 peer,且要連接到 new channel

Automatic negotiation

通常可讓 peer connection 協調處理 RTCDataChannel connection

呼叫 createDataChannel() 且不設定 negotiated 屬性,或是設定為 false。就會讓 RTCPeerConnection 自動處理 RTCDataChannel

可透過傳給 RTCDataChannel 的 open event 判斷是否有成功連線

let dataChannel = pc.createDataChannel("MyApp Channel");

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});

Manual negotiation

先用 RTCPeerConnection.createDataChannel() 產生一個新的 RTCDataChannel,設定 negotiated 為 true

使用 webserver or others,要能 signal to the remote peer,並讓他產生自己的 RTCDataChannel,且 negotiated 為 true

let dataChannel = pc.createDataChannel("MyApp Channel", {
  negotiated: true
});

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});

requestRemoteChannel(dataChannel.id);

上面的 requestRemoteChannel 就是用來 trigger negotiation,產生 remote channel with the same ID as the local channel

Buffering

WebRTC data channel 支援 outbound data 的 buffering,會自動處理,且無法控制 buffer size。

使用 bufferedAmount, bufferedAmountLowThreshold, onbufferedamountlow, and bufferedamountlow

了解 message size limits

網路傳輸的封包有 size 限制

Firefox, Chrome 使用 usrsctp library 實作 SCTP,但兩個使用方法不同,firefox 會用多個 SCTP message 傳送 large message (deprecated),chrome 是用 series of messages

少於 16 kB 的訊息不需要考慮此問題

Concerns

目前不適合使用 RTCDataChannel 傳送超過 64kB 的 message,因為 SCTP 最初用在 signaling protocol,這邊假設 message 都很小,要支援超過網路 MTU 的 message 是後來增加的功能。這個技術需要訊息編上有順序的號碼,要依序傳送。

因為有越多越多 application 傳送大 message,造成重要的 signaling message 阻塞的問題。

目前 browser 利用 end-of-record (EOR) flag 支援 larger message,這表示 message 是 last one in a series。在支援 EOR 的狀況下,RTCDataChannel 可傳送大 message (可超過 256 kB,firefox 實作可傳送 1GB),在 256 kB 的狀況下,會發生 noticeable delays。

要解決此問題,新的 stream scheduler (SCTP ndata specification) 可 interleave 在不同 stream 傳送的 message,proposal 目前還在 IETF draft,如果實作,理論上可傳送任意大小的 message

firefox 在 bug 1381145 支援 ndata

chrome 在 bug 5696

Security

透過 WebRTC 傳送的資料都會被加密,RTCDataChannel 是用 Datagram Transport Layer Security (DTLS) 加密,based on TLS (https)

WebRTC 在兩個 agents 之間做 peer-to-peer conenction,資料不會傳給 web/application server

SCTP NAT

解决iptables nat sctp协议无效的问题

A----->B-----C

IP如下:

A:1.1.1.1

B:1.1.1.2; 2.2.2.1

C:2.2.2.2

需求為,A 需要使用sctp連通C

在B機器上添加iptables規則為:

iptables -t nat -I PREROUTING -d 1.1.1.2 -p sctp --dport 11111 -j DNAT --to-destination  2.2.2.2:11111

就是把A發出的目的地址:Port 由1.1.1.2:11111轉變為2.2.2.2:11111

同時在C的接口上使用tcpdump抓包,發現並沒有接收到sctp message,為檢驗網絡是否正常(包括路由等配置),僅將上述規則中的sctp改為tcp進行tcp的連通測試

iptables -t nat -I PREROUTING -d 1.1.1.1 -p tcp --dport 11111 -j DNAT --to-destination  2.2.2.2:11111

發現C機器上可以抓到tcp報文,說明網絡沒有問題,iptables的規則也沒有問題。

因為tcpdump抓包解包並不需要系統支持特定的協議,懷疑可能是iptables規則因為某種原因沒有生效,借助google發現瞭解決辦法:iptables-nat-not-work-for-sctp

載入nfconntrackproto_sctp即可,該模塊用來對sctp進行連接跟蹤

modprobe nf_conntrack_proto_sctp

連接跟蹤模塊可以參見:nf_conntrack連接跟蹤模塊

Why is SCTP not much used/known

  • NAT: Doesn't cross NAT very well/at all (less than 1% internet home & enterprise routers do NAT on SCTP).

sctp over udp encapsulation does not work under NAT network


ORTC

Object Real-Time Communication 是新的規格,提供開發 WebRTC application 高階 API,ORTC 不使用 SDP,也不支援 Offer/Answer state machine。ORTC 使用 "sender", "receiver", "transport" 物件及設定參數,用以描述該物件的功能。"Tracks" 是由 sender 編碼,透過 transport 傳送,在 receiver 解碼。而 "data channels"是直接透過 transport 傳送。

ORTC 最初由 MS Edge 實作支援,但目前 ORTC 很多的物件定義,已經被吸收進入 WebRTC 1.0 核心標準中。

WebRTC, ORTC difference

ref: ORTC draft spec

ref: WebRTC的现状和未来(上)

References

WebRTC API

samples

WebRTC wiki

30-26之 WebRTC 的 P2P 即時通信與小範例

沒有留言:

張貼留言