Browse Source

Merge pull request #504 from jitsi/misc_session_fixes

Misc session fixes
dev1
Saúl Ibarra Corretgé 7 years ago
parent
commit
05a30aaae1

+ 47
- 42
JitsiConference.js View File

6
 import { getLogger } from 'jitsi-meet-logger';
6
 import { getLogger } from 'jitsi-meet-logger';
7
 import GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';
7
 import GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';
8
 import EventEmitter from 'events';
8
 import EventEmitter from 'events';
9
-import * as JingleSessionState from './modules/xmpp/JingleSessionState';
10
 import * as JitsiConferenceErrors from './JitsiConferenceErrors';
9
 import * as JitsiConferenceErrors from './JitsiConferenceErrors';
11
 import JitsiConferenceEventManager from './JitsiConferenceEventManager';
10
 import JitsiConferenceEventManager from './JitsiConferenceEventManager';
12
 import * as JitsiConferenceEvents from './JitsiConferenceEvents';
11
 import * as JitsiConferenceEvents from './JitsiConferenceEvents';
1235
  * @param {JitsiRemoteTrack} removedTrack
1234
  * @param {JitsiRemoteTrack} removedTrack
1236
  */
1235
  */
1237
 JitsiConference.prototype.onRemoteTrackRemoved = function(removedTrack) {
1236
 JitsiConference.prototype.onRemoteTrackRemoved = function(removedTrack) {
1238
-    let consumed = false;
1239
-
1240
     this.getParticipants().forEach(participant => {
1237
     this.getParticipants().forEach(participant => {
1241
         const tracks = participant.getTracks();
1238
         const tracks = participant.getTracks();
1242
 
1239
 
1253
                     this.transcriber.removeTrack(removedTrack);
1250
                     this.transcriber.removeTrack(removedTrack);
1254
                 }
1251
                 }
1255
 
1252
 
1256
-                consumed = true;
1257
-
1258
                 break;
1253
                 break;
1259
             }
1254
             }
1260
         }
1255
         }
1261
     }, this);
1256
     }, this);
1262
-
1263
-    if (!consumed) {
1264
-        if ((this.isP2PActive() && !removedTrack.isP2P)
1265
-             || (!this.isP2PActive() && removedTrack.isP2P)) {
1266
-            // A remote track can be removed either as a result of
1267
-            // 'source-remove' or the P2P logic which removes remote tracks
1268
-            // explicitly when switching between JVB and P2P connections.
1269
-            // The check above filters out the P2P logic case which should not
1270
-            // result in an error (which just goes over all remote tracks).
1271
-            return;
1272
-        }
1273
-        logger.error(
1274
-            'Failed to match remote track on remove'
1275
-                + ' with any of the participants',
1276
-            removedTrack.getStreamId(),
1277
-            removedTrack.getParticipantId());
1278
-    }
1279
 };
1257
 };
1280
 
1258
 
1281
 /**
1259
 /**
1352
     try {
1330
     try {
1353
         jingleSession.acceptOffer(
1331
         jingleSession.acceptOffer(
1354
             jingleOffer,
1332
             jingleOffer,
1355
-            null /* success */,
1333
+            () => {
1334
+                // If for any reason invite for the JVB session arrived after
1335
+                // the P2P has been established already the media transfer needs
1336
+                // to be turned off here.
1337
+                if (this.isP2PActive() && this.jvbJingleSession) {
1338
+                    this._suspendMediaTransferForJvbConnection();
1339
+                }
1340
+            },
1356
             error => {
1341
             error => {
1357
                 GlobalOnErrorHandler.callErrorHandler(error);
1342
                 GlobalOnErrorHandler.callErrorHandler(error);
1358
                 logger.error(
1343
                 logger.error(
1414
 
1399
 
1415
     // Terminate  the jingle session with a reason
1400
     // Terminate  the jingle session with a reason
1416
     jingleSession.terminate(
1401
     jingleSession.terminate(
1417
-        options && options.reasonTag,
1418
-        options && options.reasonMsg,
1419
         null /* success callback => we don't care */,
1402
         null /* success callback => we don't care */,
1420
         error => {
1403
         error => {
1421
             logger.warn(
1404
             logger.warn(
1422
                 'An error occurred while trying to terminate'
1405
                 'An error occurred while trying to terminate'
1423
                     + ' invalid Jingle session', error);
1406
                     + ' invalid Jingle session', error);
1407
+        }, {
1408
+            reason: options.reasonTag,
1409
+            reasonDescription: options.reasonMsg,
1410
+            sendSessionTerminate: true
1424
         });
1411
         });
1425
 };
1412
 };
1426
 
1413
 
2264
             logger.info(`Will start P2P with: ${jid}`);
2251
             logger.info(`Will start P2P with: ${jid}`);
2265
             this._startP2PSession(jid);
2252
             this._startP2PSession(jid);
2266
         }
2253
         }
