【问题标题】:Reconnecting serveral peerConnections after page reload页面重新加载后重新连接多个 peerConnections
【发布时间】:2019-02-10 05:19:16
【问题描述】:

我正在创建一个 Web 应用程序,用于使用 WebRTC 由智能手机监控,而我正在使用 socket.io 为信令服务器。

当我发送流时,我会在接收该流的“监视”页面上创建一个 RTCPeerConnection 对象。我在单独的页面上发送流。用户最多可以附加来自智能手机的四个流,因此在“观看”页面上最多有四个 RTCPeerConnection 对象。

一旦出现“传输”页面的报价,就会自动接收流,然后在“观看”页面上创建 RTCPeerConnection 对象并连接到标准 WebRTC 架构。

“传输”页面:

  function onCreateOfferSuccess(sdp){
  //console.log(sdp);
  pc.setLocalDescription(new RTCSessionDescription(sdp));
  console.log('ask');
  socket.emit('ask', {"sdp":JSON.stringify(pc.localDescription),
                      "user": loggedUserID,
                      "fromSocket": ownSocket});
}

“观看”页面:

socket.on('ask', function(offer){
   if (offer.user === loggedUserID){
      TempDescriptions = JSON.parse(offer.sdp);
      console.log(TempDescriptions)
      currTransmiterSocket = offer.fromSocket;
      console.log(currTransmiterSocket);
      getStream();
}

function getStream(){
   try {
       setTimeout(function(){
           console.log(time, 'getStream()');
           connection = getPeerConnection();

           connection.setRemoteDescription(
           new RTCSessionDescription(TempDescriptions),
           function() {
               connection.createAnswer(gotDescription, function(error){
           console.log(error)
           });
           }, function(error){
               console.log(error)
           });
       }, getStreamDelay*3000)
       getStreamDelay++
   }

   catch(err){
       console.log(err);
   }
};

我的 Web 应用程序需要这样的功能,当我们退出“观看”页面并再次返回时,必须显示之前包含的所有流。

为了实现这个功能,我使用了 oniceconnectionstatechange 方法。如果流断开连接,则执行 iceRestart 函数,该函数使用选项 {iceRestart: true}

创建报价

“传输”页面:

var options_with_restart = {offerToReceiveAudio: false,
                            offerToReceiveVideo: true,
                            iceRestart: true};

function iceRestart(event){  
  try{
      setTimeout(function(){
       pc.createOffer(options_with_restart).then(onCreateOfferSuccess, onCreateOfferError);
      },1000);
  } catch(error) {
      console.log(error);

问题是当我重新启动“观看”页面时,所有页面“传输”立即发送询问。虽然一次创建了四个 RTCPeerConnection 对象(假设用户发送四个流),但只有一个对象被连接。

我已经为这个问题苦苦挣扎了好几天。如上面的代码所示,我尝试在对 getStream() 函数的后续调用中设置增加的时间延迟,我尝试在执行 getStream()signallingState 连接/em> 函数,我尝试了其他几种方法,但都没有奏效。

如果您需要我的部分代码来提供帮助,请写信。

编辑:

“观看”页面中的 gotDescription() 方法。

function gotDescription(sdp) {
   try{
       connection.setLocalDescription(sdp,
       function() {
       registerIceCandidate();
       socket.emit('response', {"sdp": sdp,
                                "user": loggedUserID,
                                "fromSocket": ownSocket,
                                "toSocket": currTransmiterSocket});
        }, function(error){
               console.log(error)
           });
   } catch(err){
       console.log(err);
   }
}

我用RTCPeerConnection object添加console.log

控制台输出: https://i.stack.imgur.com/dQXkE.png1

日志显示连接的signalingState 是“稳定的”,但是当我开发对象时,signalingState 等于“have-remote-offer”

like here

【问题讨论】:

    标签: javascript node.js socket.io webrtc rtcpeerconnection


    【解决方案1】:

    去掉TempDescriptions全局变量,直接把sdp传给getStream(offer.sdp)

    否则,您已经调用了 4 次 socket.on('ask', function(offer){,覆盖了 TempDescriptions。然后 3 多秒后,你的 4 个setTimeouts 滚动,都访问了TempDescriptions 的最终值。

    这可能是只有一个 RTCPeerConnection 重新连接的原因。

    一般来说,使用时间延迟来分离连接似乎是个坏主意,因为它会减慢重新连接的速度。相反,为什么不发送id?例如

    socket.emit('ask', {id: connectionNumber++,
                        sdp: JSON.stringify(pc.localDescription),
                        user: loggedUserID,
                        fromSocket: ownSocket});
    

    更新:停止向window 添加全局变量

    每当您像这样分配给未声明的变量时:

    connection = getPeerConnection();
    

    ...它在window 上创建一个全局变量,例如window.connection,你也有同样的问题。您有 4 个连接,但您将它们存储在一个变量中。

    在源文件的开头键入 "use strict"; 以捕获此内容:

    ReferenceError: assignment to undeclared variable connection
    

    范围界定:一般问题

    您在这里处理 4 个连接,但您缺少确定每个实例范围的方法。

    大多数其他语言会告诉您创建一个类并创建对象实例,并将包括connection 在内的所有内容放在this 上。这是一种很好的方法。在 JS 中,您可以改用闭包。但至少你仍然需要 4 个变量来保存 4 个连接,或者一个 connections 的数组。然后你抬头看——例如来自我提到的id——处理哪个连接。

    另外,您的try/catches 不会捕获异步错误。在处理高度异步的 WebRTC API 时,我强烈推荐using promises,甚至async/await,而不是定义所有这些回调。这使得范围界定变得微不足道。例如

    const connections = [];
    
    socket.on('ask', async ({user, id, sdp, fromSocket}) => {
      try {
        if (user != loggedUserID) return;
        if (!connections[id]) {
          connections[id] = getPeerConnection();
          registerIceCandidate(connections[id]);
        }
        const connection = connections[id];
        await connection.setRemoteDescription(JSON.parse(sdp));
        await connection.setLocalDescription(await connection.createAnswer());
        socket.emit('response', {sdp,
                                 user: loggedUserID,
                                 fromSocket: ownSocket,
                                 toSocket: fromSocket});
      } catch (err) {
        console.log(err);
      }
    };
    

    这样错误处理是可靠的。

    【讨论】:

    • 感谢您的帮助。正如您所建议的,我删除了TempDescriptions 变量并将sdp 传递给getStream()。预期结果不同,现在当我附加四个流并刷新页面时,我只得到一个流和三个错误:DOMException: Failed to set local answer sdp: Called in wrong state: kStable 这是在 gotDescription() 函数中生成的(我使用 gotDescription() 方法更新问题)。我之前搜索过这个错误的信息,因为它也出现了,但谷歌对此了解不多。
    • 我发现getDescription() 在每次调用时都得到相同的sdp 而不是接收每个连接的 sdp,这就是为什么我得到kstable 状态但我不知道如何处理它。
    • 感谢您的回答和您的时间。我理解你写的内容,但我有一件事有问题,正如你之前写的那样,当'ask'发出时我也应该附加connectionNumber ++,问题是'transmit'页面无法访问其他'transmit'页面所以connectionNumber总是一样的。
    • @SalimAzeri 由您决定如何区分各种“传输”页面。例如,您可以组成一个随机 id。由于您想重新与他们联系,这可能是一个好主意,所以他们将以与上次相同的顺序出现。如果这不重要,那么忘记id 并在他们进来时给他们分配一个connection
    • 我创建了用于生成随机 id 的函数,还有一件事,我从 socket.on('ask') 复制了函数,并且我收到未定义 sdp 的错误,我应该如何从 setLocalDescription 捕获 sdp?
    猜你喜欢
    • 2018-10-24
    • 2019-08-05
    • 2013-11-09
    • 2016-10-28
    • 2013-09-18
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    相关资源
    最近更新 更多