Преглед на файлове

Merge pull request #298 from bgrozev/conn-quality

Conn quality
dev1
Дамян Минков преди 9 години
родител
ревизия
4c103b0d21

+ 32
- 6
JitsiConference.js Целия файл

@@ -21,6 +21,7 @@ var Transcriber = require("./modules/transcription/transcriber");
21 21
 import ParticipantConnectionStatus
22 22
     from "./modules/connectivity/ParticipantConnectionStatus";
23 23
 import TalkMutedDetection from "./modules/TalkMutedDetection";
24
+import ConnectionQuality from "./modules/connectivity/ConnectionQuality";
24 25
 
25 26
 /**
26 27
  * Creates a JitsiConference object with the given name and properties.
@@ -64,6 +65,20 @@ function JitsiConference(options) {
64 65
     // We need to know if the potential issue happened before or after
65 66
     // the restart.
66 67
     this.wasStopped = false;
68
+
69
+    /**
70
+     * The object which monitors local and remote connection statistics (e.g.
71
+     * sending bitrate) and calculates a number which represents the connection
72
+     * quality.
73
+     */
74
+    this.connectionQuality
75
+        = new ConnectionQuality(this, this.eventEmitter, options);
76
+
77
+    /**
78
+     * Indicates whether the connection is interrupted or not.
79
+     */
80
+    this.connectionIsInterrupted = false;
81
+
67 82
 }
68 83
 
69 84
 /**
@@ -229,17 +244,24 @@ JitsiConference.prototype.getExternalAuthUrl = function (urlForPopup) {
229 244
 };
230 245
 
231 246
 /**
232
- * Returns the local tracks.
247
+ * Returns the local tracks of the given media type, or all local tracks if no
248
+ * specific type is given.
249
+ * @param mediaType {MediaType} Optional media type (audio or video).
233 250
  */
