Javascript WebRTC未能添加第三个对等方:“0”;无法将远程应答设置为“状态稳定”;

Javascript WebRTC未能添加第三个对等方:“0”;无法将远程应答设置为“状态稳定”;,javascript,html,websocket,webrtc,Javascript,Html,Websocket,Webrtc,我正在写一个多点WebRTC视频聊天。 两个对等方在连接上没有问题,控制台中没有错误或警告,视频工作正常,但我无法成功地将第三方添加到聊天中 在主机(第一个参与者Firefox)上,尝试创建应答时,错误显示为“无法将远程应答设置为状态稳定”。在第二个参与者(Chrome)处,错误为“未能设置远程应答sdp:调用错误状态:状态\u INPROGRESS”。在第三个对等点,错误为“错误为”,无法设置远程应答sdp:在错误状态下调用:state_RECEIVEDINITIATE“ 结果是,第一个对等方

我正在写一个多点WebRTC视频聊天。 两个对等方在连接上没有问题,控制台中没有错误或警告,视频工作正常,但我无法成功地将第三方添加到聊天中

在主机(第一个参与者Firefox)上,尝试创建应答时,错误显示为“无法将远程应答设置为状态稳定”。在第二个参与者(Chrome)处,错误为“未能设置远程应答sdp:调用错误状态:状态\u INPROGRESS”。在第三个对等点,错误为“错误为”,无法设置远程应答sdp:在错误状态下调用:state_RECEIVEDINITIATE“

结果是,第一个对等方无法与第三个对等方进行视频连接。其他两个链接看起来都很好

通常,我的通信模型如下,
self\u id
是会话中每个对等方的唯一id,
locate\u peer\u connection()
将返回我们从中接收消息的特定对等方的本地对等方连接:

  • 新客户端使用信令服务器向会话发送“对等到达”

  • 会话中已存在的所有对等方setlocaldescription,创建报价并发送到新客户端

  • 新客户端创建对所有其他对等方和setremotedescription的应答

  • 新客户的视频即将发布

在node.js服务器上使用WebSocket发送信号

我有以下一些核心代码,还有一些注意事项:

  • self_id是会话中每个客户端的唯一id

  • 对等连接存储与其他节点的对等连接,对等id存储这些对象各自的用户id

  • local_stream是来自getUserMedia的本地视频流(已被视为不同的浏览器)

对这个问题有什么见解吗?我的模型有什么问题吗

// locate a peer connection according to its id
function locate_peer_connection(id) {
   var index = peer_id.indexOf(id);
   // not seen before
   if (index == -1) {
    add_peer_connection();
     peer_id.push(id);
    index = peer_id.length - 1;
  }
   return index;
 }

// add a peer connection
function add_peer_connection() {
  console.log('add peer connection');
  // add another peer connection for use
  peer_connection.push(new rtc_peer_connection({ "iceServers": [{ "url": "stun:"+stun_server }]}));

  // generic handler that sends any ice candidate to the other peer
  peer_connection[peer_connection.length - 1].onicecandidate = function (ice_event) {
    if (ice_event.candidate) {
      signaling_server.send(
        JSON.stringify({
          type: "new_ice_candidate",
          candidate: ice_event.candidate,
          id: self_id,
          token:call_token
        })
      );
      console.log('send new ice candidate, from ' + self_id);
    }
  };

  // display remote video streams when they arrive using local <video> MediaElement
  peer_connection[peer_connection.length - 1].onaddstream = function (event) {
    video_src.push(event.stream); // store this src
    video_src_id.push(peer_connection.length - 1);
    if (video_src.length == 1) { // first peer
      connect_stream_to_src(event.stream, document.getElementById("remote_video"));
      // video rotating function
      setInterval(function() {
        // rorating video src
        var video_now = video_rotate;
        if (video_rotate == video_src.length - 1) {
          video_rotate = 0;
        } else {
          video_rotate++;
        }
        var status = peer_connection[video_src_id[video_rotate]].iceConnectionState;
        if (status == "disconnected" || status == "closed") { // connection lost, do not show video
          console.log('connection ' + video_rotate + ' liveness check failed');
        } else if (video_now != video_rotate) {
          connect_stream_to_src(video_src[video_rotate], document.getElementById("remote_video"));
        }
      }, 8000);
      // hide placeholder and show remote video
      console.log('first remote video');
      document.getElementById("loading_state").style.display = "none";
      document.getElementById("open_call_state").style.display = "block";
    }
    console.log('remote video');
  };
  peer_connection[peer_connection.length - 1].addStream(local_stream);
}

