Skip to content

Fix extractRtpCapabilities() mixing up audio and video RTP header extensions - #397

Merged
ibc merged 1 commit into
v3from
fix_mixing_header_extensions
Oct 21, 2026
Merged

ibc merged 1 commit into
v3from
fix_mixing_header_extensions

Conversation

@jmillan

@jmillan jmillan commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

headerExtensionMap was indexed by preferred id alone. Since all m= sections are bundled, the browser gives the same id to a given extension in every m= section that supports it (sdes:mid, abs-send-time, transport-cc or abs-capture-time typically share their id between the audio and the video m= section), so audio and video fought for the same map slot and the last m= section in the SDP won. The other kind's extension simply disappeared from the extracted capabilities (matchHeaderExtensions() requires both kinds to be equal) and hence from the answer we generate.

Among other things, this made Chrome stop the mandatory sdes:mid extension in transceivers of the losing kind, eventually making transport.produce() fail with "Failed to execute 'setHeaderExtensionsToNegotiate' on RTCRtpTransceiver: Attempted to stop a mandatory extension".

Index headerExtensionMap by kind and preferred id so that each kind keeps its own entry. Both kinds now always get their full set of extensions, every answer carries all its extmaps (sdes:mid included), and the m= section order no longer has any effect. Added a regression test.

Fixes #394

…ensions

`headerExtensionMap` was indexed by preferred id alone. Since all m= sections are
bundled, the browser gives the same id to a given extension in every m= section
that supports it (sdes:mid, abs-send-time, transport-cc or abs-capture-time
typically share their id between the audio and the video m= section), so audio
and video fought for the same map slot and the last m= section in the SDP won.
The other kind's extension simply disappeared from the extracted capabilities
(`matchHeaderExtensions()` requires both kinds to be equal) and hence from the
answer we generate.

Among other things, this made Chrome stop the mandatory sdes:mid extension in
transceivers of the losing kind, eventually making `transport.produce()` fail
with "Failed to execute 'setHeaderExtensionsToNegotiate' on
'RTCRtpTransceiver': Attempted to stop a mandatory extension".

Index `headerExtensionMap` by kind and preferred id so that each kind keeps its
own entry. Both kinds now always get their full set of extensions, every answer
carries all its extmaps (sdes:mid included), and the m= section order no longer
has any effect. Added a regression test.
@jmillan
jmillan requested a review from ibc October 21, 2026 13:32
@ibc

ibc commented Oct 21, 2026

Copy link
Copy Markdown
Member

Assumptions in this PR are incorrect. By spec it's mandated that ALL extension RTP extension URIs in a bundled PeerConnection share the same ids across different transceivers (AKA m= sections in the SDP).

@jmillan

jmillan commented Oct 21, 2026

Copy link
Copy Markdown
Member Author

By spec it's mandated that ALL extension RTP extension URIs in a bundled PeerConnection share the same ids across different transceivers (AKA m= sections in the SDP).

Precisely the rationale of this PR.

@ibc

ibc commented Oct 21, 2026

Copy link
Copy Markdown
Member

Yeah, this is the fix.

@ibc
ibc merged commit f9007c5 into v3 Oct 21, 2026
8 checks passed
@ibc
ibc deleted the fix_mixing_header_extensions branch October 21, 2026 14:20
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.

Failed to execute 'setHeaderExtensionsToNegotiate' on 'RTCRtpTransceiver': Attempted to stop a mandatory extension

2 participants