2267
-    } else if (isModerator && this.p2pJingleSession && !shouldBeInP2P) {
2254
+    } else if (this.p2pJingleSession && !shouldBeInP2P) {
2268
         logger.info(`Will stop P2P with: ${this.p2pJingleSession.peerjid}`);
2255
         logger.info(`Will stop P2P with: ${this.p2pJingleSession.peerjid}`);
2269
 
2256
 
2270
         // Log that there will be a switch back to the JVB connection
2257
         // Log that there will be a switch back to the JVB connection
2295
 
2282
 
2296
     // Swap remote tracks, but only if the P2P has been fully established
2283
     // Swap remote tracks, but only if the P2P has been fully established
2297
     if (wasP2PEstablished) {
2284
     if (wasP2PEstablished) {
2298
-        this._resumeMediaTransferForJvbConnection();
2285
+        if (this.jvbJingleSession) {
2286
+            this._resumeMediaTransferForJvbConnection();
2287
+        }
2299
 
2288
 
2300
         // Remove remote P2P tracks
2289
         // Remove remote P2P tracks
2301
         this._removeRemoteP2PTracks();
2290
         this._removeRemoteP2PTracks();
2305
     logger.info('Stopping remote stats for P2P connection');
2294
     logger.info('Stopping remote stats for P2P connection');
2306
     this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection);
2295
     this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection);
2307
     logger.info('Stopping CallStats for P2P connection');
2296
     logger.info('Stopping CallStats for P2P connection');
2308
-    this.statistics.stopCallStats(
2309
-        this.p2pJingleSession.peerconnection);
2297
+    this.statistics.stopCallStats(this.p2pJingleSession.peerconnection);
2310
 
2298
 
2311
-    if (JingleSessionState.ENDED !== this.p2pJingleSession.state) {
2312
-        this.p2pJingleSession.terminate(
2313
-            reason ? reason : 'success',
2314
-            reasonDescription
2315
-                ? reasonDescription : 'Turing off P2P session',
2316
-            () => {
2317
-                logger.info('P2P session terminate RESULT');
2318
-            },
2319
-            error => {
2320
-                logger.warn(
2299
+    this.p2pJingleSession.terminate(
2300
+        () => {
2301
+            logger.info('P2P session terminate RESULT');
2302
+        },
2303
+        error => {
2304
+            // Because both initiator and responder are simultaneously
2305
+            // terminating their JingleSessions in case of the 'to JVB switch'
2306
+            // when 3rd participant joins, both will dispose their sessions and
2307
+            // reply with 'item-not-found' (see strophe.jingle.js). We don't
2308
+            // want to log this as an error since it's expected behaviour.
2309
+            //
2310
+            // We want them both to terminate, because in case of initiator's
2311
+            // crash the responder would stay in P2P mode until ICE fails which
2312
+            // could take up to 20 seconds.
2313
+            //
2314
+            // NOTE lack of 'reason' is considered as graceful session terminate
2315
+            // where both initiator and responder terminate their sessions
2316
+            // simultaneously.
2317
+            if (reason) {
2318
+                logger.error(
2321
                     'An error occurred while trying to terminate'
2319
                     'An error occurred while trying to terminate'
2322
-                    + ' P2P Jingle session', error);
2323
-            });
2324
-    }
2320
+                        + ' P2P Jingle session', error);
2321
+            }
2322
+        }, {
2323
+            reason: reason ? reason : 'success',
2324
+            reasonDescription: reasonDescription
2325
+                ? reasonDescription : 'Turing off P2P session',
2326
+            sendSessionTerminate: this.room
2327
+                && this.getParticipantById(
2328
+                    Strophe.getResourceFromJid(this.p2pJingleSession.peerjid))
2329
+        });
2325
 
2330
 
2326
     this.p2pJingleSession = null;
2331
     this.p2pJingleSession = null;
2327
 
2332
 

+ 1
- 1
modules/RTC/DataChannels.js View File

49
     const self = this;
49
     const self = this;
50
 
50
 
51
     dataChannel.onopen = function() {
51
     dataChannel.onopen = function() {
52
-        logger.info('Data channel opened by the Videobridge!', dataChannel);
52
+        logger.info('Data channel opened by the Videobridge!');
53
 
53
 
54
         // Code sample for sending string and/or binary data
54
         // Code sample for sending string and/or binary data
55
         // Sends String message to the bridge
55
         // Sends String message to the bridge

+ 38
- 21
modules/RTC/TraceablePeerConnection.js View File

666
     }
666
     }
667
     remoteTracksMap.set(mediaType, remoteTrack);
667
     remoteTracksMap.set(mediaType, remoteTrack);
668
 
668
 
669
-    // FIXME not cool to use RTC's eventEmitter
670
-    this.rtc.eventEmitter.emit(RTCEvents.REMOTE_TRACK_ADDED, remoteTrack);
669
+    this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_ADDED, remoteTrack);
671
 };
