From b33557e88b2e5ccc8c5b3d589fb5eaaa3bcdaf8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Sun, 26 Oct 2025 14:25:57 +0100 Subject: [PATCH 1/5] `RtpParamerters`: add optional `msid` field # Details - Specification: [RFC 8830](https://datatracker.ietf.org/doc/html/rfc8830) - WIP ## Bonus tracks - Improve some signatures and types. - `RemoteSdp`: Stop adding deprecated `a=msid-semantic` global attribute. It was dropped in favour of the new `a=msid` media session attribute as per [RFC 8830](https://datatracker.ietf.org/doc/html/rfc8830). --- src/Consumer.ts | 6 ++-- src/DataConsumer.ts | 4 +-- src/Producer.ts | 1 + src/RtpParameters.ts | 6 ++++ src/Transport.ts | 10 ++++--- src/handlers/Chrome111.ts | 29 +++++++++++++++++--- src/handlers/Chrome74.ts | 14 ++++++++-- src/handlers/FakeHandler.ts | 4 ++- src/handlers/Firefox120.ts | 14 ++++++++-- src/handlers/HandlerInterface.ts | 1 + src/handlers/ReactNative106.ts | 14 ++++++++-- src/handlers/Safari12.ts | 14 ++++++++-- src/handlers/ortc/utils.ts | 21 ++++++++++++++ src/handlers/sdp/MediaSection.ts | 3 +- src/handlers/sdp/RemoteSdp.ts | 47 ++++++++++++++++---------------- 15 files changed, 138 insertions(+), 50 deletions(-) diff --git a/src/Consumer.ts b/src/Consumer.ts index 2863bf69..837efdfd 100644 --- a/src/Consumer.ts +++ b/src/Consumer.ts @@ -7,9 +7,9 @@ import type { AppData } from './types'; const logger = new Logger('Consumer'); export type ConsumerOptions = { - id?: string; - producerId?: string; - kind?: 'audio' | 'video'; + id: string; + producerId: string; + kind: 'audio' | 'video'; rtpParameters: RtpParameters; streamId?: string; onRtpReceiver?: OnRtpReceiverCallback; diff --git a/src/DataConsumer.ts b/src/DataConsumer.ts index e015c663..62f9404c 100644 --- a/src/DataConsumer.ts +++ b/src/DataConsumer.ts @@ -7,8 +7,8 @@ const logger = new Logger('DataConsumer'); export type DataConsumerOptions = { - id?: string; - dataProducerId?: string; + id: string; + dataProducerId: string; sctpStreamParameters: SctpStreamParameters; label?: string; protocol?: string; diff --git a/src/Producer.ts b/src/Producer.ts index 725e9a7c..3cfe541d 100644 --- a/src/Producer.ts +++ b/src/Producer.ts @@ -13,6 +13,7 @@ const logger = new Logger('Producer'); export type ProducerOptions = { track?: MediaStreamTrack; + stream?: MediaStream; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; headerExtensionOptions?: ProducerHeaderExtensionOptions; diff --git a/src/RtpParameters.ts b/src/RtpParameters.ts index db16d087..555fe56e 100644 --- a/src/RtpParameters.ts +++ b/src/RtpParameters.ts @@ -164,6 +164,12 @@ export type RtpParameters = { * Parameters used for RTCP. */ rtcp?: RtcpParameters; + /** + * MSID (WebRTC MediaStream Identification). + * + * @see https://datatracker.ietf.org/doc/html/rfc8830 + */ + msid?: string; }; /** diff --git a/src/Transport.ts b/src/Transport.ts index 1b6a602b..3895e2c1 100644 --- a/src/Transport.ts +++ b/src/Transport.ts @@ -500,6 +500,7 @@ export class Transport< */ async produce({ track, + stream, encodings, codecOptions, headerExtensionOptions, @@ -587,6 +588,7 @@ export class Transport< const { localId, rtpParameters, rtpSender } = await this._handler.send({ track, + stream, encodings: normalizedEncodings, codecOptions, headerExtensionOptions, @@ -902,8 +904,8 @@ export class Transport< task.consumerOptions; optionsList.push({ - trackId: id!, - kind: kind!, + trackId: id, + kind: kind, rtpParameters, streamId, onRtpReceiver, @@ -920,9 +922,9 @@ export class Transport< task.consumerOptions; const { localId, rtpReceiver, track } = result; const consumer: Consumer = new Consumer({ - id: id!, + id, localId, - producerId: producerId!, + producerId, rtpReceiver, track, rtpParameters, diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index 15a1c0d2..cf2529f8 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -60,7 +60,7 @@ export class Chrome111 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Local stream for sending. + // Default local stream for sending if no stream is given. private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; @@ -336,6 +336,7 @@ export class Chrome111 async send({ track, + stream, encodings, codecOptions, headerExtensionOptions, @@ -345,7 +346,12 @@ export class Chrome111 this.assertNotClosed(); this.assertSendDirection(); - logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); + logger.debug( + 'send() [kind:%s, track.id:%s, stream.id:%s]', + track.kind, + track.id, + stream?.id + ); if (encodings && encodings.length > 1) { // Set rid and verify scalabilityMode in each encoding. @@ -374,7 +380,7 @@ export class Chrome111 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [this._sendStream], + streams: [stream ?? this._sendStream], sendEncodings: encodings, }); @@ -481,6 +487,11 @@ export class Chrome111 offerMediaObject, }); + console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); + + // Set msid. + sendingRtpParameters.msid = offerMediaObject.msid; + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ @@ -526,6 +537,8 @@ export class Chrome111 // Store in the map. this._mapMidTransceiver.set(localId, transceiver); + console.log('FOOOO sendingRtpParameters:', sendingRtpParameters); + return { localId, rtpParameters: sendingRtpParameters, @@ -905,11 +918,19 @@ export class Chrome111 mapLocalId.set(trackId, localId); + // We ignore MSID `trackId` when consuming and always us our computed + // `trackId` which matches the `consumer.id`. + const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( + rtpParameters.msid + ); + + console.log('FOOOOO receive() msidStreamId:%o', msidStreamId); + this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, - streamId: streamId ?? rtpParameters.rtcp!.cname!, + streamId: streamId ?? msidStreamId ?? rtpParameters.rtcp?.cname ?? '-', trackId, }); } diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index 41736d13..50e86135 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -61,7 +61,7 @@ export class Chrome74 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Local stream for sending. + // Default local stream for sending if no stream is given. private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; @@ -333,6 +333,7 @@ export class Chrome74 async send({ track, + stream, encodings, codecOptions, headerExtensionOptions, @@ -341,7 +342,12 @@ export class Chrome74 this.assertNotClosed(); this.assertSendDirection(); - logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); + logger.debug( + 'send() [kind:%s, track.id:%s, stream.id:%s]', + track.kind, + track.id, + stream?.id + ); if (encodings && encodings.length > 1) { encodings.forEach((encoding: RtpEncodingParameters, idx: number) => { @@ -352,7 +358,7 @@ export class Chrome74 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [this._sendStream], + streams: [stream ?? this._sendStream], sendEncodings: encodings, }); let offer = await this._pc.createOffer(); @@ -484,6 +490,8 @@ export class Chrome74 offerMediaObject, }); + console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/FakeHandler.ts b/src/handlers/FakeHandler.ts index 72418cf5..c668c755 100644 --- a/src/handlers/FakeHandler.ts +++ b/src/handlers/FakeHandler.ts @@ -179,7 +179,7 @@ export class FakeHandler async send( // eslint-disable-next-line @typescript-eslint/no-unused-vars - { track, encodings, codecOptions, codec }: HandlerSendOptions + { track, stream, encodings, codecOptions, codec }: HandlerSendOptions ): Promise { this.assertNotClosed(); @@ -214,6 +214,8 @@ export class FakeHandler sendingRtpParameters.mid = `mid-${utils.generateRandomNumber()}`; + sendingRtpParameters.msid = `${stream?.id ?? '-'} ${track.id}`; + if (!encodings) { encodings = [{}]; } diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index de006239..0d6b9608 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -55,7 +55,7 @@ export class Firefox120 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Local stream for sending. + // Default local stream for sending if no stream is given. private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; @@ -336,6 +336,7 @@ export class Firefox120 async send({ track, + stream, encodings, codecOptions, codec, @@ -344,7 +345,12 @@ export class Firefox120 this.assertNotClosed(); this.assertSendDirection(); - logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); + logger.debug( + 'send() [kind:%s, track.id:%s, stream.id:%s]', + track.kind, + track.id, + stream?.id + ); if (encodings && encodings.length > 1) { encodings.forEach((encoding: RtpEncodingParameters, idx: number) => { @@ -360,7 +366,7 @@ export class Firefox120 const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [this._sendStream], + streams: [stream ?? this._sendStream], sendEncodings: encodings, }); @@ -435,6 +441,8 @@ export class Firefox120 offerMediaObject, }); + console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/HandlerInterface.ts b/src/handlers/HandlerInterface.ts index 15e0c090..c37b5728 100644 --- a/src/handlers/HandlerInterface.ts +++ b/src/handlers/HandlerInterface.ts @@ -48,6 +48,7 @@ export type HandlerFactory = { export type HandlerSendOptions = { track: MediaStreamTrack; + stream?: MediaStream; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; headerExtensionOptions?: ProducerHeaderExtensionOptions; diff --git a/src/handlers/ReactNative106.ts b/src/handlers/ReactNative106.ts index 0366f143..57cf3bc4 100644 --- a/src/handlers/ReactNative106.ts +++ b/src/handlers/ReactNative106.ts @@ -61,7 +61,7 @@ export class ReactNative106 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Local stream for sending. + // Default local stream for sending if no stream is given. private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; @@ -339,6 +339,7 @@ export class ReactNative106 async send({ track, + stream, encodings, codecOptions, headerExtensionOptions, @@ -348,7 +349,12 @@ export class ReactNative106 this.assertNotClosed(); this.assertSendDirection(); - logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); + logger.debug( + 'send() [kind:%s, track.id:%s, stream.id:%s]', + track.kind, + track.id, + stream?.id + ); if (encodings && encodings.length > 1) { encodings.forEach((encoding: RtpEncodingParameters, idx: number) => { @@ -359,7 +365,7 @@ export class ReactNative106 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [this._sendStream], + streams: [stream ?? this._sendStream], sendEncodings: encodings, }); @@ -509,6 +515,8 @@ export class ReactNative106 offerMediaObject, }); + console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index cb619412..a958a082 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -60,7 +60,7 @@ export class Safari12 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Local stream for sending. + // Default local stream for sending if no stream is given. private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; @@ -343,6 +343,7 @@ export class Safari12 async send({ track, + stream, encodings, codecOptions, headerExtensionOptions, @@ -352,12 +353,17 @@ export class Safari12 this.assertNotClosed(); this.assertSendDirection(); - logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); + logger.debug( + 'send() [kind:%s, track.id:%s, stream.id:%s]', + track.kind, + track.id, + stream?.id + ); const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [this._sendStream], + streams: [stream ?? this._sendStream], }); if (onRtpSender) { @@ -485,6 +491,8 @@ export class Safari12 offerMediaObject, }); + console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); + // Set RTP encodings. sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject, diff --git a/src/handlers/ortc/utils.ts b/src/handlers/ortc/utils.ts index c749249d..16bfb298 100644 --- a/src/handlers/ortc/utils.ts +++ b/src/handlers/ortc/utils.ts @@ -70,3 +70,24 @@ export function addHeaderExtensionSupport( rtpCapabilities.headerExtensions.push(newHeaderExtension); } + +export function getMsidStreamIdAndTrackId(msid?: string): { + msidStreamId?: string; + msidTrackId?: string; +} { + if (!msid || typeof msid !== 'string') { + return { msidStreamId: undefined, msidTrackId: undefined }; + } + + /** + * `msidStreamId` must be an id or '-' (no stream). + * `msidTrackId` is an optional id. + */ + const [msidStreamId, msidTrackId] = msid.trim().split(/\s+/); + + if (!msidStreamId) { + return { msidStreamId: undefined, msidTrackId: undefined }; + } + + return { msidStreamId, msidTrackId }; +} diff --git a/src/handlers/sdp/MediaSection.ts b/src/handlers/sdp/MediaSection.ts index dab61643..e7675412 100644 --- a/src/handlers/sdp/MediaSection.ts +++ b/src/handlers/sdp/MediaSection.ts @@ -479,6 +479,7 @@ export class OfferMediaSection extends MediaSection { plainRtpParameters?: PlainRtpParameters; mid: string; kind: MediaKind | 'application'; + // Those are optionals because they are only given if `kind` is a MediaKind. offerRtpParameters?: RtpParameters; streamId?: string; trackId?: string; @@ -518,7 +519,7 @@ export class OfferMediaSection extends MediaSection { this._mediaObject.rtp = []; this._mediaObject.rtcpFb = []; this._mediaObject.fmtp = []; - this._mediaObject.msid = `${streamId ?? '-'} ${trackId}`; + this._mediaObject.msid = `${streamId} ${trackId}`; for (const codec of offerRtpParameters!.codecs) { const rtp: SdpTransform.MediaAttributes['rtp'][number] = { diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index 75d22b5b..f7c998ca 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -84,8 +84,6 @@ export class RemoteSdp { // If DTLS parameters are given, assume WebRTC and BUNDLE. if (dtlsParameters) { - this._sdpObject.msidSemantic = { semantic: 'WMS', token: '*' }; - // NOTE: We take the latest fingerprint. const numFingerprints = this._dtlsParameters!.fingerprints.length; @@ -190,15 +188,15 @@ export class RemoteSdp { // Unified-Plan with closed media section replacement. if (reuseMid) { - this._replaceMediaSection(mediaSection, reuseMid); + this.replaceMediaSection(mediaSection, reuseMid); } // Unified-Plan or Plan-B with different media kind. else if (!this._midToIndex.has(mediaSection.mid)) { - this._addMediaSection(mediaSection); + this.addMediaSection(mediaSection); } // Plan-B with same media kind. else { - this._replaceMediaSection(mediaSection); + this.replaceMediaSection(mediaSection); } } @@ -236,32 +234,32 @@ export class RemoteSdp { const oldMediaSection = this._mediaSections.find(m => m.closed); if (oldMediaSection) { - this._replaceMediaSection(mediaSection, oldMediaSection.mid); + this.replaceMediaSection(mediaSection, oldMediaSection.mid); } else { - this._addMediaSection(mediaSection); + this.addMediaSection(mediaSection); } } pauseMediaSection(mid: string): void { - const mediaSection = this._findMediaSection(mid); + const mediaSection = this.findMediaSection(mid); mediaSection.pause(); } resumeSendingMediaSection(mid: string): void { - const mediaSection = this._findMediaSection(mid); + const mediaSection = this.findMediaSection(mid); mediaSection.resume(); } resumeReceivingMediaSection(mid: string): void { - const mediaSection = this._findMediaSection(mid); + const mediaSection = this.findMediaSection(mid); mediaSection.resume(); } disableMediaSection(mid: string): void { - const mediaSection = this._findMediaSection(mid); + const mediaSection = this.findMediaSection(mid); mediaSection.disable(); } @@ -274,7 +272,7 @@ export class RemoteSdp { * transport, so instead closing it we just disable it. */ closeMediaSection(mid: string): boolean { - const mediaSection = this._findMediaSection(mid); + const mediaSection = this.findMediaSection(mid); // NOTE: Closing the first m section is a pain since it invalidates the // bundled transport, so let's avoid it. @@ -292,7 +290,7 @@ export class RemoteSdp { mediaSection.close(); // Regenerate BUNDLE mids. - this._regenerateBundleMids(); + this.regenerateBundleMids(); return true; } @@ -301,11 +299,11 @@ export class RemoteSdp { mid: string, encodings: RTCRtpEncodingParameters[] ): void { - const mediaSection = this._findMediaSection(mid) as AnswerMediaSection; + const mediaSection = this.findMediaSection(mid) as AnswerMediaSection; mediaSection.muxSimulcastStreams(encodings); - this._replaceMediaSection(mediaSection); + this.replaceMediaSection(mediaSection); } sendSctpAssociation({ @@ -322,7 +320,7 @@ export class RemoteSdp { offerMediaObject, }); - this._addMediaSection(mediaSection); + this.addMediaSection(mediaSection); } receiveSctpAssociation(): void { @@ -336,7 +334,7 @@ export class RemoteSdp { kind: 'application', }); - this._addMediaSection(mediaSection); + this.addMediaSection(mediaSection); } getSdp(): string { @@ -346,7 +344,7 @@ export class RemoteSdp { return sdpTransform.write(this._sdpObject); } - _addMediaSection(newMediaSection: MediaSection): void { + private addMediaSection(newMediaSection: MediaSection): void { if (!this._firstMid) { this._firstMid = newMediaSection.mid; } @@ -361,10 +359,13 @@ export class RemoteSdp { this._sdpObject.media.push(newMediaSection.getObject()); // Regenerate BUNDLE mids. - this._regenerateBundleMids(); + this.regenerateBundleMids(); } - _replaceMediaSection(newMediaSection: MediaSection, reuseMid?: string): void { + private replaceMediaSection( + newMediaSection: MediaSection, + reuseMid?: string + ): void { // Store it in the map. if (typeof reuseMid === 'string') { const idx = this._midToIndex.get(reuseMid); @@ -386,7 +387,7 @@ export class RemoteSdp { this._sdpObject.media[idx] = newMediaSection.getObject(); // Regenerate BUNDLE mids. - this._regenerateBundleMids(); + this.regenerateBundleMids(); } else { const idx = this._midToIndex.get(newMediaSection.mid); @@ -404,7 +405,7 @@ export class RemoteSdp { } } - _findMediaSection(mid: string): MediaSection { + private findMediaSection(mid: string): MediaSection { const idx = this._midToIndex.get(mid); if (idx === undefined) { @@ -414,7 +415,7 @@ export class RemoteSdp { return this._mediaSections[idx]!; } - _regenerateBundleMids(): void { + private regenerateBundleMids(): void { if (!this._dtlsParameters) { return; } From c6a2e702727756f6049a6a59d3dd783ed580c46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Sun, 26 Oct 2025 20:55:06 +0100 Subject: [PATCH 2/5] ready --- src/handlers/Chrome111.ts | 6 ------ src/handlers/Chrome74.ts | 2 -- src/handlers/Firefox120.ts | 2 -- src/handlers/ReactNative106.ts | 2 -- src/handlers/Safari12.ts | 2 -- 5 files changed, 14 deletions(-) diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index cf2529f8..c24ae029 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -487,8 +487,6 @@ export class Chrome111 offerMediaObject, }); - console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); - // Set msid. sendingRtpParameters.msid = offerMediaObject.msid; @@ -537,8 +535,6 @@ export class Chrome111 // Store in the map. this._mapMidTransceiver.set(localId, transceiver); - console.log('FOOOO sendingRtpParameters:', sendingRtpParameters); - return { localId, rtpParameters: sendingRtpParameters, @@ -924,8 +920,6 @@ export class Chrome111 rtpParameters.msid ); - console.log('FOOOOO receive() msidStreamId:%o', msidStreamId); - this._remoteSdp.receive({ mid: localId, kind, diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index 50e86135..200d0f00 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -490,8 +490,6 @@ export class Chrome74 offerMediaObject, }); - console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); - // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index 0d6b9608..d927cd34 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -441,8 +441,6 @@ export class Firefox120 offerMediaObject, }); - console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); - // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/ReactNative106.ts b/src/handlers/ReactNative106.ts index 57cf3bc4..4973cc14 100644 --- a/src/handlers/ReactNative106.ts +++ b/src/handlers/ReactNative106.ts @@ -515,8 +515,6 @@ export class ReactNative106 offerMediaObject, }); - console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); - // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index a958a082..29a5947b 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -491,8 +491,6 @@ export class Safari12 offerMediaObject, }); - console.log('FOOO offerMediaObject.msid:', offerMediaObject.msid); - // Set RTP encodings. sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject, From da3c87b01054d7bba7d09992550b6f12f85979be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Wed, 29 Oct 2025 11:02:45 +0100 Subject: [PATCH 3/5] instead of `stream` pass `streamId` --- src/Producer.ts | 2 +- src/Transport.ts | 4 ++-- src/handlers/Chrome111.ts | 10 +++++----- src/handlers/Chrome74.ts | 11 +++++++---- src/handlers/FakeHandler.ts | 9 +++++++-- src/handlers/Firefox120.ts | 11 +++++++---- src/handlers/HandlerInterface.ts | 13 ++++++++++--- src/handlers/ReactNative106.ts | 11 +++++++---- src/handlers/Safari12.ts | 11 +++++++---- 9 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/Producer.ts b/src/Producer.ts index 3cfe541d..8f51d13b 100644 --- a/src/Producer.ts +++ b/src/Producer.ts @@ -13,7 +13,7 @@ const logger = new Logger('Producer'); export type ProducerOptions = { track?: MediaStreamTrack; - stream?: MediaStream; + streamId?: string; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; headerExtensionOptions?: ProducerHeaderExtensionOptions; diff --git a/src/Transport.ts b/src/Transport.ts index 3895e2c1..b67effdf 100644 --- a/src/Transport.ts +++ b/src/Transport.ts @@ -500,7 +500,7 @@ export class Transport< */ async produce({ track, - stream, + streamId, encodings, codecOptions, headerExtensionOptions, @@ -588,7 +588,7 @@ export class Transport< const { localId, rtpParameters, rtpSender } = await this._handler.send({ track, - stream, + streamId, encodings: normalizedEncodings, codecOptions, headerExtensionOptions, diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index c24ae029..a4e77742 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -336,7 +336,7 @@ export class Chrome111 async send({ track, - stream, + streamId, encodings, codecOptions, headerExtensionOptions, @@ -347,10 +347,10 @@ export class Chrome111 this.assertSendDirection(); logger.debug( - 'send() [kind:%s, track.id:%s, stream.id:%s]', + 'send() [kind:%s, track.id:%s, streamId:%s]', track.kind, track.id, - stream?.id + streamId ); if (encodings && encodings.length > 1) { @@ -380,7 +380,7 @@ export class Chrome111 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [stream ?? this._sendStream], + streams: [this._sendStream], sendEncodings: encodings, }); @@ -488,7 +488,7 @@ export class Chrome111 }); // Set msid. - sendingRtpParameters.msid = offerMediaObject.msid; + sendingRtpParameters.msid = `${streamId ?? this._sendStream.id} ${track.id}`; // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index 200d0f00..c4575861 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -333,7 +333,7 @@ export class Chrome74 async send({ track, - stream, + streamId, encodings, codecOptions, headerExtensionOptions, @@ -343,10 +343,10 @@ export class Chrome74 this.assertSendDirection(); logger.debug( - 'send() [kind:%s, track.id:%s, stream.id:%s]', + 'send() [kind:%s, track.id:%s, streamId:%s]', track.kind, track.id, - stream?.id + streamId ); if (encodings && encodings.length > 1) { @@ -358,7 +358,7 @@ export class Chrome74 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [stream ?? this._sendStream], + streams: [this._sendStream], sendEncodings: encodings, }); let offer = await this._pc.createOffer(); @@ -490,6 +490,9 @@ export class Chrome74 offerMediaObject, }); + // Set msid. + sendingRtpParameters.msid = `${streamId ?? this._sendStream.id} ${track.id}`; + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/FakeHandler.ts b/src/handlers/FakeHandler.ts index c668c755..43592ae3 100644 --- a/src/handlers/FakeHandler.ts +++ b/src/handlers/FakeHandler.ts @@ -65,6 +65,8 @@ export class FakeHandler ) => ExtendedRtpCapabilities; // Local RTCP CNAME. private _cname = `CNAME-${utils.generateRandomNumber()}`; + // Default sending MediaStream id. + private _defaultSendStreamId = `${utils.generateRandomNumber()}`; // Got transport local and remote parameters. private _transportReady = false; // Next localId. @@ -179,7 +181,7 @@ export class FakeHandler async send( // eslint-disable-next-line @typescript-eslint/no-unused-vars - { track, stream, encodings, codecOptions, codec }: HandlerSendOptions + { track, streamId, encodings, codecOptions, codec }: HandlerSendOptions ): Promise { this.assertNotClosed(); @@ -214,7 +216,7 @@ export class FakeHandler sendingRtpParameters.mid = `mid-${utils.generateRandomNumber()}`; - sendingRtpParameters.msid = `${stream?.id ?? '-'} ${track.id}`; + sendingRtpParameters.msid = `${streamId ?? '-'} ${track.id}`; if (!encodings) { encodings = [{}]; @@ -237,6 +239,9 @@ export class FakeHandler mux: true, }; + // Set msid. + sendingRtpParameters.msid = `${streamId ?? this._defaultSendStreamId} ${track.id}`; + const localId = this._nextLocalId++; this._tracks.set(localId, track); diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index d927cd34..cc8e09ac 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -336,7 +336,7 @@ export class Firefox120 async send({ track, - stream, + streamId, encodings, codecOptions, codec, @@ -346,10 +346,10 @@ export class Firefox120 this.assertSendDirection(); logger.debug( - 'send() [kind:%s, track.id:%s, stream.id:%s]', + 'send() [kind:%s, track.id:%s, streamId:%s]', track.kind, track.id, - stream?.id + streamId ); if (encodings && encodings.length > 1) { @@ -366,7 +366,7 @@ export class Firefox120 const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [stream ?? this._sendStream], + streams: [this._sendStream], sendEncodings: encodings, }); @@ -441,6 +441,9 @@ export class Firefox120 offerMediaObject, }); + // Set msid. + sendingRtpParameters.msid = `${streamId ?? this._sendStream.id} ${track.id}`; + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/HandlerInterface.ts b/src/handlers/HandlerInterface.ts index c37b5728..6b0384ae 100644 --- a/src/handlers/HandlerInterface.ts +++ b/src/handlers/HandlerInterface.ts @@ -48,7 +48,13 @@ export type HandlerFactory = { export type HandlerSendOptions = { track: MediaStreamTrack; - stream?: MediaStream; + /** + * Stream id (it affects the `id` field of the `a=msid` attribute in the + * local SDP. If not given, all `Producers` will have the same `streamId` + * in their `rtpParameters.msid`. Such a value tells consuming endpoints + * which tracks to syncronize on reception. + */ + streamId?: string; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; headerExtensionOptions?: ProducerHeaderExtensionOptions; @@ -67,8 +73,9 @@ export type HandlerReceiveOptions = { kind: 'audio' | 'video'; rtpParameters: RtpParameters; /** - * Stream id. WebRTC based devices try to synchronize inbound streams with - * same streamId. If not given, the consuming device will be told to + * Stream id (it affects the `id` field of the `a=msid` attribute in the + * remote SDP. WebRTC based devices try to synchronize inbound streams with + * same `streamId`. If not given, the consuming device will be told to * synchronize all streams produced by the same endpoint. However libwebrtc * can just synchronize up to one audio stream with one video stream. */ diff --git a/src/handlers/ReactNative106.ts b/src/handlers/ReactNative106.ts index 4973cc14..de7c6b76 100644 --- a/src/handlers/ReactNative106.ts +++ b/src/handlers/ReactNative106.ts @@ -339,7 +339,7 @@ export class ReactNative106 async send({ track, - stream, + streamId, encodings, codecOptions, headerExtensionOptions, @@ -350,10 +350,10 @@ export class ReactNative106 this.assertSendDirection(); logger.debug( - 'send() [kind:%s, track.id:%s, stream.id:%s]', + 'send() [kind:%s, track.id:%s, streamId:%s]', track.kind, track.id, - stream?.id + streamId ); if (encodings && encodings.length > 1) { @@ -365,7 +365,7 @@ export class ReactNative106 const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [stream ?? this._sendStream], + streams: [this._sendStream], sendEncodings: encodings, }); @@ -515,6 +515,9 @@ export class ReactNative106 offerMediaObject, }); + // Set msid. + sendingRtpParameters.msid = `${streamId ?? this._sendStream.id} ${track.id}`; + // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index 29a5947b..493f2604 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -343,7 +343,7 @@ export class Safari12 async send({ track, - stream, + streamId, encodings, codecOptions, headerExtensionOptions, @@ -354,16 +354,16 @@ export class Safari12 this.assertSendDirection(); logger.debug( - 'send() [kind:%s, track.id:%s, stream.id:%s]', + 'send() [kind:%s, track.id:%s, streamId:%s]', track.kind, track.id, - stream?.id + streamId ); const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', - streams: [stream ?? this._sendStream], + streams: [this._sendStream], }); if (onRtpSender) { @@ -491,6 +491,9 @@ export class Safari12 offerMediaObject, }); + // Set msid. + sendingRtpParameters.msid = `${streamId ?? this._sendStream.id} ${track.id}`; + // Set RTP encodings. sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject, From 812953b9840c895a284123a642911e1f2c76dd99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Thu, 30 Oct 2025 21:31:04 +0100 Subject: [PATCH 4/5] omplete missing handles --- src/handlers/Chrome111.ts | 4 ++-- src/handlers/Chrome74.ts | 10 ++++++++-- src/handlers/FakeHandler.ts | 14 +++++++------- src/handlers/Firefox120.ts | 11 +++++++++-- src/handlers/ReactNative106.ts | 16 +++++++++++----- src/handlers/Safari12.ts | 16 +++++++++++----- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index a4e77742..f65cdb9d 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -14,6 +14,7 @@ import type { RtpHeaderExtensionDirection, } from '../RtpParameters'; import type { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; +import { RemoteSdp } from './sdp/RemoteSdp'; import * as sdpCommonUtils from './sdp/commonUtils'; import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; import * as ortcUtils from './ortc/utils'; @@ -31,7 +32,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { RemoteSdp } from './sdp/RemoteSdp'; const logger = new Logger('Chrome111'); @@ -914,7 +914,7 @@ export class Chrome111 mapLocalId.set(trackId, localId); - // We ignore MSID `trackId` when consuming and always us our computed + // We ignore MSID `trackId` when consuming and always use our computed // `trackId` which matches the `consumer.id`. const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( rtpParameters.msid diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index c4575861..77f96301 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -15,6 +15,7 @@ import type { RtpHeaderExtensionDirection, } from '../RtpParameters'; import type { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; +import { RemoteSdp } from './sdp/RemoteSdp'; import * as sdpCommonUtils from './sdp/commonUtils'; import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; import * as ortcUtils from './ortc/utils'; @@ -32,7 +33,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { RemoteSdp } from './sdp/RemoteSdp'; const logger = new Logger('Chrome74'); @@ -938,11 +938,17 @@ export class Chrome74 mapLocalId.set(trackId, localId); + // We ignore MSID `trackId` when consuming and always use our computed + // `trackId` which matches the `consumer.id`. + const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( + rtpParameters.msid + ); + this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, - streamId: streamId ?? rtpParameters.rtcp!.cname!, + streamId: streamId ?? msidStreamId ?? rtpParameters.rtcp?.cname ?? '-', trackId, }); } diff --git a/src/handlers/FakeHandler.ts b/src/handlers/FakeHandler.ts index 43592ae3..b621fb50 100644 --- a/src/handlers/FakeHandler.ts +++ b/src/handlers/FakeHandler.ts @@ -19,6 +19,13 @@ import type { ExtendedRtpCapabilities, } from '../RtpParameters'; import type { SctpCapabilities } from '../SctpParameters'; +import { FakeEventTarget } from './fakeEvents/FakeEventTarget'; +import { + FakeEventListener, + FakeAddEventListenerOptions, + FakeEventListenerOptions, +} from './fakeEvents/FakeEventListener'; +import { FakeEvent } from './fakeEvents/FakeEvent'; import type { HandlerFactory, HandlerInterface, @@ -33,13 +40,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { FakeEventTarget } from './fakeEvents/FakeEventTarget'; -import { - FakeEventListener, - FakeAddEventListenerOptions, - FakeEventListenerOptions, -} from './fakeEvents/FakeEventListener'; -import { FakeEvent } from './fakeEvents/FakeEvent'; const logger = new Logger('FakeHandler'); diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index cc8e09ac..d4f51149 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -13,8 +13,10 @@ import type { ExtendedRtpCapabilities, } from '../RtpParameters'; import type { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; +import { RemoteSdp } from './sdp/RemoteSdp'; import * as sdpCommonUtils from './sdp/commonUtils'; import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; +import * as ortcUtils from './ortc/utils'; import type { HandlerFactory, HandlerInterface, @@ -29,7 +31,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { RemoteSdp } from './sdp/RemoteSdp'; const logger = new Logger('Firefox120'); @@ -882,11 +883,17 @@ export class Firefox120 mapLocalId.set(trackId, localId); + // We ignore MSID `trackId` when consuming and always use our computed + // `trackId` which matches the `consumer.id`. + const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( + rtpParameters.msid + ); + this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, - streamId: streamId ?? rtpParameters.rtcp!.cname!, + streamId: streamId ?? msidStreamId ?? rtpParameters.rtcp?.cname ?? '-', trackId, }); } diff --git a/src/handlers/ReactNative106.ts b/src/handlers/ReactNative106.ts index de7c6b76..684c83ab 100644 --- a/src/handlers/ReactNative106.ts +++ b/src/handlers/ReactNative106.ts @@ -15,6 +15,10 @@ import type { RtpHeaderExtensionDirection, } from '../RtpParameters'; import type { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; +import { RemoteSdp } from './sdp/RemoteSdp'; +import * as sdpCommonUtils from './sdp/commonUtils'; +import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; +import * as ortcUtils from './ortc/utils'; import type { HandlerFactory, HandlerInterface, @@ -29,10 +33,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { RemoteSdp } from './sdp/RemoteSdp'; -import * as sdpCommonUtils from './sdp/commonUtils'; -import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; -import * as ortcUtils from './ortc/utils'; const logger = new Logger('ReactNative106'); @@ -972,11 +972,17 @@ export class ReactNative106 mapLocalId.set(trackId, localId); + // We ignore MSID `trackId` when consuming and always use our computed + // `trackId` which matches the `consumer.id`. + const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( + rtpParameters.msid + ); + this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, - streamId: streamId ?? rtpParameters.rtcp!.cname!, + streamId: streamId ?? msidStreamId ?? rtpParameters.rtcp?.cname ?? '-', trackId, }); } diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index 493f2604..44db1028 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -14,6 +14,10 @@ import type { RtpHeaderExtensionDirection, } from '../RtpParameters'; import type { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; +import { RemoteSdp } from './sdp/RemoteSdp'; +import * as sdpCommonUtils from './sdp/commonUtils'; +import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; +import * as ortcUtils from './ortc/utils'; import type { HandlerFactory, HandlerInterface, @@ -28,10 +32,6 @@ import type { HandlerReceiveDataChannelOptions, HandlerReceiveDataChannelResult, } from './HandlerInterface'; -import { RemoteSdp } from './sdp/RemoteSdp'; -import * as sdpCommonUtils from './sdp/commonUtils'; -import * as sdpUnifiedPlanUtils from './sdp/unifiedPlanUtils'; -import * as ortcUtils from './ortc/utils'; const logger = new Logger('Safari12'); @@ -925,11 +925,17 @@ export class Safari12 mapLocalId.set(trackId, localId); + // We ignore MSID `trackId` when consuming and always use our computed + // `trackId` which matches the `consumer.id`. + const { msidStreamId } = ortcUtils.getMsidStreamIdAndTrackId( + rtpParameters.msid + ); + this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, - streamId: streamId ?? rtpParameters.rtcp!.cname!, + streamId: streamId ?? msidStreamId ?? rtpParameters.rtcp?.cname ?? '-', trackId, }); } From 88c6e89241a23b6ab3006ae683f15b32a0e49514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Thu, 30 Oct 2025 21:32:26 +0100 Subject: [PATCH 5/5] cosmetic --- src/handlers/Chrome111.ts | 2 +- src/handlers/Chrome74.ts | 2 +- src/handlers/Firefox120.ts | 2 +- src/handlers/ReactNative106.ts | 2 +- src/handlers/Safari12.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index f65cdb9d..209d34fb 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -60,7 +60,7 @@ export class Chrome111 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Default local stream for sending if no stream is given. + // Default local stream for sending if no `streamId` is given in send(). private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index 77f96301..afbef8af 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -61,7 +61,7 @@ export class Chrome74 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Default local stream for sending if no stream is given. + // Default local stream for sending if no `streamId` is given in send(). private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index d4f51149..079c9a11 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -56,7 +56,7 @@ export class Firefox120 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Default local stream for sending if no stream is given. + // Default local stream for sending if no `streamId` is given in send(). private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; diff --git a/src/handlers/ReactNative106.ts b/src/handlers/ReactNative106.ts index 684c83ab..2bfa239b 100644 --- a/src/handlers/ReactNative106.ts +++ b/src/handlers/ReactNative106.ts @@ -61,7 +61,7 @@ export class ReactNative106 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Default local stream for sending if no stream is given. + // Default local stream for sending if no `streamId` is given in send(). private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index 44db1028..de48f60d 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -60,7 +60,7 @@ export class Safari12 // Map of RTCTransceivers indexed by MID. private readonly _mapMidTransceiver: Map = new Map(); - // Default local stream for sending if no stream is given. + // Default local stream for sending if no `streamId` is given in send(). private readonly _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false;