Browse Source

feat(multi-stream-support) Add the support for multiple local video streams.

* feat(multi-stream-support) Add the support for multiple local video streams.
This feature is behind 'sendMultipleVideoStreams' config.js flag and is currently supported only on clients running in Unified plan mode.

* squash: Use the track index from source name to find the matching transceiver on unmute.
dev1
Jaya Allamsetty 3 years ago
parent
commit
c3ab13d3da
No account linked to committer's email address

+ 31
- 11
JitsiConference.js View File

@@ -1095,6 +1095,23 @@ JitsiConference.prototype.addTrack = function(track) {
1095 1095
             return Promise.resolve(track);
1096 1096
         }
1097 1097
 
1098
+        if (FeatureFlags.isMultiStreamSupportEnabled() && mediaType === MediaType.VIDEO) {
1099
+            const addTrackPromises = [];
1100
+
1101
+            this.p2pJingleSession && addTrackPromises.push(this.p2pJingleSession.addTrack(track));
1102
+            this.jvbJingleSession && addTrackPromises.push(this.jvbJingleSession.addTrack(track));
1103
+
1104
+            return Promise.all(addTrackPromises)
1105
+                .then(() => {
1106
+                    this._setupNewTrack(track);
1107
+
1108
+                    // TODO Update presence and sent videoType message.
1109
+                    if (this.isMutedByFocus || this.isVideoMutedByFocus) {
1110
+                        this._fireMuteChangeEvent(track);
1111
+                    }
1112
+                });
1113
+        }
1114
+
1098 1115
         return Promise.reject(new Error(`Cannot add second ${mediaType} track to the conference`));
1099 1116
     }
1100 1117
 
