javascript - WebRTC peer connection does not establish on Safari, does in Chrome, Firefox - Stack Overflow

I've put together a quick snippet to test establishing a WebRTC peer connection within the same br

I've put together a quick snippet to test establishing a WebRTC peer connection within the same browser tab context.

const peerConnection1 = new RTCPeerConnection({ iceServers: [ { urls: 'stun:stun.l.google: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: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);
  });
});

This snippet works in Chrome and Firefox (tried both latest versions on Windows), but does not work in Safari, neither on iOS nor on macOS.

The log as seen in working browsers:

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 plete
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 plete
2 onicecandidate
2 ICE candidate null

The log as seen in non-working browsers:

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 plete
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 plete

What could be the reason for the difference? It looks like no host candidates at all are collected in Safari. Is this a security measure? Can I turn it off in development to make this code work? How about production? Had this been a full example with ICE and peers on different devices, how could I have made sure candidates were collected in order to establish the peer connection?

I've put together a quick snippet to test establishing a WebRTC peer connection within the same browser tab context.

const peerConnection1 = new RTCPeerConnection({ iceServers: [ { urls: 'stun:stun.l.google.: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.: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);
  });
});

This snippet works in Chrome and Firefox (tried both latest versions on Windows), but does not work in Safari, neither on iOS nor on macOS.

The log as seen in working browsers:

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 plete
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 plete
2 onicecandidate
2 ICE candidate null

The log as seen in non-working browsers:

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 plete
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 plete

What could be the reason for the difference? It looks like no host candidates at all are collected in Safari. Is this a security measure? Can I turn it off in development to make this code work? How about production? Had this been a full example with ICE and peers on different devices, how could I have made sure candidates were collected in order to establish the peer connection?

Share Improve this question asked Dec 24, 2018 at 12:25 Tomáš HübelbauerTomáš Hübelbauer 10.8k17 gold badges74 silver badges141 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

I've found the source of the problem and a workaround in this WebKit bug report:

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

The key is to call navigator.mediaDevices.getUserMedia({ video: true }) before trying to establish the peer connection. Safari seems to avoid disclosing the host candidates unless the permissions have been given first. After introducing this line to my example, the connection now succeeds.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745180357a4615389.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信