feross / simple-peer

📡 Simple WebRTC video, voice, and data channels
MIT License
7.31k stars 972 forks source link

Implement ICE restarts #579

Open t-mullen opened 4 years ago

t-mullen commented 4 years ago

When network conditions change or the current route is congested, it's possible to restart ICE and gather/select a new candidate pair. Apparently this happens very frequently with cellular networks or with certain types of NATs.

Restart is done by calling createOffer with the iceRestart option when iceConnectionState == 'failed'.

As an optimization, ICE restart can be done preemptively when iceConnectionState == 'disconnected' (before 'failed') and it won't immediately bounce back to connected.

holtwick commented 4 years ago

Might this be related?

feross commented 4 years ago

I would love to have this feature for a new project I'm working on. @t-mullen Any idea how you'd like the API design for this to work?

t-mullen commented 4 years ago

simple-peer should detect the ice connection state 'failed' and do this transparently (no public API, except maybe a max number of restart attempts).

Restart is just renegotiation with iceRestart: true in createOffer (or by callingrestartIce() if that's implemented in the browser).

https://developer.mozilla.org/en-US/docs/Web/API/RTCOfferOptions/iceRestart https://medium.com/the-making-of-whereby/ice-restarts-5d759caceda6

feross commented 4 years ago

A few questions:

t-mullen commented 4 years ago
samuelmaddock commented 4 years ago

We currently avoid glare by sending a { renegotiate: true } message from the non-initiator to the initiator. We could expand this message to be { renegotiate: { iceRestart: true/false }.

In my application, I configure peers in a star topology where clients connect to a central host. As an optimization for this approach, clients disconnect from the signal server after establishing a connection to the host.

When the client switches to a failed ICE state, they'd need to reconnect to the signal server and send the initial ICE restart offer. I'd prefer to see a flexible approach for whether initiator or non-initiator starts an ICE restart.


Some changes I'd like to propose:

With these changes, it would be up to the simple-peer consumer to decide whether to handle ICE restarts and if it should start with the initiator or non-initiator.

t-mullen commented 3 years ago

The non-initiator isn't actually capable of initiating renegotiation or ICE restart, it's only capable of requesting it from the initiator. At least with the way we currently handle glare. Maybe if we add rollbacks this can be a little simpler.

If you need to reconnect both peers to the signalling server, you should queue signals until that happens instead of delaying the ICE restart - otherwise you'll slow down gathering (and reconnection).

I think the best option is where only the initiator watches for iceConnectionState === 'failed' and begins a renegotiation/iceRestart.

soup-in-boots commented 3 years ago

Where does this feature stand? Do you need someone to work on it? I have a very strong interest in the feature, and would be able to commit working hours to it.

feross commented 3 years ago

@fauxsoup Are you still interested in committing working hours to this? I'd love to get this feature in.

soup-in-boots commented 3 years ago

Absolutely. Just want to know where to start. I saw a pull request with an example implementation, but it was out of date. Should I start there, or has other development commenced on this elsewhere?

feross commented 3 years ago

@fauxsoup Feel free to start with the example implementation that you found. We'll take a look at your PR.

@t-mullen @nazar-pc – do you have any other pointers for @fauxsoup?

draeder commented 2 years ago

I am facing this very issue among multiple libraries (torrent-discovery, bittorrent-tracker/client, @geut/discovery-swarm-webrtc) on ^9.11.0. If there is a workaround, I would love to know about it. This greatly affects the reliability of simple-peer to the point that I am going to abandon using it altogether, which will be unfortunate.

4www commented 2 years ago

Hello! Same here as @draeder ; and trying to figure out what's the expected (webrtc) way to restart a connection after the browser error logs WebRTC: ICE failed, add a TURN server and see about:webrtc for more details.

From my investigation it seems that there might be browser specific behaviors concerning the ICE restart, and the iceConnecitonState.

chromium as initiator seems to be the only way to make a peer connection that can "await and restart".

chromium seems to be able to have a RTCPeerConnection stay in iceConnectionState === "checking", after an offer was sent by the initiator, added as remote descriptor by the non-initiator, and and answer has been generated (but not signaled).

This iceConnectionState event pops on both peers, as soon as the offer is added on the non-initiator (chromium only).

This behavior is not the same on firefox; where iceConnectionState goes to "failed", on the non-initiator, a few seconds after the offer has been added as remote descriptor, and answer to local descriptor (but not yet signaled).

chromium is able to stay in the checking state, without failing, for a long duration of time (does not seem to ever fail); this allows "snail signaling", like the copy-paste-dance in my case.

Not sure if this can help, be maybe an insight in debugging.

Trying myself to make web-components for webrtc https://gitlab.com/sctlib/rtc, used for these tests