Kaynağa Gözat

Merge branch 'master' into reloads

dev1
hristoterezov 9 yıl önce
ebeveyn
işleme
a1d8f99560

+ 37
- 8
JitsiConference.js Dosyayı Görüntüle

@@ -364,6 +364,16 @@ JitsiConference.prototype.addTrack = function (track) {
364 364
     this.room.addListener(XMPPEvents.SENDRECV_STREAMS_CHANGED,
365 365
         track.ssrcHandler);
366 366
 
367
+    // Report active device to statistics
368
+    var devices = RTC.getCurrentlyAvailableMediaDevices();
369
+    device = devices.find(function (d) {
370
+        return d.kind === track.getTrack().kind + 'input'
371
+            && d.label === track.getTrack().label;
372
+    });
373
+
374
+    Statistics.sendАctiveDeviceListEvent(
375
+        RTC.getEventDataForActiveDevice(device));
376
+
367 377
     return new Promise(function (resolve, reject) {
368 378
         this.room.addStream(track.getOriginalStream(), function () {
369 379
             if (track.isVideoTrack()) {
@@ -1003,6 +1013,13 @@ JitsiConference.prototype.getConnectionTimes = function () {
1003 1013
     return this.room.connectionTimes;
1004 1014
 };
1005 1015
 
1016
+/**
1017
+ * Sets a property for the local participant.
1018
+ */
1019
+JitsiConference.prototype.setLocalParticipantProperty = function(name, value) {
1020
+    this.sendCommand("jitsi_participant_" + name, {value: value});
1021
+};
1022
+
1006 1023
 /**
1007 1024
  * Sends the given feedback through CallStats if enabled.
1008 1025
  *
@@ -1148,6 +1165,16 @@ function setupListeners(conference) {
1148 1165
     conference.room.addListener(XMPPEvents.FOCUS_LEFT, function () {
1149 1166
         conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.FOCUS_LEFT);
1150 1167
     });
1168
+    conference.room.setParticipantPropertyListener(function (node, from) {
1169
+        var participant = conference.getParticipantById(from);
1170
+        if (!participant) {
1171
+            return;
1172
+        }
1173
+
1174
+        participant.setProperty(
1175
+            node.tagName.substring("jitsi_participant_".length),
1176
+            node.value);
1177
+    });
1151 1178
 //    FIXME
1152 1179
 //    conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
1153 1180
 //        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
@@ -1164,6 +1191,16 @@ function setupListeners(conference) {
1164 1191
 
1165 1192
     conference.room.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, function (role) {
1166 1193
         conference.eventEmitter.emit(JitsiConferenceEvents.USER_ROLE_CHANGED, conference.myUserId(), role);
1194
+
1195
+        // log all events for the recorder operated by the moderator
1196
+        if (conference.statistics && conference.isModerator()) {
1197
+            conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,
1198
+                function (status, error) {
1199
+                    Statistics.sendLog(
1200
+                        "[Recorder] status: " + status
1201
+                            + (error? " error: " + error : ""));
1202
+                });
1203
+        }
1167 1204
     });
1168 1205
     conference.room.addListener(XMPPEvents.MUC_ROLE_CHANGED, conference.onUserRoleChanged.bind(conference));
1169 1206
 
@@ -1421,14 +1458,6 @@ function setupListeners(conference) {
1421 1458
                 conference.statistics.sendMuteEvent(track.isMuted(), type);
1422 1459
             });
1423 1460
 
1424
-        // log all events for the recorder operated by the moderator
1425
-        if (conference.isModerator()) {
1426
-            conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,
1427
-                function (status, error) {
1428
-                    conference.statistics.sendLog("Recorder: " + status);
1429
-                });
1430
-        }
1431
-
1432 1461
         conference.room.addListener(XMPPEvents.CREATE_OFFER_FAILED, function (e, pc) {
1433 1462
             conference.statistics.sendCreateOfferFailed(e, pc);
1434 1463
         });

+ 6
- 1
JitsiConferenceEvents.js Dosyayı Görüntüle

@@ -130,7 +130,12 @@ var JitsiConferenceEvents = {
130 130
     /**
131 131
      * Indicates that authentication status changed.
132 132
      */
133
-    AUTH_STATUS_CHANGED: "conference.auth_status_changed"
133
+    AUTH_STATUS_CHANGED: "conference.auth_status_changed",
134
+    /**
135
+     * Indicates that a the value of a specific property of a specific
136
+     * participant has changed.
137
+     */
138
+    PARTICIPANT_PROPERTY_CHANGED: "conference.participant_property_changed"
134 139
 };
135 140
 
136 141
 module.exports = JitsiConferenceEvents;

+ 39
- 0
JitsiMediaDevices.js Dosyayı Görüntüle

@@ -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
     /**
@@ -88,6 +120,13 @@ var JitsiMediaDevices = {
88 120
      */
89 121
     removeEventListener: function (event, handler) {
90 122
         eventEmitter.removeListener(event, handler);
123
+    },
124
+    /**
125
+     * Emits an event.
126
+     * @param {string} event - event name
127
+     */
128
+    emitEvent: function (event) {
129
+        eventEmitter.emit.apply(eventEmitter, arguments);
91 130
     }
92 131
 };
93 132
 

+ 11
- 1
JitsiMediaDevicesEvents.js Dosyayı Görüntüle

@@ -11,7 +11,17 @@ var JitsiMediaDevicesEvents = {
11 11
      *  MediaDeviceInfo-like objects that are currently connected.
12 12
      *  @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo
13 13
      */
14
-    DEVICE_LIST_CHANGED: "mediaDevices.devicechange"
14
+    DEVICE_LIST_CHANGED: "mediaDevices.devicechange",
15
+    /**
16
+     * Indicates that the environment is currently showing permission prompt to
17
+     * access camera and/or microphone. The event provides the following
18
+     * parameters to its listeners:
19
+     *
20
+     * @param {'chrome'|'opera'|'firefox'|'iexplorer'|'safari'|'nwjs'
21
+     *      |'react-native'|'android'} environmentType - type of browser or
22
+     *      other execution environment.
23
+     */
24
+    PERMISSION_PROMPT_IS_SHOWN: "mediaDevices.permissionPromptIsShown"
15 25
 };
16 26
 
17 27
 module.exports = JitsiMediaDevicesEvents;

+ 34
- 3
JitsiMeetJS.js Dosyayı Görüntüle

@@ -18,6 +18,11 @@ var Statistics = require("./modules/statistics/statistics");
18 18
 var Resolutions = require("./service/RTC/Resolutions");
19 19
 var ScriptUtil = require("./modules/util/ScriptUtil");
20 20
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
21
+var RTCBrowserType = require("./modules/RTC/RTCBrowserType");
22
+
23
+// The amount of time to wait until firing
24
+// JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN event
25
+var USER_MEDIA_PERMISSION_PROMPT_TIMEOUT = 500;
21 26
 
22 27
 function getLowerResolution(resolution) {
23 28
     if(!Resolutions[resolution])
@@ -103,13 +108,36 @@ var LibJitsiMeet = {
103 108
      * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
104 109
      * @param {string} options.cameraDeviceId
105 110
      * @param {string} options.micDeviceId
111
+     * @param {boolean} (firePermissionPromptIsShownEvent) - if event
112
+     *      JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN should be fired
106 113
      * @returns {Promise.<{Array.<JitsiTrack>}, JitsiConferenceError>}
107 114
      *     A promise that returns an array of created JitsiTracks if resolved,
108 115
      *     or a JitsiConferenceError if rejected.
109 116
      */
110
-    createLocalTracks: function (options) {
111
-        return RTC.obtainAudioAndVideoPermissions(options || {}).then(
112
-            function(tracks) {
117
+    createLocalTracks: function (options, firePermissionPromptIsShownEvent) {
118
+        var promiseFulfilled = false;
119
+
120
+        if (firePermissionPromptIsShownEvent === true) {
121
+            window.setTimeout(function () {
122
+                if (!promiseFulfilled) {
123
+                    var browser = RTCBrowserType.getBrowserType()
124
+                        .split('rtc_browser.')[1];
125
+
126
+                    if (RTCBrowserType.isAndroid()) {
127
+                        browser = 'android';
128
+                    }
129
+
130
+                    JitsiMediaDevices.emitEvent(
131
+                        JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
132
+                        browser);
133
+                }
134
+            }, USER_MEDIA_PERMISSION_PROMPT_TIMEOUT);
135
+        }
136
+
137
+        return RTC.obtainAudioAndVideoPermissions(options || {})
138
+            .then(function(tracks) {
139
+                promiseFulfilled = true;
140
+
113 141
                 if(!RTC.options.disableAudioLevels)
114 142
                     for(var i = 0; i < tracks.length; i++) {
115 143
                         var track = tracks[i];
@@ -124,8 +152,11 @@ var LibJitsiMeet = {
124 152
                                 });
125 153
                         }
126 154
                     }
155
+
127 156
                 return tracks;
128 157
             }).catch(function (error) {
158
+                promiseFulfilled = true;
159
+
129 160
                 Statistics.sendGetUserMediaFailed(error);
130 161
 
131 162
                 if(error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION) {

+ 29
- 0
JitsiParticipant.js Dosyayı Görüntüle

@@ -1,4 +1,5 @@
1 1
 /* global Strophe */
2
+var JitsiConferenceEvents = require('./JitsiConferenceEvents');
2 3
 
3 4
 /**
4 5
  * Represents a participant in (a member of) a conference.
@@ -21,6 +22,7 @@ function JitsiParticipant(jid, conference, displayName, isHidden){
21 22
         video: undefined
22 23
     };
23 24
     this._isHidden = isHidden;
25
+    this._properties = {};
24 26
 }
25 27
 
26 28
 /**
@@ -30,6 +32,33 @@ JitsiParticipant.prototype.getConference = function() {
30 32
     return this._conference;
31 33
 };
32 34
 
35
+/**
36
+ * Gets the value of a property of this participant.
37
+ */
38
+JitsiParticipant.prototype.getProperty = function(name) {
39
+    return this._properties[name];
40
+};
41
+
42
+/**
43
+ * Sets the value of a property of this participant, and fires an event if the
44
+ * value has changed.
45
+ * @name the name of the property.
46
+ * @value the value to set.
47
+ */
48
+JitsiParticipant.prototype.setProperty = function(name, value) {
49
+    var oldValue = this._properties[name];
50
+    this._properties[name] = value;
51
+
52
+    if (value !== oldValue) {
53
+        this._conference.eventEmitter.emit(
54
+            JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
55
+            this,
56
+            name,
57
+            oldValue,
58
+            value);
59
+    }
60
+};
61
+
33 62
 /**
34 63
  * @returns {Array.<JitsiTrack>} The list of media tracks for this participant.
35 64
  */

+ 3
- 2
doc/API.md Dosyayı Görüntüle

@@ -58,7 +58,7 @@ The ```options``` parameter is JS object with the following properties:
58 58
 JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
59 59
 ```
60 60
 
61
-* ```JitsiMeetJS.createLocalTracks(options)``` - Creates the media tracks and returns them trough ```Promise``` object. If rejected, passes ```JitsiTrackError``` instance to catch block.
61
+* ```JitsiMeetJS.createLocalTracks(options, firePermissionPromptIsShownEvent)``` - Creates the media tracks and returns them trough ```Promise``` object. If rejected, passes ```JitsiTrackError``` instance to catch block.
62 62
     - options - JS object with configuration options for the local media tracks. You can change the following properties there:
63 63
         1. devices - array with the devices - "desktop", "video" and "audio" that will be passed to GUM. If that property is not set GUM will try to get all available devices.
64 64
         2. resolution - the prefered resolution for the local video.
@@ -67,6 +67,7 @@ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
67 67
         5. minFps - the minimum frame rate for the video stream (passed to GUM)
68 68
         6. maxFps - the maximum frame rate for the video stream (passed to GUM)
69 69
         7. facingMode - facing mode for a camera (possible values - 'user', 'environment')
70
+    - firePermissionPromptIsShownEvent - optional boolean parameter. If set to ```true```, ```JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN``` will be fired when browser shows gUM permission prompt.
70 71
 
71 72
 * ```JitsiMeetJS.enumerateDevices(callback)``` - __DEPRECATED__. Use ```JitsiMeetJS.mediaDevices.enumerateDevices(callback)``` instead.
72 73
 * ```JitsiMeetJS.isDeviceListAvailable()``` - __DEPRECATED__. Use ```JitsiMeetJS.mediaDevices.isDeviceListAvailable()``` instead.
@@ -88,7 +89,6 @@ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
88 89
     - ```addEventListener(event, handler)``` - attaches an event handler.
89 90
     - ```removeEventListener(event, handler)``` - removes an event handler.
90 91
 
91
-
92 92
 * ```JitsiMeetJS.events``` - JS object that contains all events used by the API. You will need that JS object when you try to subscribe for connection or conference events.
93 93
     We have two event types - connection and conference. You can access the events with the following code ```JitsiMeetJS.events.<event_type>.<event_name>```.
94 94
     For example if you want to use the conference event that is fired when somebody leave conference you can use the following code - ```JitsiMeetJS.events.conference.USER_LEFT```.
@@ -132,6 +132,7 @@ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
132 132
 
133 133
     4. mediaDevices
134 134
         - DEVICE_LIST_CHANGED - indicates that list of currently connected devices has changed (parameters - devices(MediaDeviceInfo[])).
135
+        - PERMISSION_PROMPT_IS_SHOWN - Indicates that the environment is currently showing permission prompt to access camera and/or microphone (parameters - environmentType ('chrome'|'opera'|'firefox'|'iexplorer'|'safari'|'nwjs'|'react-native'|'android').
135 136
 
136 137
 
137 138
 * ```JitsiMeetJS.errors``` - JS object that contains all errors used by the API. You can use that object to check the reported errors from the API

+ 17
- 0
modules/RTC/RTC.js Dosyayı Görüntüle

@@ -299,6 +299,23 @@ RTC.getAudioOutputDevice = function () {
299 299
     return RTCUtils.getAudioOutputDevice();
300 300
 };
301 301
 
302
+/**
303
+ * Returns list of available media devices if its obtained, otherwise an
304
+ * empty array is returned/
305
+ * @returns {Array} list of available media devices.
306
+ */
307
+RTC.getCurrentlyAvailableMediaDevices = function () {
308
+    return RTCUtils.getCurrentlyAvailableMediaDevices();
309
+};
310
+
311
+/**
312
+ * Returns event data for device to be reported to stats.
313
+ * @returns {MediaDeviceInfo} device.
314
+ */
315
+RTC.getEventDataForActiveDevice = function (device) {
316
+    return RTCUtils.getEventDataForActiveDevice(device);
317
+};
318
+
302 319
 /**
303 320
  * Sets current audio output device.
304 321
  * @param {string} deviceId - id of 'audiooutput' device from

+ 50
- 0
modules/RTC/RTCBrowserType.js Dosyayı Görüntüle

@@ -22,41 +22,90 @@ var RTCBrowserType = {
22 22
 
23 23
     RTC_BROWSER_REACT_NATIVE: "rtc_browser.react-native",
24 24
 
25
+    /**
26
+     * Gets current browser type.
27
+     * @returns {string}
28
+     */
25 29
     getBrowserType: function () {
26 30
         return currentBrowser;
27 31
     },
28 32
 
33
+    /**
34
+     * Checks if current browser is Chrome.
35
+     * @returns {boolean}
36
+     */
29 37
     isChrome: function () {
30 38
         return currentBrowser === RTCBrowserType.RTC_BROWSER_CHROME;
31 39
     },
32 40
 
41
+    /**
42
+     * Checks if current browser is Opera.
43
+     * @returns {boolean}
44
+     */
33 45
     isOpera: function () {
34 46
         return currentBrowser === RTCBrowserType.RTC_BROWSER_OPERA;
35 47
     },
48
+
49
+    /**
50
+     * Checks if current browser is Firefox.
51
+     * @returns {boolean}
52
+     */
36 53
     isFirefox: function () {
37 54
         return currentBrowser === RTCBrowserType.RTC_BROWSER_FIREFOX;
38 55
     },
39 56
 
57
+    /**
58
+     * Checks if current browser is Internet Explorer.
59
+     * @returns {boolean}
60
+     */
40 61
     isIExplorer: function () {
41 62
         return currentBrowser === RTCBrowserType.RTC_BROWSER_IEXPLORER;
42 63
     },
43 64
 
65
+    /**
66
+     * Checks if current browser is Safari.
67
+     * @returns {boolean}
68
+     */
44 69
     isSafari: function () {
45 70
         return currentBrowser === RTCBrowserType.RTC_BROWSER_SAFARI;
46 71
     },
72
+
73
+    /**
74
+     * Checks if current environment is NWJS.
75
+     * @returns {boolean}
76
+     */
47 77
     isNWJS: function () {
48 78
         return currentBrowser === RTCBrowserType.RTC_BROWSER_NWJS;
49 79
     },
80
+
81
+    /**
82
+     * Checks if current environment is React Native.
83
+     * @returns {boolean}
84
+     */
50 85
     isReactNative: function () {
51 86
         return currentBrowser === RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
52 87
     },
88
+
89
+    /**
90
+     * Checks if Temasys RTC plugin is used.
91
+     * @returns {boolean}
92
+     */
53 93
     isTemasysPluginUsed: function () {
54 94
         return RTCBrowserType.isIExplorer() || RTCBrowserType.isSafari();
55 95
     },
96
+
97
+    /**
98
+     * Returns Firefox version.
99
+     * @returns {number|null}
100
+     */
56 101
     getFirefoxVersion: function () {
57 102
         return RTCBrowserType.isFirefox() ? browserVersion : null;
58 103
     },
59 104
 
105
+    /**
106
+     * Returns Chrome version.
107
+     * @returns {number|null}
108
+     */
60 109
     getChromeVersion: function () {
61 110
         return RTCBrowserType.isChrome() ? browserVersion : null;
62 111
     },
@@ -72,6 +121,7 @@ var RTCBrowserType = {
72 121
 
73 122
     /**
74 123
      * Whether the browser is running on an android device.
124
+     * @returns {boolean}
75 125
      */
76 126
     isAndroid: function() {
77 127
         return isAndroid;

+ 48
- 7
modules/RTC/RTCUtils.js Dosyayı Görüntüle

@@ -40,7 +40,7 @@ var featureDetectionAudioEl = document.createElement('audio');
40 40
 var isAudioOutputDeviceChangeAvailable =
41 41
     typeof featureDetectionAudioEl.setSinkId !== 'undefined';
42 42
 
43
-var currentlyAvailableMediaDevices = [];
43
+var currentlyAvailableMediaDevices;
44 44
 
45 45
 var rawEnumerateDevicesWithCallback = navigator.mediaDevices
46 46
     && navigator.mediaDevices.enumerateDevices
@@ -327,7 +327,11 @@ function pollForAvailableMediaDevices() {
327 327
     // and then plug in a new one.
328 328
     if (rawEnumerateDevicesWithCallback) {
329 329
         rawEnumerateDevicesWithCallback(function (devices) {
330
-            if (compareAvailableMediaDevices(devices)) {
330
+            // We don't fire RTCEvents.DEVICE_LIST_CHANGED for the first time
331
+            // we call enumerateDevices(). This is the initial step.
332
+            if (typeof currentlyAvailableMediaDevices === 'undefined') {
333
+                currentlyAvailableMediaDevices = devices.slice(0);
334
+            } else if (compareAvailableMediaDevices(devices)) {
331 335
                 onMediaDevicesListChanged(devices);
332 336
             }
333 337
 
@@ -382,12 +386,24 @@ function onReady (options, GUM) {
382 386
     eventEmitter.emit(RTCEvents.RTC_READY, true);
383 387
     screenObtainer.init(options, GUM);
384 388
 
385
-    if (isDeviceChangeEventSupported && RTCUtils.isDeviceListAvailable()) {
386
-        navigator.mediaDevices.addEventListener('devicechange', function () {
387
-            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
+            }
388 406
         });
389
-    } else if (RTCUtils.isDeviceListAvailable()) {
390
-        pollForAvailableMediaDevices();
391 407
     }
392 408
 }
393 409
 
@@ -1203,6 +1219,31 @@ var RTCUtils = {
1203 1219
      */
1204 1220
     getAudioOutputDevice: function () {
1205 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 };
1206 1247
     }
1207 1248
 };
1208 1249
 

+ 93
- 34
modules/statistics/CallStats.js Dosyayı Görüntüle

@@ -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 Dosyayı Görüntüle

@@ -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
 

+ 23
- 10
modules/xmpp/ChatRoom.js Dosyayı Görüntüle

@@ -87,6 +87,7 @@ function ChatRoom(connection, jid, password, XMPP, options, settings) {
87 87
     this.phoneNumber = null;
88 88
     this.phonePin = null;
89 89
     this.connectionTimes = {};
90
+    this.participantPropertyListener = null;
90 91
 }
91 92
 
92 93
 ChatRoom.prototype.initPresenceMap = function () {
@@ -325,8 +326,8 @@ ChatRoom.prototype.onPresence = function (pres) {
325 326
         {
326 327
             case "nick":
327 328
                 if(!member.isFocus) {
328
-                    var displayName = !this.xmpp.options.displayJids
329
-                        ? member.nick : Strophe.getResourceFromJid(from);
329
+                    var displayName = this.xmpp.options.displayJids
330
+                        ? Strophe.getResourceFromJid(from) : member.nick;
330 331
 
331 332
                     if (displayName && displayName.length > 0) {
332 333
                         this.eventEmitter.emit(
@@ -351,7 +352,7 @@ ChatRoom.prototype.onPresence = function (pres) {
351 352
                 this.phonePin = att.pin || null;
352 353
                 this.eventEmitter.emit(XMPPEvents.PHONE_NUMBER_CHANGED);
353 354
                 break;
354
-            default :
355
+            default:
355 356
                 this.processNode(node, from);
356 357
         }
357 358
     }
@@ -369,13 +370,26 @@ ChatRoom.prototype.onPresence = function (pres) {
369 370
     }
370 371
 };
371 372
 
373
+/**
374
+ * Sets the special listener to be used for "command"s whose name starts with
375
+ * "jitsi_participant_".
376
+ */
377
+ChatRoom.prototype.setParticipantPropertyListener = function (listener) {
378
+    this.participantPropertyListener = listener;
379
+};
380
+
372 381
 ChatRoom.prototype.processNode = function (node, from) {
373 382
     // make sure we catch all errors coming from any handler
374 383
     // otherwise we can remove the presence handler from strophe
375 384
     try {
376 385
         var tagHandler = this.presHandlers[node.tagName];
377
-        if(tagHandler)
386
+        if (node.tagName.startsWith("jitsi_participant_")) {
387
+            tagHandler = this.participantPropertyListener;
388
+        }
389
+
390
+        if(tagHandler) {
378 391
             tagHandler(node, Strophe.getResourceFromJid(from), from);
392
+        }
379 393
     } catch (e) {
380 394
         GlobalOnErrorHandler.callErrorHandler(e);
381 395
         logger.error('Error processing:' + node.tagName + ' node.', e);
@@ -560,15 +574,14 @@ ChatRoom.prototype.lockRoom = function (key, onSuccess, onError, onNotSupported)
560 574
 
561 575
 ChatRoom.prototype.addToPresence = function (key, values) {
562 576
     values.tagName = key;
563
-    this.presMap["nodes"].push(values);
577
+    this.removeFromPresence(key);
578
+    this.presMap.nodes.push(values);
564 579
 };
565 580
 
566 581
 ChatRoom.prototype.removeFromPresence = function (key) {
567
-    for(var i = 0; i < this.presMap.nodes.length; i++)
568
-    {
569
-        if(key === this.presMap.nodes[i].tagName)
570
-            this.presMap.nodes.splice(i, 1);
571
-    }
582
+    var nodes = this.presMap.nodes.filter(function(node) {
583
+        return key !== node.tagName;});
584
+    this.presMap.nodes = nodes;
572 585
 };
573 586
 
574 587
 ChatRoom.prototype.addPresenceListener = function (name, handler) {

+ 2
- 1
service/RTC/RTCEvents.js Dosyayı Görüntüle

@@ -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…
İptal
Kaydet