Преглед изворни кода

Merge pull request #150 from jitsi/cs-devices-2

Logs active devices to callstats (updated).
dev1
hristoterezov пре 9 година
родитељ
комит
b4ae228962

+ 20
- 8
JitsiConference.js Прегледај датотеку

@@ -331,6 +331,16 @@ JitsiConference.prototype.addTrack = function (track) {
331 331
     this.room.addListener(XMPPEvents.SENDRECV_STREAMS_CHANGED,
332 332
         track.ssrcHandler);
333 333
 
334
+    // Report active device to statistics
335
+    var devices = RTC.getCurrentlyAvailableMediaDevices();
336
+    device = devices.find(function (d) {
337
+        return d.kind === track.getTrack().kind + 'input'
338
+            && d.label === track.getTrack().label;
339
+    });
340
+
341
+    Statistics.sendАctiveDeviceListEvent(
342
+        RTC.getEventDataForActiveDevice(device));
343
+
334 344
     return new Promise(function (resolve, reject) {
335 345
         this.room.addStream(track.getOriginalStream(), function () {
336 346
             if (track.isVideoTrack()) {
@@ -1088,6 +1098,16 @@ function setupListeners(conference) {
1088 1098
 
1089 1099
     conference.room.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, function (role) {
1090 1100
         conference.eventEmitter.emit(JitsiConferenceEvents.USER_ROLE_CHANGED, conference.myUserId(), role);
1101
+
1102
+        // log all events for the recorder operated by the moderator
1103
+        if (conference.statistics && conference.isModerator()) {
1104
+            conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,
1105
+                function (status, error) {
1106
+                    Statistics.sendLog(
1107
+                        "[Recorder] status: " + status
1108
+                            + (error? " error: " + error : ""));
1109
+                });
1110
+        }
1091 1111
     });
1092 1112
     conference.room.addListener(XMPPEvents.MUC_ROLE_CHANGED, conference.onUserRoleChanged.bind(conference));
1093 1113
 
@@ -1341,14 +1361,6 @@ function setupListeners(conference) {
1341 1361
                 conference.statistics.sendMuteEvent(track.isMuted(), type);
1342 1362
             });
1343 1363
 
1344
-        // log all events for the recorder operated by the moderator
1345
-        if (conference.isModerator()) {
1346
-            conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,
1347
-                function (status, error) {
1348
-                    conference.statistics.sendLog("Recorder: " + status);
1349
-                });
1350
-        }
1351
-
1352 1364
         conference.room.addListener(XMPPEvents.CREATE_OFFER_FAILED, function (e, pc) {
1353 1365
             conference.statistics.sendCreateOfferFailed(e, pc);
1354 1366
         });

+ 32
- 0
JitsiMediaDevices.js Прегледај датотеку

@@ -3,6 +3,7 @@ var RTCEvents = require('./service/RTC/RTCEvents');
3 3
 var RTC = require("./modules/RTC/RTC");
4 4
 var MediaType = require('./service/RTC/MediaType');
5 5
 var JitsiMediaDevicesEvents = require('./JitsiMediaDevicesEvents');
6
+var Statistics = require("./modules/statistics/statistics");
6 7
 
7 8
 var eventEmitter = new EventEmitter();
8 9
 
@@ -11,6 +12,28 @@ RTC.addListener(RTCEvents.DEVICE_LIST_CHANGED,
11 12
         eventEmitter.emit(JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED, devices);
12 13
     });
13 14
 
15
+RTC.addListener(RTCEvents.DEVICE_LIST_AVAILABLE,
16
+    function (devices) {
17
+        // log output device
18
+        logOutputDevice(
19
+            JitsiMediaDevices.getAudioOutputDevice(),
20
+            devices);
21
+    });
22
+
23
+/**
24
+ * Gathers data and sends it to statistics.
25
+ * @param deviceID the device id to log
26
+ * @param devices list of devices
27
+ */
28
+function logOutputDevice (deviceID, devices) {
29
+    var device = devices.find(function (d) {
30
+        return d.kind === 'audiooutput' && d.deviceId === deviceID;
31
+    });
32
+
33
+    Statistics.sendАctiveDeviceListEvent(
34
+        RTC.getEventDataForActiveDevice(device));
35
+}
36
+
14 37
 var JitsiMediaDevices = {
15 38
     /**
16 39
      * Executes callback with list of media devices connected.
@@ -71,6 +94,15 @@ var JitsiMediaDevices = {
71 94
      *      otherwise
72 95
      */
73 96
     setAudioOutputDevice: function (deviceId) {
97
+
98
+        if (RTC.getCurrentlyAvailableMediaDevices().length > 0)
99
+        {
100
+            // if we have devices info report device to stats
101
+            // normally this will not happen on startup as this method is called
102
+            // too early. This will happen only on user selection of new device
103
+            logOutputDevice(deviceId, RTC.getCurrentlyAvailableMediaDevices());
104
+        }
105
+
74 106
         return RTC.setAudioOutputDevice(deviceId);
75 107
     },
76 108
     /**

+ 17
- 0
modules/RTC/RTC.js Прегледај датотеку

@@ -345,6 +345,23 @@ RTC.getAudioOutputDevice = function () {
345 345
     return RTCUtils.getAudioOutputDevice();
346 346
 };
347 347
 
348
+/**
349
+ * Returns list of available media devices if its obtained, otherwise an
350
+ * empty array is returned/
351
+ * @returns {Array} list of available media devices.
352
+ */
353
+RTC.getCurrentlyAvailableMediaDevices = function () {
354
+    return RTCUtils.getCurrentlyAvailableMediaDevices();
355
+};
356
+
357
+/**
358
+ * Returns event data for device to be reported to stats.
359
+ * @returns {MediaDeviceInfo} device.
360
+ */
361
+RTC.getEventDataForActiveDevice = function (device) {
362
+    return RTCUtils.getEventDataForActiveDevice(device);
363
+};
364
+
348 365
 /**
349 366
  * Sets current audio output device.
350 367
  * @param {string} deviceId - id of 'audiooutput' device from

+ 42
- 5
modules/RTC/RTCUtils.js Прегледај датотеку

@@ -386,12 +386,24 @@ function onReady (options, GUM) {
386 386
     eventEmitter.emit(RTCEvents.RTC_READY, true);
387 387
     screenObtainer.init(options, GUM);
388 388
 
389
-    if (isDeviceChangeEventSupported && RTCUtils.isDeviceListAvailable()) {
390
-        navigator.mediaDevices.addEventListener('devicechange', function () {
391
-            RTCUtils.enumerateDevices(onMediaDevicesListChanged);
389
+    if (RTCUtils.isDeviceListAvailable() && rawEnumerateDevicesWithCallback) {
390
+        rawEnumerateDevicesWithCallback(function (devices) {
391
+            currentlyAvailableMediaDevices = devices.splice(0);
392
+
393
+            eventEmitter.emit(RTCEvents.DEVICE_LIST_AVAILABLE,
394
+                currentlyAvailableMediaDevices);
395
+
396
+            if (isDeviceChangeEventSupported) {
397
+                navigator.mediaDevices.addEventListener(
398
+                    'devicechange',
399
+                    function () {
400
+                        RTCUtils.enumerateDevices(
401
+                            onMediaDevicesListChanged);
402
+                    });
403
+            } else {
404
+                pollForAvailableMediaDevices();
405
+            }
392 406
         });
393
-    } else if (RTCUtils.isDeviceListAvailable()) {
394
-        pollForAvailableMediaDevices();
395 407
     }
396 408
 }
397 409
 
@@ -1207,6 +1219,31 @@ var RTCUtils = {
1207 1219
      */
1208 1220
     getAudioOutputDevice: function () {
1209 1221
         return audioOutputDeviceId;
1222
+    },
1223
+
1224
+    /**
1225
+     * Returns list of available media devices if its obtained, otherwise an
1226
+     * empty array is returned/
1227
+     * @returns {Array} list of available media devices.
1228
+     */
1229
+    getCurrentlyAvailableMediaDevices: function () {
1230
+        return currentlyAvailableMediaDevices;
1231
+    },
1232
+
1233
+    /**
1234
+     * Returns event data for device to be reported to stats.
1235
+     * @returns {MediaDeviceInfo} device.
1236
+     */
1237
+    getEventDataForActiveDevice: function (device) {
1238
+        var devices = [];
1239
+        var deviceData = {
1240
+            "deviceId": device.deviceId,
1241
+            "kind":     device.kind,
1242
+            "label":    device.label,
1243
+            "groupId":  device.groupId
1244
+        };
1245
+        devices.push(deviceData);
1246
+        return { deviceList: devices };
1210 1247
     }
1211 1248
 };
1212 1249
 

+ 93
- 34
modules/statistics/CallStats.js Прегледај датотеку

@@ -20,7 +20,7 @@ var wrtcFuncNames = {
20 20
     getUserMedia:         "getUserMedia",
21 21
     iceConnectionFailure: "iceConnectionFailure",
22 22
     signalingError:       "signalingError",
23
-    applicationError:     "applicationError"
23
+    applicationLog:       "applicationLog"
24 24
 };
25 25
 
26 26
 /**
@@ -41,7 +41,8 @@ var fabricEvent = {
41 41
     fabricTerminated:"fabricTerminated",
42 42
     screenShareStart:"screenShareStart",
43 43
     screenShareStop:"screenShareStop",
44
-    dominantSpeaker:"dominantSpeaker"
44
+    dominantSpeaker:"dominantSpeaker",
45
+    activeDeviceList:"activeDeviceList"
45 46
 };
46 47
 
47 48
 var callStats = null;
@@ -53,20 +54,47 @@ function initCallback (err, msg) {
53 54
     if (err !== 'success')
54 55
         return;
55 56
 
57
+    CallStats.initialized = true;
58
+
59
+    var ret = callStats.addNewFabric(this.peerconnection,
60
+        Strophe.getResourceFromJid(this.session.peerjid),
61
+        callStats.fabricUsage.multiplex,
62
+        this.confID,
63
+        this.pcCallback.bind(this));
64
+
65
+    var fabricInitialized = (ret.status === 'success');
66
+
67
+    if(!fabricInitialized)
68
+        console.log("callstats fabric not initilized", ret.message);
69
+
56 70
     // notify callstats about failures if there were any
57 71
     if (CallStats.reportsQueue.length) {
58 72
         CallStats.reportsQueue.forEach(function (report) {
59
-            if (report.type === reportType.ERROR)
60
-            {
73
+            if (report.type === reportType.ERROR) {
61 74
                 var error = report.data;
62 75
                 CallStats._reportError.call(this, error.type, error.error,
63 76
                     error.pc);
64 77
             }
65
-            else if (report.type === reportType.EVENT)
66
-            {
67
-                var data = report.data;
78
+            // if we have and event to report and we failed to add fabric
79
+            // this event will not be reported anyway, returning an error
80
+            else if (report.type === reportType.EVENT
81
+                && fabricInitialized) {
82
+                var eventData = report.data;
68 83
                 callStats.sendFabricEvent(
69
-                    this.peerconnection, data.event, this.confID);
84
+                    this.peerconnection,
85
+                    eventData.event,
86
+                    this.confID,
87
+                    eventData.eventData);
88
+            } else if (report.type === reportType.MST_WITH_USERID) {
89
+                var data = report.data;
90
+                callStats.associateMstWithUserID(
91
+                    this.peerconnection,
92
+                    data.callStatsId,
93
+                    this.confID,
94
+                    data.ssrc,
95
+                    data.usageLabel,
96
+                    data.containerId
97
+                );
70 98
             }
71 99
         }, this);
72 100
         CallStats.reportsQueue.length = 0;
@@ -123,11 +151,6 @@ var CallStats = _try_catch(function(jingleSession, Settings, options) {
123 151
             this.userID,
124 152
             initCallback.bind(this));
125 153
 
126
-        callStats.addNewFabric(this.peerconnection,
127
-            Strophe.getResourceFromJid(jingleSession.peerjid),
128
-            callStats.fabricUsage.multiplex,
129
-            this.confID,
130
-            this.pcCallback.bind(this));
131 154
     } catch (e) {
132 155
         // The callstats.io API failed to initialize (e.g. because its
133 156
         // download failed to succeed in general or on time). Further
@@ -143,13 +166,21 @@ var CallStats = _try_catch(function(jingleSession, Settings, options) {
143 166
 // and send them to callstats on init
144 167
 CallStats.reportsQueue = [];
145 168
 
169
+/**
170
+ * Whether the library was successfully initialized using its initialize method.
171
+ * And whether we had successfully called addNewFabric.
172
+ * @type {boolean}
173
+ */
174
+CallStats.initialized = false;
175
+
146 176
 /**
147 177
  * Type of pending reports, can be event or an error.
148 178
  * @type {{ERROR: string, EVENT: string}}
149 179
  */
150 180
 var reportType = {
151 181
     ERROR: "error",
152
-    EVENT: "event"
182
+    EVENT: "event",
183
+    MST_WITH_USERID: "mstWithUserID"
153 184
 };
154 185
 
155 186
 CallStats.prototype.pcCallback = _try_catch(function (err, msg) {
@@ -162,6 +193,7 @@ CallStats.prototype.pcCallback = _try_catch(function (err, msg) {
162 193
 /**
163 194
  * Lets CallStats module know where is given SSRC rendered by providing renderer
164 195
  * tag ID.
196
+ * If the lib is not initialized yet queue the call for later, when its ready.
165 197
  * @param ssrc {number} the SSRC of the stream
166 198
  * @param isLocal {boolean} <tt>true<tt> if this stream is local or
167 199
  *        <tt>false</tt> otherwise.
@@ -191,14 +223,27 @@ function (ssrc, isLocal, usageLabel, containerId) {
191 223
             usageLabel,
192 224
             containerId
193 225
         );
194
-        callStats.associateMstWithUserID(
195
-            this.peerconnection,
196
-            callStatsId,
197
-            this.confID,
198
-            ssrc,
199
-            usageLabel,
200
-            containerId
201
-        );
226
+        if(CallStats.initialized) {
227
+            callStats.associateMstWithUserID(
228
+                this.peerconnection,
229
+                callStatsId,
230
+                this.confID,
231
+                ssrc,
232
+                usageLabel,
233
+                containerId
234
+            );
235
+        }
236
+        else {
237
+            CallStats.reportsQueue.push({
238
+                type: reportType.MST_WITH_USERID,
239
+                data: {
240
+                    callStatsId: callStatsId,
241
+                    ssrc: ssrc,
242
+                    usageLabel: usageLabel,
243
+                    containerId: containerId
244
+                }
245
+            });
246
+        }
202 247
     }).bind(this)();
203 248
 };
204 249
 
@@ -206,6 +251,7 @@ function (ssrc, isLocal, usageLabel, containerId) {
206 251
  * Notifies CallStats for mute events
207 252
  * @param mute {boolean} true for muted and false for not muted
208 253
  * @param type {String} "audio"/"video"
254
+ * @param {CallStats} cs callstats instance related to the event
209 255
  */
210 256
 CallStats.sendMuteEvent = _try_catch(function (mute, type, cs) {
211 257
 
@@ -224,6 +270,7 @@ CallStats.sendMuteEvent = _try_catch(function (mute, type, cs) {
224 270
  * Notifies CallStats for screen sharing events
225 271
  * @param start {boolean} true for starting screen sharing and
226 272
  * false for not stopping
273
+ * @param {CallStats} cs callstats instance related to the event
227 274
  */
228 275
 CallStats.sendScreenSharingEvent = _try_catch(function (start, cs) {
229 276
 
@@ -233,6 +280,7 @@ CallStats.sendScreenSharingEvent = _try_catch(function (start, cs) {
233 280
 
234 281
 /**
235 282
  * Notifies CallStats that we are the new dominant speaker in the conference.
283
+ * @param {CallStats} cs callstats instance related to the event
236 284
  */
237 285
 CallStats.sendDominantSpeakerEvent = _try_catch(function (cs) {
238 286
 
@@ -240,21 +288,33 @@ CallStats.sendDominantSpeakerEvent = _try_catch(function (cs) {
240 288
         fabricEvent.dominantSpeaker);
241 289
 });
242 290
 
291
+/**
292
+ * Notifies CallStats about active device.
293
+ * @param {{deviceList: {String:String}}} list of devices with their data
294
+ * @param {CallStats} cs callstats instance related to the event
295
+ */
296
+CallStats.sendАctiveDeviceListEvent = _try_catch(function (devicesData, cs) {
297
+
298
+    CallStats._reportEvent.call(cs, fabricEvent.activeDeviceList, devicesData);
299
+});
300
+
243 301
 /**
244 302
  * Reports an error to callstats.
245 303
  *
246 304
  * @param type the type of the error, which will be one of the wrtcFuncNames
247 305
  * @param e the error
248 306
  * @param pc the peerconnection
307
+ * @param eventData additional data to pass to event
249 308
  * @private
250 309
  */
251
-CallStats._reportEvent = function (event) {
252
-    if (callStats) {
253
-        callStats.sendFabricEvent(this.peerconnection, event, this.confID);
310
+CallStats._reportEvent = function (event, eventData) {
311
+    if (CallStats.initialized) {
312
+        callStats.sendFabricEvent(
313
+            this.peerconnection, event, this.confID, eventData);
254 314
     } else {
255 315
         CallStats.reportsQueue.push({
256 316
                 type: reportType.EVENT,
257
-                data: {event: event}
317
+                data: {event: event, eventData: eventData}
258 318
             });
259 319
     }
260 320
 };
@@ -263,7 +323,7 @@ CallStats._reportEvent = function (event) {
263 323
  * Notifies CallStats for connection setup errors
264 324
  */
265 325
 CallStats.prototype.sendTerminateEvent = _try_catch(function () {
266
-    if(!callStats) {
326
+    if(!CallStats.initialized) {
267 327
         return;
268 328
     }
269 329
     callStats.sendFabricEvent(this.peerconnection,
@@ -289,7 +349,7 @@ CallStats.prototype.sendIceConnectionFailedEvent = _try_catch(function (pc, cs){
289 349
  */
290 350
 CallStats.prototype.sendFeedback = _try_catch(
291 351
 function(overallFeedback, detailedFeedback) {
292
-    if(!callStats) {
352
+    if(!CallStats.initialized) {
293 353
         return;
294 354
     }
295 355
     var feedbackString =    '{"userID":"' + this.userID + '"' +
@@ -314,7 +374,7 @@ CallStats._reportError = function (type, e, pc) {
314 374
         logger.warn("No error is passed!");
315 375
         e = new Error("Unknown error");
316 376
     }
317
-    if (callStats) {
377
+    if (CallStats.initialized) {
318 378
         callStats.reportError(pc, this.confID, type, e);
319 379
     } else {
320 380
         CallStats.reportsQueue.push({
@@ -391,15 +451,14 @@ CallStats.sendAddIceCandidateFailed = _try_catch(function (e, pc, cs) {
391 451
 });
392 452
 
393 453
 /**
394
- * Notifies CallStats that there is an unhandled error on the page.
454
+ * Notifies CallStats that there is a log we want to report.
395 455
  *
396
- * @param {Error} e error to send
397
- * @param {RTCPeerConnection} pc connection on which failure occured.
456
+ * @param {Error} e error to send or {String} message
398 457
  * @param {CallStats} cs callstats instance related to the error (optional)
399 458
  */
400
-CallStats.sendUnhandledError = _try_catch(function (e, cs) {
459
+CallStats.sendApplicationLog = _try_catch(function (e, cs) {
401 460
     CallStats._reportError
402
-        .call(cs, wrtcFuncNames.applicationError, e, null);
461
+        .call(cs, wrtcFuncNames.applicationLog, e, null);
403 462
 });
404 463
 
405 464
 module.exports = CallStats;

+ 20
- 26
modules/statistics/statistics.js Прегледај датотеку

@@ -251,6 +251,20 @@ Statistics.prototype.sendDominantSpeakerEvent = function () {
251 251
         CallStats.sendDominantSpeakerEvent(this.callstats);
252 252
 };
253 253
 
254
+/**
255
+ * Notifies about active device.
256
+ * @param {{deviceList: {String:String}}} list of devices with their data
257
+ */
258
+Statistics.sendАctiveDeviceListEvent = function (devicesData) {
259
+    if (Statistics.callsStatsInstances.length) {
260
+        Statistics.callsStatsInstances.forEach(function (cs) {
261
+            CallStats.sendАctiveDeviceListEvent(devicesData, cs);
262
+        });
263
+    } else {
264
+        CallStats.sendАctiveDeviceListEvent(devicesData, null);
265
+    }
266
+};
267
+
254 268
 /**
255 269
  * Lets the underlying statistics module know where is given SSRC rendered by
256 270
  * providing renderer tag ID.
@@ -350,40 +364,20 @@ Statistics.prototype.sendAddIceCandidateFailed = function (e, pc) {
350 364
 };
351 365
 
352 366
 /**
353
- * Notifies CallStats that there is unhandled exception.
367
+ * Adds to CallStats an application log.
354 368
  *
355
- * @param {Error} e error to send
369
+ * @param {String} a log message to send or an {Error} object to be reported
356 370
  */
357
-Statistics.sendUnhandledError = function (e) {
371
+Statistics.sendLog = function (m) {
358 372
     if (Statistics.callsStatsInstances.length) {
359 373
         Statistics.callsStatsInstances.forEach(function (cs) {
360
-            CallStats.sendUnhandledError(e, cs);
374
+            CallStats.sendApplicationLog(m, cs);
361 375
         });
362 376
     } else {
363
-        CallStats.sendUnhandledError(e, null);
377
+        CallStats.sendApplicationLog(m, null);
364 378
     }
365 379
 };
366 380
 
367
-/**
368
- * Adds to CallStats an application log.
369
- *
370
- * @param {String} a log message to send
371
- */
372
-Statistics.sendLog = function (m) {
373
-    // uses  the same field for cs stat as unhandled error
374
-    Statistics.sendUnhandledError(m);
375
-};
376
-
377
-/**
378
- * Adds to CallStats an application log.
379
- *
380
- * @param {String} a log message to send
381
- */
382
-Statistics.prototype.sendLog = function (m) {
383
-    // uses  the same field for cs stat as unhandled error
384
-    CallStats.sendUnhandledError(m, this.callstats);
385
-};
386
-
387 381
 /**
388 382
  * Sends the given feedback through CallStats.
389 383
  *
@@ -406,7 +400,7 @@ Statistics.reportGlobalError = function (error) {
406 400
     if (error instanceof JitsiTrackError && error.gum) {
407 401
         Statistics.sendGetUserMediaFailed(error);
408 402
     } else {
409
-        Statistics.sendUnhandledError(error);
403
+        Statistics.sendLog(error);
410 404
     }
411 405
 };
412 406
 

+ 2
- 1
service/RTC/RTCEvents.js Прегледај датотеку

@@ -7,7 +7,8 @@ var RTCEvents = {
7 7
     AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed",
8 8
     TRACK_ATTACHED: "rtc.track_attached",
9 9
     AUDIO_OUTPUT_DEVICE_CHANGED: "rtc.audio_output_device_changed",
10
-    DEVICE_LIST_CHANGED: "rtc.device_list_changed"
10
+    DEVICE_LIST_CHANGED: "rtc.device_list_changed",
11
+    DEVICE_LIST_AVAILABLE: "rtc.device_list_available"
11 12
 };
12 13
 
13 14
 module.exports = RTCEvents;

Loading…
Откажи
Сачувај