Browse Source

feat(es) migrate statistics/statistics to ES6 Class

master
Naman Jain 4 months ago
parent
commit
772ab8d771
No account linked to committer's email address
2 changed files with 384 additions and 306 deletions
  1. 1
    12
      JitsiConference.js
  2. 383
    294
      modules/statistics/statistics.js

+ 1
- 12
JitsiConference.js View File

@@ -901,16 +901,6 @@ JitsiConference.prototype.getLocalVideoTracks = function() {
901 901
     return this.rtc ? this.rtc.getLocalVideoTracks() : null;
902 902
 };
903 903
 
904
-/**
905
- * Obtains the performance statistics.
906
- * @returns {Object|null}
907
- */
908
-JitsiConference.prototype.getPerformanceStats = function() {
909
-    return {
910
-        longTasksStats: this.statistics.getLongTasksStats()
911
-    };
912
-};
913
-
914 904
 /**
915 905
  * Attaches a handler for events(For example - "participant joined".) in the
916 906
  * conference. All possible event are defined in JitsiConferenceEvents.
@@ -3907,8 +3897,7 @@ JitsiConference.prototype._sendConferenceLeftAnalyticsEvent = function() {
3907 3897
         meetingId,
3908 3898
         participantId: `${meetingId}.${this._statsCurrentId}`,
3909 3899
         stats: {
3910
-            duration: Math.floor((Date.now() - this._conferenceJoinAnalyticsEventSent) / 1000),
3911
-            perf: this.getPerformanceStats()
3900
+            duration: Math.floor((Date.now() - this._conferenceJoinAnalyticsEventSent) / 1000)
3912 3901
         }
3913 3902
     }));
3914 3903
 };

+ 383
- 294
modules/statistics/statistics.js View File

@@ -13,368 +13,457 @@ import RTPStats from './RTPStatsCollector';
13 13
 const logger = require('@jitsi/logger').getLogger('modules/statistics/statistics');
14 14
 
15 15
 /**
16
- * Stores all active {@link Statistics} instances.
17
- * @type {Set<Statistics>}
16
+ * Statistics class provides various functionality related to collecting and reporting statistics.
18 17
  */
