WebRTC无法添加第三个对等:“无法在状态稳定中设置远程答案”

时间:2015-05-07 18:31:32

标签: javascript html5 websocket webrtc

我正在写一个多同步的WebRTC视频聊天。 两个对等连接没有问题,控制台没有错误或警告,视频效果很好,但我无法成功地为聊天添加第三方。

在主机(第一个参与者,Firefox)上,在尝试创建答案时,错误显示为“无法在州稳定状态下设置远程答案”。在第二个参与者(Chrome),错误是“无法设置远程回答sdp:调用错误的状态:STATE_INPROGRESS”。在第三个对等点,错误是“错误”无法设置远程回答sdp:调用错误状态:STATE_RECEIVEDINITIATE“

事实证明,第一个对等方无法与第三个对等方拥有视频。其他两个链接显得很好。

通常,我的通信模型如下所示,self_id是会话中每个对等方的唯一ID,locate_peer_connection()将返回我们收到消息的特定对等方的本地peer_connection: / p>

  • 新客户端使用信令服务器向会话发送“peer_arrival”

  • 所有同行已在会话setlocaldescription中,创建商品并发送给新客户

  • 新客户创建所有其他同行的答案和setremotedescription

  • 新客户即将推出视频

使用node.js服务器上的WebSocket完成信令。

我有一些核心代码,更多注意事项:

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

  • peer_connection将peerConnection存储到其他节点,peer_id存储这些对象的相应user_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);
}

2 个答案:

答案 0 :(得分:1)

我可能错了,但这两个一定是你的主要问题:

  1. DateTime.parse("2015-05-08 02:30:01 +0800").strftime("%Y-%m-%d %H") => "2015-05-08 02" 我在这里没有看到任何目标,因此猜测服务器只是向所有人广播此消息。当您将sdp发送到已建立的对等连接时,您必然会收到您现在获得的错误。我的建议是在消息中添加目标id,服务器可以将其转发给特定的对等体,或者服务器可以只是广播,但是对等体的signaling_server.send(...可以检查消息的目标id是否与它是自己的id,如果没有,只需忽略该消息。

  2. event_handler事件,您正在向所有远程对等体广播ICE候选者,同样这是针对单个对等体的,另一个问题可能是PeerConnection上的onicecandidate,然后再将其设置为本地和远程description会抛出错误,你需要某种机制来处理这个问题(仅在设置连接描述后添加ICE候选者)。

  3. 终于有了一个建议。我猜addIceCandidate是一个数组,如果你更改它,你可以删除peer_connection的冗余,

    你可以做类似的事情。

    locate_peer_connection

答案 1 :(得分:1)

当我实施一对多rtc广播时,我遇到了同样的问题,而mido22说的是正确的。您可能正在使用其他传入客户端发送/重置现有已建立的对等对象。您必须为每个新传入的客户端创建新的RTCPeerConenction对象。一般工作流程如下。

peerconnection=[];//empty array

然后使用getUserMedia初始化您的媒体设备 并将媒体流存储到全局变量中,以便在创建商品时添加它 完成此操作后,您可以通过唯一的身份通知您的服务器 然后,您的信令服务器可以将此广播到所有客户端,除非接收到它。然后,每个客户端将检查是否存在peerconnection数组中的唯一ID,并且像mido22表示你可以这样做

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
}