【问题标题】:Failed to execute 'setLocalDescription' on 'RTCPeerConnection无法在“RTCPeerConnection”上执行“setLocalDescription”
【发布时间】:2018-11-18 10:05:11
【问题描述】:

这个错误已经有一段时间了

一直在尝试使用 async 来等待本地描述更新,但是由于我的代码现在如何工作,它无法集成它,而且我听说 socket.on 本身已经做了 async。

我也试过在 vs 代码中使用断点来调试不能正常工作的代码。

如果有人知道解决方法,将不胜感激。 下面附上代码

'use strict';

var localStream;
var remoteStream;
var isInitiator;
var configuration = {
  iceServers: [
    {
      urls: 'stun:stun.l.google.com:19302'
    }
  ]
};
var pc = new RTCPeerConnection(configuration);

// Define action buttons.
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');

/////////////////////////////////////////////

window.room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  isInitiator = true;
  startVideo();
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  startVideo();
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

function sendMessage(message) {
  socket.emit('message', message);
}

// This client receives a message
socket.on('message', function(message) {
  try {
    if (message.type === 'offer') {
      pc.setRemoteDescription(new RTCSessionDescription(message));
      // const stream = navigator.mediaDevices.getUserMedia({
      //   audio: true,
      //   video: true
      // });
      // stream.getTracks().forEach(track => pc.addTrack(track, localStream));
      pc.setLocalDescription(
        pc.createAnswer(setLocalAndSendMessage, function(err) {
          console
            .log(err.name + ': ' + err.message)
            .then(pc.setLocalDescription);
        })
      );
    } else if (message.type === 'answer') {
      console.log('This is to check if answer was returned');
      pc.setRemoteDescription(new RTCSessionDescription(message));
    } else if (message.type === 'candidate') {
      pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
});

////////////////////////////////////////////////////

const localVideo = document.querySelector('#localVideo');
const remoteVideo = document.querySelector('#remoteVideo');

// Set up initial action buttons status: disable call and hangup.
callButton.disabled = true;
hangupButton.disabled = true;

// Add click event handlers for buttons.
callButton.addEventListener('click', callStart);
hangupButton.addEventListener('click', hangupCall);

function startVideo() {
  navigator.mediaDevices
    .getUserMedia({
      audio: true,
      video: true
    })
    .then(function(mediaStream) {
      localStream = mediaStream;
      localVideo.srcObject = mediaStream;
    })
    .catch(function(err) {
      console.log('getUserMedia() error: ' + err.name);
    });
  callButton.disabled = false;
}

function callStart() {
  createPeerConnection();
  //pc.addTrack(mediaStream);
  //stream.getTracks().forEach(track => pc.addTrack(track, localStream));
  callButton.disabled = true;
  hangupButton.disabled = false;
  if (isInitiator) {
    console.log('Sending offer to peer');
    pc.createOffer(setLocalAndSendMessage, function(err) {
      console.log(err.name + ': ' + err.message).then(pc.setLocalDescription);
    });
  }
}

/////////////////////////////////////////////////////////

function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(null);
    pc.onicecandidate = ({ candidate }) => sendMessage({ candidate });
    pc.ontrack = event => {
      if (remoteVideo.srcObject) return;
      remoteVideo.srcObject = event.streams[0];
    };
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
    return;
  }
}

function setLocalAndSendMessage(sessionDescription) {
  console.log('setLocalAndSendMessage sending message', sessionDescription);
  pc.setLocalDescription(sessionDescription);
  sendMessage(sessionDescription);
}

function hangupCall() {
  pc.close();
  pc = null;
}

【问题讨论】:

    标签: javascript socket.io webrtc


    【解决方案1】:

    没有解决方法来理解异步代码。你在这里被剪掉了。

    如果你不打算使用async/await,那么你需要解决 JavaScript 是单线程的,并且永远不能阻塞等待异步操作完成,所以你永远不能只使用直接从异步方法返回值,就像您在这里尝试做的那样:

    createAnswer 是一个异步方法,返回一个promise,而不是答案,所以这是错误:

      pc.setLocalDescription(                                    // <-- wrong
        pc.createAnswer(setLocalAndSendMessage, function(err) {  //
          console
            .log(err.name + ': ' + err.message)
            .then(pc.setLocalDescription);
        })
      );
    

    你打电话给setLocalDescription(promise),这会给你你提到的错误,因为承诺不是有效的描述。相反,a promise is an object you attach callbacks to:

    const promise = pc.createAnswer();
    promise.then(setLocalAndSendMessage, function(err) {
      console.log(err.name + ': ' + err.message);
    });
    

    或者简单地说:

    pc.createAnswer()
      .then(setLocalAndSendMessage, function(err) {
        console.log(err.name + ': ' + err.message);
      });
    

    我们甚至可以连续使用then形成promise chain:

    pc.createAnswer()
      .then(function(answer) {
        return pc.setLocalDescription(answer);
      })
      .then(function() {
        sendMessage(pc.localDescription);
      })
      .catch(function(err) {
        console.log(err.name + ': ' + err.message);
      });
    

    另外,我不应该告诉你console.log() 不会返回承诺!

    遗憾的是,您在这里复制了really old code,而RTCPeerConnection 有一些遗留的 API,并且会采取一些技巧,有时让调用者在没有真正的承诺链或检查错误的情况下调用这些协商方法。但这不可避免地会带来麻烦。

    【讨论】:

    • 非常感谢您的回复。我最近刚刚开始编写 WebRTC/js,这就是为什么我可能会犯新手错误,正如我对 console.log() 的输出内容缺乏了解所指出的那样。我将参考您的博客以获取更多信息,因为我一直在引用的代码来自官方 WebRTC Codelabs,我最初认为它是最新的,直到我不得不将它完全分解以解决其中的所有问题.
    • 我进行了更改以使用异步并将所有语法更新为新标准,但远程视频仍然不显示。由于某些示例使用 pc1 和 pc2 而不是仅使用 pc = new RTCpeerconnection(configuration),房间中是否可能有 2 个以上的用户? pastebin.com/0EHAUnEc
    • createPeerConnection() 内部的pc 似乎是与您的代码的其余部分正在使用的pc 完全不同的对等连接,所以我并不惊讶它的ontrack 永远不会触发。
    • 我从 createPeerConnection() 函数中删除了重复的新 RTCconnection,还在 pc.ontrack 中添加了一个控制台日志,以检查它是否触发并返回内部消息。编辑:原来是我的挂断按钮,导致候选人在连接后立即触发为空,导致它不显示远程视频
    猜你喜欢
    • 2022-08-21
    • 2022-10-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    • 2020-03-13
    相关资源
    最近更新 更多