670
 };
672
 
671
 
673
 /* eslint-enable max-params */
672
 /* eslint-enable max-params */
728
         return;
727
         return;
729
     }
728
     }
730
 
729
 
731
-    if (!this._removeRemoteTrack(streamId, trackId)) {
730
+    if (!this._removeRemoteTrackById(streamId, trackId)) {
732
         // NOTE this warning is always printed when user leaves the room,
731
         // NOTE this warning is always printed when user leaves the room,
733
         // because we remove remote tracks manually on MUC member left event,
732
         // because we remove remote tracks manually on MUC member left event,
734
         // before the SSRCs are removed by Jicofo. In most cases it is fine to
733
         // before the SSRCs are removed by Jicofo. In most cases it is fine to
800
     return removedTracks;
799
     return removedTracks;
801
 };
800
 };
802
 
801
 
802
+/**
803
+ * Removes and disposes given <tt>JitsiRemoteTrack</tt> instance. Emits
804
+ * {@link RTCEvents.REMOTE_TRACK_REMOVED}.
805
+ * @param {JitsiRemoteTrack} toBeRemoved
806
+ */
807
+TraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {
808
+    toBeRemoved.dispose();
809
+    const participantId = toBeRemoved.getParticipantId();
810
+    const remoteTracksMap = this.remoteTracks.get(participantId);
811
+
812
+    if (!remoteTracksMap) {
813
+        logger.error(
814
+            `removeRemoteTrack: no remote tracks map for ${participantId}`);
815
+    } else if (!remoteTracksMap.delete(toBeRemoved.getType())) {
816
+        logger.error(
817
+            `Failed to remove ${toBeRemoved} - type mapping messed up ?`);
818
+    }
819
+    this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_REMOVED, toBeRemoved);
820
+};
821
+
803
 /**
822
 /**
804
  * Removes and disposes <tt>JitsiRemoteTrack</tt> identified by given stream and
823
  * Removes and disposes <tt>JitsiRemoteTrack</tt> identified by given stream and
805
  * track ids.
824
  * track ids.
810
  * <tt>undefined</tt> if no track matching given stream and track ids was
829
  * <tt>undefined</tt> if no track matching given stream and track ids was
811
  * found.
830
  * found.
812
  */
831
  */
