Browse Source

[eslint] newline-after-var, newline-before-return, newline-per-chained-call

tags/v0.0.2
Lyubo Marinov 8 years ago
parent
commit
4581605a28
61 changed files with 1009 additions and 74 deletions
  1. 3
    0
      .eslintrc.js
  2. 54
    1
      JitsiConference.js
  3. 22
    0
      JitsiConferenceEventManager.js
  4. 1
    0
      JitsiConnection.js
  5. 1
    0
      JitsiMediaDevices.js
  6. 15
    1
      JitsiMeetJS.js
  7. 31
    17
      doc/example/example.js
  8. 1
    0
      modules/DTMF/JitsiDTMFManager.js
  9. 6
    0
      modules/RTC/DataChannels.js
  10. 10
    0
      modules/RTC/JitsiLocalTrack.js
  11. 4
    0
      modules/RTC/JitsiRemoteTrack.js
  12. 7
    0
      modules/RTC/JitsiTrack.js
  13. 32
    1
      modules/RTC/RTC.js
  14. 32
    0
      modules/RTC/RTCBrowserType.js
  15. 3
    0
      modules/RTC/RTCUIHelper.js
  16. 37
    2
      modules/RTC/RTCUtils.js
  17. 25
    2
      modules/RTC/ScreenObtainer.js
  18. 47
    0
      modules/RTC/TraceablePeerConnection.js
  19. 7
    0
      modules/connectivity/ConnectionQuality.js
  20. 4
    0
      modules/connectivity/ParticipantConnectionStatus.js
  21. 9
    0
      modules/settings/Settings.js
  22. 3
    0
      modules/statistics/AnalyticsAdapter.js
  23. 6
    0
      modules/statistics/CallStats.js
  24. 5
    0
      modules/statistics/LocalStatsCollector.js
  25. 31
    0
      modules/statistics/RTPStatsCollector.js
  26. 5
    0
      modules/statistics/statistics.js
  27. 10
    0
      modules/transcription/audioRecorder.js
  28. 10
    0
      modules/transcription/transcriber.js
  29. 1
    0
      modules/transcription/transcriptionServices/AbstractTranscriptionService.js
  30. 10
    0
      modules/transcription/transcriptionServices/SphinxTranscriptionService.js
  31. 2
    0
      modules/util/AuthUtil.js
  32. 1
    0
      modules/util/EventEmitterForwarder.js
  33. 2
    0
      modules/util/GlobalOnErrorHandler.js
  34. 2
    0
      modules/util/RandomUtil.js
  35. 2
    0
      modules/util/ScriptUtil.js
  36. 6
    0
      modules/version/ComponentsVersions.js
  37. 15
    2
      modules/xmpp/Caps.js
  38. 58
    1
      modules/xmpp/ChatRoom.js
  39. 1
    0
      modules/xmpp/JingleSession.js
  40. 120
    11
      modules/xmpp/JingleSessionPC.js
  41. 17
    0
      modules/xmpp/RtxModifier.js
  42. 38
    0
      modules/xmpp/RtxModifier.spec.js
  43. 93
    17
      modules/xmpp/SDP.js
  44. 12
    0
      modules/xmpp/SDPDiffer.js
  45. 49
    2
      modules/xmpp/SDPUtil.js
  46. 2
    0
      modules/xmpp/SDPUtil.spec.js
  47. 7
    0
      modules/xmpp/SdpConsistency.js
  48. 14
    0
      modules/xmpp/SdpTransformUtil.js
  49. 37
    3
      modules/xmpp/moderator.js
  50. 26
    10
      modules/xmpp/recording.js
  51. 13
    0
      modules/xmpp/strophe.emuc.js
  52. 28
    2
      modules/xmpp/strophe.jingle.js
  53. 6
    0
      modules/xmpp/strophe.ping.js
  54. 8
    2
      modules/xmpp/strophe.rayo.js
  55. 2
    0
      modules/xmpp/strophe.util.js
  56. 11
    0
      modules/xmpp/xmpp.js
  57. 1
    0
      service/RTC/Resolutions.js
  58. 1
    0
      service/RTC/VideoType.js
  59. 1
    0
      service/authentication/AuthenticationEvents.js
  60. 1
    0
      service/statistics/constants.js
  61. 1
    0
      service/xmpp/XMPPEvents.js

+ 3
- 0
.eslintrc.js View File

149
         'multiline-ternary': 0,
149
         'multiline-ternary': 0,
150
         'new-cap': 2,
150
         'new-cap': 2,
151
         'new-parens': 2,
151
         'new-parens': 2,
152
+        'newline-after-var': 2,
153
+        'newline-before-return': 2,
154
+        'newline-per-chained-call': 2,
152
         'no-array-constructor': 2,
155
         'no-array-constructor': 2,
153
         'no-bitwise': 2,
156
         'no-bitwise': 2,
154
         'no-continue': 2,
157
         'no-continue': 2,

+ 54
- 1
JitsiConference.js View File

40
         const errmsg
40
         const errmsg
41
             = 'Invalid conference name (no conference name passed or it '
41
             = 'Invalid conference name (no conference name passed or it '
42
                 + 'contains invalid characters like capital letters)!';
42
                 + 'contains invalid characters like capital letters)!';
43
+
43
         logger.error(errmsg);
44
         logger.error(errmsg);
44
         throw new Error(errmsg);
45
         throw new Error(errmsg);
45
     }
46
     }
184
     // leave the conference
185
     // leave the conference
185
     if (this.room) {
186
     if (this.room) {
186
         const room = this.room;
187
         const room = this.room;
188
+
187
         this.room = null;
189
         this.room = null;
190
+
188
         return room.leave().catch(() => {
191
         return room.leave().catch(() => {
189
             // remove all participants because currently the conference won't
192
             // remove all participants because currently the conference won't
190
             // be usable anyway. This is done on success automatically by the
193
             // be usable anyway. This is done on success automatically by the
249
     return new Promise((resolve, reject) => {
252
     return new Promise((resolve, reject) => {
250
         if (!this.isExternalAuthEnabled()) {
253
         if (!this.isExternalAuthEnabled()) {
251
             reject();
254
             reject();
255
+
252
             return;
256
             return;
253
         }
257
         }
254
         if (urlForPopup) {
258
         if (urlForPopup) {
266
  */
270
  */
267
 JitsiConference.prototype.getLocalTracks = function(mediaType) {
271
 JitsiConference.prototype.getLocalTracks = function(mediaType) {
268
     let tracks = [];
272
     let tracks = [];
273
+
269
     if (this.rtc) {
274
     if (this.rtc) {
270
         tracks = this.rtc.getLocalTracks(mediaType);
275
         tracks = this.rtc.getLocalTracks(mediaType);
271
     }
276
     }
277
+
272
     return tracks;
278
     return tracks;
273
 };
279
 };
274
 
280
 
416
         this.transcriber = new Transcriber();
422
         this.transcriber = new Transcriber();
417
         // add all existing local audio tracks to the transcriber
423
         // add all existing local audio tracks to the transcriber
418
         const localAudioTracks = this.getLocalTracks(MediaType.AUDIO);
424
         const localAudioTracks = this.getLocalTracks(MediaType.AUDIO);
425
+
419
         for (const localAudio of localAudioTracks) {
426
         for (const localAudio of localAudioTracks) {
420
             this.transcriber.addTrack(localAudio);
427
             this.transcriber.addTrack(localAudio);
421
         }
428
         }
422
         // and all remote audio tracks
429
         // and all remote audio tracks
423
         const remoteAudioTracks = this.rtc.getRemoteTracks(MediaType.AUDIO);
430
         const remoteAudioTracks = this.rtc.getRemoteTracks(MediaType.AUDIO);
431
+
424
         for (const remoteTrack of remoteAudioTracks) {
432
         for (const remoteTrack of remoteAudioTracks) {
425
             this.transcriber.addTrack(remoteTrack);
433
             this.transcriber.addTrack(remoteTrack);
426
         }
434
         }
427
     }
435
     }
436
+
428
     return this.transcriber;
437
     return this.transcriber;
429
 };
438
 };
430
 
439
 
439
     if (track.isVideoTrack()) {
448
     if (track.isVideoTrack()) {
440
         // Ensure there's exactly 1 local video track in the conference.
449
         // Ensure there's exactly 1 local video track in the conference.
441
         const localVideoTrack = this.rtc.getLocalVideoTrack();
450
         const localVideoTrack = this.rtc.getLocalVideoTrack();
451
+
442
         if (localVideoTrack) {
452
         if (localVideoTrack) {
443
             // Don't be excessively harsh and severe if the API client happens
453
             // Don't be excessively harsh and severe if the API client happens
444
             // to attempt to add the same local video track twice.
454
             // to attempt to add the same local video track twice.
445
             if (track === localVideoTrack) {
455
             if (track === localVideoTrack) {
446
                 return Promise.resolve(track);
456
                 return Promise.resolve(track);
447
             }
457
             }
458
+
448
             return Promise.reject(new Error(
459
             return Promise.reject(new Error(
449
                     'cannot add second video track to the conference'));
460
                     'cannot add second video track to the conference'));
450
 
461
 
538
         // Set up the ssrcHandler for the new track before we add it at the lower levels
549
         // Set up the ssrcHandler for the new track before we add it at the lower levels
539
         newTrack.ssrcHandler = function(conference, ssrcMap) {
550
         newTrack.ssrcHandler = function(conference, ssrcMap) {
540
             const trackSSRCInfo = ssrcMap.get(this.getMSID());
551
             const trackSSRCInfo = ssrcMap.get(this.getMSID());
552
+
541
             if (trackSSRCInfo) {
553
             if (trackSSRCInfo) {
542
                 this._setSSRC(trackSSRCInfo);
554
                 this._setSSRC(trackSSRCInfo);
543
                 conference.rtc.removeListener(
555
                 conference.rtc.removeListener(
548
         this.rtc.addListener(RTCEvents.SENDRECV_STREAMS_CHANGED,
560
         this.rtc.addListener(RTCEvents.SENDRECV_STREAMS_CHANGED,
549
             newTrack.ssrcHandler);
561
             newTrack.ssrcHandler);
550
     }
562
     }
563
+
551
     // Now replace the stream at the lower levels
564
     // Now replace the stream at the lower levels
552
     return this._doReplaceTrack(oldTrack, newTrack)
565
     return this._doReplaceTrack(oldTrack, newTrack)
553
         .then(() => {
566
         .then(() => {
558
                 // Now handle the addition of the newTrack at the JitsiConference level
571
                 // Now handle the addition of the newTrack at the JitsiConference level
559
                 this._setupNewTrack(newTrack);
572
                 this._setupNewTrack(newTrack);
560
             }
573
             }
574
+
561
             return Promise.resolve();
575
             return Promise.resolve();
562
         }, error => Promise.reject(new Error(error)));
576
         }, error => Promise.reject(new Error(error)));
563
 };
577
 };
577
     if (this.jingleSession) {
591
     if (this.jingleSession) {
578
         return this.jingleSession.replaceTrack(oldTrack, newTrack);
592
         return this.jingleSession.replaceTrack(oldTrack, newTrack);
579
     }
593
     }
594
+
580
     return Promise.resolve();
595
     return Promise.resolve();
581
 
596
 
582
 };
597
 };
595
                 d =>
610
                 d =>
596
                     d.kind === `${newTrack.getTrack().kind}input`
611
                     d.kind === `${newTrack.getTrack().kind}input`
597
                         && d.label === newTrack.getTrack().label);
612
                         && d.label === newTrack.getTrack().label);
613
+
598
         if (device) {
614
         if (device) {
599
             Statistics.sendActiveDeviceListEvent(
615
             Statistics.sendActiveDeviceListEvent(
600
                 RTC.getEventDataForActiveDevice(device));
616
                 RTC.getEventDataForActiveDevice(device));
693
     if (!this.jingleSession) {
709
     if (!this.jingleSession) {
694
         logger.warn('The call haven\'t been started. '
710
         logger.warn('The call haven\'t been started. '
695
             + 'Cannot generate ssrc info at the moment!');
711
             + 'Cannot generate ssrc info at the moment!');
712
+
696
         return null;
713
         return null;
697
     }
714
     }
715
+
698
     return this.jingleSession.generateNewStreamSSRCInfo();
716
     return this.jingleSession.generateNewStreamSSRCInfo();
699
 };
717
 };
700
 
718
 
726
     }
744
     }
727
 
745
 
728
     const conference = this;
746
     const conference = this;
747
+
748
+
729
     return new Promise((resolve, reject) => {
749
     return new Promise((resolve, reject) => {
730
         conference.room.lockRoom(
750
         conference.room.lockRoom(
731
             password || '',
751
             password || '',
778
         throw new Error(`Invalid value for lastN: ${lastN}`);
798
         throw new Error(`Invalid value for lastN: ${lastN}`);
779
     }
799
     }
780
     const n = Number(lastN);
800
     const n = Number(lastN);
801
+
781
     if (n < -1) {
802
     if (n < -1) {
782
         throw new RangeError('lastN cannot be smaller than -1');
803
         throw new RangeError('lastN cannot be smaller than -1');
783
     }
804
     }
804
     = function(countHidden = false) {
825
     = function(countHidden = false) {
805
 
826
 
806
         let participants = this.getParticipants();
827
         let participants = this.getParticipants();
828
+
807
         if (!countHidden) {
829
         if (!countHidden) {
808
             participants = participants.filter(p => !p.isHidden());
830
             participants = participants.filter(p => !p.isHidden());
809
         }
831
         }
810
-    // Add one for the local participant.
832
+
833
+        // Add one for the local participant.
811
         return participants.length + 1;
834
         return participants.length + 1;
812
     };
835
     };
813
 
836
 
826
  */
849
  */
827
 JitsiConference.prototype.kickParticipant = function(id) {
850
 JitsiConference.prototype.kickParticipant = function(id) {
828
     const participant = this.getParticipantById(id);
851
     const participant = this.getParticipantById(id);
852
+
829
     if (!participant) {
853
     if (!participant) {
830
         return;
854
         return;
831
     }
855
     }
838
  */
862
  */
839
 JitsiConference.prototype.muteParticipant = function(id) {
863
 JitsiConference.prototype.muteParticipant = function(id) {
840
     const participant = this.getParticipantById(id);
864
     const participant = this.getParticipantById(id);
865
+
841
     if (!participant) {
866
     if (!participant) {
842
         return;
867
         return;
843
     }
868
     }
858
 JitsiConference.prototype.onMemberJoined
883
 JitsiConference.prototype.onMemberJoined
859
     = function(jid, nick, role, isHidden) {
884
     = function(jid, nick, role, isHidden) {
860
         const id = Strophe.getResourceFromJid(jid);
885
         const id = Strophe.getResourceFromJid(jid);
886
+
861
         if (id === 'focus' || this.myUserId() === id) {
887
         if (id === 'focus' || this.myUserId() === id) {
862
             return;
888
             return;
863
         }
889
         }
864
         const participant = new JitsiParticipant(jid, this, nick, isHidden);
890
         const participant = new JitsiParticipant(jid, this, nick, isHidden);
891
+
865
         participant._role = role;
892
         participant._role = role;
866
         this.participants[id] = participant;
893
         this.participants[id] = participant;
867
         this.eventEmitter.emit(JitsiConferenceEvents.USER_JOINED, id, participant);
894
         this.eventEmitter.emit(JitsiConferenceEvents.USER_JOINED, id, participant);
873
 
900
 
874
 JitsiConference.prototype.onMemberLeft = function(jid) {
901
 JitsiConference.prototype.onMemberLeft = function(jid) {
875
     const id = Strophe.getResourceFromJid(jid);
902
     const id = Strophe.getResourceFromJid(jid);
903
+
876
     if (id === 'focus' || this.myUserId() === id) {
904
     if (id === 'focus' || this.myUserId() === id) {
877
         return;
905
         return;
878
     }
906
     }
879
     const participant = this.participants[id];
907
     const participant = this.participants[id];
908
+
880
     delete this.participants[id];
909
     delete this.participants[id];
881
 
910
 
882
     const removedTracks = this.rtc.removeRemoteTracks(id);
911
     const removedTracks = this.rtc.removeRemoteTracks(id);
895
 JitsiConference.prototype.onUserRoleChanged = function(jid, role) {
924
 JitsiConference.prototype.onUserRoleChanged = function(jid, role) {
896
     const id = Strophe.getResourceFromJid(jid);
925
     const id = Strophe.getResourceFromJid(jid);
897
     const participant = this.getParticipantById(id);
926
     const participant = this.getParticipantById(id);
927
+
898
     if (!participant) {
928
     if (!participant) {
899
         return;
929
         return;
900
     }
930
     }
905
 JitsiConference.prototype.onDisplayNameChanged = function(jid, displayName) {
935
 JitsiConference.prototype.onDisplayNameChanged = function(jid, displayName) {
906
     const id = Strophe.getResourceFromJid(jid);
936
     const id = Strophe.getResourceFromJid(jid);
907
     const participant = this.getParticipantById(id);
937
     const participant = this.getParticipantById(id);
938
+
908
     if (!participant) {
939
     if (!participant) {
909
         return;
940
         return;
910
     }
941
     }
927
 JitsiConference.prototype.onRemoteTrackAdded = function(track) {
958
 JitsiConference.prototype.onRemoteTrackAdded = function(track) {
928
     const id = track.getParticipantId();
959
     const id = track.getParticipantId();
929
     const participant = this.getParticipantById(id);
960
     const participant = this.getParticipantById(id);
961
+
930
     if (!participant) {
962
     if (!participant) {
931
         logger.error(`No participant found for id: ${id}`);
963
         logger.error(`No participant found for id: ${id}`);
964
+
932
         return;
965
         return;
933
     }
966
     }
934
 
967
 
940
     }
973
     }
941
 
974
 
942
     const emitter = this.eventEmitter;
975
     const emitter = this.eventEmitter;
976
+
943
     track.addEventListener(
977
     track.addEventListener(
944
         JitsiTrackEvents.TRACK_MUTE_CHANGED,
978
         JitsiTrackEvents.TRACK_MUTE_CHANGED,
945
         () => emitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track));
979
         () => emitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track));
1006
         // Error cause this should never happen unless something is wrong!
1040
         // Error cause this should never happen unless something is wrong!
1007
         const errmsg = `Rejecting session-initiate from non-focus user: ${
1041
         const errmsg = `Rejecting session-initiate from non-focus user: ${
1008
                  jingleSession.peerjid}`;
1042
                  jingleSession.peerjid}`;
1043
+
1009
         GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
1044
         GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
1010
         logger.error(errmsg);
1045
         logger.error(errmsg);
1011
 
1046
 
1032
     }
1067
     }
1033
     // add info whether call is cross-region
1068
     // add info whether call is cross-region
1034
     let crossRegion = null;
1069
     let crossRegion = null;
1070
+
1035
     if (window.jitsiRegionInfo) {
1071
     if (window.jitsiRegionInfo) {
1036
         crossRegion = window.jitsiRegionInfo.CrossRegion;
1072
         crossRegion = window.jitsiRegionInfo.CrossRegion;
1037
     }
1073
     }
1060
          *  problems between sdp-interop and trying to keep the ssrcs
1096
          *  problems between sdp-interop and trying to keep the ssrcs
1061
          *  consistent
1097
          *  consistent
1062
          */
1098
          */
1099
+
1063
         if (localTrack.isVideoTrack() && localTrack.isMuted() && !RTCBrowserType.isFirefox()) {
1100
         if (localTrack.isVideoTrack() && localTrack.isMuted() && !RTCBrowserType.isFirefox()) {
1064
             /**
1101
             /**
1065
              * Handles issues when the stream is added before the peerconnection
1102
              * Handles issues when the stream is added before the peerconnection
1152
     // will learn what their SSRC from the new PeerConnection which will be
1189
     // will learn what their SSRC from the new PeerConnection which will be
1153
     // created on incoming call event.
1190
     // created on incoming call event.
1154
     const self = this;
1191
     const self = this;
1192
+
1155
     this.getLocalTracks().forEach(localTrack => {
1193
     this.getLocalTracks().forEach(localTrack => {
1156
         // Reset SSRC as it will no longer be valid
1194
         // Reset SSRC as it will no longer be valid
1157
         localTrack._setSSRC(null);
1195
         localTrack._setSSRC(null);
1209
     if (!this.dtmfManager) {
1247
     if (!this.dtmfManager) {
1210
         if (!this.jingleSession) {
1248
         if (!this.jingleSession) {
1211
             logger.warn('cannot sendTones: no jingle session');
1249
             logger.warn('cannot sendTones: no jingle session');
1250
+
1212
             return;
1251
             return;
1213
         }
1252
         }
1214
 
1253
 
1215
         const peerConnection = this.jingleSession.peerconnection;
1254
         const peerConnection = this.jingleSession.peerconnection;
1255
+
1216
         if (!peerConnection) {
1256
         if (!peerConnection) {
1217
             logger.warn('cannot sendTones: no peer connection');
1257
             logger.warn('cannot sendTones: no peer connection');
1258
+
1218
             return;
1259
             return;
1219
         }
1260
         }
1220
 
1261
 
1221
         const localAudio = this.getLocalAudioTrack();
1262
         const localAudio = this.getLocalAudioTrack();
1263
+
1222
         if (!localAudio) {
1264
         if (!localAudio) {
1223
             logger.warn('cannot sendTones: no local audio stream');
1265
             logger.warn('cannot sendTones: no local audio stream');
1266
+
1224
             return;
1267
             return;
1225
         }
1268
         }
1226
         this.dtmfManager = new JitsiDTMFManager(localAudio, peerConnection);
1269
         this.dtmfManager = new JitsiDTMFManager(localAudio, peerConnection);
1236
     if (this.room) {
1279
     if (this.room) {
1237
         return this.room.isRecordingSupported();
1280
         return this.room.isRecordingSupported();
1238
     }
1281
     }
1282
+
1239
     return false;
1283
     return false;
1240
 };
1284
 };
1241
 
1285
 
1276
     if (this.room) {
1320
     if (this.room) {
1277
         return this.room.isSIPCallingSupported();
1321
         return this.room.isSIPCallingSupported();
1278
     }
1322
     }
1323
+
1279
     return false;
1324
     return false;
1280
 };
1325
 };
1281
 
1326
 
1287
     if (this.room) {
1332
     if (this.room) {
1288
         return this.room.dial(number);
1333
         return this.room.dial(number);
1289
     }
1334
     }
1335
+
1290
     return new Promise((resolve, reject) => {
1336
     return new Promise((resolve, reject) => {
1291
         reject(new Error('The conference is not created yet!'));
1337
         reject(new Error('The conference is not created yet!'));
1292
     });
1338
     });
1299
     if (this.room) {
1345
     if (this.room) {
1300
         return this.room.hangup();
1346
         return this.room.hangup();
1301
     }
1347
     }
1348
+
1302
     return new Promise((resolve, reject) => {
1349
     return new Promise((resolve, reject) => {
1303
         reject(new Error('The conference is not created yet!'));
1350
         reject(new Error('The conference is not created yet!'));
1304
     });
1351
     });
1311
     if (this.room) {
1358
     if (this.room) {
1312
         return this.room.getPhoneNumber();
1359
         return this.room.getPhoneNumber();
1313
     }
1360
     }
1361
+
1314
     return null;
1362
     return null;
1315
 };
1363
 };
1316
 
1364
 
1321
     if (this.room) {
1369
     if (this.room) {
1322
         return this.room.getPhonePin();
1370
         return this.room.getPhonePin();
1323
     }
1371
     }
1372
+
1324
     return null;
1373
     return null;
1325
 };
1374
 };
1326
 
1375
 
1332
     if (this.jingleSession) {
1381
     if (this.jingleSession) {
1333
         return this.jingleSession.getIceConnectionState();
1382
         return this.jingleSession.getIceConnectionState();
1334
     }
1383
     }
1384
+
1335
     return null;
1385
     return null;
1336
 };
1386
 };
1337
 
1387
 
1386
     const data = this.xmpp.getJingleLog();
1436
     const data = this.xmpp.getJingleLog();
1387
 
1437
 
1388
     const metadata = {};
1438
     const metadata = {};
1439
+
1389
     metadata.time = new Date();
1440
     metadata.time = new Date();
1390
     metadata.url = window.location.href;
1441
     metadata.url = window.location.href;
1391
     metadata.ua = navigator.userAgent;
1442
     metadata.ua = navigator.userAgent;
1392
 
1443
 
1393
     const log = this.xmpp.getXmppLog();
1444
     const log = this.xmpp.getXmppLog();
1445
+
1394
     if (log) {
1446
     if (log) {
1395
         metadata.xmpp = log;
1447
         metadata.xmpp = log;
1396
     }
1448
     }
1446
  */
1498
  */
1447
 JitsiConference.prototype._onTrackAttach = function(track, container) {
1499
 JitsiConference.prototype._onTrackAttach = function(track, container) {
1448
     const ssrc = track.getSSRC();
1500
     const ssrc = track.getSSRC();
1501
+
1449
     if (!container.id || !ssrc) {
1502
     if (!container.id || !ssrc) {
1450
         return;
1503
         return;
1451
     }
1504
     }

+ 22
- 0
JitsiConferenceEventManager.js View File

37
 JitsiConferenceEventManager.prototype.setupChatRoomListeners = function() {
37
 JitsiConferenceEventManager.prototype.setupChatRoomListeners = function() {
38
     const conference = this.conference;
38
     const conference = this.conference;
39
     const chatRoom = conference.room;
39
     const chatRoom = conference.room;
40
+
40
     this.chatRoomForwarder = new EventEmitterForwarder(chatRoom,
41
     this.chatRoomForwarder = new EventEmitterForwarder(chatRoom,
41
         this.conference.eventEmitter);
42
         this.conference.eventEmitter);
42
 
43
 
72
 
73
 
73
             Object.keys(chatRoom.connectionTimes).forEach(key => {
74
             Object.keys(chatRoom.connectionTimes).forEach(key => {
74
                 const value = chatRoom.connectionTimes[key];
75
                 const value = chatRoom.connectionTimes[key];
76
+
75
                 Statistics.analytics.sendEvent(`conference.${key}`, {value});
77
                 Statistics.analytics.sendEvent(`conference.${key}`, {value});
76
             });
78
             });
77
             Object.keys(chatRoom.xmpp.connectionTimes).forEach(key => {
79
             Object.keys(chatRoom.xmpp.connectionTimes).forEach(key => {
78
                 const value = chatRoom.xmpp.connectionTimes[key];
80
                 const value = chatRoom.xmpp.connectionTimes[key];
81
+
79
                 Statistics.analytics.sendEvent(`xmpp.${key}`, {value});
82
                 Statistics.analytics.sendEvent(`xmpp.${key}`, {value});
80
             });
83
             });
81
         });
84
         });
154
 
157
 
155
     const eventLogHandler
158
     const eventLogHandler
156
         = reason => Statistics.sendEventToAll(`conference.error.${reason}`);
159
         = reason => Statistics.sendEventToAll(`conference.error.${reason}`);
160
+
157
     chatRoom.addListener(XMPPEvents.SESSION_ACCEPT_TIMEOUT,
161
     chatRoom.addListener(XMPPEvents.SESSION_ACCEPT_TIMEOUT,
158
         eventLogHandler.bind(null, 'sessionAcceptTimeout'));
162
         eventLogHandler.bind(null, 'sessionAcceptTimeout'));
159
 
163
 
185
 
189
 
186
     chatRoom.setParticipantPropertyListener((node, from) => {
190
     chatRoom.setParticipantPropertyListener((node, from) => {
187
         const participant = conference.getParticipantById(from);
191
         const participant = conference.getParticipantById(from);
192
+
188
         if (!participant) {
193
         if (!participant) {
189
             return;
194
             return;
190
         }
195
         }
229
                         id: 'recorder_status',
234
                         id: 'recorder_status',
230
                         status
235
                         status
231
                     };
236
                     };
237
+
232
                     if (error) {
238
                     if (error) {
233
                         logObject.error = error;
239
                         logObject.error = error;
234
                     }
240
                     }
252
     chatRoom.addListener(XMPPEvents.MESSAGE_RECEIVED,
258
     chatRoom.addListener(XMPPEvents.MESSAGE_RECEIVED,
253
         (jid, displayName, txt, myJid, ts) => {
259
         (jid, displayName, txt, myJid, ts) => {
254
             const id = Strophe.getResourceFromJid(jid);
260
             const id = Strophe.getResourceFromJid(jid);
261
+
255
             conference.eventEmitter.emit(JitsiConferenceEvents.MESSAGE_RECEIVED,
262
             conference.eventEmitter.emit(JitsiConferenceEvents.MESSAGE_RECEIVED,
256
                 id, txt, ts);
263
                 id, txt, ts);
257
         });
264
         });
260
         (jid, status) => {
267
         (jid, status) => {
261
             const id = Strophe.getResourceFromJid(jid);
268
             const id = Strophe.getResourceFromJid(jid);
262
             const participant = conference.getParticipantById(id);
269
             const participant = conference.getParticipantById(id);
270
+
263
             if (!participant || participant._status === status) {
271
             if (!participant || participant._status === status) {
264
                 return;
272
                 return;
265
             }
273
             }
281
 
289
 
282
     chatRoom.addPresenceListener('startmuted', (data, from) => {
290
     chatRoom.addPresenceListener('startmuted', (data, from) => {
283
         let isModerator = false;
291
         let isModerator = false;
292
+
284
         if (conference.myUserId() === from && conference.isModerator()) {
293
         if (conference.myUserId() === from && conference.isModerator()) {
285
             isModerator = true;
294
             isModerator = true;
286
         } else {
295
         } else {
287
             const participant = conference.getParticipantById(from);
296
             const participant = conference.getParticipantById(from);
297
+
288
             if (participant && participant.isModerator()) {
298
             if (participant && participant.isModerator()) {
289
                 isModerator = true;
299
                 isModerator = true;
290
             }
300
             }
334
     chatRoom.addPresenceListener('devices', (data, from) => {
344
     chatRoom.addPresenceListener('devices', (data, from) => {
335
         let isAudioAvailable = false;
345
         let isAudioAvailable = false;
336
         let isVideoAvailable = false;
346
         let isVideoAvailable = false;
347
+
337
         data.children.forEach(config => {
348
         data.children.forEach(config => {
338
             if (config.tagName === 'audio') {
349
             if (config.tagName === 'audio') {
339
                 isAudioAvailable = config.value === 'true';
350
                 isAudioAvailable = config.value === 'true';
344
         });
355
         });
345
 
356
 
346
         let availableDevices;
357
         let availableDevices;
358
+
347
         if (conference.myUserId() === from) {
359
         if (conference.myUserId() === from) {
348
             availableDevices = conference.availableDevices;
360
             availableDevices = conference.availableDevices;
349
         } else {
361
         } else {
350
             const participant = conference.getParticipantById(from);
362
             const participant = conference.getParticipantById(from);
363
+
351
             if (!participant) {
364
             if (!participant) {
352
                 return;
365
                 return;
353
             }
366
             }
420
 
433
 
421
     rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {
434
     rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {
422
         const now = window.performance.now();
435
         const now = window.performance.now();
436
+
423
         logger.log('(TIME) data channel opened ', now);
437
         logger.log('(TIME) data channel opened ', now);
424
         conference.room.connectionTimes['data.channel.opened'] = now;
438
         conference.room.connectionTimes['data.channel.opened'] = now;
425
         Statistics.analytics.sendEvent('conference.dataChannel.open',
439
         Statistics.analytics.sendEvent('conference.dataChannel.open',
439
     rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,
453
     rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,
440
         (from, payload) => {
454
         (from, payload) => {
441
             const participant = conference.getParticipantById(from);
455
             const participant = conference.getParticipantById(from);
456
+
442
             if (participant) {
457
             if (participant) {
443
                 conference.eventEmitter.emit(
458
                 conference.eventEmitter.emit(
444
                     JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
459
                     JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
479
  */
494
  */
480
 JitsiConferenceEventManager.prototype.setupXMPPListeners = function() {
495
 JitsiConferenceEventManager.prototype.setupXMPPListeners = function() {
481
     const conference = this.conference;
496
     const conference = this.conference;
497
+
482
     conference.xmpp.caps.addListener(XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
498
     conference.xmpp.caps.addListener(XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
483
         from => {
499
         from => {
484
             const participant = conference.getParticipantId(
500
             const participant = conference.getParticipantId(
485
                 Strophe.getResourceFromJid(from));
501
                 Strophe.getResourceFromJid(from));
502
+
486
             if(participant) {
503
             if(participant) {
487
                 conference.eventEmitter.emit(
504
                 conference.eventEmitter.emit(
488
                     JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
505
                     JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
521
  */
538
  */
522
 JitsiConferenceEventManager.prototype.setupStatisticsListeners = function() {
539
 JitsiConferenceEventManager.prototype.setupStatisticsListeners = function() {
523
     const conference = this.conference;
540
     const conference = this.conference;
541
+
524
     if(!conference.statistics) {
542
     if(!conference.statistics) {
525
         return;
543
         return;
526
     }
544
     }
527
 
545
 
528
     conference.statistics.addAudioLevelListener((ssrc, level) => {
546
     conference.statistics.addAudioLevelListener((ssrc, level) => {
529
         const resource = conference.rtc.getResourceBySSRC(ssrc);
547
         const resource = conference.rtc.getResourceBySSRC(ssrc);
548
+
530
         if (!resource) {
549
         if (!resource) {
531
             return;
550
             return;
532
         }
551
         }
554
             }
573
             }
555
 
574
 
556
             const id = conference.rtc.getResourceBySSRC(ssrc);
575
             const id = conference.rtc.getResourceBySSRC(ssrc);
576
+
557
             if (!id) {
577
             if (!id) {
558
                 return;
578
                 return;
559
             }
579
             }
560
 
580
 
561
             // ssrc to resolution map for user id
581
             // ssrc to resolution map for user id
562
             const idResolutions = id2resolution[id] || {};
582
             const idResolutions = id2resolution[id] || {};
583
+
563
             idResolutions[ssrc] = resolution;
584
             idResolutions[ssrc] = resolution;
564
 
585
 
565
             id2resolution[id] = idResolutions;
586
             id2resolution[id] = idResolutions;
574
     conference.statistics.addByteSentStatsListener(stats => {
595
     conference.statistics.addByteSentStatsListener(stats => {
575
         conference.getLocalTracks(MediaType.AUDIO).forEach(track => {
596
         conference.getLocalTracks(MediaType.AUDIO).forEach(track => {
576
             const ssrc = track.getSSRC();
597
             const ssrc = track.getSSRC();
598
+
577
             if (!ssrc || !stats.hasOwnProperty(ssrc)) {
599
             if (!ssrc || !stats.hasOwnProperty(ssrc)) {
578
                 return;
600
                 return;
579
             }
601
             }

+ 1
- 0
JitsiConnection.js View File

1
 const JitsiConference = require('./JitsiConference');
1
 const JitsiConference = require('./JitsiConference');
2
+
2
 import * as JitsiConnectionEvents from './JitsiConnectionEvents';
3
 import * as JitsiConnectionEvents from './JitsiConnectionEvents';
3
 import XMPP from './modules/xmpp/xmpp';
4
 import XMPP from './modules/xmpp/xmpp';
4
 const Statistics = require('./modules/statistics/statistics');
5
 const Statistics = require('./modules/statistics/statistics');

+ 1
- 0
JitsiMediaDevices.js View File

102
     setAudioOutputDevice(deviceId) {
102
     setAudioOutputDevice(deviceId) {
103
 
103
 
104
         const availableDevices = RTC.getCurrentlyAvailableMediaDevices();
104
         const availableDevices = RTC.getCurrentlyAvailableMediaDevices();
105
+
105
         if (availableDevices && availableDevices.length > 0) {
106
         if (availableDevices && availableDevices.length > 0) {
106
             // if we have devices info report device to stats
107
             // if we have devices info report device to stats
107
             // normally this will not happen on startup as this method is called
108
             // normally this will not happen on startup as this method is called

+ 15
- 1
JitsiMeetJS.js View File

37
     const order = Resolutions[resolution].order;
37
     const order = Resolutions[resolution].order;
38
     let res = null;
38
     let res = null;
39
     let resName = null;
39
     let resName = null;
40
+
40
     Object.keys(Resolutions).forEach(resolution => {
41
     Object.keys(Resolutions).forEach(resolution => {
41
         const value = Resolutions[resolution];
42
         const value = Resolutions[resolution];
43
+
42
         if (!res || (res.order < value.order && value.order < order)) {
44
         if (!res || (res.order < value.order && value.order < order)) {
43
             resName = resolution;
45
             resName = resolution;
44
             res = value;
46
             res = value;
45
         }
47
         }
46
     });
48
     });
49
+
47
     return resName;
50
     return resName;
48
 }
51
 }
49
 
52
 
114
         if (window.jitsiRegionInfo
117
         if (window.jitsiRegionInfo
115
             && Object.keys(window.jitsiRegionInfo).length > 0) {
118
             && Object.keys(window.jitsiRegionInfo).length > 0) {
116
             const logObject = {};
119
             const logObject = {};
120
+
117
             for (const attr in window.jitsiRegionInfo) {
121
             for (const attr in window.jitsiRegionInfo) {
118
                 if (window.jitsiRegionInfo.hasOwnProperty(attr)) {
122
                 if (window.jitsiRegionInfo.hasOwnProperty(attr)) {
119
                     logObject[attr] = window.jitsiRegionInfo[attr];
123
                     logObject[attr] = window.jitsiRegionInfo[attr];
130
                 component: 'lib-jitsi-meet',
134
                 component: 'lib-jitsi-meet',
131
                 version: this.version
135
                 version: this.version
132
             };
136
             };
137
+
133
             Statistics.sendLog(JSON.stringify(logObject));
138
             Statistics.sendLog(JSON.stringify(logObject));
134
         }
139
         }
135
 
140
 
243
                     for(let i = 0; i < tracks.length; i++) {
248
                     for(let i = 0; i < tracks.length; i++) {
244
                         const track = tracks[i];
249
                         const track = tracks[i];
245
                         const mStream = track.getOriginalStream();
250
                         const mStream = track.getOriginalStream();
251
+
246
                         if(track.getType() === MediaType.AUDIO) {
252
                         if(track.getType() === MediaType.AUDIO) {
247
                             Statistics.startLocalStats(mStream,
253
                             Statistics.startLocalStats(mStream,
248
                                 track.setAudioLevel.bind(track));
254
                                 track.setAudioLevel.bind(track));
258
                 // set real device ids
264
                 // set real device ids
259
                 const currentlyAvailableMediaDevices
265
                 const currentlyAvailableMediaDevices
260
                     = RTC.getCurrentlyAvailableMediaDevices();
266
                     = RTC.getCurrentlyAvailableMediaDevices();
267
+
261
                 if (currentlyAvailableMediaDevices) {
268
                 if (currentlyAvailableMediaDevices) {
262
                     for(let i = 0; i < tracks.length; i++) {
269
                     for(let i = 0; i < tracks.length; i++) {
263
                         const track = tracks[i];
270
                         const track = tracks[i];
271
+
264
                         track._setRealDeviceIdFromDeviceList(
272
                         track._setRealDeviceIdFromDeviceList(
265
                             currentlyAvailableMediaDevices);
273
                             currentlyAvailableMediaDevices);
266
                     }
274
                     }
267
                 }
275
                 }
268
 
276
 
269
                 return tracks;
277
                 return tracks;
270
-            }).catch(error => {
278
+            })
279
+            .catch(error => {
271
                 promiseFulfilled = true;
280
                 promiseFulfilled = true;
272
 
281
 
273
                 if(error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION) {
282
                 if(error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION) {
296
                         id: 'chrome_extension_user_canceled',
305
                         id: 'chrome_extension_user_canceled',
297
                         message: error.message
306
                         message: error.message
298
                     };
307
                     };
308
+
299
                     Statistics.sendLog(JSON.stringify(logObject));
309
                     Statistics.sendLog(JSON.stringify(logObject));
300
                     Statistics.analytics.sendEvent(
310
                     Statistics.analytics.sendEvent(
301
                         'getUserMedia.userCancel.extensionInstall');
311
                         'getUserMedia.userCancel.extensionInstall');
305
                         id: 'usermedia_missing_device',
315
                         id: 'usermedia_missing_device',
306
                         status: error.gum.devices
316
                         status: error.gum.devices
307
                     };
317
                     };
318
+
308
                     Statistics.sendLog(JSON.stringify(logObject));
319
                     Statistics.sendLog(JSON.stringify(logObject));
309
                     Statistics.analytics.sendEvent(
320
                     Statistics.analytics.sendEvent(
310
                         `getUserMedia.deviceNotFound.${
321
                         `getUserMedia.deviceNotFound.${
316
                         = addDeviceTypeToAnalyticsEvent(
327
                         = addDeviceTypeToAnalyticsEvent(
317
                             'getUserMedia.failed',
328
                             'getUserMedia.failed',
318
                             options);
329
                             options);
330
+
319
                     Statistics.analytics.sendEvent(
331
                     Statistics.analytics.sendEvent(
320
                         `${event}.${error.name}`,
332
                         `${event}.${error.name}`,
321
                         {value: options});
333
                         {value: options});
337
     isDeviceListAvailable() {
349
     isDeviceListAvailable() {
338
         logger.warn('This method is deprecated, use '
350
         logger.warn('This method is deprecated, use '
339
             + 'JitsiMeetJS.mediaDevices.isDeviceListAvailable instead');
351
             + 'JitsiMeetJS.mediaDevices.isDeviceListAvailable instead');
352
+
340
         return this.mediaDevices.isDeviceListAvailable();
353
         return this.mediaDevices.isDeviceListAvailable();
341
     },
354
     },
342
     /**
355
     /**
350
     isDeviceChangeAvailable(deviceType) {
363
     isDeviceChangeAvailable(deviceType) {
351
         logger.warn('This method is deprecated, use '
364
         logger.warn('This method is deprecated, use '
352
             + 'JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead');
365
             + 'JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead');
366
+
353
         return this.mediaDevices.isDeviceChangeAvailable(deviceType);
367
         return this.mediaDevices.isDeviceChangeAvailable(deviceType);
354
     },
368
     },
355
     /**
369
     /**

+ 31
- 17
doc/example/example.js View File

63
         return;
63
         return;
64
     }
64
     }
65
     const participant = track.getParticipantId();
65
     const participant = track.getParticipantId();
66
+
66
     if(!remoteTracks[participant]) {
67
     if(!remoteTracks[participant]) {
67
         remoteTracks[participant] = [];
68
         remoteTracks[participant] = [];
68
     }
69
     }
69
     const idx = remoteTracks[participant].push(track);
70
     const idx = remoteTracks[participant].push(track);
71
+
70
     track.addEventListener(
72
     track.addEventListener(
71
         JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
73
         JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
72
         audioLevel => console.log(`Audio Level remote: ${audioLevel}`));
74
         audioLevel => console.log(`Audio Level remote: ${audioLevel}`));
81
             console.log(
83
             console.log(
82
                 `track audio output device was changed to ${deviceId}`));
84
                 `track audio output device was changed to ${deviceId}`));
83
     const id = participant + track.getType() + idx;
85
     const id = participant + track.getType() + idx;
86
+
84
     if(track.getType() == 'video') {
87
     if(track.getType() == 'video') {
85
         $('body').append(`<video autoplay='1' id='${participant}video${idx}' />`);
88
         $('body').append(`<video autoplay='1' id='${participant}video${idx}' />`);
86
     } else {
89
     } else {
106
         return;
109
         return;
107
     }
110
     }
108
     const tracks = remoteTracks[id];
111
     const tracks = remoteTracks[id];
112
+
109
     for(let i = 0; i < tracks.length; i++) {
113
     for(let i = 0; i < tracks.length; i++) {
110
         tracks[i].detach($(`#${id}${tracks[i].getType()}`));
114
         tracks[i].detach($(`#${id}${tracks[i].getType()}`));
111
     }
115
     }
181
 }
185
 }
182
 
186
 
183
 let isVideo = true;
187
 let isVideo = true;
188
+
184
 function switchVideo() { // eslint-disable-line no-unused-vars
189
 function switchVideo() { // eslint-disable-line no-unused-vars
185
     isVideo = !isVideo;
190
     isVideo = !isVideo;
186
     if(localTracks[1]) {
191
     if(localTracks[1]) {
200
                 });
205
                 });
201
             localTracks[1].attach($('#localVideo1')[0]);
206
             localTracks[1].attach($('#localVideo1')[0]);
202
             room.addTrack(localTracks[1]);
207
             room.addTrack(localTracks[1]);
203
-        }).catch(error => {
204
-            console.log(error);
205
-        });
208
+        })
209
+        .catch(error => console.log(error));
206
 }
210
 }
207
 
211
 
208
 function changeAudioOutput(selected) { // eslint-disable-line no-unused-vars
212
 function changeAudioOutput(selected) { // eslint-disable-line no-unused-vars
239
     // The URL to the Firefox extension for desktop sharing.
243
     // The URL to the Firefox extension for desktop sharing.
240
     desktopSharingFirefoxExtensionURL: null
244
     desktopSharingFirefoxExtensionURL: null
241
 };
245
 };
242
-JitsiMeetJS.init(initOptions).then(() => {
243
-    connection = new JitsiMeetJS.JitsiConnection(null, null, options);
244
 
246
 
245
-    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
246
-    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
247
-    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);
247
+JitsiMeetJS.init(initOptions)
248
+    .then(() => {
249
+        connection = new JitsiMeetJS.JitsiConnection(null, null, options);
250
+
251
+        connection.addEventListener(
252
+            JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
253
+            onConnectionSuccess);
254
+        connection.addEventListener(
255
+            JitsiMeetJS.events.connection.CONNECTION_FAILED,
256
+            onConnectionFailed);
257
+        connection.addEventListener(
258
+            JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
259
+            disconnect);
248
 
260
 
249
-    JitsiMeetJS.mediaDevices.addEventListener(JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED, onDeviceListChanged);
261
+        JitsiMeetJS.mediaDevices.addEventListener(
262
+            JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED,
263
+            onDeviceListChanged);
250
 
264
 
251
-    connection.connect();
252
-    JitsiMeetJS.createLocalTracks({devices: ['audio', 'video']})
253
-        .then(onLocalTracks).catch(error => {
254
-            throw error;
255
-        });
256
-}).catch(error => {
257
-    console.log(error);
258
-});
265
+        connection.connect();
266
+        JitsiMeetJS.createLocalTracks({devices: ['audio', 'video']})
267
+            .then(onLocalTracks)
268
+            .catch(error => {
269
+                throw error;
270
+            });
271
+    })
272
+    .catch(error => console.log(error));
259
 
273
 
260
 if (JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
274
 if (JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
261
     JitsiMeetJS.mediaDevices.enumerateDevices(devices => {
275
     JitsiMeetJS.mediaDevices.enumerateDevices(devices => {

+ 1
- 0
modules/DTMF/JitsiDTMFManager.js View File

2
 
2
 
3
 function JitsiDTMFManager(localAudio, peerConnection) {
3
 function JitsiDTMFManager(localAudio, peerConnection) {
4
     const audioTrack = localAudio.getTrack();
4
     const audioTrack = localAudio.getTrack();
5
+
5
     if (!audioTrack) {
6
     if (!audioTrack) {
6
         throw new Error('Failed to initialize DTMFSender: no audio track.');
7
         throw new Error('Failed to initialize DTMFSender: no audio track.');
7
     }
8
     }

+ 6
- 0
modules/RTC/DataChannels.js View File

139
             } else if ('EndpointConnectivityStatusChangeEvent' === colibriClass) {
139
             } else if ('EndpointConnectivityStatusChangeEvent' === colibriClass) {
140
                 const endpoint = obj.endpoint;
140
                 const endpoint = obj.endpoint;
141
                 const isActive = obj.active === 'true';
141
                 const isActive = obj.active === 'true';
142
+
142
                 logger.info(
143
                 logger.info(
143
                     `Endpoint connection status changed: ${endpoint} active ? ${
144
                     `Endpoint connection status changed: ${endpoint} active ? ${
144
                         isActive}`);
145
                         isActive}`);
158
     dataChannel.onclose = function() {
159
     dataChannel.onclose = function() {
159
         logger.info('The Data Channel closed', dataChannel);
160
         logger.info('The Data Channel closed', dataChannel);
160
         const idx = self._dataChannels.indexOf(dataChannel);
161
         const idx = self._dataChannels.indexOf(dataChannel);
162
+
161
         if (idx > -1) {
163
         if (idx > -1) {
162
             self._dataChannels = self._dataChannels.splice(idx, 1);
164
             self._dataChannels = self._dataChannels.splice(idx, 1);
163
         }
165
         }
215
     const tail = xxx.substring(1);
217
     const tail = xxx.substring(1);
216
     const lower = head.toLowerCase() + tail;
218
     const lower = head.toLowerCase() + tail;
217
     const upper = head.toUpperCase() + tail;
219
     const upper = head.toUpperCase() + tail;
220
+
218
     logger.log(
221
     logger.log(
219
             `sending ${lower} endpoint changed notification to the bridge: `,
222
             `sending ${lower} endpoint changed notification to the bridge: `,
220
             userResource);
223
             userResource);
238
         if (thisArg) {
241
         if (thisArg) {
239
             return dataChannels.some(callback, thisArg);
242
             return dataChannels.some(callback, thisArg);
240
         }
243
         }
244
+
241
         return dataChannels.some(callback);
245
         return dataChannels.some(callback);
242
 
246
 
243
     }
247
     }
248
+
244
     return false;
249
     return false;
245
 
250
 
246
 };
251
 };
256
     if(!this._some(dataChannel => {
261
     if(!this._some(dataChannel => {
257
         if (dataChannel.readyState == 'open') {
262
         if (dataChannel.readyState == 'open') {
258
             dataChannel.send(JSON.stringify(jsonObject));
263
             dataChannel.send(JSON.stringify(jsonObject));
264
+
259
             return true;
265
             return true;
260
         }
266
         }
261
     })) {
267
     })) {

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

146
     if(this.isVideoTrack() && this.videoType === VideoType.CAMERA) {
146
     if(this.isVideoTrack() && this.videoType === VideoType.CAMERA) {
147
         const _onNoDataFromSourceError
147
         const _onNoDataFromSourceError
148
             = this._onNoDataFromSourceError.bind(this);
148
             = this._onNoDataFromSourceError.bind(this);
149
+
149
         this._setHandler('track_mute', () => {
150
         this._setHandler('track_mute', () => {
150
             if(this._checkForCameraIssues()) {
151
             if(this._checkForCameraIssues()) {
151
                 const now = window.performance.now();
152
                 const now = window.performance.now();
153
+
152
                 this._noDataFromSourceTimeout
154
                 this._noDataFromSourceTimeout
153
                     = setTimeout(_onNoDataFromSourceError, 3000);
155
                     = setTimeout(_onNoDataFromSourceError, 3000);
154
                 this._setHandler('track_unmute', () => {
156
                 this._setHandler('track_unmute', () => {
194
 JitsiLocalTrack.prototype._fireNoDataFromSourceEvent = function() {
196
 JitsiLocalTrack.prototype._fireNoDataFromSourceEvent = function() {
195
     this.eventEmitter.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
197
     this.eventEmitter.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
196
     const eventName = `${this.getType()}.no_data_from_source`;
198
     const eventName = `${this.getType()}.no_data_from_source`;
199
+
197
     Statistics.analytics.sendEvent(eventName);
200
     Statistics.analytics.sendEvent(eventName);
198
     const log = {name: eventName};
201
     const log = {name: eventName};
202
+
199
     if (this.isAudioTrack()) {
203
     if (this.isAudioTrack()) {
200
         log.isReceivingData = this._isReceivingData();
204
         log.isReceivingData = this._isReceivingData();
201
     }
205
     }
316
             devices: [ MediaType.VIDEO ],
320
             devices: [ MediaType.VIDEO ],
317
             facingMode: this.getCameraFacingMode()
321
             facingMode: this.getCameraFacingMode()
318
         };
322
         };
323
+
319
         if (this.resolution) {
324
         if (this.resolution) {
320
             streamOptions.resolution = this.resolution;
325
             streamOptions.resolution = this.resolution;
321
         }
326
         }
390
 = function(successCallback, errorCallback) {
395
 = function(successCallback, errorCallback) {
391
     if (!this.conference) {
396
     if (!this.conference) {
392
         successCallback();
397
         successCallback();
398
+
393
         return;
399
         return;
394
     }
400
     }
395
 
401
 
474
     if (this.isVideoTrack() && !this.isActive()) {
480
     if (this.isVideoTrack() && !this.isActive()) {
475
         return true;
481
         return true;
476
     }
482
     }
483
+
477
     return !this.track || !this.track.enabled;
484
     return !this.track || !this.track.enabled;
478
 
485
 
479
 };
486
 };
517
     } else if(this.ssrc && this.ssrc.ssrcs && this.ssrc.ssrcs.length) {
524
     } else if(this.ssrc && this.ssrc.ssrcs && this.ssrc.ssrcs.length) {
518
         return this.ssrc.ssrcs[0];
525
         return this.ssrc.ssrcs[0];
519
     }
526
     }
527
+
520
     return null;
528
     return null;
521
 
529
 
522
 };
530
 };
549
     // the conference(and through the XMPP chat room ???) instead
557
     // the conference(and through the XMPP chat room ???) instead
550
     const iceConnectionState
558
     const iceConnectionState
551
         = this.conference ? this.conference.getConnectionState() : null;
559
         = this.conference ? this.conference.getConnectionState() : null;
560
+
552
     if(this._testByteSent && 'connected' === iceConnectionState) {
561
     if(this._testByteSent && 'connected' === iceConnectionState) {
553
         setTimeout(() => {
562
         setTimeout(() => {
554
             if(this._bytesSent <= 0) {
563
             if(this._bytesSent <= 0) {
636
     if(!this.stream) {
645
     if(!this.stream) {
637
         return false;
646
         return false;
638
     }
647
     }
648
+
639
     // In older version of the spec there is no muted property and
649
     // In older version of the spec there is no muted property and
640
     // readyState can have value muted. In the latest versions
650
     // readyState can have value muted. In the latest versions
641
     // readyState can have values "live" and "ended" and there is
651
     // readyState can have values "live" and "ended" and there is

+ 4
- 0
modules/RTC/JitsiRemoteTrack.js View File

1
 /* global */
1
 /* global */
2
 
2
 
3
 const JitsiTrack = require('./JitsiTrack');
3
 const JitsiTrack = require('./JitsiTrack');
4
+
4
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
5
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6
 const RTCBrowserType = require('./RTCBrowserType');
7
 const RTCBrowserType = require('./RTCBrowserType');
152
     const type = this.isVideoTrack() ? 'video' : 'audio';
153
     const type = this.isVideoTrack() ? 'video' : 'audio';
153
 
154
 
154
     const now = window.performance.now();
155
     const now = window.performance.now();
156
+
155
     console.log(`(TIME) Render ${type}:\t`, now);
157
     console.log(`(TIME) Render ${type}:\t`, now);
156
     this.conference.getConnectionTimes()[`${type}.render`] = now;
158
     this.conference.getConnectionTimes()[`${type}.render`] = now;
157
 
159
 
160
         - this.conference.getConnectionTimes()['muc.joined'])
162
         - this.conference.getConnectionTimes()['muc.joined'])
161
         - (window.connectionTimes['obtainPermissions.end']
163
         - (window.connectionTimes['obtainPermissions.end']
162
         - window.connectionTimes['obtainPermissions.start']);
164
         - window.connectionTimes['obtainPermissions.start']);
165
+
163
     this.conference.getConnectionTimes()[`${type}.ttfm`] = ttfm;
166
     this.conference.getConnectionTimes()[`${type}.ttfm`] = ttfm;
164
     console.log(`(TIME) TTFM ${type}:\t`, ttfm);
167
     console.log(`(TIME) TTFM ${type}:\t`, ttfm);
165
     let eventName = `${type}.ttfm`;
168
     let eventName = `${type}.ttfm`;
169
+
166
     if(this.hasBeenMuted) {
170
     if(this.hasBeenMuted) {
167
         eventName += '.muted';
171
         eventName += '.muted';
168
     }
172
     }

+ 7
- 0
modules/RTC/JitsiTrack.js View File

31
     }
31
     }
32
 
32
 
33
     const originalStop = stream.stop;
33
     const originalStop = stream.stop;
34
+
34
     stream.stop = function() {
35
     stream.stop = function() {
35
         originalStop.apply(stream);
36
         originalStop.apply(stream);
36
         if (jitsiTrack.isActive()) {
37
         if (jitsiTrack.isActive()) {
211
     if (this.isAudioTrack()) {
212
     if (this.isAudioTrack()) {
212
         return 'mic';
213
         return 'mic';
213
     }
214
     }
215
+
214
     return this.videoType ? this.videoType : 'default';
216
     return this.videoType ? this.videoType : 'default';
215
 
217
 
216
 };
218
 };
267
 JitsiTrack.prototype.detach = function(container) {
269
 JitsiTrack.prototype.detach = function(container) {
268
     for (let cs = this.containers, i = cs.length - 1; i >= 0; --i) {
270
     for (let cs = this.containers, i = cs.length - 1; i >= 0; --i) {
269
         const c = cs[i];
271
         const c = cs[i];
272
+
270
         if (!container) {
273
         if (!container) {
271
             RTCUtils.attachMediaStream(c, null);
274
             RTCUtils.attachMediaStream(c, null);
272
         }
275
         }
322
     if(this.stream) {
325
     if(this.stream) {
323
         return RTCUtils.getStreamID(this.stream);
326
         return RTCUtils.getStreamID(this.stream);
324
     }
327
     }
328
+
325
     return null;
329
     return null;
326
 
330
 
327
 };
331
 };
336
     if(typeof this.stream.active !== 'undefined') {
340
     if(typeof this.stream.active !== 'undefined') {
337
         return this.stream.active;
341
         return this.stream.active;
338
     }
342
     }
343
+
339
     return true;
344
     return true;
340
 
345
 
341
 };
346
 };
386
 JitsiTrack.prototype.getMSID = function() {
391
 JitsiTrack.prototype.getMSID = function() {
387
     const streamId = this.getStreamId();
392
     const streamId = this.getStreamId();
388
     const trackId = this.getTrackId();
393
     const trackId = this.getTrackId();
394
+
395
+
389
     return streamId && trackId ? `${streamId} ${trackId}` : null;
396
     return streamId && trackId ? `${streamId} ${trackId}` : null;
390
 };
397
 };
391
 
398
 

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

18
 function createLocalTracks(tracksInfo, options) {
18
 function createLocalTracks(tracksInfo, options) {
19
     const newTracks = [];
19
     const newTracks = [];
20
     let deviceId = null;
20
     let deviceId = null;
21
+
21
     tracksInfo.forEach(trackInfo => {
22
     tracksInfo.forEach(trackInfo => {
22
         if (trackInfo.mediaType === MediaType.AUDIO) {
23
         if (trackInfo.mediaType === MediaType.AUDIO) {
23
             deviceId = options.micDeviceId;
24
             deviceId = options.micDeviceId;
33
                 trackInfo.resolution,
34
                 trackInfo.resolution,
34
                 deviceId,
35
                 deviceId,
35
                 options.facingMode);
36
                 options.facingMode);
37
+
36
         newTracks.push(localTrack);
38
         newTracks.push(localTrack);
37
     });
39
     });
40
+
38
     return newTracks;
41
     return newTracks;
39
 }
42
 }
40
 
43
 
70
                 deviceId => {
73
                 deviceId => {
71
                     const remoteAudioTracks
74
                     const remoteAudioTracks
72
                         = this.getRemoteTracks(MediaType.AUDIO);
75
                         = this.getRemoteTracks(MediaType.AUDIO);
76
+
73
                     for (const track of remoteAudioTracks) {
77
                     for (const track of remoteAudioTracks) {
74
                         track.setAudioOutput(deviceId);
78
                         track.setAudioOutput(deviceId);
75
                     }
79
                     }
95
         return RTCUtils.obtainAudioAndVideoPermissions(options).then(
99
         return RTCUtils.obtainAudioAndVideoPermissions(options).then(
96
             tracksInfo => {
100
             tracksInfo => {
97
                 const tracks = createLocalTracks(tracksInfo, options);
101
                 const tracks = createLocalTracks(tracksInfo, options);
102
+
103
+
98
                 return tracks.some(track => !track._isReceivingData())
104
                 return tracks.some(track => !track._isReceivingData())
99
                     ? Promise.reject(
105
                     ? Promise.reject(
100
                         new JitsiTrackError(
106
                         new JitsiTrackError(
201
 
207
 
202
     static init(options = {}) {
208
     static init(options = {}) {
203
         this.options = options;
209
         this.options = options;
210
+
204
         return RTCUtils.init(this.options);
211
         return RTCUtils.init(this.options);
205
     }
212
     }
206
 
213
 
232
 
239
 
233
         this.peerConnections.set(newConnection.id, newConnection);
240
         this.peerConnections.set(newConnection.id, newConnection);
234
         this.peerConnectionIdCounter += 1;
241
         this.peerConnectionIdCounter += 1;
242
+
235
         return newConnection;
243
         return newConnection;
236
     }
244
     }
237
 
245
 
244
      */
252
      */
245
     _removePeerConnection(traceablePeerConnection) {
253
     _removePeerConnection(traceablePeerConnection) {
246
         const id = traceablePeerConnection.id;
254
         const id = traceablePeerConnection.id;
255
+
247
         if (this.peerConnections.has(id)) {
256
         if (this.peerConnections.has(id)) {
248
             // NOTE Remote tracks are not removed here.
257
             // NOTE Remote tracks are not removed here.
249
             this.peerConnections.delete(id);
258
             this.peerConnections.delete(id);
259
+
250
             return true;
260
             return true;
251
         }
261
         }
262
+
252
         return false;
263
         return false;
253
 
264
 
254
     }
265
     }
269
      */
280
      */
270
     getLocalVideoTrack() {
281
     getLocalVideoTrack() {
271
         const localVideo = this.getLocalTracks(MediaType.VIDEO);
282
         const localVideo = this.getLocalTracks(MediaType.VIDEO);
283
+
284
+
272
         return localVideo.length ? localVideo[0] : undefined;
285
         return localVideo.length ? localVideo[0] : undefined;
273
     }
286
     }
274
 
287
 
278
      */
291
      */
279
     getLocalAudioTrack() {
292
     getLocalAudioTrack() {
280
         const localAudio = this.getLocalTracks(MediaType.AUDIO);
293
         const localAudio = this.getLocalTracks(MediaType.AUDIO);
294
+
295
+
281
         return localAudio.length ? localAudio[0] : undefined;
296
         return localAudio.length ? localAudio[0] : undefined;
282
     }
297
     }
283
 
298
 
289
      */
304
      */
290
     getLocalTracks(mediaType) {
305
     getLocalTracks(mediaType) {
291
         let tracks = this.localTracks.slice();
306
         let tracks = this.localTracks.slice();
307
+
292
         if (mediaType !== undefined) {
308
         if (mediaType !== undefined) {
293
             tracks = tracks.filter(
309
             tracks = tracks.filter(
294
                 track => track.getType() === mediaType);
310
                 track => track.getType() === mediaType);
295
         }
311
         }
312
+
296
         return tracks;
313
         return tracks;
297
     }
314
     }
298
 
315
 
321
                 }
338
                 }
322
             }
339
             }
323
         }
340
         }
341
+
324
         return remoteTracks;
342
         return remoteTracks;
325
     }
343
     }
326
 
344
 
335
         if (this.remoteTracks[resource]) {
353
         if (this.remoteTracks[resource]) {
336
             return this.remoteTracks[resource][type];
354
             return this.remoteTracks[resource][type];
337
         }
355
         }
356
+
338
         return null;
357
         return null;
339
 
358
 
340
     }
359
     }
366
      */
385
      */
367
     setAudioMute(value) {
386
     setAudioMute(value) {
368
         const mutePromises = [];
387
         const mutePromises = [];
388
+
369
         this.getLocalTracks(MediaType.AUDIO).forEach(audioTrack => {
389
         this.getLocalTracks(MediaType.AUDIO).forEach(audioTrack => {
370
             // this is a Promise
390
             // this is a Promise
371
             mutePromises.push(value ? audioTrack.mute() : audioTrack.unmute());
391
             mutePromises.push(value ? audioTrack.mute() : audioTrack.unmute());
372
         });
392
         });
373
-        // we return a Promise from all Promises so we can wait for their execution
393
+
394
+        // We return a Promise from all Promises so we can wait for their
395
+        // execution.
374
         return Promise.all(mutePromises);
396
         return Promise.all(mutePromises);
375
     }
397
     }
376
 
398
 
377
     removeLocalTrack(track) {
399
     removeLocalTrack(track) {
378
         const pos = this.localTracks.indexOf(track);
400
         const pos = this.localTracks.indexOf(track);
401
+
379
         if (pos === -1) {
402
         if (pos === -1) {
380
             return;
403
             return;
381
         }
404
         }
435
 
458
 
436
             delete this.remoteTracks[owner];
459
             delete this.remoteTracks[owner];
437
         }
460
         }
461
+
438
         return removedTracks;
462
         return removedTracks;
439
     }
463
     }
440
 
464
 
460
                         && mediaTrack.getStreamId() == streamId
484
                         && mediaTrack.getStreamId() == streamId
461
                         && mediaTrack.getTrackId() == trackId) {
485
                         && mediaTrack.getTrackId() == trackId) {
462
                         result = mediaTrack;
486
                         result = mediaTrack;
487
+
463
                         return true;
488
                         return true;
464
                     }
489
                     }
490
+
465
                     return false;
491
                     return false;
466
 
492
 
467
                 });
493
                 });
637
             return;
663
             return;
638
         }
664
         }
639
         const audioTrack = this.getRemoteAudioTrack(resource);
665
         const audioTrack = this.getRemoteAudioTrack(resource);
666
+
640
         if(audioTrack) {
667
         if(audioTrack) {
641
             audioTrack.setAudioLevel(audioLevel);
668
             audioTrack.setAudioLevel(audioLevel);
642
         }
669
         }
654
         }
681
         }
655
 
682
 
656
         const track = this.getRemoteTrackBySSRC(ssrc);
683
         const track = this.getRemoteTrackBySSRC(ssrc);
684
+
685
+
657
         return track ? track.getParticipantId() : null;
686
         return track ? track.getParticipantId() : null;
658
     }
687
     }
659
 
688
 
676
      */
705
      */
677
     handleRemoteTrackMute(type, isMuted, from) {
706
     handleRemoteTrackMute(type, isMuted, from) {
678
         const track = this.getRemoteTrackByType(type, from);
707
         const track = this.getRemoteTrackByType(type, from);
708
+
679
         if (track) {
709
         if (track) {
680
             track.setMute(isMuted);
710
             track.setMute(isMuted);
681
         }
711
         }
688
      */
718
      */
689
     handleRemoteTrackVideoTypeChanged(value, from) {
719
     handleRemoteTrackVideoTypeChanged(value, from) {
690
         const videoTrack = this.getRemoteVideoTrack(from);
720
         const videoTrack = this.getRemoteVideoTrack(from);
721
+
691
         if (videoTrack) {
722
         if (videoTrack) {
692
             videoTrack._setVideoType(value);
723
             videoTrack._setVideoType(value);
693
         }
724
         }

+ 32
- 0
modules/RTC/RTCBrowserType.js View File

34
      */
34
      */
35
     getBrowserName() {
35
     getBrowserName() {
36
         let browser;
36
         let browser;
37
+
37
         if (RTCBrowserType.isAndroid()) {
38
         if (RTCBrowserType.isAndroid()) {
38
             browser = 'android';
39
             browser = 'android';
39
         } else {
40
         } else {
40
             browser = currentBrowser.split('rtc_browser.')[1];
41
             browser = currentBrowser.split('rtc_browser.')[1];
41
         }
42
         }
43
+
42
         return browser;
44
         return browser;
43
     },
45
     },
44
 
46
 
199
         // We can assume that user agent is chrome, because it's
201
         // We can assume that user agent is chrome, because it's
200
         // enforced when 'ext' streaming method is set
202
         // enforced when 'ext' streaming method is set
201
         const ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
203
         const ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
204
+
202
         logger.log(`This appears to be Chrome, ver: ${ver}`);
205
         logger.log(`This appears to be Chrome, ver: ${ver}`);
206
+
203
         return ver;
207
         return ver;
204
     }
208
     }
209
+
205
     return null;
210
     return null;
206
 }
211
 }
207
 
212
 
208
 function detectOpera() {
213
 function detectOpera() {
209
     const userAgent = navigator.userAgent;
214
     const userAgent = navigator.userAgent;
215
+
210
     if (userAgent.match(/Opera|OPR/)) {
216
     if (userAgent.match(/Opera|OPR/)) {
211
         currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA;
217
         currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA;
212
         const version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2];
218
         const version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2];
219
+
213
         logger.info(`This appears to be Opera, ver: ${version}`);
220
         logger.info(`This appears to be Opera, ver: ${version}`);
221
+
214
         return version;
222
         return version;
215
     }
223
     }
224
+
216
     return null;
225
     return null;
217
 }
226
 }
218
 
227
 
221
         currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX;
230
         currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX;
222
         const version = parseInt(
231
         const version = parseInt(
223
             navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
232
             navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
233
+
224
         logger.log(`This appears to be Firefox, ver: ${version}`);
234
         logger.log(`This appears to be Firefox, ver: ${version}`);
235
+
225
         return version;
236
         return version;
226
     }
237
     }
238
+
227
     return null;
239
     return null;
228
 }
240
 }
229
 
241
 
231
     if (/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
243
     if (/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
232
         currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
244
         currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
233
         logger.info('This appears to be Safari');
245
         logger.info('This appears to be Safari');
246
+
234
         // FIXME detect Safari version when needed
247
         // FIXME detect Safari version when needed
235
         return 1;
248
         return 1;
236
     }
249
     }
250
+
237
     return null;
251
     return null;
238
 }
252
 }
239
 
253
 
242
     const ua = window.navigator.userAgent;
256
     const ua = window.navigator.userAgent;
243
 
257
 
244
     const msie = ua.indexOf('MSIE ');
258
     const msie = ua.indexOf('MSIE ');
259
+
245
     if (msie > 0) {
260
     if (msie > 0) {
246
         // IE 10 or older => return version number
261
         // IE 10 or older => return version number
247
         version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
262
         version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
248
     }
263
     }
249
 
264
 
250
     const trident = ua.indexOf('Trident/');
265
     const trident = ua.indexOf('Trident/');
266
+
251
     if (!version && trident > 0) {
267
     if (!version && trident > 0) {
252
         // IE 11 => return version number
268
         // IE 11 => return version number
253
         const rv = ua.indexOf('rv:');
269
         const rv = ua.indexOf('rv:');
270
+
254
         version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
271
         version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
255
     }
272
     }
256
 
273
 
257
     const edge = ua.indexOf('Edge/');
274
     const edge = ua.indexOf('Edge/');
275
+
258
     if (!version && edge > 0) {
276
     if (!version && edge > 0) {
259
         // IE 12 => return version number
277
         // IE 12 => return version number
260
         version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
278
         version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
264
         currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER;
282
         currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER;
265
         logger.info(`This appears to be IExplorer, ver: ${version}`);
283
         logger.info(`This appears to be IExplorer, ver: ${version}`);
266
     }
284
     }
285
+
267
     return version;
286
     return version;
268
 }
287
 }
269
 
288
 
272
  */
291
  */
273
 function detectElectron() {
292
 function detectElectron() {
274
     const userAgent = navigator.userAgent;
293
     const userAgent = navigator.userAgent;
294
+
275
     if (userAgent.match(/Electron/)) {
295
     if (userAgent.match(/Electron/)) {
276
         currentBrowser = RTCBrowserType.RTC_BROWSER_ELECTRON;
296
         currentBrowser = RTCBrowserType.RTC_BROWSER_ELECTRON;
277
         const version = userAgent.match(/Electron\/([\d.]+)/)[1];
297
         const version = userAgent.match(/Electron\/([\d.]+)/)[1];
298
+
278
         logger.info(`This appears to be Electron, ver: ${version}`);
299
         logger.info(`This appears to be Electron, ver: ${version}`);
300
+
279
         return version;
301
         return version;
280
     }
302
     }
303
+
281
     return null;
304
     return null;
282
 }
305
 }
283
 
306
 
284
 function detectNWJS() {
307
 function detectNWJS() {
285
     const userAgent = navigator.userAgent;
308
     const userAgent = navigator.userAgent;
309
+
286
     if (userAgent.match(/JitsiMeetNW/)) {
310
     if (userAgent.match(/JitsiMeetNW/)) {
287
         currentBrowser = RTCBrowserType.RTC_BROWSER_NWJS;
311
         currentBrowser = RTCBrowserType.RTC_BROWSER_NWJS;
288
         const version = userAgent.match(/JitsiMeetNW\/([\d.]+)/)[1];
312
         const version = userAgent.match(/JitsiMeetNW\/([\d.]+)/)[1];
313
+
289
         logger.info(`This appears to be JitsiMeetNW, ver: ${version}`);
314
         logger.info(`This appears to be JitsiMeetNW, ver: ${version}`);
315
+
290
         return version;
316
         return version;
291
     }
317
     }
318
+
292
     return null;
319
     return null;
293
 }
320
 }
294
 
321
 
299
     // If we're remote debugging a React Native app, it may be treated as
326
     // If we're remote debugging a React Native app, it may be treated as
300
     // Chrome. Check navigator.product as well and always return some version
327
     // Chrome. Check navigator.product as well and always return some version
301
     // even if we can't get the real one.
328
     // even if we can't get the real one.
329
+
302
     if (match || navigator.product === 'ReactNative') {
330
     if (match || navigator.product === 'ReactNative') {
303
         currentBrowser = RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
331
         currentBrowser = RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
304
         let name;
332
         let name;
333
+
305
         if (match && match.length > 2) {
334
         if (match && match.length > 2) {
306
             name = match[1];
335
             name = match[1];
307
             version = match[2];
336
             version = match[2];
313
         // We're not running in a React Native environment.
342
         // We're not running in a React Native environment.
314
         version = null;
343
         version = null;
315
     }
344
     }
345
+
316
     return version;
346
     return version;
317
 }
347
 }
318
 
348
 
329
         detectSafari
359
         detectSafari
330
     ];
360
     ];
331
     // Try all browser detectors
361
     // Try all browser detectors
362
+
332
     for (let i = 0; i < detectors.length; i++) {
363
     for (let i = 0; i < detectors.length; i++) {
333
         version = detectors[i]();
364
         version = detectors[i]();
334
         if (version) {
365
         if (version) {
337
     }
368
     }
338
     logger.warn('Browser type defaults to Safari ver 1');
369
     logger.warn('Browser type defaults to Safari ver 1');
339
     currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
370
     currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
371
+
340
     return 1;
372
     return 1;
341
 }
373
 }
342
 
374
 

+ 3
- 0
modules/RTC/RTCUIHelper.js View File

23
      */
23
      */
24
     findVideoElement(containerElement) {
24
     findVideoElement(containerElement) {
25
         const videoElemName = RTCUIHelper.getVideoElementName();
25
         const videoElemName = RTCUIHelper.getVideoElementName();
26
+
26
         if (!RTCBrowserType.isTemasysPluginUsed()) {
27
         if (!RTCBrowserType.isTemasysPluginUsed()) {
27
             return $(containerElement).find(videoElemName)[0];
28
             return $(containerElement).find(videoElemName)[0];
28
         }
29
         }
29
         const matching = $(containerElement).find(
30
         const matching = $(containerElement).find(
30
                 ` ${videoElemName}>param[value="video"]`);
31
                 ` ${videoElemName}>param[value="video"]`);
32
+
31
         if (matching.length) {
33
         if (matching.length) {
32
             if (matching.length > 1) {
34
             if (matching.length > 1) {
33
                 logger.warn(
35
                 logger.warn(
34
                         'Container with more than one video elements: ',
36
                         'Container with more than one video elements: ',
35
                         containerElement);
37
                         containerElement);
36
             }
38
             }
39
+
37
             return matching.parent()[0];
40
             return matching.parent()[0];
38
         }
41
         }
39
 
42
 

+ 37
- 2
modules/RTC/RTCUtils.js View File

66
  * ready. Otherwise it is too early to assume that the devices listing is not
66
  * ready. Otherwise it is too early to assume that the devices listing is not
67
  * supported.
67
  * supported.
68
  */
68
  */
69
+
69
 function initRawEnumerateDevicesWithCallback() {
70
 function initRawEnumerateDevicesWithCallback() {
70
     rawEnumerateDevicesWithCallback = navigator.mediaDevices
71
     rawEnumerateDevicesWithCallback = navigator.mediaDevices
71
         && navigator.mediaDevices.enumerateDevices
72
         && navigator.mediaDevices.enumerateDevices
265
             const errmsg
266
             const errmsg
266
                 = '\'screen\' WebRTC media source is supported only in Chrome'
267
                 = '\'screen\' WebRTC media source is supported only in Chrome'
267
                     + ' and with Temasys plugin';
268
                     + ' and with Temasys plugin';
269
+
268
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
270
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
269
             logger.error(errmsg);
271
             logger.error(errmsg);
270
         }
272
         }
333
         return true;
335
         return true;
334
     }
336
     }
335
 
337
 
336
-    return newDevices.map(mediaDeviceInfoToJSON).sort().join('')
337
-        !== currentlyAvailableMediaDevices.map(mediaDeviceInfoToJSON).sort().join('');
338
+    return (
339
+        newDevices
340
+                .map(mediaDeviceInfoToJSON)
341
+                .sort()
342
+                .join('')
343
+            !== currentlyAvailableMediaDevices
344
+                .map(mediaDeviceInfoToJSON)
345
+                .sort()
346
+                .join(''));
338
 
347
 
339
     function mediaDeviceInfoToJSON(info) {
348
     function mediaDeviceInfoToJSON(info) {
340
         return JSON.stringify({
349
         return JSON.stringify({
538
 
547
 
539
     const device = options.devices.splice(0, 1);
548
     const device = options.devices.splice(0, 1);
540
     const devices = [];
549
     const devices = [];
550
+
541
     devices.push(device);
551
     devices.push(device);
542
     options.deviceGUM[device](
552
     options.deviceGUM[device](
543
         stream => {
553
         stream => {
578
         // (with a result which meets our requirements expressed bellow) calling
588
         // (with a result which meets our requirements expressed bellow) calling
579
         // getUserMedia once for both audio and video.
589
         // getUserMedia once for both audio and video.
580
         const audioVideo = streams.audioVideo;
590
         const audioVideo = streams.audioVideo;
591
+
581
         if (audioVideo) {
592
         if (audioVideo) {
582
             const audioTracks = audioVideo.getAudioTracks();
593
             const audioTracks = audioVideo.getAudioTracks();
594
+
583
             if (audioTracks.length) {
595
             if (audioTracks.length) {
584
                 // eslint-disable-next-line new-cap
596
                 // eslint-disable-next-line new-cap
585
                 audioStream = new webkitMediaStream();
597
                 audioStream = new webkitMediaStream();
589
             }
601
             }
590
 
602
 
591
             const videoTracks = audioVideo.getVideoTracks();
603
             const videoTracks = audioVideo.getVideoTracks();
604
+
592
             if (videoTracks.length) {
605
             if (videoTracks.length) {
593
                 // eslint-disable-next-line new-cap
606
                 // eslint-disable-next-line new-cap
594
                 videoStream = new webkitMediaStream();
607
                 videoStream = new webkitMediaStream();
693
 function defaultSetVideoSrc(element, stream) {
706
 function defaultSetVideoSrc(element, stream) {
694
     // srcObject
707
     // srcObject
695
     let srcObjectPropertyName = 'srcObject';
708
     let srcObjectPropertyName = 'srcObject';
709
+
696
     if (!(srcObjectPropertyName in element)) {
710
     if (!(srcObjectPropertyName in element)) {
697
         srcObjectPropertyName = 'mozSrcObject';
711
         srcObjectPropertyName = 'mozSrcObject';
698
         if (!(srcObjectPropertyName in element)) {
712
         if (!(srcObjectPropertyName in element)) {
701
     }
715
     }
702
     if (srcObjectPropertyName) {
716
     if (srcObjectPropertyName) {
703
         element[srcObjectPropertyName] = stream;
717
         element[srcObjectPropertyName] = stream;
718
+
704
         return;
719
         return;
705
     }
720
     }
706
 
721
 
707
     // src
722
     // src
708
     let src;
723
     let src;
724
+
709
     if (stream) {
725
     if (stream) {
710
         src = stream.jitsiObjectURL;
726
         src = stream.jitsiObjectURL;
711
         // Save the created URL for stream so we can reuse it and not keep
727
         // Save the created URL for stream so we can reuse it and not keep
738
         return new Promise((resolve, reject) => {
754
         return new Promise((resolve, reject) => {
739
             if (RTCBrowserType.isFirefox()) {
755
             if (RTCBrowserType.isFirefox()) {
740
                 const FFversion = RTCBrowserType.getFirefoxVersion();
756
                 const FFversion = RTCBrowserType.getFirefoxVersion();
757
+
741
                 if (FFversion < 40) {
758
                 if (FFversion < 40) {
742
                     rejectWithWebRTCNotSupported(
759
                     rejectWithWebRTCNotSupported(
743
                         `Firefox version too old: ${FFversion}.`
760
                         `Firefox version too old: ${FFversion}.`
766
                             element.play();
783
                             element.play();
767
                         }
784
                         }
768
                     }
785
                     }
786
+
769
                     return element;
787
                     return element;
770
                 });
788
                 });
771
                 this.getStreamID = function(stream) {
789
                 this.getStreamID = function(stream) {
772
                     let id = stream.id;
790
                     let id = stream.id;
791
+
773
                     if (!id) {
792
                     if (!id) {
774
                         let tracks = stream.getVideoTracks();
793
                         let tracks = stream.getVideoTracks();
794
+
775
                         if (!tracks || tracks.length === 0) {
795
                         if (!tracks || tracks.length === 0) {
776
                             tracks = stream.getAudioTracks();
796
                             tracks = stream.getAudioTracks();
777
                         }
797
                         }
778
                         id = tracks[0].id;
798
                         id = tracks[0].id;
779
                     }
799
                     }
800
+
780
                     return SDPUtil.filter_special_chars(id);
801
                     return SDPUtil.filter_special_chars(id);
781
                 };
802
                 };
782
                 RTCSessionDescription = mozRTCSessionDescription; // eslint-disable-line
803
                 RTCSessionDescription = mozRTCSessionDescription; // eslint-disable-line
789
 
810
 
790
                 this.peerconnection = webkitRTCPeerConnection;
811
                 this.peerconnection = webkitRTCPeerConnection;
791
                 const getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
812
                 const getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
813
+
792
                 if (navigator.mediaDevices) {
814
                 if (navigator.mediaDevices) {
793
                     this.getUserMedia = wrapGetUserMedia(getUserMedia);
815
                     this.getUserMedia = wrapGetUserMedia(getUserMedia);
794
                     this.enumerateDevices = wrapEnumerateDevices(
816
                     this.enumerateDevices = wrapEnumerateDevices(
800
                 }
822
                 }
801
                 this.attachMediaStream = wrapAttachMediaStream((element, stream) => {
823
                 this.attachMediaStream = wrapAttachMediaStream((element, stream) => {
802
                     defaultSetVideoSrc(element, stream);
824
                     defaultSetVideoSrc(element, stream);
825
+
803
                     return element;
826
                     return element;
804
                 });
827
                 });
805
                 this.getStreamID = function(stream) {
828
                 this.getStreamID = function(stream) {
813
                     // XXX The return statement is affected by automatic
836
                     // XXX The return statement is affected by automatic
814
                     // semicolon insertion (ASI). No line terminator is allowed
837
                     // semicolon insertion (ASI). No line terminator is allowed
815
                     // between the return keyword and the expression.
838
                     // between the return keyword and the expression.
839
+
816
                     return (
840
                     return (
817
                         typeof id === 'number'
841
                         typeof id === 'number'
818
                             ? id
842
                             ? id
859
                             // The container must be visible in order to play or
883
                             // The container must be visible in order to play or
860
                             // attach the stream when Temasys plugin is in use
884
                             // attach the stream when Temasys plugin is in use
861
                             const containerSel = $(element);
885
                             const containerSel = $(element);
886
+
862
                             if (RTCBrowserType.isTemasysPluginUsed()
887
                             if (RTCBrowserType.isTemasysPluginUsed()
863
                                     && !containerSel.is(':visible')) {
888
                                     && !containerSel.is(':visible')) {
864
                                 containerSel.show();
889
                                 containerSel.show();
865
                             }
890
                             }
866
                             const video = stream.getVideoTracks().length > 0;
891
                             const video = stream.getVideoTracks().length > 0;
892
+
867
                             if (video && !$(element).is(':visible')) {
893
                             if (video && !$(element).is(':visible')) {
868
                                 throw new Error(
894
                                 throw new Error(
869
                                     'video element must be visible to attach'
895
                                     'video element must be visible to attach'
982
 
1008
 
983
         options = options || {};
1009
         options = options || {};
984
         const dsOptions = options.desktopSharingExtensionExternalInstallation;
1010
         const dsOptions = options.desktopSharingExtensionExternalInstallation;
1011
+
1012
+
985
         return new Promise((resolve, reject) => {
1013
         return new Promise((resolve, reject) => {
986
             const successCallback = function(stream) {
1014
             const successCallback = function(stream) {
987
                 resolve(handleLocalStream(stream, options.resolution));
1015
                 resolve(handleLocalStream(stream, options.resolution));
1032
                 });
1060
                 });
1033
             } else {
1061
             } else {
1034
                 const hasDesktop = options.devices.indexOf('desktop') > -1;
1062
                 const hasDesktop = options.devices.indexOf('desktop') > -1;
1063
+
1035
                 if (hasDesktop) {
1064
                 if (hasDesktop) {
1036
                     options.devices.splice(options.devices.indexOf('desktop'), 1);
1065
                     options.devices.splice(options.devices.indexOf('desktop'), 1);
1037
                 }
1066
                 }
1138
         if (!rtcReady) {
1167
         if (!rtcReady) {
1139
             throw new Error('WebRTC not ready yet');
1168
             throw new Error('WebRTC not ready yet');
1140
         }
1169
         }
1170
+
1141
         return Boolean(navigator.mediaDevices
1171
         return Boolean(navigator.mediaDevices
1142
             && navigator.mediaDevices.enumerateDevices
1172
             && navigator.mediaDevices.enumerateDevices
1143
             && typeof MediaStreamTrack !== 'undefined'
1173
             && typeof MediaStreamTrack !== 'undefined'
1156
         if (rtcReady) {
1186
         if (rtcReady) {
1157
             return Promise.resolve();
1187
             return Promise.resolve();
1158
         }
1188
         }
1189
+
1159
         return new Promise(resolve => {
1190
         return new Promise(resolve => {
1160
             const listener = () => {
1191
             const listener = () => {
1161
                 eventEmitter.removeListener(RTCEvents.RTC_READY, listener);
1192
                 eventEmitter.removeListener(RTCEvents.RTC_READY, listener);
1162
                 resolve();
1193
                 resolve();
1163
             };
1194
             };
1195
+
1164
             eventEmitter.addListener(RTCEvents.RTC_READY, listener);
1196
             eventEmitter.addListener(RTCEvents.RTC_READY, listener);
1165
                 // We have no failed event, so... it either resolves or nothing
1197
                 // We have no failed event, so... it either resolves or nothing
1166
                 // happens
1198
                 // happens
1223
 
1255
 
1224
         // if we have done createObjectURL, lets clean it
1256
         // if we have done createObjectURL, lets clean it
1225
         const url = mediaStream.jitsiObjectURL;
1257
         const url = mediaStream.jitsiObjectURL;
1258
+
1226
         if (url) {
1259
         if (url) {
1227
             delete mediaStream.jitsiObjectURL;
1260
             delete mediaStream.jitsiObjectURL;
1228
             (URL || webkitURL).revokeObjectURL(url);
1261
             (URL || webkitURL).revokeObjectURL(url);
1293
             'label':    device.label,
1326
             'label':    device.label,
1294
             'groupId':  device.groupId
1327
             'groupId':  device.groupId
1295
         };
1328
         };
1329
+
1296
         devices.push(deviceData);
1330
         devices.push(deviceData);
1331
+
1297
         return { deviceList: devices };
1332
         return { deviceList: devices };
1298
     }
1333
     }
1299
 }
1334
 }

+ 25
- 2
modules/RTC/ScreenObtainer.js View File

3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
5
 const RTCBrowserType = require('./RTCBrowserType');
5
 const RTCBrowserType = require('./RTCBrowserType');
6
+
6
 import JitsiTrackError from '../../JitsiTrackError';
7
 import JitsiTrackError from '../../JitsiTrackError';
7
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
8
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
8
 
9
 
70
      */
71
      */
71
     init(options, gum) {
72
     init(options, gum) {
72
         let obtainDesktopStream = null;
73
         let obtainDesktopStream = null;
74
+
73
         this.options = options = options || {};
75
         this.options = options = options || {};
74
         gumFunction = gum;
76
         gumFunction = gum;
75
 
77
 
98
                         // I cannot find documentation about "InvalidStateError"
100
                         // I cannot find documentation about "InvalidStateError"
99
                         // but this is what we are receiving from GUM when the
101
                         // but this is what we are receiving from GUM when the
100
                         // streamId for the desktop sharing is "".
102
                         // streamId for the desktop sharing is "".
103
+
101
                         if (error && error.name == 'InvalidStateError') {
104
                         if (error && error.name == 'InvalidStateError') {
102
                             jitsiError = new JitsiTrackError(
105
                             jitsiError = new JitsiTrackError(
103
                                 JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED
106
                                 JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED
187
     obtainScreenOnFirefox(options, callback, errorCallback) {
190
     obtainScreenOnFirefox(options, callback, errorCallback) {
188
         let extensionRequired = false;
191
         let extensionRequired = false;
189
         const { desktopSharingFirefoxMaxVersionExtRequired } = this.options;
192
         const { desktopSharingFirefoxMaxVersionExtRequired } = this.options;
193
+
190
         if (desktopSharingFirefoxMaxVersionExtRequired === -1
194
         if (desktopSharingFirefoxMaxVersionExtRequired === -1
191
             || (desktopSharingFirefoxMaxVersionExtRequired >= 0
195
             || (desktopSharingFirefoxMaxVersionExtRequired >= 0
192
                 && RTCBrowserType.getFirefoxVersion()
196
                 && RTCBrowserType.getFirefoxVersion()
199
 
203
 
200
         if (!extensionRequired || firefoxExtInstalled === true) {
204
         if (!extensionRequired || firefoxExtInstalled === true) {
201
             obtainWebRTCScreen(options, callback, errorCallback);
205
             obtainWebRTCScreen(options, callback, errorCallback);
206
+
202
             return;
207
             return;
203
         }
208
         }
204
 
209
 
220
                 300);
225
                 300);
221
             logger.log(
226
             logger.log(
222
                 'Waiting for detection of jidesha on firefox to finish.');
227
                 'Waiting for detection of jidesha on firefox to finish.');
228
+
223
             return;
229
             return;
224
         }
230
         }
225
 
231
 
264
                             .then(() => {
270
                             .then(() => {
265
                                 doGetStreamFromExtension(this.options,
271
                                 doGetStreamFromExtension(this.options,
266
                                     streamCallback, failCallback);
272
                                     streamCallback, failCallback);
267
-                            }).catch(() => {
273
+                            })
274
+                            .catch(() => {
268
                                 this.handleExtensionInstallationError(options,
275
                                 this.handleExtensionInstallationError(options,
269
                                     streamCallback, failCallback);
276
                                     streamCallback, failCallback);
270
                             });
277
                             });
290
             options.listener('waitingForExtension', webStoreInstallUrl);
297
             options.listener('waitingForExtension', webStoreInstallUrl);
291
             this.checkForChromeExtensionOnInterval(options, streamCallback,
298
             this.checkForChromeExtensionOnInterval(options, streamCallback,
292
                 failCallback, e);
299
                 failCallback, e);
300
+
293
             return;
301
             return;
294
         }
302
         }
295
 
303
 
306
         if (options.checkAgain() === false) {
314
         if (options.checkAgain() === false) {
307
             failCallback(new JitsiTrackError(
315
             failCallback(new JitsiTrackError(
308
                 JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR));
316
                 JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR));
317
+
309
             return;
318
             return;
310
         }
319
         }
311
         waitForExtensionAfterInstall(this.options, options.interval, 1)
320
         waitForExtensionAfterInstall(this.options, options.interval, 1)
314
                 options.listener('extensionFound');
323
                 options.listener('extensionFound');
315
                 this.obtainScreenFromExtension(options,
324
                 this.obtainScreenFromExtension(options,
316
                     streamCallback, failCallback);
325
                     streamCallback, failCallback);
317
-            }).catch(() => {
326
+            })
327
+            .catch(() => {
318
                 this.checkForChromeExtensionOnInterval(options,
328
                 this.checkForChromeExtensionOnInterval(options,
319
                     streamCallback, failCallback);
329
                     streamCallback, failCallback);
320
             });
330
             });
358
         const s2 = extVersion.split('.');
368
         const s2 = extVersion.split('.');
359
 
369
 
360
         const len = Math.max(s1.length, s2.length);
370
         const len = Math.max(s1.length, s2.length);
371
+
361
         for (let i = 0; i < len; i++) {
372
         for (let i = 0; i < len; i++) {
362
             let n1 = 0,
373
             let n1 = 0,
363
                 n2 = 0;
374
                 n2 = 0;
382
     } catch (e) {
393
     } catch (e) {
383
         GlobalOnErrorHandler.callErrorHandler(e);
394
         GlobalOnErrorHandler.callErrorHandler(e);
384
         logger.error('Failed to parse extension version', e);
395
         logger.error('Failed to parse extension version', e);
396
+
385
         return true;
397
         return true;
386
     }
398
     }
387
 }
399
 }
390
     if (typeof chrome === 'undefined' || !chrome || !chrome.runtime) {
402
     if (typeof chrome === 'undefined' || !chrome || !chrome.runtime) {
391
         // No API, so no extension for sure
403
         // No API, so no extension for sure
392
         callback(false, false);
404
         callback(false, false);
405
+
393
         return;
406
         return;
394
     }
407
     }
395
     chrome.runtime.sendMessage(
408
     chrome.runtime.sendMessage(
401
                 logger.warn(
414
                 logger.warn(
402
                     'Extension not installed?: ', chrome.runtime.lastError);
415
                     'Extension not installed?: ', chrome.runtime.lastError);
403
                 callback(false, false);
416
                 callback(false, false);
417
+
404
                 return;
418
                 return;
405
             }
419
             }
406
             // Check installed extension version
420
             // Check installed extension version
407
             const extVersion = response.version;
421
             const extVersion = response.version;
422
+
408
             logger.log(`Extension version is: ${extVersion}`);
423
             logger.log(`Extension version is: ${extVersion}`);
409
             const updateRequired
424
             const updateRequired
410
                 = isUpdateRequired(
425
                 = isUpdateRequired(
411
                     options.desktopSharingChromeMinExtVersion,
426
                     options.desktopSharingChromeMinExtVersion,
412
                     extVersion);
427
                     extVersion);
428
+
413
             callback(!updateRequired, updateRequired);
429
             callback(!updateRequired, updateRequired);
414
         }
430
         }
415
     );
431
     );
428
             if (!response) {
444
             if (!response) {
429
                 // possibly re-wraping error message to make code consistent
445
                 // possibly re-wraping error message to make code consistent
430
                 const lastError = chrome.runtime.lastError;
446
                 const lastError = chrome.runtime.lastError;
447
+
431
                 failCallback(lastError instanceof Error
448
                 failCallback(lastError instanceof Error
432
                     ? lastError
449
                     ? lastError
433
                     : new JitsiTrackError(
450
                     : new JitsiTrackError(
434
                         JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
451
                         JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
435
                         lastError));
452
                         lastError));
453
+
436
                 return;
454
                 return;
437
             }
455
             }
438
             logger.log('Response from extension: ', response);
456
             logger.log('Response from extension: ', response);
481
     if(retries === 0) {
499
     if(retries === 0) {
482
         return Promise.reject();
500
         return Promise.reject();
483
     }
501
     }
502
+
484
     return new Promise((resolve, reject) => {
503
     return new Promise((resolve, reject) => {
485
         let currentRetries = retries;
504
         let currentRetries = retries;
486
         const interval = window.setInterval(() => {
505
         const interval = window.setInterval(() => {
523
         if(response.streamId === '') {
542
         if(response.streamId === '') {
524
             onFailure(new JitsiTrackError(
543
             onFailure(new JitsiTrackError(
525
                 JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED));
544
                 JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED));
545
+
526
             return;
546
             return;
527
         }
547
         }
528
 
548
 
546
     }
566
     }
547
     if (!options.desktopSharingFirefoxExtId) {
567
     if (!options.desktopSharingFirefoxExtId) {
548
         firefoxExtInstalled = false;
568
         firefoxExtInstalled = false;
569
+
549
         return;
570
         return;
550
     }
571
     }
551
 
572
 
552
     const img = document.createElement('img');
573
     const img = document.createElement('img');
574
+
553
     img.onload = () => {
575
     img.onload = () => {
554
         logger.log('Detected firefox screen sharing extension.');
576
         logger.log('Detected firefox screen sharing extension.');
555
         firefoxExtInstalled = true;
577
         firefoxExtInstalled = true;
566
     const src
588
     const src
567
         = `chrome://${options.desktopSharingFirefoxExtId.replace('@', '.')
589
         = `chrome://${options.desktopSharingFirefoxExtId.replace('@', '.')
568
             }/content/${document.location.hostname}.png`;
590
             }/content/${document.location.hostname}.png`;
591
+
569
     img.setAttribute('src', src);
592
     img.setAttribute('src', src);
570
 }
593
 }
571
 
594
 

+ 47
- 0
modules/RTC/TraceablePeerConnection.js View File

46
      * <tt>TracablePeerConnection</tt>.
46
      * <tt>TracablePeerConnection</tt>.
47
      * @type {RTC}
47
      * @type {RTC}
48
      */
48
      */
49
+
49
     this.rtc = rtc;
50
     this.rtc = rtc;
50
     /**
51
     /**
51
      * The peer connection identifier assigned by the RTC module.
52
      * The peer connection identifier assigned by the RTC module.
59
     this.signalingLayer = signalingLayer;
60
     this.signalingLayer = signalingLayer;
60
     this.options = options;
61
     this.options = options;
61
     let RTCPeerConnectionType = null;
62
     let RTCPeerConnectionType = null;
63
+
62
     if (RTCBrowserType.isFirefox()) {
64
     if (RTCBrowserType.isFirefox()) {
63
         RTCPeerConnectionType = mozRTCPeerConnection;
65
         RTCPeerConnectionType = mozRTCPeerConnection;
64
     } else if (RTCBrowserType.isTemasysPluginUsed()) {
66
     } else if (RTCBrowserType.isTemasysPluginUsed()) {
75
      */
77
      */
76
     this.maxstats = 0;
78
     this.maxstats = 0;
77
     const Interop = require('sdp-interop').Interop;
79
     const Interop = require('sdp-interop').Interop;
80
+
78
     this.interop = new Interop();
81
     this.interop = new Interop();
79
     const Simulcast = require('sdp-simulcast');
82
     const Simulcast = require('sdp-simulcast');
83
+
80
     this.simulcast = new Simulcast({numOfLayers: SIMULCAST_LAYERS,
84
     this.simulcast = new Simulcast({numOfLayers: SIMULCAST_LAYERS,
81
         explodeRemoteSimulcast: false});
85
         explodeRemoteSimulcast: false});
82
     this.sdpConsistency = new SdpConsistency();
86
     this.sdpConsistency = new SdpConsistency();
171
             self.peerconnection.getStats(stats => {
175
             self.peerconnection.getStats(stats => {
172
                 const results = stats.result();
176
                 const results = stats.result();
173
                 const now = new Date();
177
                 const now = new Date();
178
+
174
                 for (let i = 0; i < results.length; ++i) {
179
                 for (let i = 0; i < results.length; ++i) {
175
                     results[i].names().forEach(name => {
180
                     results[i].names().forEach(name => {
176
                         const id = `${results[i].id}-${name}`;
181
                         const id = `${results[i].id}-${name}`;
182
+
177
                         if (!self.stats[id]) {
183
                         if (!self.stats[id]) {
178
                             self.stats[id] = {
184
                             self.stats[id] = {
179
                                 startTime: now,
185
                                 startTime: now,
216
     if (!RTC.isUserStream(stream)) {
222
     if (!RTC.isUserStream(stream)) {
217
         logger.info(
223
         logger.info(
218
             'Ignored remote \'stream added\' event for non-user stream', stream);
224
             'Ignored remote \'stream added\' event for non-user stream', stream);
225
+
219
         return;
226
         return;
220
     }
227
     }
221
     // Bind 'addtrack'/'removetrack' event handlers
228
     // Bind 'addtrack'/'removetrack' event handlers
230
     }
237
     }
231
     // Call remoteTrackAdded for each track in the stream
238
     // Call remoteTrackAdded for each track in the stream
232
     const streamAudioTracks = stream.getAudioTracks();
239
     const streamAudioTracks = stream.getAudioTracks();
240
+
233
     for (const audioTrack of streamAudioTracks) {
241
     for (const audioTrack of streamAudioTracks) {
234
         this._remoteTrackAdded(stream, audioTrack);
242
         this._remoteTrackAdded(stream, audioTrack);
235
     }
243
     }
236
     const streamVideoTracks = stream.getVideoTracks();
244
     const streamVideoTracks = stream.getVideoTracks();
245
+
237
     for (const videoTrack of streamVideoTracks) {
246
     for (const videoTrack of streamVideoTracks) {
238
         this._remoteTrackAdded(stream, videoTrack);
247
         this._remoteTrackAdded(stream, videoTrack);
239
     }
248
     }
261
             new Error(
270
             new Error(
262
                 `MediaType undefined for remote track, stream id: ${streamId}`
271
                 `MediaType undefined for remote track, stream id: ${streamId}`
263
             ));
272
             ));
273
+
264
         // Abort
274
         // Abort
265
         return;
275
         return;
266
     }
276
     }
269
     const mediaLines
279
     const mediaLines
270
         = remoteSDP.media.filter(
280
         = remoteSDP.media.filter(
271
             mediaLines => mediaLines.startsWith(`m=${mediaType}`));
281
             mediaLines => mediaLines.startsWith(`m=${mediaType}`));
282
+
272
     if (!mediaLines.length) {
283
     if (!mediaLines.length) {
273
         GlobalOnErrorHandler.callErrorHandler(
284
         GlobalOnErrorHandler.callErrorHandler(
274
             new Error(
285
             new Error(
275
                 `No media lines for type ${mediaType
286
                 `No media lines for type ${mediaType
276
                      } found in remote SDP for remote track: ${streamId}`));
287
                      } found in remote SDP for remote track: ${streamId}`));
288
+
277
         // Abort
289
         // Abort
278
         return;
290
         return;
279
     }
291
     }
284
         line => {
296
         line => {
285
             const msid
297
             const msid
286
                 = RTCBrowserType.isTemasysPluginUsed() ? 'mslabel' : 'msid';
298
                 = RTCBrowserType.isTemasysPluginUsed() ? 'mslabel' : 'msid';
299
+
300
+
287
             return line.indexOf(`${msid}:${streamId}`) !== -1;
301
             return line.indexOf(`${msid}:${streamId}`) !== -1;
288
         });
302
         });
289
     if (!ssrcLines.length) {
303
     if (!ssrcLines.length) {
291
             new Error(
305
             new Error(
292
                 `No SSRC lines for streamId ${streamId
306
                 `No SSRC lines for streamId ${streamId
293
                      } for remote track, media type: ${mediaType}`));
307
                      } for remote track, media type: ${mediaType}`));
308
+
294
         // Abort
309
         // Abort
295
         return;
310
         return;
296
     }
311
     }
306
                 `No SSRC owner known for: ${trackSsrc
321
                 `No SSRC owner known for: ${trackSsrc
307
                      } for remote track, msid: ${streamId
322
                      } for remote track, msid: ${streamId
308
                      } media type: ${mediaType}`));
323
                      } media type: ${mediaType}`));
324
+
309
         // Abort
325
         // Abort
310
         return;
326
         return;
311
     }
327
     }
318
     if (!peerMediaInfo) {
334
     if (!peerMediaInfo) {
319
         GlobalOnErrorHandler.callErrorHandler(
335
         GlobalOnErrorHandler.callErrorHandler(
320
             new Error(`No peer media info available for: ${ownerEndpointId}`));
336
             new Error(`No peer media info available for: ${ownerEndpointId}`));
337
+
321
         // Abort
338
         // Abort
322
         return;
339
         return;
323
     }
340
     }
337
 TraceablePeerConnection.prototype._remoteStreamRemoved = function(stream) {
354
 TraceablePeerConnection.prototype._remoteStreamRemoved = function(stream) {
338
     if (!RTC.isUserStream(stream)) {
355
     if (!RTC.isUserStream(stream)) {
339
         const id = RTC.getStreamID(stream);
356
         const id = RTC.getStreamID(stream);
357
+
340
         logger.info(
358
         logger.info(
341
             `Ignored remote 'stream removed' event for non-user stream ${id}`);
359
             `Ignored remote 'stream removed' event for non-user stream ${id}`);
360
+
342
         return;
361
         return;
343
     }
362
     }
344
     // Call remoteTrackRemoved for each track in the stream
363
     // Call remoteTrackRemoved for each track in the stream
345
     const streamVideoTracks = stream.getVideoTracks();
364
     const streamVideoTracks = stream.getVideoTracks();
365
+
346
     for (const videoTrack of streamVideoTracks) {
366
     for (const videoTrack of streamVideoTracks) {
347
         this._remoteTrackRemoved(stream, videoTrack);
367
         this._remoteTrackRemoved(stream, videoTrack);
348
     }
368
     }
349
     const streamAudioTracks = stream.getAudioTracks();
369
     const streamAudioTracks = stream.getAudioTracks();
370
+
350
     for (const audioTrack of streamAudioTracks) {
371
     for (const audioTrack of streamAudioTracks) {
351
         this._remoteTrackRemoved(stream, audioTrack);
372
         this._remoteTrackRemoved(stream, audioTrack);
352
     }
373
     }
369
     if (!streamId) {
390
     if (!streamId) {
370
         GlobalOnErrorHandler.callErrorHandler(
391
         GlobalOnErrorHandler.callErrorHandler(
371
             new Error('Remote track removal failed - no stream ID'));
392
             new Error('Remote track removal failed - no stream ID'));
393
+
372
         // Abort
394
         // Abort
373
         return;
395
         return;
374
     }
396
     }
376
     if (!trackId) {
398
     if (!trackId) {
377
         GlobalOnErrorHandler.callErrorHandler(
399
         GlobalOnErrorHandler.callErrorHandler(
378
             new Error('Remote track removal failed - no track ID'));
400
             new Error('Remote track removal failed - no track ID'));
401
+
379
         // Abort
402
         // Abort
380
         return;
403
         return;
381
     }
404
     }
427
     if (typeof desc !== 'object' || desc === null
450
     if (typeof desc !== 'object' || desc === null
428
         || typeof desc.sdp !== 'string') {
451
         || typeof desc.sdp !== 'string') {
429
         logger.warn('An empty description was passed as an argument.');
452
         logger.warn('An empty description was passed as an argument.');
453
+
430
         return ssrcMap;
454
         return ssrcMap;
431
     }
455
     }
432
 
456
 
451
                                      .map(ssrcStr => parseInt(ssrcStr));
475
                                      .map(ssrcStr => parseInt(ssrcStr));
452
                     const primarySSRC = groupSSRCs[0];
476
                     const primarySSRC = groupSSRCs[0];
453
                     // Note that group.semantics is already present
477
                     // Note that group.semantics is already present
478
+
454
                     group.ssrcs = groupSSRCs;
479
                     group.ssrcs = groupSSRCs;
455
                     if (!groupsMap.has(primarySSRC)) {
480
                     if (!groupsMap.has(primarySSRC)) {
456
                         groupsMap.set(primarySSRC, []);
481
                         groupsMap.set(primarySSRC, []);
500
     if (typeof desc !== 'object' || desc === null
525
     if (typeof desc !== 'object' || desc === null
501
         || typeof desc.sdp !== 'string') {
526
         || typeof desc.sdp !== 'string') {
502
         logger.warn('An empty description was passed as an argument.');
527
         logger.warn('An empty description was passed as an argument.');
528
+
503
         return desc;
529
         return desc;
504
     }
530
     }
505
 
531
 
534
 
560
 
535
             if (Array.isArray(mLine.ssrcs)) {
561
             if (Array.isArray(mLine.ssrcs)) {
536
                 let i;
562
                 let i;
563
+
537
                 for (i = 0; i < mLine.ssrcs.length; i++) {
564
                 for (i = 0; i < mLine.ssrcs.length; i++) {
538
                     if (typeof mLine.ssrcs[i] === 'object'
565
                     if (typeof mLine.ssrcs[i] === 'object'
539
                         && typeof mLine.ssrcs[i].id !== 'undefined'
566
                         && typeof mLine.ssrcs[i].id !== 'undefined'
555
     }
582
     }
556
 
583
 
557
     const resStr = transform.write(session);
584
     const resStr = transform.write(session);
585
+
586
+
558
     return new RTCSessionDescription({
587
     return new RTCSessionDescription({
559
         type: desc.type,
588
         type: desc.type,
560
         sdp: resStr
589
         sdp: resStr
579
             this.trace('getLocalDescription::postTransform (Plan B)',
608
             this.trace('getLocalDescription::postTransform (Plan B)',
580
                 dumpSDP(desc));
609
                 dumpSDP(desc));
581
         }
610
         }
611
+
582
         return desc;
612
         return desc;
583
     },
613
     },
584
     remoteDescription() {
614
     remoteDescription() {
585
         let desc = this.peerconnection.remoteDescription;
615
         let desc = this.peerconnection.remoteDescription;
616
+
586
         this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
617
         this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
587
 
618
 
588
         // if we're running on FF, transform to Plan B first.
619
         // if we're running on FF, transform to Plan B first.
591
             this.trace(
622
             this.trace(
592
                 'getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
623
                 'getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
593
         }
624
         }
625
+
594
         return desc;
626
         return desc;
595
     }
627
     }
596
 };
628
 };
629
+
597
 Object.keys(getters).forEach(prop => {
630
 Object.keys(getters).forEach(prop => {
598
     Object.defineProperty(
631
     Object.defineProperty(
599
         TraceablePeerConnection.prototype,
632
         TraceablePeerConnection.prototype,
612
         this.sdpConsistency.setPrimarySsrc(ssrcInfo.ssrcs[0]);
645
         this.sdpConsistency.setPrimarySsrc(ssrcInfo.ssrcs[0]);
613
         const simGroup
646
         const simGroup
614
             = ssrcInfo.groups.find(groupInfo => groupInfo.semantics === 'SIM');
647
             = ssrcInfo.groups.find(groupInfo => groupInfo.semantics === 'SIM');
648
+
615
         if (simGroup) {
649
         if (simGroup) {
616
             this.simulcast.setSsrcCache(simGroup.ssrcs);
650
             this.simulcast.setSsrcCache(simGroup.ssrcs);
617
         }
651
         }
618
         const fidGroups
652
         const fidGroups
619
             = ssrcInfo.groups.filter(
653
             = ssrcInfo.groups.filter(
620
                 groupInfo => groupInfo.semantics === 'FID');
654
                 groupInfo => groupInfo.semantics === 'FID');
655
+
621
         if (fidGroups) {
656
         if (fidGroups) {
622
             const rtxSsrcMapping = new Map();
657
             const rtxSsrcMapping = new Map();
658
+
623
             fidGroups.forEach(fidGroup => {
659
             fidGroups.forEach(fidGroup => {
624
                 const primarySsrc = fidGroup.ssrcs[0];
660
                 const primarySsrc = fidGroup.ssrcs[0];
625
                 const rtxSsrc = fidGroup.ssrcs[1];
661
                 const rtxSsrc = fidGroup.ssrcs[1];
662
+
626
                 rtxSsrcMapping.set(primarySsrc, rtxSsrc);
663
                 rtxSsrcMapping.set(primarySsrc, rtxSsrc);
627
             });
664
             });
628
             this.rtxModifier.setSsrcCache(rtxSsrcMapping);
665
             this.rtxModifier.setSsrcCache(rtxSsrcMapping);
640
 
677
 
641
 TraceablePeerConnection.prototype.createDataChannel = function(label, opts) {
678
 TraceablePeerConnection.prototype.createDataChannel = function(label, opts) {
642
     this.trace('createDataChannel', label, opts);
679
     this.trace('createDataChannel', label, opts);
680
+
643
     return this.peerconnection.createDataChannel(label, opts);
681
     return this.peerconnection.createDataChannel(label, opts);
644
 };
682
 };
645
 
683
 
654
             }
692
             }
655
 
693
 
656
             const self = this;
694
             const self = this;
695
+
657
             this.peerconnection.setLocalDescription(description,
696
             this.peerconnection.setLocalDescription(description,
658
         () => {
697
         () => {
659
             self.trace('setLocalDescriptionOnSuccess');
698
             self.trace('setLocalDescriptionOnSuccess');
681
             if (this.options.preferH264) {
720
             if (this.options.preferH264) {
682
                 const parsedSdp = transform.parse(description.sdp);
721
                 const parsedSdp = transform.parse(description.sdp);
683
                 const videoMLine = parsedSdp.media.find(m => m.type === 'video');
722
                 const videoMLine = parsedSdp.media.find(m => m.type === 'video');
723
+
684
                 SDPUtil.preferVideoCodec(videoMLine, 'h264');
724
                 SDPUtil.preferVideoCodec(videoMLine, 'h264');
685
                 description.sdp = transform.write(parsedSdp);
725
                 description.sdp = transform.write(parsedSdp);
686
             }
726
             }
700
             }
740
             }
701
 
741
 
702
             const self = this;
742
             const self = this;
743
+
703
             this.peerconnection.setRemoteDescription(description,
744
             this.peerconnection.setRemoteDescription(description,
704
         () => {
745
         () => {
705
             self.trace('setRemoteDescriptionOnSuccess');
746
             self.trace('setRemoteDescriptionOnSuccess');
727
 TraceablePeerConnection.prototype.generateRecvonlySsrc = function() {
768
 TraceablePeerConnection.prototype.generateRecvonlySsrc = function() {
728
     // FIXME replace with SDPUtil.generateSsrc (when it's added)
769
     // FIXME replace with SDPUtil.generateSsrc (when it's added)
729
     const newSSRC = this.generateNewStreamSSRCInfo().ssrcs[0];
770
     const newSSRC = this.generateNewStreamSSRCInfo().ssrcs[0];
771
+
730
     logger.info(`Generated new recvonly SSRC: ${newSSRC}`);
772
     logger.info(`Generated new recvonly SSRC: ${newSSRC}`);
731
     this.sdpConsistency.setPrimarySsrc(newSSRC);
773
     this.sdpConsistency.setPrimarySsrc(newSSRC);
732
 };
774
 };
865
                 //  details)
907
                 //  details)
866
                 const remoteDescription = new SDP(this.remoteDescription.sdp);
908
                 const remoteDescription = new SDP(this.remoteDescription.sdp);
867
                 const localDescription = new SDP(answer.sdp);
909
                 const localDescription = new SDP(answer.sdp);
910
+
868
                 _fixAnswerRFC4145Setup(remoteDescription, localDescription);
911
                 _fixAnswerRFC4145Setup(remoteDescription, localDescription);
869
                 answer.sdp = localDescription.raw;
912
                 answer.sdp = localDescription.raw;
870
 
913
 
933
  */
976
  */
934
 TraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function() {
977
 TraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function() {
935
     let ssrcInfo = {ssrcs: [], groups: []};
978
     let ssrcInfo = {ssrcs: [], groups: []};
979
+
936
     if (!this.options.disableSimulcast
980
     if (!this.options.disableSimulcast
937
         && this.simulcast.isSupported()) {
981
         && this.simulcast.isSupported()) {
938
         for (let i = 0; i < SIMULCAST_LAYERS; i++) {
982
         for (let i = 0; i < SIMULCAST_LAYERS; i++) {
954
         //  only want to iterate through the items originally
998
         //  only want to iterate through the items originally
955
         //  on the list
999
         //  on the list
956
         const currNumSsrcs = ssrcInfo.ssrcs.length;
1000
         const currNumSsrcs = ssrcInfo.ssrcs.length;
1001
+
957
         for (let i = 0; i < currNumSsrcs; ++i) {
1002
         for (let i = 0; i < currNumSsrcs; ++i) {
958
             const primarySsrc = ssrcInfo.ssrcs[i];
1003
             const primarySsrc = ssrcInfo.ssrcs[i];
959
             const rtxSsrc = SDPUtil.generateSsrc();
1004
             const rtxSsrc = SDPUtil.generateSsrc();
1005
+
960
             ssrcInfo.ssrcs.push(rtxSsrc);
1006
             ssrcInfo.ssrcs.push(rtxSsrc);
961
             ssrcInfo.groups.push({
1007
             ssrcInfo.groups.push({
962
                 ssrcs: [primarySsrc, rtxSsrc],
1008
                 ssrcs: [primarySsrc, rtxSsrc],
964
             });
1010
             });
965
         }
1011
         }
966
     }
1012
     }
1013
+
967
     return ssrcInfo;
1014
     return ssrcInfo;
968
 };
1015
 };
969
 
1016
 

+ 7
- 0
modules/connectivity/ConnectionQuality.js View File

54
     if (simulcast) {
54
     if (simulcast) {
55
         // Find the first format with height no bigger than ours.
55
         // Find the first format with height no bigger than ours.
56
         let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);
56
         let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);
57
+
57
         if (simulcastFormat) {
58
         if (simulcastFormat) {
58
             // Sum the target fields from all simulcast layers for the given
59
             // Sum the target fields from all simulcast layers for the given
59
             // resolution (e.g. 720p + 360p + 180p).
60
             // resolution (e.g. 720p + 360p + 180p).
71
         // See GetMaxDefaultVideoBitrateKbps in
72
         // See GetMaxDefaultVideoBitrateKbps in
72
         // media/engine/webrtcvideoengine2.cc from webrtc.org
73
         // media/engine/webrtcvideoengine2.cc from webrtc.org
73
         const pixels = resolution.width * resolution.height;
74
         const pixels = resolution.width * resolution.height;
75
+
74
         if (pixels <= 320 * 240) {
76
         if (pixels <= 320 * 240) {
75
             target = 600;
77
             target = 600;
76
         } else if (pixels <= 640 * 480) {
78
         } else if (pixels <= 640 * 480) {
97
     if (millisSinceStart > 60000) {
99
     if (millisSinceStart > 60000) {
98
         return Number.MAX_SAFE_INTEGER;
100
         return Number.MAX_SAFE_INTEGER;
99
     }
101
     }
102
+
100
     // According to GCC the send side bandwidth estimation grows with at most
103
     // According to GCC the send side bandwidth estimation grows with at most
101
     // 8% per second.
104
     // 8% per second.
102
     // https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.5
105
     // https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.5
252
         let quality = 100;
255
         let quality = 100;
253
         let packetLoss;
256
         let packetLoss;
254
         // TODO: take into account packet loss for received streams
257
         // TODO: take into account packet loss for received streams
258
+
255
         if (this._localStats.packetLoss) {
259
         if (this._localStats.packetLoss) {
256
             packetLoss = this._localStats.packetLoss.upload;
260
             packetLoss = this._localStats.packetLoss.upload;
257
 
261
 
306
             // expected sending bitrate in perfect conditions
310
             // expected sending bitrate in perfect conditions
307
             let target
311
             let target
308
                 = getTarget(this._simulcast, resolution, millisSinceStart);
312
                 = getTarget(this._simulcast, resolution, millisSinceStart);
313
+
309
             target = 0.9 * target;
314
             target = 0.9 * target;
310
 
315
 
311
             quality = 100 * this._localStats.bitrate.upload / target;
316
             quality = 100 * this._localStats.bitrate.upload / target;
323
             const diffSeconds
328
             const diffSeconds
324
                 = (window.performance.now() - this._lastConnectionQualityUpdate)
329
                 = (window.performance.now() - this._lastConnectionQualityUpdate)
325
                     / 1000;
330
                     / 1000;
331
+
326
             quality
332
             quality
327
                 = Math.min(
333
                 = Math.min(
328
                     quality,
334
                     quality,
359
         // resolution instead. Consider removing this.
365
         // resolution instead. Consider removing this.
360
         const localVideoTrack
366
         const localVideoTrack
361
             = this._conference.getLocalVideoTrack();
367
             = this._conference.getLocalVideoTrack();
368
+
362
         if (localVideoTrack && localVideoTrack.resolution) {
369
         if (localVideoTrack && localVideoTrack.resolution) {
363
             data.resolution = localVideoTrack.resolution;
370
             data.resolution = localVideoTrack.resolution;
364
         }
371
         }

+ 4
- 0
modules/connectivity/ParticipantConnectionStatus.js View File

321
      */
321
      */
322
     figureOutConnectionStatus(id) {
322
     figureOutConnectionStatus(id) {
323
         const participant = this.conference.getParticipantById(id);
323
         const participant = this.conference.getParticipantById(id);
324
+
324
         if (!participant) {
325
         if (!participant) {
325
             // Probably the participant is no longer in the conference
326
             // Probably the participant is no longer in the conference
326
             // (at the time of writing this code, participant is
327
             // (at the time of writing this code, participant is
329
             // so we don't care, but let's print the warning for
330
             // so we don't care, but let's print the warning for
330
             // debugging purpose
331
             // debugging purpose
331
             logger.warn(`figure out conn status - no participant for: ${id}`);
332
             logger.warn(`figure out conn status - no participant for: ${id}`);
333
+
332
             return;
334
             return;
333
         }
335
         }
334
 
336
 
364
     onTrackRtcMuted(track) {
366
     onTrackRtcMuted(track) {
365
         const participantId = track.getParticipantId();
367
         const participantId = track.getParticipantId();
366
         const participant = this.conference.getParticipantById(participantId);
368
         const participant = this.conference.getParticipantById(participantId);
369
+
367
         logger.debug(`Detector track RTC muted: ${participantId}`);
370
         logger.debug(`Detector track RTC muted: ${participantId}`);
368
         if (!participant) {
371
         if (!participant) {
369
             logger.error(`No participant for id: ${participantId}`);
372
             logger.error(`No participant for id: ${participantId}`);
373
+
370
             return;
374
             return;
371
         }
375
         }
372
         this.rtcMutedTimestamp[participantId] = Date.now();
376
         this.rtcMutedTimestamp[participantId] = Date.now();

+ 9
- 0
modules/settings/Settings.js View File

11
  */
11
  */
12
 function getLocalStorage() {
12
 function getLocalStorage() {
13
     const global = typeof window == 'undefined' ? this : window;
13
     const global = typeof window == 'undefined' ? this : window;
14
+
15
+
14
     return global.localStorage;
16
     return global.localStorage;
15
 }
17
 }
16
 
18
 
28
  */
30
  */
29
 function generateJitsiMeetId() {
31
 function generateJitsiMeetId() {
30
     const jitsiMeetId = generateUniqueId();
32
     const jitsiMeetId = generateUniqueId();
33
+
31
     logger.log('generated id', jitsiMeetId);
34
     logger.log('generated id', jitsiMeetId);
32
 
35
 
33
     return jitsiMeetId;
36
     return jitsiMeetId;
39
  */
42
  */
40
 function generateCallStatsUsername() {
43
 function generateCallStatsUsername() {
41
     const username = UsernameGenerator.generateUsername();
44
     const username = UsernameGenerator.generateUsername();
45
+
42
     logger.log('generated callstats uid', username);
46
     logger.log('generated callstats uid', username);
43
 
47
 
44
     return username;
48
     return username;
50
         this.callStatsUserName;
54
         this.callStatsUserName;
51
 
55
 
52
         const localStorage = getLocalStorage();
56
         const localStorage = getLocalStorage();
57
+
53
         if (localStorage) {
58
         if (localStorage) {
54
             this.userId
59
             this.userId
55
                 = localStorage.getItem('jitsiMeetId') || generateJitsiMeetId();
60
                 = localStorage.getItem('jitsiMeetId') || generateJitsiMeetId();
70
      */
75
      */
71
     save() {
76
     save() {
72
         const localStorage = getLocalStorage();
77
         const localStorage = getLocalStorage();
78
+
73
         if (localStorage) {
79
         if (localStorage) {
74
             localStorage.setItem('jitsiMeetId', this.userId);
80
             localStorage.setItem('jitsiMeetId', this.userId);
75
             localStorage.setItem('callStatsUserName', this.callStatsUserName);
81
             localStorage.setItem('callStatsUserName', this.callStatsUserName);
98
      */
104
      */
99
     setSessionId(sessionId) {
105
     setSessionId(sessionId) {
100
         const localStorage = getLocalStorage();
106
         const localStorage = getLocalStorage();
107
+
101
         if (localStorage) {
108
         if (localStorage) {
102
             if (sessionId) {
109
             if (sessionId) {
103
                 localStorage.setItem('sessionId', sessionId);
110
                 localStorage.setItem('sessionId', sessionId);
122
         // We may update sessionId in localStorage from another JitsiConference
129
         // We may update sessionId in localStorage from another JitsiConference
123
         // instance and that's why we should always re-read it.
130
         // instance and that's why we should always re-read it.
124
         const localStorage = getLocalStorage();
131
         const localStorage = getLocalStorage();
132
+
133
+
125
         return localStorage ? localStorage.getItem('sessionId') : undefined;
134
         return localStorage ? localStorage.getItem('sessionId') : undefined;
126
     }
135
     }
127
 }
136
 }

+ 3
- 0
modules/statistics/AnalyticsAdapter.js View File

35
      */
35
      */
36
     drainCachedEvents() {
36
     drainCachedEvents() {
37
         const eventCacheCopy = this.eventCache.slice();
37
         const eventCacheCopy = this.eventCache.slice();
38
+
38
         this.eventCache = [];
39
         this.eventCache = [];
40
+
39
         return eventCacheCopy;
41
         return eventCacheCopy;
40
     }
42
     }
41
 
43
 
74
     sendEvent(action, data = {}) {
76
     sendEvent(action, data = {}) {
75
         const modifiedData = Object.assign(
77
         const modifiedData = Object.assign(
76
             {browserName: this.browserName}, this.permanentProperties, data);
78
             {browserName: this.browserName}, this.permanentProperties, data);
79
+
77
         this.analyticsHandlers.forEach(
80
         this.analyticsHandlers.forEach(
78
             analytics => analytics.sendEvent(action, modifiedData));
81
             analytics => analytics.sendEvent(action, modifiedData));
79
     }
82
     }

+ 6
- 0
modules/statistics/CallStats.js View File

1
 /* global $, Strophe, callstats */
1
 /* global $, Strophe, callstats */
2
 const logger = require('jitsi-meet-logger').getLogger(__filename);
2
 const logger = require('jitsi-meet-logger').getLogger(__filename);
3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
4
+
4
 import Settings from '../settings/Settings';
5
 import Settings from '../settings/Settings';
5
 
6
 
6
 const jsSHA = require('jssha');
7
 const jsSHA = require('jssha');
62
     // there is no lib, nothing to report to
63
     // there is no lib, nothing to report to
63
     if (err !== 'success') {
64
     if (err !== 'success') {
64
         CallStats.initializeFailed = true;
65
         CallStats.initializeFailed = true;
66
+
65
         return;
67
         return;
66
     }
68
     }
67
 
69
 
76
     if(!fabricInitialized) {
78
     if(!fabricInitialized) {
77
         CallStats.initializeFailed = true;
79
         CallStats.initializeFailed = true;
78
         logger.log('callstats fabric not initilized', ret.message);
80
         logger.log('callstats fabric not initilized', ret.message);
81
+
79
         return;
82
         return;
80
     }
83
     }
81
 
84
 
88
         CallStats.reportsQueue.forEach(function(report) {
91
         CallStats.reportsQueue.forEach(function(report) {
89
             if (report.type === reportType.ERROR) {
92
             if (report.type === reportType.ERROR) {
90
                 const error = report.data;
93
                 const error = report.data;
94
+
91
                 CallStats._reportError.call(this, error.type, error.error,
95
                 CallStats._reportError.call(this, error.type, error.error,
92
                     error.pc);
96
                     error.pc);
93
             } else if (report.type === reportType.EVENT
97
             } else if (report.type === reportType.EVENT
95
                 // if we have and event to report and we failed to add fabric
99
                 // if we have and event to report and we failed to add fabric
96
                 // this event will not be reported anyway, returning an error
100
                 // this event will not be reported anyway, returning an error
97
                 const eventData = report.data;
101
                 const eventData = report.data;
102
+
98
                 callStats.sendFabricEvent(
103
                 callStats.sendFabricEvent(
99
                     this.peerconnection,
104
                     this.peerconnection,
100
                     eventData.event,
105
                     eventData.event,
102
                     eventData.eventData);
107
                     eventData.eventData);
103
             } else if (report.type === reportType.MST_WITH_USERID) {
108
             } else if (report.type === reportType.MST_WITH_USERID) {
104
                 const data = report.data;
109
                 const data = report.data;
110
+
105
                 callStats.associateMstWithUserID(
111
                 callStats.associateMstWithUserID(
106
                     this.peerconnection,
112
                     this.peerconnection,
107
                     data.callStatsId,
113
                     data.callStatsId,

+ 5
- 0
modules/statistics/LocalStatsCollector.js View File

63
 function animateLevel(newLevel, lastLevel) {
63
 function animateLevel(newLevel, lastLevel) {
64
     let value = 0;
64
     let value = 0;
65
     const diff = lastLevel - newLevel;
65
     const diff = lastLevel - newLevel;
66
+
66
     if(diff > 0.2) {
67
     if(diff > 0.2) {
67
         value = lastLevel - 0.2;
68
         value = lastLevel - 0.2;
68
     } else if(diff < -0.4) {
69
     } else if(diff < -0.4) {
101
     }
102
     }
102
     context.resume();
103
     context.resume();
103
     const analyser = context.createAnalyser();
104
     const analyser = context.createAnalyser();
105
+
104
     analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;
106
     analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;
105
     analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;
107
     analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;
106
 
108
 
107
     const source = context.createMediaStreamSource(this.stream);
109
     const source = context.createMediaStreamSource(this.stream);
110
+
108
     source.connect(analyser);
111
     source.connect(analyser);
109
 
112
 
110
 
113
 
113
     this.intervalId = setInterval(
116
     this.intervalId = setInterval(
114
         () => {
117
         () => {
115
             const array = new Uint8Array(analyser.frequencyBinCount);
118
             const array = new Uint8Array(analyser.frequencyBinCount);
119
+
116
             analyser.getByteTimeDomainData(array);
120
             analyser.getByteTimeDomainData(array);
117
             const audioLevel = timeDomainDataToAudioLevel(array);
121
             const audioLevel = timeDomainDataToAudioLevel(array);
122
+
118
             if (audioLevel != self.audioLevel) {
123
             if (audioLevel != self.audioLevel) {
119
                 self.audioLevel = animateLevel(audioLevel, self.audioLevel);
124
                 self.audioLevel = animateLevel(audioLevel, self.audioLevel);
120
                 self.callback(self.audioLevel);
125
                 self.callback(self.audioLevel);

+ 31
- 0
modules/statistics/RTPStatsCollector.js View File

3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
3
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
4
 const logger = require('jitsi-meet-logger').getLogger(__filename);
5
 const RTCBrowserType = require('../RTC/RTCBrowserType');
5
 const RTCBrowserType = require('../RTC/RTCBrowserType');
6
+
6
 import * as StatisticsEvents from '../../service/statistics/Events';
7
 import * as StatisticsEvents from '../../service/statistics/Events';
7
 
8
 
8
 /* Whether we support the browser we are running into for logging statistics */
9
 /* Whether we support the browser we are running into for logging statistics */
15
  * by RTCPeerConnection#getStats mapped by RTCBrowserType.
16
  * by RTCPeerConnection#getStats mapped by RTCBrowserType.
16
  */
17
  */
17
 const KEYS_BY_BROWSER_TYPE = {};
18
 const KEYS_BY_BROWSER_TYPE = {};
19
+
18
 KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
20
 KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
19
     'ssrc': 'ssrc',
21
     'ssrc': 'ssrc',
20
     'packetsReceived': 'packetsReceived',
22
     'packetsReceived': 'packetsReceived',
67
     if(!totalPackets || totalPackets <= 0 || !lostPackets || lostPackets <= 0) {
69
     if(!totalPackets || totalPackets <= 0 || !lostPackets || lostPackets <= 0) {
68
         return 0;
70
         return 0;
69
     }
71
     }
72
+
70
     return Math.round((lostPackets / totalPackets) * 100);
73
     return Math.round((lostPackets / totalPackets) * 100);
71
 }
74
 }
72
 
75
 
180
      */
183
      */
181
     this._browserType = RTCBrowserType.getBrowserType();
184
     this._browserType = RTCBrowserType.getBrowserType();
182
     const keys = KEYS_BY_BROWSER_TYPE[this._browserType];
185
     const keys = KEYS_BY_BROWSER_TYPE[this._browserType];
186
+
183
     if (!keys) {
187
     if (!keys) {
184
         throw `The browser type '${this._browserType}' isn't supported!`;
188
         throw `The browser type '${this._browserType}' isn't supported!`;
185
     }
189
     }
242
  */
246
  */
243
 StatsCollector.prototype.start = function(startAudioLevelStats) {
247
 StatsCollector.prototype.start = function(startAudioLevelStats) {
244
     const self = this;
248
     const self = this;
249
+
245
     if(startAudioLevelStats) {
250
     if(startAudioLevelStats) {
246
         this.audioLevelsIntervalId = setInterval(
251
         this.audioLevelsIntervalId = setInterval(
247
             () => {
252
             () => {
249
                 self.peerconnection.getStats(
254
                 self.peerconnection.getStats(
250
                     report => {
255
                     report => {
251
                         let results = null;
256
                         let results = null;
257
+
252
                         if (!report || !report.result
258
                         if (!report || !report.result
253
                             || typeof report.result != 'function') {
259
                             || typeof report.result != 'function') {
254
                             results = report;
260
                             results = report;
274
                 self.peerconnection.getStats(
280
                 self.peerconnection.getStats(
275
                     report => {
281
                     report => {
276
                         let results = null;
282
                         let results = null;
283
+
277
                         if (!report || !report.result
284
                         if (!report || !report.result
278
                             || typeof report.result != 'function') {
285
                             || typeof report.result != 'function') {
279
                             // firefox
286
                             // firefox
314
     // RTCPeerConnection#getStats.
321
     // RTCPeerConnection#getStats.
315
     const keyFromName = function(name) {
322
     const keyFromName = function(name) {
316
         const key = keys[name];
323
         const key = keys[name];
324
+
317
         if (key) {
325
         if (key) {
318
             return key;
326
             return key;
319
         }
327
         }
325
     // returned by RTCPeerConnection#getStats associated with a given
333
     // returned by RTCPeerConnection#getStats associated with a given
326
     // browser-specific key.
334
     // browser-specific key.
327
     let itemStatByKey;
335
     let itemStatByKey;
336
+
328
     switch (this._browserType) {
337
     switch (this._browserType) {
329
     case RTCBrowserType.RTC_BROWSER_CHROME:
338
     case RTCBrowserType.RTC_BROWSER_CHROME:
330
     case RTCBrowserType.RTC_BROWSER_OPERA:
339
     case RTCBrowserType.RTC_BROWSER_OPERA:
345
         // Array in which each element is a key-value pair.
354
         // Array in which each element is a key-value pair.
346
         itemStatByKey = function(item, key) {
355
         itemStatByKey = function(item, key) {
347
             let value;
356
             let value;
357
+
348
             item.values.some(pair => {
358
             item.values.some(pair => {
349
                 if (pair.hasOwnProperty(key)) {
359
                 if (pair.hasOwnProperty(key)) {
350
                     value = pair[key];
360
                     value = pair[key];
361
+
351
                     return true;
362
                     return true;
352
                 }
363
                 }
364
+
353
                 return false;
365
                 return false;
354
 
366
 
355
             });
367
             });
368
+
356
             return value;
369
             return value;
357
         };
370
         };
358
         break;
371
         break;
379
     }
392
     }
380
 
393
 
381
     const getStatValue = this._getStatValue;
394
     const getStatValue = this._getStatValue;
395
+
382
     function getNonNegativeStat(report, name) {
396
     function getNonNegativeStat(report, name) {
383
         let value = getStatValue(report, name);
397
         let value = getStatValue(report, name);
398
+
384
         if (typeof value !== 'number') {
399
         if (typeof value !== 'number') {
385
             value = Number(value);
400
             value = Number(value);
386
         }
401
         }
398
             continue;
413
             continue;
399
         }
414
         }
400
         const now = this.currentStatsReport[idx];
415
         const now = this.currentStatsReport[idx];
416
+
401
         try {
417
         try {
402
             const receiveBandwidth = getStatValue(now, 'receiveBandwidth');
418
             const receiveBandwidth = getStatValue(now, 'receiveBandwidth');
403
             const sendBandwidth = getStatValue(now, 'sendBandwidth');
419
             const sendBandwidth = getStatValue(now, 'sendBandwidth');
420
+
404
             if (receiveBandwidth || sendBandwidth) {
421
             if (receiveBandwidth || sendBandwidth) {
405
                 this.conferenceStats.bandwidth = {
422
                 this.conferenceStats.bandwidth = {
406
                     'download': Math.round(receiveBandwidth / 1000),
423
                     'download': Math.round(receiveBandwidth / 1000),
411
 
428
 
412
         if(now.type == 'googCandidatePair') {
429
         if(now.type == 'googCandidatePair') {
413
             let active, ip, localip, type;
430
             let active, ip, localip, type;
431
+
414
             try {
432
             try {
415
                 ip = getStatValue(now, 'remoteAddress');
433
                 ip = getStatValue(now, 'remoteAddress');
416
                 type = getStatValue(now, 'transportType');
434
                 type = getStatValue(now, 'transportType');
422
             }
440
             }
423
             // Save the address unless it has been saved already.
441
             // Save the address unless it has been saved already.
424
             const conferenceStatsTransport = this.conferenceStats.transport;
442
             const conferenceStatsTransport = this.conferenceStats.transport;
443
+
425
             if(!conferenceStatsTransport.some(
444
             if(!conferenceStatsTransport.some(
426
                     t =>
445
                     t =>
427
                         t.ip == ip && t.type == type && t.localip == localip)) {
446
                         t.ip == ip && t.type == type && t.localip == localip)) {
437
 
456
 
438
             const local = this.currentStatsReport[now.localCandidateId];
457
             const local = this.currentStatsReport[now.localCandidateId];
439
             const remote = this.currentStatsReport[now.remoteCandidateId];
458
             const remote = this.currentStatsReport[now.remoteCandidateId];
459
+
440
             this.conferenceStats.transport.push({
460
             this.conferenceStats.transport.push({
441
                 ip: `${remote.ipAddress}:${remote.portNumber}`,
461
                 ip: `${remote.ipAddress}:${remote.portNumber}`,
442
                 type: local.transport,
462
                 type: local.transport,
451
 
471
 
452
         const before = this.previousStatsReport[idx];
472
         const before = this.previousStatsReport[idx];
453
         const ssrc = getStatValue(now, 'ssrc');
473
         const ssrc = getStatValue(now, 'ssrc');
474
+
454
         if (!before || !ssrc) {
475
         if (!before || !ssrc) {
455
             continue;
476
             continue;
456
         }
477
         }
461
         let isDownloadStream = true;
482
         let isDownloadStream = true;
462
         let key = 'packetsReceived';
483
         let key = 'packetsReceived';
463
         let packetsNow = getStatValue(now, key);
484
         let packetsNow = getStatValue(now, key);
485
+
464
         if (typeof packetsNow === 'undefined'
486
         if (typeof packetsNow === 'undefined'
465
             || packetsNow === null || packetsNow === '') {
487
             || packetsNow === null || packetsNow === '') {
466
             isDownloadStream = false;
488
             isDownloadStream = false;
496
 
518
 
497
         // TODO: clean this mess up!
519
         // TODO: clean this mess up!
498
         let nowBytesTransmitted = getStatValue(now, 'bytesSent');
520
         let nowBytesTransmitted = getStatValue(now, 'bytesSent');
521
+
499
         if(typeof nowBytesTransmitted === 'number'
522
         if(typeof nowBytesTransmitted === 'number'
500
             || typeof nowBytesTransmitted === 'string') {
523
             || typeof nowBytesTransmitted === 'string') {
501
             nowBytesTransmitted = Number(nowBytesTransmitted);
524
             nowBytesTransmitted = Number(nowBytesTransmitted);
511
 
534
 
512
         const timeMs = now.timestamp - before.timestamp;
535
         const timeMs = now.timestamp - before.timestamp;
513
         let bitrateReceivedKbps = 0, bitrateSentKbps = 0;
536
         let bitrateReceivedKbps = 0, bitrateSentKbps = 0;
537
+
514
         if (timeMs > 0) {
538
         if (timeMs > 0) {
515
             // TODO is there any reason to round here?
539
             // TODO is there any reason to round here?
516
             bitrateReceivedKbps = Math.round((bytesReceived * 8) / timeMs);
540
             bitrateReceivedKbps = Math.round((bytesReceived * 8) / timeMs);
523
         });
547
         });
524
 
548
 
525
         const resolution = {height: null, width: null};
549
         const resolution = {height: null, width: null};
550
+
526
         try {
551
         try {
527
             let height, width;
552
             let height, width;
553
+
528
             if ((height = getStatValue(now, 'googFrameHeightReceived'))
554
             if ((height = getStatValue(now, 'googFrameHeightReceived'))
529
                 && (width = getStatValue(now, 'googFrameWidthReceived'))) {
555
                 && (width = getStatValue(now, 'googFrameWidthReceived'))) {
530
                 resolution.height = height;
556
                 resolution.height = height;
555
     let bitrateDownload = 0;
581
     let bitrateDownload = 0;
556
     let bitrateUpload = 0;
582
     let bitrateUpload = 0;
557
     const resolutions = {};
583
     const resolutions = {};
584
+
558
     Object.keys(this.ssrc2stats).forEach(
585
     Object.keys(this.ssrc2stats).forEach(
559
         function(ssrc) {
586
         function(ssrc) {
560
             const ssrcStats = this.ssrc2stats[ssrc];
587
             const ssrcStats = this.ssrc2stats[ssrc];
561
             // process packet loss stats
588
             // process packet loss stats
562
             const loss = ssrcStats.loss;
589
             const loss = ssrcStats.loss;
563
             const type = loss.isDownloadStream ? 'download' : 'upload';
590
             const type = loss.isDownloadStream ? 'download' : 'upload';
591
+
564
             totalPackets[type] += loss.packetsTotal;
592
             totalPackets[type] += loss.packetsTotal;
565
             lostPackets[type] += loss.packetsLost;
593
             lostPackets[type] += loss.packetsLost;
566
 
594
 
616
         }
644
         }
617
 
645
 
618
         const now = this.currentAudioLevelsReport[idx];
646
         const now = this.currentAudioLevelsReport[idx];
647
+
619
         if (now.type != 'ssrc') {
648
         if (now.type != 'ssrc') {
620
             continue;
649
             continue;
621
         }
650
         }
622
 
651
 
623
         const before = this.baselineAudioLevelsReport[idx];
652
         const before = this.baselineAudioLevelsReport[idx];
624
         const ssrc = getStatValue(now, 'ssrc');
653
         const ssrc = getStatValue(now, 'ssrc');
654
+
625
         if (!before) {
655
         if (!before) {
626
             logger.warn(`${ssrc} not enough data`);
656
             logger.warn(`${ssrc} not enough data`);
627
             continue;
657
             continue;
644
         } catch(e) {/* not supported*/
674
         } catch(e) {/* not supported*/
645
             logger.warn('Audio Levels are not available in the statistics.');
675
             logger.warn('Audio Levels are not available in the statistics.');
646
             clearInterval(this.audioLevelsIntervalId);
676
             clearInterval(this.audioLevelsIntervalId);
677
+
647
             return;
678
             return;
648
         }
679
         }
649
 
680
 

+ 5
- 0
modules/statistics/statistics.js View File

2
 import analytics from './AnalyticsAdapter';
2
 import analytics from './AnalyticsAdapter';
3
 const CallStats = require('./CallStats');
3
 const CallStats = require('./CallStats');
4
 const EventEmitter = require('events');
4
 const EventEmitter = require('events');
5
+
5
 import JitsiTrackError from '../../JitsiTrackError';
6
 import JitsiTrackError from '../../JitsiTrackError';
6
 const logger = require('jitsi-meet-logger').getLogger(__filename);
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
7
 const LocalStats = require('./LocalStatsCollector.js');
8
 const LocalStats = require('./LocalStatsCollector.js');
8
 const RTPStats = require('./RTPStatsCollector.js');
9
 const RTPStats = require('./RTPStatsCollector.js');
9
 const ScriptUtil = require('../util/ScriptUtil');
10
 const ScriptUtil = require('../util/ScriptUtil');
11
+
10
 import * as StatisticsEvents from '../../service/statistics/Events';
12
 import * as StatisticsEvents from '../../service/statistics/Events';
11
 
13
 
12
 /**
14
 /**
130
     }
132
     }
131
     const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,
133
     const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,
132
         callback);
134
         callback);
135
+
133
     this.localStats.push(localStats);
136
     this.localStats.push(localStats);
134
     localStats.start();
137
     localStats.start();
135
 };
138
 };
193
     for(let i = 0; i < Statistics.localStats.length; i++) {
196
     for(let i = 0; i < Statistics.localStats.length; i++) {
194
         if(Statistics.localStats[i].stream === stream) {
197
         if(Statistics.localStats[i].stream === stream) {
195
             const localStats = Statistics.localStats.splice(i, 1);
198
             const localStats = Statistics.localStats.splice(i, 1);
199
+
196
             localStats[0].stop();
200
             localStats[0].stop();
197
             break;
201
             break;
198
         }
202
         }
230
 Statistics.prototype.stopCallStats = function() {
234
 Statistics.prototype.stopCallStats = function() {
231
     if(this.callStatsStarted) {
235
     if(this.callStatsStarted) {
232
         const index = Statistics.callsStatsInstances.indexOf(this.callstats);
236
         const index = Statistics.callsStatsInstances.indexOf(this.callstats);
237
+
233
         if(index > -1) {
238
         if(index > -1) {
234
             Statistics.callsStatsInstances.splice(index, 1);
239
             Statistics.callsStatsInstances.splice(index, 1);
235
         }
240
         }

+ 10
- 0
modules/transcription/audioRecorder.js View File

65
     // Create a new stream which only holds the audio track
65
     // Create a new stream which only holds the audio track
66
     const originalStream = trackRecorder.track.getOriginalStream();
66
     const originalStream = trackRecorder.track.getOriginalStream();
67
     const stream = createEmptyStream();
67
     const stream = createEmptyStream();
68
+
68
     originalStream.getAudioTracks().forEach(track => stream.addTrack(track));
69
     originalStream.getAudioTracks().forEach(track => stream.addTrack(track));
69
     // Create the MediaRecorder
70
     // Create the MediaRecorder
70
     trackRecorder.recorder = new MediaRecorder(stream,
71
     trackRecorder.recorder = new MediaRecorder(stream,
133
         // create the track recorder
134
         // create the track recorder
134
         const trackRecorder = instantiateTrackRecorder(track);
135
         const trackRecorder = instantiateTrackRecorder(track);
135
         // push it to the local array of all recorders
136
         // push it to the local array of all recorders
137
+
136
         this.recorders.push(trackRecorder);
138
         this.recorders.push(trackRecorder);
137
         // update the name of the trackRecorders
139
         // update the name of the trackRecorders
138
         this.updateNames();
140
         this.updateNames();
161
 
163
 
162
     const array = this.recorders;
164
     const array = this.recorders;
163
     let i;
165
     let i;
166
+
164
     for(i = 0; i < array.length; i++) {
167
     for(i = 0; i < array.length; i++) {
165
         if(array[i].track.getParticipantId() === track.getParticipantId()) {
168
         if(array[i].track.getParticipantId() === track.getParticipantId()) {
166
             const recorderToRemove = array[i];
169
             const recorderToRemove = array[i];
170
+
167
             if(this.isRecording) {
171
             if(this.isRecording) {
168
                 stopRecorder(recorderToRemove);
172
                 stopRecorder(recorderToRemove);
169
             } else {
173
             } else {
184
  */
188
  */
185
 audioRecorder.prototype.updateNames = function() {
189
 audioRecorder.prototype.updateNames = function() {
186
     const conference = this.jitsiConference;
190
     const conference = this.jitsiConference;
191
+
187
     this.recorders.forEach(trackRecorder => {
192
     this.recorders.forEach(trackRecorder => {
188
         if(trackRecorder.track.isLocal()) {
193
         if(trackRecorder.track.isLocal()) {
189
             trackRecorder.name = 'the transcriber';
194
             trackRecorder.name = 'the transcriber';
191
             const id = trackRecorder.track.getParticipantId();
196
             const id = trackRecorder.track.getParticipantId();
192
             const participant = conference.getParticipantById(id);
197
             const participant = conference.getParticipantById(id);
193
             const newName = participant.getDisplayName();
198
             const newName = participant.getDisplayName();
199
+
194
             if(newName !== 'undefined') {
200
             if(newName !== 'undefined') {
195
                 trackRecorder.name = newName;
201
                 trackRecorder.name = newName;
196
             }
202
             }
232
  */
238
  */
233
 audioRecorder.prototype.download = function() {
239
 audioRecorder.prototype.download = function() {
234
     const t = this;
240
     const t = this;
241
+
235
     this.recorders.forEach(trackRecorder => {
242
     this.recorders.forEach(trackRecorder => {
236
         const blob = new Blob(trackRecorder.data, {type: t.fileType});
243
         const blob = new Blob(trackRecorder.data, {type: t.fileType});
237
         const url = URL.createObjectURL(blob);
244
         const url = URL.createObjectURL(blob);
238
         const a = document.createElement('a');
245
         const a = document.createElement('a');
246
+
239
         document.body.appendChild(a);
247
         document.body.appendChild(a);
240
         a.style = 'display: none';
248
         a.style = 'display: none';
241
         a.href = url;
249
         a.href = url;
260
 
268
 
261
     const array = [];
269
     const array = [];
262
     const t = this;
270
     const t = this;
271
+
263
     this.recorders.forEach(
272
     this.recorders.forEach(
264
           recorder =>
273
           recorder =>
265
               array.push(
274
               array.push(
267
                       new Blob(recorder.data, {type: t.fileType}),
276
                       new Blob(recorder.data, {type: t.fileType}),
268
                       recorder.name,
277
                       recorder.name,
269
                       recorder.startTime)));
278
                       recorder.startTime)));
279
+
270
     return array;
280
     return array;
271
 };
281
 };
272
 
282
 

+ 10
- 0
modules/transcription/transcriber.js View File

79
     const t = this;
79
     const t = this;
80
 
80
 
81
     const callBack = blobCallBack.bind(this);
81
     const callBack = blobCallBack.bind(this);
82
+
82
     this.audioRecorder.getRecordingResults().forEach(recordingResult => {
83
     this.audioRecorder.getRecordingResults().forEach(recordingResult => {
83
         t.transcriptionService.send(recordingResult, callBack);
84
         t.transcriptionService.send(recordingResult, callBack);
84
         t.counter++;
85
         t.counter++;
109
         let offset = answer.startTime.getUTCMilliseconds()
110
         let offset = answer.startTime.getUTCMilliseconds()
110
             - this.startTime.getUTCMilliseconds();
111
             - this.startTime.getUTCMilliseconds();
111
         // transcriber time will always be earlier
112
         // transcriber time will always be earlier
113
+
112
         if (offset < 0) {
114
         if (offset < 0) {
113
             offset = 0; // presume 0 if it somehow not earlier
115
             offset = 0; // presume 0 if it somehow not earlier
114
         }
116
         }
115
 
117
 
116
         let array = '[';
118
         let array = '[';
119
+
117
         answer.wordArray.forEach(wordObject => {
120
         answer.wordArray.forEach(wordObject => {
118
             wordObject.begin += offset;
121
             wordObject.begin += offset;
119
             wordObject.end += offset;
122
             wordObject.end += offset;
164
     // arrays of Word objects
167
     // arrays of Word objects
165
     const potentialWords = []; // array of the first Word objects
168
     const potentialWords = []; // array of the first Word objects
166
     // check if any arrays are already empty and remove them
169
     // check if any arrays are already empty and remove them
170
+
167
     hasPopulatedArrays(arrays);
171
     hasPopulatedArrays(arrays);
168
 
172
 
169
     // populate all the potential Words for a first time
173
     // populate all the potential Words for a first time
173
     let lowestWordArray;
177
     let lowestWordArray;
174
     let wordToAdd;
178
     let wordToAdd;
175
     let foundSmaller;
179
     let foundSmaller;
180
+
176
     while(hasPopulatedArrays(arrays)) {
181
     while(hasPopulatedArrays(arrays)) {
177
         // first select the lowest array;
182
         // first select the lowest array;
178
         lowestWordArray = arrays[0];
183
         lowestWordArray = arrays[0];
242
             twoDimensionalArray.splice(i, 1);
247
             twoDimensionalArray.splice(i, 1);
243
         }
248
         }
244
     }
249
     }
250
+
245
     return twoDimensionalArray.length > 0;
251
     return twoDimensionalArray.length > 0;
246
 };
252
 };
247
 
253
 
259
     } else{
265
     } else{
260
         if(array[array.length - 1].begin <= word.begin) {
266
         if(array[array.length - 1].begin <= word.begin) {
261
             array.push(word);
267
             array.push(word);
268
+
262
             return;
269
             return;
263
         }
270
         }
264
         let i;
271
         let i;
272
+
265
         for(i = 0; i < array.length; i++) {
273
         for(i = 0; i < array.length; i++) {
266
             if(word.begin < array[i].begin) {
274
             if(word.begin < array[i].begin) {
267
                 array.splice(i, 0, word);
275
                 array.splice(i, 0, word);
276
+
268
                 return;
277
                 return;
269
             }
278
             }
270
         }
279
         }
302
                  FINISHED_STATE}" state. It's currently in the "${
311
                  FINISHED_STATE}" state. It's currently in the "${
303
                  this.state}" state`);
312
                  this.state}" state`);
304
     }
313
     }
314
+
305
     return this.transcription;
315
     return this.transcription;
306
 };
316
 };
307
 
317
 

+ 1
- 0
modules/transcription/transcriptionServices/AbstractTranscriptionService.js View File

18
  */
18
  */
19
 TranscriptionService.prototype.send = function send(recordingResult, callback) {
19
 TranscriptionService.prototype.send = function send(recordingResult, callback) {
20
     const t = this;
20
     const t = this;
21
+
21
     this.sendRequest(recordingResult.blob, response => {
22
     this.sendRequest(recordingResult.blob, response => {
22
         if(t.verify(response)) {
23
         if(t.verify(response)) {
23
             recordingResult.wordArray = t.formatResponse(response);
24
             recordingResult.wordArray = t.formatResponse(response);

+ 10
- 0
modules/transcription/transcriptionServices/SphinxTranscriptionService.js View File

33
     console.log(`sending an audio file  to ${this.url}`);
33
     console.log(`sending an audio file  to ${this.url}`);
34
     console.log(`the audio file being sent: ${audioFileBlob}`);
34
     console.log(`the audio file being sent: ${audioFileBlob}`);
35
     const request = new XMLHttpRequest();
35
     const request = new XMLHttpRequest();
36
+
36
     request.onreadystatechange = function() {
37
     request.onreadystatechange = function() {
37
         if(request.readyState === XMLHttpRequest.DONE
38
         if(request.readyState === XMLHttpRequest.DONE
38
             && request.status === 200) {
39
             && request.status === 200) {
61
     const result = JSON.parse(response).objects;
62
     const result = JSON.parse(response).objects;
62
     // make sure to delete the session id object, which is always
63
     // make sure to delete the session id object, which is always
63
     // the first value in the JSON array
64
     // the first value in the JSON array
65
+
64
     result.shift();
66
     result.shift();
65
     const array = [];
67
     const array = [];
68
+
66
     result.forEach(
69
     result.forEach(
67
         word =>
70
         word =>
68
             word.filler
71
             word.filler
69
                 || array.push(new Word(word.word, word.start, word.end)));
72
                 || array.push(new Word(word.word, word.start, word.end)));
73
+
70
     return array;
74
     return array;
71
 };
75
 };
72
 
76
 
83
     }
87
     }
84
     // test if the string can be parsed into valid JSON
88
     // test if the string can be parsed into valid JSON
85
     let json;
89
     let json;
90
+
86
     try{
91
     try{
87
         json = JSON.parse(response);
92
         json = JSON.parse(response);
88
     } catch (error) {
93
     } catch (error) {
89
         console.log(error);
94
         console.log(error);
95
+
90
         return false;
96
         return false;
91
     }
97
     }
92
     // check if the JSON has a "objects" value
98
     // check if the JSON has a "objects" value
95
     }
101
     }
96
     // get the "objects" value and check for a session ID
102
     // get the "objects" value and check for a session ID
97
     const array = json.objects;
103
     const array = json.objects;
104
+
98
     if(!(array[0] && array[0]['session-id'])) {
105
     if(!(array[0] && array[0]['session-id'])) {
99
         return false;
106
         return false;
100
     }
107
     }
108
+
101
     // everything seems to be in order
109
     // everything seems to be in order
102
     return true;
110
     return true;
103
 };
111
 };
110
  */
118
  */
111
 function getURL() {
119
 function getURL() {
112
     const message = 'config does not contain an url to a Sphinx4 https server';
120
     const message = 'config does not contain an url to a Sphinx4 https server';
121
+
113
     if(config.sphinxURL === undefined) {
122
     if(config.sphinxURL === undefined) {
114
         console.log(message);
123
         console.log(message);
115
     } else {
124
     } else {
116
         const toReturn = config.sphinxURL;
125
         const toReturn = config.sphinxURL;
126
+
117
         if(toReturn.includes !== undefined && toReturn.includes('https://')) {
127
         if(toReturn.includes !== undefined && toReturn.includes('https://')) {
118
             return toReturn;
128
             return toReturn;
119
         }
129
         }

+ 2
- 0
modules/util/AuthUtil.js View File

22
      */
22
      */
23
     getTokenAuthUrl(urlPattern, roomName, roleUpgrade) {
23
     getTokenAuthUrl(urlPattern, roomName, roleUpgrade) {
24
         const url = urlPattern;
24
         const url = urlPattern;
25
+
25
         if (typeof url !== 'string') {
26
         if (typeof url !== 'string') {
26
             return null;
27
             return null;
27
         }
28
         }
29
+
28
         return url.replace('{room}', roomName)
30
         return url.replace('{room}', roomName)
29
             .replace('{roleUpgrade}', roleUpgrade === true);
31
             .replace('{roleUpgrade}', roleUpgrade === true);
30
     }
32
     }

+ 1
- 0
modules/util/EventEmitterForwarder.js View File

25
 EventEmitterForwarder.prototype.forward = function(...args) {
25
 EventEmitterForwarder.prototype.forward = function(...args) {
26
     const srcEvent = args[0];
26
     const srcEvent = args[0];
27
     // This will be the "this" value for emit function.
27
     // This will be the "this" value for emit function.
28
+
28
     args[0] = this.dest;
29
     args[0] = this.dest;
29
     // Using bind.apply to pass the arguments as Array-like object ("arguments")
30
     // Using bind.apply to pass the arguments as Array-like object ("arguments")
30
     this.src.addListener(
31
     this.src.addListener(

+ 2
- 0
modules/util/GlobalOnErrorHandler.js View File

57
      */
57
      */
58
     callErrorHandler(error) {
58
     callErrorHandler(error) {
59
         const errHandler = window.onerror;
59
         const errHandler = window.onerror;
60
+
60
         if(!errHandler) {
61
         if(!errHandler) {
61
             return;
62
             return;
62
         }
63
         }
68
      */
69
      */
69
     callUnhandledRejectionHandler(error) {
70
     callUnhandledRejectionHandler(error) {
70
         const errHandler = window.onunhandledrejection;
71
         const errHandler = window.onunhandledrejection;
72
+
71
         if(!errHandler) {
73
         if(!errHandler) {
72
             return;
74
             return;
73
         }
75
         }

+ 2
- 0
modules/util/RandomUtil.js View File

61
      */
61
      */
62
     randomHexString(len) {
62
     randomHexString(len) {
63
         let ret = '';
63
         let ret = '';
64
+
64
         while (len--) {
65
         while (len--) {
65
             ret += this.randomHexDigit();
66
             ret += this.randomHexDigit();
66
         }
67
         }
68
+
67
         return ret;
69
         return ret;
68
     },
70
     },
69
     randomElement,
71
     randomElement,

+ 2
- 0
modules/util/ScriptUtil.js View File

34
             // finds the src url of the current loaded script
34
             // finds the src url of the current loaded script
35
             // and use it as base of the src supplied argument
35
             // and use it as base of the src supplied argument
36
             const scriptEl = currentExecutingScript();
36
             const scriptEl = currentExecutingScript();
37
+
37
             if(scriptEl) {
38
             if(scriptEl) {
38
                 const scriptSrc = scriptEl.src;
39
                 const scriptSrc = scriptEl.src;
39
                 const baseScriptSrc
40
                 const baseScriptSrc
40
                     = scriptSrc.substring(0, scriptSrc.lastIndexOf('/') + 1);
41
                     = scriptSrc.substring(0, scriptSrc.lastIndexOf('/') + 1);
42
+
41
                 if (scriptSrc && baseScriptSrc) {
43
                 if (scriptSrc && baseScriptSrc) {
42
                     src = baseScriptSrc + src;
44
                     src = baseScriptSrc + src;
43
                 }
45
                 }

+ 6
- 0
modules/version/ComponentsVersions.js View File

38
     = function(node, mucResource, mucJid) {
38
     = function(node, mucResource, mucJid) {
39
         if (node.attributes.xmlns !== 'http://jitsi.org/jitmeet') {
39
         if (node.attributes.xmlns !== 'http://jitsi.org/jitmeet') {
40
             logger.warn('Ignored presence versions node - invalid xmlns', node);
40
             logger.warn('Ignored presence versions node - invalid xmlns', node);
41
+
41
             return;
42
             return;
42
         }
43
         }
43
 
44
 
45
             logger.warn(
46
             logger.warn(
46
                 `Received versions not from the focus user: ${node}`,
47
                 `Received versions not from the focus user: ${node}`,
47
                 mucJid);
48
                 mucJid);
49
+
48
             return;
50
             return;
49
         }
51
         }
50
 
52
 
51
         const log = [];
53
         const log = [];
54
+
52
         node.children.forEach(item => {
55
         node.children.forEach(item => {
53
 
56
 
54
             const componentName = item.attributes.name;
57
             const componentName = item.attributes.name;
58
+
55
             if (componentName !== ComponentsVersions.FOCUS_COMPONENT
59
             if (componentName !== ComponentsVersions.FOCUS_COMPONENT
56
             && componentName !== ComponentsVersions.XMPP_SERVER_COMPONENT
60
             && componentName !== ComponentsVersions.XMPP_SERVER_COMPONENT
57
             && componentName !== ComponentsVersions.VIDEOBRIDGE_COMPONENT) {
61
             && componentName !== ComponentsVersions.VIDEOBRIDGE_COMPONENT) {
58
                 logger.warn(
62
                 logger.warn(
59
                     `Received version for not supported component name: ${
63
                     `Received version for not supported component name: ${
60
                         componentName}`);
64
                         componentName}`);
65
+
61
                 return;
66
                 return;
62
             }
67
             }
63
 
68
 
64
             const version = item.value;
69
             const version = item.value;
70
+
65
             if (this.versions[componentName] !== version) {
71
             if (this.versions[componentName] !== version) {
66
                 this.versions[componentName] = version;
72
                 this.versions[componentName] = version;
67
                 logger.info(`Got ${componentName} version: ${version}`);
73
                 logger.info(`Got ${componentName} version: ${version}`);

+ 15
- 2
modules/xmpp/Caps.js View File

11
 
11
 
12
 function compareIdentities(a, b) {
12
 function compareIdentities(a, b) {
13
     let res = 0;
13
     let res = 0;
14
+
14
     IDENTITY_PROPERTIES_FOR_COMPARE.some(key =>
15
     IDENTITY_PROPERTIES_FOR_COMPARE.some(key =>
15
         (res = ((a[key] > b[key]) && 1) || ((a[key] < b[key]) && -1)) !== 0
16
         (res = ((a[key] > b[key]) && 1) || ((a[key] < b[key]) && -1)) !== 0
16
     );
17
     );
18
+
17
     return res;
19
     return res;
18
 }
20
 }
19
 
21
 
43
         this.rooms = new Set();
45
         this.rooms = new Set();
44
 
46
 
45
         const emuc = connection.emuc;
47
         const emuc = connection.emuc;
48
+
46
         emuc.addListener(XMPPEvents.EMUC_ROOM_ADDED,
49
         emuc.addListener(XMPPEvents.EMUC_ROOM_ADDED,
47
             room => this._addChatRoom(room));
50
             room => this._addChatRoom(room));
48
         emuc.addListener(XMPPEvents.EMUC_ROOM_REMOVED,
51
         emuc.addListener(XMPPEvents.EMUC_ROOM_REMOVED,
104
     getFeatures(jid, timeout = 5000) {
107
     getFeatures(jid, timeout = 5000) {
105
         const user
108
         const user
106
             = jid in this.jidToVersion ? this.jidToVersion[jid] : null;
109
             = jid in this.jidToVersion ? this.jidToVersion[jid] : null;
110
+
107
         if(!user || !(user.version in this.versionToCapabilities)) {
111
         if(!user || !(user.version in this.versionToCapabilities)) {
108
             const node = user ? `${user.node}#${user.version}` : null;
112
             const node = user ? `${user.node}#${user.version}` : null;
113
+
114
+
109
             return new Promise((resolve, reject) =>
115
             return new Promise((resolve, reject) =>
110
                 this.disco.info(jid, node, response => {
116
                 this.disco.info(jid, node, response => {
111
                     const features = new Set();
117
                     const features = new Set();
112
-                    $(response).find('>query>feature').each((idx, el) =>
113
-                            features.add(el.getAttribute('var')));
118
+
119
+                    $(response)
120
+                        .find('>query>feature')
121
+                        .each(
122
+                            (idx, el) => features.add(el.getAttribute('var')));
114
                     if(user) {
123
                     if(user) {
115
                             // TODO: Maybe use the version + node + hash
124
                             // TODO: Maybe use the version + node + hash
116
                             // as keys?
125
                             // as keys?
121
                 }, reject , timeout)
130
                 }, reject , timeout)
122
             );
131
             );
123
         }
132
         }
133
+
124
         return Promise.resolve(this.versionToCapabilities[user.version]);
134
         return Promise.resolve(this.versionToCapabilities[user.version]);
125
     }
135
     }
126
 
136
 
175
     _generateVersion() {
185
     _generateVersion() {
176
         const identities = this.disco._identities.sort(compareIdentities);
186
         const identities = this.disco._identities.sort(compareIdentities);
177
         const features = this.disco._features.sort();
187
         const features = this.disco._features.sort();
188
+
178
         this.version = b64_sha1(
189
         this.version = b64_sha1(
179
             identities.reduce(
190
             identities.reduce(
180
                     (accumulatedValue, identity) =>
191
                     (accumulatedValue, identity) =>
200
         const version = caps.getAttribute('ver');
211
         const version = caps.getAttribute('ver');
201
         const node = caps.getAttribute('node');
212
         const node = caps.getAttribute('node');
202
         const oldVersion = this.jidToVersion[from];
213
         const oldVersion = this.jidToVersion[from];
214
+
203
         this.jidToVersion[from] = {version, node};
215
         this.jidToVersion[from] = {version, node};
204
         if(oldVersion && oldVersion.version !== version) {
216
         if(oldVersion && oldVersion.version !== version) {
205
             this.eventEmitter.emit(XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
217
             this.eventEmitter.emit(XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
206
                 from);
218
                 from);
207
         }
219
         }
220
+
208
         // return true to not remove the handler from Strophe
221
         // return true to not remove the handler from Strophe
209
         return true;
222
         return true;
210
     }
223
     }

+ 58
- 1
modules/xmpp/ChatRoom.js View File

12
 const parser = {
12
 const parser = {
13
     packet2JSON(packet, nodes) {
13
     packet2JSON(packet, nodes) {
14
         const self = this;
14
         const self = this;
15
+
16
+        // eslint-disable-next-line newline-per-chained-call
15
         $(packet).children().each(function() {
17
         $(packet).children().each(function() {
16
             const tagName = $(this).prop('tagName');
18
             const tagName = $(this).prop('tagName');
17
             const node = {
19
             const node = {
18
                 tagName
20
                 tagName
19
             };
21
             };
22
+
20
             node.attributes = {};
23
             node.attributes = {};
21
             $($(this)[0].attributes).each((index, attr) => {
24
             $($(this)[0].attributes).each((index, attr) => {
22
                 node.attributes[ attr.name ] = attr.value;
25
                 node.attributes[ attr.name ] = attr.value;
23
             });
26
             });
24
             const text = Strophe.getText($(this)[0]);
27
             const text = Strophe.getText($(this)[0]);
28
+
25
             if (text) {
29
             if (text) {
26
                 node.value = text;
30
                 node.value = text;
27
             }
31
             }
33
     json2packet(nodes, packet) {
37
     json2packet(nodes, packet) {
34
         for(let i = 0; i < nodes.length; i++) {
38
         for(let i = 0; i < nodes.length; i++) {
35
             const node = nodes[i];
39
             const node = nodes[i];
40
+
36
             if(node) {
41
             if(node) {
37
                 packet.c(node.tagName, node.attributes);
42
                 packet.c(node.tagName, node.attributes);
38
                 if(node.value) {
43
                 if(node.value) {
56
  */
61
  */
57
 function filterNodeFromPresenceJSON(pres, nodeName) {
62
 function filterNodeFromPresenceJSON(pres, nodeName) {
58
     const res = [];
63
     const res = [];
64
+
59
     for(let i = 0; i < pres.length; i++) {
65
     for(let i = 0; i < pres.length; i++) {
60
         if(pres[i].tagName === nodeName) {
66
         if(pres[i].tagName === nodeName) {
61
             res.push(pres[i]);
67
             res.push(pres[i]);
65
     return res;
71
     return res;
66
 }
72
 }
67
 
73
 
74
+// XXX As ChatRoom constructs XMPP stanzas and Strophe is build around the idea
75
+// of chaining function calls, allow long function call chains.
76
+/* eslint-disable newline-per-chained-call */
77
+
68
 export default class ChatRoom extends Listenable {
78
 export default class ChatRoom extends Listenable {
69
     constructor(connection, jid, password, XMPP, options) {
79
     constructor(connection, jid, password, XMPP, options) {
70
         super();
80
         super();
131
 
141
 
132
     sendPresence(fromJoin) {
142
     sendPresence(fromJoin) {
133
         const to = this.presMap.to;
143
         const to = this.presMap.to;
144
+
134
         if (!to || (!this.joined && !fromJoin)) {
145
         if (!to || (!this.joined && !fromJoin)) {
135
             // Too early to send presence - not initialized
146
             // Too early to send presence - not initialized
136
             return;
147
             return;
170
     doLeave() {
181
     doLeave() {
171
         logger.log('do leave', this.myroomjid);
182
         logger.log('do leave', this.myroomjid);
172
         const pres = $pres({to: this.myroomjid, type: 'unavailable' });
183
         const pres = $pres({to: this.myroomjid, type: 'unavailable' });
184
+
173
         this.presMap.length = 0;
185
         this.presMap.length = 0;
174
 
186
 
175
         // XXX Strophe is asynchronously sending by default. Unfortunately, that
187
         // XXX Strophe is asynchronously sending by default. Unfortunately, that
199
                 = $(result).find('>query>feature[var="muc_passwordprotected"]')
211
                 = $(result).find('>query>feature[var="muc_passwordprotected"]')
200
                         .length
212
                         .length
201
                     === 1;
213
                     === 1;
214
+
202
             if (locked != this.locked) {
215
             if (locked != this.locked) {
203
                 this.eventEmitter.emit(XMPPEvents.MUC_LOCK_CHANGED, locked);
216
                 this.eventEmitter.emit(XMPPEvents.MUC_LOCK_CHANGED, locked);
204
                 this.locked = locked;
217
                 this.locked = locked;
224
                     '>query>x[xmlns="jabber:x:data"]'
237
                     '>query>x[xmlns="jabber:x:data"]'
225
                     + '>field[var="muc#roomconfig_whois"]').length) {
238
                     + '>field[var="muc#roomconfig_whois"]').length) {
226
                 const errmsg = 'non-anonymous rooms not supported';
239
                 const errmsg = 'non-anonymous rooms not supported';
240
+
227
                 GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
241
                 GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
228
                 logger.error(errmsg);
242
                 logger.error(errmsg);
243
+
229
                 return;
244
                 return;
230
             }
245
             }
231
 
246
 
253
         const from = pres.getAttribute('from');
268
         const from = pres.getAttribute('from');
254
         // Parse roles.
269
         // Parse roles.
255
         const member = {};
270
         const member = {};
271
+
256
         member.show = $(pres).find('>show').text();
272
         member.show = $(pres).find('>show').text();
257
         member.status = $(pres).find('>status').text();
273
         member.status = $(pres).find('>status').text();
258
         const mucUserItem
274
         const mucUserItem
259
-            = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
275
+            = $(pres).find(
276
+                '>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
277
+
260
         member.affiliation = mucUserItem.attr('affiliation');
278
         member.affiliation = mucUserItem.attr('affiliation');
261
         member.role = mucUserItem.attr('role');
279
         member.role = mucUserItem.attr('role');
262
 
280
 
263
         // Focus recognition
281
         // Focus recognition
264
         const jid = mucUserItem.attr('jid');
282
         const jid = mucUserItem.attr('jid');
283
+
265
         member.jid = jid;
284
         member.jid = jid;
266
         member.isFocus
285
         member.isFocus
267
             = jid && jid.indexOf(`${this.moderator.getFocusUserJid()}/`) === 0;
286
             = jid && jid.indexOf(`${this.moderator.getFocusUserJid()}/`) === 0;
272
 
291
 
273
         $(pres).find('>x').remove();
292
         $(pres).find('>x').remove();
274
         const nodes = [];
293
         const nodes = [];
294
+
275
         parser.packet2JSON(pres, nodes);
295
         parser.packet2JSON(pres, nodes);
276
         this.lastPresences[from] = nodes;
296
         this.lastPresences[from] = nodes;
277
         let jibri = null;
297
         let jibri = null;
278
         // process nodes to extract data needed for MUC_JOINED and
298
         // process nodes to extract data needed for MUC_JOINED and
279
         // MUC_MEMBER_JOINED events
299
         // MUC_MEMBER_JOINED events
300
+
280
         for(let i = 0; i < nodes.length; i++) {
301
         for(let i = 0; i < nodes.length; i++) {
281
             const node = nodes[i];
302
             const node = nodes[i];
303
+
282
             switch(node.tagName) {
304
             switch(node.tagName) {
283
             case 'nick':
305
             case 'nick':
284
                 member.nick = node.value;
306
                 member.nick = node.value;
291
 
313
 
292
         if (from == this.myroomjid) {
314
         if (from == this.myroomjid) {
293
             const newRole = member.affiliation == 'owner' ? member.role : 'none';
315
             const newRole = member.affiliation == 'owner' ? member.role : 'none';
316
+
294
             if (this.role !== newRole) {
317
             if (this.role !== newRole) {
295
                 this.role = newRole;
318
                 this.role = newRole;
296
                 this.eventEmitter.emit(XMPPEvents.LOCAL_ROLE_CHANGED, this.role);
319
                 this.eventEmitter.emit(XMPPEvents.LOCAL_ROLE_CHANGED, this.role);
299
                 this.joined = true;
322
                 this.joined = true;
300
                 const now = this.connectionTimes['muc.joined']
323
                 const now = this.connectionTimes['muc.joined']
301
                     = window.performance.now();
324
                     = window.performance.now();
325
+
302
                 logger.log('(TIME) MUC joined:\t', now);
326
                 logger.log('(TIME) MUC joined:\t', now);
303
 
327
 
304
                 // set correct initial state of locked
328
                 // set correct initial state of locked
323
             // Presence update for existing participant
347
             // Presence update for existing participant
324
             // Watch role change:
348
             // Watch role change:
325
             const memberOfThis = this.members[from];
349
             const memberOfThis = this.members[from];
350
+
326
             if (memberOfThis.role != member.role) {
351
             if (memberOfThis.role != member.role) {
327
                 memberOfThis.role = member.role;
352
                 memberOfThis.role = member.role;
328
                 this.eventEmitter.emit(
353
                 this.eventEmitter.emit(
352
         // for the rest info we got in presence
377
         // for the rest info we got in presence
353
         for(let i = 0; i < nodes.length; i++) {
378
         for(let i = 0; i < nodes.length; i++) {
354
             const node = nodes[i];
379
             const node = nodes[i];
380
+
355
             switch(node.tagName) {
381
             switch(node.tagName) {
356
             case 'nick':
382
             case 'nick':
357
                 if(!member.isFocus) {
383
                 if(!member.isFocus) {
375
                 break;
401
                 break;
376
             case 'call-control': {
402
             case 'call-control': {
377
                 const att = node.attributes;
403
                 const att = node.attributes;
404
+
378
                 if(!att) {
405
                 if(!att) {
379
                     break;
406
                     break;
380
                 }
407
                 }
435
         // otherwise we can remove the presence handler from strophe
462
         // otherwise we can remove the presence handler from strophe
436
         try {
463
         try {
437
             let tagHandler = this.presHandlers[node.tagName];
464
             let tagHandler = this.presHandlers[node.tagName];
465
+
438
             if (node.tagName.startsWith('jitsi_participant_')) {
466
             if (node.tagName.startsWith('jitsi_participant_')) {
439
                 tagHandler = this.participantPropertyListener;
467
                 tagHandler = this.participantPropertyListener;
440
             }
468
             }
450
 
478
 
451
     sendMessage(body, nickname) {
479
     sendMessage(body, nickname) {
452
         const msg = $msg({to: this.roomjid, type: 'groupchat'});
480
         const msg = $msg({to: this.roomjid, type: 'groupchat'});
481
+
453
         msg.c('body', body).up();
482
         msg.c('body', body).up();
454
         if (nickname) {
483
         if (nickname) {
455
             msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
484
             msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
460
 
489
 
461
     setSubject(subject) {
490
     setSubject(subject) {
462
         const msg = $msg({to: this.roomjid, type: 'groupchat'});
491
         const msg = $msg({to: this.roomjid, type: 'groupchat'});
492
+
463
         msg.c('subject', subject);
493
         msg.c('subject', subject);
464
         this.connection.send(msg);
494
         this.connection.send(msg);
465
     }
495
     }
491
             const reasonSelect = $(pres).find(
521
             const reasonSelect = $(pres).find(
492
                     '>x[xmlns="http://jabber.org/protocol/muc#user"]'
522
                     '>x[xmlns="http://jabber.org/protocol/muc#user"]'
493
                     + '>destroy>reason');
523
                     + '>destroy>reason');
524
+
494
             if (reasonSelect.length) {
525
             if (reasonSelect.length) {
495
                 reason = reasonSelect.text();
526
                 reason = reasonSelect.text();
496
             }
527
             }
499
 
530
 
500
             this.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);
531
             this.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);
501
             this.connection.emuc.doLeave(this.roomjid);
532
             this.connection.emuc.doLeave(this.roomjid);
533
+
502
             return true;
534
             return true;
503
         }
535
         }
504
 
536
 
510
                 '>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]'
542
                 '>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]'
511
             ).length !== 0;
543
             ).length !== 0;
512
         const membersKeys = Object.keys(this.members);
544
         const membersKeys = Object.keys(this.members);
545
+
513
         if (!isSelfPresence) {
546
         if (!isSelfPresence) {
514
             delete this.members[from];
547
             delete this.members[from];
515
             this.onParticipantLeft(from, false);
548
             this.onParticipantLeft(from, false);
519
             // event.
552
             // event.
520
             membersKeys.forEach(jid => {
553
             membersKeys.forEach(jid => {
521
                 const member = this.members[jid];
554
                 const member = this.members[jid];
555
+
522
                 delete this.members[jid];
556
                 delete this.members[jid];
523
                 this.onParticipantLeft(jid, member.isFocus);
557
                 this.onParticipantLeft(jid, member.isFocus);
524
             });
558
             });
545
 
579
 
546
         const txt = $(msg).find('>body').text();
580
         const txt = $(msg).find('>body').text();
547
         const type = msg.getAttribute('type');
581
         const type = msg.getAttribute('type');
582
+
548
         if (type == 'error') {
583
         if (type == 'error') {
549
             this.eventEmitter.emit(XMPPEvents.CHAT_ERROR_RECEIVED,
584
             this.eventEmitter.emit(XMPPEvents.CHAT_ERROR_RECEIVED,
550
                 $(msg).find('>text').text(), txt);
585
                 $(msg).find('>text').text(), txt);
586
+
551
             return true;
587
             return true;
552
         }
588
         }
553
 
589
 
554
         const subject = $(msg).find('>subject');
590
         const subject = $(msg).find('>subject');
591
+
555
         if (subject.length) {
592
         if (subject.length) {
556
             const subjectText = subject.text();
593
             const subjectText = subject.text();
594
+
557
             if (subjectText || subjectText === '') {
595
             if (subjectText || subjectText === '') {
558
                 this.eventEmitter.emit(XMPPEvents.SUBJECT_CHANGED, subjectText);
596
                 this.eventEmitter.emit(XMPPEvents.SUBJECT_CHANGED, subjectText);
559
                 logger.log(`Subject is changed to ${subjectText}`);
597
                 logger.log(`Subject is changed to ${subjectText}`);
570
             if (stamp) {
608
             if (stamp) {
571
                 // the format is CCYYMMDDThh:mm:ss
609
                 // the format is CCYYMMDDThh:mm:ss
572
                 const dateParts = stamp.match(/(\d{4})(\d{2})(\d{2}T\d{2}:\d{2}:\d{2})/);
610
                 const dateParts = stamp.match(/(\d{4})(\d{2})(\d{2}T\d{2}:\d{2}:\d{2})/);
611
+
573
                 stamp = `${dateParts[1]}-${dateParts[2]}-${dateParts[3]}Z`;
612
                 stamp = `${dateParts[1]}-${dateParts[2]}-${dateParts[3]}Z`;
574
             }
613
             }
575
         }
614
         }
592
         } else if ($(pres).find(
631
         } else if ($(pres).find(
593
             '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
632
             '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
594
             const toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
633
             const toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
634
+
595
             if (toDomain === this.xmpp.options.hosts.anonymousdomain) {
635
             if (toDomain === this.xmpp.options.hosts.anonymousdomain) {
596
                 // enter the room by replying with 'not-authorized'. This would
636
                 // enter the room by replying with 'not-authorized'. This would
597
                 // result in reconnection from authorized domain.
637
                 // result in reconnection from authorized domain.
634
                     const formsubmit
674
                     const formsubmit
635
                         = $iq({to: this.roomjid, type: 'set'})
675
                         = $iq({to: this.roomjid, type: 'set'})
636
                             .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
676
                             .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
677
+
637
                     formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
678
                     formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
638
                     formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
679
                     formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
639
                     formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
680
                     formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
656
 
697
 
657
     removeFromPresence(key) {
698
     removeFromPresence(key) {
658
         const nodes = this.presMap.nodes.filter(node => key !== node.tagName);
699
         const nodes = this.presMap.nodes.filter(node => key !== node.tagName);
700
+
659
         this.presMap.nodes = nodes;
701
         this.presMap.nodes = nodes;
660
     }
702
     }
661
 
703
 
677
      */
719
      */
678
     isFocus(mucJid) {
720
     isFocus(mucJid) {
679
         const member = this.members[mucJid];
721
         const member = this.members[mucJid];
722
+
680
         if (member) {
723
         if (member) {
681
             return member.isFocus;
724
             return member.isFocus;
682
         }
725
         }
726
+
683
         return null;
727
         return null;
684
     }
728
     }
685
 
729
 
691
         if (this.members[peerJid]) {
735
         if (this.members[peerJid]) {
692
             return this.members[peerJid].role;
736
             return this.members[peerJid].role;
693
         }
737
         }
738
+
694
         return null;
739
         return null;
695
     }
740
     }
696
 
741
 
753
     getMediaPresenceInfo(endpointId, mediaType) {
798
     getMediaPresenceInfo(endpointId, mediaType) {
754
         // Will figure out current muted status by looking up owner's presence
799
         // Will figure out current muted status by looking up owner's presence
755
         const pres = this.lastPresences[`${this.roomjid}/${endpointId}`];
800
         const pres = this.lastPresences[`${this.roomjid}/${endpointId}`];
801
+
756
         if (!pres) {
802
         if (!pres) {
757
             // No presence available
803
             // No presence available
758
             return null;
804
             return null;
774
             }
820
             }
775
         } else {
821
         } else {
776
             logger.error(`Unsupported media type: ${mediaType}`);
822
             logger.error(`Unsupported media type: ${mediaType}`);
823
+
777
             return null;
824
             return null;
778
         }
825
         }
779
 
826
 
789
         if(this.recording) {
836
         if(this.recording) {
790
             return this.recording.isSupported();
837
             return this.recording.isSupported();
791
         }
838
         }
839
+
792
         return false;
840
         return false;
793
     }
841
     }
794
 
842
 
828
         if(this.moderator) {
876
         if(this.moderator) {
829
             return this.moderator.isSipGatewayEnabled();
877
             return this.moderator.isSipGatewayEnabled();
830
         }
878
         }
879
+
831
         return false;
880
         return false;
832
     }
881
     }
833
 
882
 
886
 
935
 
887
     onMute(iq) {
936
     onMute(iq) {
888
         const from = iq.getAttribute('from');
937
         const from = iq.getAttribute('from');
938
+
889
         if (from !== this.focusMucJid) {
939
         if (from !== this.focusMucJid) {
890
             logger.warn('Ignored mute from non focus peer');
940
             logger.warn('Ignored mute from non focus peer');
941
+
891
             return false;
942
             return false;
892
         }
943
         }
893
         const mute = $(iq).find('mute');
944
         const mute = $(iq).find('mute');
945
+
894
         if (mute.length) {
946
         if (mute.length) {
895
             const doMuteAudio = mute.text() === 'true';
947
             const doMuteAudio = mute.text() === 'true';
948
+
896
             this.eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, doMuteAudio);
949
             this.eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, doMuteAudio);
897
         }
950
         }
951
+
898
         return true;
952
         return true;
899
     }
953
     }
900
 
954
 
908
         return new Promise((resolve, reject) => {
962
         return new Promise((resolve, reject) => {
909
             const timeout = setTimeout(() => onMucLeft(true), 5000);
963
             const timeout = setTimeout(() => onMucLeft(true), 5000);
910
             const eventEmitter = this.eventEmitter;
964
             const eventEmitter = this.eventEmitter;
965
+
911
             function onMucLeft(doReject = false) {
966
             function onMucLeft(doReject = false) {
912
                 eventEmitter.removeListener(XMPPEvents.MUC_LEFT, onMucLeft);
967
                 eventEmitter.removeListener(XMPPEvents.MUC_LEFT, onMucLeft);
913
                 clearTimeout(timeout);
968
                 clearTimeout(timeout);
924
         });
979
         });
925
     }
980
     }
926
 }
981
 }
982
+
983
+/* eslint-enable newline-per-chained-call */

+ 1
- 0
modules/xmpp/JingleSession.js View File

73
             const errmsg
73
             const errmsg
74
                 = `attempt to initiate on session ${this.sid}
74
                 = `attempt to initiate on session ${this.sid}
75
                    in state ${this.state}`;
75
                    in state ${this.state}`;
76
+
76
             logger.error(errmsg);
77
             logger.error(errmsg);
77
             throw new Error(errmsg);
78
             throw new Error(errmsg);
78
         }
79
         }

+ 120
- 11
modules/xmpp/JingleSessionPC.js View File

2
 
2
 
3
 import {getLogger} from 'jitsi-meet-logger';
3
 import {getLogger} from 'jitsi-meet-logger';
4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
+
5
 import JingleSession from './JingleSession';
6
 import JingleSession from './JingleSession';
6
 const SDPDiffer = require('./SDPDiffer');
7
 const SDPDiffer = require('./SDPDiffer');
7
 const SDPUtil = require('./SDPUtil');
8
 const SDPUtil = require('./SDPUtil');
124
             }
125
             }
125
             // XXX this is broken, candidate is not parsed.
126
             // XXX this is broken, candidate is not parsed.
126
             const candidate = ev.candidate;
127
             const candidate = ev.candidate;
128
+
127
             if (candidate) {
129
             if (candidate) {
128
                 // Discard candidates of disabled protocols.
130
                 // Discard candidates of disabled protocols.
129
                 let protocol = candidate.protocol;
131
                 let protocol = candidate.protocol;
132
+
130
                 if (typeof protocol === 'string') {
133
                 if (typeof protocol === 'string') {
131
                     protocol = protocol.toLowerCase();
134
                     protocol = protocol.toLowerCase();
132
                     if (protocol === 'tcp' || protocol === 'ssltcp') {
135
                     if (protocol === 'tcp' || protocol === 'ssltcp') {
173
                 return;
176
                 return;
174
             }
177
             }
175
             const now = window.performance.now();
178
             const now = window.performance.now();
179
+
176
             this.room.connectionTimes[
180
             this.room.connectionTimes[
177
                     `ice.state.${this.peerconnection.iceConnectionState}`]
181
                     `ice.state.${this.peerconnection.iceConnectionState}`]
178
                 = now;
182
                 = now;
222
 
226
 
223
     sendIceCandidate(candidate) {
227
     sendIceCandidate(candidate) {
224
         const localSDP = new SDP(this.peerconnection.localDescription.sdp);
228
         const localSDP = new SDP(this.peerconnection.localDescription.sdp);
229
+
225
         if (candidate && !this.lasticecandidate) {
230
         if (candidate && !this.lasticecandidate) {
226
             const ice
231
             const ice
227
                 = SDPUtil.iceparams(
232
                 = SDPUtil.iceparams(
228
                     localSDP.media[candidate.sdpMLineIndex], localSDP.session);
233
                     localSDP.media[candidate.sdpMLineIndex], localSDP.session);
229
             const jcand = SDPUtil.candidateToJingle(candidate.candidate);
234
             const jcand = SDPUtil.candidateToJingle(candidate.candidate);
235
+
230
             if (!(ice && jcand)) {
236
             if (!(ice && jcand)) {
231
                 const errorMesssage = 'failed to get ice && jcand';
237
                 const errorMesssage = 'failed to get ice && jcand';
238
+
232
                 GlobalOnErrorHandler.callErrorHandler(new Error(errorMesssage));
239
                 GlobalOnErrorHandler.callErrorHandler(new Error(errorMesssage));
233
                 logger.error(errorMesssage);
240
                 logger.error(errorMesssage);
241
+
234
                 return;
242
                 return;
235
             }
243
             }
236
             ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
244
             ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
266
                 sid: this.sid});
274
                 sid: this.sid});
267
 
275
 
268
         const localSDP = new SDP(this.peerconnection.localDescription.sdp);
276
         const localSDP = new SDP(this.peerconnection.localDescription.sdp);
277
+
269
         for (let mid = 0; mid < localSDP.media.length; mid++) {
278
         for (let mid = 0; mid < localSDP.media.length; mid++) {
270
             const cands = candidates.filter(el => el.sdpMLineIndex == mid);
279
             const cands = candidates.filter(el => el.sdpMLineIndex == mid);
271
             const mline
280
             const mline
272
                 = SDPUtil.parse_mline(localSDP.media[mid].split('\r\n')[0]);
281
                 = SDPUtil.parse_mline(localSDP.media[mid].split('\r\n')[0]);
282
+
273
             if (cands.length > 0) {
283
             if (cands.length > 0) {
274
                 const ice
284
                 const ice
275
                     = SDPUtil.iceparams(localSDP.media[mid], localSDP.session);
285
                     = SDPUtil.iceparams(localSDP.media[mid], localSDP.session);
286
+
276
                 ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
287
                 ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
277
                 cand.c('content', {
288
                 cand.c('content', {
278
                     creator: this.initiator == this.localJid
289
                     creator: this.initiator == this.localJid
283
                     const candidate
294
                     const candidate
284
                         = SDPUtil.candidateToJingle(cands[i].candidate);
295
                         = SDPUtil.candidateToJingle(cands[i].candidate);
285
                     // Mangle ICE candidate if 'failICE' test option is enabled
296
                     // Mangle ICE candidate if 'failICE' test option is enabled
297
+
286
                     if (this.failICE) {
298
                     if (this.failICE) {
287
                         candidate.ip = '1.1.1.1';
299
                         candidate.ip = '1.1.1.1';
288
                     }
300
                     }
296
 
308
 
297
                 if (fingerprint_line) {
309
                 if (fingerprint_line) {
298
                     const tmp = SDPUtil.parse_fingerprint(fingerprint_line);
310
                     const tmp = SDPUtil.parse_fingerprint(fingerprint_line);
311
+
299
                     tmp.required = true;
312
                     tmp.required = true;
300
                     cand.c(
313
                     cand.c(
301
                         'fingerprint',
314
                         'fingerprint',
325
                 = $(content).find(
338
                 = $(content).find(
326
                     'description>'
339
                     'description>'
327
                         + 'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
340
                         + 'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
341
+
328
             ssrcs.each((idx, ssrcElement) => {
342
             ssrcs.each((idx, ssrcElement) => {
329
                 const ssrc = ssrcElement.getAttribute('ssrc');
343
                 const ssrc = ssrcElement.getAttribute('ssrc');
344
+
330
                 $(ssrcElement)
345
                 $(ssrcElement)
331
                     .find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]')
346
                     .find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]')
332
                     .each((idx, ssrcInfoElement) => {
347
                     .each((idx, ssrcInfoElement) => {
333
                         const owner = ssrcInfoElement.getAttribute('owner');
348
                         const owner = ssrcInfoElement.getAttribute('owner');
349
+
334
                         if (owner && owner.length) {
350
                         if (owner && owner.length) {
335
                             this.ssrcOwners[ssrc]
351
                             this.ssrcOwners[ssrc]
336
                                 = Strophe.getResourceFromJid(owner);
352
                                 = Strophe.getResourceFromJid(owner);
392
     setOfferCycle(jingleOfferIq, success, failure) {
408
     setOfferCycle(jingleOfferIq, success, failure) {
393
         const workFunction = finishedCallback => {
409
         const workFunction = finishedCallback => {
394
             const newRemoteSdp = this._processNewJingleOfferIq(jingleOfferIq);
410
             const newRemoteSdp = this._processNewJingleOfferIq(jingleOfferIq);
411
+
395
             this._renegotiate(newRemoteSdp)
412
             this._renegotiate(newRemoteSdp)
396
                 .then(() => {
413
                 .then(() => {
397
                     finishedCallback();
414
                     finishedCallback();
403
                     finishedCallback(error);
420
                     finishedCallback(error);
404
                 });
421
                 });
405
         };
422
         };
423
+
406
         this.modificationQueue.push(
424
         this.modificationQueue.push(
407
             workFunction,
425
             workFunction,
408
             error => {
426
             error => {
425
         // the SCTP connection established with the new bridge.
443
         // the SCTP connection established with the new bridge.
426
         this.room.eventEmitter.emit(XMPPEvents.ICE_RESTARTING);
444
         this.room.eventEmitter.emit(XMPPEvents.ICE_RESTARTING);
427
         const originalOffer = jingleOfferElem.clone();
445
         const originalOffer = jingleOfferElem.clone();
446
+
428
         jingleOfferElem.find('>content[name=\'data\']').remove();
447
         jingleOfferElem.find('>content[name=\'data\']').remove();
429
 
448
 
430
         // First set an offer without the 'data' section
449
         // First set an offer without the 'data' section
437
                     () => {
456
                     () => {
438
                         const localSDP
457
                         const localSDP
439
                             = new SDP(this.peerconnection.localDescription.sdp);
458
                             = new SDP(this.peerconnection.localDescription.sdp);
459
+
440
                         this.sendTransportAccept(localSDP, success, failure);
460
                         this.sendTransportAccept(localSDP, success, failure);
441
                     },
461
                     },
442
                     failure);
462
                     failure);
463
                 initiator: this.initiator,
483
                 initiator: this.initiator,
464
                 responder: this.responder,
484
                 responder: this.responder,
465
                 sid: this.sid });
485
                 sid: this.sid });
486
+
466
         if (this.webrtcIceTcpDisable) {
487
         if (this.webrtcIceTcpDisable) {
467
             localSDP.removeTcpCandidates = true;
488
             localSDP.removeTcpCandidates = true;
468
         }
489
         }
531
 
552
 
532
         localSDP.media.forEach((medialines, idx) => {
553
         localSDP.media.forEach((medialines, idx) => {
533
             const mline = SDPUtil.parse_mline(medialines.split('\r\n')[0]);
554
             const mline = SDPUtil.parse_mline(medialines.split('\r\n')[0]);
555
+
534
             transportAccept.c('content',
556
             transportAccept.c('content',
535
                 {
557
                 {
536
                     creator:
558
                     creator:
599
             .c(reason || 'success');
621
             .c(reason || 'success');
600
 
622
 
601
         if (text) {
623
         if (text) {
624
+            // eslint-disable-next-line newline-per-chained-call
602
             sessionTerminate.up().c('text').t(text);
625
             sessionTerminate.up().c('text').t(text);
603
         }
626
         }
604
 
627
 
638
      */
661
      */
639
     _parseSsrcInfoFromSourceAdd(sourceAddElem, currentRemoteSdp) {
662
     _parseSsrcInfoFromSourceAdd(sourceAddElem, currentRemoteSdp) {
640
         const addSsrcInfo = [];
663
         const addSsrcInfo = [];
664
+
641
         $(sourceAddElem).each((idx, content) => {
665
         $(sourceAddElem).each((idx, content) => {
642
             const name = $(content).attr('name');
666
             const name = $(content).attr('name');
643
             let lines = '';
667
             let lines = '';
668
+
644
             $(content)
669
             $(content)
645
                 .find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')
670
                 .find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')
646
                 .each(function() {
671
                 .each(function() {
647
                     const semantics = this.getAttribute('semantics');
672
                     const semantics = this.getAttribute('semantics');
648
-                    const ssrcs = $(this).find('>source').map(function() {
649
-                        return this.getAttribute('ssrc');
650
-                    }).get();
673
+                    const ssrcs
674
+                        = $(this)
675
+                            .find('>source')
676
+                            .map(function() {
677
+                                return this.getAttribute('ssrc');
678
+                            })
679
+                            .get();
651
 
680
 
652
                     if (ssrcs.length) {
681
                     if (ssrcs.length) {
653
                         lines
682
                         lines
659
             const tmp
688
             const tmp
660
                 = $(content).find(
689
                 = $(content).find(
661
                     'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
690
                     'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
691
+
662
             tmp.each(function() {
692
             tmp.each(function() {
663
                 const ssrc = $(this).attr('ssrc');
693
                 const ssrc = $(this).attr('ssrc');
694
+
664
                 if (currentRemoteSdp.containsSSRC(ssrc)) {
695
                 if (currentRemoteSdp.containsSSRC(ssrc)) {
665
                     logger.warn(
696
                     logger.warn(
666
                         `Source-add request for existing SSRC: ${ssrc}`);
697
                         `Source-add request for existing SSRC: ${ssrc}`);
698
+
667
                     return;
699
                     return;
668
                 }
700
                 }
701
+
702
+                // eslint-disable-next-line newline-per-chained-call
669
                 $(this).find('>parameter').each(function() {
703
                 $(this).find('>parameter').each(function() {
670
                     lines += `a=ssrc:${ssrc} ${$(this).attr('name')}`;
704
                     lines += `a=ssrc:${ssrc} ${$(this).attr('name')}`;
671
                     if ($(this).attr('value') && $(this).attr('value').length) {
705
                     if ($(this).attr('value') && $(this).attr('value').length) {
684
                 addSsrcInfo[idx] += lines;
718
                 addSsrcInfo[idx] += lines;
685
             });
719
             });
686
         });
720
         });
721
+
687
         return addSsrcInfo;
722
         return addSsrcInfo;
688
     }
723
     }
689
 
724
 
696
         if (!this.peerconnection.localDescription) {
731
         if (!this.peerconnection.localDescription) {
697
             logger.warn('addSource - localDescription not ready yet');
732
             logger.warn('addSource - localDescription not ready yet');
698
             setTimeout(() => this.addRemoteStream(elem), 200);
733
             setTimeout(() => this.addRemoteStream(elem), 200);
734
+
699
             return;
735
             return;
700
         }
736
         }
701
         logger.log('Processing add remote stream');
737
         logger.log('Processing add remote stream');
710
             const addSsrcInfo = this._parseSsrcInfoFromSourceAdd(elem, sdp);
746
             const addSsrcInfo = this._parseSsrcInfoFromSourceAdd(elem, sdp);
711
 
747
 
712
             const newRemoteSdp = this._processRemoteAddSource(addSsrcInfo);
748
             const newRemoteSdp = this._processRemoteAddSource(addSsrcInfo);
749
+
713
             this._renegotiate(newRemoteSdp)
750
             this._renegotiate(newRemoteSdp)
714
                 .then(() => {
751
                 .then(() => {
715
                     logger.info('Remote source-add processed');
752
                     logger.info('Remote source-add processed');
716
                     const newSdp
753
                     const newSdp
717
                         = new SDP(this.peerconnection.localDescription.sdp);
754
                         = new SDP(this.peerconnection.localDescription.sdp);
755
+
718
                     logger.log('SDPs', mySdp, newSdp);
756
                     logger.log('SDPs', mySdp, newSdp);
719
                     this.notifyMySSRCUpdate(mySdp, newSdp);
757
                     this.notifyMySSRCUpdate(mySdp, newSdp);
720
                     finishedCallback();
758
                     finishedCallback();
724
                     finishedCallback(error);
762
                     finishedCallback(error);
725
                 });
763
                 });
726
         };
764
         };
765
+
727
         this.modificationQueue.push(workFunction);
766
         this.modificationQueue.push(workFunction);
728
     }
767
     }
729
 
768
 
736
         if (!this.peerconnection.localDescription) {
775
         if (!this.peerconnection.localDescription) {
737
             logger.warn('removeSource - localDescription not ready yet');
776
             logger.warn('removeSource - localDescription not ready yet');
738
             setTimeout(() => this.removeRemoteStream(elem), 200);
777
             setTimeout(() => this.removeRemoteStream(elem), 200);
778
+
739
             return;
779
             return;
740
         }
780
         }
741
 
781
 
750
 
790
 
751
             const newRemoteSdp
791
             const newRemoteSdp
752
                 = this._processRemoteRemoveSource(removeSsrcInfo);
792
                 = this._processRemoteRemoveSource(removeSsrcInfo);
793
+
753
             this._renegotiate(newRemoteSdp)
794
             this._renegotiate(newRemoteSdp)
754
                 .then(() => {
795
                 .then(() => {
755
                     logger.info('Remote source-remove processed');
796
                     logger.info('Remote source-remove processed');
756
                     const newSdp
797
                     const newSdp
757
                         = new SDP(this.peerconnection.localDescription.sdp);
798
                         = new SDP(this.peerconnection.localDescription.sdp);
799
+
758
                     logger.log('SDPs', mySdp, newSdp);
800
                     logger.log('SDPs', mySdp, newSdp);
759
                     this.notifyMySSRCUpdate(mySdp, newSdp);
801
                     this.notifyMySSRCUpdate(mySdp, newSdp);
760
                     finishedCallback();
802
                     finishedCallback();
764
                     finishedCallback(error);
806
                     finishedCallback(error);
765
                 });
807
                 });
766
         };
808
         };
809
+
767
         this.modificationQueue.push(workFunction);
810
         this.modificationQueue.push(workFunction);
768
     }
811
     }
769
 
812
 
792
      */
835
      */
793
     _processNewJingleOfferIq(offerIq) {
836
     _processNewJingleOfferIq(offerIq) {
794
         const remoteSdp = new SDP('');
837
         const remoteSdp = new SDP('');
838
+
795
         if (this.webrtcIceTcpDisable) {
839
         if (this.webrtcIceTcpDisable) {
796
             remoteSdp.removeTcpCandidates = true;
840
             remoteSdp.removeTcpCandidates = true;
797
         }
841
         }
804
 
848
 
805
         remoteSdp.fromJingle(offerIq);
849
         remoteSdp.fromJingle(offerIq);
806
         this.readSsrcInfo($(offerIq).find('>content'));
850
         this.readSsrcInfo($(offerIq).find('>content'));
851
+
807
         return remoteSdp;
852
         return remoteSdp;
808
     }
853
     }
809
 
854
 
816
      */
861
      */
817
     _processRemoteRemoveSource(removeSsrcInfo) {
862
     _processRemoteRemoveSource(removeSsrcInfo) {
818
         const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);
863
         const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);
864
+
819
         removeSsrcInfo.forEach((lines, idx) => {
865
         removeSsrcInfo.forEach((lines, idx) => {
820
             lines = lines.split('\r\n');
866
             lines = lines.split('\r\n');
821
             lines.pop(); // remove empty last element;
867
             lines.pop(); // remove empty last element;
838
      */
884
      */
839
     _processRemoteAddSource(addSsrcInfo) {
885
     _processRemoteAddSource(addSsrcInfo) {
840
         const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);
886
         const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);
887
+
841
         addSsrcInfo.forEach((lines, idx) => {
888
         addSsrcInfo.forEach((lines, idx) => {
842
             remoteSdp.media[idx] += lines;
889
             remoteSdp.media[idx] += lines;
843
         });
890
         });
874
         //  core flows like this.
921
         //  core flows like this.
875
         return new Promise((resolve, reject) => {
922
         return new Promise((resolve, reject) => {
876
             const remoteUfrag = JingleSessionPC.getUfrag(remoteDescription.sdp);
923
             const remoteUfrag = JingleSessionPC.getUfrag(remoteDescription.sdp);
924
+
877
             if (remoteUfrag != this.remoteUfrag) {
925
             if (remoteUfrag != this.remoteUfrag) {
878
                 this.remoteUfrag = remoteUfrag;
926
                 this.remoteUfrag = remoteUfrag;
879
                 this.room.eventEmitter.emit(
927
                 this.room.eventEmitter.emit(
886
                 () => {
934
                 () => {
887
                     if (this.signalingState === 'closed') {
935
                     if (this.signalingState === 'closed') {
888
                         reject('Attempted to setRemoteDescription in state closed');
936
                         reject('Attempted to setRemoteDescription in state closed');
937
+
889
                         return;
938
                         return;
890
                     }
939
                     }
891
                     logger.debug('Renegotiate: creating answer');
940
                     logger.debug('Renegotiate: creating answer');
893
                         answer => {
942
                         answer => {
894
                             const localUfrag
943
                             const localUfrag
895
                                 = JingleSessionPC.getUfrag(answer.sdp);
944
                                 = JingleSessionPC.getUfrag(answer.sdp);
945
+
896
                             if (localUfrag != this.localUfrag) {
946
                             if (localUfrag != this.localUfrag) {
897
                                 this.localUfrag = localUfrag;
947
                                 this.localUfrag = localUfrag;
898
                                 this.room.eventEmitter.emit(
948
                                 this.room.eventEmitter.emit(
942
                 // NOTE the code below assumes that no more than 1 video track
992
                 // NOTE the code below assumes that no more than 1 video track
943
                 // can be added to the peer connection.
993
                 // can be added to the peer connection.
944
                 // Transition from no video to video (possibly screen sharing)
994
                 // Transition from no video to video (possibly screen sharing)
995
+
945
                 if (!oldTrack && newTrack && newTrack.isVideoTrack()) {
996
                 if (!oldTrack && newTrack && newTrack.isVideoTrack()) {
946
                     // Clearing current primary SSRC will make
997
                     // Clearing current primary SSRC will make
947
                     // the SdpConsistency generate a new one which will result
998
                     // the SdpConsistency generate a new one which will result
964
                     .then(() => {
1015
                     .then(() => {
965
                         const newSdp
1016
                         const newSdp
966
                             = new SDP(this.peerconnection.localDescription.sdp);
1017
                             = new SDP(this.peerconnection.localDescription.sdp);
1018
+
967
                         this.notifyMySSRCUpdate(oldSdp, newSdp);
1019
                         this.notifyMySSRCUpdate(oldSdp, newSdp);
968
                         finishedCallback();
1020
                         finishedCallback();
969
                     }, error => {
1021
                     }, error => {
972
                         finishedCallback(error);
1024
                         finishedCallback(error);
973
                     });
1025
                     });
974
             };
1026
             };
1027
+
975
             this.modificationQueue.push(
1028
             this.modificationQueue.push(
976
                 workFunction,
1029
                 workFunction,
977
                 error => {
1030
                 error => {
991
         const actualStream
1044
         const actualStream
992
             = stream && stream.getOriginalStream
1045
             = stream && stream.getOriginalStream
993
                 ? stream.getOriginalStream() : stream;
1046
                 ? stream.getOriginalStream() : stream;
1047
+
994
         if (this.peerconnection) {
1048
         if (this.peerconnection) {
995
             this.peerconnection.addStream(actualStream, ssrcInfo);
1049
             this.peerconnection.addStream(actualStream, ssrcInfo);
996
         }
1050
         }
1008
      */
1062
      */
1009
     _parseSsrcInfoFromSourceRemove(sourceRemoveElem, currentRemoteSdp) {
1063
     _parseSsrcInfoFromSourceRemove(sourceRemoveElem, currentRemoteSdp) {
1010
         const removeSsrcInfo = [];
1064
         const removeSsrcInfo = [];
1065
+
1011
         $(sourceRemoveElem).each((idx, content) => {
1066
         $(sourceRemoveElem).each((idx, content) => {
1012
             const name = $(content).attr('name');
1067
             const name = $(content).attr('name');
1013
             let lines = '';
1068
             let lines = '';
1069
+
1014
             $(content)
1070
             $(content)
1015
                 .find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')
1071
                 .find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')
1016
                 .each(function() {
1072
                 .each(function() {
1017
                     const semantics = this.getAttribute('semantics');
1073
                     const semantics = this.getAttribute('semantics');
1018
-                    const ssrcs = $(this).find('>source').map(function() {
1019
-                        return this.getAttribute('ssrc');
1020
-                    }).get();
1074
+                    const ssrcs
1075
+                        = $(this)
1076
+                            .find('>source')
1077
+                            .map(function() {
1078
+                                return this.getAttribute('ssrc');
1079
+                            })
1080
+                            .get();
1021
 
1081
 
1022
                     if (ssrcs.length) {
1082
                     if (ssrcs.length) {
1023
                         lines
1083
                         lines
1030
             const tmp
1090
             const tmp
1031
                 = $(content).find(
1091
                 = $(content).find(
1032
                     'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
1092
                     'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
1093
+
1033
             tmp.each(function() {
1094
             tmp.each(function() {
1034
                 const ssrc = $(this).attr('ssrc');
1095
                 const ssrc = $(this).attr('ssrc');
1096
+
1035
                 ssrcs.push(ssrc);
1097
                 ssrcs.push(ssrc);
1036
             });
1098
             });
1037
             currentRemoteSdp.media.forEach((media, idx) => {
1099
             currentRemoteSdp.media.forEach((media, idx) => {
1044
                 ssrcs.forEach(ssrc => {
1106
                 ssrcs.forEach(ssrc => {
1045
                     const ssrcLines
1107
                     const ssrcLines
1046
                         = SDPUtil.find_lines(media, `a=ssrc:${ssrc}`);
1108
                         = SDPUtil.find_lines(media, `a=ssrc:${ssrc}`);
1109
+
1047
                     if (ssrcLines.length) {
1110
                     if (ssrcLines.length) {
1048
                         removeSsrcInfo[idx] += `${ssrcLines.join('\r\n')}\r\n`;
1111
                         removeSsrcInfo[idx] += `${ssrcLines.join('\r\n')}\r\n`;
1049
                     }
1112
                     }
1051
                 removeSsrcInfo[idx] += lines;
1114
                 removeSsrcInfo[idx] += lines;
1052
             });
1115
             });
1053
         });
1116
         });
1117
+
1054
         return removeSsrcInfo;
1118
         return removeSsrcInfo;
1055
     }
1119
     }
1056
 
1120
 
1078
             if (!this.peerconnection) {
1142
             if (!this.peerconnection) {
1079
                 finishedCallback(
1143
                 finishedCallback(
1080
                     'Error: tried adding stream with no active peer connection');
1144
                     'Error: tried adding stream with no active peer connection');
1145
+
1081
                 return;
1146
                 return;
1082
             }
1147
             }
1083
             this.addStreamToPeerConnection(stream, ssrcInfo);
1148
             this.addStreamToPeerConnection(stream, ssrcInfo);
1090
             }
1155
             }
1091
             if (dontModifySources) {
1156
             if (dontModifySources) {
1092
                 finishedCallback();
1157
                 finishedCallback();
1158
+
1093
                 return;
1159
                 return;
1094
             }
1160
             }
1095
             const oldSdp = new SDP(this.peerconnection.localDescription.sdp);
1161
             const oldSdp = new SDP(this.peerconnection.localDescription.sdp);
1162
+
1096
             this._renegotiate()
1163
             this._renegotiate()
1097
                 .then(() => {
1164
                 .then(() => {
1098
                     const newSdp
1165
                     const newSdp
1099
                         = new SDP(this.peerconnection.localDescription.sdp);
1166
                         = new SDP(this.peerconnection.localDescription.sdp);
1100
                     // FIXME objects should not be logged
1167
                     // FIXME objects should not be logged
1168
+
1101
                     logger.log('SDPs', oldSdp, newSdp);
1169
                     logger.log('SDPs', oldSdp, newSdp);
1102
                     this.notifyMySSRCUpdate(oldSdp, newSdp);
1170
                     this.notifyMySSRCUpdate(oldSdp, newSdp);
1103
                     finishedCallback();
1171
                     finishedCallback();
1105
                     finishedCallback(error);
1173
                     finishedCallback(error);
1106
                 });
1174
                 });
1107
         };
1175
         };
1176
+
1108
         this.modificationQueue.push(
1177
         this.modificationQueue.push(
1109
             workFunction,
1178
             workFunction,
1110
             error => {
1179
             error => {
1135
         // of complications. Instead, we use the RTPSender and remove just
1204
         // of complications. Instead, we use the RTPSender and remove just
1136
         // the track.
1205
         // the track.
1137
         let track = null;
1206
         let track = null;
1207
+
1138
         if (stream.getAudioTracks() && stream.getAudioTracks().length) {
1208
         if (stream.getAudioTracks() && stream.getAudioTracks().length) {
1139
             track = stream.getAudioTracks()[0];
1209
             track = stream.getAudioTracks()[0];
1140
         } else if (stream.getVideoTracks() && stream.getVideoTracks().length) {
1210
         } else if (stream.getVideoTracks() && stream.getVideoTracks().length) {
1143
 
1213
 
1144
         if (!track) {
1214
         if (!track) {
1145
             const msg = 'Cannot remove tracks: no tracks.';
1215
             const msg = 'Cannot remove tracks: no tracks.';
1216
+
1146
             logger.log(msg);
1217
             logger.log(msg);
1218
+
1147
             return;
1219
             return;
1148
         }
1220
         }
1149
 
1221
 
1151
         this.peerconnection.peerconnection.getSenders().some(s => {
1223
         this.peerconnection.peerconnection.getSenders().some(s => {
1152
             if (s.track === track) {
1224
             if (s.track === track) {
1153
                 sender = s;
1225
                 sender = s;
1226
+
1154
                 return true;
1227
                 return true;
1155
             }
1228
             }
1229
+
1156
             return false;
1230
             return false;
1157
         });
1231
         });
1158
 
1232
 
1173
         const actualStream
1247
         const actualStream
1174
             = stream && stream.getOriginalStream
1248
             = stream && stream.getOriginalStream
1175
                 ? stream.getOriginalStream() : stream;
1249
                 ? stream.getOriginalStream() : stream;
1250
+
1176
         if (!this.peerconnection) {
1251
         if (!this.peerconnection) {
1177
             return;
1252
             return;
1178
         }
1253
         }
1196
         const workFunction = finishedCallback => {
1271
         const workFunction = finishedCallback => {
1197
             if (!this.peerconnection) {
1272
             if (!this.peerconnection) {
1198
                 finishedCallback();
1273
                 finishedCallback();
1274
+
1199
                 return;
1275
                 return;
1200
             }
1276
             }
1201
             if (RTCBrowserType.getBrowserType()
1277
             if (RTCBrowserType.getBrowserType()
1205
                 this.removeStreamFromPeerConnection(stream);
1281
                 this.removeStreamFromPeerConnection(stream);
1206
             }
1282
             }
1207
             const oldSdp = new SDP(this.peerconnection.localDescription.sdp);
1283
             const oldSdp = new SDP(this.peerconnection.localDescription.sdp);
1284
+
1208
             this._renegotiate()
1285
             this._renegotiate()
1209
                 .then(() => {
1286
                 .then(() => {
1210
                     const newSdp
1287
                     const newSdp
1211
                         = new SDP(this.peerconnection.localDescription.sdp);
1288
                         = new SDP(this.peerconnection.localDescription.sdp);
1289
+
1212
                     if (ssrcInfo) {
1290
                     if (ssrcInfo) {
1213
                         this.modifiedSSRCs[ssrcInfo.type]
1291
                         this.modifiedSSRCs[ssrcInfo.type]
1214
                             = this.modifiedSSRCs[ssrcInfo.type] || [];
1292
                             = this.modifiedSSRCs[ssrcInfo.type] || [];
1221
                     finishedCallback(error);
1299
                     finishedCallback(error);
1222
                 });
1300
                 });
1223
         };
1301
         };
1302
+
1224
         this.modificationQueue.push(
1303
         this.modificationQueue.push(
1225
             workFunction,
1304
             workFunction,
1226
             error => {
1305
             error => {
1237
 
1316
 
1238
         if (this.state !== JingleSessionState.ACTIVE) {
1317
         if (this.state !== JingleSessionState.ACTIVE) {
1239
             logger.warn(`Skipping SSRC update in '${this.state} ' state.`);
1318
             logger.warn(`Skipping SSRC update in '${this.state} ' state.`);
1319
+
1240
             return;
1320
             return;
1241
         }
1321
         }
1242
 
1322
 
1250
                 sid: this.sid
1330
                 sid: this.sid
1251
             }
1331
             }
1252
             );
1332
             );
1333
+
1253
         sdpDiffer.toJingle(remove);
1334
         sdpDiffer.toJingle(remove);
1254
         const removed = this.fixJingle(remove);
1335
         const removed = this.fixJingle(remove);
1255
 
1336
 
1316
 
1397
 
1317
             // Get XMPP error code and condition(reason)
1398
             // Get XMPP error code and condition(reason)
1318
             const errorElSel = $(errResponse).find('error');
1399
             const errorElSel = $(errResponse).find('error');
1400
+
1319
             if (errorElSel.length) {
1401
             if (errorElSel.length) {
1320
                 error.code = errorElSel.attr('code');
1402
                 error.code = errorElSel.attr('code');
1321
                 const errorReasonSel = $(errResponse).find('error :first');
1403
                 const errorReasonSel = $(errResponse).find('error :first');
1404
+
1322
                 if (errorReasonSel.length) {
1405
                 if (errorReasonSel.length) {
1323
                     error.reason = errorReasonSel[0].tagName;
1406
                     error.reason = errorReasonSel[0].tagName;
1324
                 }
1407
                 }
1398
      * @returns {boolean} true if the jingle has to be sent and false otherwise.
1481
      * @returns {boolean} true if the jingle has to be sent and false otherwise.
1399
      */
1482
      */
1400
     fixJingle(jingle) {
1483
     fixJingle(jingle) {
1401
-        /* eslint-disable no-case-declarations */
1484
+        // eslint-disable-next-line newline-per-chained-call
1402
         const action = $(jingle.nodeTree).find('jingle').attr('action');
1485
         const action = $(jingle.nodeTree).find('jingle').attr('action');
1486
+
1403
         switch (action) {
1487
         switch (action) {
1404
         case 'source-add':
1488
         case 'source-add':
1405
         case 'session-accept':
1489
         case 'session-accept':
1408
         case 'source-remove':
1492
         case 'source-remove':
1409
             this.fixSourceRemoveJingle(jingle);
1493
             this.fixSourceRemoveJingle(jingle);
1410
             break;
1494
             break;
1411
-        default:
1495
+        default: {
1412
             const errmsg = 'Unknown jingle action!';
1496
             const errmsg = 'Unknown jingle action!';
1497
+
1413
             GlobalOnErrorHandler.callErrorHandler(errmsg);
1498
             GlobalOnErrorHandler.callErrorHandler(errmsg);
1414
             logger.error(errmsg);
1499
             logger.error(errmsg);
1500
+
1415
             return false;
1501
             return false;
1416
-        }/* eslint-enable no-case-declarations */
1502
+        }
1503
+        }
1417
 
1504
 
1418
         const sources
1505
         const sources
1419
             = $(jingle.tree()).find('>jingle>content>description>source');
1506
             = $(jingle.tree()).find('>jingle>content>description>source');
1507
+
1508
+
1420
         return sources && sources.length > 0;
1509
         return sources && sources.length > 0;
1421
     }
1510
     }
1422
 
1511
 
1428
      */
1517
      */
1429
     fixSourceAddJingle(jingle) {
1518
     fixSourceAddJingle(jingle) {
1430
         let ssrcs = this.modifiedSSRCs.unmute;
1519
         let ssrcs = this.modifiedSSRCs.unmute;
1520
+
1431
         this.modifiedSSRCs.unmute = [];
1521
         this.modifiedSSRCs.unmute = [];
1432
         if (ssrcs && ssrcs.length) {
1522
         if (ssrcs && ssrcs.length) {
1433
             ssrcs.forEach(ssrcObj => {
1523
             ssrcs.forEach(ssrcObj => {
1434
                 const desc
1524
                 const desc
1435
                     = $(jingle.tree()).find(
1525
                     = $(jingle.tree()).find(
1436
                         `>jingle>content[name="${ssrcObj.mtype}"]>description`);
1526
                         `>jingle>content[name="${ssrcObj.mtype}"]>description`);
1527
+
1437
                 if (!desc || !desc.length) {
1528
                 if (!desc || !desc.length) {
1438
                     return;
1529
                     return;
1439
                 }
1530
                 }
1440
                 ssrcObj.ssrcs.forEach(ssrc => {
1531
                 ssrcObj.ssrcs.forEach(ssrc => {
1441
                     const sourceNode = desc.find(`>source[ssrc="${ssrc}"]`);
1532
                     const sourceNode = desc.find(`>source[ssrc="${ssrc}"]`);
1533
+
1442
                     sourceNode.remove();
1534
                     sourceNode.remove();
1443
                 });
1535
                 });
1444
                 ssrcObj.groups.forEach(group => {
1536
                 ssrcObj.groups.forEach(group => {
1445
                     const groupNode = desc.find(`>ssrc-group[semantics="${
1537
                     const groupNode = desc.find(`>ssrc-group[semantics="${
1446
                          group.semantics}"]:has(source[ssrc="${
1538
                          group.semantics}"]:has(source[ssrc="${
1447
                          group.ssrcs[0]}"])`);
1539
                          group.ssrcs[0]}"])`);
1540
+
1448
                     groupNode.remove();
1541
                     groupNode.remove();
1449
                 });
1542
                 });
1450
             });
1543
             });
1457
                 const desc
1550
                 const desc
1458
                     = JingleSessionPC.createDescriptionNode(
1551
                     = JingleSessionPC.createDescriptionNode(
1459
                         jingle, ssrcObj.mtype);
1552
                         jingle, ssrcObj.mtype);
1553
+
1554
+                // eslint-disable-next-line newline-per-chained-call
1460
                 const cname = Math.random().toString(36).substring(2);
1555
                 const cname = Math.random().toString(36).substring(2);
1556
+
1461
                 ssrcObj.ssrcs.forEach(ssrc => {
1557
                 ssrcObj.ssrcs.forEach(ssrc => {
1462
                     const sourceNode
1558
                     const sourceNode
1463
                         = desc.find(`>source[ssrc="${ssrc}"]`);
1559
                         = desc.find(`>source[ssrc="${ssrc}"]`);
1560
+
1464
                     sourceNode.remove();
1561
                     sourceNode.remove();
1465
                     const sourceXML
1562
                     const sourceXML
1466
                         = '<source xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"'
1563
                         = '<source xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"'
1470
                         + '<parameter xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"'
1567
                         + '<parameter xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"'
1471
                         + ` value="${cname}" name="cname" />`
1568
                         + ` value="${cname}" name="cname" />`
1472
                         + '</source>';
1569
                         + '</source>';
1570
+
1473
                     desc.append(sourceXML);
1571
                     desc.append(sourceXML);
1474
                 });
1572
                 });
1475
                 ssrcObj.groups.forEach(group => {
1573
                 ssrcObj.groups.forEach(group => {
1477
                         = desc.find(
1575
                         = desc.find(
1478
                             `>ssrc-group[semantics="${group.semantics
1576
                             `>ssrc-group[semantics="${group.semantics
1479
                                 }"]:has(source[ssrc="${group.ssrcs[0]}"])`);
1577
                                 }"]:has(source[ssrc="${group.ssrcs[0]}"])`);
1578
+
1480
                     groupNode.remove();
1579
                     groupNode.remove();
1481
                     desc.append(
1580
                     desc.append(
1482
                         `<ssrc-group semantics="${group.semantics
1581
                         `<ssrc-group semantics="${group.semantics
1496
      */
1595
      */
1497
     fixSourceRemoveJingle(jingle) {
1596
     fixSourceRemoveJingle(jingle) {
1498
         let ssrcs = this.modifiedSSRCs.mute;
1597
         let ssrcs = this.modifiedSSRCs.mute;
1598
+
1499
         this.modifiedSSRCs.mute = [];
1599
         this.modifiedSSRCs.mute = [];
1500
         if (ssrcs && ssrcs.length) {
1600
         if (ssrcs && ssrcs.length) {
1501
             ssrcs.forEach(ssrcObj => {
1601
             ssrcs.forEach(ssrcObj => {
1504
                         = $(jingle.tree()).find(
1604
                         = $(jingle.tree()).find(
1505
                             `>jingle>content[name="${ssrcObj.mtype
1605
                             `>jingle>content[name="${ssrcObj.mtype
1506
                                 }"]>description>source[ssrc="${ssrc}"]`);
1606
                                 }"]>description>source[ssrc="${ssrc}"]`);
1607
+
1507
                     sourceNode.remove();
1608
                     sourceNode.remove();
1508
                 });
1609
                 });
1509
                 ssrcObj.groups.forEach(group => {
1610
                 ssrcObj.groups.forEach(group => {
1513
                                 }"]>description>ssrc-group[semantics="${
1614
                                 }"]>description>ssrc-group[semantics="${
1514
                                 group.semantics}"]:has(source[ssrc="${
1615
                                 group.semantics}"]:has(source[ssrc="${
1515
                                 group.ssrcs[0]}"])`);
1616
                                 group.ssrcs[0]}"])`);
1617
+
1516
                     groupNode.remove();
1618
                     groupNode.remove();
1517
                 });
1619
                 });
1518
             });
1620
             });
1525
                 const desc
1627
                 const desc
1526
                     = JingleSessionPC.createDescriptionNode(
1628
                     = JingleSessionPC.createDescriptionNode(
1527
                         jingle, ssrcObj.mtype);
1629
                         jingle, ssrcObj.mtype);
1630
+
1528
                 ssrcObj.ssrcs.forEach(ssrc => {
1631
                 ssrcObj.ssrcs.forEach(ssrc => {
1529
                     const sourceNode
1632
                     const sourceNode
1530
                         = desc.find(`>source[ssrc="${ssrc}"]`);
1633
                         = desc.find(`>source[ssrc="${ssrc}"]`);
1634
+
1531
                     if (!sourceNode || !sourceNode.length) {
1635
                     if (!sourceNode || !sourceNode.length) {
1532
                         // Maybe we have to include cname, msid, etc here?
1636
                         // Maybe we have to include cname, msid, etc here?
1533
                         desc.append(
1637
                         desc.append(
1540
                         = desc.find(`>ssrc-group[semantics="${
1644
                         = desc.find(`>ssrc-group[semantics="${
1541
                              group.semantics}"]:has(source[ssrc="${
1645
                              group.semantics}"]:has(source[ssrc="${
1542
                              group.ssrcs[0]}"])`);
1646
                              group.ssrcs[0]}"])`);
1647
+
1543
                     if (!groupNode || !groupNode.length) {
1648
                     if (!groupNode || !groupNode.length) {
1544
                         desc.append(
1649
                         desc.append(
1545
                             `<ssrc-group semantics="${group.semantics
1650
                             `<ssrc-group semantics="${group.semantics
1563
         let content = $(jingle.tree()).find(`>jingle>content[name="${mtype}"]`);
1668
         let content = $(jingle.tree()).find(`>jingle>content[name="${mtype}"]`);
1564
 
1669
 
1565
         if (!content || !content.length) {
1670
         if (!content || !content.length) {
1566
-            $(jingle.tree()).find('>jingle').append(
1567
-                `<content name="${mtype}"></content>`);
1671
+            $(jingle.tree())
1672
+                .find('>jingle')
1673
+                .append(`<content name="${mtype}"></content>`);
1568
             content = $(jingle.tree()).find(`>jingle>content[name="${mtype}"]`);
1674
             content = $(jingle.tree()).find(`>jingle>content[name="${mtype}"]`);
1569
         }
1675
         }
1570
 
1676
 
1571
         let desc = content.find('>description');
1677
         let desc = content.find('>description');
1678
+
1572
         if (!desc || !desc.length) {
1679
         if (!desc || !desc.length) {
1573
             content.append(
1680
             content.append(
1574
                 `<description xmlns="urn:xmpp:jingle:apps:rtp:1" media="${
1681
                 `<description xmlns="urn:xmpp:jingle:apps:rtp:1" media="${
1575
                     mtype}"></description>`);
1682
                     mtype}"></description>`);
1576
             desc = content.find('>description');
1683
             desc = content.find('>description');
1577
         }
1684
         }
1685
+
1578
         return desc;
1686
         return desc;
1579
     }
1687
     }
1580
 
1688
 
1584
     static getUfrag(sdp) {
1692
     static getUfrag(sdp) {
1585
         const ufragLines
1693
         const ufragLines
1586
             = sdp.split('\n').filter(line => line.startsWith('a=ice-ufrag:'));
1694
             = sdp.split('\n').filter(line => line.startsWith('a=ice-ufrag:'));
1695
+
1587
         if (ufragLines.length > 0) {
1696
         if (ufragLines.length > 0) {
1588
             return ufragLines[0].substr('a=ice-ufrag:'.length);
1697
             return ufragLines[0].substr('a=ice-ufrag:'.length);
1589
         }
1698
         }

+ 17
- 0
modules/xmpp/RtxModifier.js View File

28
     const primarySsrcCname = primarySsrcInfo.cname;
28
     const primarySsrcCname = primarySsrcInfo.cname;
29
 
29
 
30
     const previousRtxSSRC = mLine.getRtxSSRC(primarySsrc);
30
     const previousRtxSSRC = mLine.getRtxSSRC(primarySsrc);
31
+
31
     if (previousRtxSSRC === rtxSsrc) {
32
     if (previousRtxSSRC === rtxSsrc) {
32
         logger.debug(`${rtxSsrc} was already associated with ${primarySsrc}`);
33
         logger.debug(`${rtxSsrc} was already associated with ${primarySsrc}`);
34
+
33
         return;
35
         return;
34
     }
36
     }
35
     if (previousRtxSSRC) {
37
     if (previousRtxSSRC) {
111
     modifyRtxSsrcs(sdpStr) {
113
     modifyRtxSsrcs(sdpStr) {
112
         const sdpTransformer = new SdpTransformWrap(sdpStr);
114
         const sdpTransformer = new SdpTransformWrap(sdpStr);
113
         const videoMLine = sdpTransformer.selectMedia('video');
115
         const videoMLine = sdpTransformer.selectMedia('video');
116
+
114
         if (!videoMLine) {
117
         if (!videoMLine) {
115
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
118
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
119
+
116
             return sdpStr;
120
             return sdpStr;
117
         }
121
         }
118
         if (videoMLine.direction === 'inactive'
122
         if (videoMLine.direction === 'inactive'
119
                 || videoMLine.direction === 'recvonly') {
123
                 || videoMLine.direction === 'recvonly') {
120
             logger.debug('RtxModifier doing nothing, video '
124
             logger.debug('RtxModifier doing nothing, video '
121
                 + 'm line is inactive or recvonly');
125
                 + 'm line is inactive or recvonly');
126
+
122
             return sdpStr;
127
             return sdpStr;
123
         }
128
         }
124
         if (videoMLine.getSSRCCount() < 1) {
129
         if (videoMLine.getSSRCCount() < 1) {
125
             logger.debug('RtxModifier doing nothing, no video ssrcs present');
130
             logger.debug('RtxModifier doing nothing, no video ssrcs present');
131
+
126
             return sdpStr;
132
             return sdpStr;
127
         }
133
         }
128
         logger.debug('Current ssrc mapping: ', this.correspondingRtxSsrcs);
134
         logger.debug('Current ssrc mapping: ', this.correspondingRtxSsrcs);
129
         const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();
135
         const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();
136
+
130
         logger.debug('Parsed primary video ssrcs ', primaryVideoSsrcs,
137
         logger.debug('Parsed primary video ssrcs ', primaryVideoSsrcs,
131
             ' making sure all have rtx streams');
138
             ' making sure all have rtx streams');
132
         for (const ssrc of primaryVideoSsrcs) {
139
         for (const ssrc of primaryVideoSsrcs) {
133
             const msid = videoMLine.getSSRCAttrValue(ssrc, 'msid');
140
             const msid = videoMLine.getSSRCAttrValue(ssrc, 'msid');
134
             const cname = videoMLine.getSSRCAttrValue(ssrc, 'cname');
141
             const cname = videoMLine.getSSRCAttrValue(ssrc, 'cname');
135
             let correspondingRtxSsrc = this.correspondingRtxSsrcs.get(ssrc);
142
             let correspondingRtxSsrc = this.correspondingRtxSsrcs.get(ssrc);
143
+
136
             if (correspondingRtxSsrc) {
144
             if (correspondingRtxSsrc) {
137
                 logger.debug(
145
                 logger.debug(
138
                     'Already have an associated rtx ssrc for'
146
                     'Already have an associated rtx ssrc for'
143
                 // If there's one in the sdp already for it, we'll just set
151
                 // If there's one in the sdp already for it, we'll just set
144
                 //  that as the corresponding one
152
                 //  that as the corresponding one
145
                 const previousAssociatedRtxStream = videoMLine.getRtxSSRC(ssrc);
153
                 const previousAssociatedRtxStream = videoMLine.getRtxSSRC(ssrc);
154
+
146
                 if (previousAssociatedRtxStream) {
155
                 if (previousAssociatedRtxStream) {
147
                     logger.debug(
156
                     logger.debug(
148
                         `Rtx stream ${previousAssociatedRtxStream} `
157
                         `Rtx stream ${previousAssociatedRtxStream} `
167
                 },
176
                 },
168
                 correspondingRtxSsrc);
177
                 correspondingRtxSsrc);
169
         }
178
         }
179
+
170
         return sdpTransformer.toRawSDP();
180
         return sdpTransformer.toRawSDP();
171
     }
181
     }
172
 
182
 
178
     stripRtx(sdpStr) {
188
     stripRtx(sdpStr) {
179
         const sdpTransformer = new SdpTransformWrap(sdpStr);
189
         const sdpTransformer = new SdpTransformWrap(sdpStr);
180
         const videoMLine = sdpTransformer.selectMedia('video');
190
         const videoMLine = sdpTransformer.selectMedia('video');
191
+
181
         if (!videoMLine) {
192
         if (!videoMLine) {
182
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
193
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
194
+
183
             return sdpStr;
195
             return sdpStr;
184
         }
196
         }
185
         if (videoMLine.direction === 'inactive'
197
         if (videoMLine.direction === 'inactive'
186
                 || videoMLine.direction === 'recvonly') {
198
                 || videoMLine.direction === 'recvonly') {
187
             logger.debug('RtxModifier doing nothing, video '
199
             logger.debug('RtxModifier doing nothing, video '
188
                 + 'm line is inactive or recvonly');
200
                 + 'm line is inactive or recvonly');
201
+
189
             return sdpStr;
202
             return sdpStr;
190
         }
203
         }
191
         if (videoMLine.getSSRCCount() < 1) {
204
         if (videoMLine.getSSRCCount() < 1) {
192
             logger.debug('RtxModifier doing nothing, no video ssrcs present');
205
             logger.debug('RtxModifier doing nothing, no video ssrcs present');
206
+
193
             return sdpStr;
207
             return sdpStr;
194
         }
208
         }
195
         if (!videoMLine.containsAnySSRCGroups()) {
209
         if (!videoMLine.containsAnySSRCGroups()) {
196
             logger.debug('RtxModifier doing nothing, '
210
             logger.debug('RtxModifier doing nothing, '
197
               + 'no video ssrcGroups present');
211
               + 'no video ssrcGroups present');
212
+
198
             return sdpStr;
213
             return sdpStr;
199
         }
214
         }
200
         const fidGroups = videoMLine.findGroups('FID');
215
         const fidGroups = videoMLine.findGroups('FID');
201
         // Remove the fid groups from the mline
216
         // Remove the fid groups from the mline
217
+
202
         videoMLine.removeGroupsBySemantics('FID');
218
         videoMLine.removeGroupsBySemantics('FID');
203
         // Get the rtx ssrcs and remove them from the mline
219
         // Get the rtx ssrcs and remove them from the mline
204
         for (const fidGroup of fidGroups) {
220
         for (const fidGroup of fidGroups) {
205
             const rtxSsrc = parseSecondarySSRC(fidGroup);
221
             const rtxSsrc = parseSecondarySSRC(fidGroup);
222
+
206
             videoMLine.removeSSRC(rtxSsrc);
223
             videoMLine.removeSSRC(rtxSsrc);
207
         }
224
         }
208
 
225
 

+ 38
- 0
modules/xmpp/RtxModifier.spec.js View File

12
  */
12
  */
13
 function numVideoSsrcs(parsedSdp) {
13
 function numVideoSsrcs(parsedSdp) {
14
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
14
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
15
+
16
+
15
     return videoMLine.ssrcs
17
     return videoMLine.ssrcs
16
     .map(ssrcInfo => ssrcInfo.id)
18
     .map(ssrcInfo => ssrcInfo.id)
17
     .filter((ssrc, index, array) => array.indexOf(ssrc) === index)
19
     .filter((ssrc, index, array) => array.indexOf(ssrc) === index)
25
  */
27
  */
26
 function getPrimaryVideoSsrc(parsedSdp) {
28
 function getPrimaryVideoSsrc(parsedSdp) {
27
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
29
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
30
+
31
+
28
     return parseInt(SDPUtil.parsePrimaryVideoSsrc(videoMLine));
32
     return parseInt(SDPUtil.parsePrimaryVideoSsrc(videoMLine));
29
 }
33
 }
30
 
34
 
38
  */
42
  */
39
 function getPrimaryVideoSsrcs(parsedSdp) {
43
 function getPrimaryVideoSsrcs(parsedSdp) {
40
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
44
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
45
+
41
     if (numVideoSsrcs(parsedSdp) === 1) {
46
     if (numVideoSsrcs(parsedSdp) === 1) {
42
         return [videoMLine.ssrcs[0].id];
47
         return [videoMLine.ssrcs[0].id];
43
     }
48
     }
44
     const simGroups = getVideoGroups(parsedSdp, 'SIM');
49
     const simGroups = getVideoGroups(parsedSdp, 'SIM');
50
+
45
     if (simGroups.length > 1) {
51
     if (simGroups.length > 1) {
46
         return;
52
         return;
47
     }
53
     }
48
     const simGroup = simGroups[0];
54
     const simGroup = simGroups[0];
55
+
56
+
49
     return SDPUtil.parseGroupSsrcs(simGroup);
57
     return SDPUtil.parseGroupSsrcs(simGroup);
50
 
58
 
51
 }
59
 }
61
  */
69
  */
62
 function getVideoGroups(parsedSdp, groupSemantics) {
70
 function getVideoGroups(parsedSdp, groupSemantics) {
63
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
71
     const videoMLine = parsedSdp.media.find(m => m.type === 'video');
72
+
64
     videoMLine.ssrcGroups = videoMLine.ssrcGroups || [];
73
     videoMLine.ssrcGroups = videoMLine.ssrcGroups || [];
74
+
65
     return videoMLine.ssrcGroups
75
     return videoMLine.ssrcGroups
66
     .filter(g => g.semantics === groupSemantics);
76
     .filter(g => g.semantics === groupSemantics);
67
 }
77
 }
85
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.singleVideoSdp));
95
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.singleVideoSdp));
86
                 const newSdp = transform.parse(newSdpStr);
96
                 const newSdp = transform.parse(newSdpStr);
87
                 const newPrimaryVideoSsrc = getPrimaryVideoSsrc(newSdp);
97
                 const newPrimaryVideoSsrc = getPrimaryVideoSsrc(newSdp);
98
+
88
                 expect(newPrimaryVideoSsrc).toEqual(this.primaryVideoSsrc);
99
                 expect(newPrimaryVideoSsrc).toEqual(this.primaryVideoSsrc);
89
           // Should now have an rtx ssrc as well
100
           // Should now have an rtx ssrc as well
90
                 expect(numVideoSsrcs(newSdp)).toEqual(2);
101
                 expect(numVideoSsrcs(newSdp)).toEqual(2);
91
           // Should now have an FID group
102
           // Should now have an FID group
92
                 const fidGroups = getVideoGroups(newSdp, 'FID');
103
                 const fidGroups = getVideoGroups(newSdp, 'FID');
104
+
93
                 expect(fidGroups.length).toEqual(1);
105
                 expect(fidGroups.length).toEqual(1);
94
 
106
 
95
                 const fidGroup = fidGroups[0];
107
                 const fidGroup = fidGroups[0];
96
                 const fidGroupPrimarySsrc = SDPUtil.parseGroupSsrcs(fidGroup)[0];
108
                 const fidGroupPrimarySsrc = SDPUtil.parseGroupSsrcs(fidGroup)[0];
109
+
97
                 expect(fidGroupPrimarySsrc).toEqual(this.primaryVideoSsrc);
110
                 expect(fidGroupPrimarySsrc).toEqual(this.primaryVideoSsrc);
98
             });
111
             });
99
 
112
 
112
                 newSdp = transform.parse(newSdpStr);
125
                 newSdp = transform.parse(newSdpStr);
113
                 fidGroup = getVideoGroups(newSdp, 'FID')[0];
126
                 fidGroup = getVideoGroups(newSdp, 'FID')[0];
114
                 const newFidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
127
                 const newFidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
128
+
115
                 expect(newFidGroupRtxSsrc).toEqual(fidGroupRtxSsrc);
129
                 expect(newFidGroupRtxSsrc).toEqual(fidGroupRtxSsrc);
116
             });
130
             });
117
 
131
 
125
 
139
 
126
                 let fidGroup = getVideoGroups(newSdp, 'FID')[0];
140
                 let fidGroup = getVideoGroups(newSdp, 'FID')[0];
127
                 const fidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
141
                 const fidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
142
+
128
                 this.rtxModifier.clearSsrcCache();
143
                 this.rtxModifier.clearSsrcCache();
129
 
144
 
130
           // Now pass the original sdp through again
145
           // Now pass the original sdp through again
132
                 newSdp = transform.parse(newSdpStr);
147
                 newSdp = transform.parse(newSdpStr);
133
                 fidGroup = getVideoGroups(newSdp, 'FID')[0];
148
                 fidGroup = getVideoGroups(newSdp, 'FID')[0];
134
                 const newFidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
149
                 const newFidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
150
+
135
                 expect(newFidGroupRtxSsrc).not.toEqual(fidGroupRtxSsrc);
151
                 expect(newFidGroupRtxSsrc).not.toEqual(fidGroupRtxSsrc);
136
             });
152
             });
137
 
153
 
141
           // -->The rtx ssrc used should be the one we set
157
           // -->The rtx ssrc used should be the one we set
142
                 const forcedRtxSsrc = 123456;
158
                 const forcedRtxSsrc = 123456;
143
                 const ssrcCache = new Map();
159
                 const ssrcCache = new Map();
160
+
144
                 ssrcCache.set(this.primaryVideoSsrc, forcedRtxSsrc);
161
                 ssrcCache.set(this.primaryVideoSsrc, forcedRtxSsrc);
145
                 this.rtxModifier.setSsrcCache(ssrcCache);
162
                 this.rtxModifier.setSsrcCache(ssrcCache);
146
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.singleVideoSdp));
163
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.singleVideoSdp));
148
 
165
 
149
                 const fidGroup = getVideoGroups(newSdp, 'FID')[0];
166
                 const fidGroup = getVideoGroups(newSdp, 'FID')[0];
150
                 const fidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
167
                 const fidGroupRtxSsrc = SDPUtil.parseGroupSsrcs(fidGroup)[1];
168
+
151
                 expect(fidGroupRtxSsrc).toEqual(forcedRtxSsrc);
169
                 expect(fidGroupRtxSsrc).toEqual(forcedRtxSsrc);
152
             });
170
             });
153
         });
171
         });
164
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.multipleVideoSdp));
182
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(this.multipleVideoSdp));
165
                 const newSdp = transform.parse(newSdpStr);
183
                 const newSdp = transform.parse(newSdpStr);
166
                 const newPrimaryVideoSsrcs = getPrimaryVideoSsrcs(newSdp);
184
                 const newPrimaryVideoSsrcs = getPrimaryVideoSsrcs(newSdp);
185
+
167
                 expect(newPrimaryVideoSsrcs).toEqual(this.primaryVideoSsrcs);
186
                 expect(newPrimaryVideoSsrcs).toEqual(this.primaryVideoSsrcs);
168
           // Should now have rtx ssrcs as well
187
           // Should now have rtx ssrcs as well
169
                 expect(numVideoSsrcs(newSdp)).toEqual(this.primaryVideoSsrcs.length * 2);
188
                 expect(numVideoSsrcs(newSdp)).toEqual(this.primaryVideoSsrcs.length * 2);
170
           // Should now have FID groups
189
           // Should now have FID groups
171
                 const fidGroups = getVideoGroups(newSdp, 'FID');
190
                 const fidGroups = getVideoGroups(newSdp, 'FID');
191
+
172
                 expect(fidGroups.length).toEqual(this.primaryVideoSsrcs.length);
192
                 expect(fidGroups.length).toEqual(this.primaryVideoSsrcs.length);
173
                 fidGroups.forEach(fidGroup => {
193
                 fidGroups.forEach(fidGroup => {
174
                     const fidGroupPrimarySsrc = SDPUtil.parseGroupSsrcs(fidGroup)[0];
194
                     const fidGroupPrimarySsrc = SDPUtil.parseGroupSsrcs(fidGroup)[0];
195
+
175
                     expect(this.primaryVideoSsrcs.indexOf(fidGroupPrimarySsrc)).not.toEqual(-1);
196
                     expect(this.primaryVideoSsrcs.indexOf(fidGroupPrimarySsrc)).not.toEqual(-1);
176
                 });
197
                 });
177
             });
198
             });
186
                 const rtxMapping = new Map();
207
                 const rtxMapping = new Map();
187
                 let fidGroups = getVideoGroups(newSdp, 'FID');
208
                 let fidGroups = getVideoGroups(newSdp, 'FID');
188
           // Save the first mapping that is made
209
           // Save the first mapping that is made
210
+
189
                 fidGroups.forEach(fidGroup => {
211
                 fidGroups.forEach(fidGroup => {
190
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
212
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
191
                     const fidGroupPrimarySsrc = fidSsrcs[0];
213
                     const fidGroupPrimarySsrc = fidSsrcs[0];
192
                     const fidGroupRtxSsrc = fidSsrcs[1];
214
                     const fidGroupRtxSsrc = fidSsrcs[1];
215
+
193
                     rtxMapping.set(fidGroupPrimarySsrc, fidGroupRtxSsrc);
216
                     rtxMapping.set(fidGroupPrimarySsrc, fidGroupRtxSsrc);
194
                 });
217
                 });
195
           // Now pass the original sdp through again and make sure we get the same mapping
218
           // Now pass the original sdp through again and make sure we get the same mapping
200
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
223
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
201
                     const fidGroupPrimarySsrc = fidSsrcs[0];
224
                     const fidGroupPrimarySsrc = fidSsrcs[0];
202
                     const fidGroupRtxSsrc = fidSsrcs[1];
225
                     const fidGroupRtxSsrc = fidSsrcs[1];
226
+
203
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
227
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
204
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).toEqual(fidGroupRtxSsrc);
228
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).toEqual(fidGroupRtxSsrc);
205
                 });
229
                 });
216
                 const rtxMapping = new Map();
240
                 const rtxMapping = new Map();
217
                 let fidGroups = getVideoGroups(newSdp, 'FID');
241
                 let fidGroups = getVideoGroups(newSdp, 'FID');
218
           // Save the first mapping that is made
242
           // Save the first mapping that is made
243
+
219
                 fidGroups.forEach(fidGroup => {
244
                 fidGroups.forEach(fidGroup => {
220
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
245
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
221
                     const fidGroupPrimarySsrc = fidSsrcs[0];
246
                     const fidGroupPrimarySsrc = fidSsrcs[0];
222
                     const fidGroupRtxSsrc = fidSsrcs[1];
247
                     const fidGroupRtxSsrc = fidSsrcs[1];
248
+
223
                     rtxMapping.set(fidGroupPrimarySsrc, fidGroupRtxSsrc);
249
                     rtxMapping.set(fidGroupPrimarySsrc, fidGroupRtxSsrc);
224
                 });
250
                 });
225
 
251
 
232
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
258
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
233
                     const fidGroupPrimarySsrc = fidSsrcs[0];
259
                     const fidGroupPrimarySsrc = fidSsrcs[0];
234
                     const fidGroupRtxSsrc = fidSsrcs[1];
260
                     const fidGroupRtxSsrc = fidSsrcs[1];
261
+
235
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
262
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
236
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).not.toEqual(fidGroupRtxSsrc);
263
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).not.toEqual(fidGroupRtxSsrc);
237
                 });
264
                 });
242
           // Call modifyRtxSsrcs
269
           // Call modifyRtxSsrcs
243
           // -->The rtx ssrc used should be the one we set
270
           // -->The rtx ssrc used should be the one we set
244
                 const rtxMapping = new Map();
271
                 const rtxMapping = new Map();
272
+
245
                 this.primaryVideoSsrcs.forEach(ssrc => {
273
                 this.primaryVideoSsrcs.forEach(ssrc => {
246
                     rtxMapping.set(ssrc, SDPUtil.generateSsrc());
274
                     rtxMapping.set(ssrc, SDPUtil.generateSsrc());
247
                 });
275
                 });
251
                 const newSdp = transform.parse(newSdpStr);
279
                 const newSdp = transform.parse(newSdpStr);
252
 
280
 
253
                 const fidGroups = getVideoGroups(newSdp, 'FID');
281
                 const fidGroups = getVideoGroups(newSdp, 'FID');
282
+
254
                 fidGroups.forEach(fidGroup => {
283
                 fidGroups.forEach(fidGroup => {
255
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
284
                     const fidSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
256
                     const fidGroupPrimarySsrc = fidSsrcs[0];
285
                     const fidGroupPrimarySsrc = fidSsrcs[0];
257
                     const fidGroupRtxSsrc = fidSsrcs[1];
286
                     const fidGroupRtxSsrc = fidSsrcs[1];
287
+
258
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
288
                     expect(rtxMapping.has(fidGroupPrimarySsrc)).toBe(true);
259
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).toEqual(fidGroupRtxSsrc);
289
                     expect(rtxMapping.get(fidGroupPrimarySsrc)).toEqual(fidGroupRtxSsrc);
260
                 });
290
                 });
265
             it('should handle a recvonly video mline', function() {
295
             it('should handle a recvonly video mline', function() {
266
                 const sdp = SampleSdpStrings.plainVideoSdp;
296
                 const sdp = SampleSdpStrings.plainVideoSdp;
267
                 const videoMLine = sdp.media.find(m => m.type === 'video');
297
                 const videoMLine = sdp.media.find(m => m.type === 'video');
298
+
268
                 videoMLine.direction = 'recvonly';
299
                 videoMLine.direction = 'recvonly';
269
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
300
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
301
+
270
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
302
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
271
             });
303
             });
272
 
304
 
273
             it('should handle an inactive video mline', function() {
305
             it('should handle an inactive video mline', function() {
274
                 const sdp = SampleSdpStrings.plainVideoSdp;
306
                 const sdp = SampleSdpStrings.plainVideoSdp;
275
                 const videoMLine = sdp.media.find(m => m.type === 'video');
307
                 const videoMLine = sdp.media.find(m => m.type === 'video');
308
+
276
                 videoMLine.direction = 'inactive';
309
                 videoMLine.direction = 'inactive';
277
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
310
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
311
+
278
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
312
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
279
             });
313
             });
280
 
314
 
281
             it('should handle a video mline with no video ssrcs', function() {
315
             it('should handle a video mline with no video ssrcs', function() {
282
                 const sdp = SampleSdpStrings.plainVideoSdp;
316
                 const sdp = SampleSdpStrings.plainVideoSdp;
283
                 const videoMLine = sdp.media.find(m => m.type === 'video');
317
                 const videoMLine = sdp.media.find(m => m.type === 'video');
318
+
284
                 videoMLine.ssrcs = [];
319
                 videoMLine.ssrcs = [];
285
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
320
                 const newSdpStr = this.rtxModifier.modifyRtxSsrcs(this.transform.write(sdp));
321
+
286
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
322
                 expect(newSdpStr).toEqual(this.transform.write(sdp));
287
             });
323
             });
288
         });
324
         });
295
             const newSdpStr = this.rtxModifier.stripRtx(sdpStr);
331
             const newSdpStr = this.rtxModifier.stripRtx(sdpStr);
296
             const newSdp = transform.parse(newSdpStr);
332
             const newSdp = transform.parse(newSdpStr);
297
             const fidGroups = getVideoGroups(newSdp, 'FID');
333
             const fidGroups = getVideoGroups(newSdp, 'FID');
334
+
298
             expect(fidGroups.length).toEqual(0);
335
             expect(fidGroups.length).toEqual(0);
299
             expect(numVideoSsrcs(newSdp)).toEqual(1);
336
             expect(numVideoSsrcs(newSdp)).toEqual(1);
300
         });
337
         });
301
         it('should do nothing to an sdp with no rtx', function() {
338
         it('should do nothing to an sdp with no rtx', function() {
302
             const sdpStr = transform.write(SampleSdpStrings.plainVideoSdp);
339
             const sdpStr = transform.write(SampleSdpStrings.plainVideoSdp);
303
             const newSdpStr = this.rtxModifier.stripRtx(sdpStr);
340
             const newSdpStr = this.rtxModifier.stripRtx(sdpStr);
341
+
304
             expect(newSdpStr).toEqual(sdpStr);
342
             expect(newSdpStr).toEqual(sdpStr);
305
         });
343
         });
306
     });
344
     });

+ 93
- 17
modules/xmpp/SDP.js View File

5
 // SDP STUFF
5
 // SDP STUFF
6
 function SDP(sdp) {
6
 function SDP(sdp) {
7
     const media = sdp.split('\r\nm=');
7
     const media = sdp.split('\r\nm=');
8
+
8
     for (let i = 1, length = media.length; i < length; i++) {
9
     for (let i = 1, length = media.length; i < length; i++) {
9
         let media_i = `m=${media[i]}`;
10
         let media_i = `m=${media[i]}`;
11
+
10
         if (i != length - 1) {
12
         if (i != length - 1) {
11
             media_i += '\r\n';
13
             media_i += '\r\n';
12
         }
14
         }
46
     const self = this;
48
     const self = this;
47
     const media_ssrcs = {};
49
     const media_ssrcs = {};
48
     let tmp;
50
     let tmp;
51
+
49
     for (let mediaindex = 0; mediaindex < self.media.length; mediaindex++) {
52
     for (let mediaindex = 0; mediaindex < self.media.length; mediaindex++) {
50
         tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:');
53
         tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:');
51
         const mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:'));
54
         const mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:'));
55
             ssrcs: {},
58
             ssrcs: {},
56
             ssrcGroups: []
59
             ssrcGroups: []
57
         };
60
         };
61
+
58
         media_ssrcs[mediaindex] = media;
62
         media_ssrcs[mediaindex] = media;
59
         tmp.forEach(line => {
63
         tmp.forEach(line => {
60
             const linessrc = line.substring(7).split(' ')[0];
64
             const linessrc = line.substring(7).split(' ')[0];
61
             // allocate new ChannelSsrc
65
             // allocate new ChannelSsrc
66
+
62
             if(!media.ssrcs[linessrc]) {
67
             if(!media.ssrcs[linessrc]) {
63
                 media.ssrcs[linessrc] = {
68
                 media.ssrcs[linessrc] = {
64
                     ssrc: linessrc,
69
                     ssrc: linessrc,
72
             const idx = line.indexOf(' ');
77
             const idx = line.indexOf(' ');
73
             const semantics = line.substr(0, idx).substr(13);
78
             const semantics = line.substr(0, idx).substr(13);
74
             const ssrcs = line.substr(14 + semantics.length).split(' ');
79
             const ssrcs = line.substr(14 + semantics.length).split(' ');
80
+
75
             if (ssrcs.length) {
81
             if (ssrcs.length) {
76
                 media.ssrcGroups.push({
82
                 media.ssrcGroups.push({
77
                     semantics,
83
                     semantics,
80
             }
86
             }
81
         });
87
         });
82
     }
88
     }
89
+
83
     return media_ssrcs;
90
     return media_ssrcs;
84
 };
91
 };
85
 /**
92
 /**
91
     // FIXME this code is really strange - improve it if you can
98
     // FIXME this code is really strange - improve it if you can
92
     const medias = this.getMediaSsrcMap();
99
     const medias = this.getMediaSsrcMap();
93
     let result = false;
100
     let result = false;
101
+
94
     Object.keys(medias).forEach(mediaindex => {
102
     Object.keys(medias).forEach(mediaindex => {
95
         if (result) {
103
         if (result) {
96
             return;
104
             return;
99
             result = true;
107
             result = true;
100
         }
108
         }
101
     });
109
     });
110
+
102
     return result;
111
     return result;
103
 };
112
 };
104
 
113
 
105
 // remove iSAC and CN from SDP
114
 // remove iSAC and CN from SDP
106
 SDP.prototype.mangle = function() {
115
 SDP.prototype.mangle = function() {
107
     let i, j, lines, mline, newdesc, rtpmap;
116
     let i, j, lines, mline, newdesc, rtpmap;
117
+
108
     for (i = 0; i < this.media.length; i++) {
118
     for (i = 0; i < this.media.length; i++) {
109
         lines = this.media[i].split('\r\n');
119
         lines = this.media[i].split('\r\n');
110
         lines.pop(); // remove empty last element
120
         lines.pop(); // remove empty last element
133
 SDP.prototype.removeSessionLines = function(prefix) {
143
 SDP.prototype.removeSessionLines = function(prefix) {
134
     const self = this;
144
     const self = this;
135
     const lines = SDPUtil.find_lines(this.session, prefix);
145
     const lines = SDPUtil.find_lines(this.session, prefix);
146
+
136
     lines.forEach(line => {
147
     lines.forEach(line => {
137
         self.session = self.session.replace(`${line}\r\n`, '');
148
         self.session = self.session.replace(`${line}\r\n`, '');
138
     });
149
     });
139
     this.raw = this.session + this.media.join('');
150
     this.raw = this.session + this.media.join('');
151
+
140
     return lines;
152
     return lines;
141
 };
153
 };
142
 // remove lines matching prefix from a media section specified by mediaindex
154
 // remove lines matching prefix from a media section specified by mediaindex
144
 SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
156
 SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
145
     const self = this;
157
     const self = this;
146
     const lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
158
     const lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
159
+
147
     lines.forEach(line => {
160
     lines.forEach(line => {
148
         self.media[mediaindex]
161
         self.media[mediaindex]
149
             = self.media[mediaindex].replace(`${line}\r\n`, '');
162
             = self.media[mediaindex].replace(`${line}\r\n`, '');
150
     });
163
     });
151
     this.raw = this.session + this.media.join('');
164
     this.raw = this.session + this.media.join('');
165
+
152
     return lines;
166
     return lines;
153
 };
167
 };
154
 
168
 
156
 SDP.prototype.toJingle = function(elem, thecreator) {
170
 SDP.prototype.toJingle = function(elem, thecreator) {
157
     let i, j, k, lines, mline, rtpmap, ssrc, tmp;
171
     let i, j, k, lines, mline, rtpmap, ssrc, tmp;
158
     // new bundle plan
172
     // new bundle plan
173
+
159
     lines = SDPUtil.find_lines(this.session, 'a=group:');
174
     lines = SDPUtil.find_lines(this.session, 'a=group:');
160
     if (lines.length) {
175
     if (lines.length) {
161
         for (i = 0; i < lines.length; i++) {
176
         for (i = 0; i < lines.length; i++) {
162
             tmp = lines[i].split(' ');
177
             tmp = lines[i].split(' ');
163
             const semantics = tmp.shift().substr(8);
178
             const semantics = tmp.shift().substr(8);
179
+
164
             elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics});
180
             elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics});
165
             for (j = 0; j < tmp.length; j++) {
181
             for (j = 0; j < tmp.length; j++) {
166
                 elem.c('content', {name: tmp[j]}).up();
182
                 elem.c('content', {name: tmp[j]}).up();
176
             continue; // eslint-disable-line no-continue
192
             continue; // eslint-disable-line no-continue
177
         }
193
         }
178
         const assrcline = SDPUtil.find_line(this.media[i], 'a=ssrc:');
194
         const assrcline = SDPUtil.find_line(this.media[i], 'a=ssrc:');
195
+
179
         if (assrcline) {
196
         if (assrcline) {
180
             ssrc = assrcline.substring(7).split(' ')[0]; // take the first
197
             ssrc = assrcline.substring(7).split(' ')[0]; // take the first
181
         } else {
198
         } else {
184
 
201
 
185
         elem.c('content', {creator: thecreator, name: mline.media});
202
         elem.c('content', {creator: thecreator, name: mline.media});
186
         const amidline = SDPUtil.find_line(this.media[i], 'a=mid:');
203
         const amidline = SDPUtil.find_line(this.media[i], 'a=mid:');
204
+
187
         if (amidline) {
205
         if (amidline) {
188
             // prefer identifier from a=mid if present
206
             // prefer identifier from a=mid if present
189
             const mid = SDPUtil.parse_mid(amidline);
207
             const mid = SDPUtil.parse_mid(amidline);
208
+
190
             elem.attrs({ name: mid });
209
             elem.attrs({ name: mid });
191
         }
210
         }
192
 
211
 
202
                 elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
221
                 elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
203
                 // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
222
                 // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
204
                 const afmtpline = SDPUtil.find_line(this.media[i], `a=fmtp:${mline.fmt[j]}`);
223
                 const afmtpline = SDPUtil.find_line(this.media[i], `a=fmtp:${mline.fmt[j]}`);
224
+
205
                 if (afmtpline) {
225
                 if (afmtpline) {
206
                     tmp = SDPUtil.parse_fmtp(afmtpline);
226
                     tmp = SDPUtil.parse_fmtp(afmtpline);
207
                     for (k = 0; k < tmp.length; k++) {
227
                     for (k = 0; k < tmp.length; k++) {
213
                 elem.up();
233
                 elem.up();
214
             }
234
             }
215
             const crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
235
             const crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
236
+
216
             if (crypto.length) {
237
             if (crypto.length) {
217
                 elem.c('encryption', {required: 1});
238
                 elem.c('encryption', {required: 1});
218
                 crypto.forEach(
239
                 crypto.forEach(
225
                 elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
246
                 elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
226
                 // FIXME: group by ssrc and support multiple different ssrcs
247
                 // FIXME: group by ssrc and support multiple different ssrcs
227
                 const ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
248
                 const ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
249
+
228
                 if(ssrclines.length > 0) {
250
                 if(ssrclines.length > 0) {
229
                     ssrclines.forEach(line => {
251
                     ssrclines.forEach(line => {
230
                         const idx = line.indexOf(' ');
252
                         const idx = line.indexOf(' ');
231
                         const linessrc = line.substr(0, idx).substr(7);
253
                         const linessrc = line.substr(0, idx).substr(7);
254
+
232
                         if (linessrc != ssrc) {
255
                         if (linessrc != ssrc) {
233
                             elem.up();
256
                             elem.up();
234
                             ssrc = linessrc;
257
                             ssrc = linessrc;
235
                             elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
258
                             elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
236
                         }
259
                         }
237
                         const kv = line.substr(idx + 1);
260
                         const kv = line.substr(idx + 1);
261
+
238
                         elem.c('parameter');
262
                         elem.c('parameter');
239
                         if (kv.indexOf(':') == -1) {
263
                         if (kv.indexOf(':') == -1) {
240
                             elem.attrs({ name: kv });
264
                             elem.attrs({ name: kv });
241
                         } else {
265
                         } else {
242
                             const k = kv.split(':', 2)[0];
266
                             const k = kv.split(':', 2)[0];
267
+
243
                             elem.attrs({ name: k });
268
                             elem.attrs({ name: k });
244
 
269
 
245
                             let v = kv.split(':', 2)[1];
270
                             let v = kv.split(':', 2)[1];
271
+
246
                             v = SDPUtil.filter_special_chars(v);
272
                             v = SDPUtil.filter_special_chars(v);
247
                             elem.attrs({ value: v });
273
                             elem.attrs({ value: v });
248
                         }
274
                         }
252
                     elem.up();
278
                     elem.up();
253
                     elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
279
                     elem.c('source', { ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
254
                     elem.c('parameter');
280
                     elem.c('parameter');
255
-                    elem.attrs({name: 'cname', value:Math.random().toString(36).substring(7)});
281
+                    elem.attrs({
282
+                        name: 'cname',
283
+
284
+                        // eslint-disable-next-line newline-per-chained-call
285
+                        value: Math.random().toString(36).substring(7)
286
+                    });
256
                     elem.up();
287
                     elem.up();
257
                     // FIXME what case does this code handle ? remove ???
288
                     // FIXME what case does this code handle ? remove ???
258
                     let msid = null;
289
                     let msid = null;
259
                     // FIXME what is this ? global APP.RTC in SDP ?
290
                     // FIXME what is this ? global APP.RTC in SDP ?
260
                     const localTrack = APP.RTC.getLocalTracks(mline.media);
291
                     const localTrack = APP.RTC.getLocalTracks(mline.media);
292
+
261
                     if (localTrack) {
293
                     if (localTrack) {
262
                         // FIXME before this changes the track id was accessed,
294
                         // FIXME before this changes the track id was accessed,
263
                         // but msid stands for the stream id, makes no sense ?
295
                         // but msid stands for the stream id, makes no sense ?
280
 
312
 
281
                 // XEP-0339 handle ssrc-group attributes
313
                 // XEP-0339 handle ssrc-group attributes
282
                 const ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
314
                 const ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
315
+
283
                 ssrc_group_lines.forEach(line => {
316
                 ssrc_group_lines.forEach(line => {
284
                     const idx = line.indexOf(' ');
317
                     const idx = line.indexOf(' ');
285
                     const semantics = line.substr(0, idx).substr(13);
318
                     const semantics = line.substr(0, idx).substr(13);
286
                     const ssrcs = line.substr(14 + semantics.length).split(' ');
319
                     const ssrcs = line.substr(14 + semantics.length).split(' ');
320
+
287
                     if (ssrcs.length) {
321
                     if (ssrcs.length) {
288
                         elem.c('ssrc-group', { semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
322
                         elem.c('ssrc-group', { semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
289
                         ssrcs.forEach(ssrc => elem.c('source', { ssrc }).up());
323
                         ssrcs.forEach(ssrc => elem.c('source', { ssrc }).up());
349
         elem.up(); // end of content
383
         elem.up(); // end of content
350
     }
384
     }
351
     elem.up();
385
     elem.up();
386
+
352
     return elem;
387
     return elem;
353
 };
388
 };
354
 
389
 
355
 SDP.prototype.transportToJingle = function(mediaindex, elem) {
390
 SDP.prototype.transportToJingle = function(mediaindex, elem) {
356
     let tmp;
391
     let tmp;
357
     const self = this;
392
     const self = this;
393
+
358
     elem.c('transport');
394
     elem.c('transport');
359
 
395
 
360
     // XEP-0343 DTLS/SCTP
396
     // XEP-0343 DTLS/SCTP
361
     const sctpmap
397
     const sctpmap
362
         = SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:', self.session);
398
         = SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:', self.session);
399
+
363
     if (sctpmap) {
400
     if (sctpmap) {
364
         const sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
401
         const sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
402
+
365
         elem.c('sctpmap', {
403
         elem.c('sctpmap', {
366
             xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
404
             xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
367
             number: sctpAttrs[0], /* SCTP port */
405
             number: sctpAttrs[0], /* SCTP port */
376
     // XEP-0320
414
     // XEP-0320
377
     const fingerprints
415
     const fingerprints
378
         = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
416
         = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
417
+
379
     fingerprints.forEach(line => {
418
     fingerprints.forEach(line => {
380
         tmp = SDPUtil.parse_fingerprint(line);
419
         tmp = SDPUtil.parse_fingerprint(line);
381
         tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
420
         tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
395
         // XEP-0176
434
         // XEP-0176
396
         if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
435
         if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
397
             const lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
436
             const lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
437
+
398
             lines.forEach(line => {
438
             lines.forEach(line => {
399
                 const candidate = SDPUtil.candidateToJingle(line);
439
                 const candidate = SDPUtil.candidateToJingle(line);
440
+
400
                 if (self.failICE) {
441
                 if (self.failICE) {
401
                     candidate.ip = '1.1.1.1';
442
                     candidate.ip = '1.1.1.1';
402
                 }
443
                 }
403
                 const protocol = candidate
444
                 const protocol = candidate
404
                         && typeof candidate.protocol === 'string'
445
                         && typeof candidate.protocol === 'string'
405
                     ? candidate.protocol.toLowerCase() : '';
446
                     ? candidate.protocol.toLowerCase() : '';
447
+
406
                 if ((self.removeTcpCandidates
448
                 if ((self.removeTcpCandidates
407
                         && (protocol === 'tcp' || protocol === 'ssltcp'))
449
                         && (protocol === 'tcp' || protocol === 'ssltcp'))
408
                     || (self.removeUdpCandidates && protocol === 'udp')) {
450
                     || (self.removeUdpCandidates && protocol === 'udp')) {
417
 
459
 
418
 SDP.prototype.rtcpFbToJingle = function(mediaindex, elem, payloadtype) { // XEP-0293
460
 SDP.prototype.rtcpFbToJingle = function(mediaindex, elem, payloadtype) { // XEP-0293
419
     const lines = SDPUtil.find_lines(this.media[mediaindex], `a=rtcp-fb:${payloadtype}`);
461
     const lines = SDPUtil.find_lines(this.media[mediaindex], `a=rtcp-fb:${payloadtype}`);
462
+
420
     lines.forEach(line => {
463
     lines.forEach(line => {
421
         const tmp = SDPUtil.parse_rtcpfb(line);
464
         const tmp = SDPUtil.parse_rtcpfb(line);
465
+
422
         if (tmp.type == 'trr-int') {
466
         if (tmp.type == 'trr-int') {
423
             elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
467
             elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
424
             elem.up();
468
             elem.up();
435
 SDP.prototype.rtcpFbFromJingle = function(elem, payloadtype) { // XEP-0293
479
 SDP.prototype.rtcpFbFromJingle = function(elem, payloadtype) { // XEP-0293
436
     let media = '';
480
     let media = '';
437
     let tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
481
     let tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
482
+
438
     if (tmp.length) {
483
     if (tmp.length) {
439
         media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
484
         media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
440
         if (tmp.attr('value')) {
485
         if (tmp.attr('value')) {
452
         }
497
         }
453
         media += '\r\n';
498
         media += '\r\n';
454
     });
499
     });
500
+
455
     return media;
501
     return media;
456
 };
502
 };
457
 
503
 
458
 // construct an SDP from a jingle stanza
504
 // construct an SDP from a jingle stanza
459
 SDP.prototype.fromJingle = function(jingle) {
505
 SDP.prototype.fromJingle = function(jingle) {
460
     const self = this;
506
     const self = this;
507
+
461
     this.raw = 'v=0\r\n'
508
     this.raw = 'v=0\r\n'
462
         + 'o=- 1923518516 2 IN IP4 0.0.0.0\r\n'// FIXME
509
         + 'o=- 1923518516 2 IN IP4 0.0.0.0\r\n'// FIXME
463
         + 's=-\r\n'
510
         + 's=-\r\n'
464
         + 't=0 0\r\n';
511
         + 't=0 0\r\n';
512
+
465
     // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
513
     // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
466
-    if ($(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').length) {
467
-        $(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').each((idx, group) => {
468
-            const contents = $(group).find('>content').map((idx, content) => content.getAttribute('name')).get();
514
+    const groups
515
+        = $(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]');
516
+
517
+    if (groups.length) {
518
+        groups.each((idx, group) => {
519
+            const contents
520
+                = $(group)
521
+                    .find('>content')
522
+                    .map((idx, content) => content.getAttribute('name'))
523
+                    .get();
524
+
469
             if (contents.length > 0) {
525
             if (contents.length > 0) {
470
-                self.raw += `a=group:${group.getAttribute('semantics') || group.getAttribute('type')} ${contents.join(' ')}\r\n`;
526
+                self.raw
527
+                    += `a=group:${group.getAttribute('semantics') || group.getAttribute('type')} ${contents.join(' ')}\r\n`;
471
             }
528
             }
472
         });
529
         });
473
     }
530
     }
475
     this.session = this.raw;
532
     this.session = this.raw;
476
     jingle.find('>content').each(function() {
533
     jingle.find('>content').each(function() {
477
         const m = self.jingle2media($(this));
534
         const m = self.jingle2media($(this));
535
+
478
         self.media.push(m);
536
         self.media.push(m);
479
     });
537
     });
480
 
538
 
498
         '>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
556
         '>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
499
 
557
 
500
     let tmp = { media: desc.attr('media') };
558
     let tmp = { media: desc.attr('media') };
559
+
501
     tmp.port = '1';
560
     tmp.port = '1';
502
     if (content.attr('senders') == 'rejected') {
561
     if (content.attr('senders') == 'rejected') {
503
         // estos hack to reject an m-line.
562
         // estos hack to reject an m-line.
514
         media += `a=sctpmap:${sctp.attr('number')} ${sctp.attr('protocol')}`;
573
         media += `a=sctpmap:${sctp.attr('number')} ${sctp.attr('protocol')}`;
515
 
574
 
516
         const streamCount = sctp.attr('streams');
575
         const streamCount = sctp.attr('streams');
576
+
517
         if (streamCount) {
577
         if (streamCount) {
518
             media += ` ${streamCount}\r\n`;
578
             media += ` ${streamCount}\r\n`;
519
         } else {
579
         } else {
520
             media += '\r\n';
580
             media += '\r\n';
521
         }
581
         }
522
     } else {
582
     } else {
523
-        tmp.fmt = desc.find('payload-type').map(
524
-            function() {
525
-                return this.getAttribute('id');
526
-            }).get();
583
+        tmp.fmt
584
+            = desc
585
+                .find('payload-type')
586
+                .map(function() {
587
+                    return this.getAttribute('id');
588
+                })
589
+                .get();
527
         media += `${SDPUtil.build_mline(tmp)}\r\n`;
590
         media += `${SDPUtil.build_mline(tmp)}\r\n`;
528
     }
591
     }
529
 
592
 
587
         media += `${SDPUtil.build_rtpmap(this)}\r\n`;
650
         media += `${SDPUtil.build_rtpmap(this)}\r\n`;
588
         if ($(this).find('>parameter').length) {
651
         if ($(this).find('>parameter').length) {
589
             media += `a=fmtp:${this.getAttribute('id')} `;
652
             media += `a=fmtp:${this.getAttribute('id')} `;
590
-            media += $(this).find('parameter').map(function() {
591
-                return (this.getAttribute('name')
592
-                        ? `${this.getAttribute('name')}=` : '')
593
-                    + this.getAttribute('value');
594
-            }).get().join('; ');
653
+            media
654
+                += $(this)
655
+                    .find('parameter')
656
+                    .map(function() {
657
+                        return (this.getAttribute('name')
658
+                                ? `${this.getAttribute('name')}=` : '')
659
+                            + this.getAttribute('value');
660
+                    })
661
+                    .get()
662
+                    .join('; ');
595
             media += '\r\n';
663
             media += '\r\n';
596
         }
664
         }
597
         // xep-0293
665
         // xep-0293
609
 
677
 
610
     content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function() {
678
     content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function() {
611
         let protocol = this.getAttribute('protocol');
679
         let protocol = this.getAttribute('protocol');
680
+
612
         protocol = typeof protocol === 'string' ? protocol.toLowerCase() : '';
681
         protocol = typeof protocol === 'string' ? protocol.toLowerCase() : '';
613
 
682
 
614
         if ((self.removeTcpCandidates
683
         if ((self.removeTcpCandidates
625
     // XEP-0339 handle ssrc-group attributes
694
     // XEP-0339 handle ssrc-group attributes
626
     content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
695
     content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
627
         const semantics = this.getAttribute('semantics');
696
         const semantics = this.getAttribute('semantics');
628
-        const ssrcs = $(this).find('>source').map(function() {
629
-            return this.getAttribute('ssrc');
630
-        }).get();
697
+        const ssrcs
698
+            = $(this)
699
+                .find('>source')
700
+                .map(function() {
701
+                    return this.getAttribute('ssrc');
702
+                })
703
+                .get();
631
 
704
 
632
         if (ssrcs.length) {
705
         if (ssrcs.length) {
633
             media += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\r\n`;
706
             media += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\r\n`;
637
     tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
710
     tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
638
     tmp.each(function() {
711
     tmp.each(function() {
639
         const ssrc = this.getAttribute('ssrc');
712
         const ssrc = this.getAttribute('ssrc');
713
+
714
+        // eslint-disable-next-line newline-per-chained-call
640
         $(this).find('>parameter').each(function() {
715
         $(this).find('>parameter').each(function() {
641
             const name = this.getAttribute('name');
716
             const name = this.getAttribute('name');
642
             let value = this.getAttribute('value');
717
             let value = this.getAttribute('value');
718
+
643
             value = SDPUtil.filter_special_chars(value);
719
             value = SDPUtil.filter_special_chars(value);
644
             media += `a=ssrc:${ssrc} ${name}`;
720
             media += `a=ssrc:${ssrc} ${name}`;
645
             if (value && value.length) {
721
             if (value && value.length) {

+ 12
- 0
modules/xmpp/SDPDiffer.js View File

36
                 return false;
36
                 return false;
37
             }
37
             }
38
         }
38
         }
39
+
39
         return true;
40
         return true;
40
     }
41
     }
41
 
42
 
42
     const myMedias = this.mySDP.getMediaSsrcMap();
43
     const myMedias = this.mySDP.getMediaSsrcMap();
43
     const othersMedias = this.otherSDP.getMediaSsrcMap();
44
     const othersMedias = this.otherSDP.getMediaSsrcMap();
44
     const newMedia = {};
45
     const newMedia = {};
46
+
45
     Object.keys(othersMedias).forEach(othersMediaIdx => {
47
     Object.keys(othersMedias).forEach(othersMediaIdx => {
46
         const myMedia = myMedias[othersMediaIdx];
48
         const myMedia = myMedias[othersMediaIdx];
47
         const othersMedia = othersMedias[othersMediaIdx];
49
         const othersMedia = othersMedias[othersMediaIdx];
50
+
48
         if(!myMedia && othersMedia) {
51
         if(!myMedia && othersMedia) {
49
             // Add whole channel
52
             // Add whole channel
50
             newMedia[othersMediaIdx] = othersMedia;
53
             newMedia[othersMediaIdx] = othersMedia;
54
+
51
             return;
55
             return;
52
         }
56
         }
53
         // Look for new ssrcs across the channel
57
         // Look for new ssrcs across the channel
72
 
76
 
73
             // try to match the other ssrc-group with an ssrc-group of ours
77
             // try to match the other ssrc-group with an ssrc-group of ours
74
             let matched = false;
78
             let matched = false;
79
+
75
             for (let i = 0; i < myMedia.ssrcGroups.length; i++) {
80
             for (let i = 0; i < myMedia.ssrcGroups.length; i++) {
76
                 const mySsrcGroup = myMedia.ssrcGroups[i];
81
                 const mySsrcGroup = myMedia.ssrcGroups[i];
82
+
77
                 if (otherSsrcGroup.semantics == mySsrcGroup.semantics
83
                 if (otherSsrcGroup.semantics == mySsrcGroup.semantics
78
                     && arrayEquals.apply(otherSsrcGroup.ssrcs,
84
                     && arrayEquals.apply(otherSsrcGroup.ssrcs,
79
                                       [mySsrcGroup.ssrcs])) {
85
                                       [mySsrcGroup.ssrcs])) {
99
             }
105
             }
100
         });
106
         });
101
     });
107
     });
108
+
102
     return newMedia;
109
     return newMedia;
103
 };
110
 };
104
 
111
 
109
     const sdpMediaSsrcs = this.getNewMedia();
116
     const sdpMediaSsrcs = this.getNewMedia();
110
 
117
 
111
     let modified = false;
118
     let modified = false;
119
+
112
     Object.keys(sdpMediaSsrcs).forEach(mediaindex => {
120
     Object.keys(sdpMediaSsrcs).forEach(mediaindex => {
113
         modified = true;
121
         modified = true;
114
         const media = sdpMediaSsrcs[mediaindex];
122
         const media = sdpMediaSsrcs[mediaindex];
123
+
115
         modify.c('content', {name: media.mid});
124
         modify.c('content', {name: media.mid});
116
 
125
 
117
         modify.c('description',
126
         modify.c('description',
121
         // generate sources from lines
130
         // generate sources from lines
122
         Object.keys(media.ssrcs).forEach(ssrcNum => {
131
         Object.keys(media.ssrcs).forEach(ssrcNum => {
123
             const mediaSsrc = media.ssrcs[ssrcNum];
132
             const mediaSsrc = media.ssrcs[ssrcNum];
133
+
124
             modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
134
             modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
125
             modify.attrs({ssrc: mediaSsrc.ssrc});
135
             modify.attrs({ssrc: mediaSsrc.ssrc});
126
             // iterate over ssrc lines
136
             // iterate over ssrc lines
127
             mediaSsrc.lines.forEach(line => {
137
             mediaSsrc.lines.forEach(line => {
128
                 const idx = line.indexOf(' ');
138
                 const idx = line.indexOf(' ');
129
                 const kv = line.substr(idx + 1);
139
                 const kv = line.substr(idx + 1);
140
+
130
                 modify.c('parameter');
141
                 modify.c('parameter');
131
                 if (kv.indexOf(':') == -1) {
142
                 if (kv.indexOf(':') == -1) {
132
                     modify.attrs({ name: kv });
143
                     modify.attrs({ name: kv });
134
                     const nv = kv.split(':', 2);
145
                     const nv = kv.split(':', 2);
135
                     const name = nv[0];
146
                     const name = nv[0];
136
                     const value = SDPUtil.filter_special_chars(nv[1]);
147
                     const value = SDPUtil.filter_special_chars(nv[1]);
148
+
137
                     modify.attrs({ name });
149
                     modify.attrs({ name });
138
                     modify.attrs({ value });
150
                     modify.attrs({ value });
139
                 }
151
                 }

+ 49
- 2
modules/xmpp/SDPUtil.js View File

1
 import {getLogger} from 'jitsi-meet-logger';
1
 import {getLogger} from 'jitsi-meet-logger';
2
 const logger = getLogger(__filename);
2
 const logger = getLogger(__filename);
3
+
3
 import RandomUtil from '../util/RandomUtil';
4
 import RandomUtil from '../util/RandomUtil';
4
 const RTCBrowserType = require('../RTC/RTCBrowserType');
5
 const RTCBrowserType = require('../RTC/RTCBrowserType');
5
 
6
 
12
     iceparams(mediadesc, sessiondesc) {
13
     iceparams(mediadesc, sessiondesc) {
13
         let data = null;
14
         let data = null;
14
         let pwd, ufrag;
15
         let pwd, ufrag;
16
+
15
         if ((ufrag = SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc))
17
         if ((ufrag = SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc))
16
                 && (pwd = SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))) {
18
                 && (pwd = SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))) {
17
             data = {
19
             data = {
19
                 pwd: SDPUtil.parse_icepwd(pwd)
21
                 pwd: SDPUtil.parse_icepwd(pwd)
20
             };
22
             };
21
         }
23
         }
24
+
22
         return data;
25
         return data;
23
     },
26
     },
24
     parse_iceufrag(line) {
27
     parse_iceufrag(line) {
47
             parts.pop();
50
             parts.pop();
48
         }
51
         }
49
         data.fmt = parts;
52
         data.fmt = parts;
53
+
50
         return data;
54
         return data;
51
     },
55
     },
52
     build_mline(mline) {
56
     build_mline(mline) {
61
         data.name = parts.shift();
65
         data.name = parts.shift();
62
         data.clockrate = parts.shift();
66
         data.clockrate = parts.shift();
63
         data.channels = parts.length ? parts.shift() : '1';
67
         data.channels = parts.length ? parts.shift() : '1';
68
+
64
         return data;
69
         return data;
65
     },
70
     },
66
     /**
71
     /**
74
         const protocol = parts[1];
79
         const protocol = parts[1];
75
         // Stream count is optional
80
         // Stream count is optional
76
         const streamCount = parts.length > 2 ? parts[2] : null;
81
         const streamCount = parts.length > 2 ? parts[2] : null;
82
+
83
+
77
         return [sctpPort, protocol, streamCount];// SCTP port
84
         return [sctpPort, protocol, streamCount];// SCTP port
78
     },
85
     },
79
     build_rtpmap(el) {
86
     build_rtpmap(el) {
80
         let line = `a=rtpmap:${el.getAttribute('id')} ${el.getAttribute('name')}/${el.getAttribute('clockrate')}`;
87
         let line = `a=rtpmap:${el.getAttribute('id')} ${el.getAttribute('name')}/${el.getAttribute('clockrate')}`;
88
+
81
         if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
89
         if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
82
             line += `/${el.getAttribute('channels')}`;
90
             line += `/${el.getAttribute('channels')}`;
83
         }
91
         }
92
+
84
         return line;
93
         return line;
85
     },
94
     },
86
     parse_crypto(line) {
95
     parse_crypto(line) {
93
         if (parts.length) {
102
         if (parts.length) {
94
             data['session-params'] = parts.join(' ');
103
             data['session-params'] = parts.join(' ');
95
         }
104
         }
105
+
96
         return data;
106
         return data;
97
     },
107
     },
98
     parse_fingerprint(line) { // RFC 4572
108
     parse_fingerprint(line) { // RFC 4572
101
 
111
 
102
         data.hash = parts.shift();
112
         data.hash = parts.shift();
103
         data.fingerprint = parts.shift();
113
         data.fingerprint = parts.shift();
114
+
104
         // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
115
         // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
105
         return data;
116
         return data;
106
     },
117
     },
112
         parts = parts.join(' ').split(';');
123
         parts = parts.join(' ').split(';');
113
         for (let i = 0; i < parts.length; i++) {
124
         for (let i = 0; i < parts.length; i++) {
114
             let key = parts[i].split('=')[0];
125
             let key = parts[i].split('=')[0];
126
+
115
             while (key.length && key[0] == ' ') {
127
             while (key.length && key[0] == ' ') {
116
                 key = key.substring(1);
128
                 key = key.substring(1);
117
             }
129
             }
118
             const value = parts[i].split('=')[1];
130
             const value = parts[i].split('=')[1];
131
+
119
             if (key && value) {
132
             if (key && value) {
120
                 data.push({name: key, value});
133
                 data.push({name: key, value});
121
             } else if (key) {
134
             } else if (key) {
123
                 data.push({name: '', value: key});
136
                 data.push({name: '', value: key});
124
             }
137
             }
125
         }
138
         }
139
+
126
         return data;
140
         return data;
127
     },
141
     },
128
     parse_icecandidate(line) {
142
     parse_icecandidate(line) {
157
             }
171
             }
158
         }
172
         }
159
         candidate.network = '1';
173
         candidate.network = '1';
160
-        candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
174
+
175
+        // not applicable to SDP -- FIXME: should be unique, not just random
176
+        // eslint-disable-next-line newline-per-chained-call
177
+        candidate.id = Math.random().toString(36).substr(2, 10);
178
+
161
         return candidate;
179
         return candidate;
162
     },
180
     },
163
     build_icecandidate(cand) {
181
     build_icecandidate(cand) {
164
         let line = [`a=candidate:${cand.foundation}`, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
182
         let line = [`a=candidate:${cand.foundation}`, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
183
+
165
         line += ' ';
184
         line += ' ';
166
         switch (cand.type) {
185
         switch (cand.type) {
167
         case 'srflx':
186
         case 'srflx':
188
         line += 'generation';
207
         line += 'generation';
189
         line += ' ';
208
         line += ' ';
190
         line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
209
         line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
210
+
191
         return line;
211
         return line;
192
     },
212
     },
193
     parse_ssrc(desc) {
213
     parse_ssrc(desc) {
200
         for (let i = 0; i < lines.length; i++) {
220
         for (let i = 0; i < lines.length; i++) {
201
             if (lines[i].substring(0, 7) == 'a=ssrc:') {
221
             if (lines[i].substring(0, 7) == 'a=ssrc:') {
202
                 const idx = lines[i].indexOf(' ');
222
                 const idx = lines[i].indexOf(' ');
223
+
203
                 data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
224
                 data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
204
             }
225
             }
205
         }
226
         }
227
+
206
         return data;
228
         return data;
207
     },
229
     },
208
     parse_rtcpfb(line) {
230
     parse_rtcpfb(line) {
209
         const parts = line.substr(10).split(' ');
231
         const parts = line.substr(10).split(' ');
210
         const data = {};
232
         const data = {};
233
+
211
         data.pt = parts.shift();
234
         data.pt = parts.shift();
212
         data.type = parts.shift();
235
         data.type = parts.shift();
213
         data.params = parts;
236
         data.params = parts;
237
+
214
         return data;
238
         return data;
215
     },
239
     },
216
     parse_extmap(line) {
240
     parse_extmap(line) {
217
         const parts = line.substr(9).split(' ');
241
         const parts = line.substr(9).split(' ');
218
         const data = {};
242
         const data = {};
243
+
219
         data.value = parts.shift();
244
         data.value = parts.shift();
220
         if (data.value.indexOf('/') === -1) {
245
         if (data.value.indexOf('/') === -1) {
221
             data.direction = 'both';
246
             data.direction = 'both';
225
         }
250
         }
226
         data.uri = parts.shift();
251
         data.uri = parts.shift();
227
         data.params = parts;
252
         data.params = parts;
253
+
228
         return data;
254
         return data;
229
     },
255
     },
230
     find_line(haystack, needle, sessionpart) {
256
     find_line(haystack, needle, sessionpart) {
231
         let lines = haystack.split('\r\n');
257
         let lines = haystack.split('\r\n');
258
+
232
         for (let i = 0; i < lines.length; i++) {
259
         for (let i = 0; i < lines.length; i++) {
233
             if (lines[i].substring(0, needle.length) == needle) {
260
             if (lines[i].substring(0, needle.length) == needle) {
234
                 return lines[i];
261
                 return lines[i];
244
                 return lines[j];
271
                 return lines[j];
245
             }
272
             }
246
         }
273
         }
274
+
247
         return false;
275
         return false;
248
     },
276
     },
249
     find_lines(haystack, needle, sessionpart) {
277
     find_lines(haystack, needle, sessionpart) {
265
                 needles.push(lines[j]);
293
                 needles.push(lines[j]);
266
             }
294
             }
267
         }
295
         }
296
+
268
         return needles;
297
         return needles;
269
     },
298
     },
270
     candidateToJingle(line) {
299
     candidateToJingle(line) {
275
         } else if (line.substring(0, 12) != 'a=candidate:') {
304
         } else if (line.substring(0, 12) != 'a=candidate:') {
276
             logger.log('parseCandidate called with a line that is not a candidate line');
305
             logger.log('parseCandidate called with a line that is not a candidate line');
277
             logger.log(line);
306
             logger.log(line);
307
+
278
             return null;
308
             return null;
279
         }
309
         }
280
         if (line.substring(line.length - 2) == '\r\n') {// chomp it
310
         if (line.substring(line.length - 2) == '\r\n') {// chomp it
286
         if (elems[6] != 'typ') {
316
         if (elems[6] != 'typ') {
287
             logger.log('did not find typ in the right place');
317
             logger.log('did not find typ in the right place');
288
             logger.log(line);
318
             logger.log(line);
319
+
289
             return null;
320
             return null;
290
         }
321
         }
291
         candidate.foundation = elems[0].substring(12);
322
         candidate.foundation = elems[0].substring(12);
317
             }
348
             }
318
         }
349
         }
319
         candidate.network = '1';
350
         candidate.network = '1';
320
-        candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
351
+
352
+        // not applicable to SDP -- FIXME: should be unique, not just random
353
+        // eslint-disable-next-line newline-per-chained-call
354
+        candidate.id = Math.random().toString(36).substr(2, 10);
355
+
321
         return candidate;
356
         return candidate;
322
     },
357
     },
323
     candidateFromJingle(cand) {
358
     candidateFromJingle(cand) {
324
         let line = 'a=candidate:';
359
         let line = 'a=candidate:';
360
+
325
         line += cand.getAttribute('foundation');
361
         line += cand.getAttribute('foundation');
326
         line += ' ';
362
         line += ' ';
327
         line += cand.getAttribute('component');
363
         line += cand.getAttribute('component');
329
 
365
 
330
         let protocol = cand.getAttribute('protocol');
366
         let protocol = cand.getAttribute('protocol');
331
         // use tcp candidates for FF
367
         // use tcp candidates for FF
368
+
332
         if (RTCBrowserType.isFirefox() && protocol.toLowerCase() == 'ssltcp') {
369
         if (RTCBrowserType.isFirefox() && protocol.toLowerCase() == 'ssltcp') {
333
             protocol = 'tcp';
370
             protocol = 'tcp';
334
         }
371
         }
369
         line += 'generation';
406
         line += 'generation';
370
         line += ' ';
407
         line += ' ';
371
         line += cand.getAttribute('generation') || '0';
408
         line += cand.getAttribute('generation') || '0';
409
+
372
         return `${line}\r\n`;
410
         return `${line}\r\n`;
373
     },
411
     },
374
 
412
 
384
             .length;
422
             .length;
385
         const numGroups
423
         const numGroups
386
             = (videoMLine.ssrcGroups && videoMLine.ssrcGroups.length) || 0;
424
             = (videoMLine.ssrcGroups && videoMLine.ssrcGroups.length) || 0;
425
+
387
         if (numSsrcs > 1 && numGroups === 0) {
426
         if (numSsrcs > 1 && numGroups === 0) {
388
             // Ambiguous, can't figure out the primary
427
             // Ambiguous, can't figure out the primary
389
             return;
428
             return;
390
         }
429
         }
391
         let primarySsrc = null;
430
         let primarySsrc = null;
431
+
392
         if (numSsrcs === 1) {
432
         if (numSsrcs === 1) {
393
             primarySsrc = videoMLine.ssrcs[0].id;
433
             primarySsrc = videoMLine.ssrcs[0].id;
394
         } else if (numSsrcs === 2) {
434
         } else if (numSsrcs === 2) {
396
             const fidGroup
436
             const fidGroup
397
                 = videoMLine.ssrcGroups.find(
437
                 = videoMLine.ssrcGroups.find(
398
                     group => group.semantics === 'FID');
438
                     group => group.semantics === 'FID');
439
+
399
             if (fidGroup) {
440
             if (fidGroup) {
400
                 primarySsrc = fidGroup.ssrcs.split(' ')[0];
441
                 primarySsrc = fidGroup.ssrcs.split(' ')[0];
401
             }
442
             }
404
             const simGroup
445
             const simGroup
405
                 = videoMLine.ssrcGroups.find(
446
                 = videoMLine.ssrcGroups.find(
406
                     group => group.semantics === 'SIM');
447
                     group => group.semantics === 'SIM');
448
+
407
             if (simGroup) {
449
             if (simGroup) {
408
                 primarySsrc = simGroup.ssrcs.split(' ')[0];
450
                 primarySsrc = simGroup.ssrcs.split(' ')[0];
409
             }
451
             }
410
         }
452
         }
453
+
411
         return primarySsrc;
454
         return primarySsrc;
412
     },
455
     },
413
 
456
 
431
     getSsrcAttribute(mLine, ssrc, attributeName) {
474
     getSsrcAttribute(mLine, ssrc, attributeName) {
432
         for (let i = 0; i < mLine.ssrcs.length; ++i) {
475
         for (let i = 0; i < mLine.ssrcs.length; ++i) {
433
             const ssrcLine = mLine.ssrcs[i];
476
             const ssrcLine = mLine.ssrcs[i];
477
+
434
             if (ssrcLine.id === ssrc
478
             if (ssrcLine.id === ssrc
435
                 && ssrcLine.attribute === attributeName) {
479
                 && ssrcLine.attribute === attributeName) {
436
                 return ssrcLine.value;
480
                 return ssrcLine.value;
475
      */
519
      */
476
     preferVideoCodec(videoMLine, codecName) {
520
     preferVideoCodec(videoMLine, codecName) {
477
         let payloadType = null;
521
         let payloadType = null;
522
+
478
         for (let i = 0; i < videoMLine.rtp.length; ++i) {
523
         for (let i = 0; i < videoMLine.rtp.length; ++i) {
479
             const rtp = videoMLine.rtp[i];
524
             const rtp = videoMLine.rtp[i];
525
+
480
             if (rtp.codec === codecName) {
526
             if (rtp.codec === codecName) {
481
                 payloadType = rtp.payload;
527
                 payloadType = rtp.payload;
482
                 break;
528
                 break;
485
         if (payloadType) {
531
         if (payloadType) {
486
             const payloadTypes = videoMLine.payloads.split(' ').map(p => parseInt(p));
532
             const payloadTypes = videoMLine.payloads.split(' ').map(p => parseInt(p));
487
             const payloadIndex = payloadTypes.indexOf(payloadType);
533
             const payloadIndex = payloadTypes.indexOf(payloadType);
534
+
488
             payloadTypes.splice(payloadIndex, 1);
535
             payloadTypes.splice(payloadIndex, 1);
489
             payloadTypes.unshift(payloadType);
536
             payloadTypes.unshift(payloadType);
490
             videoMLine.payloads = payloadTypes.join(' ');
537
             videoMLine.payloads = payloadTypes.join(' ');

+ 2
- 0
modules/xmpp/SDPUtil.spec.js View File

13
         it('should move a preferred codec to the front', () => {
13
         it('should move a preferred codec to the front', () => {
14
             const sdp = SampleSdpStrings.multiCodecVideoSdp;
14
             const sdp = SampleSdpStrings.multiCodecVideoSdp;
15
             const videoMLine = sdp.media.find(m => m.type === 'video');
15
             const videoMLine = sdp.media.find(m => m.type === 'video');
16
+
16
             SDPUtil.preferVideoCodec(videoMLine, 'H264');
17
             SDPUtil.preferVideoCodec(videoMLine, 'H264');
17
             const newPayloadTypesOrder
18
             const newPayloadTypesOrder
18
                 = videoMLine.payloads.split(' ').map(ptStr => parseInt(ptStr));
19
                 = videoMLine.payloads.split(' ').map(ptStr => parseInt(ptStr));
20
+
19
             expect(newPayloadTypesOrder[0]).toEqual(126);
21
             expect(newPayloadTypesOrder[0]).toEqual(126);
20
         });
22
         });
21
     });
23
     });

+ 7
- 0
modules/xmpp/SdpConsistency.js View File

61
     makeVideoPrimarySsrcsConsistent(sdpStr) {
61
     makeVideoPrimarySsrcsConsistent(sdpStr) {
62
         const sdpTransformer = new SdpTransformWrap(sdpStr);
62
         const sdpTransformer = new SdpTransformWrap(sdpStr);
63
         const videoMLine = sdpTransformer.selectMedia('video');
63
         const videoMLine = sdpTransformer.selectMedia('video');
64
+
64
         if (!videoMLine) {
65
         if (!videoMLine) {
65
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
66
             logger.error(`No 'video' media found in the sdp: ${sdpStr}`);
67
+
66
             return sdpStr;
68
             return sdpStr;
67
         }
69
         }
68
         if (videoMLine.direction === 'inactive') {
70
         if (videoMLine.direction === 'inactive') {
69
             logger.info(
71
             logger.info(
70
                 'Sdp-consistency doing nothing, video mline is inactive');
72
                 'Sdp-consistency doing nothing, video mline is inactive');
73
+
71
             return sdpStr;
74
             return sdpStr;
72
         }
75
         }
73
         if (videoMLine.direction === 'recvonly') {
76
         if (videoMLine.direction === 'recvonly') {
84
             }
87
             }
85
         } else {
88
         } else {
86
             const newPrimarySsrc = videoMLine.getPrimaryVideoSsrc();
89
             const newPrimarySsrc = videoMLine.getPrimaryVideoSsrc();
90
+
87
             if (!newPrimarySsrc) {
91
             if (!newPrimarySsrc) {
88
                 logger.info('Sdp-consistency couldn\'t parse new primary ssrc');
92
                 logger.info('Sdp-consistency couldn\'t parse new primary ssrc');
93
+
89
                 return sdpStr;
94
                 return sdpStr;
90
             }
95
             }
91
             if (this.cachedPrimarySsrc) {
96
             if (this.cachedPrimarySsrc) {
97
                     if (group.semantics === 'FID') {
102
                     if (group.semantics === 'FID') {
98
                         const primarySsrc = parsePrimarySSRC(group);
103
                         const primarySsrc = parsePrimarySSRC(group);
99
                         const rtxSsrc = parseSecondarySSRC(group);
104
                         const rtxSsrc = parseSecondarySSRC(group);
105
+
100
                         if (primarySsrc === newPrimarySsrc) {
106
                         if (primarySsrc === newPrimarySsrc) {
101
                             group.ssrcs
107
                             group.ssrcs
102
                                 = `${this.cachedPrimarySsrc} ${rtxSsrc}`;
108
                                 = `${this.cachedPrimarySsrc} ${rtxSsrc}`;
110
                         this.cachedPrimarySsrc}`);
116
                         this.cachedPrimarySsrc}`);
111
             }
117
             }
112
         }
118
         }
119
+
113
         return sdpTransformer.toRawSDP();
120
         return sdpTransformer.toRawSDP();
114
     }
121
     }
115
 }
122
 }

+ 14
- 0
modules/xmpp/SdpTransformUtil.js View File

27
     if (!mLine.ssrcs) {
27
     if (!mLine.ssrcs) {
28
         return 0;
28
         return 0;
29
     }
29
     }
30
+
30
     return mLine.ssrcs
31
     return mLine.ssrcs
31
             .map(ssrcInfo => ssrcInfo.id)
32
             .map(ssrcInfo => ssrcInfo.id)
32
             .filter((ssrc, index, array) => array.indexOf(ssrc) === index)
33
             .filter((ssrc, index, array) => array.indexOf(ssrc) === index)
73
     selectMedia(mediaType) {
74
     selectMedia(mediaType) {
74
         const selectedMLine
75
         const selectedMLine
75
             = this.parsedSDP.media.find(mLine => mLine.type === mediaType);
76
             = this.parsedSDP.media.find(mLine => mLine.type === mediaType);
77
+
78
+
76
         return selectedMLine ? new MLineWrap(selectedMLine) : null;
79
         return selectedMLine ? new MLineWrap(selectedMLine) : null;
77
     }
80
     }
78
 
81
 
109
         if (!this.mLine.ssrcs) {
112
         if (!this.mLine.ssrcs) {
110
             this.mLine.ssrcs = [];
113
             this.mLine.ssrcs = [];
111
         }
114
         }
115
+
112
         return this.mLine.ssrcs;
116
         return this.mLine.ssrcs;
113
     }
117
     }
114
 
118
 
136
         if (!this.mLine.ssrcGroups) {
140
         if (!this.mLine.ssrcGroups) {
137
             this.mLine.ssrcGroups = [];
141
             this.mLine.ssrcGroups = [];
138
         }
142
         }
143
+
139
         return this.mLine.ssrcGroups;
144
         return this.mLine.ssrcGroups;
140
     }
145
     }
141
 
146
 
160
         const attribute = this._ssrcs.find(
165
         const attribute = this._ssrcs.find(
161
             ssrcObj => ssrcObj.id == ssrcNumber
166
             ssrcObj => ssrcObj.id == ssrcNumber
162
             && ssrcObj.attribute === attrName);
167
             && ssrcObj.attribute === attrName);
168
+
169
+
163
         return attribute && attribute.value;
170
         return attribute && attribute.value;
164
     }
171
     }
165
 
172
 
255
         }
262
         }
256
 
263
 
257
         const numSsrcs = _getSSRCCount(this.mLine);
264
         const numSsrcs = _getSSRCCount(this.mLine);
265
+
258
         if (numSsrcs === 1) {
266
         if (numSsrcs === 1) {
259
             // Not using _ssrcs on purpose here
267
             // Not using _ssrcs on purpose here
260
             return this.mLine.ssrcs[0].id;
268
             return this.mLine.ssrcs[0].id;
262
             // Look for a SIM or FID group
270
             // Look for a SIM or FID group
263
         if (this.mLine.ssrcGroups) {
271
         if (this.mLine.ssrcGroups) {
264
             const simGroup = this.findGroup('SIM');
272
             const simGroup = this.findGroup('SIM');
273
+
265
             if (simGroup) {
274
             if (simGroup) {
266
                 return parsePrimarySSRC(simGroup);
275
                 return parsePrimarySSRC(simGroup);
267
             }
276
             }
268
             const fidGroup = this.findGroup('FID');
277
             const fidGroup = this.findGroup('FID');
278
+
269
             if (fidGroup) {
279
             if (fidGroup) {
270
                 return parsePrimarySSRC(fidGroup);
280
                 return parsePrimarySSRC(fidGroup);
271
             }
281
             }
283
      */
293
      */
284
     getRtxSSRC(primarySsrc) {
294
     getRtxSSRC(primarySsrc) {
285
         const fidGroup = this.findGroupByPrimarySSRC('FID', primarySsrc);
295
         const fidGroup = this.findGroupByPrimarySSRC('FID', primarySsrc);
296
+
297
+
286
         return fidGroup && parseSecondarySSRC(fidGroup);
298
         return fidGroup && parseSecondarySSRC(fidGroup);
287
     }
299
     }
288
 
300
 
318
             if (ssrcGroupInfo.semantics === 'FID') {
330
             if (ssrcGroupInfo.semantics === 'FID') {
319
                 // secondary FID streams should be filtered out
331
                 // secondary FID streams should be filtered out
320
                 const secondarySsrc = parseSecondarySSRC(ssrcGroupInfo);
332
                 const secondarySsrc = parseSecondarySSRC(ssrcGroupInfo);
333
+
321
                 videoSSRCs.splice(
334
                 videoSSRCs.splice(
322
                     videoSSRCs.indexOf(secondarySsrc), 1);
335
                     videoSSRCs.indexOf(secondarySsrc), 1);
323
             }
336
             }
324
         }
337
         }
338
+
325
         return videoSSRCs;
339
         return videoSSRCs;
326
     }
340
     }
327
 
341
 

+ 37
- 3
modules/xmpp/moderator.js View File

5
 const AuthenticationEvents
5
 const AuthenticationEvents
6
     = require('../../service/authentication/AuthenticationEvents');
6
     = require('../../service/authentication/AuthenticationEvents');
7
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
7
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
8
+
8
 import Settings from '../settings/Settings';
9
 import Settings from '../settings/Settings';
9
 
10
 
10
 function createExpBackoffTimer(step) {
11
 function createExpBackoffTimer(step) {
11
     let count = 1;
12
     let count = 1;
13
+
14
+
12
     return function(reset) {
15
     return function(reset) {
13
         // Reset call
16
         // Reset call
14
         if (reset) {
17
         if (reset) {
15
             count = 1;
18
             count = 1;
19
+
16
             return;
20
             return;
17
         }
21
         }
18
         // Calculate next timeout
22
         // Calculate next timeout
19
         const timeout = Math.pow(2, count - 1);
23
         const timeout = Math.pow(2, count - 1);
24
+
20
         count += 1;
25
         count += 1;
26
+
21
         return timeout * step;
27
         return timeout * step;
22
     };
28
     };
23
 }
29
 }
49
                 logger.warn(
55
                 logger.warn(
50
                     `Ignoring sessionId from different origin: ${
56
                     `Ignoring sessionId from different origin: ${
51
                         event.origin}`);
57
                         event.origin}`);
58
+
52
                 return;
59
                 return;
53
             }
60
             }
54
             Settings.setSessionId(event.data.sessionId);
61
             Settings.setSessionId(event.data.sessionId);
74
 Moderator.prototype.onMucMemberLeft = function(jid) {
81
 Moderator.prototype.onMucMemberLeft = function(jid) {
75
     logger.info(`Someone left is it focus ? ${jid}`);
82
     logger.info(`Someone left is it focus ? ${jid}`);
76
     const resource = Strophe.getResourceFromJid(jid);
83
     const resource = Strophe.getResourceFromJid(jid);
84
+
77
     if (resource === 'focus') {
85
     if (resource === 'focus') {
78
         logger.info(
86
         logger.info(
79
             'Focus has left the room - leaving conference');
87
             'Focus has left the room - leaving conference');
96
     // Get focus component address
104
     // Get focus component address
97
     let focusComponent = this.options.connection.hosts.focus;
105
     let focusComponent = this.options.connection.hosts.focus;
98
     // If not specified use default:  'focus.domain'
106
     // If not specified use default:  'focus.domain'
107
+
99
     if (!focusComponent) {
108
     if (!focusComponent) {
100
         focusComponent = `focus.${this.options.connection.hosts.domain}`;
109
         focusComponent = `focus.${this.options.connection.hosts.domain}`;
101
     }
110
     }
111
+
102
     return focusComponent;
112
     return focusComponent;
103
 };
113
 };
104
 
114
 
211
             }).up();
221
             }).up();
212
     }
222
     }
213
     elem.up();
223
     elem.up();
224
+
214
     return elem;
225
     return elem;
215
 };
226
 };
216
 
227
 
217
 
228
 
218
 Moderator.prototype.parseSessionId = function(resultIq) {
229
 Moderator.prototype.parseSessionId = function(resultIq) {
230
+    // eslint-disable-next-line newline-per-chained-call
219
     const sessionId = $(resultIq).find('conference').attr('session-id');
231
     const sessionId = $(resultIq).find('conference').attr('session-id');
232
+
220
     if (sessionId) {
233
     if (sessionId) {
221
         logger.info(`Received sessionId:  ${sessionId}`);
234
         logger.info(`Received sessionId:  ${sessionId}`);
222
         Settings.setSessionId(sessionId);
235
         Settings.setSessionId(sessionId);
224
 };
237
 };
225
 
238
 
226
 Moderator.prototype.parseConfigOptions = function(resultIq) {
239
 Moderator.prototype.parseConfigOptions = function(resultIq) {
227
-
228
-    this.setFocusUserJid(
229
-        $(resultIq).find('conference').attr('focusjid'));
240
+    // eslint-disable-next-line newline-per-chained-call
241
+    this.setFocusUserJid($(resultIq).find('conference').attr('focusjid'));
230
 
242
 
231
     const authenticationEnabled
243
     const authenticationEnabled
232
         = $(resultIq).find(
244
         = $(resultIq).find(
247
         this.parseSessionId(resultIq);
259
         this.parseSessionId(resultIq);
248
     }
260
     }
249
 
261
 
262
+    // eslint-disable-next-line newline-per-chained-call
250
     const authIdentity = $(resultIq).find('>conference').attr('identity');
263
     const authIdentity = $(resultIq).find('>conference').attr('identity');
251
 
264
 
252
     this.eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,
265
     this.eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,
299
     // If the session is invalid, remove and try again without session ID to get
312
     // If the session is invalid, remove and try again without session ID to get
300
     // a new one
313
     // a new one
301
     const invalidSession = $(error).find('>error>session-invalid').length;
314
     const invalidSession = $(error).find('>error>session-invalid').length;
315
+
302
     if (invalidSession) {
316
     if (invalidSession) {
303
         logger.info('Session expired! - removing');
317
         logger.info('Session expired! - removing');
304
         Settings.clearSessionId();
318
         Settings.clearSessionId();
305
     }
319
     }
306
     if ($(error).find('>error>graceful-shutdown').length) {
320
     if ($(error).find('>error>graceful-shutdown').length) {
307
         this.eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
321
         this.eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
322
+
308
         return;
323
         return;
309
     }
324
     }
310
     // Check for error returned by the reservation system
325
     // Check for error returned by the reservation system
311
     const reservationErr = $(error).find('>error>reservation-error');
326
     const reservationErr = $(error).find('>error>reservation-error');
327
+
312
     if (reservationErr.length) {
328
     if (reservationErr.length) {
313
         // Trigger error event
329
         // Trigger error event
314
         const errorCode = reservationErr.attr('error-code');
330
         const errorCode = reservationErr.attr('error-code');
315
         const errorTextNode = $(error).find('>error>text');
331
         const errorTextNode = $(error).find('>error>text');
316
         let errorMsg;
332
         let errorMsg;
333
+
317
         if (errorTextNode) {
334
         if (errorTextNode) {
318
             errorMsg = errorTextNode.text();
335
             errorMsg = errorTextNode.text();
319
         }
336
         }
320
         this.eventEmitter.emit(
337
         this.eventEmitter.emit(
321
                 XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
338
                 XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
339
+
322
         return;
340
         return;
323
     }
341
     }
324
     // Not authorized to create new room
342
     // Not authorized to create new room
325
     if ($(error).find('>error>not-authorized').length) {
343
     if ($(error).find('>error>not-authorized').length) {
326
         logger.warn('Unauthorized to start the conference', error);
344
         logger.warn('Unauthorized to start the conference', error);
327
         const toDomain = Strophe.getDomainFromJid(error.getAttribute('to'));
345
         const toDomain = Strophe.getDomainFromJid(error.getAttribute('to'));
346
+
328
         if (toDomain !== this.options.connection.hosts.anonymousdomain) {
347
         if (toDomain !== this.options.connection.hosts.anonymousdomain) {
329
             // FIXME "is external" should come either from the focus or
348
             // FIXME "is external" should come either from the focus or
330
             // config.js
349
             // config.js
331
             this.externalAuthEnabled = true;
350
             this.externalAuthEnabled = true;
332
         }
351
         }
333
         this.eventEmitter.emit(XMPPEvents.AUTHENTICATION_REQUIRED);
352
         this.eventEmitter.emit(XMPPEvents.AUTHENTICATION_REQUIRED);
353
+
334
         return;
354
         return;
335
     }
355
     }
336
     const waitMs = this.getNextErrorTimeout();
356
     const waitMs = this.getNextErrorTimeout();
337
     const errmsg = `Focus error, retry after ${waitMs}`;
357
     const errmsg = `Focus error, retry after ${waitMs}`;
358
+
338
     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
359
     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
339
     logger.error(errmsg, error);
360
     logger.error(errmsg, error);
340
     // Show message
361
     // Show message
342
     const retrySec = waitMs / 1000;
363
     const retrySec = waitMs / 1000;
343
     // FIXME: message is duplicated ? Do not show in case of session invalid
364
     // FIXME: message is duplicated ? Do not show in case of session invalid
344
     // which means just a retry
365
     // which means just a retry
366
+
345
     if (!invalidSession) {
367
     if (!invalidSession) {
346
         this.eventEmitter.emit(
368
         this.eventEmitter.emit(
347
                 XMPPEvents.FOCUS_DISCONNECTED, focusComponent, retrySec);
369
                 XMPPEvents.FOCUS_DISCONNECTED, focusComponent, retrySec);
368
 
390
 
369
     // Reset the error timeout (because we haven't failed here).
391
     // Reset the error timeout (because we haven't failed here).
370
     this.getNextErrorTimeout(true);
392
     this.getNextErrorTimeout(true);
393
+    // eslint-disable-next-line newline-per-chained-call
371
     if ('true' === $(result).find('conference').attr('ready')) {
394
     if ('true' === $(result).find('conference').attr('ready')) {
372
         // Reset the non-error timeout (because we've succeeded here).
395
         // Reset the non-error timeout (because we've succeeded here).
373
         this.getNextTimeout(true);
396
         this.getNextTimeout(true);
375
         callback();
398
         callback();
376
     } else {
399
     } else {
377
         const waitMs = this.getNextTimeout();
400
         const waitMs = this.getNextTimeout();
401
+
378
         logger.info(`Waiting for the focus... ${waitMs}`);
402
         logger.info(`Waiting for the focus... ${waitMs}`);
379
         window.setTimeout(() => this.allocateConferenceFocus(callback),
403
         window.setTimeout(() => this.allocateConferenceFocus(callback),
380
             waitMs);
404
             waitMs);
389
                 this.parseSessionId(result);
413
                 this.parseSessionId(result);
390
                 resolve();
414
                 resolve();
391
             }, error => {
415
             }, error => {
416
+                // eslint-disable-next-line newline-per-chained-call
392
                 const code = $(error).find('>error').attr('code');
417
                 const code = $(error).find('>error').attr('code');
418
+
393
                 reject(error, code);
419
                 reject(error, code);
394
             }
420
             }
395
         );
421
         );
415
         'machine-uid': Settings.getMachineId()
441
         'machine-uid': Settings.getMachineId()
416
     };
442
     };
417
     let str = 'auth url'; // for logger
443
     let str = 'auth url'; // for logger
444
+
418
     if (popup) {
445
     if (popup) {
419
         attrs.popup = true;
446
         attrs.popup = true;
420
         str = `POPUP ${str}`;
447
         str = `POPUP ${str}`;
435
     this.connection.sendIQ(
462
     this.connection.sendIQ(
436
         iq,
463
         iq,
437
         result => {
464
         result => {
465
+            // eslint-disable-next-line newline-per-chained-call
438
             let url = $(result).find('login-url').attr('url');
466
             let url = $(result).find('login-url').attr('url');
467
+
439
             url = decodeURIComponent(url);
468
             url = decodeURIComponent(url);
440
             if (url) {
469
             if (url) {
441
                 logger.info(`Got ${str}: ${url}`);
470
                 logger.info(`Got ${str}: ${url}`);
455
 Moderator.prototype.logout = function(callback) {
484
 Moderator.prototype.logout = function(callback) {
456
     const iq = $iq({to: this.getFocusComponent(), type: 'set'});
485
     const iq = $iq({to: this.getFocusComponent(), type: 'set'});
457
     const sessionId = Settings.getSessionId();
486
     const sessionId = Settings.getSessionId();
487
+
458
     if (!sessionId) {
488
     if (!sessionId) {
459
         callback();
489
         callback();
490
+
460
         return;
491
         return;
461
     }
492
     }
462
     iq.c('logout', {
493
     iq.c('logout', {
466
     this.connection.sendIQ(
497
     this.connection.sendIQ(
467
         iq,
498
         iq,
468
         result => {
499
         result => {
500
+            // eslint-disable-next-line newline-per-chained-call
469
             let logoutUrl = $(result).find('logout').attr('logout-url');
501
             let logoutUrl = $(result).find('logout').attr('logout-url');
502
+
470
             if (logoutUrl) {
503
             if (logoutUrl) {
471
                 logoutUrl = decodeURIComponent(logoutUrl);
504
                 logoutUrl = decodeURIComponent(logoutUrl);
472
             }
505
             }
476
         },
509
         },
477
         error => {
510
         error => {
478
             const errmsg = 'Logout error';
511
             const errmsg = 'Logout error';
512
+
479
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
513
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
480
             logger.error(errmsg, error);
514
             logger.error(errmsg, error);
481
         }
515
         }

+ 26
- 10
modules/xmpp/recording.js View File

54
 
54
 
55
 Recording.prototype.handleJibriPresence = function(jibri) {
55
 Recording.prototype.handleJibriPresence = function(jibri) {
56
     const attributes = jibri.attributes;
56
     const attributes = jibri.attributes;
57
+
57
     if(!attributes) {
58
     if(!attributes) {
58
         return;
59
         return;
59
     }
60
     }
60
 
61
 
61
     const newState = attributes.status;
62
     const newState = attributes.status;
63
+
62
     logger.log('Handle jibri presence : ', newState);
64
     logger.log('Handle jibri presence : ', newState);
63
 
65
 
64
     if (newState === this.state) {
66
     if (newState === this.state) {
91
         options = options || {};
93
         options = options || {};
92
 
94
 
93
     // FIXME jibri does not accept IQ without 'url' attribute set ?
95
     // FIXME jibri does not accept IQ without 'url' attribute set ?
94
-        const iq = $iq({to: this.focusMucJid, type: 'set'})
95
-        .c('jibri', {
96
-            'xmlns': 'http://jitsi.org/protocol/jibri',
97
-            'action': state === Recording.status.ON
98
-                    ? Recording.action.START
99
-                    : Recording.action.STOP,
100
-            'streamid': options.streamId,
101
-        }).up();
96
+        const iq
97
+            = $iq({to: this.focusMucJid, type: 'set'})
98
+                .c('jibri', {
99
+                    'xmlns': 'http://jitsi.org/protocol/jibri',
100
+                    'action': state === Recording.status.ON
101
+                            ? Recording.action.START
102
+                            : Recording.action.STOP,
103
+                    'streamid': options.streamId,
104
+                })
105
+                .up();
102
 
106
 
103
         logger.log(`Set jibri recording: ${state}`, iq.nodeTree);
107
         logger.log(`Set jibri recording: ${state}`, iq.nodeTree);
104
         logger.log(iq.nodeTree);
108
         logger.log(iq.nodeTree);
106
         iq,
110
         iq,
107
         result => {
111
         result => {
108
             logger.log('Result', result);
112
             logger.log('Result', result);
109
-            callback($(result).find('jibri').attr('state'),
110
-            $(result).find('jibri').attr('url'));
113
+
114
+            const jibri = $(result).find('jibri');
115
+
116
+            callback(jibri.attr('state'), jibri.attr('url'));
111
         },
117
         },
112
         error => {
118
         error => {
113
             logger.log('Failed to start recording, error: ', error);
119
             logger.log('Failed to start recording, error: ', error);
128
                 ? Recording.action.START
134
                 ? Recording.action.START
129
                 : Recording.action.STOP,
135
                 : Recording.action.STOP,
130
             mucjid: this.roomjid});
136
             mucjid: this.roomjid});
137
+
131
         if (state === 'off') {
138
         if (state === 'off') {
132
             iq.attrs({rid: this.jireconRid});
139
             iq.attrs({rid: this.jireconRid});
133
         }
140
         }
134
 
141
 
135
         logger.log('Start recording');
142
         logger.log('Start recording');
136
         const self = this;
143
         const self = this;
144
+
137
         this.connection.sendIQ(
145
         this.connection.sendIQ(
138
         iq,
146
         iq,
139
         result => {
147
         result => {
140
             // TODO wait for an IQ with the real status, since this is
148
             // TODO wait for an IQ with the real status, since this is
141
             // provisional?
149
             // provisional?
150
+            // eslint-disable-next-line newline-per-chained-call
142
             self.jireconRid = $(result).find('recording').attr('rid');
151
             self.jireconRid = $(result).find('recording').attr('rid');
143
             logger.log(
152
             logger.log(
144
                 `Recording ${
153
                 `Recording ${
163
 Recording.prototype.setRecordingColibri
172
 Recording.prototype.setRecordingColibri
164
 = function(state, callback, errCallback, options) {
173
 = function(state, callback, errCallback, options) {
165
     const elem = $iq({to: this.focusMucJid, type: 'set'});
174
     const elem = $iq({to: this.focusMucJid, type: 'set'});
175
+
166
     elem.c('conference', {
176
     elem.c('conference', {
167
         xmlns: 'http://jitsi.org/protocol/colibri'
177
         xmlns: 'http://jitsi.org/protocol/colibri'
168
     });
178
     });
169
     elem.c('recording', {state, token: options.token});
179
     elem.c('recording', {state, token: options.token});
170
 
180
 
171
     const self = this;
181
     const self = this;
182
+
172
     this.connection.sendIQ(elem,
183
     this.connection.sendIQ(elem,
173
         result => {
184
         result => {
174
             logger.log('Set recording "', state, '". Result:', result);
185
             logger.log('Set recording "', state, '". Result:', result);
180
 
191
 
181
             if (newState === 'pending') {
192
             if (newState === 'pending') {
182
                 self.connection.addHandler(iq => {
193
                 self.connection.addHandler(iq => {
194
+                    // eslint-disable-next-line newline-per-chained-call
183
                     const state = $(iq).find('recording').attr('state');
195
                     const state = $(iq).find('recording').attr('state');
196
+
184
                     if (state) {
197
                     if (state) {
185
                         self.state = newState;
198
                         self.state = newState;
186
                         callback(state);
199
                         callback(state);
209
         break;
222
         break;
210
     default: {
223
     default: {
211
         const errmsg = 'Unknown recording type!';
224
         const errmsg = 'Unknown recording type!';
225
+
212
         GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
226
         GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
213
         logger.error(errmsg);
227
         logger.error(errmsg);
214
         break;
228
         break;
244
         statusChangeHandler(Recording.status.FAILED,
258
         statusChangeHandler(Recording.status.FAILED,
245
                             JitsiRecorderErrors.NO_TOKEN);
259
                             JitsiRecorderErrors.NO_TOKEN);
246
         logger.error('No token passed!');
260
         logger.error('No token passed!');
261
+
247
         return;
262
         return;
248
     }
263
     }
249
 
264
 
253
                     : Recording.status.OFF;
268
                     : Recording.status.OFF;
254
 
269
 
255
     const self = this;
270
     const self = this;
271
+
256
     logger.log('Toggle recording (old state, new state): ', oldState, newState);
272
     logger.log('Toggle recording (old state, new state): ', oldState, newState);
257
     this.setRecording(
273
     this.setRecording(
258
         newState,
274
         newState,

+ 13
- 0
modules/xmpp/strophe.emuc.js View File

6
 
6
 
7
 import {getLogger} from 'jitsi-meet-logger';
7
 import {getLogger} from 'jitsi-meet-logger';
8
 const logger = getLogger(__filename);
8
 const logger = getLogger(__filename);
9
+
9
 import ChatRoom from './ChatRoom';
10
 import ChatRoom from './ChatRoom';
10
 import {ConnectionPluginListenable} from './ConnectionPlugin';
11
 import {ConnectionPluginListenable} from './ConnectionPlugin';
11
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
12
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
34
 
35
 
35
     createRoom(jid, password, options) {
36
     createRoom(jid, password, options) {
36
         const roomJid = Strophe.getBareJidFromJid(jid);
37
         const roomJid = Strophe.getBareJidFromJid(jid);
38
+
37
         if (this.rooms[roomJid]) {
39
         if (this.rooms[roomJid]) {
38
             const errmsg = 'You are already in the room!';
40
             const errmsg = 'You are already in the room!';
41
+
39
             logger.error(errmsg);
42
             logger.error(errmsg);
40
             throw new Error(errmsg);
43
             throw new Error(errmsg);
41
         }
44
         }
43
             password, this.xmpp, options);
46
             password, this.xmpp, options);
44
         this.eventEmitter.emit(
47
         this.eventEmitter.emit(
45
             XMPPEvents.EMUC_ROOM_ADDED, this.rooms[roomJid]);
48
             XMPPEvents.EMUC_ROOM_ADDED, this.rooms[roomJid]);
49
+
46
         return this.rooms[roomJid];
50
         return this.rooms[roomJid];
47
     }
51
     }
48
 
52
 
61
         }
65
         }
62
 
66
 
63
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
67
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
68
+
64
         if(!room) {
69
         if(!room) {
65
             return;
70
             return;
66
         }
71
         }
79
     onPresenceUnavailable(pres) {
84
     onPresenceUnavailable(pres) {
80
         const from = pres.getAttribute('from');
85
         const from = pres.getAttribute('from');
81
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
86
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
87
+
82
         if(!room) {
88
         if(!room) {
83
             return;
89
             return;
84
         }
90
         }
85
 
91
 
86
         room.onPresenceUnavailable(pres, from);
92
         room.onPresenceUnavailable(pres, from);
93
+
87
         return true;
94
         return true;
88
     }
95
     }
89
 
96
 
90
     onPresenceError(pres) {
97
     onPresenceError(pres) {
91
         const from = pres.getAttribute('from');
98
         const from = pres.getAttribute('from');
92
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
99
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
100
+
93
         if(!room) {
101
         if(!room) {
94
             return;
102
             return;
95
         }
103
         }
96
 
104
 
97
         room.onPresenceError(pres, from);
105
         room.onPresenceError(pres, from);
106
+
98
         return true;
107
         return true;
99
     }
108
     }
100
 
109
 
102
         // FIXME: this is a hack. but jingle on muc makes nickchanges hard
111
         // FIXME: this is a hack. but jingle on muc makes nickchanges hard
103
         const from = msg.getAttribute('from');
112
         const from = msg.getAttribute('from');
104
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
113
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
114
+
105
         if(!room) {
115
         if(!room) {
106
             return;
116
             return;
107
         }
117
         }
108
 
118
 
109
         room.onMessage(msg, from);
119
         room.onMessage(msg, from);
120
+
110
         return true;
121
         return true;
111
     }
122
     }
112
 
123
 
113
     onMute(iq) {
124
     onMute(iq) {
114
         const from = iq.getAttribute('from');
125
         const from = iq.getAttribute('from');
115
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
126
         const room = this.rooms[Strophe.getBareJidFromJid(from)];
127
+
116
         if(!room) {
128
         if(!room) {
117
             return;
129
             return;
118
         }
130
         }
119
 
131
 
120
         room.onMute(iq);
132
         room.onMute(iq);
133
+
121
         return true;
134
         return true;
122
     }
135
     }
123
 }
136
 }

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

2
 
2
 
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
+
5
 import JingleSessionPC from './JingleSessionPC';
6
 import JingleSessionPC from './JingleSessionPC';
6
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
7
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
7
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 import Statistics from '../statistics/statistics';
9
 import Statistics from '../statistics/statistics';
9
 import ConnectionPlugin from './ConnectionPlugin';
10
 import ConnectionPlugin from './ConnectionPlugin';
10
 
11
 
12
+// XXX Strophe is build around the idea of chaining function calls so allow long
13
+// function call chains.
14
+/* eslint-disable newline-per-chained-call */
15
+
11
 class JingleConnectionPlugin extends ConnectionPlugin {
16
 class JingleConnectionPlugin extends ConnectionPlugin {
12
     constructor(xmpp, eventEmitter) {
17
     constructor(xmpp, eventEmitter) {
13
         super();
18
         super();
39
             to: fromJid,
44
             to: fromJid,
40
             id: iq.getAttribute('id')
45
             id: iq.getAttribute('id')
41
         });
46
         });
47
+
42
         logger.log(`on jingle ${action} from ${fromJid}`, iq);
48
         logger.log(`on jingle ${action} from ${fromJid}`, iq);
43
         let sess = this.sessions[sid];
49
         let sess = this.sessions[sid];
50
+
44
         if ('session-initiate' != action) {
51
         if ('session-initiate' != action) {
45
             if (!sess) {
52
             if (!sess) {
46
                 ack.attrs({ type: 'error' });
53
                 ack.attrs({ type: 'error' });
50
                     .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
57
                     .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
51
                 logger.warn('invalid session id', iq);
58
                 logger.warn('invalid session id', iq);
52
                 this.connection.send(ack);
59
                 this.connection.send(ack);
60
+
53
                 return true;
61
                 return true;
54
             }
62
             }
55
             // local jid is not checked
63
             // local jid is not checked
61
                     .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
69
                     .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
62
                     .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
70
                     .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
63
                 this.connection.send(ack);
71
                 this.connection.send(ack);
72
+
64
                 return true;
73
                 return true;
65
             }
74
             }
66
         } else if (sess !== undefined) {
75
         } else if (sess !== undefined) {
71
                 .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
80
                 .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
72
             logger.warn('duplicate session id', sid, iq);
81
             logger.warn('duplicate session id', sid, iq);
73
             this.connection.send(ack);
82
             this.connection.send(ack);
83
+
74
             return true;
84
             return true;
75
         }
85
         }
76
         const now = window.performance.now();
86
         const now = window.performance.now();
77
         // see http://xmpp.org/extensions/xep-0166.html#concepts-session
87
         // see http://xmpp.org/extensions/xep-0166.html#concepts-session
88
+
78
         switch (action) {
89
         switch (action) {
79
         case 'session-initiate': {
90
         case 'session-initiate': {
80
             logger.log('(TIME) received session-initiate:\t', now);
91
             logger.log('(TIME) received session-initiate:\t', now);
81
             const startMuted = $(iq).find('jingle>startmuted');
92
             const startMuted = $(iq).find('jingle>startmuted');
93
+
82
             if (startMuted && startMuted.length > 0) {
94
             if (startMuted && startMuted.length > 0) {
83
                 const audioMuted = startMuted.attr('audio');
95
                 const audioMuted = startMuted.attr('audio');
84
                 const videoMuted = startMuted.attr('video');
96
                 const videoMuted = startMuted.attr('video');
97
+
85
                 this.eventEmitter.emit(XMPPEvents.START_MUTED_FROM_FOCUS,
98
                 this.eventEmitter.emit(XMPPEvents.START_MUTED_FROM_FOCUS,
86
                             audioMuted === 'true', videoMuted === 'true');
99
                             audioMuted === 'true', videoMuted === 'true');
87
             }
100
             }
105
             logger.log('terminating...', sess.sid);
118
             logger.log('terminating...', sess.sid);
106
             let reasonCondition = null;
119
             let reasonCondition = null;
107
             let reasonText = null;
120
             let reasonText = null;
121
+
108
             if ($(iq).find('>jingle>reason').length) {
122
             if ($(iq).find('>jingle>reason').length) {
109
                 reasonCondition
123
                 reasonCondition
110
-                        = $(iq).find('>jingle>reason>:first')[0].tagName;
124
+                    = $(iq).find('>jingle>reason>:first')[0].tagName;
111
                 reasonText = $(iq).find('>jingle>reason>text').text();
125
                 reasonText = $(iq).find('>jingle>reason>text').text();
112
             }
126
             }
113
             this.terminate(sess.sid, reasonCondition, reasonText);
127
             this.terminate(sess.sid, reasonCondition, reasonText);
122
 
136
 
123
             sess.replaceTransport($(iq).find('>jingle'), () => {
137
             sess.replaceTransport($(iq).find('>jingle'), () => {
124
                 const successTime = window.performance.now();
138
                 const successTime = window.performance.now();
139
+
125
                 logger.info(
140
                 logger.info(
126
                         '(TIME) Transport replace success!', successTime);
141
                         '(TIME) Transport replace success!', successTime);
127
                 Statistics.analytics.sendEvent(
142
                 Statistics.analytics.sendEvent(
151
             break;
166
             break;
152
         }
167
         }
153
         this.connection.send(ack);
168
         this.connection.send(ack);
169
+
154
         return true;
170
         return true;
155
     }
171
     }
156
 
172
 
181
                 .c('service', {host: `turn.${this.connection.domain}`}),
197
                 .c('service', {host: `turn.${this.connection.domain}`}),
182
             res => {
198
             res => {
183
                 const iceservers = [];
199
                 const iceservers = [];
200
+
184
                 $(res).find('>services>service').each((idx, el) => {
201
                 $(res).find('>services>service').each((idx, el) => {
185
                     el = $(el);
202
                     el = $(el);
186
                     const dict = {};
203
                     const dict = {};
187
                     const type = el.attr('type');
204
                     const type = el.attr('type');
205
+
188
                     switch (type) {
206
                     switch (type) {
189
                     case 'stun':
207
                     case 'stun':
190
                         dict.url = `stun:${el.attr('host')}`;
208
                         dict.url = `stun:${el.attr('host')}`;
198
                         dict.url = `${type}:`;
216
                         dict.url = `${type}:`;
199
                         const username = el.attr('username');
217
                         const username = el.attr('username');
200
                             // https://code.google.com/p/webrtc/issues/detail?id=1508
218
                             // https://code.google.com/p/webrtc/issues/detail?id=1508
219
+
201
                         if (username) {
220
                         if (username) {
202
                             if (navigator.userAgent.match(
221
                             if (navigator.userAgent.match(
203
                                     /Chrom(e|ium)\/([0-9]+)\./)
222
                                     /Chrom(e|ium)\/([0-9]+)\./)
213
                         }
232
                         }
214
                         dict.url += el.attr('host');
233
                         dict.url += el.attr('host');
215
                         const port = el.attr('port');
234
                         const port = el.attr('port');
235
+
216
                         if (port && port != '3478') {
236
                         if (port && port != '3478') {
217
                             dict.url += `:${el.attr('port')}`;
237
                             dict.url += `:${el.attr('port')}`;
218
                         }
238
                         }
219
                         const transport = el.attr('transport');
239
                         const transport = el.attr('transport');
240
+
220
                         if (transport && transport != 'udp') {
241
                         if (transport && transport != 'udp') {
221
                             dict.url += `?transport=${transport}`;
242
                             dict.url += `?transport=${transport}`;
222
                         }
243
                         }
241
      */
262
      */
242
     getLog() {
263
     getLog() {
243
         const data = {};
264
         const data = {};
265
+
244
         Object.keys(this.sessions).forEach(sid => {
266
         Object.keys(this.sessions).forEach(sid => {
245
             const session = this.sessions[sid];
267
             const session = this.sessions[sid];
246
             const pc = session.peerconnection;
268
             const pc = session.peerconnection;
269
+
247
             if (pc && pc.updateLog) {
270
             if (pc && pc.updateLog) {
248
                 // FIXME: should probably be a .dump call
271
                 // FIXME: should probably be a .dump call
249
                 data[`jingle_${sid}`] = {
272
                 data[`jingle_${sid}`] = {
253
                 };
276
                 };
254
             }
277
             }
255
         });
278
         });
279
+
256
         return data;
280
         return data;
257
     }
281
     }
258
 }
282
 }
259
 
283
 
284
+/* eslint-enable newline-per-chained-call */
260
 
285
 
261
 module.exports = function(XMPP, eventEmitter) {
286
 module.exports = function(XMPP, eventEmitter) {
262
-    Strophe.addConnectionPlugin('jingle',
287
+    Strophe.addConnectionPlugin(
288
+        'jingle',
263
         new JingleConnectionPlugin(XMPP, eventEmitter));
289
         new JingleConnectionPlugin(XMPP, eventEmitter));
264
 };
290
 };

+ 6
- 0
modules/xmpp/strophe.ping.js View File

2
 
2
 
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
+
5
 import ConnectionPlugin from './ConnectionPlugin';
6
 import ConnectionPlugin from './ConnectionPlugin';
6
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
7
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
7
 
8
 
57
      */
58
      */
58
     ping(jid, success, error, timeout) {
59
     ping(jid, success, error, timeout) {
59
         const iq = $iq({type: 'get', to: jid});
60
         const iq = $iq({type: 'get', to: jid});
61
+
60
         iq.c('ping', {xmlns: Strophe.NS.PING});
62
         iq.c('ping', {xmlns: Strophe.NS.PING});
61
         this.connection.sendIQ(iq, success, error, timeout);
63
         this.connection.sendIQ(iq, success, error, timeout);
62
     }
64
     }
71
         this.xmpp.caps.getFeatures(jid).then(features =>
73
         this.xmpp.caps.getFeatures(jid).then(features =>
72
             callback(features.has('urn:xmpp:ping')), error => {
74
             callback(features.has('urn:xmpp:ping')), error => {
73
             const errmsg = 'Ping feature discovery error';
75
             const errmsg = 'Ping feature discovery error';
76
+
74
             GlobalOnErrorHandler.callErrorHandler(
77
             GlobalOnErrorHandler.callErrorHandler(
75
                 new Error(`${errmsg}: ${error}`));
78
                 new Error(`${errmsg}: ${error}`));
76
             logger.error(errmsg, error);
79
             logger.error(errmsg, error);
88
     startInterval(remoteJid, interval = PING_INTERVAL) {
91
     startInterval(remoteJid, interval = PING_INTERVAL) {
89
         if (this.intervalId) {
92
         if (this.intervalId) {
90
             const errmsg = 'Ping task scheduled already';
93
             const errmsg = 'Ping task scheduled already';
94
+
91
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
95
             GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
92
             logger.error(errmsg);
96
             logger.error(errmsg);
97
+
93
             return;
98
             return;
94
         }
99
         }
95
         this.intervalId = window.setInterval(() => {
100
         this.intervalId = window.setInterval(() => {
98
             }, error => {
103
             }, error => {
99
                 this.failedPings += 1;
104
                 this.failedPings += 1;
100
                 const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
105
                 const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
106
+
101
                 if (this.failedPings >= PING_THRESHOLD) {
107
                 if (this.failedPings >= PING_THRESHOLD) {
102
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
108
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
103
                     logger.error(errmsg, error);
109
                     logger.error(errmsg, error);

+ 8
- 2
modules/xmpp/strophe.rayo.js View File

2
 
2
 
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
+
5
 import ConnectionPlugin from './ConnectionPlugin';
6
 import ConnectionPlugin from './ConnectionPlugin';
6
 
7
 
7
 const RAYO_XMLNS = 'urn:xmpp:rayo:1';
8
 const RAYO_XMLNS = 'urn:xmpp:rayo:1';
22
         return new Promise((resolve, reject) => {
23
         return new Promise((resolve, reject) => {
23
             if(!focusMucJid) {
24
             if(!focusMucJid) {
24
                 reject(new Error('Internal error!'));
25
                 reject(new Error('Internal error!'));
26
+
25
                 return;
27
                 return;
26
             }
28
             }
27
             const req = $iq({
29
             const req = $iq({
28
                 type: 'set',
30
                 type: 'set',
29
                 to: focusMucJid
31
                 to: focusMucJid
30
             });
32
             });
33
+
31
             req.c('dial', {
34
             req.c('dial', {
32
                 xmlns: RAYO_XMLNS,
35
                 xmlns: RAYO_XMLNS,
33
                 to,
36
                 to,
48
             this.connection.sendIQ(req, result => {
51
             this.connection.sendIQ(req, result => {
49
                 logger.info('Dial result ', result);
52
                 logger.info('Dial result ', result);
50
 
53
 
54
+                // eslint-disable-next-line newline-per-chained-call
51
                 const resource = $(result).find('ref').attr('uri');
55
                 const resource = $(result).find('ref').attr('uri');
52
-                this.call_resource
53
-                    = resource.substr('xmpp:'.length);
56
+
57
+                this.call_resource = resource.substr('xmpp:'.length);
54
                 logger.info(`Received call resource: ${this.call_resource}`);
58
                 logger.info(`Received call resource: ${this.call_resource}`);
55
                 resolve();
59
                 resolve();
56
             }, error => {
60
             }, error => {
65
             if (!this.call_resource) {
69
             if (!this.call_resource) {
66
                 reject(new Error('No call in progress'));
70
                 reject(new Error('No call in progress'));
67
                 logger.warn('No call in progress');
71
                 logger.warn('No call in progress');
72
+
68
                 return;
73
                 return;
69
             }
74
             }
70
 
75
 
72
                 type: 'set',
77
                 type: 'set',
73
                 to: this.call_resource
78
                 to: this.call_resource
74
             });
79
             });
80
+
75
             req.c('hangup', {
81
             req.c('hangup', {
76
                 xmlns: RAYO_XMLNS
82
                 xmlns: RAYO_XMLNS
77
             });
83
             });

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

4
  */
4
  */
5
 import {getLogger} from 'jitsi-meet-logger';
5
 import {getLogger} from 'jitsi-meet-logger';
6
 const logger = getLogger(__filename);
6
 const logger = getLogger(__filename);
7
+
7
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 
9
 
9
 /**
10
 /**
68
         case Strophe.LogLevel.WARN:
69
         case Strophe.LogLevel.WARN:
69
             logger.warn(`Strophe: ${msg}`);
70
             logger.warn(`Strophe: ${msg}`);
70
             const errStatusCapture = lastErrorStatusRegExpr.exec(msg);
71
             const errStatusCapture = lastErrorStatusRegExpr.exec(msg);
72
+
71
             if (errStatusCapture && errStatusCapture.length === 2) {
73
             if (errStatusCapture && errStatusCapture.length === 2) {
72
                 lastErrorStatus = parseInt(errStatusCapture[1]);
74
                 lastErrorStatus = parseInt(errStatusCapture[1]);
73
                 logger.debug(
75
                 logger.debug(

+ 11
- 0
modules/xmpp/xmpp.js View File

2
 
2
 
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
 const logger = getLogger(__filename);
4
 const logger = getLogger(__filename);
5
+
5
 import RandomUtil from '../util/RandomUtil';
6
 import RandomUtil from '../util/RandomUtil';
6
 import * as JitsiConnectionErrors from '../../JitsiConnectionErrors';
7
 import * as JitsiConnectionErrors from '../../JitsiConnectionErrors';
7
 import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
8
 import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
105
     connectionHandler(password, status, msg) {
106
     connectionHandler(password, status, msg) {
106
         const now = window.performance.now();
107
         const now = window.performance.now();
107
         const statusStr = Strophe.getStatusString(status).toLowerCase();
108
         const statusStr = Strophe.getStatusString(status).toLowerCase();
109
+
108
         this.connectionTimes[statusStr] = now;
110
         this.connectionTimes[statusStr] = now;
109
         logger.log(
111
         logger.log(
110
             `(TIME) Strophe ${statusStr}${msg ? `[${msg}]` : ''}:\t`,
112
             `(TIME) Strophe ${statusStr}${msg ? `[${msg}]` : ''}:\t`,
119
 
121
 
120
             // Schedule ping ?
122
             // Schedule ping ?
121
             const pingJid = this.connection.domain;
123
             const pingJid = this.connection.domain;
124
+
122
             this.connection.ping.hasPingSupport(
125
             this.connection.ping.hasPingSupport(
123
                 pingJid,
126
                 pingJid,
124
                 hasPing => {
127
                 hasPing => {
152
             this.connection.ping.stopInterval();
155
             this.connection.ping.stopInterval();
153
             const wasIntentionalDisconnect = this.disconnectInProgress;
156
             const wasIntentionalDisconnect = this.disconnectInProgress;
154
             const errMsg = msg ? msg : this.lastErrorMsg;
157
             const errMsg = msg ? msg : this.lastErrorMsg;
158
+
155
             this.disconnectInProgress = false;
159
             this.disconnectInProgress = false;
156
             if (this.anonymousConnectionFailed) {
160
             if (this.anonymousConnectionFailed) {
157
                 // prompt user for username and password
161
                 // prompt user for username and password
175
                 // XXX if the last request error is within 5xx range it means it
179
                 // XXX if the last request error is within 5xx range it means it
176
                 // was a server failure
180
                 // was a server failure
177
                 const lastErrorStatus = Strophe.getLastErrorStatus();
181
                 const lastErrorStatus = Strophe.getLastErrorStatus();
182
+
178
                 if (lastErrorStatus >= 500 && lastErrorStatus < 600) {
183
                 if (lastErrorStatus >= 500 && lastErrorStatus < 600) {
179
                     this.eventEmitter.emit(
184
                     this.eventEmitter.emit(
180
                         JitsiConnectionEvents.CONNECTION_FAILED,
185
                         JitsiConnectionEvents.CONNECTION_FAILED,
239
      */
244
      */
240
     attach(options) {
245
     attach(options) {
241
         const now = this.connectionTimes.attaching = window.performance.now();
246
         const now = this.connectionTimes.attaching = window.performance.now();
247
+
242
         logger.log(`(TIME) Strophe Attaching\t:${now}`);
248
         logger.log(`(TIME) Strophe Attaching\t:${now}`);
243
         this.connection.attach(options.jid, options.sid,
249
         this.connection.attach(options.jid, options.sid,
244
             parseInt(options.rid,10) + 1,
250
             parseInt(options.rid,10) + 1,
256
                     || this.options.hosts.domain;
262
                     || this.options.hosts.domain;
257
             // Force authenticated domain if room is appended with '?login=true'
263
             // Force authenticated domain if room is appended with '?login=true'
258
             // or if we're joining with the token
264
             // or if we're joining with the token
265
+
259
             if (this.options.hosts.anonymousdomain
266
             if (this.options.hosts.anonymousdomain
260
                     && (window.location.search.indexOf('login=true') !== -1
267
                     && (window.location.search.indexOf('login=true') !== -1
261
                         || this.options.token)) {
268
                         || this.options.token)) {
263
             }
270
             }
264
             jid = configDomain || window.location.hostname;
271
             jid = configDomain || window.location.hostname;
265
         }
272
         }
273
+
266
         return this._connect(jid, password);
274
         return this._connect(jid, password);
267
     }
275
     }
268
 
276
 
297
      */
305
      */
298
     getJingleLog() {
306
     getJingleLog() {
299
         const jingle = this.connection.jingle;
307
         const jingle = this.connection.jingle;
308
+
309
+
300
         return jingle ? jingle.getLog() : {};
310
         return jingle ? jingle.getLog() : {};
301
     }
311
     }
302
 
312
 
334
                 || !this.connection
344
                 || !this.connection
335
                 || !this.connection.connected) {
345
                 || !this.connection.connected) {
336
             this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);
346
             this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);
347
+
337
             return;
348
             return;
338
         }
349
         }
339
 
350
 

+ 1
- 0
service/RTC/Resolutions.js View File

52
         order: 1
52
         order: 1
53
     }
53
     }
54
 };
54
 };
55
+
55
 module.exports = Resolutions;
56
 module.exports = Resolutions;

+ 1
- 0
service/RTC/VideoType.js View File

13
      */
13
      */
14
     DESKTOP: 'desktop'
14
     DESKTOP: 'desktop'
15
 };
15
 };
16
+
16
 module.exports = VideoType;
17
 module.exports = VideoType;

+ 1
- 0
service/authentication/AuthenticationEvents.js View File

9
      */
9
      */
10
     IDENTITY_UPDATED: 'authentication.identity_updated'
10
     IDENTITY_UPDATED: 'authentication.identity_updated'
11
 };
11
 };
12
+
12
 module.exports = AuthenticationEvents;
13
 module.exports = AuthenticationEvents;

+ 1
- 0
service/statistics/constants.js View File

1
 const Constants = {
1
 const Constants = {
2
     LOCAL_JID: 'local'
2
     LOCAL_JID: 'local'
3
 };
3
 };
4
+
4
 module.exports = Constants;
5
 module.exports = Constants;

+ 1
- 0
service/xmpp/XMPPEvents.js View File

156
     // changed.
156
     // changed.
157
     ICE_CONNECTION_STATE_CHANGED: 'xmpp.ice_connection_state_changed'
157
     ICE_CONNECTION_STATE_CHANGED: 'xmpp.ice_connection_state_changed'
158
 };
158
 };
159
+
159
 module.exports = XMPPEvents;
160
 module.exports = XMPPEvents;

Loading…
Cancel
Save