Webrtc 呼叫连接,即使我没有在对等候选项中添加冰候选者


const pc1 = new RTCPeerConnection(null);
const pc2 = new RTCPeerConnection(null);
async function call(){
const offer = await pc1.createOffer();
pc1.setLocalDescription(offer);
pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
pc2.setLocalDescription(answer);
pc1.setRemoteDescription(pc2.localDescription);
}
async function showVideo(){
const config={audio:true,video:true};
stream = await navigator.mediaDevices.getUserMedia(config);
pc1.addStream(stream);
}

这是我的代码的简化版本。

现在,在这段代码中,我不会将icecandidate添加到peer中,也不会监听onicecandidate。但如果我调用call((两次,我的连接就会建立起来。

我使用事件处理程序来更改iceconnectionstate,发现当我第一次调用时,它的iceconnection状态处于检查状态,而当我第二次调用它时,它被调用,状态变为完成。

所以我想知道,即使没有将icecandidate添加到另一个对等点,如何启动检查并进行第二次连接?

四个原因:代码中的错误、Chrome的行为、没有防火墙以及trickle ICE的工作原理。

1( 代码中的错误

首先:以下集合pc1.setRemoteDescription(null):

pc2.setLocalDescription(answer);
pc1.setRemoteDescription(pc2.localDescription); // pc2.localDescription == null here

因为CCD_ 2是一种不会立即完成的异步方法。

现在pc2.localDescription最终设置了,所以第二次调用call()时,它就在那里,协商就开始了。

要解决此问题,您必须使用awaitthen:等待承诺

await pc2.setLocalDescription(answer);
pc1.setRemoteDescription(pc2.localDescription); // pc2.localDescription is set!

2( 如果没有NAT,就不需要ICE服务器

浏览器可以使用"主机"候选者(您机器的IP(与同一局域网上的其他机器或自身进行通信。不需要ICE服务器来发现这些。

3( Trickle ICE是一种优化

使用onicecandidate的单个ice候选者的信令(滴流(是一种旨在加快协商的优化。一旦setLocalDescription成功,浏览器的内部ICE代理就会启动,在发现ICE候选时将其插入localDescription本身。等待几秒钟进行谈判,根本不需要涓涓细流:所有ICE候选人都会在报价中,并发送答案。

4( Chrome中一个有趣的行为

我怀疑在第二次调用call()Chrome的ICE代理时,会记住上次收集的候选主机,并在setLocalDescription的成功回调运行完成之前立即将它们插入到offer和localDescription中。这个可能是一个错误,也可能是规范规定的工作方式。无论如何,行为似乎因浏览器atm的不同而有所不同,所以我今天不会依赖它。

SDP中ICE的复制/证明步骤

  1. 在Chrome和Firefox中运行这个fiddle
  2. 单击Call!按钮一次,然后再单击一次
  3. 观察Firefox:您将看到0 candidates(输出两次(;两次;没有连接
  4. 观察Chrome:它在第二次时间与3 candidates连接,并带有候选者
  5. 现在取消对行// await wait(2000);的注释
  6. 两个浏览器现在都连接,2秒钟后连接48 candidates

系统中的实际候选数量可能有所不同,但行为不应该如此。

当我在Firefox中运行此程序时,它每次都不会连接,除非我将其修改为等待或滴入候选程序(。

浏览器更新:这两款浏览器都有漏洞:Firefox限制性太强,Chrome在从ICE故障中恢复时过于宽容

最新更新