|
|
@@ -17,6 +17,7 @@ var JitsiTrackError = require("./JitsiTrackError");
|
|
17
|
17
|
var Settings = require("./modules/settings/Settings");
|
|
18
|
18
|
var ComponentsVersions = require("./modules/version/ComponentsVersions");
|
|
19
|
19
|
var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
|
|
|
20
|
+var MediaType = require("./service/RTC/MediaType");
|
|
20
|
21
|
|
|
21
|
22
|
/**
|
|
22
|
23
|
* Creates a JitsiConference object with the given name and properties.
|
|
|
@@ -35,17 +36,10 @@ function JitsiConference(options) {
|
|
35
|
36
|
logger.error(errmsg);
|
|
36
|
37
|
throw new Error(errmsg);
|
|
37
|
38
|
}
|
|
38
|
|
- this.options = options;
|
|
39
|
|
- this.connection = this.options.connection;
|
|
40
|
|
- this.xmpp = this.connection.xmpp;
|
|
41
|
39
|
this.eventEmitter = new EventEmitter();
|
|
42
|
|
- var confID = this.options.name + '@' + this.xmpp.options.hosts.muc;
|
|
43
|
40
|
this.settings = new Settings();
|
|
44
|
|
- this.room = this.xmpp.createRoom(this.options.name, this.options.config,
|
|
45
|
|
- this.settings);
|
|
46
|
|
- this.componentsVersions = new ComponentsVersions(this.room);
|
|
47
|
|
- this.room.updateDeviceAvailability(RTC.getDeviceAvailability());
|
|
48
|
|
- this.rtc = new RTC(this.room, options);
|
|
|
41
|
+ this._init(options);
|
|
|
42
|
+ this.rtc = new RTC(this, options);
|
|
49
|
43
|
this.statistics = new Statistics(this.xmpp, {
|
|
50
|
44
|
callStatsID: this.options.config.callStatsID,
|
|
51
|
45
|
callStatsSecret: this.options.config.callStatsSecret,
|
|
|
@@ -70,6 +64,45 @@ function JitsiConference(options) {
|
|
70
|
64
|
this.isMutedByFocus = false;
|
|
71
|
65
|
}
|
|
72
|
66
|
|
|
|
67
|
+/**
|
|
|
68
|
+ * Initializes the conference object properties
|
|
|
69
|
+ * @param options overrides this.options
|
|
|
70
|
+ */
|
|
|
71
|
+JitsiConference.prototype._init = function (options) {
|
|
|
72
|
+ if(!options)
|
|
|
73
|
+ options = {};
|
|
|
74
|
+ if(!this.options) {
|
|
|
75
|
+ this.options = options;
|
|
|
76
|
+ } else {
|
|
|
77
|
+ // Override config options
|
|
|
78
|
+ var config = options.config || {};
|
|
|
79
|
+ for(var key in config)
|
|
|
80
|
+ this.options.config[key] = config[key] || this.options.config[key];
|
|
|
81
|
+ }
|
|
|
82
|
+
|
|
|
83
|
+ // Override connection and xmpp properties (Usefull if the connection
|
|
|
84
|
+ // reloaded)
|
|
|
85
|
+ this.connection = options.connection || this.connection;
|
|
|
86
|
+ this.xmpp = this.connection.xmpp;
|
|
|
87
|
+
|
|
|
88
|
+ this.room = this.xmpp.createRoom(this.options.name, this.options.config,
|
|
|
89
|
+ this.settings);
|
|
|
90
|
+ this.componentsVersions = new ComponentsVersions(this.room);
|
|
|
91
|
+ this.room.updateDeviceAvailability(RTC.getDeviceAvailability());
|
|
|
92
|
+
|
|
|
93
|
+}
|
|
|
94
|
+
|
|
|
95
|
+/**
|
|
|
96
|
+ * Reloads the conference
|
|
|
97
|
+ * @param options {object} options to be overriden
|
|
|
98
|
+ */
|
|
|
99
|
+JitsiConference.prototype.reload = function (options) {
|
|
|
100
|
+ this.leave().then(function(){
|
|
|
101
|
+ this._init(options || {});
|
|
|
102
|
+ this.join();
|
|
|
103
|
+ }.bind(this));
|
|
|
104
|
+}
|
|
|
105
|
+
|
|
73
|
106
|
/**
|
|
74
|
107
|
* Joins the conference.
|
|
75
|
108
|
* @param password {string} the password
|
|
|
@@ -343,6 +376,7 @@ JitsiConference.prototype.addTrack = function (track) {
|
|
343
|
376
|
});
|
|
344
|
377
|
}
|
|
345
|
378
|
this.rtc.addLocalTrack(track);
|
|
|
379
|
+
|
|
346
|
380
|
if (track.startMuted) {
|
|
347
|
381
|
track.mute();
|
|
348
|
382
|
}
|
|
|
@@ -360,7 +394,7 @@ JitsiConference.prototype.addTrack = function (track) {
|
|
360
|
394
|
track.muteHandler);
|
|
361
|
395
|
track.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,
|
|
362
|
396
|
track.audioLevelHandler);
|
|
363
|
|
- //FIXME: This dependacy is not necessary. This is quick fix.
|
|
|
397
|
+
|
|
364
|
398
|
track._setConference(this);
|
|
365
|
399
|
|
|
366
|
400
|
// send event for starting screen sharing
|
|
|
@@ -422,8 +456,7 @@ JitsiConference.prototype.removeTrack = function (track) {
|
|
422
|
456
|
return new Promise(function (resolve, reject) {
|
|
423
|
457
|
this.room.removeStream(track.getOriginalStream(), function(){
|
|
424
|
458
|
track._setSSRC(null);
|
|
425
|
|
- //FIXME: This dependacy is not necessary. This is quick fix.
|
|
426
|
|
- track._setConference(this);
|
|
|
459
|
+ track._setConference(null);
|
|
427
|
460
|
this.rtc.removeLocalTrack(track);
|
|
428
|
461
|
track.removeEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED,
|
|
429
|
462
|
track.muteHandler);
|
|
|
@@ -669,6 +702,84 @@ JitsiConference.prototype.onTrackAdded = function (track) {
|
|
669
|
702
|
emitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
|
|
670
|
703
|
};
|
|
671
|
704
|
|
|
|
705
|
+/**
|
|
|
706
|
+ * Handles incoming call event.
|
|
|
707
|
+ */
|
|
|
708
|
+JitsiConference.prototype.onIncomingCall =
|
|
|
709
|
+function (jingleSession, jingleOffer, now) {
|
|
|
710
|
+ if (!this.room.isFocus(jingleSession.peerjid)) {
|
|
|
711
|
+ // Error cause this should never happen unless something is wrong!
|
|
|
712
|
+ var errmsg = "Rejecting session-initiate from non-focus user: "
|
|
|
713
|
+ + jingleSession.peerjid;
|
|
|
714
|
+ GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
|
|
|
715
|
+ logger.error(errmsg);
|
|
|
716
|
+ return;
|
|
|
717
|
+ }
|
|
|
718
|
+
|
|
|
719
|
+ // Accept incoming call
|
|
|
720
|
+ this.room.setJingleSession(jingleSession);
|
|
|
721
|
+ this.room.connectionTimes["session.initiate"] = now;
|
|
|
722
|
+ try{
|
|
|
723
|
+ jingleSession.initialize(false /* initiator */,this.room);
|
|
|
724
|
+ } catch (error) {
|
|
|
725
|
+ GlobalOnErrorHandler.callErrorHandler(error);
|
|
|
726
|
+ };
|
|
|
727
|
+
|
|
|
728
|
+ this.rtc.onIncommingCall(jingleSession);
|
|
|
729
|
+ // Add local Tracks to the ChatRoom
|
|
|
730
|
+ this.rtc.localTracks.forEach(function(localTrack) {
|
|
|
731
|
+ var ssrcInfo = null;
|
|
|
732
|
+ if(localTrack.isVideoTrack() && localTrack.isMuted()) {
|
|
|
733
|
+ /**
|
|
|
734
|
+ * Handles issues when the stream is added before the peerconnection
|
|
|
735
|
+ * is created. The peerconnection is created when second participant
|
|
|
736
|
+ * enters the call. In that use case the track doesn't have
|
|
|
737
|
+ * information about it's ssrcs and no jingle packets are sent. That
|
|
|
738
|
+ * can cause inconsistent behavior later.
|
|
|
739
|
+ *
|
|
|
740
|
+ * For example:
|
|
|
741
|
+ * If we mute the stream and than second participant enter it's
|
|
|
742
|
+ * remote SDP won't include that track. On unmute we are not sending
|
|
|
743
|
+ * any jingle packets which will brake the unmute.
|
|
|
744
|
+ *
|
|
|
745
|
+ * In order to solve issues like the above one here we have to
|
|
|
746
|
+ * generate the ssrc information for the track .
|
|
|
747
|
+ */
|
|
|
748
|
+ localTrack._setSSRC(
|
|
|
749
|
+ this.room.generateNewStreamSSRCInfo());
|
|
|
750
|
+ ssrcInfo = {
|
|
|
751
|
+ mtype: localTrack.getType(),
|
|
|
752
|
+ type: "addMuted",
|
|
|
753
|
+ ssrc: localTrack.ssrc,
|
|
|
754
|
+ msid: localTrack.initialMSID
|
|
|
755
|
+ };
|
|
|
756
|
+ }
|
|
|
757
|
+ try {
|
|
|
758
|
+ this.room.addStream(
|
|
|
759
|
+ localTrack.getOriginalStream(), function () {}, function () {},
|
|
|
760
|
+ ssrcInfo, true);
|
|
|
761
|
+ } catch(e) {
|
|
|
762
|
+ GlobalOnErrorHandler.callErrorHandler(e);
|
|
|
763
|
+ logger.error(e);
|
|
|
764
|
+ }
|
|
|
765
|
+ }.bind(this));
|
|
|
766
|
+
|
|
|
767
|
+ jingleSession.acceptOffer(jingleOffer, null,
|
|
|
768
|
+ function (error) {
|
|
|
769
|
+ GlobalOnErrorHandler.callErrorHandler(error);
|
|
|
770
|
+ logger.error(
|
|
|
771
|
+ "Failed to accept incoming Jingle session", error);
|
|
|
772
|
+ }
|
|
|
773
|
+ );
|
|
|
774
|
+
|
|
|
775
|
+ // Start callstats as soon as peerconnection is initialized,
|
|
|
776
|
+ // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never
|
|
|
777
|
+ // happen in case if user doesn't have or denied permission to
|
|
|
778
|
+ // both camera and microphone.
|
|
|
779
|
+ this.statistics.startCallStats(jingleSession, this.settings);
|
|
|
780
|
+ this.statistics.startRemoteStats(jingleSession.peerconnection);
|
|
|
781
|
+}
|
|
|
782
|
+
|
|
672
|
783
|
JitsiConference.prototype.updateDTMFSupport = function () {
|
|
673
|
784
|
var somebodySupportsDTMF = false;
|
|
674
|
785
|
var participants = this.getParticipants();
|
|
|
@@ -915,47 +1026,29 @@ JitsiConference.prototype.isCallstatsEnabled = function () {
|
|
915
|
1026
|
return this.statistics.isCallstatsEnabled();
|
|
916
|
1027
|
}
|
|
917
|
1028
|
|
|
|
1029
|
+
|
|
|
1030
|
+/**
|
|
|
1031
|
+ * Handles track attached to container (Calls associateStreamWithVideoTag method
|
|
|
1032
|
+ * from statistics module)
|
|
|
1033
|
+ * @param track the track
|
|
|
1034
|
+ * @param container the container
|
|
|
1035
|
+ */
|
|
|
1036
|
+JitsiConference.prototype._onTrackAttach = function(track, container) {
|
|
|
1037
|
+ var ssrc = track.getSSRC();
|
|
|
1038
|
+ if (!container.id || !ssrc) {
|
|
|
1039
|
+ return;
|
|
|
1040
|
+ }
|
|
|
1041
|
+ this.statistics.associateStreamWithVideoTag(
|
|
|
1042
|
+ ssrc, track.isLocal(), track.getUsageLabel(), container.id);
|
|
|
1043
|
+}
|
|
|
1044
|
+
|
|
918
|
1045
|
/**
|
|
919
|
1046
|
* Setups the listeners needed for the conference.
|
|
920
|
1047
|
* @param conference the conference
|
|
921
|
1048
|
*/
|
|
922
|
1049
|
function setupListeners(conference) {
|
|
923
|
1050
|
conference.xmpp.addListener(
|
|
924
|
|
- XMPPEvents.CALL_INCOMING, function (jingleSession, jingleOffer, now) {
|
|
925
|
|
-
|
|
926
|
|
- if (conference.room.isFocus(jingleSession.peerjid)) {
|
|
927
|
|
- // Accept incoming call
|
|
928
|
|
- conference.room.setJingleSession(jingleSession);
|
|
929
|
|
- conference.room.connectionTimes["session.initiate"] = now;
|
|
930
|
|
- try{
|
|
931
|
|
- jingleSession.initialize(false /* initiator */,
|
|
932
|
|
- conference.room);
|
|
933
|
|
- } catch (error) {
|
|
934
|
|
- GlobalOnErrorHandler.callErrorHandler(error);
|
|
935
|
|
- };
|
|
936
|
|
- conference.rtc.onIncommingCall(jingleSession);
|
|
937
|
|
- jingleSession.acceptOffer(jingleOffer, null,
|
|
938
|
|
- function (error) {
|
|
939
|
|
- GlobalOnErrorHandler.callErrorHandler(error);
|
|
940
|
|
- logger.error(
|
|
941
|
|
- "Failed to accept incoming Jingle session", error);
|
|
942
|
|
- }
|
|
943
|
|
- );
|
|
944
|
|
- // Start callstats as soon as peerconnection is initialized,
|
|
945
|
|
- // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never
|
|
946
|
|
- // happen in case if user doesn't have or denied permission to
|
|
947
|
|
- // both camera and microphone.
|
|
948
|
|
- conference.statistics.startCallStats(jingleSession, conference.settings);
|
|
949
|
|
- conference.statistics.startRemoteStats(jingleSession.peerconnection);
|
|
950
|
|
- } else {
|
|
951
|
|
- // Error cause this should never happen unless something is wrong!
|
|
952
|
|
- var errmsg
|
|
953
|
|
- = "Rejecting session-initiate from non-focus user: "
|
|
954
|
|
- + jingleSession.peerjid;
|
|
955
|
|
- GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
|
|
956
|
|
- logger.error(errmsg);
|
|
957
|
|
- }
|
|
958
|
|
- });
|
|
|
1051
|
+ XMPPEvents.CALL_INCOMING, conference.onIncomingCall.bind(conference));
|
|
959
|
1052
|
|
|
960
|
1053
|
conference.room.addListener(XMPPEvents.ICE_RESTARTING, function () {
|
|
961
|
1054
|
// All data channels have to be closed, before ICE restart
|
|
|
@@ -1206,6 +1299,21 @@ function setupListeners(conference) {
|
|
1206
|
1299
|
conference.rtc.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
|
|
1207
|
1300
|
conference.room.updateDeviceAvailability(devices);
|
|
1208
|
1301
|
});
|
|
|
1302
|
+
|
|
|
1303
|
+ conference.room.addPresenceListener("videomuted", function (values, from) {
|
|
|
1304
|
+ conference.rtc.handleRemoteTrackMute(MediaType.VIDEO,
|
|
|
1305
|
+ values.value == "true", from);
|
|
|
1306
|
+ });
|
|
|
1307
|
+
|
|
|
1308
|
+ conference.room.addPresenceListener("audiomuted", function (values, from) {
|
|
|
1309
|
+ conference.rtc.handleRemoteTrackMute(MediaType.AUDIO,
|
|
|
1310
|
+ values.value == "true", from);
|
|
|
1311
|
+ });
|
|
|
1312
|
+
|
|
|
1313
|
+ conference.room.addPresenceListener("videoType", function(data, from) {
|
|
|
1314
|
+ conference.rtc.handleRemoteTrackVideoTypeChanged(data.value, from);
|
|
|
1315
|
+ });
|
|
|
1316
|
+
|
|
1209
|
1317
|
conference.room.addPresenceListener("devices", function (data, from) {
|
|
1210
|
1318
|
var isAudioAvailable = false;
|
|
1211
|
1319
|
var isVideoAvailable = false;
|
|
|
@@ -1305,17 +1413,6 @@ function setupListeners(conference) {
|
|
1305
|
1413
|
new Error("ICE fail"));
|
|
1306
|
1414
|
});
|
|
1307
|
1415
|
|
|
1308
|
|
- conference.rtc.addListener(RTCEvents.TRACK_ATTACHED,
|
|
1309
|
|
- function(track, container) {
|
|
1310
|
|
- var ssrc = track.getSSRC();
|
|
1311
|
|
- if (!container.id || !ssrc) {
|
|
1312
|
|
- return;
|
|
1313
|
|
- }
|
|
1314
|
|
- conference.statistics.associateStreamWithVideoTag(
|
|
1315
|
|
- ssrc, track.isLocal(), track.getUsageLabel(), container.id);
|
|
1316
|
|
-
|
|
1317
|
|
- });
|
|
1318
|
|
-
|
|
1319
|
1416
|
conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED,
|
|
1320
|
1417
|
function (track) {
|
|
1321
|
1418
|
if(!track.isLocal())
|