m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

WebRTCを試す

WebRTC

とあるサイトで簡単に実験できる情報が記載されていたので、半信半疑ながら試す

ベースとする環境

OS

CentOS 64bit

Webアプリサーバ

dev.webrtc.com

シグナリングサーバ

192.168.51.129:9001

TURNサーバ

なし

手順

環境
[root@localhost ~]# node -v
v0.10.25
[root@localhost ~]# npm -v
1.3.24
[root@localhost ~]#
socketioをインストール
[root@localhost ~]# mkdir node_sample1
[root@localhost ~]# cd node_sample1/
[root@localhost node_sample1]# npm install socket.io

たくさんの関連モジュールをダウンロードしてインストールする

[root@localhost node_sample1]#
socket.ioを使用してシグナリングサーバを作成
var port = 9001;
var io = require('socket.io').listen(port);
console.log((new Date()) + " Server is listening on port " + port);
 
io.sockets.on('connection', function(socket) {
  socket.on('message', function(message) {
    socket.broadcast.emit('message', message);
  });
 
  socket.on('disconnect', function() {
    socket.broadcast.emit('user disconnected');
  });
});
nodejsを起動
[root@localhost node_sample1]# node signaling.js
   info  - socket.io started
Mon Mar 31 2014 19:33:46 GMT+0900 (JST) Server is listening on port 9001
別途Webサーバのアクセスとして、sample.htmlを作成
<!DOCTYPE html>
<html>
<head>
  <title>WebRTC 1 to 1 signaling</title>