// handle new peer
function new_peer(signal) {
  // locate peer connection
  var id = locate_peer_connection(signal.id);
  console.log('new peer ' + id);
  // create offer
  peer_connection[id].createOffer(function(sdp) {
    peer_connection[id].setLocalDescription(sdp, 
    function() { // call back
      console.log('set local, send offer, connection '+ id);
      signaling_server.send(
        JSON.stringify({
          token: call_token,
          id: self_id,
          type:"new_offer",
          sdp: sdp
        })
      );
    }, log_error);
  }, log_error);
}

// handle offer
function new_offer_handler(signal) {
  var id = locate_peer_connection(signal.id);
  console.log('new offer ' + id);
  // set remote description
  peer_connection[id].setRemoteDescription(
    new rtc_session_description(signal.sdp), 
    function() { // call back
      peer_connection[id].createAnswer(function(sdp) {
        peer_connection[id].setLocalDescription(sdp, function () {
          console.log('set local, send answer, connection '+ id);
          signaling_server.send(
            JSON.stringify({
              token: call_token,
              id: self_id,
              type:"new_answer",
              sdp: sdp
            })
          );
        }, 
        log_error);
    }, log_error);
  }, log_error);
}

// handle answer
function new_answer_handler(signal) {
  var id = locate_peer_connection(signal.id);
  console.log('new answer ' + id);
  peer_connection[id].setRemoteDescription(new rtc_session_description(signal.sdp),
    function() {
      console.log('receive offer answer, set remote, connection '+ id);
    }
    , log_error);
}

// handle ice candidate
function ice_candidate_handler(signal) {
  var id = locate_peer_connection(signal.id);
  console.log('get new_ice_candidate from ' + id);
  if (typeof(RTCIceCandidate) != "undefined") {
    peer_connection[id].addIceCandidate(
        new RTCIceCandidate(signal.candidate)
    );
  } else { // firefox
    peer_connection[id].addIceCandidate(
      new mozRTCIceCandidate(signal.candidate)
    );
  }
}

function event_handler(event) {
  var signal = JSON.parse(event.data);
  if (signal.type === "peer_arrival") {
    new_peer(signal);
  } else if (signal.type === "new_ice_candidate") {
    ice_candidate_handler(signal);
  } else if (signal.type === "new_offer") { // get peer description offer
    new_offer_handler(signal);
  } else if (signal.type === "new_answer") { // get peer description answer
    new_answer_handler(signal);
  } else if (signal.type === "new_chat_message") { // chat message and file sharing info
    add_chat_message(signal);
  } else if (signal.type === "new_file_thumbnail_part") { // thumbnail
    store_file_part(signal.name, "thumbnail", signal.id, signal.part, signal.length, signal.data);
    if (file_store[signal.id].thumbnail.parts.length == signal.length) {
      document.getElementById("file_list").innerHTML = get_file_div(signal.id, signal.name)+document.getElementById("file_list").innerHTML;
      document.getElementById("file-img-"+signal.id).src = file_store[signal.id].thumbnail.parts.join(""); 
    }
  } else if (signal.type === "new_file_part") { // file
    console.log('get new_file_part ' + signal.id);
    store_file_part(signal.name, "file", signal.id, signal.part, signal.length, signal.data);
    update_file_progress(signal.name, signal.id, file_store[signal.id].file.parts.length, signal.length);
  }
}

