【问题标题】:WebRTC stuck in connecting stateWebRTC 卡在连接状态
【发布时间】:2020-05-26 11:04:02
【问题描述】:

我已经成功地将 WebRTC 连接的提议、答案和候选冰从 A 传递到 B。此时,连接卡在 "connecting" 状态。启动器(A)似乎超时或一段时间后切换到"failed"状态,而其远程(B)永久保持在"connecting"状态。

任何帮助将不胜感激。

创建对等点(A 和 B):

let peer = new RTCPeerConnection({
    iceServers: [
        {
            urls: [
                "stun:stun1.l.google.com:19302",
                "stun:stun2.l.google.com:19302",
            ],
        },
        {
            urls: [
                "stun:global.stun.twilio.com:3478?transport=udp",
            ],
        },
    ],
    iceCandidatePoolSize: 10,
});

创建报价 (A):

peer.onnegotiationneeded = async () => {
    offer = await peer.createOffer();
    await peer.setLocalDescription(offer);
};

收集冰候选人(A):

peer.onicecandidate = (evt) => {
    if (evt.candidate) {
        iceCandidates.push(evt.candidate);
    } else {
        // send offer and iceCandidates to B through signaling server
        // this part is working perfectly
    }
};

创建答案并填充候选冰 (B):

await peer.setRemoteDescription(offer);

let answer = await this._peer.createAnswer();
await peer.setLocalDescription(answer);

// send answer back to A through signaling server

for (let candidate of sigData.iceCandidates) {
    await peer.addIceCandidate(candidate);
}

通过信令服务器 (A) 从 B 应答:

await peer.setRemoteDescription(answer);

检测连接状态变化(A和B):

peer.onconnectionstatechange = () => {
    console.log("state changed")
    console.log(peer.connectionState);
}

另请注意,有两次成功连接,但我还没有看到它再次工作。

编辑:我忘了说我也在创建一个数据通道(没有这个onicecandidate 事件似乎不会调用)。在构造 RTCPeerConnection 并附加任何事件处理程序后立即调用它。

let channel = peer.createDataChannel("...", {
    id: ...,
    ordered: true,
});

编辑 2:正如@jib 建议的那样,我现在也在 B 收集候选冰并将它们送回 A 进行添加。但是,完全相同的问题仍然存在。

编辑 3:似乎在我第一次硬重新加载 A 的网页和 B 的网页时连接。连接停止工作,直到我再次硬重新加载。有谁知道为什么会这样?至少我应该能够暂时继续开发,直到我弄清楚这个问题。

编辑 4:我删除了我正在使用的 iceServers 并将 RTCPeerConnection 构造函数留空。不知何故,它现在更可靠了。但我还没有在 iOS Safari 上成功连接!

【问题讨论】:

  • 你只是在一方面收集 ICE 候选人。此外,您正在缓存它们,这违背了 Trickle ICE 的全部目的。相反,尽快向他们发出信号。一般来说,您希望 A 和 B 具有相同的代码,而不是不同的代码。
  • @jib 谢谢,我会尝试进行这些更改。没想到你也要把另一边的寒冰候选人召集起来。我暂时不太担心缓存候选冰的延迟,只是想先让它工作:) 编辑:无论如何,所有候选冰似乎都是同时通过的,所以缓存它们不会似乎没什么区别。
  • @jib 我已经尝试在 B 中收集 ice 候选人并将他们发送回 A,然后 A 调用 addIceCandidate,但仍然没有运气。

标签: javascript webrtc


【解决方案1】:

在两个浏览器窗口中打开this,然后点击其中一个窗口中的Connect 按钮。这是代码:

const pc = new RTCPeerConnection();

call.onclick = async () => {
  const stream = await navigator.mediaDevices.getUserMedia({video:true,audio:true})
  video.srcObject = stream;
  for (const track of stream.getTracks()) {
    pc.addTrack(track, stream);
  }
};

pc.ontrack = ({streams}) => video.srcObject = streams[0];
pc.oniceconnectionstatechange = () => console.log(pc.iceConnectionState);
pc.onicecandidate = ({candidate}) => sc.send({candidate});
pc.onnegotiationneeded = async () => {
  await pc.setLocalDescription(await pc.createOffer());
  sc.send({sdp: pc.localDescription});
}

const sc = new localSocket(); // localStorage signaling hack
sc.onmessage = async ({data: {sdp, candidate}}) => {
  if (sdp) {
    await pc.setRemoteDescription(sdp);
    if (sdp.type == "offer") {
      await pc.setLocalDescription(await pc.createAnswer());
      sc.send({sdp: pc.localDescription});
    }
  } else if (candidate) await pc.addIceCandidate(candidate);
}

A 和 B 的来源相同。将 localSocket hack 替换为您喜欢的信号通道(例如 websocket)。

不要缓存 ICE 候选对象,因为这违背了 Trickle ICE 的目的。它可能在本地看起来很快,但在实际网络中,ICE 可能需要一些时间。

事实上,如果您延迟发送报价/答案,直到所有本地候选人都收集完毕,发送候选人是没有意义的,因为那时候选人已经嵌入报价/答案 (pc.localDescription)。

【讨论】:

  • 通话按钮对我不起作用,可能是因为我没有相机。但是我设法通过删除我的自定义iceServers 并将RTCPeerConnection 构造函数留空,就像上面一样。你知道为什么我使用的iceServers 不起作用吗?我听说如果我把iceServers留空,浏览器可能没有任何默认值,所以我不想冒险。
  • 另外,如果我将一个空的ondatachannel 事件附加到 B,连接会突然停止工作。
【解决方案2】:

终于!几周后,我发现了这个问题,这在我的问题中包含的代码中并不明显,但对于遇到类似问题的任何人可能仍然有用。

我假设在onnegotiationneeded 事件触发并创建了提议/答案之后完成了冰收集。

由于这个不正确的假设,我在这个阶段与 ice 候选人一起发出了提议/答案,但非常频繁(根据我的经验,总是在 iOS Safari 中)此时尚未创建提议/答案。

我通过创建两个承诺来解决这个问题:a)完成 ice 候选人收集,b)创建提议/答案。我在两个 promise 上使用了Promise.all,当它们都完成时,我通过信令服务器一次性发送了 ice 候选和 offer/answer。

这行得通,但当然,将来我应该“涓涓”这些信息,通过发送零碎的信息,而不是等待一切完全完成。但是我以后会担心这个,因为目前我正在使用 HTTP 请求,这太麻烦了。

编辑:当包含iceServers 时,我的连接仍然总是卡住,所以我创建了一个new question。但是,当不包含 iceServers 时,本地连接现在是 100% 完全可靠的 :)

【讨论】:

  • 嗨,您能提供更多信息吗?我的出版商坚持收集和订阅者坚持连接
  • 基本上,我的信号系统在一条消息中同时发送提议/答案和候选冰。一旦创建了报价/答案,我就会发送此消息,然后再确保此时已完全收集到冰候选人。在我的情况下,我需要确保收集了两个候选冰并且生成了报价/答案,然后再通过我的信号系统发送它们。如果您单独发送它们,那么这可能不是您的问题。
  • 是的,我签入了 janus 服务器,一切正常,但我的服务器出现问题,可能我需要检查更多有关 ice 的信息,谢谢
猜你喜欢
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
相关资源
最近更新 更多