Browse Source

fix(TPC) add muted tracks to TPC but not to the RTCPeerConnection.

Fixes https://github.com/jitsi/jitsi-meet/issues/9988.
tags/v0.0.2
Jaya Allamsetty 4 years ago
parent
commit
e566291864
No account linked to committer's email address
3 changed files with 83 additions and 163 deletions
  1. 23
    142
      modules/RTC/TPCUtils.js
  2. 53
    16
      modules/RTC/TraceablePeerConnection.js
  3. 7
    5
      modules/xmpp/JingleSessionPC.js

+ 23
- 142
modules/RTC/TPCUtils.js View File

@@ -80,34 +80,6 @@ export class TPCUtils {
80 80
         ];
81 81
     }
82 82
 
83
-    /**
84
-     * Returns the transceiver associated with a given RTCRtpSender/RTCRtpReceiver.
85
-     *
86
-     * @param {string} mediaType - type of track associated with the transceiver 'audio' or 'video'.
87
-     * @param {JitsiLocalTrack} localTrack - local track to be used for lookup.
88
-     * @returns {RTCRtpTransceiver}
89
-     */
90
-    _findTransceiver(mediaType, localTrack = null) {
91
-        let transceiver = null;
92
-
93
-        // Check if the local track has been removed from the peerconnection already.
94
-        const trackRemoved = !localTrack
95
-            || (localTrack
96
-                && browser.doesVideoMuteByStreamRemove()
97
-                && localTrack.isVideoTrack()
98
-                && localTrack.isMuted());
99
-
100
-        if (trackRemoved) {
101
-            transceiver = this.pc.peerconnection.getTransceivers()
102
-                .find(t => t.receiver?.track?.kind === mediaType);
103
-        } else if (localTrack) {
104
-            transceiver = this.pc.peerconnection.getTransceivers()
105
-                .find(t => t.sender?.track?.id === localTrack.getTrackId());
106
-        }
107
-
108
-        return transceiver;
109
-    }
110
-
111 83
     /**
112 84
      * Obtains stream encodings that need to be configured on the given track based
113 85
      * on the track media type and the simulcast setting.
@@ -169,6 +141,21 @@ export class TPCUtils {
169 141
         });
170 142
     }
171 143
 
144
+    /**
145
+     * Returns the transceiver associated with a given RTCRtpSender/RTCRtpReceiver.
146
+     *
147
+     * @param {string} mediaType - type of track associated with the transceiver 'audio' or 'video'.
148
+     * @param {JitsiLocalTrack} localTrack - local track to be used for lookup.
149
+     * @returns {RTCRtpTransceiver}
150
+     */
151
+    findTransceiver(mediaType, localTrack = null) {
152
+        const transceiver = localTrack?.track && localTrack.getOriginalStream()
153
+            ? this.pc.peerconnection.getTransceivers().find(t => t.sender?.track?.id === localTrack.getTrackId())
154
+            : this.pc.peerconnection.getTransceivers().find(t => t.receiver?.track?.kind === mediaType);
155
+
156
+        return transceiver;
157
+    }
158
+
172 159
     /**
173 160
      * Takes in a *unified plan* offer and inserts the appropriate
174 161
      * parameters for adding simulcast receive support.
@@ -271,24 +258,6 @@ export class TPCUtils {
271 258
         }
272 259
     }
273 260
 
274
-    /**
275
-     * Adds a track on the RTCRtpSender as part of the unmute operation.
276
-     * @param {JitsiLocalTrack} localTrack - track to be unmuted.
277
-     * @returns {Promise<void>} - resolved when done.
278
-     */
279
-    addTrackUnmute(localTrack) {
280
-        const mediaType = localTrack.getType();
281
-        const track = localTrack.getTrack();
282
-        const transceiver = this._findTransceiver(mediaType);
283
-
284
-        if (!transceiver) {
285
-            return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));
286
-        }
287
-        logger.debug(`${this.pc} Adding ${localTrack}`);
288
-
289
-        return transceiver.sender.replaceTrack(track);
290
-    }
291
-
292 261
     /**
293 262
      * Returns the calculated active state of the simulcast encodings based on the frame height requested for the send
294 263
      * stream. All the encodings that have a resolution lower than the frame height requested will be enabled.
@@ -368,24 +337,6 @@ export class TPCUtils {
368 337
         return encodingsBitrates;
369 338
     }
370 339
 
371
-    /**
372
-     * Removes the track from the RTCRtpSender as part of the mute operation.
373
-     * @param {JitsiLocalTrack} localTrack - track to be removed.
374
-     * @returns {Promise<void>} - resolved when done.
375
-     */
376
-    removeTrackMute(localTrack) {
377
-        const mediaType = localTrack.getType();
378
-        const transceiver = this._findTransceiver(mediaType, localTrack);
379
-
380
-        if (!transceiver) {
381
-            return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));
382
-        }
383
-
384
-        logger.debug(`${this.pc} Removing ${localTrack}`);
385
-
386
-        return transceiver.sender.replaceTrack(null);
387
-    }
388
-
389 340
     /**
390 341
      * Replaces the existing track on a RTCRtpSender with the given track.
391 342
      * @param {JitsiLocalTrack} oldTrack - existing track on the sender that needs to be removed.
@@ -393,86 +344,16 @@ export class TPCUtils {
393 344
      * @returns {Promise<void>} - resolved when done.
394 345
      */
