webrtcmobile-safarirtcpeerconnection

WebRTC with perfect negotiation - Rollback on mobile Safari does not work


I am trying to implement perfect WebRTC negotiation for my small video conferencing application by considering the examples from the following page:

https://blog.mozilla.org/webrtc/perfect-negotiation-in-webrtc/

Unortunately I did not manage to make it fully work, especially mobile safari seems to handle rollback behavior its own way, here is the code that handles the rollback behavior:

      if (description) {
        const offerCollision = description.type == 'offer' && (makingOffer || pc.signalingState != 'stable');
        this.ignoreOffer = !this.polite && offerCollision;
        if (this.ignoreOffer) {
          return;
        }
        if (offerCollision) {
          await Promise.all([pc.setRemoteDescription({ type: 'rollback' }), pc.setRemoteDescription(description)]);

So when on mobile safari an offer collision (offerCollision === true) is detected and pc.setRemoteDescription({ type: 'rollback' }) is called as it's implemented in my code, it throws an error of type InvalidStateError. Taking a closer look at the documentation about this type of error in MDN (https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setRemoteDescription#Browser_compatibility) shows:

"The RTCPeerConnection is closed, or it's in a state which isn't compatible with the specified description's type. For example, if the type is rollback and the signaling state is one of stable, have-local-pranswer, or have-remote-pranswer, this exception is thrown, because you can't roll back a connection that's either fully established or is in the final stage of becoming connected."

Checking the peer connections signaling state just before rolling back shows that it is in the state "have-local-offer" which should be ok since MDN says that rollback is not possible (throws InvalidStateError) in the states stable, have-local-pranswer, or have-remote-pranswer.

For the other case when my Desktop Chrome browser runs in an offer collision everything just works as expected with the same signaling state just before rollback is initiated.

Does someone here have an idea what's potentially wrong or to be handled differrently for mobile Safari.


Solution

  • As mentioned in the comments, Safari (both iOS/mobile and macOS) has a known bug with { type: 'rollback' }. It also does not yet support optional descriptions in setLocalDescription/setRemoteDescription, the latest perfect negotiation recommendation in the spec.

    This can be fixed by discarding the colliding peer connections and retrying (as suggested). To do this, handle the error when setting a remote description with the following steps:

    1. Reset any state variables (e.g. makingOffer, isSettingRemoteAnswerPending)
    2. Close the peer connection by calling peerConnection.close()
    3. Tear down any peer connection event listeners (e.g. negotiationneeded, icecandidate, track)
    4. Signal to the other peer to do the same e.g. via websocket messaging
    5. On receiving the signal to restart, the other can go through the same steps, and finally create a new offer to kick off the negotiation process again.

    An example of this flow can be seen in this demo.