2

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

当我发送一个流时,我在接收这个流的“watch”页面上创建了一个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()函数的后续调用中设置增加的时间延迟,我尝试signallingState在执行getStream()函数之前检查连接,我尝试了其他几种方法,但都没有奏效。

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

编辑:

“观看”页面中的 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);
   }
}

console.log加上RTCPeerConnection object

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

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

像这儿

4

1 回答 1

1

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

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

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

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

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

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

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

connection = getPeerConnection();

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

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

ReferenceError: assignment to undeclared variable connection

范围:一般问题

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

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

此外,您的try/ catches 不会捕获异步错误。在处理高度异步的 WebRTC API 时,我强烈建议不要定义所有这些回调,而是使用 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);
  }
};

这种方式的错误处理是可靠的。

于 2019-02-10T13:44:29.073 回答