diff --git a/src/handlers/sdp/MediaSection.ts b/src/handlers/sdp/MediaSection.ts index c619f5de..a0a57616 100644 --- a/src/handlers/sdp/MediaSection.ts +++ b/src/handlers/sdp/MediaSection.ts @@ -338,6 +338,31 @@ export class AnswerMediaSection extends MediaSection { }); } + // Starting with Chrome 148, libwebrtc records every extmap ID seen in + // a local offer into a per-PeerConnection history + // (historical_rtp_header_extensions_ in BaseChannel, introduced in + // https://webrtc.googlesource.com/src/+/5788235). Any subsequent + // setLocalDescription that tries to bind the same ID to a different + // 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" + // and could reuse them when negotiating new m-sections (e.g. adding + // audio after video simulcast), triggering the reassignment error. + // Including them back in the answer — even without server support — + // causes Chrome to permanently associate those IDs with their original + // URIs, preventing any future reuse. + const confirmedExtUris = new Set(this._mediaObject.ext.map(e => e.uri)); + + for (const offerExt of offerMediaObject.ext ?? []) { + if (!confirmedExtUris.has(offerExt.uri)) { + this._mediaObject.ext.push({ + uri: offerExt.uri, + value: offerExt.value, + }); + } + } + // Allow both 1 byte and 2 bytes length header extensions since // mediasoup can receive both at any time. if (offerMediaObject.extmapAllowMixed === 'extmap-allow-mixed') { diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 31b91544..2c09c85d 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -18,8 +18,6 @@ import type { MediaKind, RtpParameters } from '../../RtpParameters'; import type { SctpParameters } from '../../SctpParameters'; import { version as mediasoupClientVersion } from '../../'; -const DependencyDescriptorCodecs = ['av1', 'h264']; - const logger = new Logger('RemoteSdp'); export class RemoteSdp { @@ -175,22 +173,6 @@ export class RemoteSdp { codecOptions, }); - const mediaObject = mediaSection.getObject(); - - // Remove Dependency Descriptor extension unless there is support for - // the codec in mediasoup. - const ddCodec = mediaObject.rtp.find(rtp => - DependencyDescriptorCodecs.includes(rtp.codec.toLowerCase()) - ); - - if (!ddCodec) { - mediaObject.ext = mediaObject.ext?.filter( - extmap => - extmap.uri !== - 'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension' - ); - } - // Unified-Plan with closed media section replacement. if (reuseMid) { this.replaceMediaSection(mediaSection, reuseMid);