813
-TraceablePeerConnection.prototype._removeRemoteTrack
832
+TraceablePeerConnection.prototype._removeRemoteTrackById
814
 = function(streamId, trackId) {
833
 = function(streamId, trackId) {
815
     const toBeRemoved = this._getRemoteTrackById(streamId, trackId);
834
     const toBeRemoved = this._getRemoteTrackById(streamId, trackId);
816
 
835
 
817
     if (toBeRemoved) {
836
     if (toBeRemoved) {
818
-        toBeRemoved.dispose();
819
-
820
-        const remoteTracksMap
821
-            = this.remoteTracks.get(toBeRemoved.getParticipantId());
822
-
823
-        // If _getRemoteTrackById succeeded it must be a valid value or
824
-        // we're good to crash
825
-        if (!remoteTracksMap.delete(toBeRemoved.getType())) {
826
-            logger.error(
827
-                `Failed to remove ${toBeRemoved} - type mapping messed up ?`);
828
-        }
829
-
830
-        // FIXME not cool to use RTC's eventEmitter
831
-        this.rtc.eventEmitter.emit(
832
-            RTCEvents.REMOTE_TRACK_REMOVED, toBeRemoved);
837
+        this._removeRemoteTrack(toBeRemoved);
833
     }
838
     }
834
 
839
 
835
     return toBeRemoved;
840
     return toBeRemoved;
1435
 
1440
 
1436
             if (localUfrag !== this.localUfrag) {
1441
             if (localUfrag !== this.localUfrag) {
1437
                 this.localUfrag = localUfrag;
1442
                 this.localUfrag = localUfrag;
1438
-                this.rtc.eventEmitter.emit(
1443
+                this.eventEmitter.emit(
1439
                     RTCEvents.LOCAL_UFRAG_CHANGED, this, localUfrag);
1444
                     RTCEvents.LOCAL_UFRAG_CHANGED, this, localUfrag);
1440
             }
1445
             }
1441
             successCallback();
1446
             successCallback();
1508
 
1513
 
1509
             if (remoteUfrag !== this.remoteUfrag) {
1514
             if (remoteUfrag !== this.remoteUfrag) {
1510
                 this.remoteUfrag = remoteUfrag;
1515
                 this.remoteUfrag = remoteUfrag;
1511
-                this.rtc.eventEmitter.emit(
1516
+                this.eventEmitter.emit(
1512
                     RTCEvents.REMOTE_UFRAG_CHANGED, this, remoteUfrag);
1517
                     RTCEvents.REMOTE_UFRAG_CHANGED, this, remoteUfrag);
1513
             }
1518
             }
1514
             successCallback();
1519
             successCallback();
1543
     this.sdpConsistency.clearVideoSsrcCache();
1548
     this.sdpConsistency.clearVideoSsrcCache();
1544
 };
1549
 };
1545
 
1550
 
