2015年5月27日 星期三

WebRTC


一、WebRTC架構

當兩個電腦都在各自私有網路,透過NAT連接到公有網路,此時無法得知遠端電腦的私有IP,所以透過Stun Server 伺服器,當兩端的電腦連線到Stun Server 就可以得知各自的網路IP,再透過NAT伺服器內部路由表對照,就會對應到內部的私有IP,此時就能建立P2P連線。


當兩個不同電腦PeerAPeerB 通訊時,彼此之間一定要知道對方的IPPORT號,但如果兩台電腦並沒有對外IP時,就需要TURNNAT穿過防火牆, PeerA 會先產生Offer 訊號,WebRTC經由signal channelPeerA電腦端的Offer訊息傳給PeerB,當PeerB signal channel接收到PeerAOffer 的訊息,會立即回覆Answer PeerA,此時這兩台電腦就會以ICE candidat 開始互傳訊息給對方。



二、範例練習

Index.html
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
    <title></title>
</head>
<body>
    <div>Content goes here.</div>
    <video id="localVideo" autoplay muted controls></video>
    <video id="remoteVideo" autoplay controls></video>
    <div>
        <button id="startButton">Start</button>
        <button id="callButton">Call</button>
        <button id="hangupButton">Hang Up</button>       
    </div>
</body>
</html>
  
l   navigator.getUserMedia :瀏覽器可呼叫視訊串流的語法
    navigator.getUserMedia = navigator.getUserMedia ||
              navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
            navigator.getUserMedia({
                video: true
            }, gotStream,
              function (error) {
                  console.log('navigator.getUserMedia error: ', error);
              });

l   webkitRTCPeerConnection: 建立遠端連線
   var servers = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] }; //Stun Server
   var localPeerConnection = new webkitRTCPeerConnection(servers);

l   addIceCandidate : 透過Turn/Stun 機制, 取得相關的IPPort
localPeerConnection.onicecandidate = gotLocalIceCandidate; 
//建立本機的 ICE Candidate
        function gotLocalIceCandidate(event) {
            if (event.candidate) {
//將自己的ICE Candidate 附加在遠端電腦上 (為了與遠端互換串流)
                remotePeerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
            }
        }

l   RTCSessionDescription 交換多媒體的傳輸通道
localPeerConnection.createOffer(gotLocalDescription);   //產生offer
function gotLocalDescription(description) {
    localPeerConnection.setLocalDescription(description);
    remotePeerConnection.setRemoteDescription(description);
    remotePeerConnection.createAnswer(gotRemoteDescription); //回應 Answer
}
function gotRemoteDescription(description) {
    remotePeerConnection.setLocalDescription(description);
    localPeerConnection.setRemoteDescription(description);
}
使用Session Description ProtocolSDP協定的 offer answer 來交換多媒體相關的資訊

完整範例 Javascript
    <script>
        var localStream;  //取得本地端的視訊串流
        var localPeerConnection;   //本地端的連線
        var remotePeerConnection;  //遠端的連線 
        var localVideo = document.getElementById('localVideo'); 
        var remoteVideo = document.getElementById('remoteVideo');
           
        var startButton = document.getElementById('startButton');
        var callButton = document.getElementById('callButton');
        var hangupButton = document.getElementById('hangupButton');
        startButton.disabled = false;
        callButton.disabled = true;
        hangupButton.disabled = true;
        startButton.onclick = start;  //按下startButton 觸發start function
        callButton.onclick = call;   //按下callButton 觸發call function
        hangupButton.onclick = hangup;

//取得本地端的串流的函式
        function gotStream(stream) {
            localVideo.src = URL.createObjectURL(stream);
            localStream = stream;  //將此全域變數loalStream 儲存本地端視訊串流
            callButton.disabled = false;
        }

//取得遠端串流的函式
        function gotRemoteStream(event) {
            remoteVideo.src = URL.createObjectURL(event.stream);
        }

//建立本機的 ICE Candidate
        function gotLocalIceCandidate(event) {
            if (event.candidate) {
//將自己的ICE Candidate 附加在遠端電腦上 (為了與遠端互換串流)
                remotePeerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
            }
        }

//建立遠機的 ICE Candidate
        function gotRemoteIceCandidate(event) {
            if (event.candidate) {
//將遠端的ICE Candidate 附加在本機電腦上(為了與本機互換串流)
                localPeerConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
            }
        }

        function start() {
            startButton.disabled = true;
            navigator.getUserMedia = navigator.getUserMedia ||
              navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
            navigator.getUserMedia({
                video: true
            }, gotStream,
              function (error) {
                  console.log('navigator.getUserMedia error: ', error);
              });
        }

        function call() {
            callButton.disabled = true;
            hangupButton.disabled = false;                       
      //用來當作STUN Server 的公用伺服器,Google 免費提供
            var servers = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
            localPeerConnection = new webkitRTCPeerConnection(servers);  //本地端和STUN Server建立連線
            localPeerConnection.onicecandidate = gotLocalIceCandidate;     //建立本地端的ICE
            remotePeerConnection = new webkitRTCPeerConnection(remotServer); //遠端建立連線
            remotePeerConnection.onicecandidate = gotRemoteIceCandidate;  //建立遠端的ICE
            remotePeerConnection.onaddstream = gotRemoteStream;  //取得遠端的視訊串流

//取得本機電腦的視訊串流,並將串流加在localPeerConnection
            localPeerConnection.addStream(localStream);        
//由本機產生offer 給遠端電腦,並開始將本地端的電腦和遠端的電腦進行傳輸設定
      localPeerConnection.createOffer(gotLocalDescription);
        }
       //透過 setLocalDescription() 將該物件設定為 local description,再將其傳送給對方。
        function gotLocalDescription(description) {
            localPeerConnection.setLocalDescription(description);
            remotePeerConnection.setRemoteDescription(description);
//當對方接收到 offer 的資料,必須回傳一個 answer 作為回應
            remotePeerConnection.createAnswer(gotRemoteDescription);
        }

//透過 setLocalDescription() 將該物件設定為remotePeerConnection,再傳送給本地端
        function gotRemoteDescription(description) {
            remotePeerConnection.setLocalDescription(description);
            localPeerConnection.setRemoteDescription(description);
        }

//結束雙方的通訊
        function hangup() {
            localPeerConnection.close();
            remotePeerConnection.close();
            localPeerConnection = null;
            remotePeerConnection = null;
            hangupButton.disabled = true;
            callButton.disabled = false;
        }
    </script>
  
測試結果:
    作者目前受限於環境,只在區域網內測試結果,無法嘗試兩台不同PC互相通訊,如果以上有觀念錯誤地方,請高手們幫忙導正觀念。

參考文獻


沒有留言:

張貼留言