395 346
     replaceTrack(oldTrack, newTrack) {
396
-        if (oldTrack && newTrack) {
397
-            const mediaType = newTrack.getType();
398
-            const stream = newTrack.getOriginalStream();
399
-
400
-            // Ignore cases when the track is replaced while the device is in a muted state,like
401
-            // replacing camera when video muted or replacing mic when audio muted. These JitsiLocalTracks
402
-            // do not have a mediastream attached. Replace track will be called again when the device is
403
-            // unmuted and the track will be replaced on the peerconnection then.
404
-            if (!stream) {
405
-                this.pc.localTracks.delete(oldTrack.rtcId);
406
-                this.pc.localTracks.set(newTrack.rtcId, newTrack);
407
-
408
-                return Promise.resolve();
409
-            }
410
-
411
-            const transceiver = this._findTransceiver(mediaType, oldTrack);
412
-            const track = newTrack.getTrack();
347
+        const mediaType = newTrack?.getType() ?? oldTrack?.getType();
348
+        const transceiver = this.findTransceiver(mediaType, oldTrack);
349
+        const track = newTrack?.getTrack() ?? null;
413 350
 
414
-            if (!transceiver) {
415
-                return Promise.reject(new Error('replace track failed'));
416
-            }
417
-            logger.debug(`${this.pc} Replacing ${oldTrack} with ${newTrack}`);
418
-
419
-            return transceiver.sender.replaceTrack(track)
420
-                .then(() => {
421
-                    const ssrc = this.pc.localSSRCs.get(oldTrack.rtcId);
422
-
423
-                    this.pc.localTracks.delete(oldTrack.rtcId);
424
-                    this.pc.localSSRCs.delete(oldTrack.rtcId);
425
-                    this.pc._addedStreams = this.pc._addedStreams.filter(s => s !== stream);
426
-                    this.pc.localTracks.set(newTrack.rtcId, newTrack);
427
-
428
-                    this.pc._addedStreams.push(stream);
429
-                    this.pc.localSSRCs.set(newTrack.rtcId, ssrc);
430
-                });
431
-        } else if (oldTrack && !newTrack) {
432
-            return this.removeTrackMute(oldTrack)
433
-                .then(() => {
434
-                    const mediaType = oldTrack.getType();
435
-                    const transceiver = this._findTransceiver(mediaType);
436
-
437
-                    // Change the direction on the transceiver to 'recvonly' so that a 'removetrack'
438
-                    // is fired on the associated media stream on the remote peer.
439
-                    if (transceiver) {
440
-                        transceiver.direction = MediaDirection.RECVONLY;
441
-                    }
442
-
443
-                    // Remove the old track from the list of local tracks.
444
-                    this.pc.localTracks.delete(oldTrack.rtcId);
445
-                    this.pc.localSSRCs.delete(oldTrack.rtcId);
446
-                });
447
-        } else if (newTrack && !oldTrack) {
448
-            return this.addTrackUnmute(newTrack)
449
-                .then(() => {
450
-                    const mediaType = newTrack.getType();
451
-                    const transceiver = this._findTransceiver(mediaType, newTrack);
452
-
453
-                    // Change the direction on the transceiver back to 'sendrecv' so that a 'track'
454
-                    // event is fired on the remote peer.
455
-                    if (transceiver) {
456
-                        transceiver.direction = MediaDirection.SENDRECV;
457
-                    }
458
-
459
-                    // Avoid configuring the encodings on Chromium/Safari until simulcast is configured
460
-                    // for the newly added track using SDP munging which happens during the renegotiation.
461
-                    const promise = browser.usesSdpMungingForSimulcast()
462
-                        ? Promise.resolve()
463
-                        : this.setEncodings(newTrack);
464
-
465
-                    return promise
466
-                        .then(() => {
467
-                            // Add the new track to the list of local tracks.
468
-                            this.pc.localTracks.set(newTrack.rtcId, newTrack);
469
-                        });
470
-                });
351
+        if (!transceiver) {
352
+            return Promise.reject(new Error('replace track failed'));
471 353
         }
354
+        logger.debug(`${this.pc} Replacing ${oldTrack} with ${newTrack}`);
472 355
 
473
-        logger.info(`${this.pc} TPCUtils.replaceTrack called with no new track and no old track`);
474
-
475
-        return Promise.resolve();
356
+        return transceiver.sender.replaceTrack(track);
476 357
     }
477 358
 
478 359
     /**
@@ -496,7 +377,7 @@ export class TPCUtils {
496 377
      */
497 378
     setEncodings(track) {
498 379
         const mediaType = track.getType();
499
-        const transceiver = this._findTransceiver(mediaType, track);
380
+        const transceiver = this.findTransceiver(mediaType, track);
500 381
         const parameters = transceiver?.sender?.getParameters();
501 382
 
502 383
         // Resolve if the encodings are not available yet. This happens immediately after the track is added to the

+ 53
- 16
modules/RTC/TraceablePeerConnection.js View File

@@ -1698,19 +1698,21 @@ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false
1698 1698
     }
1699 1699
 
1700 1700
     this.localTracks.set(rtcId, track);
1701
+    const webrtcStream = track.getOriginalStream();
1701 1702
 
1702 1703
     if (this._usesUnifiedPlan) {
1703
-        try {
1704
-            this.tpcUtils.addTrack(track, isInitiator);
1705
-        } catch (error) {
1706
-            logger.error(`${this} Adding track=${track} failed: ${error?.message}`);
1704
+        logger.debug(`${this} TPC.addTrack using unified plan`);
1705
+        if (webrtcStream) {
1706
+            try {
1707
+                this.tpcUtils.addTrack(track, isInitiator);
1708
+            } catch (error) {
1709
+                logger.error(`${this} Adding track=${track} failed: ${error?.message}`);
1707 1710
 
1708
-            return Promise.reject(error);
1711
+                return Promise.reject(error);
1712
+            }
1709 1713
         }
1710 1714
     } else {
1711 1715
         // Use addStream API for the plan-b case.
1712
-        const webrtcStream = track.getOriginalStream();
1713
-
1714 1716
         if (webrtcStream) {
1715 1717
             this._addStream(webrtcStream);
1716 1718
 
@@ -1753,7 +1755,7 @@ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false
1753 1755
 
1754 1756
     // On Firefox, the encodings have to be configured on the sender only after the transceiver is created.
1755 1757
     if (browser.isFirefox()) {
1756
-        promiseChain = promiseChain.then(() => this.tpcUtils.setEncodings(track));
1758
+        promiseChain = promiseChain.then(() => webrtcStream && this.tpcUtils.setEncodings(track));
1757 1759
     }
1758 1760
 
1759 1761
     return promiseChain;
@@ -1761,19 +1763,21 @@ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false
1761 1763
 
1762 1764
 /**
1763 1765
  * Adds local track as part of the unmute operation.
1764
- * @param {JitsiLocalTrack} track the track to be added as part of the unmute
1765
- * operation
1766
+ * @param {JitsiLocalTrack} track the track to be added as part of the unmute operation.
1767
+ *
1766 1768
  * @return {Promise<boolean>} Promise that resolves to true if the underlying PeerConnection's
1767 1769
  * state has changed and renegotiation is required, false if no renegotiation is needed or
1768 1770
  * Promise is rejected when something goes wrong.
1769 1771
  */
1770 1772
 TraceablePeerConnection.prototype.addTrackUnmute = function(track) {
1773
+    logger.info(`${this} Adding track=${track} as unmute`);
1774
+
1771 1775
     if (!this._assertTrackBelongs('addTrackUnmute', track)) {
1772 1776
         // Abort
1777
+
1773 1778
         return Promise.reject('Track not found on the peerconnection');
1774 1779
     }
1775 1780
 
1776
-    logger.info(`${this} Adding track=${track} as unmute`);
1777 1781
     const webRtcStream = track.getOriginalStream();
1778 1782
 
1779 1783
     if (!webRtcStream) {
@@ -1783,7 +1787,7 @@ TraceablePeerConnection.prototype.addTrackUnmute = function(track) {
1783 1787
     }
1784 1788
 
1785 1789
     if (this._usesUnifiedPlan) {
1786
-        return this.tpcUtils.addTrackUnmute(track);
1790
+        return this.tpcUtils.replaceTrack(null, track).then(() => this.isP2P);
1787 1791
     }
1788 1792
 
1789 1793
     this._addStream(webRtcStream);
@@ -1825,7 +1829,7 @@ TraceablePeerConnection.prototype._removeStream = function(mediaStream) {
1825 1829
 TraceablePeerConnection.prototype._assertTrackBelongs = function(
1826 1830
         methodName,
1827 1831
         localTrack) {
1828
-    const doesBelong = this.localTracks.has(localTrack.rtcId);
1832
+    const doesBelong = this.localTracks.has(localTrack?.rtcId);
1829 1833
 
1830 1834
     if (!doesBelong) {
1831 1835
         logger.error(`${this} ${methodName}: track=${localTrack} does not belong to pc`);
@@ -1979,11 +1983,44 @@ TraceablePeerConnection.prototype.findSenderForTrack = function(track) {
1979 1983
  * Otherwise no renegotiation is needed.
1980 1984
  */
1981 1985
 TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
1986
+    if (!(oldTrack || newTrack)) {
1987
+        logger.info(`${this} replaceTrack called with no new track and no old track`);
1988
+
1989
+        return Promise.resolve();
1990
+    }
1991
+
1982 1992
     if (this._usesUnifiedPlan) {
1983 1993
         logger.debug(`${this} TPC.replaceTrack using unified plan`);
1994
+        const stream = newTrack?.getOriginalStream();
1995
+        const promise = newTrack && !stream
1996
+
1997
+            // Ignore cases when the track is replaced while the device is in a muted state.
1998
+            // The track will be replaced again on the peerconnection when the user unmutes.
1999
+            ? Promise.resolve()
2000
+            : this.tpcUtils.replaceTrack(oldTrack, newTrack);
2001
+
2002
+        return promise
2003
+            .then(() => {
2004
+                oldTrack && this.localTracks.delete(oldTrack.rtcId);
2005
+                newTrack && this.localTracks.set(newTrack.rtcId, newTrack);
1984 2006
 
1985
-        // Renegotiate only in the case of P2P. We rely on 'negotiationeeded' to be fired for JVB.
1986
-        return this.tpcUtils.replaceTrack(oldTrack, newTrack).then(() => this.isP2P);
2007
+                const mediaType = newTrack?.getType() ?? oldTrack?.getType();
2008
+                const transceiver = this.tpcUtils.findTransceiver(mediaType, oldTrack);
2009
+
2010
+                if (transceiver) {
2011
+                    // Set the transceiver direction.
2012
+                    transceiver.direction = newTrack ? MediaDirection.SENDRECV : MediaDirection.RECVONLY;
2013
+                }
2014
+
2015
+                // Avoid configuring the encodings on Chromium/Safari until simulcast is configured
2016
+                // for the newly added track using SDP munging which happens during the renegotiation.
2017
+                const configureEncodingsPromise = browser.usesSdpMungingForSimulcast() || !newTrack
2018
+                    ? Promise.resolve()
2019
+                    : this.tpcUtils.setEncodings(newTrack);
2020
+
2021
+                // Renegotiate only in the case of P2P. We rely on 'negotiationeeded' to be fired for JVB.
2022
+                return configureEncodingsPromise.then(() => this.isP2P);
2023
+            });
1987 2024
     }
1988 2025
 
1989 2026
     logger.debug(`${this} TPC.replaceTrack using plan B`);
@@ -2021,7 +2058,7 @@ TraceablePeerConnection.prototype.removeTrackMute = function(localTrack) {
2021 2058
     }
2022 2059
 
2023 2060
     if (this._usesUnifiedPlan) {
2024
-        return this.tpcUtils.removeTrackMute(localTrack);
2061
+        return this.tpcUtils.replaceTrack(localTrack, null);
2025 2062
     }
2026 2063
 
2027 2064
     if (webRtcStream) {

+ 7
- 5
modules/xmpp/JingleSessionPC.js View File

@@ -2278,11 +2278,13 @@ export default class JingleSessionPC extends JingleSession {
2278 2278
                     if (shouldRenegotiate && oldLocalSDP && tpc.remoteDescription.sdp) {
2279 2279
                         this._renegotiate()
2280 2280
                             .then(() => {
2281
-                                // The results are ignored, as this check failure is not
2282
-                                // enough to fail the whole operation. It will log
2283
-                                // an error inside.
2284
-                                this._verifyNoSSRCChanged(
2285
-                                    operationName, new SDP(oldLocalSDP));
2281
+                                // The results are ignored, as this check failure is not enough to fail the whole
2282
+                                // operation. It will log an error inside for plan-b.
2283
+                                !this.usesUnifiedPlan && this._verifyNoSSRCChanged(operationName, new SDP(oldLocalSDP));
2284
+                                const newLocalSdp = tpc.localDescription.sdp;
2285
+
2286
+                                // Signal the ssrc if an unmute operation results in a new ssrc being generated.
2287
+                                this.notifyMySSRCUpdate(new SDP(oldLocalSDP), new SDP(newLocalSdp));
2286 2288
                                 finishedCallback();
2287 2289
                             });
2288 2290
                     } else {

Loading…
Cancel
Save