enebularでシグナリング

# 概要

enebularでwebrtcのシグナリング（ネゴシエーション）やってみた。


# 参考にしたページ

https://qiita.com/massie_g/items/f5baf316652bbc6fcef1

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/c2b61743-eca6-234c-f094-ff5ba95c0d34.png)

# flow

関数は、delete msg._session;のみ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/c9692835-d6d1-66fb-e2d5-51b0bb06fcc6.png)

# 仕組み

2つのブラウザを開く。AとBとする
Aでstart mediaして、conectする。
Bでstart mediaする。
Aでsendすると、両方のtaにwebsocketで送られる。
Bでcopy2してrecvする。
Bでsendすると、両方のtaにwebsocketで送られる。
Aでcopy2してrecvする。
シグナリング完了。

Aで
createoffer
setlocaldescription
onicecadidate
Bで
setremotedescription
ceateanser
setlocaldescription
onicecandidate
Aで
setremotwdescription




# サンプルコード


```
let localVideo = document.getElementById('local_video');
let remoteVideo = document.getElementById('remote_video');
let localStream = null;
let peerConnection = null;
let textForSendSdp = document.getElementById('text_for_send_sdp');
let textToReceiveSdp = document.getElementById('text_for_receive_sdp');
let ta = document.getElementById('ta');
navigator.getUserMedia  = navigator.getUserMedia    || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription;
function isUseVideo() {
  let useVideo = document.getElementById('use_video').checked;
  return useVideo;
}
function isUseAudio() {
  let useAudio = document.getElementById('use_audio').checked;
  return useAudio;
}
function isSendOnly() {
  let sendOnly = document.getElementById('send_only').checked;
  return sendOnly;
}
function startVideo() {
  let useVideo = isUseVideo();
  let useAudio = isUseAudio();
  if ((!useVideo) && (!useAudio)) 
  {
    alert('NO media to capture');
    return;
  }
  getDeviceStream({
    video: useVideo, 
    audio: useAudio
  }).then(function(stream) {
    logStream('localStream', stream);
    localStream = stream;
    playVideo(localVideo, stream);
  }).catch(function (error) {
    alert('getUserMedia error:' + error);
    return;
  });
}
function stopVideo() {
  pauseVideo(localVideo);
  stopLocalStream(localStream);
}
function stopLocalStream(stream) {
  let tracks = stream.getTracks();
  if (!tracks) 
  {
    alert('NO tracks');
    return;
  }
  for (let track of tracks) 
  {
    track.stop();
  }
}
function logStream(msg, stream) {
  var videoTracks = stream.getVideoTracks();
  if (videoTracks) 
  {
    for (var i = 0; i < videoTracks.length; i++) 
    {
      var track = videoTracks[i];
    }
  }
  var audioTracks = stream.getAudioTracks();
  if (audioTracks) 
  {
    for (var i = 0; i < audioTracks.length; i++) 
    {
      var track = audioTracks[i];
    }
  }
}
function getDeviceStream(option) {
  if ('getUserMedia' in navigator.mediaDevices) 
  {
    return navigator.mediaDevices.getUserMedia(option);
  }
  else
  {
    return new Promise(function(resolve, reject) {    
      navigator.getUserMedia(option, resolve, reject);
    });      
  }
}
function playVideo(element, stream) {
  if ('srcObject' in element)
  {
    if (!element.srcObject) 
    {
      element.srcObject = stream;
    }
    else
    {
    }
  }
  else
  {
    element.src = window.URL.createObjectURL(stream);
  }
  element.play();
  element.volume = 0;
}
function pauseVideo(element) {
  element.pause();
  if ('srcObject' in element) 
  {
    element.srcObject = null;
  }
  else
  {
    if (element.src && (element.src !== ''))
    {
      window.URL.revokeObjectURL(element.src);
    }
    element.src = '';
  }
}
function onSdpText() {
  let text = textToReceiveSdp.value;
  text = _trimTailDoubleLF(text);
  if (peerConnection)
  {
    let answer = new RTCSessionDescription({
      type: 'answer',
      sdp: text,
    });
    setAnswer(answer);
  }
  else
  {
    let offer = new RTCSessionDescription({
      type: 'offer',
      sdp: text,
    });
    setOffer(offer);
  }
  textToReceiveSdp.value ='';
}
function sendSdp(sessionDescription) {
  textForSendSdp.value = sessionDescription.sdp;
  textForSendSdp.focus();
  textForSendSdp.select();
}
function copySdp() {
  textForSendSdp.focus();
  textForSendSdp.select();
  document.execCommand('copy');
}
function _trimTailDoubleLF(str) {
  const trimed = str.trim();
  return trimed + String.fromCharCode(13, 10);
}
function prepareNewConnection() {
  let pc_config = {
    "iceServers": []
  };
  let peer = new RTCPeerConnection(pc_config);
  if ('ontrack' in peer) 
  {
    peer.ontrack = function(event) {
      let stream = event.streams[0];
      playVideo(remoteVideo, stream);
      if (event.streams.length > 1)
      {
        alert('got multi-stream, but play only 1 stream');
      }
    };
  }
  else 
  {
    peer.onaddstream = function(event) {
      let stream = event.stream;
      playVideo(remoteVideo, stream);
    };
  }
  peer.onicecandidate = function(evt) {
    if (evt.candidate)
    {
    } 
    else 
    {
      sendSdp(peer.localDescription);
    }
  };
  if (localStream)
  {
    if ('addTrack' in peer)
    {
      let tracks = localStream.getTracks();
      for (let track of tracks) 
      {
        let sender = peer.addTrack(track, localStream);
      }
    }
    else 
    {
      peer.addStream(localStream);          
    }
  }
  else 
  {
    alert('no local stream, but continue.');
  }
  return peer;
}
function makeOffer() {
  peerConnection = prepareNewConnection();
  let options = {};
  if (localStream)
  {
    if (isSendOnly())
    {
      options = { 
        offerToReceiveAudio: false,
        offerToReceiveVideo: false
      };
    }
  }
  else
  {
    options = { 
      offerToReceiveAudio: true, 
      offerToReceiveVideo: true 
    };
    if ('addTransceiver' in peerConnection) 
    {
      peerConnection.addTransceiver('video').setDirection('recvonly');
      peerConnection.addTransceiver('audio').setDirection('recvonly');
    }
  }
  peerConnection.createOffer(options).then(function (sessionDescription) {
    return peerConnection.setLocalDescription(sessionDescription);
  }).then(function() {
  }).catch(function(err) {
    alert(err);
  });
}
function setOffer(sessionDescription) {
  if (peerConnection)
  {
    alert('peerConnection alreay exist!');
  }
  peerConnection = prepareNewConnection();
  peerConnection.setRemoteDescription(sessionDescription).then(function() {
    makeAnswer();
  }).catch(function(err) {
    alert('setRemoteDescription(offer) ERROR: ' + err);
  });
}
function makeAnswer() {
  if (! peerConnection)
  {
    alert('peerConnection NOT exist!');
    return;
  }
  let options = {};
  if (!localStream) 
  {
    if ('addTransceiver' in peerConnection) 
    {
      peerConnection.addTransceiver('video').setDirection('recvonly');
      peerConnection.addTransceiver('audio').setDirection('recvonly');
    }
  }
  peerConnection.createAnswer(options).then(function (sessionDescription) {
    return peerConnection.setLocalDescription(sessionDescription);
  }).then(function() {
  }).catch(function(err) {
    alert(err);
  });
}
function setAnswer(sessionDescription) {
  if (!peerConnection)
  {
    alert('peerConnection NOT exist!');
    return;
  }
  peerConnection.setRemoteDescription(sessionDescription).then(function() {
  }).catch(function(err) {
    alert('setRemoteDescription(answer) ERROR: ' + err);
  });
}
function connect() {
  if (!peerConnection)
  {
    makeOffer();
  }
  else 
  {
    alert('peer already exist.');
  }
}
function hangUp() {
  if (peerConnection) 
  {
    peerConnection.close();
    peerConnection = null;
    pauseVideo(remoteVideo);
  }
  else
  {
    alert('peer NOT exist.');
  }
}
function send0() {
	ws0.send(textForSendSdp.value);
}
function copy2() {
  textToReceiveSdp.value = ta.value;
}
var url = "wss://ev2-prod-node-red-1b385b63-611.herokuapp.com/of";
var ws0 = new WebSocket(url);
ws0.onmessage = function(e) {
	ta.value = e.data;
};
ws0.onerror = function(e) {
	alert("ng0");
};

```

# 成果物

https://cdpn.io/ohisama/debug/vYNYPwj/mVAbGDaQvDGk

以上。