Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/handlers/sdp/MediaSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
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).

// 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') {
Expand Down
18 changes: 0 additions & 18 deletions src/handlers/sdp/RemoteSdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down