// generic error handler
function log_error(error) {
  console.log(error);
}
//根据对等连接的id定位对等连接
功能定位\u对等\u连接(id){
var指数=对等id.indexOf(id);
//前所未见
如果(索引==-1){
添加对等连接();
对等推送(id);
索引=peer_id.length-1;
}
收益指数;
}
//添加对等连接
函数add_peer_connection(){
log(“添加对等连接”);
//添加另一个对等连接以供使用
peer_connection.push(新的rtc_peer_连接({“iceServers”:[{“url”:“stun:+stun_server}]}));
//将任何ice候选者发送到其他对等方的通用处理程序
对等连接[peer\u connection.length-1]。onicecandidate=函数(ice\u事件){
if(ice_事件候选){
信令发送(
JSON.stringify({
类型:“新的ice候选人”,
候选人:ice_事件候选人,
id:self_id,
令牌:呼叫令牌
})
);
console.log('从'+self_id'发送新的ice候选者);
}
};
//使用本地MediaElement显示到达时的远程视频流
对等连接[peer\u connection.length-1]。onaddstream=函数(事件){
video_src.push(event.stream);//存储此src
视频\u src\u id.push(peer\u connection.length-1);
如果(video_src.length==1){//第一个对等点
将_stream_连接到_src(event.stream,document.getElementById(“远程视频”));
//视频旋转功能
setInterval(函数(){
//旋转视频
var video\u now=video\u rotate;
if(video\u rotate==video\u src.length-1){
视频旋转=0;
}否则{
视频旋转++;
}
var status=peer\u connection[video\u src\u id[video\u rotate]]。iceConnectionState;
如果(状态==“断开连接”| |状态==“关闭”){//连接丢失,则不显示视频
console.log('connection'+video_rotate+'liveness check failed');
}else if(视频\u now!=视频\u旋转){
将_stream_连接到_src(video_src[video_rotate],document.getElementById(“remote_video”);
}
}, 8000);
//隐藏占位符并显示远程视频
console.log(“第一个远程视频”);
document.getElementById(“加载状态”).style.display=“无”;
document.getElementById(“打开\调用\状态”).style.display=“块”;
}
console.log(“远程视频”);
};
对等连接[peer\u connection.length-1].addStream(本地\u流);
}
//处理新同事
功能新对等(信号){
//定位对等连接
var id=定位对等连接(signal.id);
console.log('newpeer'+id);
//创建报价
对等连接[id]。createOffer(函数(sdp){
对等连接[id]。setLocalDescription(sdp,
函数(){//回调
log('set local,send offer,connection'+id);
信令发送(
JSON.stringify({
令牌:调用令牌,
id:self_id,
键入:“新报价”,
sdp:sdp
})
);
},日志错误);
},日志错误);
}
//处理报价
新功能\u提供\u处理程序(信号){
var id=定位对等连接(signal.id);
console.log(“新报价”+id);
//设置远程描述
对等连接[id]。setRemoteDescription(
新的rtc会话描述(signal.sdp),
函数(){//回调
对等连接[id]。createAnswer(函数(sdp){
对等连接[id]。setLocalDescription(sdp,函数(){
log('set local,send answer,connection'+id);
信令发送(
JSON.stringify({
令牌:调用令牌,
id:self_id,
键入:“新答案”,
sdp:sdp
})
);
}, 
日志错误);
},日志错误);
},日志错误);
}
//处理答案
功能新应答处理器(信号){
var id=定位对等连接(signal.id);
console.log('new answer'+id);
对等连接[id].setRemoteDescription(新的rtc_会话_说明(signal.sdp),
函数(){
console.log('receive offer answer,set remote,connection
if(peer_connection[signal.id]){
    //do something...
}else{
    peer_connection[signal.id] = new PeerConnection(...
}
peerconnection=[];//empty array
if(peerconnection[signal.id])
{
//do something with existing peerconnections
}
else
{
peerconnection[signal.id]=new RTCPeerConnection({"stun_server_address"});
//register peerconnection[signal.id].onicecandidate callback
//register peerconnection[signal.id].onaddstream callback
//createoffer
//if local stream is ready then
peerconnection[signal.id].addStream(localStream);
//and rest of the stuff go as it is  like in one-to-one call..
//setLocalDescriptor setRemoteDescriptor
}