@@ -1327,6 +1344,8 @@ JitsiConference.prototype._removeLocalSourceOnReject = function(jingleSession, e
1327 1344
  * @param {JitsiLocalTrack} newTrack the new track being created
1328 1345
  */
1329 1346
 JitsiConference.prototype._setupNewTrack = function(newTrack) {
1347
+    const mediaType = newTrack.getType();
1348
+
1330 1349
     if (newTrack.isAudioTrack() || (newTrack.isVideoTrack() && newTrack.videoType !== VideoType.DESKTOP)) {
1331 1350
         // Report active device to statistics
1332 1351
         const devices = RTC.getCurrentlyAvailableMediaDevices();
@@ -1338,6 +1357,16 @@ JitsiConference.prototype._setupNewTrack = function(newTrack) {
1338 1357
         }
1339 1358
     }
1340 1359
 
1360
+    // Create a source name for this track if it doesn't exist.
1361
+    if (FeatureFlags.isSourceNameSignalingEnabled() && !newTrack.getSourceName()) {
1362
+        const sourceName = getSourceNameForJitsiTrack(
1363
+            this.myUserId(),
1364
+            mediaType,
1365
+            this.getLocalTracks(mediaType)?.length);
1366
+
1367
+        newTrack.setSourceName(sourceName);
1368
+    }
1369
+
1341 1370
     this.rtc.addLocalTrack(newTrack);
1342 1371
     newTrack.setConference(this);
1343 1372
 
@@ -1363,13 +1392,7 @@ JitsiConference.prototype._setNewVideoType = function(track) {
1363 1392
     if (FeatureFlags.isSourceNameSignalingEnabled() && track) {
1364 1393
         // FIXME once legacy signaling using 'sendCommand' is removed, signalingLayer.setTrackVideoType must be adjusted
1365 1394
         // to send the presence (not just modify it).
1366
-        this._signalingLayer.setTrackVideoType(
1367
-            getSourceNameForJitsiTrack(
1368
-                this.myUserId(),
1369
-                track.getType(),
1370
-                0
1371
-            ),
1372
-            track.videoType);
1395
+        this._signalingLayer.setTrackVideoType(track.getSourceName(), track.videoType);
1373 1396
 
1374 1397
         // TODO: Optimize to detect whether presence was changed, for now always report changed to send presence
1375 1398
         return true;
@@ -1403,10 +1426,7 @@ JitsiConference.prototype._setTrackMuteStatus = function(mediaType, localTrack,
1403 1426
     if (FeatureFlags.isSourceNameSignalingEnabled()) {
1404 1427
         // TODO When legacy signaling part is removed, remember to adjust signalingLayer.setTrackMuteStatus, so that
1405 1428
         // it triggers sending the presence (it only updates it for now, because the legacy code below sends).
1406
-        this._signalingLayer.setTrackMuteStatus(
1407
-            getSourceNameForJitsiTrack(this.myUserId(), mediaType, 0),
1408
-            isMuted
1409
-        );
1429
+        this._signalingLayer.setTrackMuteStatus(localTrack?.getSourceName(), isMuted);
1410 1430
     }
1411 1431
 
1412 1432
     if (!this.room) {

+ 21
- 0
modules/RTC/JitsiLocalTrack.js View File

@@ -163,6 +163,9 @@ export default class JitsiLocalTrack extends JitsiTrack {
163 163
         // correspond to the id of a matching device from the available device list.
164 164
         this._realDeviceId = this.deviceId === '' ? undefined : this.deviceId;
165 165
 
166
+        // The source name that will be signaled for this track.
167
+        this._sourceName = null;
168
+
166 169
         this._trackMutedTS = 0;
167 170
 
168 171
         this._onDeviceListWillChange = devices => {
@@ -668,6 +671,15 @@ export default class JitsiLocalTrack extends JitsiTrack {
668 671
         return this.conference && this.conference.myUserId();
669 672
     }
670 673
 
674
+    /**
675
+     * Returns the source name associated with the jitsi track.
676
+     *
677
+     * @returns {string | null} source name
678
+     */
679
+    getSourceName() {
680
+        return this._sourceName;
681
+    }
682
+
671 683
     /**
672 684
      * Returns if associated MediaStreamTrack is in the 'ended' state
673 685
      *
@@ -861,6 +873,15 @@ export default class JitsiLocalTrack extends JitsiTrack {
861 873
             });
862 874
     }
863 875
 
876
+    /**
877
+     * Sets the source name to be used for signaling the jitsi track.
878
+     *
879
+     * @param {string} name The source name.
880
+     */
881
+    setSourceName(name) {
882
+        this._sourceName = name;
883
+    }
884
+
864 885
     /**
865 886
      * Stops the associated MediaStream.
866 887
      *

+ 36
- 3
modules/RTC/TPCUtils.js View File

@@ -339,21 +339,54 @@ export class TPCUtils {
339 339
 
340 340
     /**
341 341
      * Replaces the existing track on a RTCRtpSender with the given track.
342
+     *
342 343
      * @param {JitsiLocalTrack} oldTrack - existing track on the sender that needs to be removed.
343 344
      * @param {JitsiLocalTrack} newTrack - new track that needs to be added to the sender.
344
-     * @returns {Promise<void>} - resolved when done.
345
+     * @returns {Promise<RTCRtpTransceiver>} - resolved with the associated transceiver when done, rejected otherwise.
345 346
      */
346 347
     replaceTrack(oldTrack, newTrack) {
347 348
         const mediaType = newTrack?.getType() ?? oldTrack?.getType();
348
-        const transceiver = this.findTransceiver(mediaType, oldTrack);
349 349
         const track = newTrack?.getTrack() ?? null;
350
+        let transceiver;
351
+
352
+        // If old track exists, replace the track on the corresponding sender.
353
+        if (oldTrack) {
354
+            transceiver = this.pc.peerconnection.getTransceivers().find(t => t.sender.track === oldTrack.getTrack());
355
+
356
+        // Find the first recvonly transceiver when more than one track of the same media type is being added to the pc.
357
+        // As part of the track addition, a new m-line was added to the remote description with direction set to
358
+        // recvonly.
359
+        } else if (this.pc.getLocalTracks(mediaType)?.length && !newTrack.conference) {
360
+            transceiver = this.pc.peerconnection.getTransceivers().find(
361
+                t => t.receiver.track.kind === mediaType
362
+                && t.direction === MediaDirection.RECVONLY
363
+                && t.currentDirection === MediaDirection.INACTIVE);
364
+
365
+        // For unmute operations, find the transceiver based on the track index in the source name if present, otherwise
366
+        // it is assumed to be the first local track that was added to the peerconnection.
367
+        } else {
368
+            transceiver = this.pc.peerconnection.getTransceivers().find(t => t.receiver.track.kind === mediaType);
369
+
370
+            const sourceName = newTrack.getSourceName();
371
+
372
+            if (sourceName) {
373
+                const trackIndex = Number(sourceName.split('-')[1].substring(1));
374
+
375
+                if (trackIndex) {
376
+                    transceiver = this.pc.peerconnection.getTransceivers()
377
+                        .filter(t => t.receiver.track.kind === mediaType
378
+                            && t.direction !== MediaDirection.RECVONLY)[trackIndex];
379
+                }
380
+            }
381
+        }
350 382
 
351 383
         if (!transceiver) {
352 384
             return Promise.reject(new Error('replace track failed'));
353 385
         }
354 386
         logger.debug(`${this.pc} Replacing ${oldTrack} with ${newTrack}`);
355 387
 
356
-        return transceiver.sender.replaceTrack(track);
388
+        return transceiver.sender.replaceTrack(track)
389
+            .then(() => Promise.resolve(transceiver));
357 390
     }
358 391
 
359 392
     /**

+ 1
- 2
modules/RTC/TraceablePeerConnection.js View File

@@ -1952,10 +1952,9 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
1952 1952
             // The track will be replaced again on the peerconnection when the user unmutes.
1953 1953
             ? Promise.resolve()
1954 1954
             : this.tpcUtils.replaceTrack(oldTrack, newTrack);
1955
-        const transceiver = this.tpcUtils.findTransceiver(mediaType, oldTrack);
1956 1955
 
1957 1956
         return promise
1958
-            .then(() => {
1957
+            .then(transceiver => {
1959 1958
                 oldTrack && this.localTracks.delete(oldTrack.rtcId);
1960 1959
                 newTrack && this.localTracks.set(newTrack.rtcId, newTrack);
1961 1960
                 const mediaActive = mediaType === MediaType.AUDIO

+ 12
- 1
modules/flags/FeatureFlags.js View File

@@ -13,8 +13,19 @@ class FeatureFlags {
13 13
      */
14 14
     init(flags) {
15 15
         this._sourceNameSignaling = Boolean(flags.sourceNameSignaling);
16
+        this._sendMultipleVideoStreams = Boolean(flags.sendMultipleVideoStreams);
16 17
 
17
-        logger.info(`Source name signaling: ${this._sourceNameSignaling}`);
18
+        logger.info(`Source name signaling: ${this._sourceNameSignaling},`
19
+            + ` Send multiple video streams: ${this._sendMultipleVideoStreams}`);
20
+    }
21
+
22
+    /**
23
+     * Checks if multiple local video streams support is enabled.
24
+     *
25
+     * @returns {boolean}
26
+     */
27
+    isMultiStreamSupportEnabled() {
28
+        return this._sourceNameSignaling && this._sendMultipleVideoStreams;
18 29
     }
19 30
 
20 31
     /**

+ 29
- 14
modules/sdp/LocalSdpMunger.js View File

@@ -182,17 +182,18 @@ export default class LocalSdpMunger {
182 182
     }
183 183
 
184 184
     /**
185
-     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending
186
-     * the id of {@link LocalSdpMunger#tpc} at the end, preceding by a dash
187
-     * sign.
185
+     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending the id of {@link LocalSdpMunger#tpc} at the end,
186
+     * preceding by a dash sign.
188 187
      *
189
-     * @param {MLineWrap} mediaSection - The media part (audio or video) of the
190
-     * session description which will be modified in place.
188
+     * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be
189
+     * modified in place.
191 190
      * @returns {void}
192 191
      * @private
193 192
      */
194 193
     _transformMediaIdentifiers(mediaSection) {
194
+        const mediaType = mediaSection.mLine?.type;
195 195
         const pcId = this.tpc.id;
196
+        const sourceToMsidMap = new Map();
196 197
 
197 198
         for (const ssrcLine of mediaSection.ssrcs) {
198 199
             switch (ssrcLine.attribute) {
@@ -205,15 +206,29 @@ export default class LocalSdpMunger {
205 206
                 if (ssrcLine.value) {
206 207
                     const streamAndTrackIDs = ssrcLine.value.split(' ');
207 208
 
208
-                    if (streamAndTrackIDs.length === 2) {
209
-                        ssrcLine.value
210
-                            = this._generateMsidAttribute(
211
-                                mediaSection.mLine?.type,
212
-                                streamAndTrackIDs[1],
213
-                                streamAndTrackIDs[0]);
214
-                    } else {
215
-                        logger.warn(`Unable to munge local MSID - weird format detected: ${ssrcLine.value}`);
209
+                    let streamId = streamAndTrackIDs[0];
210
+                    const trackId = streamAndTrackIDs[1];
211
+
212
+                    // eslint-disable-next-line max-depth
213
+                    if (FeatureFlags.isMultiStreamSupportEnabled()
214
+                        && this.tpc.usesUnifiedPlan()
215
+                        && mediaType === MediaType.VIDEO) {
216
+
217
+                        // eslint-disable-next-line max-depth
218
+                        if (streamId === '-' || !streamId) {
219
+                            streamId = `${this.localEndpointId}-${mediaType}`;
220
+                        }
221
+
222
+                        // eslint-disable-next-line max-depth
223
+                        if (!sourceToMsidMap.has(trackId)) {
224
+                            streamId = `${streamId}-${sourceToMsidMap.size}`;
225
+                            sourceToMsidMap.set(trackId, streamId);
226
+                        }
216 227
                     }
228
+
229
+                    ssrcLine.value = this._generateMsidAttribute(mediaType, trackId, sourceToMsidMap.get(trackId));
230
+                } else {
231
+                    logger.warn(`Unable to munge local MSID - weird format detected: ${ssrcLine.value}`);
217 232
                 }
218 233
                 break;
219 234
             }
@@ -246,7 +261,7 @@ export default class LocalSdpMunger {
246 261
                     .find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');
247 262
 
248 263
                 if (!msidExists) {
249
-                    const generatedMsid = this._generateMsidAttribute(mediaSection.mLine?.type, trackId);
264
+                    const generatedMsid = this._generateMsidAttribute(mediaType, trackId);
250 265
 
251 266
                     mediaSection.ssrcs.push({
252 267
                         id: source,

+ 37
- 41
modules/sdp/RtxModifier.js View File

@@ -1,5 +1,6 @@
1 1
 import { getLogger } from '@jitsi/logger';
2 2
 
3
+import MediaDirection from '../../service/RTC/MediaDirection';
3 4
 import * as MediaType from '../../service/RTC/MediaType';
4 5
 
5 6
 import SDPUtil from './SDPUtil';
@@ -93,40 +94,44 @@ export default class RtxModifier {
93 94
     }
94 95
 
95 96
     /**
96
-     * Adds RTX ssrcs for any video ssrcs that don't
97
-     *  already have them.  If the video ssrc has been
98
-     *  seen before, and already had an RTX ssrc generated,
99
-     *  the same RTX ssrc will be used again.
97
+     * Adds RTX ssrcs for any video ssrcs that don't already have them.  If the video ssrc has been seen before, and
98
+     * already had an RTX ssrc generated, the same RTX ssrc will be used again.
99
+     *
100 100
      * @param {string} sdpStr sdp in raw string format
101
+     * @returns {string} The modified sdp in raw string format.
101 102
      */
102 103
     modifyRtxSsrcs(sdpStr) {
104
+        let modified = false;
103 105
         const sdpTransformer = new SdpTransformWrap(sdpStr);
104
-        const videoMLine = sdpTransformer.selectMedia(MediaType.VIDEO)?.[0];
106
+        const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);
105 107
 
106
-        if (!videoMLine) {
108
+        if (!videoMLines?.length) {
107 109
             logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);
108 110
 
109 111
             return sdpStr;
110 112
         }
111 113
 
112
-        return this.modifyRtxSsrcs2(videoMLine)
113
-            ? sdpTransformer.toRawSDP() : sdpStr;
114
+        for (const videoMLine of videoMLines) {
115
+            if (this.modifyRtxSsrcs2(videoMLine)) {
116
+                modified = true;
117
+            }
118
+        }
119
+
120
+        return modified ? sdpTransformer.toRawSDP() : sdpStr;
114 121
     }
115 122
 
116 123
     /**
117
-     * Does the same thing as {@link modifyRtxSsrcs}, but takes the
118
-     *  {@link MLineWrap} instance wrapping video media as an argument.
124
+     * Does the same thing as {@link modifyRtxSsrcs}, but takes the {@link MLineWrap} instance wrapping video media as
125
+     * an argument.
119 126
      * @param {MLineWrap} videoMLine
120
-     * @return {boolean} <tt>true</tt> if the SDP wrapped by
121
-     *  {@link SdpTransformWrap} has been modified or <tt>false</tt> otherwise.
127
+     * @return {boolean} <tt>true</tt> if the SDP wrapped by {@link SdpTransformWrap} has been modified or
128
+     * <tt>false</tt> otherwise.
122 129
      */
123 130
     modifyRtxSsrcs2(videoMLine) {
124
-        if (videoMLine.direction === 'recvonly') {
125
-
131
+        if (videoMLine.direction === MediaDirection.RECVONLY) {
126 132
             return false;
127 133
         }
128 134
         if (videoMLine.getSSRCCount() < 1) {
129
-
130 135
             return false;
131 136
         }
132 137
         const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();
@@ -164,46 +169,37 @@ export default class RtxModifier {
164 169
     }
165 170
 
166 171
     /**
167
-     * Strip all rtx streams from the given sdp
172
+     * Strip all rtx streams from the given sdp.
173
+     *
168 174
      * @param {string} sdpStr sdp in raw string format
169 175
      * @returns {string} sdp string with all rtx streams stripped
170 176
      */
171 177
     stripRtx(sdpStr) {
172 178
         const sdpTransformer = new SdpTransformWrap(sdpStr);
173
-        const videoMLine = sdpTransformer.selectMedia(MediaType.VIDEO)?.[0];
179
+        const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);
174 180
 
175
-        if (!videoMLine) {
181
+        if (!videoMLines?.length) {
176 182
             logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);
177 183
 
178 184
             return sdpStr;
179 185
         }
180
-        if (videoMLine.direction === 'recvonly') {
181
-            logger.debug('RtxModifier doing nothing, video m line is recvonly');
182 186
 
183
-            return sdpStr;
184
-        }
185
-        if (videoMLine.getSSRCCount() < 1) {
186
-            logger.debug('RtxModifier doing nothing, no video ssrcs present');
187
+        for (const videoMLine of videoMLines) {
188
+            if (videoMLine.direction !== MediaDirection.RECVONLY
189
+                && videoMLine.getSSRCCount()
190
+                && videoMLine.containsAnySSRCGroups()) {
191
+                const fidGroups = videoMLine.findGroups('FID');
187 192
 
188
-            return sdpStr;
189
-        }
190
-        if (!videoMLine.containsAnySSRCGroups()) {
191
-            logger.debug('RtxModifier doing nothing, '
192
-              + 'no video ssrcGroups present');
193
-
194
-            return sdpStr;
195
-        }
196
-        const fidGroups = videoMLine.findGroups('FID');
193
+                // Remove the fid groups from the mline
194
+                videoMLine.removeGroupsBySemantics('FID');
197 195
 
198
-        // Remove the fid groups from the mline
196
+                // Get the rtx ssrcs and remove them from the mline
197
+                for (const fidGroup of fidGroups) {
198
+                    const rtxSsrc = parseSecondarySSRC(fidGroup);
199 199
 
200
-        videoMLine.removeGroupsBySemantics('FID');
201
-
202
-        // Get the rtx ssrcs and remove them from the mline
203
-        for (const fidGroup of fidGroups) {
204
-            const rtxSsrc = parseSecondarySSRC(fidGroup);
205
-
206
-            videoMLine.removeSSRC(rtxSsrc);
200
+                    videoMLine.removeSSRC(rtxSsrc);
201
+                }
202
+            }
207 203
         }
208 204
 
209 205
         return sdpTransformer.toRawSDP();

+ 37
- 0
modules/sdp/SDP.js View File

@@ -1,5 +1,8 @@
1 1
 /* global $ */
2 2
 
3
+import clonedeep from 'lodash.clonedeep';
4
+import transform from 'sdp-transform';
5
+
3 6
 import MediaDirection from '../../service/RTC/MediaDirection';
4 7
 import browser from '../browser';
5 8
 import FeatureFlags from '../flags/FeatureFlags';
@@ -48,6 +51,40 @@ SDP.prototype.removeTcpCandidates = false;
48 51
  */
49 52
 SDP.prototype.removeUdpCandidates = false;
50 53
 
54
+/**
55
+ * Adds a new m-line to the description so that a new local source can then be attached to the transceiver that gets
56
+ * added after a reneogtiation cycle.
57
+ *
58
+ * @param {Mediatype} mediaType media type of the new source that is being added.
59
+ */
60
+SDP.prototype.addMlineForNewLocalSource = function(mediaType) {
61
+    const mid = this.media.length;
62
+    const sdp = transform.parse(this.raw);
63
+    const mline = clonedeep(sdp.media.find(m => m.type === mediaType));
64
+
65
+    // Edit media direction, mid and remove the existing ssrc lines in the m-line.
66
+    mline.mid = mid;
67
+    mline.direction = MediaDirection.RECVONLY;
68
+
69
+    // Remove the ssrcs and source groups.
70
+    mline.msid = undefined;
71
+    mline.ssrcs = undefined;
72
+    mline.ssrcGroups = undefined;
73
+
74
+    sdp.media = sdp.media.concat(mline);
75
+
76
+    // We regenerate the BUNDLE group (since we added a new m-line)
77
+    sdp.groups.forEach(group => {
78
+        if (group.type === 'BUNDLE') {
79
+            const mids = group.mids.split(' ');
80
+
81
+            mids.push(mid);
82
+            group.mids = mids.join(' ');
83
+        }
84
+    });
85
+    this.raw = transform.write(sdp);
86
+};
87
+
51 88
 /**
52 89
  * Returns map of MediaChannel mapped per channel idx.
53 90
  */

+ 47
- 1
modules/xmpp/JingleSessionPC.js View File

@@ -5,6 +5,7 @@ import { $iq, Strophe } from 'strophe.js';
5 5
 
6 6
 import * as CodecMimeType from '../../service/RTC/CodecMimeType';
7 7
 import MediaDirection from '../../service/RTC/MediaDirection';
8
+import * as MediaType from '../../service/RTC/MediaType';
8 9
 import {
9 10
     ICE_DURATION,
10 11
     ICE_STATE_CHANGED
@@ -1383,7 +1384,7 @@ export default class JingleSessionPC extends JingleSession {
1383 1384
                     sid: this.sid
1384 1385
                 })
1385 1386
                 .c('content', {
1386
-                    name: 'video',
1387
+                    name: MediaType.VIDEO,
1387 1388
                     senders
1388 1389
                 });
1389 1390
 
@@ -2030,6 +2031,51 @@ export default class JingleSessionPC extends JingleSession {
2030 2031
             });
2031 2032
     }
2032 2033
 
2034
+    /**
2035
+     * Adds a new track to the peerconnection. This method needs to be called only when a secondary JitsiLocalTrack is
2036
+     * being added to the peerconnection for the first time.
2037
+     *
2038
+     * @param {JitsiLocalTrack} localTrack track to be added to the peer connection.
2039
+     * @returns {Promise<void>} that resolves when the track is successfully added to the peerconnection, rejected
2040
+     * otherwise.
2041
+     */
2042
+    addTrack(localTrack) {
2043
+        if (!FeatureFlags.isMultiStreamSupportEnabled()
2044
+            || !this.usesUnifiedPlan
2045
+            || localTrack.type !== MediaType.VIDEO) {
2046
+            return Promise.reject(new Error('Multiple tracks of a given media type are not supported'));
2047
+        }
2048
+
2049
+        const workFunction = finishedCallback => {
2050
+            const remoteSdp = new SDP(this.peerconnection.peerconnection.remoteDescription.sdp);
2051
+
2052
+            // Add a new transceiver by adding a new mline in the remote description.
2053
+            remoteSdp.addMlineForNewLocalSource(MediaType.VIDEO);
2054
+            this._renegotiate(remoteSdp.raw)
2055
+                .then(() => finishedCallback(), error => finishedCallback(error));
2056
+        };
2057
+
2058
+        return new Promise((resolve, reject) => {
2059
+            logger.debug(`${this} Queued renegotiation after addTrack`);
2060
+
2061
+            this.modificationQueue.push(
2062
+                workFunction,
2063
+                error => {
2064
+                    if (error) {
2065
+                        logger.error(`${this} renegotiation after addTrack error`, error);
2066
+                        reject(error);
2067
+                    } else {
2068
+                        logger.debug(`${this} renegotiation after addTrack executed - OK`);
2069
+
2070
+                        // Replace the track on the newly generated transceiver.
2071
+                        return this.replaceTrack(null, localTrack)
2072
+                            .then(() => resolve())
2073
+                            .catch(() => reject());
2074
+                    }
2075
+                });
2076
+        });
2077
+    }
2078
+
2033 2079
     /**
2034 2080
      * Replaces <tt>oldTrack</tt> with <tt>newTrack</tt> and performs a single
2035 2081
      * offer/answer cycle after both operations are done. Either

+ 13
- 0
types/auto/modules/RTC/JitsiLocalTrack.d.ts View File

@@ -81,6 +81,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
81 81
      */
82 82
     _testDataSent: boolean;
83 83
     _realDeviceId: string;
84
+    _sourceName: string;
84 85
     _trackMutedTS: number;
85 86
     _onDeviceListWillChange: (devices: any) => void;
86 87
     _onAudioOutputDeviceChanged: any;
@@ -220,6 +221,12 @@ export default class JitsiLocalTrack extends JitsiTrack {
220 221
      * Colibri endpoint id/MUC nickname in case of Jitsi-meet.
221 222
      */
222 223
     getParticipantId(): string;
224
+    /**
225
+     * Returns the source name associated with the jitsi track.
226
+     *
227
+     * @returns {string | null} source name
228
+     */
229
+    getSourceName(): string | null;
223 230
     /**
224 231
      * Returns if associated MediaStreamTrack is in the 'ended' state
225 232
      *
@@ -269,6 +276,12 @@ export default class JitsiLocalTrack extends JitsiTrack {
269 276
      * @returns {Promise}
270 277
      */
271 278
     setEffect(effect: any): Promise<any>;
279
+    /**
280
+     * Sets the source name to be used for signaling the jitsi track.
281
+     *
282
+     * @param {string} name The source name.
283
+     */
284
+    setSourceName(name: string): void;
272 285
     /**
273 286
      * Stops the associated MediaStream.
274 287
      *

+ 3
- 2
types/auto/modules/RTC/TPCUtils.d.ts View File

@@ -108,11 +108,12 @@ export class TPCUtils {
108 108
     calculateEncodingsBitrates(localVideoTrack: any): Array<number>;
109 109
     /**
110 110
      * Replaces the existing track on a RTCRtpSender with the given track.
111
+     *
111 112
      * @param {JitsiLocalTrack} oldTrack - existing track on the sender that needs to be removed.
112 113
      * @param {JitsiLocalTrack} newTrack - new track that needs to be added to the sender.
113
-     * @returns {Promise<void>} - resolved when done.
114
+     * @returns {Promise<RTCRtpTransceiver>} - resolved with the associated transceiver when done, rejected otherwise.
114 115
      */
115
-    replaceTrack(oldTrack: any, newTrack: any): Promise<void>;
116
+    replaceTrack(oldTrack: any, newTrack: any): Promise<RTCRtpTransceiver>;
116 117
     /**
117 118
     * Enables/disables audio transmission on the peer connection. When
118 119
     * disabled the audio transceiver direction will be set to 'inactive'

+ 7
- 0
types/auto/modules/flags/FeatureFlags.d.ts View File

@@ -11,6 +11,13 @@ declare class FeatureFlags {
11 11
      */
12 12
     init(flags: any): void;
13 13
     _sourceNameSignaling: boolean;
14
+    _sendMultipleVideoStreams: boolean;
15
+    /**
16
+     * Checks if multiple local video streams support is enabled.
17
+     *
18
+     * @returns {boolean}
19
+     */
20
+    isMultiStreamSupportEnabled(): boolean;
14 21
     /**
15 22
      * Checks if the source name signaling is enabled.
16 23
      *

+ 4
- 5
types/auto/modules/sdp/LocalSdpMunger.d.ts View File

@@ -42,12 +42,11 @@ export default class LocalSdpMunger {
42 42
      */
43 43
     _generateMsidAttribute(mediaType: string, trackId: string, streamId?: string): string | null;
44 44
     /**
45
-     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending
46
-     * the id of {@link LocalSdpMunger#tpc} at the end, preceding by a dash
47
-     * sign.
45
+     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending the id of {@link LocalSdpMunger#tpc} at the end,
46
+     * preceding by a dash sign.
48 47
      *
49
-     * @param {MLineWrap} mediaSection - The media part (audio or video) of the
50
-     * session description which will be modified in place.
48
+     * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be
49
+     * modified in place.
51 50
      * @returns {void}
52 51
      * @private
53 52
      */

+ 10
- 9
types/auto/modules/sdp/RtxModifier.d.ts View File

@@ -25,23 +25,24 @@ export default class RtxModifier {
25 25
      */
26 26
     setSsrcCache(ssrcMapping: Map<any, any>): void;
27 27
     /**
28
-     * Adds RTX ssrcs for any video ssrcs that don't
29
-     *  already have them.  If the video ssrc has been
30
-     *  seen before, and already had an RTX ssrc generated,
31
-     *  the same RTX ssrc will be used again.
28
+     * Adds RTX ssrcs for any video ssrcs that don't already have them.  If the video ssrc has been seen before, and
29
+     * already had an RTX ssrc generated, the same RTX ssrc will be used again.
30
+     *
32 31
      * @param {string} sdpStr sdp in raw string format
32
+     * @returns {string} The modified sdp in raw string format.
33 33
      */
34 34
     modifyRtxSsrcs(sdpStr: string): string;
35 35
     /**
36
-     * Does the same thing as {@link modifyRtxSsrcs}, but takes the
37
-     *  {@link MLineWrap} instance wrapping video media as an argument.
36
+     * Does the same thing as {@link modifyRtxSsrcs}, but takes the {@link MLineWrap} instance wrapping video media as
37
+     * an argument.
38 38
      * @param {MLineWrap} videoMLine
39
-     * @return {boolean} <tt>true</tt> if the SDP wrapped by
40
-     *  {@link SdpTransformWrap} has been modified or <tt>false</tt> otherwise.
39
+     * @return {boolean} <tt>true</tt> if the SDP wrapped by {@link SdpTransformWrap} has been modified or
40
+     * <tt>false</tt> otherwise.
41 41
      */
42 42
     modifyRtxSsrcs2(videoMLine: any): boolean;
43 43
     /**
44
-     * Strip all rtx streams from the given sdp
44
+     * Strip all rtx streams from the given sdp.
45
+     *
45 46
      * @param {string} sdpStr sdp in raw string format
46 47
      * @returns {string} sdp string with all rtx streams stripped
47 48
      */

+ 7
- 0
types/auto/modules/sdp/SDP.d.ts View File

@@ -29,6 +29,13 @@ export default class SDP {
29 29
      * @type {boolean}
30 30
      */
31 31
     removeUdpCandidates: boolean;
32
+    /**
33
+     * Adds a new m-line to the description so that a new local source can then be attached to the transceiver that gets
34
+     * added after a reneogtiation cycle.
35
+     *
36
+     * @param {Mediatype} mediaType media type of the new source that is being added.
37
+     */
38
+    addMlineForNewLocalSource(mediaType: any): void;
32 39
     /**
33 40
      * Returns map of MediaChannel mapped per channel idx.
34 41
      */

+ 9
- 0
types/auto/modules/xmpp/JingleSessionPC.d.ts View File

@@ -454,6 +454,15 @@ export default class JingleSessionPC extends JingleSession {
454 454
      * @private
455 455
      */
456 456
     private _initiatorRenegotiate;
457
+    /**
458
+     * Adds a new track to the peerconnection. This method needs to be called only when a secondary JitsiLocalTrack is
459
+     * being added to the peerconnection for the first time.
460
+     *
461
+     * @param {JitsiLocalTrack} localTrack track to be added to the peer connection.
462
+     * @returns {Promise<void>} that resolves when the track is successfully added to the peerconnection, rejected
463
+     * otherwise.
464
+     */
465
+    addTrack(localTrack: any): Promise<void>;
457 466
     /**
458 467
      * Replaces <tt>oldTrack</tt> with <tt>newTrack</tt> and performs a single
459 468
      * offer/answer cycle after both operations are done. Either

Loading…
Cancel
Save