19
-let _instances;
18
+export default class Statistics {
20 19
 
21
-/**
22
- * Init statistic options
23
- * @param options
24
- */
25
-Statistics.init = function(options) {
26
-    Statistics.audioLevelsEnabled = !options.disableAudioLevels;
27
-    if (typeof options.pcStatsInterval === 'number') {
28
-        Statistics.pcStatsInterval = options.pcStatsInterval;
29
-    }
20
+    /**
21
+     * Stores all active Statistics instances.
22
+     * @type {Set<Statistics>}
23
+     */
24
+    static _instances;
30 25
 
31
-    if (typeof options.audioLevelsInterval === 'number') {
32
-        Statistics.audioLevelsInterval = options.audioLevelsInterval;
26
+    /**
27
+     * Static getter for instances property
28
+     * Returns the Set holding all active Statistics instances. Lazily
29
+     * initializes the Set to allow any Set polyfills to be applied.
30
+     * @type {Set<Statistics>}
31
+     */
32
+    static get instances() {
33
+        if (!Statistics._instances) {
34
+            Statistics._instances = new Set();
35
+        }
36
+
37
+        return Statistics._instances;
33 38
     }
34 39
 
35
-    Statistics.disableThirdPartyRequests = options.disableThirdPartyRequests;
40
+    /**
41
+     * Flag indicating whether audio levels are enabled or not.
42
+     * @static
43
+     * @type {boolean}
44
+     */
45
+    static audioLevelsEnabled = false;
36 46
 
37
-    LocalStats.init();
38
-    WatchRTC.init(options);
39
-};
47
+    /**
48
+     * The interval for audio levels stats collection.
49
+     * @static
50
+     * @type {number}
51
+     */
52
+    static audioLevelsInterval = 200;
40 53
 
41
-/**
42
- * The options to configure Statistics.
43
- * @typedef {Object} StatisticsOptions
44
- * @property {string} userName - The user name to use
45
- * @property {string} roomName - The room name we are currently in.
46
- *
47
- * @param {JitsiConference} conference - The conference instance from which the statistics were initialized.
48
- * @param {StatisticsOptions} options - The options to use creating the
49
- * Statistics.
50
- */
51
-export default function Statistics(conference, options) {
52 54
     /**
53
-     * {@link RTPStats} mapped by {@link TraceablePeerConnection.id} which
54
-     * collect RTP statistics for each peerconnection.
55
-     * @type {Map<string, RTPStats}
55
+     * The interval for peer connection stats collection.
56
+     * @static
57
+     * @type {number}
56 58
      */
57
-    this.rtpStatsMap = new Map();
58
-    this.eventEmitter = new EventEmitter();
59
-    this.conference = conference;
60
-    this.xmpp = conference?.xmpp;
61
-    this.options = options || {};
59
+    static pcStatsInterval = 10000;
62 60
 
63
-    Statistics.instances.add(this);
61
+    /**
62
+     * Flag indicating whether third party requests are disabled.
63
+     * @static
64
+     * @type {boolean}
65
+     */
66
+    static disableThirdPartyRequests = false;
64 67
 
65
-    RTCStats.attachToConference(this.conference);
68
+    /**
69
+     * Analytics adapter for sending events.
70
+     * @static
71
+     * @type {Object}
72
+     */
73
+    static analytics = analytics;
66 74
 
67
-    // WatchRTC is not required to work for react native
68
-    if (!browser.isReactNative()) {
69
-        WatchRTC.start(this.options.roomName, this.options.userName);
70
-    }
75
+    /**
76
+     * Array holding local statistics collectors.
77
+     * @static
78
+     * @type {Array}
79
+     */
80
+    static localStats = [];
71 81
 
72
-}
73
-Statistics.audioLevelsEnabled = false;
74
-Statistics.audioLevelsInterval = 200;
75
-Statistics.pcStatsInterval = 10000;
76
-Statistics.disableThirdPartyRequests = false;
77
-Statistics.analytics = analytics;
82
+    /**
83
+     * Local JID constant.
84
+     * @static
85
+     * @type {string}
86
+     */
87
+    static LOCAL_JID = require('../../service/statistics/constants').LOCAL_JID;
78 88
 
79
-Object.defineProperty(Statistics, 'instances', {
80 89
     /**
81
-     * Returns the Set holding all active {@link Statistics} instances. Lazily
82
-     * initializes the Set to allow any Set polyfills to be applied.
83
-     * @type {Set<Statistics>}
90
+     * Init statistic options
91
+     * @static
92
+     * @param {Object} options - The options to initialize statistics with
84 93
      */
85
-    get() {
86
-        if (!_instances) {
87
-            _instances = new Set();
94
+    static init(options) {
95
+        Statistics.audioLevelsEnabled = !options.disableAudioLevels;
96
+        if (typeof options.pcStatsInterval === 'number') {
97
+            Statistics.pcStatsInterval = options.pcStatsInterval;
88 98
         }
89 99
 
90
-        return _instances;
91
-    }
92
-});
100
+        if (typeof options.audioLevelsInterval === 'number') {
101
+            Statistics.audioLevelsInterval = options.audioLevelsInterval;
102
+        }
93 103
 
94
-/**
95
- * Starts collecting RTP stats for given peerconnection.
96
- * @param {TraceablePeerConnection} peerconnection
97
- */
98
-Statistics.prototype.startRemoteStats = function(peerconnection) {
99
-    this.stopRemoteStats(peerconnection);
100
-
101
-    try {
102
-        const rtpStats
103
-            = new RTPStats(
104
-                peerconnection,
105
-                Statistics.audioLevelsInterval,
106
-                Statistics.pcStatsInterval,
107
-                this.eventEmitter);
108
-
109
-        rtpStats.start(Statistics.audioLevelsEnabled);
110
-        this.rtpStatsMap.set(peerconnection.id, rtpStats);
111
-    } catch (e) {
112
-        logger.error(`Failed to start collecting remote statistics: ${e}`);
113
-    }
114
-};
104
+        if (typeof options.longTasksStatsInterval === 'number') {
105
+            Statistics.longTasksStatsInterval = options.longTasksStatsInterval;
106
+        }
115 107
 
116
-Statistics.localStats = [];
108
+        Statistics.disableThirdPartyRequests = options.disableThirdPartyRequests;
117 109
 
118
-Statistics.startLocalStats = function(track, callback) {
119
-    if (browser.isIosBrowser()) {
120
-        // On iOS browsers audio is lost if the audio input device is in use by another app
121
-        // https://bugs.webkit.org/show_bug.cgi?id=233473
122
-        // The culprit was using the AudioContext, so now we close the AudioContext during
123
-        // the track being muted, and re-instantiate it afterwards.
124
-        track.addEventListener(
125
-        JitsiTrackEvents.NO_DATA_FROM_SOURCE,
110
+        LocalStats.init();
111
+        WatchRTC.init(options);
112
+    }
126 113
 
127
-        /**
128
-         * Closes AudioContext on no audio data, and enables it on data received again.
129
-         *
130
-         * @param {boolean} value - Whether we receive audio data or not.
131
-         */
132
-        async value => {
133
-            if (value) {
134
-                for (const localStat of Statistics.localStats) {
135
-                    localStat.stop();
114
+    /**
115
+     * Starts collecting local statistics for a track.
116
+     * @static
117
+     * @param {JitsiTrack} track - The track to collect statistics for
118
+     * @param {Function} callback - The callback to invoke with audio levels
119
+     */
120
+    static startLocalStats(track, callback) {
121
+        if (browser.isIosBrowser()) {
122
+            // On iOS browsers audio is lost if the audio input device is in use by another app
123
+            // https://bugs.webkit.org/show_bug.cgi?id=233473
124
+            // The culprit was using the AudioContext, so now we close the AudioContext during
125
+            // the track being muted, and re-instantiate it afterwards.
126
+            track.addEventListener(
127
+            JitsiTrackEvents.NO_DATA_FROM_SOURCE,
128
+
129
+            /**
130
+             * Closes AudioContext on no audio data, and enables it on data received again.
131
+             *
132
+             * @param {boolean} value - Whether we receive audio data or not.
133
+             */
134
+            async value => {
135
+                if (value) {
136
+                    for (const localStat of Statistics.localStats) {
137
+                        localStat.stop();
138
+                    }
139
+
140
+                    await LocalStats.disconnectAudioContext();
141
+                } else {
142
+                    LocalStats.connectAudioContext();
143
+                    for (const localStat of Statistics.localStats) {
144
+                        localStat.start();
145
+                    }
136 146
                 }
147
+            });
148
+        }
137 149
 
138
-                await LocalStats.disconnectAudioContext();
139
-            } else {
140
-                LocalStats.connectAudioContext();
141
-                for (const localStat of Statistics.localStats) {
142
-                    localStat.start();
143
-                }
144
-            }
145
-        });
146
-    }
150
+        if (!Statistics.audioLevelsEnabled) {
151
+            return;
152
+        }
147 153
 
148
-    if (!Statistics.audioLevelsEnabled) {
149
-        return;
150
-    }
154
+        track.addEventListener(
155
+            JitsiTrackEvents.LOCAL_TRACK_STOPPED,
156
+            () => {
157
+                Statistics.stopLocalStats(track);
158
+            });
151 159
 
152
-    track.addEventListener(
153
-        JitsiTrackEvents.LOCAL_TRACK_STOPPED,
154
-        () => {
155
-            Statistics.stopLocalStats(track);
156
-        });
160
+        const stream = track.getOriginalStream();
161
+        const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,
162
+            callback);
157 163
 
158
-    const stream = track.getOriginalStream();
159
-    const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,
160
-        callback);
164
+        this.localStats.push(localStats);
165
+        localStats.start();
166
+    }
161 167
 
162
-    this.localStats.push(localStats);
163
-    localStats.start();
164
-};
168
+    /**
169
+     * Stops collecting local statistics for a track.
170
+     * @static
171
+     * @param {JitsiTrack} track - The track to stop collecting statistics for
172
+     */
173
+    static stopLocalStats(track) {
174
+        if (!Statistics.audioLevelsEnabled) {
175
+            return;
176
+        }
165 177
 
166
-Statistics.prototype.addAudioLevelListener = function(listener) {
167
-    if (!Statistics.audioLevelsEnabled) {
168
-        return;
169
-    }
170
-    this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);
171
-};
178
+        const stream = track.getOriginalStream();
172 179
 
173
-Statistics.prototype.removeAudioLevelListener = function(listener) {
174
-    if (!Statistics.audioLevelsEnabled) {
175
-        return;
180
+        for (let i = 0; i < Statistics.localStats.length; i++) {
181
+            if (Statistics.localStats[i].stream === stream) {
182
+                const localStats = Statistics.localStats.splice(i, 1);
183
+
184
+                localStats[0].stop();
185
+                break;
186
+            }
187
+        }
176 188
     }
177
-    this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
178
-};
179 189
 
180
-Statistics.prototype.addBeforeDisposedListener = function(listener) {
181
-    this.eventEmitter.on(StatisticsEvents.BEFORE_DISPOSED, listener);
182
-};
190
+    /**
191
+     * Sends event to analytics and logs a message to the logger/console.
192
+     * @static
193
+     * @param {string | Object} event - The event name, or an object which represents the entire event
194
+     * @param {Object} properties - Properties to attach to the event
195
+     */
196
+    static sendAnalyticsAndLog(event, properties = {}) {
197
+        if (!event) {
198
+            logger.warn('No event or event name given.');
183 199
 
184
-Statistics.prototype.removeBeforeDisposedListener = function(listener) {
185
-    this.eventEmitter.removeListener(
186
-        StatisticsEvents.BEFORE_DISPOSED, listener);
187
-};
200
+            return;
201
+        }
188 202
 
189
-Statistics.prototype.addConnectionStatsListener = function(listener) {
190
-    this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);
191
-};
203
+        let eventToLog;
192 204
 
193
-Statistics.prototype.removeConnectionStatsListener = function(listener) {
194
-    this.eventEmitter.removeListener(
195
-        StatisticsEvents.CONNECTION_STATS,
196
-        listener);
197
-};
205
+        // Also support an API with a single object as an event.
206
+        if (typeof event === 'object') {
207
+            eventToLog = event;
208
+        } else {
209
+            eventToLog = {
210
+                name: event,
211
+                properties
212
+            };
213
+        }
198 214
 
199
-Statistics.prototype.addEncodeTimeStatsListener = function(listener) {
200
-    this.eventEmitter.on(StatisticsEvents.ENCODE_TIME_STATS, listener);
201
-};
215
+        logger.debug(JSON.stringify(eventToLog));
202 216
 
203
-Statistics.prototype.removeEncodeTimeStatsListener = function(listener) {
204
-    this.eventEmitter.removeListener(StatisticsEvents.ENCODE_TIME_STATS, listener);
205
-};
217
+        // We do this last, because it may modify the object which is passed.
218
+        this.analytics.sendEvent(event, properties);
219
+    }
206 220
 
207
-Statistics.prototype.addByteSentStatsListener = function(listener) {
208
-    this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);
209
-};
221
+    /**
222
+     * Sends event to analytics.
223
+     * @static
224
+     * @param {string | Object} eventName - The event name, or an object which represents the entire event
225
+     * @param {Object} properties - Properties to attach to the event
226
+     */
227
+    static sendAnalytics(eventName, properties = {}) {
228
+        this.analytics.sendEvent(eventName, properties);
229
+    }
210 230
 
