WebRTC对等连接无法在Safari上建立,但可以在Chrome,Firefox中建立

时间:2018-12-24 12:25:43

标签: javascript safari webrtc mobile-safari sdp

我整理了一个简短的代码段,以测试在同一浏览器选项卡上下文中建立WebRTC对等连接。

const peerConnection1 = new RTCPeerConnection({ iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] });
peerConnection1.addEventListener('signalingstatechange', _ => log('1 signaling state ' + peerConnection1.signalingState));
peerConnection1.addEventListener('icegatheringstatechange', _ => log('1 ICE gathering state ' + peerConnection1.iceGatheringState));
peerConnection1.addEventListener('connectionstatechange', _ => log('1 connection state ' + peerConnection1.connectionState));

const peerConnection2 = new RTCPeerConnection({ iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] });
peerConnection2.addEventListener('signalingstatechange', _ => log('2 signaling state ' + peerConnection1.signalingState));
peerConnection2.addEventListener('icegatheringstatechange', _ => log('2 ICE gathering state ' + peerConnection1.iceGatheringState));
peerConnection2.addEventListener('connectionstatechange', _ => log('2 connection state ' + peerConnection1.connectionState));

const dataChannel = peerConnection1.createDataChannel(null);

const offer = await peerConnection1.createOffer();
await peerConnection1.setLocalDescription(offer);
await peerConnection2.setRemoteDescription(offer);

const answer = await peerConnection2.createAnswer();
await peerConnection2.setLocalDescription(answer);
await peerConnection1.setRemoteDescription(answer);

peerConnection1.addEventListener('icecandidate', event => {
  log('1 ICE candidate ' + (event.candidate ? event.candidate.candidate : 'null'))
  if (event.candidate !== null) {
    peerConnection2.addIceCandidate(event.candidate);
  }
});

peerConnection2.addEventListener('icecandidate', event => {
  log('2 ICE candidate ' + (event.candidate ? event.candidate.candidate : 'null'))
  if (event.candidate !== null) {
    peerConnection1.addIceCandidate(event.candidate);
  }
});

dataChannel.addEventListener('open', () => {
  dataChannel.send('message from 1 to 2');
});

dataChannel.addEventListener('message', event => {
  log('2: ' + event.data);
});

peerConnection2.addEventListener('datachannel', event => {
  monitor(event.channel, 'dc 2');
  event.channel.addEventListener('open', () => {
    event.channel.send('message from 2 to 1');
  });

  event.channel.addEventListener('message', event => {
    log('1: ' + event.data);
  });
});

此代码段可在Chrome和Firefox中使用(在Windows上都尝试了这两个最新版本),但在Safari中(无论是在iOS上还是在macOS上)都无效。

在工作的浏览器中看到的日志:

1 onnegotiationneeded
1 onsignalingstatechange
1 signaling state have-local-offer
2 onsignalingstatechange
2 signaling state have-local-offer
2 onsignalingstatechange
2 signaling state have-local-offer
1 onsignalingstatechange
1 signaling state stable
1 onicegatheringstatechange
1 ICE gathering state gathering
1 onicecandidate
1 ICE candidate candidate:0 1 UDP 2122252543 ... 59263 typ host
1 onicecandidate
1 ICE candidate candidate:2 1 TCP 2105524479 ... 9 typ host tcptype active
2 onicegatheringstatechange
2 ICE gathering state gathering
2 onicecandidate
2 ICE candidate candidate:0 1 UDP 2122252543 ... 59264 typ host
2 onicecandidate
2 ICE candidate candidate:2 1 TCP 2105524479 ... 9 typ host tcptype active
2 oniceconnectionstatechange
1 oniceconnectionstatechange
1 oniceconnectionstatechange
2 oniceconnectionstatechange
dc 1 onopen
2 ondatachannel
dc 2 onopen
dc 2 onmessage
1: message from 1 to 2
dc 1 onmessage
2: message from 2 to 1
1 onicecandidate
1 ICE candidate candidate:1 1 UDP 1686052863 ... 59263 typ srflx raddr ... rport 59263
1 onicegatheringstatechange
1 ICE gathering state complete
1 onicecandidate
1 ICE candidate null
2 onicecandidate
2 ICE candidate candidate:1 1 UDP 1686052863 ... 59264 typ srflx raddr ... rport 59264
2 onicegatheringstatechange
2 ICE gathering state complete
2 onicecandidate
2 ICE candidate null

在无法运行的浏览器中看到的日志:

1 onnegotiationneeded
1 onsignalingstatechange
1 signaling state have-local-offer
1 onicegatheringstatechange
1 ICE gathering state gathering
1 onconnectionstatechange
1 connection state connecting
2 onsignalingstatechange
2 signaling state have-local-offer
2 onsignalingstatechange
2 signaling state have-local-offer
2 onicegatheringstatechange
2 ICE gathering state gathering
2 onconnectionstatechange
2 connection state connecting
1 onsignalingstatechange
1 signaling state stable
1 oniceconnectionstatechange
1 onicecandidate
1 ICE candidate candidate:842163049 1 udp 1677729535 ... 55297 typ srflx raddr 0.0.0.0 rport 0 generation 0 ufrag e+HS network-cost 50
1 onicecandidate
1 ICE candidate null
1 onicegatheringstatechange
1 ICE gathering state complete
2 oniceconnectionstatechange
2 onicecandidate
2 ICE candidate candidate:842163049 1 udp 1677729535 ... 53858 typ srflx raddr 0.0.0.0 rport 0 generation 0 ufrag X+Uv network-cost 50
2 onicecandidate
2 ICE candidate null
2 onicegatheringstatechange
2 ICE gathering state complete

造成差异的原因可能是什么?似乎在Safari中根本没有收集到任何主机候选。这是安全措施吗?我可以在开发中将其关闭以使此代码正常工作吗?生产怎么样?如果这是在不同设备上使用ICE和对等方的完整示例,我如何确保收集到候选人以建立对等方连接?

1 个答案:

答案 0 :(得分:2)

在此WebKit错误报告中,我找到了问题的根源和解决方法:

https://bugs.webkit.org/show_bug.cgi?id=189503

关键是在尝试建立对等连接之前调用navigator.mediaDevices.getUserMedia({ video: true })。除非先获得许可,否则Safari似乎避免公开候选主机。在我的示例中介绍了这一行之后,现在连接成功。