如何修复 InvalidStateError:当没有远程 SDP 时无法添加 ICE 候选

Nev*_*uss 3 p2p webrtc

我正在创建一个 webRTC 视频聊天,它在从 firefox 发起呼叫时向呼叫者显示所有活动成员,而接收者正在使用 chrome 显示此错误“未捕获(承诺)DOMException:无法在‘RTCPeerConnection’上执行‘addIceCandidate’:错误处理 ICE 候选人”。当从 firefox 发起呼叫并且接收者使用 firefox 时,我收到两个错误 Invalidstate: cannot add ICECandidate when there is no remote SDP and ICE failed, add a STUN and see about:webrtc

我不知道我在哪里犯了错误

/ 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();
      }
    });
Run Code Online (Sandbox Code Playgroud)

我希望视频通话可以正常工作不想使用任何 API,请帮我看看我哪里出错了。

jib*_*jib 9

setRemoteDescription(offer)在请求相机之前先打电话。

这使RTCPeerConnection处于正确的信令状态 ( "have-remote-offer") 以正确接收和处理远程 ICE 候选对象。

当报价进来时,没有时间首先请求摄像头。传入的报价通常紧随其后的是您的信号通道上的涓流 ICE 候选人。addIceCandidate如果没有看到报价,将不知道如何处理这些。

setRemoteDescription调用移动到getMedia承诺链中的调用之前以修复它。在返回答案之前,您有更多的时间。

尽管这仍然不是很好,因为这种方法通常最终会阻止对相机的用户权限提示进行初始 WebRTC 协商。这称为紧耦合。可悲的是,WebRTC 的当前状态鼓励它,因为getUserMedia在大多数浏览器中获得最佳 IP 模式是封闭的。

最后,这里有很多旧的 API 用法。有关要使用的较新 API,请参阅我的其他答案