How to fix InvalidStateError: Cannot add ICE candidate when there is no remote SDP

十年热恋 提交于 2020-07-02 22:52:28

问题


I am creating a webRTC video chat that shows a caller all active members when initiating a call from firefox and the receiver is using chrome this error is displayed "Uncaught (in promise) DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate". And when a call is initiated from firefox and receiver uses firefox I get two errors Invalidstate: cannot add ICE candidate when there is no remote SDP and ICE failed, add a STUN and see about:webrtc for details

I dont know where I am making a mistake

/ define all data here
var usersOnline,id,currentCaller,room,caller,localUser,media,memberInfo;
// All subscribed members.
var users = [];


var token = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
// create random user id
var userId = Math.random().toString(36).substring(2, 15);
// create random username
var username = token;
// authonticating user
var currentUser = {
    username: token,
    userId: userId
}
// stringify user data 
currentUser = JSON.stringify(currentUser);
var pusher = new Pusher('KEY', {
    authEndpoint: '../auth.php',
    auth: {
        params: JSON.parse(currentUser)
    },
    cluster: 'ap2',
    forceTLS: true
});

var state = pusher.connection.state;

var channel = pusher.subscribe('presence-conference');

channel.bind("pusher:subscription_succeeded", function (members) {
  console.log(members);
  id = channel.members.me.id;
  document.getElementById('mydetails').innerHTML = 'Online Now: '  + '   ( ' + (members.count - 1) +')';
  members.each(member => {
    if (member.id != channel.members.me.id) {
      users.push(member.id);
    }
  });
  renderOnline();
 });
 // Add user online
channel.bind("pusher:member_added", member => {
  users.push(member.id);
  renderOnline();
});
channel.bind("pusher:member_removed", member => {
// for remove member from list:
  var index = users.indexOf(member.id);
  users.splice(index, 1);
  if (member.id == room) {
    endCall();
  }
  renderOnline();
});