234
-JitsiConference.prototype.getLocalTracks = function () {
251
+JitsiConference.prototype.getLocalTracks = function (mediaType) {
252
+    let tracks = [];
235 253
     if (this.rtc) {
236
-        return this.rtc.localTracks.slice();
237
-    } else {
238
-        return [];
254
+        tracks = this.rtc.localTracks.slice();
239 255
     }
256
+    if (mediaType !== undefined) {
257
+        tracks = tracks.filter(
258
+            (track) => {
259
+                return track && track.getType && track.getType() === mediaType;
260
+            });
261
+    }
262
+    return tracks;
240 263
 };
241 264
 
242
-
243 265
 /**
244 266
  * Attaches a handler for events(For example - "participant joined".) in the conference. All possible event are defined
245 267
  * in JitsiConferenceEvents.
@@ -1247,4 +1269,8 @@ JitsiConference.prototype.broadcastEndpointMessage = function (payload) {
1247 1269
     this.sendEndpointMessage("", payload);
1248 1270
 };
1249 1271
 
1272
+JitsiConference.prototype.isConnectionInterrupted = function () {
1273
+    return this.connectionIsInterrupted;
1274
+};
1275
+
1250 1276
 module.exports = JitsiConference;

+ 18
- 13
JitsiConferenceEventManager.js Целия файл

@@ -99,19 +99,22 @@ JitsiConferenceEventManager.prototype.setupChatRoomListeners = function () {
99 99
         JitsiConferenceEvents.CONFERENCE_JOINED);
100 100
     // send some analytics events
101 101
     chatRoom.addListener(XMPPEvents.MUC_JOINED,
102
-        function ()
103
-        {
104
-            for (var ckey in chatRoom.connectionTimes){
105
-                var cvalue = chatRoom.connectionTimes[ckey];
106
-                Statistics.analytics.sendEvent('conference.' + ckey,
107
-                    {value: cvalue});
102
+        () => {
103
+            let key, value;
104
+
105
+            this.conference.connectionIsInterrupted = false;
106
+
107
+            for (key in chatRoom.connectionTimes){
108
+                value = chatRoom.connectionTimes[key];
109
+                Statistics.analytics.sendEvent('conference.' + key,
110
+                    {value: value});
108 111
             }
109
-            for (var xkey in chatRoom.xmpp.connectionTimes){
110
-                var xvalue = chatRoom.xmpp.connectionTimes[xkey];
111
-                Statistics.analytics.sendEvent('xmpp.' + xkey,
112
-                    {value: xvalue});
112
+            for (key in chatRoom.xmpp.connectionTimes){
113
+                value = chatRoom.xmpp.connectionTimes[key];
114
+                Statistics.analytics.sendEvent('xmpp.' + key,
115
+                    {value: value});
113 116
             }
114
-        });
117
+    });
115 118
 
116 119
     this.chatRoomForwarder.forward(XMPPEvents.ROOM_JOIN_ERROR,
117 120
         JitsiConferenceEvents.CONFERENCE_FAILED,
@@ -195,8 +198,9 @@ JitsiConferenceEventManager.prototype.setupChatRoomListeners = function () {
195 198
     this.chatRoomForwarder.forward(XMPPEvents.CONNECTION_INTERRUPTED,
196 199
         JitsiConferenceEvents.CONNECTION_INTERRUPTED);
197 200
     chatRoom.addListener(XMPPEvents.CONNECTION_INTERRUPTED,
198
-        function () {
201
+        () => {
199 202
             Statistics.sendEventToAll('connection.interrupted');
203
+            this.conference.connectionIsInterrupted = true;
200 204
         });
201 205
 
202 206
     this.chatRoomForwarder.forward(XMPPEvents.RECORDER_STATE_CHANGED,
@@ -208,8 +212,9 @@ JitsiConferenceEventManager.prototype.setupChatRoomListeners = function () {
208 212
     this.chatRoomForwarder.forward(XMPPEvents.CONNECTION_RESTORED,
209 213
         JitsiConferenceEvents.CONNECTION_RESTORED);
210 214
     chatRoom.addListener(XMPPEvents.CONNECTION_RESTORED,
211
-        function () {
215
+        () => {
212 216
             Statistics.sendEventToAll('connection.restored');
217
+            this.conference.connectionIsInterrupted = false;
213 218
         });
214 219
 
215 220
     this.chatRoomForwarder.forward(XMPPEvents.CONFERENCE_SETUP_FAILED,

+ 1
- 0
JitsiConferenceEvents.js Целия файл

@@ -42,6 +42,7 @@ export const CONNECTION_INTERRUPTED = "conference.connectionInterrupted";
42 42
 export const CONNECTION_RESTORED = "conference.connectionRestored";
43 43
 /**
44 44
  * New local connection statistics are received.
45
+ * @deprecated Use ConnectionQualityEvents.LOCAL_STATS_UPDATED instead.
45 46
  */
46 47
 export const CONNECTION_STATS = "conference.connectionStats";
47 48
 /**

+ 3
- 1
JitsiMeetJS.js Целия файл

@@ -7,6 +7,7 @@ import * as JitsiConferenceEvents from "./JitsiConferenceEvents";
7 7
 import * as JitsiConnectionErrors from "./JitsiConnectionErrors";
8 8
 import * as JitsiConnectionEvents from "./JitsiConnectionEvents";
9 9
 import * as JitsiMediaDevicesEvents from "./JitsiMediaDevicesEvents";
10
+import * as ConnectionQualityEvents from "./service/connectivity/ConnectionQualityEvents";
10 11
 import JitsiTrackError from "./JitsiTrackError";
11 12
 import * as JitsiTrackErrors from "./JitsiTrackErrors";
12 13
 import * as JitsiTrackEvents from "./JitsiTrackEvents";
@@ -76,7 +77,8 @@ var LibJitsiMeet = {
76 77
         conference: JitsiConferenceEvents,
77 78
         connection: JitsiConnectionEvents,
78 79
         track: JitsiTrackEvents,
79
-        mediaDevices: JitsiMediaDevicesEvents
80
+        mediaDevices: JitsiMediaDevicesEvents,
81
+        connectionQuality: ConnectionQualityEvents
80 82
     },
81 83
     errors: {
82 84
         conference: JitsiConferenceErrors,

+ 8
- 0
modules/RTC/RTCBrowserType.js Целия файл

@@ -147,6 +147,14 @@ var RTCBrowserType = {
147 147
      */
148 148
     isAndroid: function() {
149 149
         return isAndroid;
150
+    },
151
+
152
+    /**
153
+     * Whether jitsi-meet supports simulcast on the current browser.
154
+     * @returns {boolean}
155
+     */
156
+    supportsSimulcast: function() {
157
+        return RTCBrowserType.isChrome();
150 158
     }
151 159
 
152 160
     // Add version getters for other browsers when needed

+ 435
- 0
modules/connectivity/ConnectionQuality.js Целия файл

@@ -0,0 +1,435 @@
1
+import * as ConnectionQualityEvents
2
+    from "../../service/connectivity/ConnectionQualityEvents";
3
+import * as ConferenceEvents from "../../JitsiConferenceEvents";
4
+import {getLogger} from "jitsi-meet-logger";
5
+import RTCBrowserType from "../RTC/RTCBrowserType";
6
+
7
+var XMPPEvents = require('../../service/xmpp/XMPPEvents');
8
+var MediaType = require('../../service/RTC/MediaType');
9
+var VideoType = require('../../service/RTC/VideoType');
10
+var Resolutions = require("../../service/RTC/Resolutions");
11
+
12
+const logger = getLogger(__filename);
13
+
14
+/**
15
+ * The value to use for the "type" field for messages sent by ConnectionQuality
16
+ * over the data channel.
17
+ */
18
+const STATS_MESSAGE_TYPE = "stats";
19
+
20
+/**
21
+ * See media/engine/simulcast.ss from webrtc.org
22
+ */
23
+const kSimulcastFormats = [
24
+    { width: 1920, height: 1080, layers:3, max: 5000, target: 4000, min: 800 },
25
+    { width: 1280, height: 720,  layers:3, max: 2500, target: 2500, min: 600 },
26
+    { width: 960,  height: 540,  layers:3, max: 900,  target: 900, min: 450 },
27
+    { width: 640,  height: 360,  layers:2, max: 700,  target: 500, min: 150 },
28
+    { width: 480,  height: 270,  layers:2, max: 450,  target: 350, min: 150 },
29
+    { width: 320,  height: 180,  layers:1, max: 200,  target: 150, min: 30 }
30
+];
31
+
32
+/**
33
+ * The initial bitrate for video in kbps.
34
+ */
35
+var startBitrate = 800;
36
+
37
+/**
38
+ * Gets the expected bitrate (in kbps) in perfect network conditions.
39
+ * @param simulcast {boolean} whether simulcast is enabled or not.
40
+ * @param resolution {Resolution} the resolution.
41
+ * @param millisSinceStart {number} the number of milliseconds since sending
42
+ * video started.
43
+ */
44
+function getTarget(simulcast, resolution, millisSinceStart) {
45
+    // Completely ignore the bitrate in the first 5 seconds, as the first
46
+    // event seems to fire very early and the value is suspicious and causes
47
+    // false positives.
48
+    if (millisSinceStart < 5000) {
49
+        return 1;
50
+    }
51
+
52
+    let target = 0;
53
+    let height = Math.min(resolution.height, resolution.width);
54
+
55
+    if (simulcast) {
56
+        // Find the first format with height no bigger than ours.
57
+        let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);
58
+        if (simulcastFormat) {
59
+            // Sum the target fields from all simulcast layers for the given
60
+            // resolution (e.g. 720p + 360p + 180p).
61
+            for (height = simulcastFormat.height; height >= 180; height /=2) {
62
+                simulcastFormat
63
+                    = kSimulcastFormats.find(f => f.height == height);
64
+                if (simulcastFormat) {
65
+                    target += simulcastFormat.target;
66
+                } else {
67
+                    break;
68
+                }
69
+            }
70
+        }
71
+    } else {
72
+        // See GetMaxDefaultVideoBitrateKbps in
73
+        // media/engine/webrtcvideoengine2.cc from webrtc.org
74
+        let pixels = resolution.width * resolution.height;
75
+        if (pixels <= 320 * 240) {
76
+            target = 600;
77
+        } else if (pixels <= 640 * 480) {
78
+            target =  1700;
79
+        } else if (pixels <= 960 * 540) {
80
+            target = 2000;
81
+        } else {
82
+            target = 2500;
83
+        }
84
+    }
85
+
86
+    // Allow for an additional 1 second for ramp up -- delay any initial drop
87
+    // of connection quality by 1 second.
88
+    return Math.min(target, rampUp(Math.max(0, millisSinceStart - 1000)));
89
+}
90
+
91
+/**
92
+ * Gets the bitrate to which GCC would have ramped up in perfect network
93
+ * conditions after millisSinceStart milliseconds.
94
+ * @param millisSinceStart {number} the number of milliseconds since sending
95
+ * video was enabled.
96
+ */
97
+function rampUp(millisSinceStart) {
98
+    if (millisSinceStart > 60000) {
99
+        return Number.MAX_SAFE_INTEGER;
100
+    }
101
+    // According to GCC the send side bandwidth estimation grows with at most
102
+    // 8% per second.
103
+    // https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.5
104
+    return startBitrate * Math.pow(1.08, millisSinceStart / 1000);
105
+}
106
+
107
+/**
108
+ * A class which monitors the local statistics coming from the RTC modules, and
109
+ * calculates a "connection quality" value, in percent, for the media
110
+ * connection. A value of 100% indicates a very good network connection, and a
111
+ * value of 0% indicates a poor connection.
112
+ */
113
+export default class ConnectionQuality {
114
+    constructor(conference, eventEmitter, options) {
115
+        this.eventEmitter = eventEmitter;
116
+
117
+        /**
118
+         * The owning JitsiConference.
119
+         */
120
+        this._conference = conference;
121
+
122
+        /**
123
+         * Whether simulcast is supported. Note that even if supported, it is
124
+         * currently not used for screensharing, which is why we have an
125
+         * additional check.
126
+         */
127
+        this._simulcast
128
+            = !options.disableSimulcast && RTCBrowserType.supportsSimulcast();
129
+
130
+        /**
131
+         * Holds statistics about the local connection quality.
132
+         */
133
+        this._localStats = {connectionQuality: 100};
134
+
135
+        /**
136
+         * The time this._localStats.connectionQuality was last updated.
137
+         */
138
+        this._lastConnectionQualityUpdate = -1;
139
+
140
+        /**
141
+         * Maps a participant ID to an object holding connection quality
142
+         * statistics received from this participant.
143
+         */
144
+        this._remoteStats = {};
145
+
146
+        /**
147
+         * The time that the ICE state last changed to CONNECTED. We use this
148
+         * to calculate how much time we as a sender have had to ramp-up.
149
+         */
150
+        this._timeIceConnected = -1;
151
+
152
+        /**
153
+         * The time that local video was unmuted. We use this to calculate how
154
+         * much time we as a sender have had to ramp-up.
155
+         */
156
+        this._timeVideoUnmuted = -1;
157
+
158
+
159
+        // We assume a global startBitrate value for the sake of simplicity.
160
+        if (options.startBitrate && options.startBitrate > 0) {
161
+            startBitrate = options.startBitrate;
162
+        }
163
+
164
+        // TODO: consider ignoring these events and letting the user of
165
+        // lib-jitsi-meet handle these separately.
166
+        conference.on(
167
+            ConferenceEvents.CONNECTION_INTERRUPTED,
168
+            () => {
169
+                this._updateLocalConnectionQuality(0);
170
+                this.eventEmitter.emit(
171
+                    ConnectionQualityEvents.LOCAL_STATS_UPDATED,
172
+                    this._localStats);
173
+                this._broadcastLocalStats();
174
+            });
175
+
176
+        conference.room.addListener(
177
+            XMPPEvents.ICE_CONNECTION_STATE_CHANGED,
178
+            (newState) => {
179
+                if (newState === 'connected') {
180
+                    this._timeIceConnected = window.performance.now();
181
+                }
182
+            });
183
+
184
+        // Listen to DataChannel message from other participants in the
185
+        // conference, and update the _remoteStats field accordingly.
186
+        conference.on(
187
+            ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
188
+            (participant, payload) => {
189
+                if (payload.type === STATS_MESSAGE_TYPE) {
190
+                    this._updateRemoteStats(
191
+                        participant.getId(), payload.values);
192
+                }
193
+        });
194
+
195
+        // Listen to local statistics events originating from the RTC module
196
+        // and update the _localStats field.
197
+        // Oh, and by the way, the resolutions of all remote participants are
198
+        // also piggy-backed in these "local" statistics. It's obvious, really,
199
+        // if one carefully reads the *code* (but not the docs) in
200
+        // UI/VideoLayout/VideoLayout.js#updateLocalConnectionStats in
201
+        // jitsi-meet
202
+        // TODO: We should keep track of the remote resolution in _remoteStats,
203
+        // and notify about changes via separate events.
204
+        conference.on(
205
+            ConferenceEvents.CONNECTION_STATS,
206
+            this._updateLocalStats.bind(this));
207
+
208
+        // Save the last time we were unmuted.
209
+        conference.on(
210
+            ConferenceEvents.TRACK_MUTE_CHANGED,
211
+            (track) => {
212
+                if (track.isVideoTrack()) {
213
+                    if (track.isMuted()) {
214
+                        this._timeVideoUnmuted = -1;
215
+                    } else {
216
+                        this._maybeUpdateUnmuteTime();
217
+                    }
218
+                }
219
+            });
220
+        conference.on(
221
+            ConferenceEvents.TRACK_ADDED,
222
+            (track) => {
223
+                if (track.isVideoTrack() && !track.isMuted())
224
+                {
225
+                    this._maybeUpdateUnmuteTime();
226
+                }
227
+            });
228
+    }
229
+
230
+    /**
231
+     * Sets _timeVideoUnmuted if it was previously unset. If it was already set,
232
+     * doesn't change it.
233
+     */
234
+    _maybeUpdateUnmuteTime() {
235
+        if (this._timeVideoUnmuted < 0) {
236
+            this._timeVideoUnmuted = window.performance.now();
237
+        }
238
+    }
239
+
240
+    /**
241
+     * Calculates a new "connection quality" value.
242
+     * @param videoType {VideoType} the type of the video source (camera or
243
+     * a screen capture).
244
+     * @param isMuted {boolean} whether the local video is muted.
245
+     * @param resolutionName {Resolution} the input resolution used by the
246
+     * camera.
247
+     * @returns {*} the newly calculated connection quality.
248
+     */
249
+    _calculateConnectionQuality(videoType, isMuted, resolutionName) {
250
+
251
+        // resolutionName is an index into Resolutions (where "720" is
252
+        // "1280x720" and "960" is "960x720" ...).
253
+        let resolution = Resolutions[resolutionName];
254
+
255
+        let quality = 100;
256
+
257
+        if (isMuted || !resolution || videoType === VideoType.DESKTOP
258
+            || this._timeIceConnected < 0
259
+            || this._timeVideoUnmuted < 0) {
260
+
261
+            // Calculate a value based on packet loss only.
262
+            if (!this._localStats.packetLoss
263
+                || this._localStats.packetLoss.total === undefined) {
264
+                logger.error("Cannot calculate connection quality, unknown "
265
+                    + "packet loss.");
266
+                quality = 100;
267
+            } else {
268
+                let loss = this._localStats.packetLoss.total;
269
+                if (loss <= 2) {
270
+                    quality = 100;
271
+                } else if (loss <= 4) {
272
+                    quality = 70; // 4 bars
273
+                } else if (loss <= 6) {
274
+                    quality = 50; // 3 bars
275
+                } else if (loss <= 8) {
276
+                    quality = 30; // 2 bars
277
+                } else if (loss <= 12) {
278
+                    quality = 10; // 1 bars
279
+                } else {
280
+                    quality = 0; // Still 1 bar, but slower climb-up.
281
+                }
282
+            }
283
+        } else {
284
+            // Calculate a value based on the sending bitrate.
285
+
286
+            // time since sending of video was enabled.
287
+            let millisSinceStart = window.performance.now()
288
+                    - Math.max(this._timeVideoUnmuted, this._timeIceConnected);
289
+
290
+            // expected sending bitrate in perfect conditions
291
+            let target
292
+                = getTarget(this._simulcast, resolution, millisSinceStart);
293
+            target = 0.9 * target;
294
+
295
+            quality = 100 * this._localStats.bitrate.upload / target;
296
+
297
+            // Whatever the bitrate, drop early if there is significant loss
298
+            if (this._localStats.packetLoss
299
+                && this._localStats.packetLoss.total >= 10) {
300
+                quality = Math.min(quality, 30);
301
+            }
302
+        }
303
+
304
+        // Make sure that the quality doesn't climb quickly
305
+        if (this._lastConnectionQualityUpdate > 0)
306
+        {
307
+            let maxIncreasePerSecond = 2;
308
+            let prevConnectionQuality = this._localStats.connectionQuality;
309
+            let diffSeconds
310
+                = (window.performance.now()
311
+                    - this._lastConnectionQualityUpdate) / 1000;
312
+            quality = Math.min(
313
+                quality,
314
+                prevConnectionQuality + diffSeconds * maxIncreasePerSecond);
315
+        }
316
+
317
+        return Math.min(100, quality);
318
+    }
319
+
320
+    /**
321
+     * Updates the localConnectionQuality value
322
+     * @param values {number} the new value. Should be in [0, 100].
323
+     */
324
+    _updateLocalConnectionQuality(value) {
325
+        this._localStats.connectionQuality = value;
326
+        this._lastConnectionQualityUpdate = window.performance.now();
327
+    }
328
+
329
+    /**
330
+     * Broadcasts the local statistics to all other participants in the
331
+     * conference.
332
+     */
333
+    _broadcastLocalStats() {
334
+        // Send only the data that remote participants care about.
335
+        let data = {
336
+            bitrate: this._localStats.bitrate,
337
+            packetLoss: this._localStats.packetLoss,
338
+            connectionQuality: this._localStats.connectionQuality
339
+        };
340
+
341
+        // TODO: It looks like the remote participants don't really "care"
342
+        // about the resolution, and they look at their local rendered
343
+        // resolution instead. Consider removing this.
344
+        let localVideoTrack
345
+            = this._conference.getLocalTracks(MediaType.VIDEO)
346
+                .find(track => track.isVideoTrack());
347
+        if (localVideoTrack && localVideoTrack.resolution) {
348
+            data.resolution = localVideoTrack.resolution;
349
+        }
350
+
351
+        try {
352
+            this._conference.broadcastEndpointMessage({
353
+                type: STATS_MESSAGE_TYPE,
354
+                values: data });
355
+        } catch (e) {
356
+            // We often hit this in the beginning of a call, before the data
357
+            // channel is ready. It is not a big problem, because we will
358
+            // send the statistics again after a few seconds, and the error is
359
+            // already logged elsewhere. So just ignore it.
360
+
361
+            //let errorMsg = "Failed to broadcast local stats";
362
+            //logger.error(errorMsg, e);
363
+            //GlobalOnErrorHandler.callErrorHandler(
364
+            //    new Error(errorMsg + ": " + e));
365
+        }
366
+    }
367
+
368
+    /**
369
+     * Updates the local statistics
370
+     * @param data new statistics
371
+     */
372
+    _updateLocalStats(data) {
373
+        let key;
374
+        let updateLocalConnectionQuality
375
+            = !this._conference.isConnectionInterrupted();
376
+        let localVideoTrack =
377
+                this._conference.getLocalTracks(MediaType.VIDEO)
378
+                    .find(track => track.isVideoTrack());
379
+        let videoType = localVideoTrack ? localVideoTrack.videoType : undefined;
380
+        let isMuted = localVideoTrack ? localVideoTrack.isMuted() : true;
381
+        let resolution = localVideoTrack ? localVideoTrack.resolution : null;
382
+
383
+        if (!isMuted) {
384
+            this._maybeUpdateUnmuteTime();
385
+        }
386
+
387
+        // Copy the fields already in 'data'.
388
+        for (key in data) {
389
+            if (data.hasOwnProperty(key)) {
390
+                this._localStats[key] = data[key];
391
+            }
392
+        }
393
+
394
+        // And re-calculate the connectionQuality field.
395
+        if (updateLocalConnectionQuality) {
396
+            this._updateLocalConnectionQuality(
397
+                this._calculateConnectionQuality(
398
+                    videoType,
399
+                    isMuted,
400
+                    resolution));
401
+        }
402
+
403
+        this.eventEmitter.emit(
404
+            ConnectionQualityEvents.LOCAL_STATS_UPDATED,
405
+            this._localStats);
406
+        this._broadcastLocalStats();
407
+    }
408
+
409
+    /**
410
+     * Updates remote statistics
411
+     * @param id the id of the remote participant
412
+     * @param data the statistics received
413
+     */
414
+    _updateRemoteStats(id, data) {
415
+            // Use only the fields we need
416
+            this._remoteStats[id] = {
417
+                bitrate: data.bitrate,
418
+                packetLoss: data.packetLoss,
419
+                connectionQuality: data.connectionQuality
420
+            };
421
+
422
+            this.eventEmitter.emit(
423
+                ConnectionQualityEvents.REMOTE_STATS_UPDATED,
424
+                id,
425
+                this._remoteStats[id]);
426
+    }
427
+
428
+    /**
429
+     * Returns the local statistics.
430
+     * Exported only for use in jitsi-meet-torture.
431
+     */
432
+    getStats() {
433
+        return this._localStats;
434
+    }
435
+}

