4

I have two peers that want to connect to each other via WebRTC. Typically the first peer would create an offer and send it to the second via a signalling channel/server, the second peer would respond with an answer. This scenario works fine.

However, is it possible to support the case where both peers happen to try to connect to each other simultaneously both sending SDP offers to one another concurrently via the signalling server.

// Both peers do this simultaneously:
const conn = new RTCPeerConnection(null);
const sdpOffer = await conn.createOffer();
await conn.setLocalDescription(sdpOffer);
signalingService.send(peerId, sdpOffer);

// At some point in the future both peers also receive an SDP offer 
// (rather than answer) from the other peer whom they sent an offer to 
// via the signaling service. If this was an answer we'd call 
// RTCPeerConnection.setRemoteDescription, however this doesn't work for an 
// offer: 

conn.setRemoteDescription(peerSDPOffer); 
// In Chrome results in "DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer"

I even tried to "convert" the received peer offers into answers by rewriting the SDP type from offer to answer and setup:actpass to setup:active but that doesn't seem to work, instead I just get a new exception.

So the question is, is this simultaneous connect/offer use case supported in some fashion - or should I close one side/peer RTCPeerConnection & instantiate a new one using RTCPeerConnection.createAnswer this time?

dbotha
  • 1,501
  • 4
  • 20
  • 38
  • 1
    My signaling server intrinsically assigns every peer a session id; my simple strategy to avoid this double offer is that the peer with the higher id starts the offer. You’ll need some sort of coordination like that. – deceze Jan 01 '19 at 10:46
  • Thanks @deceze - that's exactly what I'm going to do – dbotha Jan 01 '19 at 10:50

2 Answers2

4

This situation is known as "signaling glare". The WebRTC API does not really define how to handle this (except for something called "rollback" but it is not implemented in any browser yet and nobody has missed it so far) so you have to avoid this situation yourself.

Simply replacing the a=setup won't work since the underlying DTLS mechanism still needs the concept of a client and a server.

Philipp Hancke
  • 15,855
  • 2
  • 23
  • 31
  • 2
    Firefox implements "rollback" FWIW. It's not firing all the events it should according to the spec, but in this simple situation it should work just fine to roll back the initial offer of one of the peer connections. – jib Jan 01 '19 at 14:13
  • It's hilarious to have core fundamentals of "client" and "server" in an inherently P2P system LOL – Brian Cannard May 20 '19 at 20:09
  • For those who came here looking for glare-avoidance / perfect negotiation solutions: the next Chrome version (M80) will also handle rollback (testable in Chrome Canary at the time). The newest webrtc-spec made some changes to avoid race situations and also includes an example, see: https://www.w3.org/TR/webrtc/#perfect-negotiation-example – nasskalte.juni Dec 19 '19 at 14:20
3

The answer for how to avoid glare these days is to use the Perfect Negotiation Pattern: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation

However, what OP described does work with the slight modification of setting setup:active on one peer and setup:passive on the other: https://codepen.io/evan-brass/pen/mdpgWGG?editors=0010

It might not work for audio / video connections (because those may require negotiating codecs), but I've tested it on Chrome / Firefox / Safari for DataChannel only connections.

You could choose which peer is active and which is passive using whatever system you use to determine 'politeness' in perfect negotiation. One possibility would be to compare the DTLS fingerprints and make whichever one is larger the active peer.

evan-brass
  • 31
  • 3
  • Great! : * Offered a best-practice suggestion before trying to muddle with OPs second-best code. * Supported best-practice suggestion with link to in-depth coverage. * Also offered a "this might work too" suggestion dealing directly with OP's code. Could be better: * No explanation at all of what is the "Perfect Negotiation Pattern" that you suggest as the best option. (Don't expect people to follow links until they are convinced by your answer and want to know more. And you never know when a link will break permanently.) * No sample code at all for your primary suggestion. (Don't expect peop – skylize Apr 23 '22 at 00:16