|
|
@@ -3,8 +3,6 @@
|
|
3
|
3
|
var logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
4
|
4
|
var RTC = require("./modules/RTC/RTC");
|
|
5
|
5
|
var XMPPEvents = require("./service/xmpp/XMPPEvents");
|
|
6
|
|
-var AuthenticationEvents = require("./service/authentication/AuthenticationEvents");
|
|
7
|
|
-var RTCEvents = require("./service/RTC/RTCEvents");
|
|
8
|
6
|
var EventEmitter = require("events");
|
|
9
|
7
|
var JitsiConferenceEvents = require("./JitsiConferenceEvents");
|
|
10
|
8
|
var JitsiConferenceErrors = require("./JitsiConferenceErrors");
|
|
|
@@ -17,6 +15,7 @@ var JitsiTrackError = require("./JitsiTrackError");
|
|
17
|
15
|
var Settings = require("./modules/settings/Settings");
|
|
18
|
16
|
var ComponentsVersions = require("./modules/version/ComponentsVersions");
|
|
19
|
17
|
var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
|
|
|
18
|
+var JitsiConferenceEventManager = require("./JitsiConferenceEventManager");
|
|
20
|
19
|
|
|
21
|
20
|
/**
|
|
22
|
21
|
* Creates a JitsiConference object with the given name and properties.
|
|
|
@@ -35,25 +34,13 @@ function JitsiConference(options) {
|
|
35
|
34
|
logger.error(errmsg);
|
|
36
|
35
|
throw new Error(errmsg);
|
|
37
|
36
|
}
|
|
38
|
|
- this.options = options;
|
|
39
|
|
- this.connection = this.options.connection;
|
|
40
|
|
- this.xmpp = this.connection.xmpp;
|
|
41
|
37
|
this.eventEmitter = new EventEmitter();
|
|
42
|
|
- var confID = this.options.name + '@' + this.xmpp.options.hosts.muc;
|
|
43
|
38
|
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);
|
|
49
|
|
- this.statistics = new Statistics(this.xmpp, {
|
|
50
|
|
- callStatsID: this.options.config.callStatsID,
|
|
51
|
|
- callStatsSecret: this.options.config.callStatsSecret,
|
|
52
|
|
- disableThirdPartyRequests:
|
|
53
|
|
- this.options.config.disableThirdPartyRequests,
|
|
54
|
|
- roomName: this.options.name
|
|
55
|
|
- });
|
|
56
|
|
- setupListeners(this);
|
|
|
39
|
+ this.retries = 0;
|
|
|
40
|
+ this.options = options;
|
|
|
41
|
+ this.eventManager = new JitsiConferenceEventManager(this);
|
|
|
42
|
+ this._init(options);
|
|
|
43
|
+ this.componentsVersions = new ComponentsVersions(this);
|
|
57
|
44
|
this.participants = {};
|
|
58
|
45
|
this.lastDominantSpeaker = null;
|
|
59
|
46
|
this.dtmfManager = null;
|
|
|
@@ -71,6 +58,73 @@ function JitsiConference(options) {
|
|
71
|
58
|
this.reportedAudioSSRCs = {};
|
|
72
|
59
|
}
|
|
73
|
60
|
|
|
|
61
|
+/**
|
|
|
62
|
+ * Initializes the conference object properties
|
|
|
63
|
+ * @param options {object}
|
|
|
64
|
+ * @param connection {JitsiConnection} overrides this.connection
|
|
|
65
|
+ * @param roomState {object} the state of the ChatRoom instance received by
|
|
|
66
|
+ * ChatRoom.exportState. If this property is set it will be passed to
|
|
|
67
|
+ * ChatRoom.loadState
|
|
|
68
|
+ */
|
|
|
69
|
+JitsiConference.prototype._init = function (options) {
|
|
|
70
|
+ if(!options)
|
|
|
71
|
+ options = {};
|
|
|
72
|
+
|
|
|
73
|
+ // Override connection and xmpp properties (Usefull if the connection
|
|
|
74
|
+ // reloaded)
|
|
|
75
|
+ if(options.connection) {
|
|
|
76
|
+ this.connection = options.connection;
|
|
|
77
|
+ this.xmpp = this.connection.xmpp;
|
|
|
78
|
+ // Setup XMPP events only if we have new connection object.
|
|
|
79
|
+ this.eventManager.setupXMPPListeners();
|
|
|
80
|
+ }
|
|
|
81
|
+
|
|
|
82
|
+ this.retries++;
|
|
|
83
|
+ this.room = this.xmpp.createRoom(this.options.name, this.options.config,
|
|
|
84
|
+ this.settings, (this.retries < 4 ? 3 : null));
|
|
|
85
|
+
|
|
|
86
|
+ this.eventManager.setupChatRoomListeners();
|
|
|
87
|
+
|
|
|
88
|
+ //restore previous presence options
|
|
|
89
|
+ if(options.roomState) {
|
|
|
90
|
+ this.room.loadState(options.roomState);
|
|
|
91
|
+ }
|
|
|
92
|
+ this.room.updateDeviceAvailability(RTC.getDeviceAvailability());
|
|
|
93
|
+
|
|
|
94
|
+ if(!this.rtc) {
|
|
|
95
|
+ this.rtc = new RTC(this, options);
|
|
|
96
|
+ this.eventManager.setupRTCListeners();
|
|
|
97
|
+ }
|
|
|
98
|
+
|
|
|
99
|
+
|
|
|
100
|
+ if(!this.statistics) {
|
|
|
101
|
+ this.statistics = new Statistics(this.xmpp, {
|
|
|
102
|
+ callStatsID: this.options.config.callStatsID,
|
|
|
103
|
+ callStatsSecret: this.options.config.callStatsSecret,
|
|
|
104
|
+ disableThirdPartyRequests:
|
|
|
105
|
+ this.options.config.disableThirdPartyRequests,
|
|
|
106
|
+ roomName: this.options.name
|
|
|
107
|
+ });
|
|
|
108
|
+ }
|
|
|
109
|
+ // Always add listeners because on reload we are executing leave and the
|
|
|
110
|
+ // listeners are removed from statistics module.
|
|
|
111
|
+ this.eventManager.setupStatisticsListeners();
|
|
|
112
|
+}
|
|
|
113
|
+
|
|
|
114
|
+/**
|
|
|
115
|
+ * Reloads the conference
|
|
|
116
|
+ * @param options {object} options to be overriden
|
|
|
117
|
+ */
|
|
|
118
|
+JitsiConference.prototype._reload = function (options) {
|
|
|
119
|
+ var roomState = this.room.exportState();
|
|
|
120
|
+ if(!options)
|
|
|
121
|
+ options = {};
|
|
|
122
|
+ options.roomState = roomState;
|
|
|
123
|
+ this.leave(true);
|
|
|
124
|
+ this._init(options);
|
|
|
125
|
+ this.join();
|
|
|
126
|
+}
|
|
|
127
|
+
|
|
74
|
128
|
/**
|
|
75
|
129
|
* Joins the conference.
|
|
76
|
130
|
* @param password {string} the password
|
|
|
@@ -91,16 +145,17 @@ JitsiConference.prototype.isJoined = function () {
|
|
91
|
145
|
* Leaves the conference and calls onMemberLeft for every participant.
|
|
92
|
146
|
*/
|
|
93
|
147
|
JitsiConference.prototype._leaveRoomAndRemoveParticipants = function () {
|
|
|
148
|
+ // remove all participants
|
|
|
149
|
+ this.getParticipants().forEach(function (participant) {
|
|
|
150
|
+ this.onMemberLeft(participant.getJid());
|
|
|
151
|
+ }.bind(this));
|
|
|
152
|
+
|
|
94
|
153
|
// leave the conference
|
|
95
|
154
|
if (this.room) {
|
|
96
|
155
|
this.room.leave();
|
|
97
|
156
|
}
|
|
98
|
157
|
|
|
99
|
158
|
this.room = null;
|
|
100
|
|
- // remove all participants
|
|
101
|
|
- this.getParticipants().forEach(function (participant) {
|
|
102
|
|
- this.onMemberLeft(participant.getJid());
|
|
103
|
|
- }.bind(this));
|
|
104
|
159
|
|
|
105
|
160
|
this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
|
|
106
|
161
|
}
|
|
|
@@ -108,9 +163,16 @@ JitsiConference.prototype._leaveRoomAndRemoveParticipants = function () {
|
|
108
|
163
|
* Leaves the conference.
|
|
109
|
164
|
* @returns {Promise}
|
|
110
|
165
|
*/
|
|
111
|
|
-JitsiConference.prototype.leave = function () {
|
|
|
166
|
+JitsiConference.prototype.leave = function (dontRemoveLocalTracks) {
|
|
112
|
167
|
var conference = this;
|
|
113
|
168
|
|
|
|
169
|
+ this.statistics.stopCallStats();
|
|
|
170
|
+ this.rtc.closeAllDataChannels();
|
|
|
171
|
+ if(dontRemoveLocalTracks) {
|
|
|
172
|
+ this._leaveRoomAndRemoveParticipants();
|
|
|
173
|
+ return Promise.resolve();
|
|
|
174
|
+ }
|
|
|
175
|
+
|
|
114
|
176
|
return Promise.all(
|
|
115
|
177
|
conference.getLocalTracks().map(function (track) {
|
|
116
|
178
|
return conference.removeTrack(track);
|
|
|
@@ -369,6 +431,7 @@ JitsiConference.prototype.addTrack = function (track) {
|
|
369
|
431
|
});
|
|
370
|
432
|
}
|
|
371
|
433
|
this.rtc.addLocalTrack(track);
|
|
|
434
|
+
|
|
372
|
435
|
if (track.startMuted) {
|
|
373
|
436
|
track.mute();
|
|
374
|
437
|
}
|
|
|
@@ -386,7 +449,7 @@ JitsiConference.prototype.addTrack = function (track) {
|
|
386
|
449
|
track.muteHandler);
|
|
387
|
450
|
track.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,
|
|
388
|
451
|
track.audioLevelHandler);
|
|
389
|
|
- //FIXME: This dependacy is not necessary. This is quick fix.
|
|
|
452
|
+
|
|
390
|
453
|
track._setConference(this);
|
|
391
|
454
|
|
|
392
|
455
|
// send event for starting screen sharing
|
|
|
@@ -694,6 +757,84 @@ JitsiConference.prototype.onTrackAdded = function (track) {
|
|
694
|
757
|
emitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
|
|
695
|
758
|
};
|
|
696
|
759
|
|
|
|
760
|
+/**
|
|
|
761
|
+ * Handles incoming call event.
|
|
|
762
|
+ */
|
|
|
763
|
+JitsiConference.prototype.onIncomingCall =
|
|
|
764
|
+function (jingleSession, jingleOffer, now) {
|
|
|
765
|
+ if (!this.room.isFocus(jingleSession.peerjid)) {
|
|
|
766
|
+ // Error cause this should never happen unless something is wrong!
|
|
|
767
|
+ var errmsg = "Rejecting session-initiate from non-focus user: "
|
|
|
768
|
+ + jingleSession.peerjid;
|
|
|
769
|
+ GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
|
|
|
770
|
+ logger.error(errmsg);
|
|
|
771
|
+ return;
|
|
|
772
|
+ }
|
|
|
773
|
+
|
|
|
774
|
+ // Accept incoming call
|
|
|
775
|
+ this.room.setJingleSession(jingleSession);
|
|
|
776
|
+ this.room.connectionTimes["session.initiate"] = now;
|
|
|
777
|
+ try{
|
|
|
778
|
+ jingleSession.initialize(false /* initiator */,this.room);
|
|
|
779
|
+ } catch (error) {
|
|
|
780
|
+ GlobalOnErrorHandler.callErrorHandler(error);
|
|
|
781
|
+ };
|
|
|
782
|
+
|
|
|
783
|
+ this.rtc.onIncommingCall(jingleSession);
|
|
|
784
|
+ // Add local Tracks to the ChatRoom
|
|
|
785
|
+ this.rtc.localTracks.forEach(function(localTrack) {
|
|
|
786
|
+ var ssrcInfo = null;
|
|
|
787
|
+ if(localTrack.isVideoTrack() && localTrack.isMuted()) {
|
|
|
788
|
+ /**
|
|
|
789
|
+ * Handles issues when the stream is added before the peerconnection
|
|
|
790
|
+ * is created. The peerconnection is created when second participant
|
|
|
791
|
+ * enters the call. In that use case the track doesn't have
|
|
|
792
|
+ * information about it's ssrcs and no jingle packets are sent. That
|
|
|
793
|
+ * can cause inconsistent behavior later.
|
|
|
794
|
+ *
|
|
|
795
|
+ * For example:
|
|
|
796
|
+ * If we mute the stream and than second participant enter it's
|
|
|
797
|
+ * remote SDP won't include that track. On unmute we are not sending
|
|
|
798
|
+ * any jingle packets which will brake the unmute.
|
|
|
799
|
+ *
|
|
|
800
|
+ * In order to solve issues like the above one here we have to
|
|
|
801
|
+ * generate the ssrc information for the track .
|
|
|
802
|
+ */
|
|
|
803
|
+ localTrack._setSSRC(
|
|
|
804
|
+ this.room.generateNewStreamSSRCInfo());
|
|
|
805
|
+ ssrcInfo = {
|
|
|
806
|
+ mtype: localTrack.getType(),
|
|
|
807
|
+ type: "addMuted",
|
|
|
808
|
+ ssrc: localTrack.ssrc,
|
|
|
809
|
+ msid: localTrack.initialMSID
|
|
|
810
|
+ };
|
|
|
811
|
+ }
|
|
|
812
|
+ try {
|
|
|
813
|
+ this.room.addStream(
|
|
|
814
|
+ localTrack.getOriginalStream(), function () {}, function () {},
|
|
|
815
|
+ ssrcInfo, true);
|
|
|
816
|
+ } catch(e) {
|
|
|
817
|
+ GlobalOnErrorHandler.callErrorHandler(e);
|
|
|
818
|
+ logger.error(e);
|
|
|
819
|
+ }
|
|
|
820
|
+ }.bind(this));
|
|
|
821
|
+
|
|
|
822
|
+ jingleSession.acceptOffer(jingleOffer, null,
|
|
|
823
|
+ function (error) {
|
|
|
824
|
+ GlobalOnErrorHandler.callErrorHandler(error);
|
|
|
825
|
+ logger.error(
|
|
|
826
|
+ "Failed to accept incoming Jingle session", error);
|
|
|
827
|
+ }
|
|
|
828
|
+ );
|
|
|
829
|
+
|
|
|
830
|
+ // Start callstats as soon as peerconnection is initialized,
|
|
|
831
|
+ // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never
|
|
|
832
|
+ // happen in case if user doesn't have or denied permission to
|
|
|
833
|
+ // both camera and microphone.
|
|
|
834
|
+ this.statistics.startCallStats(jingleSession, this.settings);
|
|
|
835
|
+ this.statistics.startRemoteStats(jingleSession.peerconnection);
|
|
|
836
|
+}
|
|
|
837
|
+
|
|
697
|
838
|
JitsiConference.prototype.updateDTMFSupport = function () {
|
|
698
|
839
|
var somebodySupportsDTMF = false;
|
|
699
|
840
|
var participants = this.getParticipants();
|
|
|
@@ -947,6 +1088,22 @@ JitsiConference.prototype.isCallstatsEnabled = function () {
|
|
947
|
1088
|
return this.statistics.isCallstatsEnabled();
|
|
948
|
1089
|
}
|
|
949
|
1090
|
|
|
|
1091
|
+
|
|
|
1092
|
+/**
|
|
|
1093
|
+ * Handles track attached to container (Calls associateStreamWithVideoTag method
|
|
|
1094
|
+ * from statistics module)
|
|
|
1095
|
+ * @param track the track
|
|
|
1096
|
+ * @param container the container
|
|
|
1097
|
+ */
|
|
|
1098
|
+JitsiConference.prototype._onTrackAttach = function(track, container) {
|
|
|
1099
|
+ var ssrc = track.getSSRC();
|
|
|
1100
|
+ if (!container.id || !ssrc) {
|
|
|
1101
|
+ return;
|
|
|
1102
|
+ }
|
|
|
1103
|
+ this.statistics.associateStreamWithVideoTag(
|
|
|
1104
|
+ ssrc, track.isLocal(), track.getUsageLabel(), container.id);
|
|
|
1105
|
+}
|
|
|
1106
|
+
|
|
950
|
1107
|
/**
|
|
951
|
1108
|
* Reports detected audio problem with the media stream related to the passed
|
|
952
|
1109
|
* ssrc.
|
|
|
@@ -1050,469 +1207,21 @@ JitsiConference.prototype.sendApplicationLog = function(message) {
|
|
1050
|
1207
|
};
|
|
1051
|
1208
|
|
|
1052
|
1209
|
/**
|
|
1053
|
|
- * Setups the listeners needed for the conference.
|
|
1054
|
|
- * @param conference the conference
|
|
|
1210
|
+ * Checks if the user identified by given <tt>mucJid</tt> is the conference
|
|
|
1211
|
+ * focus.
|
|
|
1212
|
+ * @param mucJid the full MUC address of the user to be checked.
|
|
|
1213
|
+ * @returns {boolean} <tt>true</tt> if MUC user is the conference focus.
|
|
1055
|
1214
|
*/
|
|
1056
|
|
-function setupListeners(conference) {
|
|
1057
|
|
- conference.xmpp.addListener(
|
|
1058
|
|
- XMPPEvents.CALL_INCOMING, function (jingleSession, jingleOffer, now) {
|
|
1059
|
|
-
|
|
1060
|
|
- if (conference.room.isFocus(jingleSession.peerjid)) {
|
|
1061
|
|
- // Accept incoming call
|
|
1062
|
|
- conference.room.setJingleSession(jingleSession);
|
|
1063
|
|
- conference.room.connectionTimes["session.initiate"] = now;
|
|
1064
|
|
- try{
|
|
1065
|
|
- jingleSession.initialize(false /* initiator */,
|
|
1066
|
|
- conference.room);
|
|
1067
|
|
- } catch (error) {
|
|
1068
|
|
- GlobalOnErrorHandler.callErrorHandler(error);
|
|
1069
|
|
- };
|
|
1070
|
|
- conference.rtc.onIncommingCall(jingleSession);
|
|
1071
|
|
- jingleSession.acceptOffer(jingleOffer, null,
|
|
1072
|
|
- function (error) {
|
|
1073
|
|
- GlobalOnErrorHandler.callErrorHandler(error);
|
|
1074
|
|
- logger.error(
|
|
1075
|
|
- "Failed to accept incoming Jingle session", error);
|
|
1076
|
|
- }
|
|
1077
|
|
- );
|
|
1078
|
|
- // Start callstats as soon as peerconnection is initialized,
|
|
1079
|
|
- // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never
|
|
1080
|
|
- // happen in case if user doesn't have or denied permission to
|
|
1081
|
|
- // both camera and microphone.
|
|
1082
|
|
- conference.statistics.startCallStats(jingleSession, conference.settings);
|
|
1083
|
|
- conference.statistics.startRemoteStats(jingleSession.peerconnection);
|
|
1084
|
|
- } else {
|
|
1085
|
|
- // Error cause this should never happen unless something is wrong!
|
|
1086
|
|
- var errmsg
|
|
1087
|
|
- = "Rejecting session-initiate from non-focus user: "
|
|
1088
|
|
- + jingleSession.peerjid;
|
|
1089
|
|
- GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
|
|
1090
|
|
- logger.error(errmsg);
|
|
1091
|
|
- }
|
|
1092
|
|
- });
|
|
1093
|
|
-
|
|
1094
|
|
- conference.room.addListener(XMPPEvents.ICE_RESTARTING, function () {
|
|
1095
|
|
- // All data channels have to be closed, before ICE restart
|
|
1096
|
|
- // otherwise Chrome will not trigger "opened" event for the channel
|
|
1097
|
|
- // established with the new bridge
|
|
1098
|
|
- conference.rtc.closeAllDataChannels();
|
|
1099
|
|
- });
|
|
1100
|
|
-
|
|
1101
|
|
- conference.room.addListener(XMPPEvents.LOCAL_UFRAG_CHANGED, function (ufrag) {
|
|
1102
|
|
- Statistics.sendLog("Local ufrag: " + ufrag);
|
|
1103
|
|
- });
|
|
1104
|
|
- conference.room.addListener(XMPPEvents.REMOTE_UFRAG_CHANGED, function (ufrag) {
|
|
1105
|
|
- Statistics.sendLog("Remote ufrag: " + ufrag);
|
|
1106
|
|
- });
|
|
1107
|
|
-
|
|
1108
|
|
- conference.room.addListener(XMPPEvents.REMOTE_TRACK_ADDED,
|
|
1109
|
|
- function (data) {
|
|
1110
|
|
- var track = conference.rtc.createRemoteTrack(data);
|
|
1111
|
|
- if (track) {
|
|
1112
|
|
- conference.onTrackAdded(track);
|
|
1113
|
|
- }
|
|
1114
|
|
- }
|
|
1115
|
|
- );
|
|
1116
|
|
- conference.room.addListener(XMPPEvents.REMOTE_TRACK_REMOVED,
|
|
1117
|
|
- function (streamId, trackId) {
|
|
1118
|
|
- conference.getParticipants().forEach(function(participant) {
|
|
1119
|
|
- var tracks = participant.getTracks();
|
|
1120
|
|
- for(var i = 0; i < tracks.length; i++) {
|
|
1121
|
|
- if(tracks[i]
|
|
1122
|
|
- && tracks[i].getStreamId() == streamId
|
|
1123
|
|
- && tracks[i].getTrackId() == trackId) {
|
|
1124
|
|
- var track = participant._tracks.splice(i, 1)[0];
|
|
1125
|
|
- conference.eventEmitter.emit(
|
|
1126
|
|
- JitsiConferenceEvents.TRACK_REMOVED, track);
|
|
1127
|
|
- return;
|
|
1128
|
|
- }
|
|
1129
|
|
- }
|
|
1130
|
|
- });
|
|
1131
|
|
- }
|
|
1132
|
|
- );
|
|
1133
|
|
-
|
|
1134
|
|
- conference.room.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS,
|
|
1135
|
|
- function (value) {
|
|
1136
|
|
- // set isMutedByFocus when setAudioMute Promise ends
|
|
1137
|
|
- conference.rtc.setAudioMute(value).then(
|
|
1138
|
|
- function() {
|
|
1139
|
|
- conference.isMutedByFocus = true;
|
|
1140
|
|
- },
|
|
1141
|
|
- function() {
|
|
1142
|
|
- logger.warn(
|
|
1143
|
|
- "Error while audio muting due to focus request");
|
|
1144
|
|
- });
|
|
1145
|
|
- }
|
|
1146
|
|
- );
|
|
1147
|
|
-
|
|
1148
|
|
- conference.room.addListener(XMPPEvents.SUBJECT_CHANGED, function (subject) {
|
|
1149
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.SUBJECT_CHANGED,
|
|
1150
|
|
- subject);
|
|
1151
|
|
- });
|
|
1152
|
|
-
|
|
1153
|
|
- conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
|
|
1154
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED);
|
|
1155
|
|
- });
|
|
1156
|
|
- conference.room.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
|
|
1157
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,
|
|
1158
|
|
- JitsiConferenceErrors.CONNECTION_ERROR, pres);
|
|
1159
|
|
- });
|
|
1160
|
|
- conference.room.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
|
|
1161
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,
|
|
1162
|
|
- JitsiConferenceErrors.CONNECTION_ERROR, pres);
|
|
1163
|
|
- });
|
|
1164
|
|
- conference.room.addListener(XMPPEvents.ROOM_MAX_USERS_ERROR,
|
|
1165
|
|
- function (pres) {
|
|
1166
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,
|
|
1167
|
|
- JitsiConferenceErrors.CONFERENCE_MAX_USERS, pres);
|
|
1168
|
|
- });
|
|
1169
|
|
- conference.room.addListener(XMPPEvents.PASSWORD_REQUIRED, function (pres) {
|
|
1170
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.PASSWORD_REQUIRED, pres);
|
|
1171
|
|
- });
|
|
1172
|
|
- conference.room.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, function () {
|
|
1173
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.AUTHENTICATION_REQUIRED);
|
|
1174
|
|
- });
|
|
1175
|
|
- conference.room.addListener(XMPPEvents.BRIDGE_DOWN, function () {
|
|
1176
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);
|
|
1177
|
|
- });
|
|
1178
|
|
- conference.room.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) {
|
|
1179
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.RESERVATION_ERROR, code, msg);
|
|
1180
|
|
- });
|
|
1181
|
|
- conference.room.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () {
|
|
1182
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.GRACEFUL_SHUTDOWN);
|
|
1183
|
|
- });
|
|
1184
|
|
- conference.room.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function (session, error) {
|
|
1185
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.JINGLE_FATAL_ERROR, error);
|
|
1186
|
|
- });
|
|
1187
|
|
- conference.room.addListener(XMPPEvents.MUC_DESTROYED, function (reason) {
|
|
1188
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONFERENCE_DESTROYED, reason);
|
|
1189
|
|
- });
|
|
1190
|
|
- conference.room.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, function (err, msg) {
|
|
1191
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_ERROR, JitsiConferenceErrors.CHAT_ERROR, err, msg);
|
|
1192
|
|
- });
|
|
1193
|
|
- conference.room.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focus, retrySec) {
|
|
1194
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.FOCUS_DISCONNECTED, focus, retrySec);
|
|
1195
|
|
- });
|
|
1196
|
|
- conference.room.addListener(XMPPEvents.FOCUS_LEFT, function () {
|
|
1197
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.FOCUS_LEFT);
|
|
1198
|
|
- });
|
|
1199
|
|
- conference.room.setParticipantPropertyListener(function (node, from) {
|
|
1200
|
|
- var participant = conference.getParticipantById(from);
|
|
1201
|
|
- if (!participant) {
|
|
1202
|
|
- return;
|
|
1203
|
|
- }
|
|
1204
|
|
-
|
|
1205
|
|
- participant.setProperty(
|
|
1206
|
|
- node.tagName.substring("jitsi_participant_".length),
|
|
1207
|
|
- node.value);
|
|
1208
|
|
- });
|
|
1209
|
|
-
|
|
1210
|
|
- conference.room.addListener(XMPPEvents.KICKED, function () {
|
|
1211
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.KICKED);
|
|
1212
|
|
- });
|
|
1213
|
|
-
|
|
1214
|
|
- conference.room.addListener(XMPPEvents.MUC_MEMBER_JOINED, conference.onMemberJoined.bind(conference));
|
|
1215
|
|
- conference.room.addListener(XMPPEvents.MUC_MEMBER_LEFT, conference.onMemberLeft.bind(conference));
|
|
1216
|
|
-
|
|
1217
|
|
- conference.room.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, conference.onDisplayNameChanged.bind(conference));
|
|
1218
|
|
-
|
|
1219
|
|
- conference.room.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, function (role) {
|
|
1220
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.USER_ROLE_CHANGED, conference.myUserId(), role);
|
|
1221
|
|
-
|
|
1222
|
|
- // log all events for the recorder operated by the moderator
|
|
1223
|
|
- if (conference.statistics && conference.isModerator()) {
|
|
1224
|
|
- conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,
|
|
1225
|
|
- function (status, error) {
|
|
1226
|
|
- Statistics.sendLog(
|
|
1227
|
|
- "[Recorder] status: " + status
|
|
1228
|
|
- + (error? " error: " + error : ""));
|
|
1229
|
|
- });
|
|
1230
|
|
- }
|
|
1231
|
|
- });
|
|
1232
|
|
- conference.room.addListener(XMPPEvents.MUC_ROLE_CHANGED, conference.onUserRoleChanged.bind(conference));
|
|
1233
|
|
-
|
|
1234
|
|
- conference.room.addListener(XMPPEvents.CONNECTION_INTERRUPTED, function () {
|
|
1235
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_INTERRUPTED);
|
|
1236
|
|
- });
|
|
1237
|
|
-
|
|
1238
|
|
- conference.room.addListener(XMPPEvents.RECORDER_STATE_CHANGED,
|
|
1239
|
|
- function (state) {
|
|
1240
|
|
- conference.eventEmitter.emit(
|
|
1241
|
|
- JitsiConferenceEvents.RECORDER_STATE_CHANGED, state);
|
|
1242
|
|
- });
|
|
1243
|
|
-
|
|
1244
|
|
- conference.room.addListener(XMPPEvents.PHONE_NUMBER_CHANGED, function () {
|
|
1245
|
|
- conference.eventEmitter.emit(
|
|
1246
|
|
- JitsiConferenceEvents.PHONE_NUMBER_CHANGED);
|
|
1247
|
|
- });
|
|
1248
|
|
-
|
|
1249
|
|
- conference.room.addListener(XMPPEvents.CONNECTION_RESTORED, function () {
|
|
1250
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);
|
|
1251
|
|
- });
|
|
1252
|
|
- conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED,
|
|
1253
|
|
- function (error) {
|
|
1254
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,
|
|
1255
|
|
- JitsiConferenceErrors.SETUP_FAILED, error);
|
|
1256
|
|
- });
|
|
1257
|
|
-
|
|
1258
|
|
- conference.room.addListener(AuthenticationEvents.IDENTITY_UPDATED, function (authEnabled, authIdentity) {
|
|
1259
|
|
- conference.authEnabled = authEnabled;
|
|
1260
|
|
- conference.authIdentity = authIdentity;
|
|
1261
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.AUTH_STATUS_CHANGED, authEnabled, authIdentity);
|
|
1262
|
|
- });
|
|
1263
|
|
-
|
|
1264
|
|
- conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) {
|
|
1265
|
|
- var id = Strophe.getResourceFromJid(jid);
|
|
1266
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.MESSAGE_RECEIVED, id, txt, ts);
|
|
1267
|
|
- });
|
|
1268
|
|
-
|
|
1269
|
|
- conference.room.addListener(XMPPEvents.PRESENCE_STATUS, function (jid, status) {
|
|
1270
|
|
- var id = Strophe.getResourceFromJid(jid);
|
|
1271
|
|
- var participant = conference.getParticipantById(id);
|
|
1272
|
|
- if (!participant || participant._status === status) {
|
|
1273
|
|
- return;
|
|
1274
|
|
- }
|
|
1275
|
|
- participant._status = status;
|
|
1276
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.USER_STATUS_CHANGED, id, status);
|
|
1277
|
|
- });
|
|
1278
|
|
-
|
|
1279
|
|
- conference.rtc.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (id) {
|
|
1280
|
|
- if(conference.lastDominantSpeaker !== id && conference.room) {
|
|
1281
|
|
- conference.lastDominantSpeaker = id;
|
|
1282
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, id);
|
|
1283
|
|
- }
|
|
1284
|
|
- if (conference.statistics && conference.myUserId() === id) {
|
|
1285
|
|
- // We are the new dominant speaker.
|
|
1286
|
|
- conference.statistics.sendDominantSpeakerEvent();
|
|
1287
|
|
- }
|
|
1288
|
|
- });
|
|
1289
|
|
-
|
|
1290
|
|
- conference.rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, function () {
|
|
1291
|
|
- var now = window.performance.now();
|
|
1292
|
|
- logger.log("(TIME) data channel opened ", now);
|
|
1293
|
|
- conference.room.connectionTimes["data.channel.opened"] = now;
|
|
1294
|
|
- });
|
|
1295
|
|
-
|
|
1296
|
|
- conference.rtc.addListener(RTCEvents.LASTN_CHANGED, function (oldValue, newValue) {
|
|
1297
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.IN_LAST_N_CHANGED, oldValue, newValue);
|
|
1298
|
|
- });
|
|
1299
|
|
-
|
|
1300
|
|
- conference.rtc.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
|
|
1301
|
|
- function (lastNEndpoints, endpointsEnteringLastN) {
|
|
1302
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,
|
|
1303
|
|
- lastNEndpoints, endpointsEnteringLastN);
|
|
1304
|
|
- });
|
|
1305
|
|
-
|
|
1306
|
|
- conference.xmpp.addListener(XMPPEvents.START_MUTED_FROM_FOCUS,
|
|
1307
|
|
- function (audioMuted, videoMuted) {
|
|
1308
|
|
- conference.startAudioMuted = audioMuted;
|
|
1309
|
|
- conference.startVideoMuted = videoMuted;
|
|
1310
|
|
-
|
|
1311
|
|
- // mute existing local tracks because this is initial mute from
|
|
1312
|
|
- // Jicofo
|
|
1313
|
|
- conference.getLocalTracks().forEach(function (track) {
|
|
1314
|
|
- if (conference.startAudioMuted && track.isAudioTrack()) {
|
|
1315
|
|
- track.mute();
|
|
1316
|
|
- }
|
|
1317
|
|
- if (conference.startVideoMuted && track.isVideoTrack()) {
|
|
1318
|
|
- track.mute();
|
|
1319
|
|
- }
|
|
1320
|
|
- });
|
|
1321
|
|
-
|
|
1322
|
|
- conference.eventEmitter.emit(JitsiConferenceEvents.STARTED_MUTED);
|
|
1323
|
|
- });
|
|
1324
|
|
-
|
|
1325
|
|
- conference.room.addPresenceListener("startmuted", function (data, from) {
|
|
1326
|
|
- var isModerator = false;
|
|
1327
|
|
- if (conference.myUserId() === from && conference.isModerator()) {
|
|
1328
|
|
- isModerator = true;
|
|
1329
|
|
- } else {
|
|
1330
|
|
- var participant = conference.getParticipantById(from);
|
|
1331
|
|
- if (participant && participant.isModerator()) {
|
|
1332
|
|
- isModerator = true;
|
|
1333
|
|
- }
|
|
1334
|
|
- }
|
|
1335
|
|
-
|
|
1336
|
|
- if (!isModerator) {
|
|
1337
|
|
- return;
|
|
1338
|
|
- }
|
|
1339
|
|
-
|
|
1340
|
|
- var startAudioMuted = data.attributes.audio === 'true';
|
|
1341
|
|
- var startVideoMuted = data.attributes.video === 'true';
|
|
1342
|
|
-
|
|
1343
|
|
- var updated = false;
|
|
1344
|
|
-
|
|
1345
|
|
- if (startAudioMuted !== conference.startMutedPolicy.audio) {
|
|
1346
|
|
- conference.startMutedPolicy.audio = startAudioMuted;
|
|
1347
|
|
- updated = true;
|
|
1348
|
|
- }
|
|
1349
|
|
-
|
|
1350
|
|
- if (startVideoMuted !== conference.startMutedPolicy.video) {
|
|
1351
|
|
- conference.startMutedPolicy.video = startVideoMuted;
|
|
1352
|
|
- updated = true;
|
|
1353
|
|
- }
|
|
1354
|
|
-
|
|
1355
|
|
- if (updated) {
|
|
1356
|
|
- conference.eventEmitter.emit(
|
|
1357
|
|
- JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
|
|
1358
|
|
- conference.startMutedPolicy
|
|
1359
|
|
- );
|
|
1360
|
|
- }
|
|
1361
|
|
- });
|
|
1362
|
|
-
|
|
1363
|
|
- conference.rtc.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
|
|
1364
|
|
- conference.room.updateDeviceAvailability(devices);
|
|
1365
|
|
- });
|
|
1366
|
|
- conference.room.addPresenceListener("devices", function (data, from) {
|
|
1367
|
|
- var isAudioAvailable = false;
|
|
1368
|
|
- var isVideoAvailable = false;
|
|
1369
|
|
- data.children.forEach(function (config) {
|
|
1370
|
|
- if (config.tagName === 'audio') {
|
|
1371
|
|
- isAudioAvailable = config.value === 'true';
|
|
1372
|
|
- }
|
|
1373
|
|
- if (config.tagName === 'video') {
|
|
1374
|
|
- isVideoAvailable = config.value === 'true';
|
|
1375
|
|
- }
|
|
1376
|
|
- });
|
|
1377
|
|
-
|
|
1378
|
|
- var availableDevices;
|
|
1379
|
|
- if (conference.myUserId() === from) {
|
|
1380
|
|
- availableDevices = conference.availableDevices;
|
|
1381
|
|
- } else {
|
|
1382
|
|
- var participant = conference.getParticipantById(from);
|
|
1383
|
|
- if (!participant) {
|
|
1384
|
|
- return;
|
|
1385
|
|
- }
|
|
1386
|
|
-
|
|
1387
|
|
- availableDevices = participant._availableDevices;
|
|
1388
|
|
- }
|
|
1389
|
|
-
|
|
1390
|
|
- var updated = false;
|
|
1391
|
|
-
|
|
1392
|
|
- if (availableDevices.audio !== isAudioAvailable) {
|
|
1393
|
|
- updated = true;
|
|
1394
|
|
- availableDevices.audio = isAudioAvailable;
|
|
1395
|
|
- }
|
|
1396
|
|
-
|
|
1397
|
|
- if (availableDevices.video !== isVideoAvailable) {
|
|
1398
|
|
- updated = true;
|
|
1399
|
|
- availableDevices.video = isVideoAvailable;
|
|
1400
|
|
- }
|
|
1401
|
|
-
|
|
1402
|
|
- if (updated) {
|
|
1403
|
|
- conference.eventEmitter.emit(
|
|
1404
|
|
- JitsiConferenceEvents.AVAILABLE_DEVICES_CHANGED,
|
|
1405
|
|
- from, availableDevices);
|
|
1406
|
|
- }
|
|
1407
|
|
- });
|
|
1408
|
|
-
|
|
1409
|
|
- if(conference.statistics) {
|
|
1410
|
|
- //FIXME: Maybe remove event should not be associated with the conference.
|
|
1411
|
|
- conference.statistics.addAudioLevelListener(function (ssrc, level) {
|
|
1412
|
|
- var userId = null;
|
|
1413
|
|
-
|
|
1414
|
|
- var resource = conference.rtc.getResourceBySSRC(ssrc);
|
|
1415
|
|
- if (!resource)
|
|
1416
|
|
- return;
|
|
1417
|
|
-
|
|
1418
|
|
- conference.rtc.setAudioLevel(resource, level);
|
|
1419
|
|
- });
|
|
1420
|
|
-
|
|
1421
|
|
- conference.statistics.addAudioProblemListener(function (ssrc) {
|
|
1422
|
|
- conference._reportAudioProblem(ssrc)
|
|
1423
|
|
- });
|
|
1424
|
|
-
|
|
1425
|
|
- conference.statistics.addConnectionStatsListener(function (stats) {
|
|
1426
|
|
- var ssrc2resolution = stats.resolution;
|
|
1427
|
|
-
|
|
1428
|
|
- var id2resolution = {};
|
|
1429
|
|
-
|
|
1430
|
|
- // preprocess resolutions: group by user id, skip incorrect
|
|
1431
|
|
- // resolutions etc.
|
|
1432
|
|
- Object.keys(ssrc2resolution).forEach(function (ssrc) {
|
|
1433
|
|
- var resolution = ssrc2resolution[ssrc];
|
|
1434
|
|
-
|
|
1435
|
|
- if (!resolution.width || !resolution.height ||
|
|
1436
|
|
- resolution.width == -1 || resolution.height == -1) {
|
|
1437
|
|
- return;
|
|
1438
|
|
- }
|
|
1439
|
|
-
|
|
1440
|
|
- var id = conference.rtc.getResourceBySSRC(ssrc);
|
|
1441
|
|
- if (!id) {
|
|
1442
|
|
- return;
|
|
1443
|
|
- }
|
|
1444
|
|
-
|
|
1445
|
|
- // ssrc to resolution map for user id
|
|
1446
|
|
- var idResolutions = id2resolution[id] || {};
|
|
1447
|
|
- idResolutions[ssrc] = resolution;
|
|
1448
|
|
-
|
|
1449
|
|
- id2resolution[id] = idResolutions;
|
|
1450
|
|
- });
|
|
1451
|
|
-
|
|
1452
|
|
- stats.resolution = id2resolution;
|
|
1453
|
|
-
|
|
1454
|
|
- conference.eventEmitter.emit(
|
|
1455
|
|
- JitsiConferenceEvents.CONNECTION_STATS, stats);
|
|
1456
|
|
- });
|
|
1457
|
|
- conference.room.addListener(XMPPEvents.DISPOSE_CONFERENCE,
|
|
1458
|
|
- function () {
|
|
1459
|
|
- conference.statistics.dispose();
|
|
1460
|
|
- });
|
|
1461
|
|
-
|
|
1462
|
|
- conference.room.addListener(XMPPEvents.CONNECTION_ICE_FAILED,
|
|
1463
|
|
- function (pc) {
|
|
1464
|
|
- conference.statistics.sendIceConnectionFailedEvent(pc);
|
|
1465
|
|
- conference.room.eventEmitter.emit(
|
|
1466
|
|
- XMPPEvents.CONFERENCE_SETUP_FAILED,
|
|
1467
|
|
- new Error("ICE fail"));
|
|
1468
|
|
- });
|
|
1469
|
|
-
|
|
1470
|
|
- conference.rtc.addListener(RTCEvents.TRACK_ATTACHED,
|
|
1471
|
|
- function(track, container) {
|
|
1472
|
|
- var ssrc = track.getSSRC();
|
|
1473
|
|
- if (!container.id || !ssrc) {
|
|
1474
|
|
- return;
|
|
1475
|
|
- }
|
|
1476
|
|
- conference.statistics.associateStreamWithVideoTag(
|
|
1477
|
|
- ssrc, track.isLocal(), track.getUsageLabel(), container.id);
|
|
1478
|
|
-
|
|
1479
|
|
- });
|
|
1480
|
|
-
|
|
1481
|
|
- conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED,
|
|
1482
|
|
- function (track) {
|
|
1483
|
|
- if(!track.isLocal())
|
|
1484
|
|
- return;
|
|
1485
|
|
- var type = (track.getType() === "audio")? "audio" : "video";
|
|
1486
|
|
- conference.statistics.sendMuteEvent(track.isMuted(), type);
|
|
1487
|
|
- });
|
|
1488
|
|
-
|
|
1489
|
|
- conference.room.addListener(XMPPEvents.CREATE_OFFER_FAILED, function (e, pc) {
|
|
1490
|
|
- conference.statistics.sendCreateOfferFailed(e, pc);
|
|
1491
|
|
- });
|
|
1492
|
|
-
|
|
1493
|
|
- conference.room.addListener(XMPPEvents.CREATE_ANSWER_FAILED, function (e, pc) {
|
|
1494
|
|
- conference.statistics.sendCreateAnswerFailed(e, pc);
|
|
1495
|
|
- });
|
|
1496
|
|
-
|
|
1497
|
|
- conference.room.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_FAILED,
|
|
1498
|
|
- function (e, pc) {
|
|
1499
|
|
- conference.statistics.sendSetLocalDescFailed(e, pc);
|
|
1500
|
|
- }
|
|
1501
|
|
- );
|
|
1502
|
|
-
|
|
1503
|
|
- conference.room.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_FAILED,
|
|
1504
|
|
- function (e, pc) {
|
|
1505
|
|
- conference.statistics.sendSetRemoteDescFailed(e, pc);
|
|
1506
|
|
- }
|
|
1507
|
|
- );
|
|
1508
|
|
-
|
|
1509
|
|
- conference.room.addListener(XMPPEvents.ADD_ICE_CANDIDATE_FAILED,
|
|
1510
|
|
- function (e, pc) {
|
|
1511
|
|
- conference.statistics.sendAddIceCandidateFailed(e, pc);
|
|
1512
|
|
- }
|
|
1513
|
|
- );
|
|
1514
|
|
- }
|
|
1515
|
|
-}
|
|
|
1215
|
+JitsiConference.prototype._isFocus = function (mucJid) {
|
|
|
1216
|
+ return this.room.isFocus(mucJid);
|
|
|
1217
|
+};
|
|
1516
|
1218
|
|
|
|
1219
|
+/**
|
|
|
1220
|
+ * Fires CONFERENCE_FAILED event with INCOMPATIBLE_SERVER_VERSIONS parameter
|
|
|
1221
|
+ */
|
|
|
1222
|
+JitsiConference.prototype._fireIncompatibleVersionsEvent = function () {
|
|
|
1223
|
+ this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,
|
|
|
1224
|
+ JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS);
|
|
|
1225
|
+};
|
|
1517
|
1226
|
|
|
1518
|
1227
|
module.exports = JitsiConference;
|