function renderOnline(){
  var list = "";
  users.forEach(function(user) {
    list +=
      `<li>` +
      user +//this will call user
      ` <input type="button" style="float:right;"  value="Call" onclick="callUser('` +
      user +
      `')" id="makeCall" /></li>`;
  });
  document.getElementById("userDetails").innerHTML = list;
}
        //To iron over browser implementation anomalies like prefixes
    GetRTCPeerConnection();
    GetRTCSessionDescription();
    GetRTCIceCandidate();
    prepareCaller();
    function prepareCaller() {
      //Initializing a peer connection
      caller = new window.RTCPeerConnection();
      //Listen for ICE Candidates and send them to remote peers
      caller.onicecandidate = function(evt) {
        if (!evt.candidate) return;
        console.log("onicecandidate called");
        onIceCandidate(caller, evt);
      };
      //onaddstream handler to receive remote feed and show in remoteview video element
      caller.onaddstream = function(evt) {
        console.log("onaddstream called");
        if("srcObject" in document.getElementById("selfview")){
            document.getElementById("selfview").srcObject = evt.stream;
        }else{
        if (window.URL) {
          document.getElementById("remoteview").src = window.URL.createObjectURL(
            evt.stream
          );
        } else {
          document.getElementById("remoteview").src = evt.stream;
        }
      }
      };
    }
    function getCam() {
      //Get local audio/video feed and show it in selfview video element
      return navigator.mediaDevices.getUserMedia({
         audio: {
             echoCancellation: true,
             sampleSize:8
         },
         video: {
            width: 1080,
            height: 720,
            aspectRatio: { ideal: 1.777778 }
         }
      });
    }

    function GetRTCIceCandidate() {
      window.RTCIceCandidate =
        window.RTCIceCandidate ||
        window.webkitRTCIceCandidate ||
        window.mozRTCIceCandidate ||
        window.msRTCIceCandidate;

      return window.RTCIceCandidate;
    }

    function GetRTCPeerConnection() {
      window.RTCPeerConnection =
        window.RTCPeerConnection ||
        window.webkitRTCPeerConnection ||
        window.mozRTCPeerConnection ||
        window.msRTCPeerConnection;
      return window.RTCPeerConnection;
    }

    function GetRTCSessionDescription() {
      window.RTCSessionDescription =
        window.RTCSessionDescription ||
        window.webkitRTCSessionDescription ||
        window.mozRTCSessionDescription ||
        window.msRTCSessionDescription;
      return window.RTCSessionDescription;
    }

    //Create and send offer to remote peer on button click
    function callUser(user) {
      getCam()
        .then(stream => {
            if("srcObject" in document.getElementById("selfview")){
            document.getElementById("selfview").srcObject = stream;
        }else{
          if (window.URL) {
            document.getElementById("selfview").src = window.URL.createObjectURL(
              stream
            );
          } else {
            document.getElementById("selfview").src = stream;
          }
        }
          toggleEndCallButton();
          caller.addStream(stream);
          localUserMedia = stream;
          caller.createOffer().then(function(desc) {
            caller.setLocalDescription(new RTCSessionDescription(desc));
            channel.trigger("client-sdp", {
              sdp: desc,
              room: user,
              from: id
            });
            room = user;
          });
        })
        .catch(error => {
          console.log("an error occured", error);
        });
    }

    function endCall() {
      room = undefined;
      caller.close();
      for (let track of localUserMedia.getTracks()) {
        track.stop();
      }
      prepareCaller();
      toggleEndCallButton();
    }

    function endCurrentCall() {
      channel.trigger("client-endcall", {
        room: room
      });

      endCall();
    }

    //Send the ICE Candidate to the remote peer
    function onIceCandidate(peer, evt) {
      if (evt.candidate) {
        channel.trigger("client-candidate", {
          candidate: evt.candidate,
          room: room
        });
      }
    }

    function toggleEndCallButton() {
      if (document.getElementById("endCall").style.display == "block") {
        document.getElementById("endCall").style.display = "none";
      } else {
        document.getElementById("endCall").style.display = "block";
      }
    }

    //Listening for the candidate message from a peer sent from onicecandidate handler
    channel.bind("client-candidate", function(msg) {
      if (msg.room == room) {
        console.log("candidate received");
        caller.addIceCandidate(new RTCIceCandidate(msg.candidate));
      }
    });

    //Listening for Session Description Protocol message with session details from remote peer
    channel.bind("client-sdp", function(msg) {
      if (msg.room == id) {
        console.log("sdp received");
        var answer = confirm(
          "You have a call from: " + msg.from + "Would you like to answer?"
        );
        if (!answer) {
          return channel.trigger("client-reject", { room: msg.room, rejected: id });
        }
        room = msg.room;
        getCam()
          .then(stream => {
            localUserMedia = stream;
            toggleEndCallButton();
            if("srcObject" in document.getElementById("selfview")){
            document.getElementById("selfview").srcObject = stream;
        }else{
            if (window.URL) {
              document.getElementById("selfview").src = window.URL.createObjectURL(
                stream
              );
            } else {
              document.getElementById("selfview").src = stream;
            }
          }
            caller.addStream(stream);
            var sessionDesc = new RTCSessionDescription(msg.sdp);
            caller.setRemoteDescription(sessionDesc);
            caller.createAnswer().then(function(sdp) {
              caller.setLocalDescription(new RTCSessionDescription(sdp));
              channel.trigger("client-answer", {
                sdp: sdp,
                room: room
              });
            });
          })
          .catch(error => {
            console.log("an error occured", error);
          });
      }
    });

    //Listening for answer to offer sent to remote peer
    channel.bind("client-answer", function(answer) {
      if (answer.room == room) {
        console.log("answer received");
        caller.setRemoteDescription(new RTCSessionDescription(answer.sdp));
      }
    });

    channel.bind("client-reject", function(answer) {
      if (answer.room == room) {
        console.log("Call declined");
        alert("call to " + answer.rejected + "was politely declined");
        endCall();
      }
    });

    channel.bind("client-endcall", function(answer) {
      if (answer.room == room) {
        console.log("Call Ended");
        endCall();
      }
    });

I EXPECTED that the video call will work don't want to use any API, help me see where I went wrong.


回答1:


Call setRemoteDescription(offer) before requesting the camera.

This puts the RTCPeerConnection in the right signaling state ("have-remote-offer") to receive and process remote ICE candidates correctly.

There's no time to request the camera first when an offer comes in. Incoming offers are typically followed closely by trickled ICE candidates on your signaling channel. addIceCandidate won't know what to do with those if it hasn't seen an offer.

Move the setRemoteDescription call ahead of the getMedia call in the promise chain to fix it. You have more time then before returning an answer.

Though that's still not great, since this approach often ends up blocking initial WebRTC negotiation on a user permission prompt for the camera. This is called tight coupling. Sadly, the current state of WebRTC encourages it, since getting the best IP mode is gated on getUserMedia in most browsers.

Lastly, there's a lot of old API usage here. See my other answer for newer APIs to use.



来源:https://stackoverflow.com/questions/57256828/how-to-fix-invalidstateerror-cannot-add-ice-candidate-when-there-is-no-remote-s

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!