+ 4
- 1
modules/xmpp/JingleSessionPC.js Целия файл

@@ -142,6 +142,9 @@ JingleSessionPC.prototype.doInitialize = function () {
142 142
                     ":\t", now);
143 143
         Statistics.analytics.sendEvent(
144 144
             'ice.' + self.peerconnection.iceConnectionState, {value: now});
145
+        self.room.eventEmitter.emit(
146
+            XMPPEvents.ICE_CONNECTION_STATE_CHANGED,
147
+            self.peerconnection.iceConnectionState);
145 148
         switch (self.peerconnection.iceConnectionState) {
146 149
             case 'connected':
147 150
 
@@ -813,7 +816,7 @@ JingleSessionPC.prototype._modifySources = function (successCallback, queueCallb
813 816
      * optional error through (1) logger, (2) GlobalOnErrorHandler, and (3)
814 817
      * queueCallback.
815 818
      *
816
-     * @param {string} errmsg the error messsage to report
819
+     * @param {string} errmsg the error message to report
817 820
      * @param {*} error an optional error to report in addition to errmsg
818 821
      */
819 822
     function reportError(errmsg, err) {

+ 0
- 7
service/connectionquality/CQEvents.js Целия файл

@@ -1,7 +0,0 @@
1
-var CQEvents = {
2
-    LOCALSTATS_UPDATED: "cq.localstats_updated",
3
-    REMOTESTATS_UPDATED: "cq.remotestats_updated",
4
-    STOP: "cq.stop"
5
-};
6
-
7
-module.exports = CQEvents;

+ 10
- 0
service/connectivity/ConnectionQualityEvents.js Целия файл

@@ -0,0 +1,10 @@
1
+/**
2
+ * Indicates that the local connection statistics were updated.
3
+ */
4
+export const LOCAL_STATS_UPDATED = "cq.local_stats_updated";
5
+
6
+/**
7
+ * Indicates that the connection statistics for a particular remote participant
8
+ * were updated.
9
+ */
10
+export const REMOTE_STATS_UPDATED = "cq.remote_stats_updated";

+ 4
- 1
service/xmpp/XMPPEvents.js Целия файл

@@ -183,6 +183,9 @@ var XMPPEvents = {
183 183
     LOCAL_UFRAG_CHANGED: "xmpp.local_ufrag_changed",
184 184
     // Designates an event indicating that the local ICE username fragment of
185 185
     // the jingle session has changed.
186
-    REMOTE_UFRAG_CHANGED: "xmpp.remote_ufrag_changed"
186
+    REMOTE_UFRAG_CHANGED: "xmpp.remote_ufrag_changed",
187
+    // Designates an event indicating that the local ICE connection state has
188
+    // changed.
189
+    ICE_CONNECTION_STATE_CHANGED: "xmpp.ice_connection_state_changed"
187 190
 };
188 191
 module.exports = XMPPEvents;

Loading…
Отказ
Запис