Parcourir la source

fix(multi-stream) Add video tracks sequencially.

If there are more than one video track at pc creation time, add them sequencially to avoid renegotiation loop.
tags/v0.0.2
Jaya Allamsetty il y a 3 ans
Parent
révision
75c87a47cd

+ 2
- 2
JitsiConference.js Voir le fichier

@@ -1102,8 +1102,8 @@ JitsiConference.prototype.addTrack = function(track) {
1102 1102
         if (FeatureFlags.isMultiStreamSupportEnabled() && mediaType === MediaType.VIDEO) {
1103 1103
             const addTrackPromises = [];
1104 1104
 
1105
-            this.p2pJingleSession && addTrackPromises.push(this.p2pJingleSession.addTrack(track));
1106
-            this.jvbJingleSession && addTrackPromises.push(this.jvbJingleSession.addTrack(track));
1105
+            this.p2pJingleSession && addTrackPromises.push(this.p2pJingleSession.addTracks([ track ]));
1106
+            this.jvbJingleSession && addTrackPromises.push(this.jvbJingleSession.addTracks([ track ]));
1107 1107
 
1108 1108
             return Promise.all(addTrackPromises)
1109 1109
                 .then(() => {

+ 68
- 38
modules/xmpp/JingleSessionPC.js Voir le fichier

@@ -984,10 +984,20 @@ export default class JingleSessionPC extends JingleSession {
984 984
         }
985 985
         const workFunction = finishedCallback => {
986 986
             const addTracks = [];
987
-
988
-            for (const localTrack of localTracks) {
989
-                addTracks.push(this.peerconnection.addTrack(localTrack, this.isInitiator));
987
+            const audioTracks = localTracks.filter(track => track.getType() === MediaType.AUDIO);
988
+            const videoTracks = localTracks.filter(track => track.getType() === MediaType.VIDEO);
989
+            let tracks = localTracks;
990
+
991
+            // Add only 1 video track at a time. Adding 2 or more video tracks to the peerconnection at the same time
992
+            // makes the browser go into a renegotiation loop by firing 'negotiationneeded' event after every
993
+            // renegotiation.
994
+            if (FeatureFlags.isMultiStreamSupportEnabled() && videoTracks.length > 1) {
995
+                tracks = [ ...audioTracks, videoTracks[0] ];
996
+            }
997
+            for (const track of tracks) {
998
+                addTracks.push(this.peerconnection.addTrack(track, this.isInitiator));
990 999
             }
1000
+            videoTracks.length && videoTracks.splice(0, 1);
991 1001
 
992 1002
             Promise.all(addTracks)
993 1003
                 .then(() => this.peerconnection.createOffer(this.mediaConstraints))
@@ -997,6 +1007,13 @@ export default class JingleSessionPC extends JingleSession {
997 1007
                     // the transformation chain.
998 1008
                     this.sendSessionInitiate(this.peerconnection.localDescription.sdp);
999 1009
                 })
1010
+                .then(() => {
1011
+                    if (videoTracks.length) {
1012
+                        return this.addTracks(videoTracks);
1013
+                    }
1014
+
1015
+                    return Promise.resolve();
1016
+                })
1000 1017
                 .then(() => finishedCallback(), error => finishedCallback(error));
1001 1018
         };
1002 1019
 
@@ -1109,15 +1126,22 @@ export default class JingleSessionPC extends JingleSession {
1109 1126
     setOfferAnswerCycle(jingleOfferAnswerIq, success, failure, localTracks = []) {
1110 1127
         const workFunction = finishedCallback => {
1111 1128
             const addTracks = [];
1112
-
1113
-            for (const track of localTracks) {
1129
+            const audioTracks = localTracks.filter(track => track.getType() === MediaType.AUDIO);
1130
+            const videoTracks = localTracks.filter(track => track.getType() === MediaType.VIDEO);
1131
+            let tracks = localTracks;
1132
+
1133
+            // Add only 1 video track at a time. Adding 2 or more video tracks to the peerconnection at the same time
1134
+            // makes the browser go into a renegotiation loop by firing 'negotiationneeded' event after every
1135
+            // renegotiation.
1136
+            if (FeatureFlags.isMultiStreamSupportEnabled() && videoTracks.length > 1) {
1137
+                tracks = [ ...audioTracks, videoTracks[0] ];
1138
+            }
1139
+            for (const track of tracks) {
1114 1140
                 addTracks.push(this.peerconnection.addTrack(track, this.isInitiator));
1115 1141
             }
1116
-
1117
-            const newRemoteSdp
1118
-                = this._processNewJingleOfferIq(jingleOfferAnswerIq);
1119
-            const oldLocalSdp
1120
-                = this.peerconnection.localDescription.sdp;
1142
+            videoTracks.length && videoTracks.splice(0, 1);
1143
+            const newRemoteSdp = this._processNewJingleOfferIq(jingleOfferAnswerIq);
1144
+            const oldLocalSdp = this.peerconnection.localDescription.sdp;
1121 1145
 
1122 1146
             const bridgeSession
1123 1147
                 = $(jingleOfferAnswerIq)
@@ -1131,22 +1155,23 @@ export default class JingleSessionPC extends JingleSession {
1131 1155
 
1132 1156
             Promise.all(addTracks)
1133 1157
                 .then(() => this._renegotiate(newRemoteSdp.raw))
1158
+                .then(() => {
1159
+                    if (videoTracks.length) {
1160
+                        return this.addTracks(videoTracks);
1161
+                    }
1162
+
1163
+                    return Promise.resolve();
1164
+                })
1134 1165
                 .then(() => {
1135 1166
                     if (this.state === JingleSessionState.PENDING) {
1136 1167
                         this.state = JingleSessionState.ACTIVE;
1137 1168
 
1138
-                        // #1 Sync up video transfer active/inactive only after
1139
-                        // the initial O/A cycle. We want to adjust the video
1140
-                        // media direction only in the local SDP and the Jingle
1141
-                        // contents direction included in the initial
1142
-                        // offer/answer is mapped to the remote SDP. Jingle
1143
-                        // 'content-modify' IQ is processed in a way that it
1144
-                        // will only modify local SDP when remote peer is no
1145
-                        // longer interested in receiving video content.
1146
-                        // Changing media direction in the remote SDP will mess
1147
-                        // up our SDP translation chain (simulcast, video mute,
1148
-                        // RTX etc.)
1149
-                        //
1169
+                        // #1 Sync up video transfer active/inactive only after the initial O/A cycle. We want to
1170
+                        // adjust the video media direction only in the local SDP and the Jingle contents direction
1171
+                        // included in the initial offer/answer is mapped to the remote SDP. Jingle 'content-modify'
1172
+                        // IQ is processed in a way that it will only modify local SDP when remote peer is no longer
1173
+                        // interested in receiving video content. Changing media direction in the remote SDP will mess
1174
+                        // up our SDP translation chain (simulcast, video mute, RTX etc.)
1150 1175
                         // #2 Sends the max frame height if it was set, before the session-initiate/accept
1151 1176
                         if (this.isP2P
1152 1177
                             && (!this._localVideoActive || this.localRecvMaxFrameHeight)) {
@@ -1154,16 +1179,12 @@ export default class JingleSessionPC extends JingleSession {
1154 1179
                         }
1155 1180
                     }
1156 1181
 
1157
-                    // Old local SDP will be available when we're setting answer
1158
-                    // for the first time, but not when offer and it's fine
1159
-                    // since we're generating an answer now it will contain all
1160
-                    // our SSRCs
1182
+                    // Old local SDP will be available when we're setting answer for the first time, but not when offer
1183
+                    // and it's fine since we're generating an answer now it will contain all our SSRCs.
1161 1184
                     if (oldLocalSdp) {
1162
-                        const newLocalSdp
1163
-                            = new SDP(this.peerconnection.localDescription.sdp);
1185
+                        const newLocalSdp = new SDP(this.peerconnection.localDescription.sdp);
1164 1186
 
1165
-                        this.notifyMySSRCUpdate(
1166
-                            new SDP(oldLocalSdp), newLocalSdp);
1187
+                        this.notifyMySSRCUpdate(new SDP(oldLocalSdp), newLocalSdp);
1167 1188
                     }
1168 1189
                 })
1169 1190
                 .then(() => finishedCallback(), error => finishedCallback(error));
@@ -2015,20 +2036,25 @@ export default class JingleSessionPC extends JingleSession {
2015 2036
      * Adds a new track to the peerconnection. This method needs to be called only when a secondary JitsiLocalTrack is
2016 2037
      * being added to the peerconnection for the first time.
2017 2038
      *
2018
-     * @param {JitsiLocalTrack} localTrack track to be added to the peer connection.
2039
+     * @param {Array<JitsiLocalTrack>} localTracks - Tracks to be added to the peer connection.
2019 2040
      * @returns {Promise<void>} that resolves when the track is successfully added to the peerconnection, rejected
2020 2041
      * otherwise.
2021 2042
      */
2022
-    addTrack(localTrack) {
2023
-        if (!FeatureFlags.isMultiStreamSupportEnabled() || localTrack.type !== MediaType.VIDEO) {
2024
-            return Promise.reject(new Error('Multiple tracks of a given media type are not supported'));
2043
+    addTracks(localTracks = null) {
2044
+        if (!FeatureFlags.isMultiStreamSupportEnabled()
2045
+            || !localTracks?.length
2046
+            || localTracks.find(track => track.getType() !== MediaType.VIDEO)) {
2047
+            return Promise.reject(new Error('Multiple tracks of the given media type are not supported'));
2025 2048
         }
2026 2049
 
2050
+        const replaceTracks = [];
2027 2051
         const workFunction = finishedCallback => {
2028 2052
             const remoteSdp = new SDP(this.peerconnection.peerconnection.remoteDescription.sdp);
2029 2053
 
2030
-            // Add a new transceiver by adding a new mline in the remote description.
2031
-            remoteSdp.addMlineForNewLocalSource(MediaType.VIDEO);
2054
+            // Add transceivers by adding a new mline in the remote description for each track.
2055
+            for (const track of localTracks) {
2056
+                remoteSdp.addMlineForNewLocalSource(track.getType());
2057
+            }
2032 2058
 
2033 2059
             // Always initiate a responder renegotiate since the new m-line is added to remote SDP.
2034 2060
             const remoteDescription = new RTCSessionDescription({
@@ -2052,8 +2078,12 @@ export default class JingleSessionPC extends JingleSession {
2052 2078
                     } else {
2053 2079
                         logger.debug(`${this} renegotiation after addTrack executed - OK`);
2054 2080
 
2055
-                        // Replace the track on the newly generated transceiver.
2056
-                        return this.replaceTrack(null, localTrack)
2081
+                        // Replace the tracks on the newly generated transceivers.
2082
+                        for (const track of localTracks) {
2083
+                            replaceTracks.push(this.replaceTrack(null, track));
2084
+                        }
2085
+
2086
+                        return Promise.all(replaceTracks)
2057 2087
                             .then(() => resolve())
2058 2088
                             .catch(() => reject());
2059 2089
                     }

+ 2
- 2
types/auto/modules/xmpp/JingleSessionPC.d.ts Voir le fichier

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

Chargement…
Annuler
Enregistrer