From 8e9f1b1327869ead31d22bea693110a5c6dd46d8 Mon Sep 17 00:00:00 2001 From: snnz Date: Mon, 28 Dec 2020 02:01:57 +0300 Subject: [PATCH 1/8] Reuse closed m= sections in Firefox --- src/handlers/Firefox60.ts | 13 +++------- src/handlers/sdp/RemoteSdp.ts | 46 ++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/handlers/Firefox60.ts b/src/handlers/Firefox60.ts index 14595094..12f35da9 100644 --- a/src/handlers/Firefox60.ts +++ b/src/handlers/Firefox60.ts @@ -333,11 +333,6 @@ export class Firefox60 extends HandlerInterface sendingRemoteRtpParameters.codecs = ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); - // NOTE: Firefox fails sometimes to properly anticipate the closed media - // section that it should use, so don't reuse closed media sections. - // https://github.com/versatica/mediasoup-client/issues/104 - // - // const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver( track, { direction: 'sendonly', streams: [ this._sendStream ] }); @@ -373,7 +368,7 @@ export class Firefox60 extends HandlerInterface localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - const offerMediaObject = localSdpObject.media[localSdpObject.media.length - 1]; + const offerMediaObject = localSdpObject.media[localSdpObject.media.findIndex(s => s.mid == localId)]; // Set RTCP CNAME. sendingRtpParameters.rtcp.cname = @@ -421,6 +416,7 @@ export class Firefox60 extends HandlerInterface this._remoteSdp!.send( { offerMediaObject, + localSdpMedia : localSdpObject.media, offerRtpParameters : sendingRtpParameters, answerRtpParameters : sendingRemoteRtpParameters, codecOptions, @@ -456,10 +452,7 @@ export class Firefox60 extends HandlerInterface transceiver.sender.replaceTrack(null); this._pc.removeTrack(transceiver.sender); - // NOTE: Cannot use closeMediaSection() due to the the note above in send() - // method. - // this._remoteSdp!.closeMediaSection(transceiver.mid); - this._remoteSdp!.disableMediaSection(transceiver.mid!); + this._remoteSdp!.closeMediaSection(transceiver.mid!); const offer = await this._pc.createOffer(); diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index d6366848..896ba647 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -160,6 +160,7 @@ export class RemoteSdp { offerMediaObject, reuseMid, + localSdpMedia, offerRtpParameters, answerRtpParameters, codecOptions, @@ -168,6 +169,7 @@ export class RemoteSdp { offerMediaObject: any; reuseMid?: string; + localSdpMedia?: any; offerRtpParameters: RtpParameters; answerRtpParameters: RtpParameters; codecOptions?: ProducerCodecOptions; @@ -197,7 +199,10 @@ export class RemoteSdp // Unified-Plan or Plan-B with different media kind. else if (!this._midToIndex.has(mediaSection.mid)) { - this._addMediaSection(mediaSection); + if (localSdpMedia) + this._syncMediaWithLocalSdp(localSdpMedia, mediaSection); + else + this._addMediaSection(mediaSection); } // Plan-B with same media kind. else @@ -442,6 +447,45 @@ export class RemoteSdp } } + _syncMediaWithLocalSdp(localSdpMedia: any, newMediaSection: MediaSection): void + { + if (!this._firstMid) + this._firstMid = newMediaSection.mid; + + // Append new section to the existing vector and the SDP object + let idx = this._mediaSections.length; + this._mediaSections.push(newMediaSection); + this._sdpObject.media.push(newMediaSection.getObject()); + + // Add it to the map + this._midToIndex.set(newMediaSection.mid, idx); + + // Copy data to the temporary collections + const mediaSections = this._mediaSections.slice(), media = this._sdpObject.media.slice(); + // Clean up existing collections + this._mediaSections.length = this._sdpObject.media.length = 0; + + // Refill media sections vector and SDP object media + // using the order of sections in the local SDP offer + for (const mediaSection of localSdpMedia) + { + const i = this._midToIndex.get(String(mediaSection.mid)); + if (i !== undefined) + { + this._mediaSections.push(mediaSections[i]); + this._sdpObject.media.push(media[i]); + } + } + + // Recreate map + this._midToIndex.clear(); + for (idx = 0; idx < this._mediaSections.length; ++idx) + this._midToIndex.set(this._mediaSections[idx].mid, idx); + + // Regenerate BUNDLE mids. + this._regenerateBundleMids(); + } + _regenerateBundleMids(): void { if (!this._dtlsParameters) From 8ddd007099b4c866c67b509b2b16f86dbfa2435a Mon Sep 17 00:00:00 2001 From: Sergey Nozhenko Date: Fri, 18 Feb 2022 13:49:15 +0300 Subject: [PATCH 2/8] Cosmetic changes addressing PR comments --- src/handlers/sdp/RemoteSdp.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 896ba647..a1e663dc 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -452,21 +452,21 @@ export class RemoteSdp if (!this._firstMid) this._firstMid = newMediaSection.mid; - // Append new section to the existing vector and the SDP object + // Append new section to the existing vector and the SDP object. let idx = this._mediaSections.length; this._mediaSections.push(newMediaSection); this._sdpObject.media.push(newMediaSection.getObject()); - // Add it to the map + // Add it to the map. this._midToIndex.set(newMediaSection.mid, idx); - // Copy data to the temporary collections + // Copy data to the temporary collections. const mediaSections = this._mediaSections.slice(), media = this._sdpObject.media.slice(); - // Clean up existing collections - this._mediaSections.length = this._sdpObject.media.length = 0; + // Clean up existing collections. + this._mediaSections.length = this._sdpObject.media.length = 0; // Refill media sections vector and SDP object media - // using the order of sections in the local SDP offer + // using the order of sections in the local SDP offer. for (const mediaSection of localSdpMedia) { const i = this._midToIndex.get(String(mediaSection.mid)); @@ -477,10 +477,12 @@ export class RemoteSdp } } - // Recreate map + // Recreate map. this._midToIndex.clear(); for (idx = 0; idx < this._mediaSections.length; ++idx) + { this._midToIndex.set(this._mediaSections[idx].mid, idx); + } // Regenerate BUNDLE mids. this._regenerateBundleMids(); From bbf4ceacac41f822028a6a642c7b74184a053c37 Mon Sep 17 00:00:00 2001 From: Sergey Nozhenko Date: Sat, 18 Feb 2023 00:17:37 +0300 Subject: [PATCH 3/8] lint errors fixed --- src/handlers/Firefox60.ts | 3 ++- src/handlers/sdp/RemoteSdp.ts | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/handlers/Firefox60.ts b/src/handlers/Firefox60.ts index e175e3d1..8b43aa63 100644 --- a/src/handlers/Firefox60.ts +++ b/src/handlers/Firefox60.ts @@ -386,7 +386,8 @@ export class Firefox60 extends HandlerInterface localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - const offerMediaObject = localSdpObject.media[localSdpObject.media.findIndex(s => s.mid == localId)]; + const offerMediaObject = + localSdpObject.media[localSdpObject.media.findIndex((s) => s.mid == localId)]; // Set RTCP CNAME. sendingRtpParameters.rtcp.cname = diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 6b05cab6..80afe4e8 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -475,6 +475,7 @@ export class RemoteSdp // Append new section to the existing vector and the SDP object. let idx = this._mediaSections.length; + this._mediaSections.push(newMediaSection); this._sdpObject.media.push(newMediaSection.getObject()); @@ -482,7 +483,9 @@ export class RemoteSdp this._midToIndex.set(newMediaSection.mid, idx); // Copy data to the temporary collections. - const mediaSections = this._mediaSections.slice(), media = this._sdpObject.media.slice(); + const mediaSections = this._mediaSections.slice(); + const media = this._sdpObject.media.slice(); + // Clean up existing collections. this._mediaSections.length = this._sdpObject.media.length = 0; @@ -491,6 +494,7 @@ export class RemoteSdp for (const mediaSection of localSdpMedia) { const i = this._midToIndex.get(String(mediaSection.mid)); + if (i !== undefined) { this._mediaSections.push(mediaSections[i]); From 87f82b2db29cb4f7527fa09618cc3e21425a119f Mon Sep 17 00:00:00 2001 From: Sergey Nozhenko Date: Tue, 21 Mar 2023 00:34:14 +0300 Subject: [PATCH 4/8] lint errors fixed --- src/handlers/sdp/RemoteSdp.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 3e9fb61b..b2f0e446 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -202,9 +202,13 @@ export class RemoteSdp else if (!this._midToIndex.has(mediaSection.mid)) { if (localSdpMedia) + { this._syncMediaWithLocalSdp(localSdpMedia, mediaSection); + } else + { this._addMediaSection(mediaSection); + } } // Plan-B with same media kind. else @@ -477,7 +481,9 @@ export class RemoteSdp _syncMediaWithLocalSdp(localSdpMedia: any, newMediaSection: MediaSection): void { if (!this._firstMid) + { this._firstMid = newMediaSection.mid; + } // Append new section to the existing vector and the SDP object. let idx = this._mediaSections.length; From 64171e2df5671cba4ff91253812be00d6e4a1a74 Mon Sep 17 00:00:00 2001 From: Sergey Nozhenko Date: Sun, 12 Oct 2025 02:54:36 +0300 Subject: [PATCH 5/8] Accidentally deleted removeTrack restored. --- src/handlers/Firefox120.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index 2ac66641..417b214e 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -514,6 +514,8 @@ export class Firefox120 void transceiver.sender.replaceTrack(null); + this._pc.removeTrack(transceiver.sender); + const mediaSectionClosed = this._remoteSdp.closeMediaSection( transceiver.mid! ); From cd7da415d75156de00a0dea38fa8472cf4828583 Mon Sep 17 00:00:00 2001 From: Sergey Nozhenko Date: Sun, 12 Oct 2025 05:09:39 +0300 Subject: [PATCH 6/8] Typescript errors fixed. --- src/handlers/Firefox120.ts | 2 +- src/handlers/sdp/RemoteSdp.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index 417b214e..f21d3e63 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -422,7 +422,7 @@ export class Firefox120 localSdpObject = sdpTransform.parse(this._pc.localDescription!.sdp); const offerMediaObject = - localSdpObject.media[localSdpObject.media.findIndex((s) => s.mid == localId)]; + localSdpObject.media[localSdpObject.media.findIndex((s) => s.mid == localId)]!; // Set RTCP CNAME. sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 4b58b1de..674253a5 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -438,15 +438,15 @@ export class RemoteSdp { const i = this._midToIndex.get(String(mediaSection.mid)); if (i !== undefined) { - this._mediaSections.push(mediaSections[i]); - this._sdpObject.media.push(media[i]); + this._mediaSections.push(mediaSections[i]!); + this._sdpObject.media.push(media[i]!); } } // Recreate map. this._midToIndex.clear(); for (idx = 0; idx < this._mediaSections.length; ++idx) { - this._midToIndex.set(this._mediaSections[idx].mid, idx); + this._midToIndex.set(this._mediaSections[idx]!.mid, idx); } // Regenerate BUNDLE mids. From 6ac825cdea7a5d0ad2f8387ee2f3141001762510 Mon Sep 17 00:00:00 2001 From: snnz Date: Fri, 24 Apr 2026 13:59:32 +0300 Subject: [PATCH 7/8] Mid from the old closed media section is used as reuseMid instead of recreating the internal sections array. --- src/handlers/Firefox120.ts | 8 +++-- src/handlers/sdp/RemoteSdp.ts | 56 ++++------------------------------- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index 0f9db5b6..1fcaf72a 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -432,8 +432,10 @@ export class Firefox120 localSdpObject = sdpTransform.parse(this._pc.localDescription!.sdp); - const offerMediaObject = - localSdpObject.media[localSdpObject.media.findIndex((s) => s.mid == localId)]!; + const idx = localSdpObject.media.findIndex((s) => s.mid == localId); + const oldMediaSection = this._remoteSdp.getMediaSection(idx); + + const offerMediaObject = localSdpObject.media[idx]!; // Set RTCP CNAME. sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ @@ -485,7 +487,7 @@ export class Firefox120 this._remoteSdp.send({ offerMediaObject, - localSdpMedia : localSdpObject.media, + reuseMid: oldMediaSection && oldMediaSection.closed ? oldMediaSection.mid : undefined, offerRtpParameters: sendingRtpParameters, answerRtpParameters: sendingRemoteRtpParameters, codecOptions, diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index c51df965..1877e2d6 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -199,12 +199,7 @@ export class RemoteSdp { } // Unified-Plan or Plan-B with different media kind. else if (!this._midToIndex.has(mediaSection.mid)) { - if (localSdpMedia) { - this.syncMediaWithLocalSdp(localSdpMedia, mediaSection); - } - else { - this.addMediaSection(mediaSection); - } + this.addMediaSection(mediaSection); } // Plan-B with same media kind. else { @@ -360,6 +355,10 @@ export class RemoteSdp { return sdpTransform.write(this._sdpObject); } + getMediaSection(idx: number): MediaSection | undefined { + return idx < this._mediaSections.length ? this._mediaSections[idx] : undefined; + } + private addMediaSection(newMediaSection: MediaSection): void { if (!this._firstMid) { this._firstMid = newMediaSection.mid; @@ -421,51 +420,6 @@ export class RemoteSdp { } } - private syncMediaWithLocalSdp( - localSdpMedia: SdpTransform.MediaDescription[], - newMediaSection: MediaSection - ): void { - if (!this._firstMid) { - this._firstMid = newMediaSection.mid; - } - - // Append new section to the existing vector and the SDP object. - let idx = this._mediaSections.length; - - this._mediaSections.push(newMediaSection); - this._sdpObject.media.push(newMediaSection.getObject()); - - // Add it to the map. - this._midToIndex.set(newMediaSection.mid, idx); - - // Copy data to the temporary collections. - const mediaSections = this._mediaSections.slice(); - const media = this._sdpObject.media.slice(); - - // Clean up existing collections. - this._mediaSections.length = this._sdpObject.media.length = 0; - - // Refill media sections vector and SDP object media - // using the order of sections in the local SDP offer. - for (const mediaSection of localSdpMedia) { - const i = this._midToIndex.get(String(mediaSection.mid)); - - if (i !== undefined) { - this._mediaSections.push(mediaSections[i]!); - this._sdpObject.media.push(media[i]!); - } - } - - // Recreate map. - this._midToIndex.clear(); - for (idx = 0; idx < this._mediaSections.length; ++idx) { - this._midToIndex.set(this._mediaSections[idx]!.mid, idx); - } - - // Regenerate BUNDLE mids. - this.regenerateBundleMids(); - } - private findMediaSection(mid: string): MediaSection { const idx = this._midToIndex.get(mid); From 9f01bf4e809d2fc643aa51c563c54cf70b893543 Mon Sep 17 00:00:00 2001 From: snnz Date: Fri, 24 Apr 2026 14:01:56 +0300 Subject: [PATCH 8/8] Forgot to remove extra parameter in the RemoteSdp.send --- src/handlers/sdp/RemoteSdp.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 1877e2d6..7d75e4aa 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -154,14 +154,12 @@ export class RemoteSdp { send({ offerMediaObject, reuseMid, - localSdpMedia, offerRtpParameters, answerRtpParameters, codecOptions, }: { offerMediaObject: SdpTransform.MediaDescription; reuseMid?: string; - localSdpMedia?: SdpTransform.MediaDescription[]; offerRtpParameters: RtpParameters; answerRtpParameters: RtpParameters; codecOptions?: ProducerCodecOptions;