</head>
<body>
  <button type="button" onclick="startVideo();">Start video</button>
  <button type="button" onclick="stopVideo();">Stop video</button>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <button type="button" onclick="connect();">Connect</button>
  <button type="button" onclick="hangUp();">Hang Up</button>
  <br />
  <div>
   <video id="local-video" autoplay style="width: 240px; height: 180px; border: 1px solid black;"></video>
   <video id="remote-video" autoplay style="width: 240px; height: 180px; border: 1px solid black;"></video>
  </div>

  <p>
   SDP to send:<br />
   <textarea id="text-for-send-sdp" rows="5" cols="100" disabled="1">SDP to send</textarea>
  </p>
  <p>
   SDP to receive:<br />
   <textarea id="text-for-receive-sdp" rows="5" cols="100"></textarea><br />
   <button type="button" onclick="onSDP();">Receive SDP</button>
  </p>

  <p>
   ICE Candidate to send:<br />
   <textarea id="text-for-send-ice" rows="5" cols="100" disabled="1">ICE Candidate to send</textarea>
  </p>
  <p>
   ICE Candidates to receive:<br />
   <textarea id="text-for-receive-ice" rows="5" cols="100"></textarea><br />
   <button type="button" onclick="onICE();">Receive ICE Candidates</button>
  </p>

  <!---- socket ------>
  <script src="http://192.168.51.129:9001/socket.io/socket.io.js"></script>

  <script>
  var localVideo = document.getElementById('local-video');
  var remoteVideo = document.getElementById('remote-video');
  var localStream = null;
  var peerConnection = null;
  var peerStarted = false;
  var mediaConstraints = {'mandatory': {'OfferToReceiveAudio':false, 'OfferToReceiveVideo':true }};


  // ---- socket ------
  // create socket
  var socketReady = false;
  var port = 9001;
  var socket = io.connect('http://192.168.51.129:' + port + '/');
  // socket: channel connected
  socket.on('connect', onOpened)
        .on('message', onMessage);

  function onOpened(evt) {
    console.log('socket opened.');
    socketReady = true;
  }

  // socket: accept connection request
  function onMessage(evt) {
    if (evt.type === 'offer') {
      console.log("Received offer, set offer, sending answer....")
      onOffer(evt);
    } else if (evt.type === 'answer' && peerStarted) {
      console.log('Received answer, settinng answer SDP');
          onAnswer(evt);
    } else if (evt.type === 'candidate' && peerStarted) {
      console.log('Received ICE candidate...');
          onCandidate(evt);
    } else if (evt.type === 'user dissconnected' && peerStarted) {
      console.log("disconnected");
      stop();
    }
  }



  // ----------------- handshake --------------
  var textForSendSDP = document.getElementById('text-for-send-sdp');
  var textForSendICE = document.getElementById('text-for-send-ice');
  var textToReceiveSDP = document.getElementById('text-for-receive-sdp');
  var textToReceiveICE = document.getElementById('text-for-receive-ice');
  var iceSeparator = '------ ICE Candidate -------';
  var CR = String.fromCharCode(13);

  function onSDP() {
    var text = textToReceiveSDP.value;
        var evt = JSON.parse(text);
        if (peerConnection) {
          onAnswer(evt);
        }
        else {
          onOffer(evt);
        }

        textToReceiveSDP.value ="";
  }

  //--- multi ICE candidate ---
  function onICE() {
    var text = textToReceiveICE.value;
        var arr = text.split(iceSeparator);
        for (var i = 1, len = arr.length; i < len; i++) {
      var evt = JSON.parse(arr[i]);
          onCandidate(evt);
    }

        textToReceiveICE.value ="";
  }


  function onOffer(evt) {
    console.log("Received offer...")
        console.log(evt);
    setOffer(evt);
        sendAnswer(evt);
        peerStarted = true;  // ++
  }

  function onAnswer(evt) {
    console.log("Received Answer...")
        console.log(evt);
        setAnswer(evt);
  }

  function onCandidate(evt) {
    var candidate = new RTCIceCandidate({sdpMLineIndex:evt.sdpMLineIndex, sdpMid:evt.sdpMid, candidate:evt.candidate});
    console.log("Received Candidate...")
        console.log(candidate);
    peerConnection.addIceCandidate(candidate);
  }

  function sendSDP(sdp) {
    var text = JSON.stringify(sdp);
        console.log("---sending sdp text ---");
        console.log(text);
        textForSendSDP.value = text;

        // send via socket
        socket.json.send(sdp);
  }

  function sendCandidate(candidate) {
    var text = JSON.stringify(candidate);
        console.log("---sending candidate text ---");
        console.log(text);
        textForSendICE.value = (textForSendICE.value + CR + iceSeparator + CR + text + CR);
        textForSendICE.scrollTop = textForSendICE.scrollHeight;

        // send via socket
        socket.json.send(candidate);
  }

  // ---------------------- video handling -----------------------
  // start local video
  function startVideo() {
        navigator.webkitGetUserMedia({video: true, audio: false},
    function (stream) { // success
      localStream = stream;
      localVideo.src = window.webkitURL.createObjectURL(stream);
      localVideo.play();
          localVideo.volume = 0;
    },
    function (error) { // error
      console.error('An error occurred: [CODE ' + error.code + ']');
      return;
    }
        );
  }

  // stop local video
  function stopVideo() {
    localVideo.src = "";
    localStream.stop();
  }

  // ---------------------- connection handling -----------------------
  function prepareNewConnection() {
    var pc_config = {"iceServers":[]};
    var peer = null;
    try {
      peer = new webkitRTCPeerConnection(pc_config);
    } catch (e) {
      console.log("Failed to create peerConnection, exception: " + e.message);
    }

    // send any ice candidates to the other peer
    peer.onicecandidate = function (evt) {
      if (evt.candidate) {
        console.log(evt.candidate);
        sendCandidate({type: "candidate",
                          sdpMLineIndex: evt.candidate.sdpMLineIndex,
                          sdpMid: evt.candidate.sdpMid,
                          candidate: evt.candidate.candidate}
                );
      } else {
        console.log("End of candidates. ------------------- phase=" + evt.eventPhase);
      }
    };

    console.log('Adding local stream...');
    peer.addStream(localStream);

    peer.addEventListener("addstream", onRemoteStreamAdded, false);
    peer.addEventListener("removestream", onRemoteStreamRemoved, false)

    // when remote adds a stream, hand it on to the local video element
    function onRemoteStreamAdded(event) {
      console.log("Added remote stream");
      remoteVideo.src = window.webkitURL.createObjectURL(event.stream);
    }

    // when remote removes a stream, remove it from the local video element
    function onRemoteStreamRemoved(event) {
      console.log("Remove remote stream");
      remoteVideo.src = "";
    }

    return peer;
  }

  function sendOffer() {
    peerConnection = prepareNewConnection();
    peerConnection.createOffer(function (sessionDescription) { // in case of success
      peerConnection.setLocalDescription(sessionDescription);
      console.log("Sending: SDP");
      console.log(sessionDescription);
      sendSDP(sessionDescription);
    }, function () { // in case of error
      console.log("Create Offer failed");
    }, mediaConstraints);
  }

  function setOffer(evt) {
    if (peerConnection) {
          console.error('peerConnection alreay exist!');
        }
    peerConnection = prepareNewConnection();
    peerConnection.setRemoteDescription(new RTCSessionDescription(evt));
  }

  function sendAnswer(evt) {
    console.log('sending Answer. Creating remote session description...' );
        if (! peerConnection) {
          console.error('peerConnection NOT exist!');
          return;
        }

    peerConnection.createAnswer(function (sessionDescription) { // in case of success
      peerConnection.setLocalDescription(sessionDescription);
      console.log("Sending: SDP");
      console.log(sessionDescription);
      sendSDP(sessionDescription);
    }, function () { // in case of error
      console.log("Create Answer failed");
    }, mediaConstraints);
  }

  function setAnswer(evt) {
    if (! peerConnection) {
          console.error('peerConnection NOT exist!');
          return;
        }
        peerConnection.setRemoteDescription(new RTCSessionDescription(evt));
  }

  // -------- handling user UI event -----
  // start the connection upon user request
  function connect() {
    if (!peerStarted && localStream && socketReady) { // **
        //if (!peerStarted && localStream) { // --
      sendOffer();
      peerStarted = true;
    } else {
      alert("Local stream not running yet - try again.");
    }
  }

  // stop the connection upon user request
  function hangUp() {
    console.log("Hang up.");
    stop();
  }

  function stop() {
    peerConnection.close();
    peerConnection = null;
    peerStarted = false;
  }

  </script>
</body>
</html>

確認

f:id:m_shige1979:20140331221911j:plain
※2つの画面を開いて1つのPCでテスト。何故かPCの設定のせいかカメラの角度がおかしい…

参考

http://html5experts.jp/mganeko/5349/
※基本、ここの内容をコピーしました

まとめ

WebRTCはとても簡単に設定して試すことには成功した。一部のブラウザでしか動作できないが、
Webで動画のチャットなども実験できるかも…
今回はソースコピーだけでほとんど理解していないのでデータの流れがどうなっているかきちんと確認する必要がある。

複数のPCで試す環境とカメラ&マイクが必要になりそう。適当に安いの買ってやってみよう。