RTCDataChannel for signaling?

后端 未结 1 1779
伪装坚强ぢ
伪装坚强ぢ 2021-01-13 08:50

I\'ve been reading this article for a signaling solution. The author mentions about signaling with RTCDataChannel when connections are established.

相关标签:
1条回答
  • 2021-01-13 09:43

    Each side initially declares which audio and/or video tracks it is going to send, so that the right number of ports can be opened, and resolutions and formats that work for both peers can be determined. A signaling channel is needed to send the resulting SDP offer/answer, as well as trickle ICE candidates for each of the ports, to the other side.

    Once connected, if you leave this setup alone - basically never add tracks to the connection, remove any, or alter track attributes significantly - then you wont need the signaling server again.

    If you do change any of those things however, then a re-negotiation is needed, which is just what it sounds like: another round over the signaling channel much like the first one.

    Reasons to add a track may be a second camera, another video-source (from another participant perhaps), or maybe screen-sharing, something like that.

    The article is correct that a data channel may be used. Here's a demo! (Firefox only for now.)

    The article is wrong about a signaling service being required - provided you have another means of discovery - as this demo lamely proves.

    The initial connection is chat-only, but either side can push to add video to the mix. The re-negotiation for this is done over a data channel (since there's no signaling server!)

    Instructions for using the fiddle:

    1. There is no server (since it's a fiddle), so press the Offer button and copy the offer.
    2. Paste the offer to the same spot in the same fiddle in another tab or on another machine.
    3. Press ENTER, then copy the answer you get and paste it back in the first fiddle.
    4. Press ENTER again (not addTrack yet!)
    5. You are now connected with two data-channels: one for chat and another for signaling.
    6. Now press addTrack on either end and video should show up on the other end.
    7. Press addTrack in the other direction, and you should have video going both ways.

    var dc = null, sc = null, pc = new mozRTCPeerConnection(), live = false;
    pc.onaddstream = e => v2.mozSrcObject = e.stream;
    pc.ondatachannel = e => dc? scInit(sc = e.channel) : dcInit(dc = e.channel);
    v2.onloadedmetadata = e => { log("Face time!"); };
    
    function addTrack() {
      navigator.mediaDevices.getUserMedia({video:true, audio:true})
      .then(stream => pc.addStream(v1.mozSrcObject = stream));
    }
    
    pc.onnegotiationneeded = e => {
      pc.createOffer().then(d => pc.setLocalDescription(d)).then(() => {
        if (live) sc.send(JSON.stringify({ "sdp": pc.localDescription }));
      }).catch(failed);
    };
    
    function scInit() {
      sc.onmessage = e => {
        var msg = JSON.parse(e.data);
        if (msg.sdp) {
          var desc = new mozRTCSessionDescription(JSON.parse(e.data).sdp);
          if (desc.type == "offer") {
            pc.setRemoteDescription(desc).then(() => pc.createAnswer())
            .then(answer => pc.setLocalDescription(answer)).then(() => {
              sc.send(JSON.stringify({ "sdp": pc.localDescription }));
            }).catch(failed);
          } else {
            pc.setRemoteDescription(desc).catch(failed);
          }
        } else if (msg.candidate) {
          pc.addIceCandidate(new mozRTCIceCandidate(msg.candidate)).catch(failed);
        }
      };
    }
    
    function dcInit() {
      dc.onopen = () => { live = true; log("Chat!"); };
      dc.onmessage = e => log(e.data);
    }
    
    function createOffer() {
      button.disabled = true;
      dcInit(dc = pc.createDataChannel("chat"));
      scInit(sc = pc.createDataChannel("signaling"));
      pc.createOffer().then(d => pc.setLocalDescription(d)).catch(failed);
      pc.onicecandidate = e => {
        if (e.candidate) return;
        if (!live) {
          offer.value = pc.localDescription.sdp;
          offer.select();
          answer.placeholder = "Paste answer here";
        } else {
          sc.send(JSON.stringify({ "candidate": e.candidate }));
        }
      };
    };
    
    offer.onkeypress = e => {
      if (e.keyCode != 13 || pc.signalingState != "stable") return;
      button.disabled = offer.disabled = true;
      var obj = { type:"offer", sdp:offer.value };
      pc.setRemoteDescription(new mozRTCSessionDescription(obj))
      .then(() => pc.createAnswer()).then(d => pc.setLocalDescription(d))
      .catch(failed);
      pc.onicecandidate = e => {
        if (e.candidate) return;
        if (!live) {
          answer.focus();
          answer.value = pc.localDescription.sdp;
          answer.select();
        } else {
          sc.send(JSON.stringify({ "candidate": e.candidate }));
        }
      };
    };
    
    answer.onkeypress = e => {
      if (e.keyCode != 13 || pc.signalingState != "have-local-offer") return;
      answer.disabled = true;
      var obj = { type:"answer", sdp:answer.value };
      pc.setRemoteDescription(new mozRTCSessionDescription(obj)).catch(failed);
    };
    
    chat.onkeypress = e => {
      if (e.keyCode != 13) return;
      dc.send(chat.value);
      log(chat.value);
      chat.value = "";
    };
    
    var log = msg => div.innerHTML += "<p>" + msg + "</p>";
    var failed = e => log(e.name + ": " + e.message + " line " + e.lineNumber);
    <video id="v1" height="120" width="160" autoplay muted></video>
    <video id="v2" height="120" width="160" autoplay></video><br>
    <button id="button" onclick="createOffer()">Offer:</button>
    <textarea id="offer" placeholder="Paste offer here"></textarea><br>
    Answer: <textarea id="answer"></textarea><br>
    <button id="button" onclick="addTrack()">AddTrack</button>
    <div id="div"></div><br>
    Chat: <input id="chat"></input><br>

    0 讨论(0)
提交回复
热议问题