diff --git a/modules/RTC/TPCUtils.js b/modules/RTC/TPCUtils.js index 90aa2a6a14..ada4e85a23 100644 --- a/modules/RTC/TPCUtils.js +++ b/modules/RTC/TPCUtils.js @@ -360,12 +360,14 @@ export class TPCUtils { /** * Adds {@link JitsiLocalTrack} to the WebRTC peerconnection for the first time. + * * @param {JitsiLocalTrack} track - track to be added to the peerconnection. * @param {boolean} isInitiator - boolean that indicates if the endpoint is offerer in a p2p connection. - * @returns {void} + * @returns {RTCRtpTransceiver} - the transceiver that the track was added to. */ addTrack(localTrack, isInitiator) { const track = localTrack.getTrack(); + let transceiver; if (isInitiator) { const streams = []; @@ -385,13 +387,18 @@ export class TPCUtils { if (!browser.isFirefox()) { transceiverInit.sendEncodings = this._getStreamEncodings(localTrack); } - this.pc.peerconnection.addTransceiver(track, transceiverInit); + transceiver = this.pc.peerconnection.addTransceiver(track, transceiverInit); } else { // Use pc.addTrack() for responder case so that we can re-use the m-lines that were created // when setRemoteDescription was called. pc.addTrack() automatically attaches to any existing // unused "recv-only" transceiver. - this.pc.peerconnection.addTrack(track); + const sender = this.pc.peerconnection.addTrack(track); + + // Find the corresponding transceiver that the track was attached to. + transceiver = this.pc.peerconnection.getTransceivers().find(t => t.sender === sender); } + + return transceiver; } /** diff --git a/modules/RTC/TraceablePeerConnection.js b/modules/RTC/TraceablePeerConnection.js index b3eed39efe..b5b266a51c 100644 --- a/modules/RTC/TraceablePeerConnection.js +++ b/modules/RTC/TraceablePeerConnection.js @@ -327,6 +327,13 @@ export default function TraceablePeerConnection( */ this._localTrackTransceiverMids = new Map(); + /** + * Holds the SSRC map for the local tracks. + * + * @type {Map} + */ + this._ssrcMap = null; + // override as desired this.trace = (what, info) => { logger.trace(what, info); @@ -1123,117 +1130,80 @@ TraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) { }; /** - * Returns a map with keys msid/mediaType and TrackSSRCInfo values. - * @param {RTCSessionDescription} desc the local description. - * @return {Map} + * Processes the local SDP and creates an SSRC map for every local track. + * + * @param {string} localSDP - SDP from the local description. + * @returns {void} */ -TraceablePeerConnection.prototype._extractSSRCMap = function(desc) { - /** - * Track SSRC infos mapped by stream ID (msid) or mediaType (unified-plan) - * @type {Map} - */ +TraceablePeerConnection.prototype._processAndExtractSourceInfo = function(localSDP) { const ssrcMap = new Map(); - /** - * Groups mapped by primary SSRC number - * @type {Map>} - */ - const groupsMap = new Map(); - - if (typeof desc !== 'object' || desc === null - || typeof desc.sdp !== 'string') { - logger.warn('An empty description was passed as an argument'); - - return ssrcMap; - } - - const session = transform.parse(desc.sdp); - - if (!Array.isArray(session.media)) { + if (!localSDP || typeof localSDP !== 'string') { return ssrcMap; } - - let media = session.media; - - media = media.filter(mline => mline.direction === MediaDirection.SENDONLY + const session = transform.parse(localSDP); + const media = session.media.filter(mline => mline.direction === MediaDirection.SENDONLY || mline.direction === MediaDirection.SENDRECV); - let index = 0; - - for (const mLine of media) { - if (!Array.isArray(mLine.ssrcs)) { - continue; // eslint-disable-line no-continue - } + if (!Array.isArray(media)) { + return; + } - if (Array.isArray(mLine.ssrcGroups)) { - for (const group of mLine.ssrcGroups) { - if (typeof group.semantics !== 'undefined' && typeof group.ssrcs !== 'undefined') { - // Parse SSRCs and store as numbers - const groupSSRCs = group.ssrcs.split(' ').map(ssrcStr => parseInt(ssrcStr, 10)); - const primarySSRC = groupSSRCs[0]; + for (const localTrack of this.localTracks.values()) { + const sourceName = localTrack.getSourceName(); + const trackIndex = getSourceIndexFromSourceName(sourceName); + const mediaType = localTrack.getType(); + const mLines = media.filter(m => m.type === mediaType); + const ssrcGroups = mLines[trackIndex].ssrcGroups; + let ssrcs = mLines[trackIndex].ssrcs; + + if (ssrcs?.length) { + // Filter the ssrcs with 'cname' attribute. + ssrcs = ssrcs.filter(s => s.attribute === 'cname'); + + const msid = `${this.rtc.getLocalEndpointId()}-${mediaType}-${trackIndex}`; + const ssrcInfo = { + ssrcs: [], + groups: [], + msid + }; - // Note that group.semantics is already present - group.ssrcs = groupSSRCs; + ssrcs.forEach(ssrc => ssrcInfo.ssrcs.push(ssrc.id)); - // eslint-disable-next-line max-depth - if (!groupsMap.has(primarySSRC)) { - groupsMap.set(primarySSRC, []); - } - groupsMap.get(primarySSRC).push(group); + if (Array.isArray(ssrcGroups)) { + for (const group of ssrcGroups) { + group.ssrcs = group.ssrcs.split(' ').map(ssrcStr => parseInt(ssrcStr, 10)); + ssrcInfo.groups.push(group); } - } - const simGroup = mLine.ssrcGroups.find(group => group.semantics === 'SIM'); + const simGroup = ssrcGroups.find(group => group.semantics === 'SIM'); - // Add a SIM group if its missing in the description (happens on Firefox). - if (!simGroup) { - const groupSsrcs = mLine.ssrcGroups.map(group => group.ssrcs[0]); + // Add a SIM group if its missing in the description (happens on Firefox). + if (this.isSpatialScalabilityOn() && !simGroup) { + const groupSsrcs = ssrcGroups.map(group => group.ssrcs[0]); - groupsMap.get(groupSsrcs[0]).push({ - semantics: 'SIM', - ssrcs: groupSsrcs - }); + ssrcInfo.groups.push({ + semantics: 'SIM', + ssrcs: groupSsrcs + }); + } } - } - let ssrcs = mLine.ssrcs; - - // Filter the ssrcs with 'cname' attribute. - ssrcs = ssrcs.filter(s => s.attribute === 'cname'); - - for (const ssrc of ssrcs) { - // Use the mediaType as key for the source map for unified plan clients since msids are not part of - // the standard and the unified plan SDPs do not have a proper msid attribute for the sources. - // Also the ssrcs for sources do not change for Unified plan clients since RTCRtpSender#replaceTrack is - // used for switching the tracks so it is safe to use the mediaType as the key for the TrackSSRCInfo map. - const key = `${mLine.type}-${index}`; - const ssrcNumber = ssrc.id; - let ssrcInfo = ssrcMap.get(key); - - if (!ssrcInfo) { - ssrcInfo = { - ssrcs: [], - groups: [], - msid: key - }; - ssrcMap.set(key, ssrcInfo); - } - ssrcInfo.ssrcs.push(ssrcNumber); + ssrcMap.set(sourceName, ssrcInfo); - if (groupsMap.has(ssrcNumber)) { - const ssrcGroups = groupsMap.get(ssrcNumber); + const oldSsrcInfo = this.localSSRCs.get(localTrack.rtcId); + const oldSsrc = this._extractPrimarySSRC(oldSsrcInfo); + const newSsrc = this._extractPrimarySSRC(ssrcInfo); - for (const group of ssrcGroups) { - ssrcInfo.groups.push(group); - } + if (oldSsrc !== newSsrc) { + oldSsrc && logger.error(`${this} Overwriting SSRC for track=${localTrack}] with ssrc=${newSsrc}`); + this.localSSRCs.set(localTrack.rtcId, ssrcInfo); + localTrack.setSsrc(newSsrc); + this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_SSRC_UPDATED, localTrack, newSsrc); } } - - // Currently multi-stream is supported for video only. - mLine.type === MediaType.VIDEO && index++; } - - return ssrcMap; + this._ssrcMap = ssrcMap; }; /** @@ -1327,7 +1297,7 @@ const getters = { } // See the method's doc for more info about this transformation. - desc = this.localSdpMunger.transformStreamIdentifiers(desc); + desc = this.localSdpMunger.transformStreamIdentifiers(desc, this._ssrcMap); return desc; }, @@ -1502,6 +1472,7 @@ TraceablePeerConnection.prototype._updateAv1DdHeaders = function(description) { */ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false) { const rtcId = track.rtcId; + let transceiver; logger.info(`${this} adding ${track}`); if (this.localTracks.has(rtcId)) { @@ -1513,7 +1484,12 @@ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false const webrtcStream = track.getOriginalStream(); try { - this.tpcUtils.addTrack(track, isInitiator); + transceiver = this.tpcUtils.addTrack(track, isInitiator); + + if (transceiver?.mid) { + this._localTrackTransceiverMids.set(track.rtcId, transceiver.mid.toString()); + } + if (track) { if (track.isAudioTrack()) { this._hasHadAudioTrack = true; @@ -1814,23 +1790,21 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) { } } - if (transceiver) { - // In the scenario where we remove the oldTrack (oldTrack is not null and newTrack is null) on FF - // if we change the direction to RECVONLY, create answer will generate SDP with only 1 receive - // only ssrc instead of keeping all 6 ssrcs that we currently have. Stopping the screen sharing - // and then starting it again will trigger 2 rounds of source-remove and source-add replacing - // the 6 ssrcs for the screen sharing with 1 receive only ssrc and then removing the receive - // only ssrc and adding the same 6 ssrcs. On the remote participant's side the same ssrcs will - // be reused on a new m-line and if the remote participant is FF due to - // https://bugzilla.mozilla.org/show_bug.cgi?id=1768729 the video stream won't be rendered. - // That's why we need keep the direction to SENDRECV for FF. - // - // NOTE: If we return back to the approach of not removing the track for FF and instead using the - // enabled property for mute or stopping screensharing we may need to change the direction to - // RECVONLY if FF still sends the media even though the enabled flag is set to false. - transceiver.direction - = newTrack || browser.isFirefox() ? MediaDirection.SENDRECV : MediaDirection.RECVONLY; - } + // In the scenario where we remove the oldTrack (oldTrack is not null and newTrack is null) on FF + // if we change the direction to RECVONLY, create answer will generate SDP with only 1 receive + // only ssrc instead of keeping all 6 ssrcs that we currently have. Stopping the screen sharing + // and then starting it again will trigger 2 rounds of source-remove and source-add replacing + // the 6 ssrcs for the screen sharing with 1 receive only ssrc and then removing the receive + // only ssrc and adding the same 6 ssrcs. On the remote participant's side the same ssrcs will + // be reused on a new m-line and if the remote participant is FF due to + // https://bugzilla.mozilla.org/show_bug.cgi?id=1768729 the video stream won't be rendered. + // That's why we need keep the direction to SENDRECV for FF. + // + // NOTE: If we return back to the approach of not removing the track for FF and instead using the + // enabled property for mute or stopping screensharing we may need to change the direction to + // RECVONLY if FF still sends the media even though the enabled flag is set to false. + transceiver.direction + = newTrack || browser.isFirefox() ? MediaDirection.SENDRECV : MediaDirection.RECVONLY; // Avoid re-configuring the encodings on Chromium/Safari, this is needed only on Firefox. const configureEncodingsPromise @@ -2626,9 +2600,7 @@ TraceablePeerConnection.prototype._createOfferOrAnswer = function(isOffer, const dumpSDP(resultSdp)); } - const ssrcMap = this._extractSSRCMap(resultSdp); - - this._processLocalSSRCsMap(ssrcMap); + this._processAndExtractSourceInfo(resultSdp.sdp); resolveFn(resultSdp); } catch (e) { @@ -2715,47 +2687,6 @@ TraceablePeerConnection.prototype._extractPrimarySSRC = function(ssrcObj) { return null; }; -/** - * Goes over the SSRC map extracted from the latest local description and tries - * to match them with the local tracks (by MSID). Will update the values - * currently stored in the {@link TraceablePeerConnection.localSSRCs} map. - * @param {Map} ssrcMap - * @private - */ -TraceablePeerConnection.prototype._processLocalSSRCsMap = function(ssrcMap) { - for (const track of this.localTracks.values()) { - const sourceName = track.getSourceName(); - const sourceIndex = getSourceIndexFromSourceName(sourceName); - const sourceIdentifier = `${track.getType()}-${sourceIndex}`; - - if (ssrcMap.has(sourceIdentifier)) { - const newSSRC = ssrcMap.get(sourceIdentifier); - - if (!newSSRC) { - logger.error(`${this} No SSRC found for stream=${sourceIdentifier}`); - - return; - } - const oldSSRC = this.localSSRCs.get(track.rtcId); - const newSSRCNum = this._extractPrimarySSRC(newSSRC); - const oldSSRCNum = this._extractPrimarySSRC(oldSSRC); - - // eslint-disable-next-line no-negated-condition - if (newSSRCNum !== oldSSRCNum) { - oldSSRCNum && logger.error(`${this} Overwriting SSRC for track=${track}] with ssrc=${newSSRC}`); - this.localSSRCs.set(track.rtcId, newSSRC); - track.setSsrc(newSSRCNum); - this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_SSRC_UPDATED, track, newSSRCNum); - } - } else if (!track.isVideoTrack() && !track.isMuted()) { - // It is normal to find no SSRCs for a muted video track in - // the local SDP as the recv-only SSRC is no longer munged in. - // So log the warning only if it's not a muted video track. - logger.warn(`${this} No SSRCs found in the local SDP for track=${track}, stream=${sourceIdentifier}`); - } - } -}; - /** * Track the SSRCs seen so far. * @param {number} ssrc - SSRC. diff --git a/modules/sdp/LocalSdpMunger.js b/modules/sdp/LocalSdpMunger.js index 1690e3d062..226b7ea6b3 100644 --- a/modules/sdp/LocalSdpMunger.js +++ b/modules/sdp/LocalSdpMunger.js @@ -1,14 +1,11 @@ -import { getLogger } from '@jitsi/logger'; +import { isEqual } from 'lodash-es'; import { MediaDirection } from '../../service/RTC/MediaDirection'; import { MediaType } from '../../service/RTC/MediaType'; -import { getSourceNameForJitsiTrack } from '../../service/RTC/SignalingLayer'; import browser from '../browser'; import { SdpTransformWrap } from './SdpTransformUtil'; -const logger = getLogger(__filename); - /** * Fakes local SDP exposed to {@link JingleSessionPC} through the local * description getter. Modifies the SDP, so that it will contain muted local @@ -28,77 +25,75 @@ export default class LocalSdpMunger { constructor(tpc, localEndpointId) { this.tpc = tpc; this.localEndpointId = localEndpointId; - this.audioSourcesToMsidMap = new Map(); - this.videoSourcesToMsidMap = new Map(); - } - - /** - * Returns a string that can be set as the MSID attribute for a source. - * - * @param {string} mediaType - Media type of the source. - * @param {string} trackId - Id of the MediaStreamTrack associated with the source. - * @param {string} streamId - Id of the MediaStream associated with the source. - * @returns {string|null} - */ - _generateMsidAttribute(mediaType, trackId, streamId) { - if (!(mediaType && trackId)) { - logger.error(`Unable to munge local MSID - track id=${trackId} or media type=${mediaType} is missing`); - - return null; - } - const pcId = this.tpc.id; - - return `${streamId}-${pcId} ${trackId}-${pcId}`; } /** - * Updates or adds a 'msid' attribute in the format '---' - * example - d8ff91-video-0-1 - * All other attributes like 'cname', 'label' and 'mslabel' are removed since these are not processed by Jicofo. + * Updates or adds a 'msid' attribute in the format. All other attributes like 'cname', 'label' and 'mslabel' are + * removed since these are not processed by Jicofo. * * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be * modified in place. * @returns {void} * @private */ - _transformMediaIdentifiers(mediaSection) { - const mediaType = mediaSection.mLine?.type; - const mediaDirection = mediaSection.mLine?.direction; - const msidLine = mediaSection.mLine?.msid; - const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ]; - const streamId = `${this.localEndpointId}-${mediaType}`; - let trackId = msidLine ? msidLine.split(' ')[1] : `${this.localEndpointId}-${mediaSection.mLine.mid}`; - - // Always overwrite msid since we want the msid to be in this format even if the browser generates one. - for (const source of sources) { - const msid = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'msid'); - - if (msid) { - trackId = msid.value.split(' ')[1]; + _transformMediaIdentifiers(mediaSection, ssrcMap) { + const mediaType = mediaSection.mLine.type; + const mediaDirection = mediaSection.mLine.direction; + const sources = [ ...new Set(mediaSection.mLine.ssrcs?.map(s => s.id)) ]; + let sourceName; + + if (ssrcMap.size) { + for (const [ id, trackSsrcs ] of ssrcMap.entries()) { + if (isEqual([ ...sources ].sort(), [ ...trackSsrcs.ssrcs ].sort())) { + sourceName = id; + } } - this._updateSourcesToMsidMap(mediaType, streamId, trackId); - const storedStreamId = mediaType === MediaType.VIDEO - ? this.videoSourcesToMsidMap.get(trackId) - : this.audioSourcesToMsidMap.get(trackId); - - const generatedMsid = this._generateMsidAttribute(mediaType, trackId, storedStreamId); - - // Update the msid if the 'msid' attribute exists. - if (msid) { - msid.value = generatedMsid; - - // Generate the 'msid' attribute if there is a local source. - } else if (mediaDirection === MediaDirection.SENDONLY || mediaDirection === MediaDirection.SENDRECV) { - mediaSection.ssrcs.push({ - id: source, - attribute: 'msid', - value: generatedMsid - }); + for (const source of sources) { + if ((mediaDirection === MediaDirection.SENDONLY || mediaDirection === MediaDirection.SENDRECV) + && sourceName) { + const msid = ssrcMap.get(sourceName).msid; + const generatedMsid = `${msid}-${this.tpc.id}`; + const existingMsid = mediaSection.ssrcs + .find(ssrc => ssrc.id === source && ssrc.attribute === 'msid'); + + // Always overwrite msid since we want the msid to be in this format even if the browser generates + // one. '---' example - d8ff91-video-0-1 + if (existingMsid) { + existingMsid.value = generatedMsid; + } else { + mediaSection.ssrcs.push({ + id: source, + attribute: 'msid', + value: generatedMsid + }); + } + + // Inject source names as a=ssrc:3124985624 name:endpointA-v0 + mediaSection.ssrcs.push({ + id: source, + attribute: 'name', + value: sourceName + }); + + const videoType = this.tpc.getLocalVideoTracks() + .find(track => track.getSourceName() === sourceName) + ?.getVideoType(); + + if (mediaType === MediaType.VIDEO && videoType) { + // Inject videoType as a=ssrc:1234 videoType:desktop. + mediaSection.ssrcs.push({ + id: source, + attribute: 'videoType', + value: videoType + }); + } + } } } - // Ignore the 'cname', 'label' and 'mslabel' attributes and only have the 'msid' attribute. - mediaSection.ssrcs = mediaSection.ssrcs.filter(ssrc => ssrc.attribute === 'msid'); + // Ignore the 'cname', 'label' and 'mslabel' attributes. + mediaSection.ssrcs = mediaSection.ssrcs + .filter(ssrc => ssrc.attribute === 'msid' || ssrc.attribute === 'name' || ssrc.attribute === 'videoType'); // On FF when the user has started muted create answer will generate a recv only SSRC. We don't want to signal // this SSRC in order to reduce the load of the xmpp server for large calls. Therefore the SSRC needs to be @@ -122,43 +117,16 @@ export default class LocalSdpMunger { } /** - * Updates the MSID map. + * This transformation will make sure that stream identifiers are unique across all of the local PeerConnections + * even if the same stream is used by multiple instances at the same time. It also injects 'sourceName' and + * 'videoType' attribute. * - * @param {string} mediaType The media type. - * @param {string} streamId The stream id. - * @param {string} trackId The track id. - * @returns {void} - */ - _updateSourcesToMsidMap(mediaType, streamId, trackId) { - if (mediaType === MediaType.VIDEO) { - if (!this.videoSourcesToMsidMap.has(trackId)) { - const generatedStreamId = `${streamId}-${this.videoSourcesToMsidMap.size}`; - - this.videoSourcesToMsidMap.set(trackId, generatedStreamId); - } - } else if (!this.audioSourcesToMsidMap.has(trackId)) { - const generatedStreamId = `${streamId}-${this.audioSourcesToMsidMap.size}`; - - this.audioSourcesToMsidMap.set(trackId, generatedStreamId); - } - } - - /** - * This transformation will make sure that stream identifiers are unique - * across all of the local PeerConnections even if the same stream is used - * by multiple instances at the same time. - * Each PeerConnection assigns different SSRCs to the same local - * MediaStream, but the MSID remains the same as it's used to identify - * the stream by the WebRTC backend. The transformation will append - * {@link TraceablePeerConnection#id} at the end of each stream's identifier - * ("cname", "msid", "label" and "mslabel"). - * - * @param {RTCSessionDescription} sessionDesc - The local session - * description (this instance remains unchanged). + * @param {RTCSessionDescription} sessionDesc - The local session description (this instance remains unchanged). + * @param {Map} ssrcMap - The SSRC and source map for the local tracks. * @return {RTCSessionDescription} - Transformed local session description * (a modified copy of the one given as the input). */ - transformStreamIdentifiers(sessionDesc) { + transformStreamIdentifiers(sessionDesc, ssrcMap) { if (!sessionDesc || !sessionDesc.sdp || !sessionDesc.type) { return sessionDesc; } @@ -167,80 +135,18 @@ export default class LocalSdpMunger { const audioMLine = transformer.selectMedia(MediaType.AUDIO)?.[0]; if (audioMLine) { - this._transformMediaIdentifiers(audioMLine); - this._injectSourceNames(audioMLine); + this._transformMediaIdentifiers(audioMLine, ssrcMap); } const videoMlines = transformer.selectMedia(MediaType.VIDEO); for (const videoMLine of videoMlines) { - this._transformMediaIdentifiers(videoMLine); - this._injectSourceNames(videoMLine); + this._transformMediaIdentifiers(videoMLine, ssrcMap); } - // Reset the local tracks based maps for msid after every transformation since Chrome 122 is generating - // a new set of SSRCs for the same source when the direction of transceiver changes because of a remote - // source getting added on the p2p connection. - this.audioSourcesToMsidMap.clear(); - this.videoSourcesToMsidMap.clear(); - return new RTCSessionDescription({ type: sessionDesc.type, sdp: transformer.toRawSDP() }); } - - /** - * Injects source names. Source names are need to for multiple streams per endpoint support. The final plan is to - * use the "mid" attribute for source names, but because the SDP to Jingle conversion still operates in the Plan-B - * semantics (one source name per media), a custom "name" attribute is injected into SSRC lines.. - * - * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be - * modified in place. - * @returns {void} - * @private - */ - _injectSourceNames(mediaSection) { - const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ]; - const mediaType = mediaSection.mLine?.type; - - if (!mediaType) { - throw new Error('_transformMediaIdentifiers - no media type in mediaSection'); - } - - for (const source of sources) { - const nameExists = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'name'); - const msid = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'msid').value; - const streamId = msid.split(' ')[0]; - - // Example stream id: d8ff91-video-8-1 - // In the example above 8 is the track index - const trackIndexParts = streamId.split('-'); - const trackIndex = trackIndexParts[trackIndexParts.length - 2]; - const sourceName = getSourceNameForJitsiTrack(this.localEndpointId, mediaType, trackIndex); - - if (!nameExists) { - // Inject source names as a=ssrc:3124985624 name:endpointA-v0 - mediaSection.ssrcs.push({ - id: source, - attribute: 'name', - value: sourceName - }); - } - - if (mediaType === MediaType.VIDEO) { - const videoType = this.tpc.getLocalVideoTracks().find(track => track.getSourceName() === sourceName) - ?.getVideoType(); - - if (videoType) { - // Inject videoType as a=ssrc:1234 videoType:desktop. - mediaSection.ssrcs.push({ - id: source, - attribute: 'videoType', - value: videoType - }); - } - } - } - } } diff --git a/modules/sdp/LocalSdpMunger.spec.js b/modules/sdp/LocalSdpMunger.spec.js index 5f1df8196e..8dbafee664 100644 --- a/modules/sdp/LocalSdpMunger.spec.js +++ b/modules/sdp/LocalSdpMunger.spec.js @@ -2,7 +2,6 @@ import * as transform from 'sdp-transform'; import { MockPeerConnection } from '../RTC/MockClasses'; -import FeatureFlags from '../flags/FeatureFlags'; import LocalSdpMunger from './LocalSdpMunger'; import { default as SampleSdpStrings } from './SampleSdpStrings.js'; @@ -26,7 +25,6 @@ describe('TransformSdpsForUnifiedPlan', () => { const localEndpointId = 'sRdpsdg'; beforeEach(() => { - FeatureFlags.init({ }); localSdpMunger = new LocalSdpMunger(tpc, localEndpointId); }); describe('StripSsrcs', () => { @@ -38,7 +36,7 @@ describe('TransformSdpsForUnifiedPlan', () => { type: 'offer', sdp: sdpStr }); - const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc); + const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc, {}); const newSdp = transform.parse(transformedDesc.sdp); const audioSsrcs = getSsrcLines(newSdp, 'audio'); const videoSsrcs = getSsrcLines(newSdp, 'video'); @@ -56,7 +54,20 @@ describe('TransformSdpsForUnifiedPlan', () => { type: 'offer', sdp: sdpStr }); - const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc); + const ssrcMap = new Map(); + + ssrcMap.set('sRdpsdg-v0', { + ssrcs: [ 1757014965, 1479742055, 1089111804 ], + msid: 'sRdpsdg-video-0', + groups: [ { + semantics: 'SIM', + ssrcs: [ 1757014965, 1479742055, 1089111804 ] } ] + }); + ssrcMap.set('sRdpsdg-a0', { + ssrcs: [ 124723944 ], + msid: 'sRdpsdg-audio-0' + }); + const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc, ssrcMap); const newSdp = transform.parse(transformedDesc.sdp); audioSsrcs = getSsrcLines(newSdp, 'audio'); @@ -79,14 +90,24 @@ describe('TransformSdpsForUnifiedPlan', () => { type: 'offer', sdp: sdpStr }); - const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc); + const ssrcMap = new Map(); + + ssrcMap.set('sRdpsdg-v0', { + ssrcs: [ 984899560 ], + msid: 'sRdpsdg-video-0' + }); + ssrcMap.set('sRdpsdg-a0', { + ssrcs: [ 124723944 ], + msid: 'sRdpsdg-audio-0' + }); + const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc, ssrcMap); const newSdp = transform.parse(transformedDesc.sdp); const videoSsrcs = getSsrcLines(newSdp, 'video'); for (const ssrcLine of videoSsrcs) { if (ssrcLine.attribute === 'msid') { - const msid = ssrcLine.value.split(' ')[0]; + const msid = ssrcLine.value; expect(msid).toBe(`${localEndpointId}-video-0-${tpc.id}`); } @@ -102,7 +123,17 @@ describe('TransformSdpsForUnifiedPlan', () => { type: 'offer', sdp: sdpStr }); - const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc); + const ssrcMap = new Map(); + + ssrcMap.set('sRdpsdg-v0', { + ssrcs: [ 984899560 ], + msid: 'sRdpsdg-video-0' + }); + ssrcMap.set('sRdpsdg-a0', { + ssrcs: [ 124723944 ], + msid: 'sRdpsdg-audio-0' + }); + const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc, ssrcMap); const newSdp = transform.parse(transformedDesc.sdp); const videoSsrcs = getSsrcLines(newSdp, 'video'); const msidExists = videoSsrcs.find(s => s.attribute === 'msid'); @@ -124,7 +155,17 @@ describe('Transform msids for source-name signaling', () => { type: 'offer', sdp: sdpStr }); - const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc); + const ssrcMap = new Map(); + + ssrcMap.set('sRdpsdg-v0', { + ssrcs: [ 1757014965, 984899560, 1479742055, 855213044, 1089111804, 2963867077 ], + msid: 'sRdpsdg-video-0' + }); + ssrcMap.set('sRdpsdg-a0', { + ssrcs: [ 124723944 ], + msid: 'sRdpsdg-audio-0' + }); + const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc, ssrcMap); const newSdp = transform.parse(transformedDesc.sdp); audioMsidLine = getSsrcLines(newSdp, 'audio').find(ssrc => ssrc.attribute === 'msid')?.value; @@ -134,7 +175,6 @@ describe('Transform msids for source-name signaling', () => { }; it('should transform', () => { - FeatureFlags.init({ }); transformStreamIdentifiers(); expect(audioMsid).toBe('sRdpsdg-audio-0-1'); diff --git a/modules/sdp/SDP.spec.js b/modules/sdp/SDP.spec.js index 5ea5e1a98e..bb857e0767 100644 --- a/modules/sdp/SDP.spec.js +++ b/modules/sdp/SDP.spec.js @@ -650,8 +650,6 @@ describe('SDP', () => { }); sdp.toJingle(accept, 'responder'); - console.log(accept.tree()); - const { nodeTree } = accept; const content = nodeTree.querySelectorAll('jingle>content');