如何在Chrome中为WebRTC调用者设置远程描述而不出错? [英] How to set remote description for a WebRTC caller in Chrome without errors?

查看:3307
本文介绍了如何在Chrome中为WebRTC调用者设置远程描述而不出错?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望逻辑上没有任何缺陷。

I hope there is no flaw in the logic.

第1步:来电者创建优惠

第2步:来电设置localDescription

Step 2: caller sets localDescription

第3步:来电者将说明发送至callee

Step 3: caller sends the description to the callee

// -------------------------------- ---------------------- //

//------------------------------------------------------//

第4步:被叫收到优惠设置远程描述

Step 4: callee receives the offer sets remote description

步骤5:被调用者创建答案

步骤6:被调用方设置本地描述

Step 6: callee sets local description

步骤7:被叫方将描述发送给来电者

Step 7: callee send the description to caller

// --------------------------------------- --------------- //

//------------------------------------------------------//

第8步:来电者收到答案并设置远程说明

Step 8: caller receives the answer and sets remote description

这里是上面的代码

const socket = io();
const constraints = {
  audio: true,
  video: true
};
const configuration = {
  iceServers: [{
    "url": "stun:23.21.150.121"
  }, {
    "url": "stun:stun.l.google.com:19302"
  }]
};

const selfView = $('#selfView')[0];
const remoteView = $('#remoteView')[0];

var pc = new RTCPeerConnection(configuration);

pc.onicecandidate = ({
  candidate
}) => {
  socket.emit('message', {
    to: $('#remote').val(),
    candidate: candidate
  });
};

pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    socket.emit('message', {
      to: $('#remote').val(),
      desc: pc.localDescription
    });
  } catch (err) {
    console.error(err);
  }
};

pc.ontrack = (event) => {
  // don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

socket.on('message', async ({
  from,
  desc,
  candidate
}) => {
  $('#remote').val(from);
  try {
    if (desc) {
      // if we get an offer, we need to reply with an answer
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) => pc.addTrack(track, stream));
        selfView.srcObject = stream;
        await pc.setLocalDescription(await pc.createAnswer());
        console.log(pc.localDescription);
        socket.emit({
          to: from,
          desc: pc.localDescription
        });
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc).catch(err => console.log(err));
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(err => console.log(err));
    }
  } catch (err) {
    console.error(err);
  }
});


async function start() {
  try {
    // get local stream, show it in self-view and add it to be sent
    const stream = await requestUserMedia(constraints);
    stream.getTracks().forEach((track) => pc.addTrack(track, stream));
    attachMediaStream(selfView, stream);
  } catch (err) {
    console.error(err);
  }
}

socket.on('id', (data) => {
  $('#myid').text(data.id);
});


// this function is called once the caller hits connect after inserting the unique id of the callee
async function connect() {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    socket.emit('message', {
      to: $('#remote').val(),
      desc: pc.localDescription
    });
  } catch (err) {
    console.error(err);
  }
}

socket.on('error', data => {
  console.log(data);
});

现在执行步骤8


DOMException:无法在
'RTCPeerConnection'上执行'setRemoteDescription':无法设置远程商品sdp:在错误的
状态下调用:kHaveLocalOffer

DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

DOMException:无法在
'RTCPeerConnection'上执行'addIceCandidate':处理ICE候选者时出错

DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate

尝试调试但没有在逻辑或代码中发现任何缺陷。注意到一个奇怪的事情是 pc 对象有 localDescription currentLocalDescription 我认为创建答案的被叫方必须同时具有 answer 的描述类型,而是显示 localDescription offer currentLocalDescription 类型为 answer

Tried to debug but didn't find any flaw in the logic or code. Noticed one weird thing that the the pc object has localDescription and currentLocalDescription and i think the callee who creates the answer must have both the description type to be answer but instead shows the localDescription to be offer and currentLocalDescription type is answer.


我不知道是否应该表现得像那样,因为我是先生。

I have no idea if it is supposed to behave like that or not as I am begginer.

提前致谢。

推荐答案

您的代码是正确的。这是一个长期存在的 Chrome中的错误 negotiationneeded

Your code is correct. This is a long-standing bug in Chrome with negotiationneeded.

我在小提琴(右键单击并在两个相邻的窗口中打开,然后单击一个中的调用)。

I've instrumented it in a fiddle (right-click and open in TWO adjacent windows, then click call in one).

在Firefox中, 有用。提议者协商一次,因为你一次添加两个曲目(视频/音频):

In Firefox, it works. The offerer negotiates once because you add two tracks (video/audio) at once:

negotiating in stable
onmessage answer

并且,在回答者方面,您在之外添加的曲目'稳定'州被添加到答案中:

and, on the answerer side, the tracks you add outside of 'stable' state are added to the answer:

onmessage offer
adding audio track
adding video track

但是在Chrome中,它被破坏了,触发 negotiationneeded 在提供者上两次,每次跟踪添加一次:

But in Chrome, it's broken, firing negotiationneeded twice on the offerer, once per track added:

negotiating in stable
negotiating in stable
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

并在回答者一边解雇 negotiationneeded 两次,甚至不在'稳定'州:

and firing negotiationneeded twice on the answerer side, which isn't even in 'stable' state:

onmessage offer
adding audio track
adding video track
negotiating in have-remote-offer
negotiating in have-remote-offer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

这些额外事件会造成倒数的破坏这里两端都出现状态错误。

These extra events cause the havoc of reciprocal state errors seen on both ends here.

具体来说,Chrome违反了 spec 此处:

To be specific, Chrome violates two parts of the spec here:


  1. 排队任务以触发此事件。 排队可防止在一次性对连接进行多次修改的常见情况下过早触发协商。

如果连接的信号状态不是稳定,则中止这些步骤[以触发事件]。

If connection's signaling state is not "stable", abort these steps [to fire the event].



解决方法



解决两个 Chrome错误需要(使用 async / await 为简洁起见):

Workaround

Working around both Chrome bugs requires (using async/await for brevity):

let negotiating = false;
pc.onnegotiationneeded = async e => {
  try {
    if (negotiating || pc.signalingState != "stable") return;
    negotiating = true;
    /* Your async/await-using code goes here */
  } finally {
    negotiating = false;
  }
}

这篇关于如何在Chrome中为WebRTC调用者设置远程描述而不出错?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