4.7 Session Negotiation Model

Many changes to state of an RTCPeerConnection will require communication with the remote side via the signaling channel, in order to have the desired effect. The app can be kept informed as to when it needs to do signaling, by listening to the negotiationneeded event. This event is fired according to the state of the connection’s negotiation-needed flag, represented by a [[NegotiationNeeded]] internal slot.

4.7.1 Setting Negotiation-Needed

This section is non-normative.

If an operation is performed on an RTCPeerConnection that requires signaling, the connection will be marked as needing negotiation. Examples of such operations include adding or stopping an RTCRtpTransceiver, or adding the first RTCDataChannel.

Internal changes within the implementation can also result in the connection being marked as needing negotiation.

Note that the exact procedures for updating the negotiation-needed flag are specified below.

4.7.2 Clearing Negotiation-Needed

This section is non-normative.

The negotiation-needed flag is cleared when a session description of type “answeris set successfully, and the supplied description matches the state of the RTCRtpTransceivers and RTCDataChannels that currently exist on the RTCPeerConnection. Specifically, this means that all non-stopped transceivers have an associated section in the local description with matching properties, and, if any data channels have been created, a data section exists in the local description.

Note that the exact procedures for updating the negotiation-needed flag are specified below.

4.7.3 Updating the Negotiation-Needed flag

The process below occurs where referenced elsewhere in this document. It also may occur as a result of internal changes within the implementation that affect negotiation. If such changes occur, the user agent MUST update the negotiation-needed flag.

To update the negotiation-needed flag for connection, run the following steps:

  1. If the length of connection.[[Operations]] is not 0, then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps.

  2. Queue a task to run the following steps:

    1. If connection.[[IsClosed]] is true, abort these steps.

    2. If the length of connection.[[Operations]] is not 0, then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps.

    3. If connection’s signaling state is not “stable“, abort these steps.

      Note

      The negotiation-needed flag will be updated once the state transitions to “stable“, as part of the steps for setting a session description.

    4. If the result of checking if negotiation is needed is false, clear the negotiation-needed flag by setting connection.[[NegotiationNeeded]] to false, and abort these steps.

    5. If connection.[[NegotiationNeeded]] is already true, abort these steps.

    6. Set connection.[[NegotiationNeeded]] to true.

    7. Fire an event named negotiationneeded at connection.

    Note

    The task queueing prevents negotiationneeded from firing prematurely, in the common situation where multiple modifications to connection are being made at once.

    Additionally, we avoid racing with negotiation methods by only firing negotiationneeded when the operations chain is empty.

To check if negotiation is needed for connection, perform the following checks:

  1. If any implementation-specific negotiation is required, as described at the start of this section, return true.

  2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return true.

  3. Let description be connection.[[CurrentLocalDescription]].

  4. If connection has created any RTCDataChannels, and no m= section in description has been negotiated yet for data, return true.

  5. For each transceiver in connection’s set of transceivers, perform the following checks:

    1. If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is false, return true.

    2. If transceiver isn’t stopped and isn’t yet associated with an m= section in description, return true.

    3. If transceiver isn’t stopped and is associated with an m= section in description then perform the following checks:

      1. If transceiver.[[Direction]] is “sendrecv“ or “sendonly“, and the associated m= section in description either doesn’t contain a single a=msid line, or the number of MSIDs from the a=msid lines in this m= section, or the MSID values themselves, differ from what is in transceiver.sender.[[AssociatedMediaStreamIds]], return true.

      2. If description is of type “offer“, and the direction of the associated m= section in neither connection.[[CurrentLocalDescription]] nor connection.[[CurrentRemoteDescription]] matches transceiver.[[Direction]], return true. In this step, when the direction is compared with a direction found in [[CurrentRemoteDescription]], the description’s direction must be reversed to represent the peer’s point of view.

      3. If description is of type “answer“, and the direction of the associated m= section in the description does not match transceiver.[[Direction]] intersected with the offered direction (as described in [RFC8829] (section 5.3.1.)), return true.

    4. If transceiver is stopped and is associated with an m= section, but the associated m= section is not yet rejected in connection.[[CurrentLocalDescription]] or connection.[[CurrentRemoteDescription]], return true.

  6. If all the preceding checks were performed and true was not returned, nothing remains to be negotiated; return false.