Переглянути джерело

fix(multi-stream-support) Support muting of desktop track.

* fix(multi-stream-support) Support muting of desktop track.

* fix(multi-stream) Always initiate responder renegotiation even for p2p.
This is needed since the new m-line is always added to the remote description.

* fix(multi-stream) Support multi-stream only on Unfied plan endpoints.

* fix(multi-stream) Advertise source-name signaling to Jicofo when supported.

* fix(JitsiLocalTrack): Remove unnecessary reject.

* fix: add the correct source name attribute for the second video track.

* squash: Address review comments.
dev1
Jaya Allamsetty 3 роки тому
джерело
коміт
aed1fa446b
Аккаунт користувача з таким Email не знайдено

+ 6
- 1
JitsiConference.js Переглянути файл

@@ -1107,8 +1107,9 @@ JitsiConference.prototype.addTrack = function(track) {
1107 1107
             return Promise.all(addTrackPromises)
1108 1108
                 .then(() => {
1109 1109
                     this._setupNewTrack(track);
1110
+                    this._sendBridgeVideoTypeMessage(track);
1111
+                    this._updateRoomPresence(this.getActiveMediaSession());
1110 1112
 
1111
-                    // TODO Update presence and sent videoType message.
1112 1113
                     if (this.isMutedByFocus || this.isVideoMutedByFocus) {
1113 1114
                         this._fireMuteChangeEvent(track);
1114 1115
                     }
@@ -1269,6 +1270,10 @@ JitsiConference.prototype.replaceTrack = function(oldTrack, newTrack) {
1269 1270
         logger.warn(`JitsiConference.replaceTrack oldTrack (${oldTrack} does not belong to this conference`);
1270 1271
     }
1271 1272
 
1273
+    if (FeatureFlags.isMultiStreamSupportEnabled() && oldTrack && newTrack && oldTrack.isVideoTrack()) {
1274
+        newTrack.setSourceName(oldTrack.getSourceName());
1275
+    }
1276
+
1272 1277
     // Now replace the stream at the lower levels
1273 1278
     return this._doReplaceTrack(oldTrackBelongsToConference ? oldTrack : null, newTrack)
1274 1279
         .then(() => {

+ 6
- 0
JitsiMeetJS.js Переглянути файл

@@ -147,6 +147,12 @@ export default _mergeNamespaceAndModule({
147 147
         Settings.init(options.externalStorage);
148 148
         Statistics.init(options);
149 149
 
150
+        // Multi-stream is supported only on endpoints running in Unified plan mode and the flag to disable unified
151
+        // plan also needs to be taken into consideration.
152
+        if (typeof options.enableUnifiedOnChrome !== 'undefined') {
153
+            options.flags.enableUnifiedOnChrome = options.enableUnifiedOnChrome;
154
+        }
155
+
150 156
         // Configure the feature flags.
151 157
         FeatureFlags.init(options.flags || { });
152 158
 

+ 9
- 4
modules/RTC/JitsiLocalTrack.js Переглянути файл

@@ -20,6 +20,7 @@ import {
20 20
     createNoDataFromSourceEvent
21 21
 } from '../../service/statistics/AnalyticsEvents';
22 22
 import browser from '../browser';
23
+import FeatureFlags from '../flags/FeatureFlags';
23 24
 import Statistics from '../statistics/statistics';
24 25
 
25 26
 import JitsiTrack from './JitsiTrack';
@@ -354,7 +355,8 @@ export default class JitsiLocalTrack extends JitsiTrack {
354 355
      * @returns {Promise}
355 356
      */
356 357
     _setMuted(muted) {
357
-        if (this.isMuted() === muted) {
358
+        if (this.isMuted() === muted
359
+            && !(this.videoType === VideoType.DESKTOP && FeatureFlags.isMultiStreamSupportEnabled())) {
358 360
             return Promise.resolve();
359 361
         }
360 362
 
@@ -367,8 +369,12 @@ export default class JitsiLocalTrack extends JitsiTrack {
367 369
         // A function that will print info about muted status transition
368 370
         const logMuteInfo = () => logger.info(`Mute ${this}: ${muted}`);
369 371
 
372
+        // In the multi-stream mode, desktop tracks are muted from jitsi-meet instead of being removed from the
373
+        // conference. This is needed because we don't want the client to signal a source-remove to the remote peer for
374
+        // the desktop track when screenshare is stopped. Later when screenshare is started again, the same sender will
375
+        // be re-used without the need for signaling a new ssrc through source-add.
370 376
         if (this.isAudioTrack()
371
-                || this.videoType === VideoType.DESKTOP
377
+                || (this.videoType === VideoType.DESKTOP && !FeatureFlags.isMultiStreamSupportEnabled())
372 378
                 || !browser.doesVideoMuteByStreamRemove()) {
373 379
             logMuteInfo();
374 380
 
@@ -442,8 +448,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
442 448
                     this._startStreamEffect(this._streamEffect);
443 449
                 }
444 450
 
445
-                this.containers.map(
446
-                    cont => RTCUtils.attachMediaStream(cont, this.stream));
451
+                this.containers.map(cont => RTCUtils.attachMediaStream(cont, this.stream));
447 452
 
448 453
                 return this._addStreamToConferenceAsUnmute();
449 454
             });

+ 9
- 7
modules/RTC/TPCUtils.js Переглянути файл

@@ -354,6 +354,11 @@ export class TPCUtils {
354 354
     replaceTrack(oldTrack, newTrack) {
355 355
         const mediaType = newTrack?.getType() ?? oldTrack?.getType();
356 356
         const track = newTrack?.getTrack() ?? null;
357
+        const isNewLocalSource = FeatureFlags.isMultiStreamSupportEnabled()
358
+            && this.pc.getLocalTracks(mediaType)?.length
359
+            && !oldTrack
360
+            && newTrack
361
+            && !newTrack.conference;
357 362
         let transceiver;
358 363
 
359 364
         // If old track exists, replace the track on the corresponding sender.
@@ -363,20 +368,17 @@ export class TPCUtils {
363 368
         // Find the first recvonly transceiver when more than one track of the same media type is being added to the pc.
364 369
         // As part of the track addition, a new m-line was added to the remote description with direction set to
365 370
         // recvonly.
366
-        } else if (FeatureFlags.isMultiStreamSupportEnabled()
367
-            && this.pc.getLocalTracks(mediaType)?.length
368
-            && !newTrack.conference) {
371
+        } else if (isNewLocalSource) {
369 372
             transceiver = this.pc.peerconnection.getTransceivers().find(
370 373
                 t => t.receiver.track.kind === mediaType
371 374
                 && t.direction === MediaDirection.RECVONLY
372 375
                 && t.currentDirection === MediaDirection.INACTIVE);
373 376
 
374
-        // For unmute operations, find the transceiver based on the track index in the source name if present, otherwise
375
-        // it is assumed to be the first local track that was added to the peerconnection.
377
+        // For mute/unmute operations, find the transceiver based on the track index in the source name if present,
378
+        // otherwise it is assumed to be the first local track that was added to the peerconnection.
376 379
         } else {
377 380
             transceiver = this.pc.peerconnection.getTransceivers().find(t => t.receiver.track.kind === mediaType);
378
-
379
-            const sourceName = newTrack?.getSourceName();
381
+            const sourceName = newTrack?.getSourceName() ?? oldTrack?.getSourceName();
380 382
 
381 383
             if (sourceName) {
382 384
                 const trackIndex = Number(sourceName.split('-')[1].substring(1));

+ 3
- 5
modules/RTC/TraceablePeerConnection.js Переглянути файл

@@ -2008,11 +2008,9 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
2008 2008
 TraceablePeerConnection.prototype.removeTrackMute = function(localTrack) {
2009 2009
     const webRtcStream = localTrack.getOriginalStream();
2010 2010
 
2011
-    this.trace(
2012
-        'removeStreamMute',
2013
-        localTrack.rtcId, webRtcStream ? webRtcStream.id : null);
2011
+    this.trace('removeTrackMute', localTrack.rtcId, webRtcStream ? webRtcStream.id : null);
2014 2012
 
2015
-    if (!this._assertTrackBelongs('removeStreamMute', localTrack)) {
2013
+    if (!this._assertTrackBelongs('removeTrackMute', localTrack)) {
2016 2014
         // Abort - nothing to be done here
2017 2015
         return Promise.reject('Track not found in the peerconnection');
2018 2016
     }
@@ -2028,7 +2026,7 @@ TraceablePeerConnection.prototype.removeTrackMute = function(localTrack) {
2028 2026
         return Promise.resolve(true);
2029 2027
     }
2030 2028
 
2031
-    logger.error(`${this} removeStreamMute - no WebRTC stream for track=${localTrack}`);
2029
+    logger.error(`${this} removeTrackMute - no WebRTC stream for track=${localTrack}`);
2032 2030
 
2033 2031
     return Promise.reject('Stream not found');
2034 2032
 };

+ 9
- 2
modules/flags/FeatureFlags.js Переглянути файл

@@ -1,5 +1,7 @@
1 1
 import { getLogger } from '@jitsi/logger';
2 2
 
3
+import browser from '../browser';
4
+
3 5
 const logger = getLogger('FeatureFlags');
4 6
 
5 7
 /**
@@ -15,8 +17,13 @@ class FeatureFlags {
15 17
         this._sourceNameSignaling = Boolean(flags.sourceNameSignaling);
16 18
         this._sendMultipleVideoStreams = Boolean(flags.sendMultipleVideoStreams);
17 19
 
20
+        // For Chromium, check if Unified plan is enabled.
21
+        this._usesUnifiedPlan = browser.supportsUnifiedPlan()
22
+            && (!browser.isChromiumBased() || (flags.enableUnifiedOnChrome ?? true));
23
+
18 24
         logger.info(`Source name signaling: ${this._sourceNameSignaling},`
19
-            + ` Send multiple video streams: ${this._sendMultipleVideoStreams}`);
25
+            + ` Send multiple video streams: ${this._sendMultipleVideoStreams},`
26
+            + ` uses Unified plan: ${this._usesUnifiedPlan}`);
20 27
     }
21 28
 
22 29
     /**
@@ -25,7 +32,7 @@ class FeatureFlags {
25 32
      * @returns {boolean}
26 33
      */
27 34
     isMultiStreamSupportEnabled() {
28
-        return this._sourceNameSignaling && this._sendMultipleVideoStreams;
35
+        return this._sourceNameSignaling && this._sendMultipleVideoStreams && this._usesUnifiedPlan;
29 36
     }
30 37
 
31 38
     /**

+ 12
- 8
modules/sdp/LocalSdpMunger.js Переглянути файл

@@ -165,7 +165,7 @@ export default class LocalSdpMunger {
165 165
      */
166 166
     _generateMsidAttribute(mediaType, trackId, streamId = null) {
167 167
         if (!(mediaType && trackId)) {
168
-            logger.warn(`Unable to munge local MSID - track id=${trackId} or media type=${mediaType} is missing`);
168
+            logger.error(`Unable to munge local MSID - track id=${trackId} or media type=${mediaType} is missing`);
169 169
 
170 170
             return null;
171 171
         }
@@ -210,9 +210,7 @@ export default class LocalSdpMunger {
210 210
                     const trackId = streamAndTrackIDs[1];
211 211
 
212 212
                     // eslint-disable-next-line max-depth
213
-                    if (FeatureFlags.isMultiStreamSupportEnabled()
214
-                        && this.tpc.usesUnifiedPlan()
215
-                        && mediaType === MediaType.VIDEO) {
213
+                    if (FeatureFlags.isMultiStreamSupportEnabled() && mediaType === MediaType.VIDEO) {
216 214
 
217 215
                         // eslint-disable-next-line max-depth
218 216
                         if (streamId === '-' || !streamId) {
@@ -260,7 +258,7 @@ export default class LocalSdpMunger {
260 258
                 const msidExists = mediaSection.ssrcs
261 259
                     .find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');
262 260
 
263
-                if (!msidExists) {
261
+                if (!msidExists && trackId) {
264 262
                     const generatedMsid = this._generateMsidAttribute(mediaType, trackId);
265 263
 
266 264
                     mediaSection.ssrcs.push({
@@ -327,9 +325,13 @@ export default class LocalSdpMunger {
327 325
             this._injectSourceNames(audioMLine);
328 326
         }
329 327
 
330
-        const videoMLine = transformer.selectMedia(MediaType.VIDEO)?.[0];
328
+        const videoMlines = transformer.selectMedia(MediaType.VIDEO);
329
+
330
+        if (!FeatureFlags.isMultiStreamSupportEnabled()) {
331
+            videoMlines.splice(1);
332
+        }
331 333
 
332
-        if (videoMLine) {
334
+        for (const videoMLine of videoMlines) {
333 335
             this._transformMediaIdentifiers(videoMLine);
334 336
             this._injectSourceNames(videoMLine);
335 337
         }
@@ -364,13 +366,15 @@ export default class LocalSdpMunger {
364 366
 
365 367
         for (const source of sources) {
366 368
             const nameExists = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'name');
369
+            const msid = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'msid')?.value;
370
+            const trackIndex = msid ? msid.split('-')[2] : null;
367 371
 
368 372
             if (!nameExists) {
369 373
                 // Inject source names as a=ssrc:3124985624 name:endpointA-v0
370 374
                 mediaSection.ssrcs.push({
371 375
                     id: source,
372 376
                     attribute: 'name',
373
-                    value: getSourceNameForJitsiTrack(this.localEndpointId, mediaType, 0)
377
+                    value: getSourceNameForJitsiTrack(this.localEndpointId, mediaType, trackIndex)
374 378
                 });
375 379
             }
376 380
         }

+ 9
- 4
modules/xmpp/JingleSessionPC.js Переглянути файл

@@ -2020,9 +2020,7 @@ export default class JingleSessionPC extends JingleSession {
2020 2020
      * otherwise.
2021 2021
      */
2022 2022
     addTrack(localTrack) {
2023
-        if (!FeatureFlags.isMultiStreamSupportEnabled()
2024
-            || !this.usesUnifiedPlan
2025
-            || localTrack.type !== MediaType.VIDEO) {
2023
+        if (!FeatureFlags.isMultiStreamSupportEnabled() || localTrack.type !== MediaType.VIDEO) {
2026 2024
             return Promise.reject(new Error('Multiple tracks of a given media type are not supported'));
2027 2025
         }
2028 2026
 
@@ -2031,7 +2029,14 @@ export default class JingleSessionPC extends JingleSession {
2031 2029
 
2032 2030
             // Add a new transceiver by adding a new mline in the remote description.
2033 2031
             remoteSdp.addMlineForNewLocalSource(MediaType.VIDEO);
2034
-            this._renegotiate(remoteSdp.raw)
2032
+
2033
+            // Always initiate a responder renegotiate since the new m-line is added to remote SDP.
2034
+            const remoteDescription = new RTCSessionDescription({
2035
+                type: 'offer',
2036
+                sdp: remoteSdp.raw
2037
+            });
2038
+
2039
+            this._responderRenegotiate(remoteDescription)
2035 2040
                 .then(() => finishedCallback(), error => finishedCallback(error));
2036 2041
         };
2037 2042
 

+ 7
- 0
modules/xmpp/xmpp.js Переглянути файл

@@ -9,6 +9,7 @@ import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
9 9
 import { XMPPEvents } from '../../service/xmpp/XMPPEvents';
10 10
 import browser from '../browser';
11 11
 import { E2EEncryption } from '../e2ee/E2EEncryption';
12
+import FeatureFlags from '../flags/FeatureFlags';
12 13
 import Statistics from '../statistics/statistics';
13 14
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
14 15
 import Listenable from '../util/Listenable';
@@ -249,6 +250,12 @@ export default class XMPP extends Listenable {
249 250
         if (E2EEncryption.isSupported(this.options)) {
250 251
             this.caps.addFeature(FEATURE_E2EE, false, true);
251 252
         }
253
+
254
+        // Advertise source-name signaling when the endpoint supports it.
255
+        if (FeatureFlags.isSourceNameSignalingEnabled()) {
256
+            logger.info('Source-name signaling is enabled');
257
+            this.caps.addFeature('http://jitsi.org/source-name');
258
+        }
252 259
     }
253 260
 
254 261
     /**

+ 1
- 0
types/auto/modules/flags/FeatureFlags.d.ts Переглянути файл

@@ -12,6 +12,7 @@ declare class FeatureFlags {
12 12
     init(flags: any): void;
13 13
     _sourceNameSignaling: boolean;
14 14
     _sendMultipleVideoStreams: boolean;
15
+    _usesUnifiedPlan: any;
15 16
     /**
16 17
      * Checks if multiple local video streams support is enabled.
17 18
      *

Завантаження…
Відмінити
Зберегти