211
-Statistics.prototype.removeByteSentStatsListener = function(listener) {
212
-    this.eventEmitter.removeListener(StatisticsEvents.BYTE_SENT_STATS,
213
-        listener);
214
-};
231
+    /**
232
+     * The options to configure Statistics.
233
+     * @typedef {Object} StatisticsOptions
234
+     * @property {string} userName - The user name to use
235
+     * @property {string} roomName - The room name we are currently in.
236
+     *
237
+     * @param {JitsiConference} conference - The conference instance from which the statistics were initialized.
238
+     * @param {StatisticsOptions} options - The options to use creating the
239
+     * Statistics.
240
+     */
241
+    constructor(conference, options) {
242
+        /**
243
+         * {@link RTPStats} mapped by {@link TraceablePeerConnection.id} which
244
+         * collect RTP statistics for each peerconnection.
245
+         * @type {Map<string, RTPStats}
246
+         */
247
+        this.rtpStatsMap = new Map();
248
+        this.eventEmitter = new EventEmitter();
249
+        this.conference = conference;
250
+        this.xmpp = conference?.xmpp;
251
+        this.options = options || {};
215 252
 
216
-/**
217
- * Add a listener that would be notified on a LONG_TASKS_STATS event.
218
- *
219
- * @param {Function} listener a function that would be called when notified.
220
- * @returns {void}
221
- */
222
-Statistics.prototype.addLongTasksStatsListener = function(listener) {
223
-    this.eventEmitter.on(StatisticsEvents.LONG_TASKS_STATS, listener);
224
-};
253
+        Statistics.instances.add(this);
225 254
 
226
-/**
227
- * Obtains the current value of the LongTasks event statistics.
228
- *
229
- * @returns {Object|null} stats object if the observer has been
230
- * created, null otherwise.
231
- */
232
-Statistics.prototype.getLongTasksStats = function() {
233
-    return this.performanceObserverStats
234
-        ? this.performanceObserverStats.getLongTasksStats()
235
-        : null;
236
-};
255
+        RTCStats.attachToConference(this.conference);
237 256
 
238
-/**
239
- * Removes the given listener for the LONG_TASKS_STATS event.
240
- *
241
- * @param {Function} listener the listener we want to remove.
242
- * @returns {void}
243
- */
244
-Statistics.prototype.removeLongTasksStatsListener = function(listener) {
245
-    this.eventEmitter.removeListener(StatisticsEvents.LONG_TASKS_STATS, listener);
246
-};
247
-
248
-/**
249
- * Updates the list of speakers for which the audio levels are to be calculated. This is needed for the jvb pc only.
250
- *
251
- * @param {Array<string>} speakerList The list of remote endpoint ids.
252
- * @returns {void}
253
- */
254
-Statistics.prototype.setSpeakerList = function(speakerList) {
255
-    for (const rtpStats of Array.from(this.rtpStatsMap.values())) {
256
-        if (!rtpStats.peerconnection.isP2P) {
257
-            rtpStats.setSpeakerList(speakerList);
257
+        // WatchRTC is not required to work for react native
258
+        if (!browser.isReactNative()) {
259
+            WatchRTC.start(this.options.roomName, this.options.userName);
258 260
         }
259 261
     }
260
-};
261 262
 
262
-Statistics.prototype.dispose = function() {
263
-    try {
264
-        this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);
263
+    /**
264
+     * Starts collecting RTP stats for given peerconnection.
265
+     * @param {TraceablePeerConnection} peerconnection
266
+     */
267
+    startRemoteStats(peerconnection) {
268
+        this.stopRemoteStats(peerconnection);
269
+
270
+        try {
271
+            const rtpStats
272
+                = new RTPStats(
273
+                    peerconnection,
274
+                    Statistics.audioLevelsInterval,
275
+                    Statistics.pcStatsInterval,
276
+                    this.eventEmitter);
277
+
278
+            rtpStats.start(Statistics.audioLevelsEnabled);
279
+            this.rtpStatsMap.set(peerconnection.id, rtpStats);
280
+        } catch (e) {
281
+            logger.error(`Failed to start collecting remote statistics: ${e}`);
282
+        }
283
+    }
265 284
 
266
-        for (const tpcId of this.rtpStatsMap.keys()) {
267
-            this._stopRemoteStats(tpcId);
285
+    /**
286
+     * Adds a listener for audio level events.
287
+     * @param {Function} listener - The listener to add
288
+     */
289
+    addAudioLevelListener(listener) {
290
+        if (!Statistics.audioLevelsEnabled) {
291
+            return;
268 292
         }
269
-        if (this.eventEmitter) {
270
-            this.eventEmitter.removeAllListeners();
293
+        this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);
294
+    }
295
+
296
+    /**
297
+     * Removes an audio level listener.
298
+     * @param {Function} listener - The listener to remove
299
+     */
300
+    removeAudioLevelListener(listener) {
301
+        if (!Statistics.audioLevelsEnabled) {
302
+            return;
271 303
         }
272
-    } finally {
273
-        Statistics.instances.delete(this);
304
+        this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
274 305
     }
275
-};
276 306
 
277
-Statistics.stopLocalStats = function(track) {
278
-    if (!Statistics.audioLevelsEnabled) {
279
-        return;
307
+    /**
308
+     * Adds a listener for before disposed events.
309
+     * @param {Function} listener - The listener to add
310
+     */
311
+    addBeforeDisposedListener(listener) {
312
+        this.eventEmitter.on(StatisticsEvents.BEFORE_DISPOSED, listener);
280 313
     }
281 314
 
282
-    const stream = track.getOriginalStream();
315
+    /**
316
+     * Removes a before disposed listener.
317
+     * @param {Function} listener - The listener to remove
318
+     */
319
+    removeBeforeDisposedListener(listener) {
320
+        this.eventEmitter.removeListener(
321
+            StatisticsEvents.BEFORE_DISPOSED, listener);
322
+    }
283 323
 
284
-    for (let i = 0; i < Statistics.localStats.length; i++) {
285
-        if (Statistics.localStats[i].stream === stream) {
286
-            const localStats = Statistics.localStats.splice(i, 1);
324
+    /**
325
+     * Adds a listener for connection stats events.
326
+     * @param {Function} listener - The listener to add
327
+     */
328
+    addConnectionStatsListener(listener) {
329
+        this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);
330
+    }
287 331
 
288
-            localStats[0].stop();
289
-            break;
290
-        }
332
+    /**
333
+     * Removes a connection stats listener.
334
+     * @param {Function} listener - The listener to remove
335
+     */
336
+    removeConnectionStatsListener(listener) {
337
+        this.eventEmitter.removeListener(
338
+            StatisticsEvents.CONNECTION_STATS,
339
+            listener);
291 340
     }
292
-};
293 341
 
294
-/**
295
- * Stops remote RTP stats for given peerconnection ID.
296
- * @param {string} tpcId {@link TraceablePeerConnection.id}
297
- * @private
298
- */
299
-Statistics.prototype._stopRemoteStats = function(tpcId) {
300
-    const rtpStats = this.rtpStatsMap.get(tpcId);
342
+    /**
343
+     * Adds a listener for encode time stats events.
344
+     * @param {Function} listener - The listener to add
345
+     */
346
+    addEncodeTimeStatsListener(listener) {
347
+        this.eventEmitter.on(StatisticsEvents.ENCODE_TIME_STATS, listener);
348
+    }
301 349
 
302
-    if (rtpStats) {
303
-        rtpStats.stop();
304
-        this.rtpStatsMap.delete(tpcId);
350
+    /**
351
+     * Removes an encode time stats listener.
352
+     * @param {Function} listener - The listener to remove
353
+     */
354
+    removeEncodeTimeStatsListener(listener) {
355
+        this.eventEmitter.removeListener(StatisticsEvents.ENCODE_TIME_STATS, listener);
305 356
     }
306
-};
307 357
 
308
-/**
309
- * Stops collecting RTP stats for given peerconnection
310
- * @param {TraceablePeerConnection} tpc
311
- */
312
-Statistics.prototype.stopRemoteStats = function(tpc) {
313
-    this._stopRemoteStats(tpc.id);
314
-};
358
+    /**
359
+     * Adds a listener for byte sent stats events.
360
+     * @param {Function} listener - The listener to add
361
+     */
362
+    addByteSentStatsListener(listener) {
363
+        this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);
364
+    }
315 365
 
316
-/**
317
- * Sends the given feedback
318
- *
319
- * @param overall an integer between 1 and 5 indicating the user's rating.
320
- * @param comment the comment from the user.
321
- * @returns {Promise} Resolves immediately.
322
- */
323
-Statistics.prototype.sendFeedback = function(overall, comment) {
324
-    // Statistics.analytics.sendEvent is currently fire and forget, without
325
-    // confirmation of successful send.
326
-    Statistics.analytics.sendEvent(
327
-        FEEDBACK,
328
-        {
329
-            rating: overall,
330
-            comment
331
-        });
366
+    /**
367
+     * Removes a byte sent stats listener.
368
+     * @param {Function} listener - The listener to remove
369
+     */
370
+    removeByteSentStatsListener(listener) {
371
+        this.eventEmitter.removeListener(StatisticsEvents.BYTE_SENT_STATS,
372
+            listener);
373
+    }
332 374
 
333
-    return Promise.resolve();
334
-};
375
+    /**
376
+     * Add a listener that would be notified on a LONG_TASKS_STATS event.
377
+     *
378
+     * @param {Function} listener a function that would be called when notified.
379
+     * @returns {void}
380
+     */
381
+    addLongTasksStatsListener(listener) {
382
+        this.eventEmitter.on(StatisticsEvents.LONG_TASKS_STATS, listener);
383
+    }
335 384
 
336
-Statistics.LOCAL_JID = require('../../service/statistics/constants').LOCAL_JID;
385
+    /**
386
+     * Removes the given listener for the LONG_TASKS_STATS event.
387
+     *
388
+     * @param {Function} listener the listener we want to remove.
389
+     * @returns {void}
390
+     */
391
+    removeLongTasksStatsListener(listener) {
392
+        this.eventEmitter.removeListener(StatisticsEvents.LONG_TASKS_STATS, listener);
393
+    }
337 394
 
338
-/**
339
- * Sends event to analytics and logs a message to the logger/console.
340
- *
341
- * @param {string | Object} event the event name, or an object which
342
- * represents the entire event.
343
- * @param {Object} properties properties to attach to the event (if an event
344
- * name as opposed to an event object is provided).
345
- */
346
-Statistics.sendAnalyticsAndLog = function(event, properties = {}) {
347
-    if (!event) {
348
-        logger.warn('No event or event name given.');
395
+    /**
396
+     * Updates the list of speakers for which the audio levels are to be calculated. This is needed for the jvb pc only.
397
+     *
398
+     * @param {Array<string>} speakerList The list of remote endpoint ids.
399
+     * @returns {void}
400
+     */
401
+    setSpeakerList(speakerList) {
402
+        for (const rtpStats of Array.from(this.rtpStatsMap.values())) {
403
+            if (!rtpStats.peerconnection.isP2P) {
404
+                rtpStats.setSpeakerList(speakerList);
405
+            }
406
+        }
407
+    }
349 408
 
350
-        return;
409
+    /**
410
+     * Disposes of this instance, stopping any ongoing stats collection.
411
+     */
412
+    dispose() {
413
+        try {
414
+            this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);
415
+
416
+            for (const tpcId of this.rtpStatsMap.keys()) {
417
+                this._stopRemoteStats(tpcId);
418
+            }
419
+            if (this.eventEmitter) {
420
+                this.eventEmitter.removeAllListeners();
421
+            }
422
+        } finally {
423
+            Statistics.instances.delete(this);
424
+        }
351 425
     }
352 426
 
353
-    let eventToLog;
427
+    /**
428
+     * Stops remote RTP stats for given peerconnection ID.
429
+     * @param {string} tpcId {@link TraceablePeerConnection.id}
430
+     * @private
431
+     */
432
+    _stopRemoteStats(tpcId) {
433
+        const rtpStats = this.rtpStatsMap.get(tpcId);
354 434
 
355
-    // Also support an API with a single object as an event.
356
-    if (typeof event === 'object') {
357
-        eventToLog = event;
358
-    } else {
359
-        eventToLog = {
360
-            name: event,
361
-            properties
362
-        };
435
+        if (rtpStats) {
436
+            rtpStats.stop();
437
+            this.rtpStatsMap.delete(tpcId);
438
+        }
363 439
     }
364 440
 
365
-    logger.debug(JSON.stringify(eventToLog));
441
+    /**
442
+     * Stops collecting RTP stats for given peerconnection
443
+     * @param {TraceablePeerConnection} tpc
444
+     */
445
+    stopRemoteStats(tpc) {
446
+        this._stopRemoteStats(tpc.id);
447
+    }
366 448
 
367
-    // We do this last, because it may modify the object which is passed.
368
-    this.analytics.sendEvent(event, properties);
369
-};
449
+    /**
450
+     * Sends the given feedback
451
+     *
452
+     * @param {number} overall an integer between 1 and 5 indicating the user's rating.
453
+     * @param {string} comment the comment from the user.
454
+     * @returns {Promise} Resolves immediately.
455
+     */
456
+    sendFeedback(overall, comment) {
457
+        // Statistics.analytics.sendEvent is currently fire and forget, without
458
+        // confirmation of successful send.
459
+        Statistics.analytics.sendEvent(
460
+            FEEDBACK,
461
+            {
462
+                rating: overall,
463
+                comment
464
+            });
465
+
466
+        return Promise.resolve();
467
+    }
468
+}
370 469
 
371
-/**
372
- * Sends event to analytics.
373
- *
374
- * @param {string | Object} eventName the event name, or an object which
375
- * represents the entire event.
376
- * @param {Object} properties properties to attach to the event
377
- */
378
-Statistics.sendAnalytics = function(eventName, properties = {}) {
379
-    this.analytics.sendEvent(eventName, properties);
380
-};

Loading…
Cancel
Save