1551
+/**
1552
+ * Closes underlying WebRTC PeerConnection instance and removes all remote
1553
+ * tracks by emitting {@link RTCEvents.REMOTE_TRACK_REMOVED} for each one of
1554
+ * them.
1555
+ */
1546
 TraceablePeerConnection.prototype.close = function() {
1556
 TraceablePeerConnection.prototype.close = function() {
1547
     this.trace('stop');
1557
     this.trace('stop');
1548
 
1558
 
1552
     this.signalingLayer.off(
1562
     this.signalingLayer.off(
1553
         SignalingEvents.PEER_VIDEO_TYPE_CHANGED, this._peerVideoTypeChanged);
1563
         SignalingEvents.PEER_VIDEO_TYPE_CHANGED, this._peerVideoTypeChanged);
1554
 
1564
 
1565
+    for (const peerTracks of this.remoteTracks.values()) {
1566
+        for (const remoteTrack of peerTracks.values()) {
1567
+            this._removeRemoteTrack(remoteTrack);
1568
+        }
1569
+    }
1570
+    this.remoteTracks.clear();
1571
+
1555
     if (!this.rtc._removePeerConnection(this)) {
1572
     if (!this.rtc._removePeerConnection(this)) {
1556
         logger.error('RTC._removePeerConnection returned false');
1573
         logger.error('RTC._removePeerConnection returned false');
1557
     }
1574
     }

+ 7
- 7
modules/xmpp/JingleSession.js View File

138
      */
138
      */
139
     removeSources(contents) {}
139
     removeSources(contents) {}
140
 
140
 
141
-    /* eslint-disable max-params */
142
-
143
     /**
141
     /**
144
      * Terminates this Jingle session by sending session-terminate
142
      * Terminates this Jingle session by sending session-terminate
145
-     * @param reason XMPP Jingle error condition
146
-     * @param text some meaningful error message
147
      * @param success a callback called once the 'session-terminate' packet has
143
      * @param success a callback called once the 'session-terminate' packet has
148
      * been acknowledged with RESULT.
144
      * been acknowledged with RESULT.
149
      * @param failure a callback called when either timeout occurs or ERROR
145
      * @param failure a callback called when either timeout occurs or ERROR
150
      * response is received.
146
      * response is received.
147
+     * @param {Object} options
148
+     * @param {string} [options.reason] XMPP Jingle error condition
149
+     * @param {string} [options.reasonDescription] some meaningful error message
150
+     * @param {boolean} [options.sendSessionTerminate=true] set to false to skip
151
+     * sending session-terminate. It may not make sense to send it if the XMPP
152
+     * connection has been closed already or if the remote peer has disconnected
151
      */
153
      */
152
-    terminate(reason, text, success, failure) {}
153
-
154
-    /* eslint-enable max-params */
154
+    terminate(success, failure, options) {}
155
 
155
 
156
     /**
156
     /**
157
      * Handles an offer from the remote peer (prepares to accept a session).
157
      * Handles an offer from the remote peer (prepares to accept a session).

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

674
         this.setOfferAnswerCycle(
674
         this.setOfferAnswerCycle(
675
             jingleOffer,
675
             jingleOffer,
676
             () => {
676
             () => {
677
-                this.state = JingleSessionState.ACTIVE;
678
-
679
                 // FIXME we may not care about RESULT packet for session-accept
677
                 // FIXME we may not care about RESULT packet for session-accept
680
                 // then we should either call 'success' here immediately or
678
                 // then we should either call 'success' here immediately or
681
                 // modify sendSessionAccept method to do that
679
                 // modify sendSessionAccept method to do that
697
         if (!this.isInitiator) {
695
         if (!this.isInitiator) {
698
             throw new Error('Trying to invite from the responder session');
696
             throw new Error('Trying to invite from the responder session');
699
         }
697
         }
700
-        for (const localTrack of localTracks) {
701
-            this.peerconnection.addTrack(localTrack);
702
-        }
703
-        this.peerconnection.createOffer(
704
-            this.sendSessionInitiate.bind(this),
705
-            error => logger.error('Failed to create offer', error),
706
-            this.mediaConstraints);
698
+        const workFunction = finishedCallback => {
699
+            for (const localTrack of localTracks) {
700
+                this.peerconnection.addTrack(localTrack);
701
+            }
702
+            this.peerconnection.createOffer(
703
+                sdp => {
704
+                    this.sendSessionInitiate(
705
+                        sdp,
706
+                        finishedCallback,
707
+                        finishedCallback
708
+                    );
709
+                },
710
+                error => {
711
+                    logger.error(
712
+                        'Failed to create an offer',
713
+                        error,
714
+                        this.mediaConstraints);
715
+                    finishedCallback(error);
716
+                },
717
+                this.mediaConstraints);
718
+        };
719
+
720
+        this.modificationQueue.push(
721
+            workFunction,
722
+            error => {
723
+                if (error) {
724
+                    logger.error('invite error', error);
725
+                } else {
726
+                    logger.debug('invite executed - OK');
727
+                }
728
+            });
707
     }
729
     }
708
 
730
 
709
     /**
731
     /**
710
      * Sends 'session-initiate' to the remote peer.
732
      * Sends 'session-initiate' to the remote peer.
711
      * @param {object} sdp the local session description object as defined by
733
      * @param {object} sdp the local session description object as defined by
712
      * the WebRTC standard.
734
      * the WebRTC standard.
735
+     * @param {function} success executed when the operation succeeds.
736
+     * @param {function(error)} failure executed when the operation fails with
737
+     * an error passed as an argument.
713
      * @private
738
      * @private
714
      */
739
      */
715
-    sendSessionInitiate(sdp) {
740
+    sendSessionInitiate(sdp, success, failure) {
716
         logger.log('createdOffer', sdp);
741
         logger.log('createdOffer', sdp);
717
         const sendJingle = () => {
742
         const sendJingle = () => {
718
             let init = $iq({
743
             let init = $iq({
740
                     logger.error('"session-initiate" error', error);
765
                     logger.error('"session-initiate" error', error);
741
                 },
766
                 },
742
                 IQ_TIMEOUT);
767
                 IQ_TIMEOUT);
768
+
769
+            // NOTE the callback is executed immediately as we don't want to
770
+            // wait for the XMPP response which would delay the startup process.
771
+            success();
743
         };
772
         };
744
 
773
 
745
         this.peerconnection.setLocalDescription(
774
         this.peerconnection.setLocalDescription(
746
             sdp, sendJingle,
775
             sdp, sendJingle,
747
             error => {
776
             error => {
748
                 logger.error('session-init setLocalDescription failed', error);
777
                 logger.error('session-init setLocalDescription failed', error);
778
+                failure(error);
749
             }
779
             }
750
         );
780
         );
751
     }
781
     }
761
         this.setOfferAnswerCycle(
791
         this.setOfferAnswerCycle(
762
             jingleAnswer,
792
             jingleAnswer,
763
             () => {
793
             () => {
764
-                this.state = JingleSessionState.ACTIVE;
765
                 logger.info('setAnswer - succeeded');
794
                 logger.info('setAnswer - succeeded');
766
             },
795
             },
767
             error => {
796
             error => {
795
 
824
 
796
             const newRemoteSdp
825
             const newRemoteSdp
797
                 = this._processNewJingleOfferIq(jingleOfferAnswerIq);
826
                 = this._processNewJingleOfferIq(jingleOfferAnswerIq);
827
+            const oldLocalSdp
828
+                = this.peerconnection.localDescription.sdp;
798
 
829
 
799
-            this._renegotiate(newRemoteSdp)
830
+            this._renegotiate(newRemoteSdp.raw)
800
                 .then(() => {
831
                 .then(() => {
832
+                    if (this.state === JingleSessionState.PENDING) {
833
+                        this.state = JingleSessionState.ACTIVE;
834
+                    }
835
+
836
+                    // Old local SDP will be available when we're setting answer
837
+                    // for the first time, but not when offer and it's fine
838
+                    // since we're generating an answer now it will contain all
839
+                    // our SSRCs
840
+                    if (oldLocalSdp) {
841
+                        const newLocalSdp
842
+                            = new SDP(this.peerconnection.localDescription.sdp);
843
+
844
+                        this.notifyMySSRCUpdate(
845
+                            new SDP(oldLocalSdp), newLocalSdp);
846
+                    }
847
+
801
                     finishedCallback();
848
                     finishedCallback();
802
                 }, error => {
849
                 }, error => {
803
                     logger.error(
850
                     logger.error(
1004
             IQ_TIMEOUT);
1051
             IQ_TIMEOUT);
1005
     }
1052
     }
1006
 
1053
 
1007
-    /* eslint-disable max-params */
1008
-
1009
     /**
1054
     /**
1010
      * @inheritDoc
1055
      * @inheritDoc
1011
      */
1056
      */
1012
-    terminate(reason, text, success, failure) {
1013
-        let sessionTerminate = $iq({
1014
-            to: this.peerjid,
1015
-            type: 'set'
1016
-        })
1017
-        .c('jingle', {
1018
-            xmlns: 'urn:xmpp:jingle:1',
1019
-            action: 'session-terminate',
1020
-            initiator: this.initiator,
1021
-            sid: this.sid
1022
-        })
1023
-        .c('reason')
1024
-        .c(reason || 'success');
1025
-
1026
-        if (text) {
1027
-            // eslint-disable-next-line newline-per-chained-call
1028
-            sessionTerminate.up().c('text').t(text);
1057
+    terminate(success, failure, options) {
1058
+        if (this.state === JingleSessionState.ENDED) {
1059
+            return;
1029
         }
1060
         }
1030
 
1061
 
1031
-        // Calling tree() to print something useful
1032
-        sessionTerminate = sessionTerminate.tree();
1033
-        logger.info('Sending session-terminate', sessionTerminate);
1062
+        if (!options || Boolean(options.sendSessionTerminate)) {
1063
+            let sessionTerminate
1064
+                = $iq({
1065
+                    to: this.peerjid,
1066
+                    type: 'set'
1067
+                })
1068
+                .c('jingle', {
1069
+                    xmlns: 'urn:xmpp:jingle:1',
1070
+                    action: 'session-terminate',
1071
+                    initiator: this.initiator,
1072
+                    sid: this.sid
1073
+                })
1074
+                .c('reason')
1075
+                .c((options && options.reason) || 'success');
1076
+
1077
+            if (options && options.reasonDescription) {
1078
+                sessionTerminate.up()
1079
+                    .c('text')
1080
+                    .t(options.reasonDescription);
1081
+            }
1034
 
1082
 
1035
-        this.connection.sendIQ(
1036
-            sessionTerminate,
1037
-            success,
1038
-            this.newJingleErrorHandler(sessionTerminate, failure), IQ_TIMEOUT);
1083
+            // Calling tree() to print something useful
1084
+            sessionTerminate = sessionTerminate.tree();
1085
+            logger.info('Sending session-terminate', sessionTerminate);
1086
+            this.connection.sendIQ(
1087
+                sessionTerminate,
1088
+                success,
1089
+                this.newJingleErrorHandler(sessionTerminate, failure),
1090
+                IQ_TIMEOUT);
1091
+        } else {
1092
+            logger.info(`Skipped sending session-terminate for ${this}`);
1093
+        }
1039
 
1094
 
1040
         // this should result in 'onTerminated' being called by strope.jingle.js
1095
         // this should result in 'onTerminated' being called by strope.jingle.js
1041
         this.connection.jingle.terminate(this.sid);
1096
         this.connection.jingle.terminate(this.sid);
1042
     }
1097
     }
1043
 
1098
 
1044
-    /* eslint-enable max-params */
1045
-
1046
     /**
1099
     /**
1047
      *
1100
      *
1048
      * @param reasonCondition
1101
      * @param reasonCondition
1197
                     ? this._processRemoteAddSource(addOrRemoveSsrcInfo)
1250
                     ? this._processRemoteAddSource(addOrRemoveSsrcInfo)
1198
                     : this._processRemoteRemoveSource(addOrRemoveSsrcInfo);
1251
                     : this._processRemoteRemoveSource(addOrRemoveSsrcInfo);
1199
 
1252
 
1200
-            this._renegotiate(newRemoteSdp)
1253
+            this._renegotiate(newRemoteSdp.raw)
1201
                 .then(() => {
1254
                 .then(() => {
1202
                     const newLocalSdp
1255
                     const newLocalSdp
1203
                         = new SDP(this.peerconnection.localDescription.sdp);
1256
                         = new SDP(this.peerconnection.localDescription.sdp);
1302
 
1355
 
1303
     /**
1356
     /**
1304
      * Do a new o/a flow using the existing remote description
1357
      * Do a new o/a flow using the existing remote description
1305
-     * @param {SDP object} optionalRemoteSdp optional remote sdp
1358
+     * @param {string} [optionalRemoteSdp] optional, raw remote sdp
1306
      *  to use.  If not provided, the remote sdp from the
1359
      *  to use.  If not provided, the remote sdp from the
1307
      *  peerconnection will be used
1360
      *  peerconnection will be used
1308
      * @returns {Promise} promise which resolves when the
1361
      * @returns {Promise} promise which resolves when the
1311
      */
1364
      */
1312
     _renegotiate(optionalRemoteSdp) {
1365
     _renegotiate(optionalRemoteSdp) {
1313
         const remoteSdp
1366
         const remoteSdp
1314
-            = optionalRemoteSdp
1315
-                || new SDP(this.peerconnection.remoteDescription.sdp);
1367
+            = optionalRemoteSdp || this.peerconnection.remoteDescription.sdp;
1368
+
1369
+        if (!remoteSdp) {
1370
+            return Promise.reject(
1371
+                'Can not renegotiate without remote description,'
1372
+                    + `- current state: ${this.state}`);
1373
+        }
1374
+
1316
         const remoteDescription = new RTCSessionDescription({
1375
         const remoteDescription = new RTCSessionDescription({
1317
             type: this.isInitiator ? 'answer' : 'offer',
1376
             type: this.isInitiator ? 'answer' : 'offer',
1318
-            sdp: remoteSdp.raw
1377
+            sdp: remoteSdp
1319
         });
1378
         });
1320
 
1379
 
1321
         return new Promise((resolve, reject) => {
1380
         return new Promise((resolve, reject) => {
1461
                 this.peerconnection.addTrack(newTrack);
1520
                 this.peerconnection.addTrack(newTrack);
1462
             }
1521
             }
1463
 
1522
 
1464
-            if ((oldTrack || newTrack) && oldLocalSdp) {
1523
+            if ((oldTrack || newTrack)
1524
+                && this.state === JingleSessionState.ACTIVE) {
1465
                 this._renegotiate()
1525
                 this._renegotiate()
1466
                     .then(() => {
1526
                     .then(() => {
1467
                         const newLocalSDP
1527
                         const newLocalSDP
1695
      */
1755
      */
1696
     setMediaTransferActive(active) {
1756
     setMediaTransferActive(active) {
1697
         const workFunction = finishedCallback => {
1757
         const workFunction = finishedCallback => {
1758
+
1759
+            // Because the value is modified on the queue it's impossible to
1760
+            // check it's final value reliably prior to submitting the task.
1761
+            // The rule here is that the last submitted state counts.
1762
+            // Check the value here to avoid unnecessary renegotiation cycle.
1763
+            if (this.mediaTransferActive === active) {
1764
+                finishedCallback();
1765
+
1766
+                return;
1767
+            }
1698
             this.mediaTransferActive = active;
1768
             this.mediaTransferActive = active;
1699
             if (this.peerconnection) {
1769
             if (this.peerconnection) {
1700
                 this.peerconnection.setMediaTransferActive(
1770
                 this.peerconnection.setMediaTransferActive(
1844
             // FIXME: Maybe we can include part of the session object
1914
             // FIXME: Maybe we can include part of the session object
1845
             // error.session = this;
1915
             // error.session = this;
1846
 
1916
 
1847
-            logger.error('Jingle error', error);
1848
             if (failureCb) {
1917
             if (failureCb) {
1849
                 failureCb(error);
1918
                 failureCb(error);
1919
+            } else {
1920
+                logger.error('Jingle error', error);
1850
             }
1921
             }
1851
         };
1922
         };
1852
     }
1923
     }

+ 0
- 2
modules/xmpp/strophe.jingle.js View File

4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
 
5
 
6
 import JingleSessionPC from './JingleSessionPC';
6
 import JingleSessionPC from './JingleSessionPC';
7
-import * as JingleSessionState from './JingleSessionState';
8
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
7
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
9
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
10
 import RandomUtil from '../util/RandomUtil';
9
 import RandomUtil from '../util/RandomUtil';
188
                     = $(iq).find('>jingle>reason>:first')[0].tagName;
187
                     = $(iq).find('>jingle>reason>:first')[0].tagName;
189
                 reasonText = $(iq).find('>jingle>reason>text').text();
188
                 reasonText = $(iq).find('>jingle>reason>text').text();
190
             }
189
             }
191
-            sess.state = JingleSessionState.ENDED;
192
             this.terminate(sess.sid, reasonCondition, reasonText);
190
             this.terminate(sess.sid, reasonCondition, reasonText);
193
             this.eventEmitter.emit(XMPPEvents.CALL_ENDED,
191
             this.eventEmitter.emit(XMPPEvents.CALL_ENDED,
194
                 sess, reasonCondition, reasonText);
192
                 sess, reasonCondition, reasonText);

Loading…
Cancel
Save