Skip to content

fix: VP8/H264 simulcast producer fails on mic change in Chrome 148+ (…#367

Closed
panov-va wants to merge 1 commit into
versatica:v3from
panov-va:fix/chrome148-extmap-id-reassignment
Closed

fix: VP8/H264 simulcast producer fails on mic change in Chrome 148+ (…#367
panov-va wants to merge 1 commit into
versatica:v3from
panov-va:fix/chrome148-extmap-id-reassignment

Conversation

@panov-va
Copy link
Copy Markdown

Background

Chrome 148 ships libwebrtc commit 5788235 ("Consolidate RTP header extension validation in BaseChannel"), which enforces global uniqueness of extmap IDs for the lifetime of an RTCPeerConnection. Once an ID is bound to a URI in any bundled m-section, it's recorded in historical_rtp_header_extensions_ and cannot be reused for a different URI — not in later offer/answer exchanges, not in new m-sections. The violation surfaces as:

RTP extension ID reassignment not supported (<old_uri> → <new_uri>)

…thrown from setLocalDescription.

Root cause

AnswerMediaSection builds the synthetic SDP answer from answerRtpParameters.headerExtensions, i.e. the intersection of Chrome's offer and the server's routerRtpCapabilities. Anything Chrome offered that the server doesn't support (video-layers-allocation00 for simulcast, dependency-descriptor for VP8/VP9) is dropped from the answer.

That used to be fine. Under the new rules, those dropped extensions still get registered in Chrome's internal ID history when the offer is set as local description. When a later m-section needs to allocate IDs for audio extensions like ssrc-audio-level, Chrome may pick an ID previously occupied by one of the dropped video extensions, and the strict check kills the renegotiation.

For VP8/VP9 the problem was worse: RemoteSdp.send() explicitly stripped dependency-descriptor from the answer after AnswerMediaSection had already built it. Correct before, actively harmful now.

Fix

  • src/handlers/sdp/MediaSection.ts — after AnswerMediaSection emits the confirmed (server-supported) extensions, a second pass writes back every remaining offer extension with no direction attribute (sendrecv per RFC 5285). Chrome registers the ID against its original URI and won't hand it out to anything else.
  • src/handlers/sdp/RemoteSdp.ts — removed the post-processing step that stripped dependency-descriptor for non-AV1/H264 codecs. It ran after AnswerMediaSection and silently undid the pinning on VP8/VP9.

Why this is safe

  • AV1 / H264dependency-descriptor is in routerRtpCapabilities for these codecs, so it already shows up in answerRtpParameters.headerExtensions and the first loop emits it as active. The deleted RemoteSdp filter was a no-op here anyway: it only triggered when ddCodec was missing (i.e. VP8/VP9).
  • VP8 / VP9dependency-descriptor now appears in the answer as a pin-only entry. The server doesn't activate it (not in routerRtpCapabilities for these codecs) and ignores it. Chrome won't put the extension on VP8/VP9 packets either, since there's no SVC layer structure to describe. Net effect: the extmap ID is locked to the URI and nothing else changes.
  • Other browsersAnswerMediaSection is shared with Chrome111, Chrome74, Firefox120, Safari12, ReactNative106. Either they confirmed the extension (second loop skips it) or they dropped it themselves, in which case a pin entry in the synthetic answer is harmless.

Reproduction (mediasoup-demo)

The stock demo UI doesn't let you pick devices individually, so:

  1. Split webcam/mic into separate "Enable Webcam" and "Enable Mic" buttons backed by enableWebcam() / enableMic().
  2. Add a "Change Mic" button that does disableMic() then enableMic() with a different deviceId.
  3. Open the demo in Chrome 148+ with VP8 as the preferred video codec.
  4. Click Enable WebcamEnable MicChange Mic.

Without the patch, setLocalDescription throws RTP extension ID reassignment not supported (dependency-descriptor → ssrc-audio-level). With it, the sequence goes through cleanly on both VP8 and H264.

@panov-va
Copy link
Copy Markdown
Author

Hi @ibc! Could you take a look at this PR, please? The new Chrome 148 behaviour breaks every mediasoup app I know of that has a similar flow, and the 148 beta is about to start rolling out to users.

// URI is rejected with "RTP extension ID reassignment not supported".
//
// Previously, extensions unsupported by the server were simply omitted
// from the synthetic answer. Chrome then considered their IDs "free"
Copy link
Copy Markdown

@pnts-se-whereby pnts-se-whereby Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is true, is it really the correct libwebrtc behaviour? Does it make sense to record the ID as assigned and at the same time consider it as free if the remote did not support it (i.e. excluded it from the answer).

@ibc
Copy link
Copy Markdown
Member

ibc commented Apr 17, 2026

As per discussion and research in the issue ticket #368, let's mark this PR as draft since it's almost guaranteed that this is a bug/regression in libwebrtc.

See https://issues.webrtc.org/issues/503013383.

@ibc ibc marked this pull request as draft April 17, 2026 08:55
@ibc
Copy link
Copy Markdown
Member

ibc commented Apr 21, 2026

Closing because thhe issue has been fixed in Chrome:

@ibc ibc closed this Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

VP8/H264 simulcast producer causes "RTP extension ID reassignment not supported" in Chrome 148+

3 participants