In WebRTC, when a peer adds a screen-share track to a running call, what changes in the next SDP offer and what must stay fixed?
answer
- an event asks for a new exchange
- old sections keep their place
- a new mid joins the group
- bundled means no new transport
- the count never shrinks
basics
~20 sAdding the track fires negotiationneeded. The next offer keeps each existing m= section at its index and mid, adds a video section (appended or in a recycled port-0 slot), and puts its mid in the BUNDLE group, sharing the existing transport.
solid answer
~50 s`addTrack` on a call in the `stable` state sets the negotiation-needed flag and fires `negotiationneeded`, so the application runs another offer/answer exchange. The new offer follows RFC 9429's rules for subsequent offers: the `o=` line's session version increments; every existing `m=` section stays at its index with the same `a=mid`; and the screen share gets a new `m=video` section with a fresh MID, appended at the end or placed in a slot whose port is 0 after an earlier rejection. Because bundling was already negotiated, `a=group:BUNDLE` simply gains that MID: the new section carries no ICE credentials or candidates of its own and shares the existing transport, so no new gathering or DTLS handshake is needed. ICE credentials stay unchanged unless an ICE restart is requested. The answer again mirrors the section count, and the answerer may accept the new section or reject it with port 0.
go deeper
Recall that adding a track mid-call needs a fresh offer and answer, triggered by the negotiationneeded event, over the same signalling channel.
Explain the subsequent-offer rules: fixed indexes and MIDs, a new or recycled section, BUNDLE membership, an incremented session version and unchanged ICE credentials.
Show you can read a renegotiation offer and spot a broken one, such as a reordered section, a missing BUNDLE MID or an unintended ICE restart, and explain stop versus inactive.
Weigh how often a product should renegotiate, against reserving transceivers up front, given signalling round trips, glare exposure and interop with endpoints that bundle poorly.
## The trigger: negotiation needed A WebRTC call's media layout lives in its last completed **offer/answer** exchange. Any change to that layout, such as a new track, a stopped track or the first data channel, needs another exchange. The W3C WebRTC 1.0 API tracks this with a **negotiation-needed flag**: when an operation that requires signalling is performed, the flag is set and a `negotiationneeded` event fires. The event is queued so that several changes made together produce one event, and it fires only when the signalling state is `stable` and no other operation is in progress. Adding a screen share is the textbook case. `addTrack` either reuses an existing transceiver of the same kind that has never sent media, or, as here, creates a new **transceiver** with direction `sendrecv`. A new transceiver needs a new `m=` section, so negotiation is needed. ## What an m= section is An SDP description holds one **media section** (`m=` section) per transceiver, plus one `m=application` section for data channels. Each section is identified by its `a=mid` value (the media identification) and by its **index**, its position in the description. RFC 3264 matches sections between successive descriptions by index, so: - the number of `m=` sections **never decreases** from one offer to the next; - a section that is no longer used is **not deleted** but kept with port 0; - a new stream is added **at the end**, or by reusing (recycling) a port-0 slot. ## The subsequent offer, line by line RFC 9429 has a dedicated procedure for offers made after a session exists. For the screen-share case: | Part of the offer | First call (audio and camera) | After adding the screen share | |---|---|---| | `o=` session version | N | N+1 | | `m=` sections | audio (mid 0), video (mid 1) | audio (0), video (1), video (2) | | `a=mid` of existing sections | 0, 1 | unchanged | | `a=group:BUNDLE` | 0 1 | 0 1 2 | | ICE `ufrag` / `pwd` | as negotiated | unchanged unless an ICE restart is requested | | `a=msid` of existing sections | as negotiated | unchanged | A sketch of the media part of that offer (ports and most attributes elided): ``` a=group:BUNDLE 0 1 2 m=audio <port> UDP/TLS/RTP/SAVPF 111 a=mid:0 m=video <port> UDP/TLS/RTP/SAVPF 96 a=mid:1 m=video <port> UDP/TLS/RTP/SAVPF 96 a=mid:2 a=sendrecv a=msid:<stream-id> <track-id> ``` Applying it gives the answerer a transceiver for the new section and fires a `track` event that surfaces the screen share as a new remote track. Its answer contains three sections, in the same order. If it does not want the stream it rejects section 2 with port 0. If it has nothing to send back, it answers that section `recvonly`. ## Why the new section costs no new transport: BUNDLE **BUNDLE** (RFC 8843, since revised as RFC 9143) lets several `m=` sections share one transport, meaning one ICE-selected 5-tuple and one DTLS association. JSEP always offers a single BUNDLE group across all sections. Once the first exchange has negotiated bundling: - the new section's MID is simply **added to** `a=group:BUNDLE`; - a section bundled into another carries **no** `a=ice-ufrag`, `a=ice-pwd` or `a=candidate` lines of its own; - JSEP gathers candidates only for new sections that are *not* bundled, so here no new gathering phase starts; - RFC 9429 notes that once bundling is negotiated, unbundling is no longer possible. The bundle policy matters mainly for the **initial** offer, where it decides how much to hedge against a peer that does not support BUNDLE. RFC 9429 makes `balanced` the default (one transport-carrying section per media type), defines `max-compat`, and introduces an identically defined `must-bundle` policy while deprecating RFC 8829's `max-bundle`, because implementations had followed a pre-standard reading of it. The W3C API's `bundlePolicy` enum still lists `balanced`, `max-compat` and `max-bundle`. ## Removing the screen share later Stopping the transceiver does not delete its section. RFC 9429 says the next offer generates that `m=` section with **port 0** and its `a=msid` lines removed. A later new transceiver may then **recycle** the slot with a new MID, and the section count still never drops. Changing direction instead (say `a=inactive`) keeps the section alive and its port open. That is the right tool for a pause, not for a removal. ## Common misreadings 1. "The new offer contains only the changed section." Every offer is a complete description of the session. 2. "Sections can be reordered so the new one comes first." Indexes are how the two sides match sections. 3. "Each new track needs its own ICE and DTLS setup." Not once bundling has been negotiated. 4. "Renegotiation means an ICE restart." A restart is a separate request that changes the ICE credentials.
- Why does a WebRTC subsequent offer never delete the m= section of a stopped transceiver?Because RFC 3264 matches sections between successive descriptions by index, so the count of `m=` sections may never fall. RFC 9429 has the next offer keep the stopped transceiver's section with port 0 and its `a=msid` lines removed; a later new transceiver can recycle that slot with a fresh MID.
- What does a WebRTC application send when negotiationneeded fires mid-call?A complete new offer, produced by `createOffer` or an argument-free `setLocalDescription` and delivered over the same signalling channel as the first one. The remote peer applies it, answers, and the offerer applies the answer, exactly as at call setup; the event does not fire again until the state is `stable`.
saying these in an interview costs you the question
- A renegotiation offer contains only the newly added m= section.
- The new section may be inserted first to mark its priority.
- Each new track needs its own ICE gathering and DTLS handshake.
- Stopping a track deletes its m= section from later offers.
- Adding a track always forces an ICE restart.