Browse Source

feat(browser_caps): Implement browser capabilities API. (#690)

In order to include browser capabilities in RTCBrowserType I needed to
refactor RTCBrowserType.
dev1
hristoterezov 7 years ago
parent
commit
44d2c7ba21

+ 4
- 0
.eslintignore View File

3
 lib-jitsi-meet.js.map
3
 lib-jitsi-meet.js.map
4
 lib-jitsi-meet.min.js
4
 lib-jitsi-meet.min.js
5
 lib-jitsi-meet.min.map
5
 lib-jitsi-meet.min.map
6
+browser_capabilities.js
7
+browser_capabilities.js.map
8
+browser_capabilities.min.js
9
+browser_capabilities.min.map
6
 
10
 
7
 # Third-party source code which we (1) do not want to modify or (2) try to
11
 # Third-party source code which we (1) do not want to modify or (2) try to
8
 # modify as little as possible.
12
 # modify as little as possible.

+ 1
- 0
.gitignore View File

3
 .idea/
3
 .idea/
4
 *.iml
4
 *.iml
5
 .*.tmp
5
 .*.tmp
6
+browser_capabilities.*
6
 deploy-local.sh
7
 deploy-local.sh
7
 .remote-sync.json
8
 .remote-sync.json
8
 lib-jitsi-meet.*
9
 lib-jitsi-meet.*

+ 4
- 4
JitsiConference.js View File

33
     from './modules/connectivity/ParticipantConnectionStatus';
33
     from './modules/connectivity/ParticipantConnectionStatus';
34
 import P2PDominantSpeakerDetection from './modules/P2PDominantSpeakerDetection';
34
 import P2PDominantSpeakerDetection from './modules/P2PDominantSpeakerDetection';
35
 import RTC from './modules/RTC/RTC';
35
 import RTC from './modules/RTC/RTC';
36
-import RTCBrowserType from './modules/RTC/RTCBrowserType';
36
+import browser from './modules/browser';
37
 import * as RTCEvents from './service/RTC/RTCEvents';
37
 import * as RTCEvents from './service/RTC/RTCEvents';
38
 import Statistics from './modules/statistics/statistics';
38
 import Statistics from './modules/statistics/statistics';
39
 import TalkMutedDetection from './modules/TalkMutedDetection';
39
 import TalkMutedDetection from './modules/TalkMutedDetection';
1387
             errorMsg: 'Rejecting session-initiate from non-focus and'
1387
             errorMsg: 'Rejecting session-initiate from non-focus and'
1388
                         + `non-moderator user: ${jingleSession.remoteJid}`
1388
                         + `non-moderator user: ${jingleSession.remoteJid}`
1389
         };
1389
         };
1390
-    } else if (!RTCBrowserType.isP2PSupported()) {
1390
+    } else if (!browser.supportsP2P()) {
1391
         rejectReason = {
1391
         rejectReason = {
1392
             reason: 'unsupported-applications',
1392
             reason: 'unsupported-applications',
1393
             reasonDescription: 'P2P not supported',
1393
             reasonDescription: 'P2P not supported',
1543
     }
1543
     }
1544
 
1544
 
1545
     if (bridgeChannelType === 'datachannel'
1545
     if (bridgeChannelType === 'datachannel'
1546
-        && !RTCBrowserType.supportsDataChannels()) {
1546
+        && !browser.supportsDataChannels()) {
1547
         bridgeChannelType = 'websocket';
1547
         bridgeChannelType = 'websocket';
1548
     }
1548
     }
1549
 
1549
 
2486
  * @private
2486
  * @private
2487
  */
2487
  */
2488
 JitsiConference.prototype._maybeStartOrStopP2P = function(userLeftEvent) {
2488
 JitsiConference.prototype._maybeStartOrStopP2P = function(userLeftEvent) {
2489
-    if (!RTCBrowserType.isP2PSupported()
2489
+    if (!browser.supportsP2P()
2490
         || !this.isP2PEnabled()
2490
         || !this.isP2PEnabled()
2491
         || this.isP2PTestModeEnabled()) {
2491
         || this.isP2PTestModeEnabled()) {
2492
         logger.info('Auto P2P disabled');
2492
         logger.info('Auto P2P disabled');

+ 9
- 6
JitsiMediaDevices.js View File

1
 import EventEmitter from 'events';
1
 import EventEmitter from 'events';
2
-import * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';
2
+
3
 import * as MediaType from './service/RTC/MediaType';
3
 import * as MediaType from './service/RTC/MediaType';
4
+import browser from './modules/browser';
4
 import RTC from './modules/RTC/RTC';
5
 import RTC from './modules/RTC/RTC';
5
-import RTCBrowserType from './modules/RTC/RTCBrowserType';
6
 import RTCEvents from './service/RTC/RTCEvents';
6
 import RTCEvents from './service/RTC/RTCEvents';
7
 import Statistics from './modules/statistics/statistics';
7
 import Statistics from './modules/statistics/statistics';
8
 
8
 
9
+import * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';
10
+
9
 const eventEmitter = new EventEmitter();
11
 const eventEmitter = new EventEmitter();
10
 
12
 
11
 /**
13
 /**
80
      * @returns {boolean}
82
      * @returns {boolean}
81
      */
83
      */
82
     isMultipleAudioInputSupported() {
84
     isMultipleAudioInputSupported() {
83
-        return !RTCBrowserType.isFirefox();
85
+        return !browser.isFirefox();
84
     },
86
     },
85
 
87
 
86
     /**
88
     /**
147
      * @returns {boolean}
149
      * @returns {boolean}
148
      */
150
      */
149
     supportsVideo() {
151
     supportsVideo() {
150
-        // Defer to RTCBrowserType to allow exposure of the api to the consumer
151
-        // but prevent other files from having to import JitsiMediaDevices.
152
-        return RTCBrowserType.supportsVideo();
152
+        // Defer to the browser capabilities to allow exposure of the api to the
153
+        // consumer but prevent other files from having to import
154
+        // JitsiMediaDevices.
155
+        return browser.supportsVideo();
153
     }
156
     }
154
 };
157
 };
155
 
158
 

+ 4
- 4
JitsiMeetJS.js View File

25
 import { ParticipantConnectionStatus }
25
 import { ParticipantConnectionStatus }
26
     from './modules/connectivity/ParticipantConnectionStatus';
26
     from './modules/connectivity/ParticipantConnectionStatus';
27
 import RTC from './modules/RTC/RTC';
27
 import RTC from './modules/RTC/RTC';
28
-import RTCBrowserType from './modules/RTC/RTCBrowserType';
28
+import browser from './modules/browser';
29
 import RTCUIHelper from './modules/RTC/RTCUIHelper';
29
 import RTCUIHelper from './modules/RTC/RTCUIHelper';
30
 import ScriptUtil from './modules/util/ScriptUtil';
30
 import ScriptUtil from './modules/util/ScriptUtil';
31
 import Statistics from './modules/statistics/statistics';
31
 import Statistics from './modules/statistics/statistics';
257
                 if (!promiseFulfilled) {
257
                 if (!promiseFulfilled) {
258
                     JitsiMediaDevices.emitEvent(
258
                     JitsiMediaDevices.emitEvent(
259
                         JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
259
                         JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
260
-                        RTCBrowserType.getBrowserName());
260
+                        browser.getName());
261
                 }
261
                 }
262
             }, USER_MEDIA_PERMISSION_PROMPT_TIMEOUT);
262
             }, USER_MEDIA_PERMISSION_PROMPT_TIMEOUT);
263
         }
263
         }
316
                 promiseFulfilled = true;
316
                 promiseFulfilled = true;
317
 
317
 
318
                 if (error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION
318
                 if (error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION
319
-                    && !RTCBrowserType.usesNewGumFlow()) {
319
+                    && !browser.usesNewGumFlow()) {
320
                     const oldResolution = options.resolution || '720';
320
                     const oldResolution = options.resolution || '720';
321
                     const newResolution = getLowerResolution(oldResolution);
321
                     const newResolution = getLowerResolution(oldResolution);
322
 
322
 
481
         AuthUtil,
481
         AuthUtil,
482
         RTCUIHelper,
482
         RTCUIHelper,
483
         ScriptUtil,
483
         ScriptUtil,
484
-        RTCBrowserType
484
+        browser
485
     }
485
     }
486
 };
486
 };

+ 44
- 0
doc/browser-capabilities.md View File

1
+# Jitsi Meet browser capabilities
2
+
3
+An API for checking browser capabilities for Jitsi Meet.
4
+
5
+## Installation
6
+
7
+To use the Jitsi Meet browser capabilities API in your application you need to add a JS file with `BrowserCapabilities` implementation:
8
+
9
+```javascript
10
+<script src="https://example.com/libs/browser_capabilities.min.js"></script>
11
+```
12
+
13
+## API
14
+
15
+### Constructor
16
+
17
+* **isUsingIFrame**: `true` if Jitsi Meet is loaded in iframe and `false` otherwise. The default value is `false`.
18
+* **browserInfo**: Information about the browser which capabilities will be checked. If not specified `BrowserCapabilities` class will detect the current browser. `browserInfo` is JS object with the following properties:
19
+    * **name**: The name of the browser compatible with the names returned by [bowser](https://github.com/lancedikson/bowser#all-detected-browsers)
20
+    * **version**: The version of the browser compatible with the version returned by [bowser](https://github.com/lancedikson/bowser#all-detected-browsers)
21
+
22
+**Example:**
23
+```javascript
24
+const browserCapabilities = new BrowserCapabilities(); // not using iframe;  capabilities for the current browser
25
+const browserCapabilities1 = new BrowserCapabilities(true); // using iframe; capabilities for the current browser.
26
+const browserCapabilities1 = new BrowserCapabilities(true, {
27
+    name: 'Chrome',
28
+    version: '63.0'
29
+}); // using iframe; capabilities for Chrome 63.0
30
+```
31
+
32
+### Methods:
33
+
34
+* **isSupported** - returns `true` if the browser is supported by Jitsi Meet and `false` otherwise.
35
+
36
+* **supportsAudioIn** - returns `true` if the browser supports incoming audio and `false` otherwise.
37
+
38
+* **supportsAudioOut** - returns `true` if the browser supports outgoing audio and `false` otherwise.
39
+
40
+* **supportsVideoIn** - returns `true` if the browser is supports incoming video and `false` otherwise.
41
+
42
+* **supportsVideoOut** - returns `true` if the browser is supports outgoing video and `false` otherwise.
43
+
44
+* **supportsScreenSharing** - returns `true` if the browser is supports screen sharing and `false` otherwise.

+ 6
- 6
modules/RTC/JitsiLocalTrack.js View File

12
     NO_DATA_FROM_SOURCE,
12
     NO_DATA_FROM_SOURCE,
13
     TRACK_MUTE_CHANGED
13
     TRACK_MUTE_CHANGED
14
 } from '../../JitsiTrackEvents';
14
 } from '../../JitsiTrackEvents';
15
-import RTCBrowserType from './RTCBrowserType';
15
+import browser from '../browser';
16
 import RTCUtils from './RTCUtils';
16
 import RTCUtils from './RTCUtils';
17
 import CameraFacingMode from '../../service/RTC/CameraFacingMode';
17
 import CameraFacingMode from '../../service/RTC/CameraFacingMode';
18
 import * as MediaType from '../../service/RTC/MediaType';
18
 import * as MediaType from '../../service/RTC/MediaType';
78
         this.sourceId = sourceId;
78
         this.sourceId = sourceId;
79
         this.sourceType = sourceType;
79
         this.sourceType = sourceType;
80
 
80
 
81
-        if (RTCBrowserType.usesNewGumFlow()) {
81
+        if (browser.usesNewGumFlow()) {
82
             // Get the resolution from the track itself because it cannot be
82
             // Get the resolution from the track itself because it cannot be
83
             // certain which resolution webrtc has fallen back to using.
83
             // certain which resolution webrtc has fallen back to using.
84
             this.resolution = track.getSettings().height;
84
             this.resolution = track.getSettings().height;
90
             // FIXME Currently, Firefox is ignoring our constraints about
90
             // FIXME Currently, Firefox is ignoring our constraints about
91
             // resolutions so we do not store it, to avoid wrong reporting of
91
             // resolutions so we do not store it, to avoid wrong reporting of
92
             // local track resolution.
92
             // local track resolution.
93
-            this.resolution = RTCBrowserType.isFirefox() ? null : resolution;
93
+            this.resolution = browser.isFirefox() ? null : resolution;
94
         }
94
         }
95
 
95
 
96
         this.deviceId = deviceId;
96
         this.deviceId = deviceId;
354
 
354
 
355
         if (this.isAudioTrack()
355
         if (this.isAudioTrack()
356
                 || this.videoType === VideoType.DESKTOP
356
                 || this.videoType === VideoType.DESKTOP
357
-                || !RTCBrowserType.doesVideoMuteByStreamRemove()) {
357
+                || !browser.doesVideoMuteByStreamRemove()) {
358
             logMuteInfo();
358
             logMuteInfo();
359
             if (this.track) {
359
             if (this.track) {
360
                 this.track.enabled = !muted;
360
                 this.track.enabled = !muted;
384
                 facingMode: this.getCameraFacingMode()
384
                 facingMode: this.getCameraFacingMode()
385
             };
385
             };
386
 
386
 
387
-            if (RTCBrowserType.usesNewGumFlow()) {
387
+            if (browser.usesNewGumFlow()) {
388
                 promise
388
                 promise
389
                     = RTCUtils.newObtainAudioAndVideoPermissions(Object.assign(
389
                     = RTCUtils.newObtainAudioAndVideoPermissions(Object.assign(
390
                         {},
390
                         {},
402
             promise.then(streamsInfo => {
402
             promise.then(streamsInfo => {
403
                 const mediaType = this.getType();
403
                 const mediaType = this.getType();
404
                 const streamInfo
404
                 const streamInfo
405
-                    = RTCBrowserType.usesNewGumFlow()
405
+                    = browser.usesNewGumFlow()
406
                         ? streamsInfo.find(
406
                         ? streamsInfo.find(
407
                             info => info.track.kind === mediaType)
407
                             info => info.track.kind === mediaType)
408
                         : streamsInfo.find(
408
                         : streamsInfo.find(

+ 2
- 2
modules/RTC/JitsiRemoteTrack.js View File

1
 import { createTtfmEvent } from '../../service/statistics/AnalyticsEvents';
1
 import { createTtfmEvent } from '../../service/statistics/AnalyticsEvents';
2
 import JitsiTrack from './JitsiTrack';
2
 import JitsiTrack from './JitsiTrack';
3
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
3
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
4
-import RTCBrowserType from './RTCBrowserType';
4
+import browser from '../browser';
5
 import Statistics from '../statistics/statistics';
5
 import Statistics from '../statistics/statistics';
6
 
6
 
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
263
             ttfmTrackerVideoAttached = true;
263
             ttfmTrackerVideoAttached = true;
264
         }
264
         }
265
 
265
 
266
-        if (RTCBrowserType.isTemasysPluginUsed()) {
266
+        if (browser.isTemasysPluginUsed()) {
267
             // XXX Don't require Temasys unless it's to be used because it
267
             // XXX Don't require Temasys unless it's to be used because it
268
             // doesn't run on React Native, for example.
268
             // doesn't run on React Native, for example.
269
             const AdapterJS = require('./adapter.screenshare');
269
             const AdapterJS = require('./adapter.screenshare');

+ 2
- 2
modules/RTC/RTC.js View File

10
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
10
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
11
 import Listenable from '../util/Listenable';
11
 import Listenable from '../util/Listenable';
12
 import * as MediaType from '../../service/RTC/MediaType';
12
 import * as MediaType from '../../service/RTC/MediaType';
13
-import RTCBrowserType from './RTCBrowserType';
13
+import browser from '../browser';
14
 import RTCEvents from '../../service/RTC/RTCEvents';
14
 import RTCEvents from '../../service/RTC/RTCEvents';
15
 import RTCUtils from './RTCUtils';
15
 import RTCUtils from './RTCUtils';
16
 import Statistics from '../statistics/statistics';
16
 import Statistics from '../statistics/statistics';
201
      * @returns {*} Promise object that will receive the new JitsiTracks
201
      * @returns {*} Promise object that will receive the new JitsiTracks
202
      */
202
      */
203
     static obtainAudioAndVideoPermissions(options) {
203
     static obtainAudioAndVideoPermissions(options) {
204
-        const usesNewGumFlow = RTCBrowserType.usesNewGumFlow();
204
+        const usesNewGumFlow = browser.usesNewGumFlow();
205
         const obtainMediaPromise = usesNewGumFlow
205
         const obtainMediaPromise = usesNewGumFlow
206
             ? RTCUtils.newObtainAudioAndVideoPermissions(options)
206
             ? RTCUtils.newObtainAudioAndVideoPermissions(options)
207
             : RTCUtils.obtainAudioAndVideoPermissions(options);
207
             : RTCUtils.obtainAudioAndVideoPermissions(options);

+ 0
- 557
modules/RTC/RTCBrowserType.js View File

1
-import { getLogger } from 'jitsi-meet-logger';
2
-
3
-let browserVersion; // eslint-disable-line prefer-const
4
-
5
-let currentBrowser;
6
-
7
-const logger = getLogger(__filename);
8
-
9
-const RTCBrowserType = {
10
-
11
-    RTC_BROWSER_CHROME: 'rtc_browser.chrome',
12
-
13
-    RTC_BROWSER_OPERA: 'rtc_browser.opera',
14
-
15
-    RTC_BROWSER_FIREFOX: 'rtc_browser.firefox',
16
-
17
-    RTC_BROWSER_IEXPLORER: 'rtc_browser.iexplorer',
18
-
19
-    RTC_BROWSER_EDGE: 'rtc_browser.edge',
20
-
21
-    RTC_BROWSER_SAFARI: 'rtc_browser.safari',
22
-
23
-    RTC_BROWSER_NWJS: 'rtc_browser.nwjs',
24
-
25
-    RTC_BROWSER_ELECTRON: 'rtc_browser.electron',
26
-
27
-    RTC_BROWSER_REACT_NATIVE: 'rtc_browser.react-native',
28
-
29
-    /**
30
-     * Tells whether or not the <tt>MediaStream/tt> is removed from
31
-     * the <tt>PeerConnection</tt> and disposed on video mute (in order to turn
32
-     * off the camera device).
33
-     * @return {boolean} <tt>true</tt> if the current browser supports this
34
-     * strategy or <tt>false</tt> otherwise.
35
-     */
36
-    doesVideoMuteByStreamRemove() {
37
-        return !(
38
-            RTCBrowserType.isFirefox()
39
-            || RTCBrowserType.isEdge()
40
-            || RTCBrowserType.isSafariWithWebrtc()
41
-        );
42
-    },
43
-
44
-    /**
45
-     * Gets current browser type.
46
-     * @returns {string}
47
-     */
48
-    getBrowserType() {
49
-        return currentBrowser;
50
-    },
51
-
52
-    /**
53
-     * Gets current browser name, split from the type.
54
-     * @returns {string}
55
-     */
56
-    getBrowserName() {
57
-        const isAndroid = navigator.userAgent.indexOf('Android') !== -1;
58
-
59
-        if (isAndroid) {
60
-            return 'android';
61
-        }
62
-
63
-        return currentBrowser.split('rtc_browser.')[1];
64
-    },
65
-
66
-    /**
67
-     * Checks if current browser is Chrome.
68
-     * @returns {boolean}
69
-     */
70
-    isChrome() {
71
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_CHROME;
72
-    },
73
-
74
-    /**
75
-     * Checks if current browser is Opera.
76
-     * @returns {boolean}
77
-     */
78
-    isOpera() {
79
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_OPERA;
80
-    },
81
-
82
-    /**
83
-     * Checks if current browser is Firefox.
84
-     * @returns {boolean}
85
-     */
86
-    isFirefox() {
87
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_FIREFOX;
88
-    },
89
-
90
-    /**
91
-     * Checks if current browser is Internet Explorer.
92
-     * @returns {boolean}
93
-     */
94
-    isIExplorer() {
95
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_IEXPLORER;
96
-    },
97
-
98
-    /**
99
-     * Checks if current browser is Microsoft Edge.
100
-     * @returns {boolean}
101
-     */
102
-    isEdge() {
103
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_EDGE;
104
-    },
105
-
106
-    /**
107
-     * Checks if current browser is Safari.
108
-     * @returns {boolean}
109
-     */
110
-    isSafari() {
111
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_SAFARI;
112
-    },
113
-
114
-    /**
115
-     * Checks if current browser is a Safari and a version of Safari that
116
-     * supports native webrtc.
117
-     *
118
-     * @returns {boolean}
119
-     */
120
-    isSafariWithWebrtc() {
121
-        return RTCBrowserType.isSafari()
122
-            && RTCBrowserType.getSafariVersion() >= 11;
123
-    },
124
-
125
-    /**
126
-     * Checks if current environment is NWJS.
127
-     * @returns {boolean}
128
-     */
129
-    isNWJS() {
130
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_NWJS;
131
-    },
132
-
133
-    /**
134
-     * Checks if current environment is Electron.
135
-     * @returns {boolean}
136
-     */
137
-    isElectron() {
138
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_ELECTRON;
139
-    },
140
-
141
-    /**
142
-     * Check whether or not the current browser support peer to peer connections
143
-     * @return {boolean} <tt>true</tt> if p2p is supported or <tt>false</tt>
144
-     * otherwise.
145
-     */
146
-    isP2PSupported() {
147
-        return !RTCBrowserType.isEdge();
148
-    },
149
-
150
-    /**
151
-     * Checks if current environment is React Native.
152
-     * @returns {boolean}
153
-     */
154
-    isReactNative() {
155
-        return currentBrowser === RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
156
-    },
157
-
158
-    /**
159
-     * Checks if Temasys RTC plugin is used.
160
-     * @returns {boolean}
161
-     */
162
-    isTemasysPluginUsed() {
163
-        // Temasys do not support Microsoft Edge:
164
-        // http://support.temasys.com.sg/support/solutions/articles/
165
-        // 5000654345-can-the-temasys-webrtc-plugin-be-used-with-microsoft-edge-
166
-        return (
167
-            (RTCBrowserType.isSafari()
168
-                && !RTCBrowserType.isSafariWithWebrtc())
169
-            || (RTCBrowserType.isIExplorer()
170
-                && RTCBrowserType.getIExplorerVersion() < 12)
171
-        );
172
-    },
173
-
174
-    /**
175
-     * Returns whether or not the current browser should be using the new
176
-     * getUserMedia flow, which utilizes the adapter shim. This method should
177
-     * be temporary and used while migrating all browsers to use adapter and
178
-     * the new getUserMedia.
179
-     *
180
-     * @returns {boolean}
181
-     */
182
-    usesNewGumFlow() {
183
-        return (RTCBrowserType.isChrome()
184
-                && RTCBrowserType.getChromeVersion() >= 61)
185
-            || RTCBrowserType.isFirefox()
186
-            || RTCBrowserType.isSafariWithWebrtc();
187
-
188
-    },
189
-
190
-    /**
191
-     * Checks if the current browser triggers 'onmute'/'onunmute' events when
192
-     * user's connection is interrupted and the video stops playback.
193
-     * @returns {*|boolean} 'true' if the event is supported or 'false'
194
-     * otherwise.
195
-     */
196
-    isVideoMuteOnConnInterruptedSupported() {
197
-        return RTCBrowserType.isChrome() || RTCBrowserType.isElectron();
198
-    },
199
-
200
-    /**
201
-     * Returns Firefox version.
202
-     * @returns {number|null}
203
-     */
204
-    getFirefoxVersion() {
205
-        return RTCBrowserType.isFirefox() ? browserVersion : null;
206
-    },
207
-
208
-    /**
209
-     * Returns Chrome version.
210
-     * @returns {number|null}
211
-     */
212
-    getChromeVersion() {
213
-        return RTCBrowserType.isChrome() ? browserVersion : null;
214
-    },
215
-
216
-    /**
217
-     * Returns Opera version.
218
-     * @returns {number|null}
219
-     */
220
-    getOperaVersion() {
221
-        return RTCBrowserType.isOpera() ? browserVersion : null;
222
-    },
223
-
224
-    /**
225
-     * Returns Internet Explorer version.
226
-     *
227
-     * @returns {number|null}
228
-     */
229
-    getIExplorerVersion() {
230
-        return RTCBrowserType.isIExplorer() ? browserVersion : null;
231
-    },
232
-
233
-    /**
234
-     * Returns Edge version.
235
-     *
236
-     * @returns {number|null}
237
-     */
238
-    getEdgeVersion() {
239
-        return RTCBrowserType.isEdge() ? browserVersion : null;
240
-    },
241
-
242
-    /**
243
-     * Returns Safari version.
244
-     *
245
-     * @returns {number|null}
246
-     */
247
-    getSafariVersion() {
248
-        return RTCBrowserType.isSafari ? browserVersion : null;
249
-    },
250
-
251
-    usesPlanB() {
252
-        return !RTCBrowserType.usesUnifiedPlan();
253
-    },
254
-
255
-    usesUnifiedPlan() {
256
-        return RTCBrowserType.isFirefox();
257
-    },
258
-
259
-    /**
260
-     * Checks if the current browser reports upload and download bandwidth
261
-     * statistics.
262
-     * @return {boolean}
263
-     */
264
-    supportsBandwidthStatistics() {
265
-        // FIXME bandwidth stats are currently not implemented for FF on our
266
-        // side, but not sure if not possible ?
267
-        return !RTCBrowserType.isFirefox() && !RTCBrowserType.isEdge();
268
-    },
269
-
270
-    /**
271
-     * Checks if the current browser supports WebRTC datachannels.
272
-     * @return {boolean}
273
-     */
274
-    supportsDataChannels() {
275
-        // NOTE: Edge does not yet implement DataChannel.
276
-        return !RTCBrowserType.isEdge();
277
-    },
278
-
279
-    /**
280
-     * Checks if the current browser reports round trip time statistics for
281
-     * the ICE candidate pair.
282
-     * @return {boolean}
283
-     */
284
-    supportsRTTStatistics() {
285
-        // Firefox does not seem to report RTT for ICE candidate pair:
286
-        // eslint-disable-next-line max-len
287
-        // https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
288
-        // It does report mozRTT for RTP streams, but at the time of this
289
-        // writing it's value does not make sense most of the time
290
-        // (is reported as 1):
291
-        // https://bugzilla.mozilla.org/show_bug.cgi?id=1241066
292
-        // For Chrome and others we rely on 'googRtt'.
293
-        return !RTCBrowserType.isFirefox() && !RTCBrowserType.isEdge();
294
-    },
295
-
296
-    /**
297
-     * Whether jitsi-meet supports simulcast on the current browser.
298
-     * @returns {boolean}
299
-     */
300
-    supportsSimulcast() {
301
-        return RTCBrowserType.isChrome()
302
-            || RTCBrowserType.isFirefox()
303
-            || RTCBrowserType.isElectron()
304
-            || RTCBrowserType.isNWJS()
305
-            || RTCBrowserType.isReactNative();
306
-    },
307
-
308
-    supportsRtx() {
309
-        return !RTCBrowserType.isFirefox();
310
-    },
311
-
312
-    supportsRtpSender() {
313
-        return RTCBrowserType.isFirefox();
314
-    },
315
-
316
-    /**
317
-     * Returns whether or not the current browser can support capturing video,
318
-     * be it camera or desktop, and displaying received video.
319
-     *
320
-     * @returns {boolean}
321
-     */
322
-    supportsVideo() {
323
-        // Currently Safari using webrtc/adapter does not support video due in
324
-        // part to Safari only supporting H264 and the bridge sending VP8.
325
-        return !RTCBrowserType.isSafariWithWebrtc();
326
-    }
327
-};
328
-
329
-/**
330
- * detectOpera() must be called before detectChrome() !!!
331
- * otherwise Opera wil be detected as Chrome
332
- */
333
-function detectChrome() {
334
-    if (navigator.webkitGetUserMedia) {
335
-        currentBrowser = RTCBrowserType.RTC_BROWSER_CHROME;
336
-
337
-        // We can assume that user agent is chrome, because it's enforced when
338
-        // 'ext' streaming method is set.
339
-        const verMatch = navigator.userAgent.match(/chrome\/(\d+)\./i);
340
-        const ver = verMatch ? parseInt(verMatch[1], 10) : 'undefined';
341
-
342
-        logger.log(`This appears to be Chrome, ver: ${ver}`);
343
-
344
-        return ver;
345
-    }
346
-
347
-    return null;
348
-}
349
-
350
-/**
351
- *
352
- */
353
-function detectOpera() {
354
-    const userAgent = navigator.userAgent;
355
-
356
-    if (userAgent.match(/Opera|OPR/)) {
357
-        currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA;
358
-        const version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2];
359
-
360
-        logger.info(`This appears to be Opera, ver: ${version}`);
361
-
362
-        return version;
363
-    }
364
-
365
-    return null;
366
-}
367
-
368
-/**
369
- *
370
- */
371
-function detectFirefox() {
372
-    if (navigator.mozGetUserMedia) {
373
-        currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX;
374
-        const version = parseInt(
375
-            navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
376
-
377
-        logger.log(`This appears to be Firefox, ver: ${version}`);
378
-
379
-        return version;
380
-    }
381
-
382
-    return null;
383
-}
384
-
385
-/**
386
- *
387
- */
388
-function detectSafari() {
389
-    if (/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
390
-        currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
391
-
392
-        const matchedVersionParts
393
-            = navigator.userAgent.match(/version\/(\d+(\.\d+)?)/i);
394
-        const versionString = matchedVersionParts && matchedVersionParts[1];
395
-        const version = versionString ? parseInt(versionString, 10) : -1;
396
-
397
-        logger.info(`This appears to be Safari, ver: ${version}`);
398
-
399
-        return version;
400
-    }
401
-
402
-    return null;
403
-}
404
-
405
-/**
406
- * Detects IE.
407
- */
408
-function detectIE() {
409
-    let version;
410
-    const ua = window.navigator.userAgent;
411
-
412
-    const msie = ua.indexOf('MSIE ');
413
-
414
-    if (msie > 0) {
415
-        // IE 10 or older => return version number
416
-        version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
417
-    }
418
-
419
-    const trident = ua.indexOf('Trident/');
420
-
421
-    if (!version && trident > 0) {
422
-        // IE 11 => return version number
423
-        const rv = ua.indexOf('rv:');
424
-
425
-        version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
426
-    }
427
-
428
-    if (version) {
429
-        currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER;
430
-        logger.info(`This appears to be IExplorer, ver: ${version}`);
431
-    }
432
-
433
-    return version;
434
-}
435
-
436
-/**
437
- * Detects Edge.
438
- */
439
-function detectEdge() {
440
-    let version;
441
-    const ua = window.navigator.userAgent;
442
-
443
-    const edge = ua.indexOf('Edge/');
444
-
445
-    if (!version && edge > 0) {
446
-        version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
447
-    }
448
-
449
-    if (version) {
450
-        currentBrowser = RTCBrowserType.RTC_BROWSER_EDGE;
451
-        logger.info(`This appears to be Edge, ver: ${version}`);
452
-    }
453
-
454
-    return version;
455
-}
456
-
457
-/**
458
- * Detects Electron environment.
459
- */
460
-function detectElectron() {
461
-    const userAgent = navigator.userAgent;
462
-
463
-    if (userAgent.match(/Electron/)) {
464
-        currentBrowser = RTCBrowserType.RTC_BROWSER_ELECTRON;
465
-        const version = userAgent.match(/Electron\/([\d.]+)/)[1];
466
-
467
-        logger.info(`This appears to be Electron, ver: ${version}`);
468
-
469
-        return version;
470
-    }
471
-
472
-    return null;
473
-}
474
-
475
-/**
476
- *
477
- */
478
-function detectNWJS() {
479
-    const userAgent = navigator.userAgent;
480
-
481
-    if (userAgent.match(/JitsiMeetNW/)) {
482
-        currentBrowser = RTCBrowserType.RTC_BROWSER_NWJS;
483
-        const version = userAgent.match(/JitsiMeetNW\/([\d.]+)/)[1];
484
-
485
-        logger.info(`This appears to be JitsiMeetNW, ver: ${version}`);
486
-
487
-        return version;
488
-    }
489
-
490
-    return null;
491
-}
492
-
493
-/**
494
- *
495
- */
496
-function detectReactNative() {
497
-    const match
498
-        = navigator.userAgent.match(/\b(react[ \t_-]*native)(?:\/(\S+))?/i);
499
-    let version;
500
-
501
-    // If we're remote debugging a React Native app, it may be treated as
502
-    // Chrome. Check navigator.product as well and always return some version
503
-    // even if we can't get the real one.
504
-
505
-    if (match || navigator.product === 'ReactNative') {
506
-        currentBrowser = RTCBrowserType.RTC_BROWSER_REACT_NATIVE;
507
-        let name;
508
-
509
-        if (match && match.length > 2) {
510
-            name = match[1];
511
-            version = match[2];
512
-        }
513
-        name || (name = 'react-native');
514
-        version || (version = 'unknown');
515
-        console.info(`This appears to be ${name}, ver: ${version}`);
516
-    } else {
517
-        // We're not running in a React Native environment.
518
-        version = null;
519
-    }
520
-
521
-    return version;
522
-}
523
-
524
-/**
525
- *
526
- */
527
-function detectBrowser() {
528
-    let version;
529
-    const detectors = [
530
-        detectReactNative,
531
-        detectElectron,
532
-        detectNWJS,
533
-        detectOpera,
534
-        detectChrome,
535
-        detectFirefox,
536
-        detectEdge,
537
-        detectIE,
538
-        detectSafari
539
-    ];
540
-
541
-    // Try all browser detectors
542
-
543
-    for (let i = 0; i < detectors.length; i++) {
544
-        version = detectors[i]();
545
-        if (version) {
546
-            return version;
547
-        }
548
-    }
549
-    logger.warn('Browser type defaults to Safari ver 1');
550
-    currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
551
-
552
-    return 1;
553
-}
554
-
555
-browserVersion = detectBrowser();
556
-
557
-export default RTCBrowserType;

+ 6
- 6
modules/RTC/RTCUIHelper.js View File

1
 /* global $ */
1
 /* global $ */
2
 
2
 
3
-import RTCBrowserType from './RTCBrowserType';
3
+import browser from '../browser';
4
 
4
 
5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6
 
6
 
12
      * @returns {string} 'video' or 'object' string name of WebRTC video element
12
      * @returns {string} 'video' or 'object' string name of WebRTC video element
13
      */
13
      */
14
     getVideoElementName() {
14
     getVideoElementName() {
15
-        return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video';
15
+        return browser.isTemasysPluginUsed() ? 'object' : 'video';
16
     },
16
     },
17
 
17
 
18
     /**
18
     /**
25
     findVideoElement(containerElement) {
25
     findVideoElement(containerElement) {
26
         const videoElemName = RTCUIHelper.getVideoElementName();
26
         const videoElemName = RTCUIHelper.getVideoElementName();
27
 
27
 
28
-        if (!RTCBrowserType.isTemasysPluginUsed()) {
28
+        if (!browser.isTemasysPluginUsed()) {
29
             return $(containerElement).find(videoElemName)[0];
29
             return $(containerElement).find(videoElemName)[0];
30
         }
30
         }
31
         const matching
31
         const matching
51
      * @returns {boolean}
51
      * @returns {boolean}
52
      */
52
      */
53
     isResizeEventSupported() {
53
     isResizeEventSupported() {
54
-        return !RTCBrowserType.isTemasysPluginUsed();
54
+        return !browser.isTemasysPluginUsed();
55
     },
55
     },
56
 
56
 
57
     /**
57
     /**
61
      * @param volume the volume value to be set.
61
      * @param volume the volume value to be set.
62
      */
62
      */
63
     setVolume(streamElement, volume) {
63
     setVolume(streamElement, volume) {
64
-        if (!RTCBrowserType.isIExplorer()) {
64
+        if (!browser.isIExplorer()) {
65
             streamElement.volume = volume;
65
             streamElement.volume = volume;
66
         }
66
         }
67
     },
67
     },
73
      * @param autoPlay 'true' or 'false'
73
      * @param autoPlay 'true' or 'false'
74
      */
74
      */
75
     setAutoPlay(streamElement, autoPlay) {
75
     setAutoPlay(streamElement, autoPlay) {
76
-        if (!RTCBrowserType.isIExplorer()) {
76
+        if (!browser.isIExplorer()) {
77
             streamElement.autoplay = autoPlay;
77
             streamElement.autoplay = autoPlay;
78
         }
78
         }
79
     }
79
     }

+ 42
- 42
modules/RTC/RTCUtils.js View File

18
 import Listenable from '../util/Listenable';
18
 import Listenable from '../util/Listenable';
19
 import * as MediaType from '../../service/RTC/MediaType';
19
 import * as MediaType from '../../service/RTC/MediaType';
20
 import Resolutions from '../../service/RTC/Resolutions';
20
 import Resolutions from '../../service/RTC/Resolutions';
21
-import RTCBrowserType from './RTCBrowserType';
21
+import browser from '../browser';
22
 import RTCEvents from '../../service/RTC/RTCEvents';
22
 import RTCEvents from '../../service/RTC/RTCEvents';
23
 import ortcRTCPeerConnection from './ortc/RTCPeerConnection';
23
 import ortcRTCPeerConnection from './ortc/RTCPeerConnection';
24
 import screenObtainer from './ScreenObtainer';
24
 import screenObtainer from './ScreenObtainer';
31
 // XXX Don't require Temasys unless it's to be used because it doesn't run on
31
 // XXX Don't require Temasys unless it's to be used because it doesn't run on
32
 // React Native, for example.
32
 // React Native, for example.
33
 const AdapterJS
33
 const AdapterJS
34
-    = RTCBrowserType.isTemasysPluginUsed()
34
+    = browser.isTemasysPluginUsed()
35
         ? require('./adapter.screenshare')
35
         ? require('./adapter.screenshare')
36
         : undefined;
36
         : undefined;
37
 
37
 
38
 // Require adapter only for certain browsers. This is being done for
38
 // Require adapter only for certain browsers. This is being done for
39
 // react-native, which has its own shims, and while browsers are being migrated
39
 // react-native, which has its own shims, and while browsers are being migrated
40
 // over to use adapter's shims.
40
 // over to use adapter's shims.
41
-if (RTCBrowserType.usesNewGumFlow()) {
41
+if (browser.usesNewGumFlow()) {
42
     require('webrtc-adapter');
42
     require('webrtc-adapter');
43
 }
43
 }
44
 
44
 
186
     // have stable support of new constraints format. For more information
186
     // have stable support of new constraints format. For more information
187
     // @see https://github.com/jitsi/lib-jitsi-meet/pull/136
187
     // @see https://github.com/jitsi/lib-jitsi-meet/pull/136
188
     const isNewStyleConstraintsSupported
188
     const isNewStyleConstraintsSupported
189
-        = RTCBrowserType.isFirefox()
190
-        || RTCBrowserType.isEdge()
191
-        || RTCBrowserType.isReactNative()
192
-        || RTCBrowserType.isTemasysPluginUsed();
189
+        = browser.isFirefox()
190
+        || browser.isEdge()
191
+        || browser.isReactNative()
192
+        || browser.isTemasysPluginUsed();
193
 
193
 
194
     if (um.indexOf('video') >= 0) {
194
     if (um.indexOf('video') >= 0) {
195
         // same behaviour as true
195
         // same behaviour as true
239
             constraints, isNewStyleConstraintsSupported, options.resolution);
239
             constraints, isNewStyleConstraintsSupported, options.resolution);
240
     }
240
     }
241
     if (um.indexOf('audio') >= 0) {
241
     if (um.indexOf('audio') >= 0) {
242
-        if (RTCBrowserType.isReactNative()) {
242
+        if (browser.isReactNative()) {
243
             // The react-native-webrtc project that we're currently using
243
             // The react-native-webrtc project that we're currently using
244
             // expects the audio constraint to be a boolean.
244
             // expects the audio constraint to be a boolean.
245
             constraints.audio = true;
245
             constraints.audio = true;
246
-        } else if (RTCBrowserType.isFirefox()) {
246
+        } else if (browser.isFirefox()) {
247
             if (options.micDeviceId) {
247
             if (options.micDeviceId) {
248
                 constraints.audio = {
248
                 constraints.audio = {
249
                     mandatory: {},
249
                     mandatory: {},
284
         }
284
         }
285
     }
285
     }
286
     if (um.indexOf('screen') >= 0) {
286
     if (um.indexOf('screen') >= 0) {
287
-        if (RTCBrowserType.isChrome()) {
287
+        if (browser.isChrome()) {
288
             constraints.video = {
288
             constraints.video = {
289
                 mandatory: {
289
                 mandatory: {
290
                     chromeMediaSource: 'screen',
290
                     chromeMediaSource: 'screen',
294
                 },
294
                 },
295
                 optional: []
295
                 optional: []
296
             };
296
             };
297
-        } else if (RTCBrowserType.isTemasysPluginUsed()) {
297
+        } else if (browser.isTemasysPluginUsed()) {
298
             constraints.video = {
298
             constraints.video = {
299
                 optional: [
299
                 optional: [
300
                     {
300
                     {
302
                     }
302
                     }
303
                 ]
303
                 ]
304
             };
304
             };
305
-        } else if (RTCBrowserType.isFirefox()) {
305
+        } else if (browser.isFirefox()) {
306
             constraints.video = {
306
             constraints.video = {
307
                 mozMediaSource: 'window',
307
                 mozMediaSource: 'window',
308
                 mediaSource: 'window'
308
                 mediaSource: 'window'
343
     // seems to work only when enabled in one getUserMedia call, we cannot get
343
     // seems to work only when enabled in one getUserMedia call, we cannot get
344
     // fake audio separate by fake video this later can be a problem with some
344
     // fake audio separate by fake video this later can be a problem with some
345
     // of the tests
345
     // of the tests
346
-    if (RTCBrowserType.isFirefox() && options.firefox_fake_device) {
346
+    if (browser.isFirefox() && options.firefox_fake_device) {
347
         // seems to be fixed now, removing this experimental fix, as having
347
         // seems to be fixed now, removing this experimental fix, as having
348
         // multiple audio tracks brake the tests
348
         // multiple audio tracks brake the tests
349
         // constraints.audio = true;
349
         // constraints.audio = true;
874
         initRawEnumerateDevicesWithCallback();
874
         initRawEnumerateDevicesWithCallback();
875
 
875
 
876
         return new Promise((resolve, reject) => {
876
         return new Promise((resolve, reject) => {
877
-            if (RTCBrowserType.usesNewGumFlow()) {
877
+            if (browser.usesNewGumFlow()) {
878
                 this.RTCPeerConnectionType = window.RTCPeerConnection;
878
                 this.RTCPeerConnectionType = window.RTCPeerConnection;
879
 
879
 
880
                 this.getUserMedia
880
                 this.getUserMedia
918
 
918
 
919
                 this.getStreamID = stream => stream.id;
919
                 this.getStreamID = stream => stream.id;
920
                 this.getTrackID = track => track.id;
920
                 this.getTrackID = track => track.id;
921
-            } else if (RTCBrowserType.isChrome() // this is chrome < 61
922
-                    || RTCBrowserType.isOpera()
923
-                    || RTCBrowserType.isNWJS()
924
-                    || RTCBrowserType.isElectron()
925
-                    || RTCBrowserType.isReactNative()) {
921
+            } else if (browser.isChrome() // this is chrome < 61
922
+                    || browser.isOpera()
923
+                    || browser.isNWJS()
924
+                    || browser.isElectron()
925
+                    || browser.isReactNative()) {
926
 
926
 
927
                 this.RTCPeerConnectionType = webkitRTCPeerConnection;
927
                 this.RTCPeerConnectionType = webkitRTCPeerConnection;
928
                 const getUserMedia
928
                 const getUserMedia
968
                         return this.audioTracks;
968
                         return this.audioTracks;
969
                     };
969
                     };
970
                 }
970
                 }
971
-            } else if (RTCBrowserType.isEdge()) {
971
+            } else if (browser.isEdge()) {
972
                 this.RTCPeerConnectionType = ortcRTCPeerConnection;
972
                 this.RTCPeerConnectionType = ortcRTCPeerConnection;
973
                 this.getUserMedia
973
                 this.getUserMedia
974
                     = wrapGetUserMedia(
974
                     = wrapGetUserMedia(
1001
                 this.getTrackID = function(track) {
1001
                 this.getTrackID = function(track) {
1002
                     return track.jitsiRemoteId || track.id;
1002
                     return track.jitsiRemoteId || track.id;
1003
                 };
1003
                 };
1004
-            } else if (RTCBrowserType.isTemasysPluginUsed()) {
1004
+            } else if (browser.isTemasysPluginUsed()) {
1005
                 // Detect IE/Safari
1005
                 // Detect IE/Safari
1006
                 const webRTCReadyCb = () => {
1006
                 const webRTCReadyCb = () => {
1007
                     this.RTCPeerConnectionType = RTCPeerConnection;
1007
                     this.RTCPeerConnectionType = RTCPeerConnection;
1021
                                 // is in use
1021
                                 // is in use
1022
                                 const containerSel = $(element);
1022
                                 const containerSel = $(element);
1023
 
1023
 
1024
-                                if (RTCBrowserType.isTemasysPluginUsed()
1024
+                                if (browser.isTemasysPluginUsed()
1025
                                         && !containerSel.is(':visible')) {
1025
                                         && !containerSel.is(':visible')) {
1026
                                     containerSel.show();
1026
                                     containerSel.show();
1027
                                 }
1027
                                 }
1081
             this._initPCConstraints(options);
1081
             this._initPCConstraints(options);
1082
 
1082
 
1083
             // Call onReady() if Temasys plugin is not used
1083
             // Call onReady() if Temasys plugin is not used
1084
-            if (!RTCBrowserType.isTemasysPluginUsed()) {
1084
+            if (!browser.isTemasysPluginUsed()) {
1085
                 onReady(options, this.getUserMediaWithConstraints.bind(this));
1085
                 onReady(options, this.getUserMediaWithConstraints.bind(this));
1086
                 resolve();
1086
                 resolve();
1087
             }
1087
             }
1104
      * in peer to peer connection mode.
1104
      * in peer to peer connection mode.
1105
      */
1105
      */
1106
     _initPCConstraints(options) {
1106
     _initPCConstraints(options) {
1107
-        if (RTCBrowserType.isFirefox()) {
1107
+        if (browser.isFirefox()) {
1108
             this.pcConstraints = {};
1108
             this.pcConstraints = {};
1109
-        } else if (RTCBrowserType.isChrome()
1110
-            || RTCBrowserType.isOpera()
1111
-            || RTCBrowserType.isNWJS()
1112
-            || RTCBrowserType.isElectron()
1113
-            || RTCBrowserType.isReactNative()) {
1109
+        } else if (browser.isChrome()
1110
+            || browser.isOpera()
1111
+            || browser.isNWJS()
1112
+            || browser.isElectron()
1113
+            || browser.isReactNative()) {
1114
             this.pcConstraints = { optional: [
1114
             this.pcConstraints = { optional: [
1115
                 { googHighStartBitrate: 0 },
1115
                 { googHighStartBitrate: 0 },
1116
                 { googPayloadPadding: true },
1116
                 { googPayloadPadding: true },
1249
     _newGetDesktopMedia(
1249
     _newGetDesktopMedia(
1250
             desktopSharingExtensionExternalInstallation,
1250
             desktopSharingExtensionExternalInstallation,
1251
             desktopSharingSources) {
1251
             desktopSharingSources) {
1252
-        if (!screenObtainer.isSupported() || !RTCBrowserType.supportsVideo()) {
1252
+        if (!screenObtainer.isSupported() || !browser.supportsVideo()) {
1253
             return Promise.reject(
1253
             return Promise.reject(
1254
                 new Error('Desktop sharing is not supported!'));
1254
                 new Error('Desktop sharing is not supported!'));
1255
         }
1255
         }
1306
                 && options.devices.indexOf('desktop') !== -1) {
1306
                 && options.devices.indexOf('desktop') !== -1) {
1307
                 reject(new Error('Desktop sharing is not supported!'));
1307
                 reject(new Error('Desktop sharing is not supported!'));
1308
             }
1308
             }
1309
-            if (RTCBrowserType.isFirefox()
1309
+            if (browser.isFirefox()
1310
 
1310
 
1311
                     // XXX The react-native-webrtc implementation that we
1311
                     // XXX The react-native-webrtc implementation that we
1312
                     // utilize on React Native at the time of this writing does
1312
                     // utilize on React Native at the time of this writing does
1314
                     // https://www.w3.org/TR/mediacapture-streams/#constructors
1314
                     // https://www.w3.org/TR/mediacapture-streams/#constructors
1315
                     // and instead has a single constructor which expects (an
1315
                     // and instead has a single constructor which expects (an
1316
                     // NSNumber as) a MediaStream ID.
1316
                     // NSNumber as) a MediaStream ID.
1317
-                    || RTCBrowserType.isReactNative()
1318
-                    || RTCBrowserType.isTemasysPluginUsed()) {
1317
+                    || browser.isReactNative()
1318
+                    || browser.isTemasysPluginUsed()) {
1319
                 const GUM = function(device, s, e) {
1319
                 const GUM = function(device, s, e) {
1320
                     this.getUserMediaWithConstraints(device, s, e, options);
1320
                     this.getUserMediaWithConstraints(device, s, e, options);
1321
                 };
1321
                 };
1525
             const umDevices = options.devices || [ 'audio', 'video' ];
1525
             const umDevices = options.devices || [ 'audio', 'video' ];
1526
             const requestedCaptureDevices = umDevices.filter(device =>
1526
             const requestedCaptureDevices = umDevices.filter(device =>
1527
                 device === 'audio'
1527
                 device === 'audio'
1528
-                || (device === 'video' && RTCBrowserType.supportsVideo()));
1528
+                || (device === 'video' && browser.supportsVideo()));
1529
 
1529
 
1530
             if (!requestedCaptureDevices.length) {
1530
             if (!requestedCaptureDevices.length) {
1531
                 return Promise.resolve();
1531
                 return Promise.resolve();
1662
     isDeviceChangeAvailable(deviceType) {
1662
     isDeviceChangeAvailable(deviceType) {
1663
         return deviceType === 'output' || deviceType === 'audiooutput'
1663
         return deviceType === 'output' || deviceType === 'audiooutput'
1664
             ? isAudioOutputDeviceChangeAvailable
1664
             ? isAudioOutputDeviceChangeAvailable
1665
-            : RTCBrowserType.isChrome()
1666
-                || RTCBrowserType.isFirefox()
1667
-                || RTCBrowserType.isOpera()
1668
-                || RTCBrowserType.isTemasysPluginUsed()
1669
-                || RTCBrowserType.isNWJS()
1670
-                || RTCBrowserType.isElectron()
1671
-                || RTCBrowserType.isEdge();
1665
+            : browser.isChrome()
1666
+                || browser.isFirefox()
1667
+                || browser.isOpera()
1668
+                || browser.isTemasysPluginUsed()
1669
+                || browser.isNWJS()
1670
+                || browser.isElectron()
1671
+                || browser.isEdge();
1672
     }
1672
     }
1673
 
1673
 
1674
     /**
1674
     /**
1679
     stopMediaStream(mediaStream) {
1679
     stopMediaStream(mediaStream) {
1680
         mediaStream.getTracks().forEach(track => {
1680
         mediaStream.getTracks().forEach(track => {
1681
             // stop() not supported with IE
1681
             // stop() not supported with IE
1682
-            if (!RTCBrowserType.isTemasysPluginUsed() && track.stop) {
1682
+            if (!browser.isTemasysPluginUsed() && track.stop) {
1683
                 track.stop();
1683
                 track.stop();
1684
             }
1684
             }
1685
         });
1685
         });

+ 12
- 14
modules/RTC/ScreenObtainer.js View File

2
 
2
 
3
 import JitsiTrackError from '../../JitsiTrackError';
3
 import JitsiTrackError from '../../JitsiTrackError';
4
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
4
 import * as JitsiTrackErrors from '../../JitsiTrackErrors';
5
-import RTCBrowserType from './RTCBrowserType';
5
+import browser from '../browser';
6
 
6
 
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
7
 const logger = require('jitsi-meet-logger').getLogger(__filename);
8
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
8
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
139
      * @private
139
      * @private
140
      */
140
      */
141
     _createObtainStreamMethod(options) {
141
     _createObtainStreamMethod(options) {
142
-        if (RTCBrowserType.isNWJS()) {
142
+        if (browser.isNWJS()) {
143
             return (_, onSuccess, onFailure) => {
143
             return (_, onSuccess, onFailure) => {
144
                 window.JitsiMeetNW.obtainDesktopStream(
144
                 window.JitsiMeetNW.obtainDesktopStream(
145
                     onSuccess,
145
                     onSuccess,
174
                             && onFailure(jitsiError);
174
                             && onFailure(jitsiError);
175
                     });
175
                     });
176
             };
176
             };
177
-        } else if (RTCBrowserType.isElectron()) {
177
+        } else if (browser.isElectron()) {
178
             return this.obtainScreenOnElectron;
178
             return this.obtainScreenOnElectron;
179
-        } else if (RTCBrowserType.isTemasysPluginUsed()) {
179
+        } else if (browser.isTemasysPluginUsed()) {
180
             // XXX Don't require Temasys unless it's to be used because it
180
             // XXX Don't require Temasys unless it's to be used because it
181
             // doesn't run on React Native, for example.
181
             // doesn't run on React Native, for example.
182
             const plugin
182
             const plugin
198
             logger.info('Using Temasys plugin for desktop sharing');
198
             logger.info('Using Temasys plugin for desktop sharing');
199
 
199
 
200
             return obtainWebRTCScreen;
200
             return obtainWebRTCScreen;
201
-        } else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) {
202
-            if ((RTCBrowserType.getChromeVersion()
203
-                    || RTCBrowserType.getOperaVersion()) < 34) {
201
+        } else if (browser.isChrome() || browser.isOpera()) {
202
+            if (browser.isVersionLessThan('34')) {
204
                 logger.info('Chrome extension not supported until ver 34');
203
                 logger.info('Chrome extension not supported until ver 34');
205
 
204
 
206
                 return null;
205
                 return null;
219
                 });
218
                 });
220
 
219
 
221
             return this.obtainScreenFromExtension;
220
             return this.obtainScreenFromExtension;
222
-        } else if (RTCBrowserType.isFirefox()) {
221
+        } else if (browser.isFirefox()) {
223
             if (options.desktopSharingFirefoxDisabled) {
222
             if (options.desktopSharingFirefoxDisabled) {
224
                 return null;
223
                 return null;
225
             } else if (window.location.protocol === 'http:') {
224
             } else if (window.location.protocol === 'http:') {
236
 
235
 
237
         logger.log(
236
         logger.log(
238
             'Screen sharing not supported by the current browser: ',
237
             'Screen sharing not supported by the current browser: ',
239
-            RTCBrowserType.getBrowserType(),
240
-            RTCBrowserType.getBrowserName());
238
+            browser.getName());
241
 
239
 
242
         return null;
240
         return null;
243
     },
241
     },
262
 
260
 
263
         if (desktopSharingFirefoxMaxVersionExtRequired === -1
261
         if (desktopSharingFirefoxMaxVersionExtRequired === -1
264
             || (desktopSharingFirefoxMaxVersionExtRequired >= 0
262
             || (desktopSharingFirefoxMaxVersionExtRequired >= 0
265
-                && RTCBrowserType.getFirefoxVersion()
266
-                    <= desktopSharingFirefoxMaxVersionExtRequired)) {
263
+                && !browser.isVersionGreaterThan(
264
+                    desktopSharingFirefoxMaxVersionExtRequired))) {
267
             extensionRequired = true;
265
             extensionRequired = true;
268
             logger.log(
266
             logger.log(
269
                 `Jidesha extension required on firefox version ${
267
                 `Jidesha extension required on firefox version ${
270
-                    RTCBrowserType.getFirefoxVersion()}`);
268
+                    browser.getVersion()}`);
271
         }
269
         }
272
 
270
 
273
         if (!extensionRequired || firefoxExtInstalled === true) {
271
         if (!extensionRequired || firefoxExtInstalled === true) {
394
             // extension "Download Chrome Extension" allows us to open
392
             // extension "Download Chrome Extension" allows us to open
395
             // the chrome webstore and install from there and then activate our
393
             // the chrome webstore and install from there and then activate our
396
             // extension
394
             // extension
397
-            if (RTCBrowserType.isOpera()) {
395
+            if (browser.isOpera()) {
398
                 this.handleExternalInstall(options, streamCallback,
396
                 this.handleExternalInstall(options, streamCallback,
399
                     failCallback);
397
                     failCallback);
400
 
398
 

+ 30
- 30
modules/RTC/TraceablePeerConnection.js View File

9
 import LocalSdpMunger from './LocalSdpMunger';
9
 import LocalSdpMunger from './LocalSdpMunger';
10
 import RTC from './RTC';
10
 import RTC from './RTC';
11
 import RTCUtils from './RTCUtils';
11
 import RTCUtils from './RTCUtils';
12
-import RTCBrowserType from './RTCBrowserType';
12
+import browser from '../browser';
13
 import RTCEvents from '../../service/RTC/RTCEvents';
13
 import RTCEvents from '../../service/RTC/RTCEvents';
14
 import RtxModifier from '../xmpp/RtxModifier';
14
 import RtxModifier from '../xmpp/RtxModifier';
15
 
15
 
216
         logger.debug(what, info);
216
         logger.debug(what, info);
217
 
217
 
218
         /*
218
         /*
219
-        if (info && RTCBrowserType.isIExplorer()) {
219
+        if (info && browser.isIExplorer()) {
220
             if (info.length > 1024) {
220
             if (info.length > 1024) {
221
                 logger.warn('WTRACE', what, info.substr(1024));
221
                 logger.warn('WTRACE', what, info.substr(1024));
222
             }
222
             }
233
     this.onicecandidate = null;
233
     this.onicecandidate = null;
234
     this.peerconnection.onicecandidate = event => {
234
     this.peerconnection.onicecandidate = event => {
235
         // FIXME: this causes stack overflow with Temasys Plugin
235
         // FIXME: this causes stack overflow with Temasys Plugin
236
-        if (!RTCBrowserType.isTemasysPluginUsed()) {
236
+        if (!browser.isTemasysPluginUsed()) {
237
             this.trace(
237
             this.trace(
238
                 'onicecandidate',
238
                 'onicecandidate',
239
                 JSON.stringify(event.candidate, null, ' '));
239
                 JSON.stringify(event.candidate, null, ' '));
277
     };
277
     };
278
 
278
 
279
     // XXX: do all non-firefox browsers which we support also support this?
279
     // XXX: do all non-firefox browsers which we support also support this?
280
-    if (!RTCBrowserType.isFirefox() && this.maxstats) {
280
+    if (!browser.isFirefox() && this.maxstats) {
281
         this.statsinterval = window.setInterval(() => {
281
         this.statsinterval = window.setInterval(() => {
282
             this.peerconnection.getStats(stats => {
282
             this.peerconnection.getStats(stats => {
283
                 const results = stats.result();
283
                 const results = stats.result();
378
  */
378
  */
379
 TraceablePeerConnection.prototype.isSimulcastOn = function() {
379
 TraceablePeerConnection.prototype.isSimulcastOn = function() {
380
     return !this.options.disableSimulcast
380
     return !this.options.disableSimulcast
381
-        && RTCBrowserType.supportsSimulcast()
381
+        && browser.supportsSimulcast()
382
 
382
 
383
         // Firefox has been added as supporting simulcast, but it is
383
         // Firefox has been added as supporting simulcast, but it is
384
         // experimental so we only want to do it for firefox if the config
384
         // experimental so we only want to do it for firefox if the config
385
-        // option is set.  Unfortunately, RTCBrowserType::supportsSimulcast()
385
+        // option is set.  Unfortunately, browser::supportsSimulcast()
386
         // doesn't have a reference to the config options, so we have
386
         // doesn't have a reference to the config options, so we have
387
         // to do it here
387
         // to do it here
388
-        && (!RTCBrowserType.isFirefox()
388
+        && (!browser.isFirefox()
389
             || this.options.enableFirefoxSimulcast);
389
             || this.options.enableFirefoxSimulcast);
390
 };
390
 };
391
 
391
 
546
     }
546
     }
547
 
547
 
548
     // Bind 'addtrack'/'removetrack' event handlers
548
     // Bind 'addtrack'/'removetrack' event handlers
549
-    if (RTCBrowserType.isChrome() || RTCBrowserType.isNWJS()
550
-        || RTCBrowserType.isElectron() || RTCBrowserType.isEdge()) {
549
+    if (browser.isChrome() || browser.isNWJS()
550
+        || browser.isElectron() || browser.isEdge()) {
551
         stream.onaddtrack = event => {
551
         stream.onaddtrack = event => {
552
             this._remoteTrackAdded(stream, event.track);
552
             this._remoteTrackAdded(stream, event.track);
553
         };
553
         };
616
     ssrcLines = ssrcLines.filter(
616
     ssrcLines = ssrcLines.filter(
617
         line => {
617
         line => {
618
             const msid
618
             const msid
619
-                = RTCBrowserType.isTemasysPluginUsed() ? 'mslabel' : 'msid';
619
+                = browser.isTemasysPluginUsed() ? 'mslabel' : 'msid';
620
 
620
 
621
 
621
 
622
             return line.indexOf(`${msid}:${streamId}`) !== -1;
622
             return line.indexOf(`${msid}:${streamId}`) !== -1;
1184
         this.trace('getLocalDescription::preTransform', dumpSDP(desc));
1184
         this.trace('getLocalDescription::preTransform', dumpSDP(desc));
1185
 
1185
 
1186
         // if we're running on FF, transform to Plan B first.
1186
         // if we're running on FF, transform to Plan B first.
1187
-        if (RTCBrowserType.usesUnifiedPlan()) {
1187
+        if (browser.usesUnifiedPlan()) {
1188
             desc = this.interop.toPlanB(desc);
1188
             desc = this.interop.toPlanB(desc);
1189
             this.trace('getLocalDescription::postTransform (Plan B)',
1189
             this.trace('getLocalDescription::postTransform (Plan B)',
1190
                 dumpSDP(desc));
1190
                 dumpSDP(desc));
1194
                 dumpSDP(desc));
1194
                 dumpSDP(desc));
1195
         }
1195
         }
1196
 
1196
 
1197
-        if (RTCBrowserType.doesVideoMuteByStreamRemove()) {
1197
+        if (browser.doesVideoMuteByStreamRemove()) {
1198
             desc = this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(desc);
1198
             desc = this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(desc);
1199
             logger.debug(
1199
             logger.debug(
1200
                 'getLocalDescription::postTransform (munge local SDP)', desc);
1200
                 'getLocalDescription::postTransform (munge local SDP)', desc);
1218
         this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
1218
         this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
1219
 
1219
 
1220
         // if we're running on FF, transform to Plan B first.
1220
         // if we're running on FF, transform to Plan B first.
1221
-        if (RTCBrowserType.usesUnifiedPlan()) {
1221
+        if (browser.usesUnifiedPlan()) {
1222
             desc = this.interop.toPlanB(desc);
1222
             desc = this.interop.toPlanB(desc);
1223
             this.trace(
1223
             this.trace(
1224
                 'getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
1224
                 'getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
1264
         this._addStream(webrtcStream);
1264
         this._addStream(webrtcStream);
1265
 
1265
 
1266
     // It's not ok for a track to not have a WebRTC stream if:
1266
     // It's not ok for a track to not have a WebRTC stream if:
1267
-    } else if (!RTCBrowserType.doesVideoMuteByStreamRemove()
1267
+    } else if (!browser.doesVideoMuteByStreamRemove()
1268
                 || track.isAudioTrack()
1268
                 || track.isAudioTrack()
1269
                 || (track.isVideoTrack() && !track.isMuted())) {
1269
                 || (track.isVideoTrack() && !track.isMuted())) {
1270
         logger.error(`${this} no WebRTC stream for: ${track}`);
1270
         logger.error(`${this} no WebRTC stream for: ${track}`);
1271
     }
1271
     }
1272
 
1272
 
1273
     // Muted video tracks do not have WebRTC stream
1273
     // Muted video tracks do not have WebRTC stream
1274
-    if (RTCBrowserType.doesVideoMuteByStreamRemove()
1274
+    if (browser.doesVideoMuteByStreamRemove()
1275
             && track.isVideoTrack() && track.isMuted()) {
1275
             && track.isVideoTrack() && track.isMuted()) {
1276
         const ssrcInfo = this.generateNewStreamSSRCInfo(track);
1276
         const ssrcInfo = this.generateNewStreamSSRCInfo(track);
1277
 
1277
 
1342
  * @param {MediaStream} mediaStream
1342
  * @param {MediaStream} mediaStream
1343
  */
1343
  */
1344
 TraceablePeerConnection.prototype._removeStream = function(mediaStream) {
1344
 TraceablePeerConnection.prototype._removeStream = function(mediaStream) {
1345
-    if (RTCBrowserType.isFirefox()) {
1345
+    if (browser.isFirefox()) {
1346
         this._handleFirefoxRemoveStream(mediaStream);
1346
         this._handleFirefoxRemoveStream(mediaStream);
1347
     } else {
1347
     } else {
1348
         this.peerconnection.removeStream(mediaStream);
1348
         this.peerconnection.removeStream(mediaStream);
1407
     this.localSSRCs.delete(localTrack.rtcId);
1407
     this.localSSRCs.delete(localTrack.rtcId);
1408
 
1408
 
1409
     if (webRtcStream) {
1409
     if (webRtcStream) {
1410
-        if (RTCBrowserType.isFirefox()) {
1410
+        if (browser.isFirefox()) {
1411
             this._handleFirefoxRemoveStream(webRtcStream);
1411
             this._handleFirefoxRemoveStream(webRtcStream);
1412
         } else {
1412
         } else {
1413
             this.peerconnection.removeStream(webRtcStream);
1413
             this.peerconnection.removeStream(webRtcStream);
1627
     localSdp = this._ensureSimulcastGroupIsLast(localSdp);
1627
     localSdp = this._ensureSimulcastGroupIsLast(localSdp);
1628
 
1628
 
1629
     // if we're using unified plan, transform to it first.
1629
     // if we're using unified plan, transform to it first.
1630
-    if (RTCBrowserType.usesUnifiedPlan()) {
1630
+    if (browser.usesUnifiedPlan()) {
1631
         localSdp = this.interop.toUnifiedPlan(localSdp);
1631
         localSdp = this.interop.toUnifiedPlan(localSdp);
1632
         this.trace(
1632
         this.trace(
1633
             'setLocalDescription::postTransform (Unified Plan)',
1633
             'setLocalDescription::postTransform (Unified Plan)',
1746
     }
1746
     }
1747
 
1747
 
1748
     // If the browser uses unified plan, transform to it first
1748
     // If the browser uses unified plan, transform to it first
1749
-    if (RTCBrowserType.usesUnifiedPlan()) {
1749
+    if (browser.usesUnifiedPlan()) {
1750
         // eslint-disable-next-line no-param-reassign
1750
         // eslint-disable-next-line no-param-reassign
1751
         description = new RTCSessionDescription({
1751
         description = new RTCSessionDescription({
1752
             type: description.type,
1752
             type: description.type,
1778
 
1778
 
1779
     // Safari WebRTC errors when no supported video codec is found in the offer.
1779
     // Safari WebRTC errors when no supported video codec is found in the offer.
1780
     // To prevent the error, inject H264 into the video mLine.
1780
     // To prevent the error, inject H264 into the video mLine.
1781
-    if (RTCBrowserType.isSafariWithWebrtc()) {
1781
+    if (browser.isSafariWithWebrtc()) {
1782
         logger.debug('Maybe injecting H264 into the remote description');
1782
         logger.debug('Maybe injecting H264 into the remote description');
1783
 
1783
 
1784
         // eslint-disable-next-line no-param-reassign
1784
         // eslint-disable-next-line no-param-reassign
1966
  * @private
1966
  * @private
1967
  */
1967
  */
1968
 const _fixAnswerRFC4145Setup = function(offer, answer) {
1968
 const _fixAnswerRFC4145Setup = function(offer, answer) {
1969
-    if (!RTCBrowserType.isChrome()) {
1969
+    if (!browser.isChrome()) {
1970
         // It looks like Firefox doesn't agree with the fix (at least in its
1970
         // It looks like Firefox doesn't agree with the fix (at least in its
1971
         // current implementation) because it effectively remains active even
1971
         // current implementation) because it effectively remains active even
1972
         // after we tell it to become passive. Apart from Firefox which I tested
1972
         // after we tell it to become passive. Apart from Firefox which I tested
2017
         successCallback,
2017
         successCallback,
2018
         failureCallback,
2018
         failureCallback,
2019
         constraints) {
2019
         constraints) {
2020
-    if (RTCBrowserType.supportsRtpSender() && this.isSimulcastOn()) {
2020
+    if (browser.supportsRtpSender() && this.isSimulcastOn()) {
2021
         const videoSender
2021
         const videoSender
2022
             = this.peerconnection.getSenders().find(sender =>
2022
             = this.peerconnection.getSenders().find(sender =>
2023
                 sender.track.kind === 'video');
2023
                 sender.track.kind === 'video');
2068
                 `create${logName}OnSuccess::preTransform`, dumpSDP(resultSdp));
2068
                 `create${logName}OnSuccess::preTransform`, dumpSDP(resultSdp));
2069
 
2069
 
2070
             // if we're using unified plan, transform to Plan B.
2070
             // if we're using unified plan, transform to Plan B.
2071
-            if (RTCBrowserType.usesUnifiedPlan()) {
2071
+            if (browser.usesUnifiedPlan()) {
2072
                 // eslint-disable-next-line no-param-reassign
2072
                 // eslint-disable-next-line no-param-reassign
2073
                 resultSdp = this.interop.toPlanB(resultSdp);
2073
                 resultSdp = this.interop.toPlanB(resultSdp);
2074
                 this.trace(
2074
                 this.trace(
2092
              *  after that, when we try and go back to unified plan it will
2092
              *  after that, when we try and go back to unified plan it will
2093
              *  complain about unmapped ssrcs)
2093
              *  complain about unmapped ssrcs)
2094
              */
2094
              */
2095
-            if (!RTCBrowserType.isFirefox()) {
2095
+            if (!browser.isFirefox()) {
2096
                 // If there are no local video tracks, then a "recvonly"
2096
                 // If there are no local video tracks, then a "recvonly"
2097
                 // SSRC needs to be generated
2097
                 // SSRC needs to be generated
2098
                 if (!this.hasAnyTracksOfType(MediaType.VIDEO)
2098
                 if (!this.hasAnyTracksOfType(MediaType.VIDEO)
2124
                     dumpSDP(resultSdp));
2124
                     dumpSDP(resultSdp));
2125
             }
2125
             }
2126
 
2126
 
2127
-            if (!this.options.disableRtx && RTCBrowserType.supportsRtx()) {
2127
+            if (!this.options.disableRtx && browser.supportsRtx()) {
2128
                 // eslint-disable-next-line no-param-reassign
2128
                 // eslint-disable-next-line no-param-reassign
2129
                 resultSdp = new RTCSessionDescription({
2129
                 resultSdp = new RTCSessionDescription({
2130
                     type: resultSdp.type,
2130
                     type: resultSdp.type,
2299
     // TODO (brian): After moving all browsers to adapter, check if adapter is
2299
     // TODO (brian): After moving all browsers to adapter, check if adapter is
2300
     // accounting for different getStats apis, making the browser-checking-if
2300
     // accounting for different getStats apis, making the browser-checking-if
2301
     // unnecessary.
2301
     // unnecessary.
2302
-    if (RTCBrowserType.isFirefox()
2303
-            || RTCBrowserType.isTemasysPluginUsed()
2304
-            || RTCBrowserType.isReactNative()) {
2302
+    if (browser.isFirefox()
2303
+            || browser.isTemasysPluginUsed()
2304
+            || browser.isReactNative()) {
2305
         this.peerconnection.getStats(
2305
         this.peerconnection.getStats(
2306
             null,
2306
             null,
2307
             callback,
2307
             callback,
2310
                 // Making sure that getStats won't fail if error callback is
2310
                 // Making sure that getStats won't fail if error callback is
2311
                 // not passed.
2311
                 // not passed.
2312
             }));
2312
             }));
2313
-    } else if (RTCBrowserType.isSafariWithWebrtc()) {
2313
+    } else if (browser.isSafariWithWebrtc()) {
2314
         // FIXME: Safari's native stats implementation is not compatibile with
2314
         // FIXME: Safari's native stats implementation is not compatibile with
2315
         // existing stats processing logic. Skip implementing stats for now to
2315
         // existing stats processing logic. Skip implementing stats for now to
2316
         // at least get native webrtc Safari available for use.
2316
         // at least get native webrtc Safari available for use.
2351
             groups: []
2351
             groups: []
2352
         };
2352
         };
2353
     }
2353
     }
2354
-    if (!this.options.disableRtx && RTCBrowserType.supportsRtx()) {
2354
+    if (!this.options.disableRtx && browser.supportsRtx()) {
2355
         // Specifically use a for loop here because we'll
2355
         // Specifically use a for loop here because we'll
2356
         //  be adding to the list we're iterating over, so we
2356
         //  be adding to the list we're iterating over, so we
2357
         //  only want to iterate through the items originally
2357
         //  only want to iterate through the items originally

+ 1
- 1
modules/RTC/ortc/README.md View File

17
 
17
 
18
 * BUNDLE is assumed (single transport for all the local and remote media streams).
18
 * BUNDLE is assumed (single transport for all the local and remote media streams).
19
 * `rtcp-mux` is assumed (not a real problem nowadays).
19
 * `rtcp-mux` is assumed (not a real problem nowadays).
20
-* Calling `createOffer()` is not implemented, so P2P mode is not supported (`RTCBrowserType.isP2PSupported()` returns `false`).
20
+* Calling `createOffer()` is not implemented, so P2P mode is not supported (`browser.supportsP2P()` returns `false`).
21
 * Calling `setRemoteDescription()` with a SDP answer with mangled SSRC values is currently unsupported (those new SSRC values will not be used for sending media).
21
 * Calling `setRemoteDescription()` with a SDP answer with mangled SSRC values is currently unsupported (those new SSRC values will not be used for sending media).
22
 * If the app calls `createAnswer()`, mangles SSRC values, and applies by calling `setLocalDescription()`, those new SSRC values are currently ignored.
22
 * If the app calls `createAnswer()`, mangles SSRC values, and applies by calling `setLocalDescription()`, those new SSRC values are currently ignored.
23
 * Simulcast not supported (currently Edge supports `maxFramerate` per encoding but it does not support `maxBitrate`, `resolutionScale` or `framerateScale`, so it's not worth it).
23
 * Simulcast not supported (currently Edge supports `maxFramerate` per encoding but it does not support `maxBitrate`, `resolutionScale` or `framerateScale`, so it's not worth it).

+ 280
- 0
modules/browser/BrowserCapabilities.js View File

1
+import BrowserDetection from './BrowserDetection';
2
+import capabilitiesDB from './capabilities.json';
3
+
4
+// TODO: All methods should use capabilities.json when possible.
5
+
6
+// NOTE: Now we are extending BrowserDetection in order to preserve
7
+// RTCBrowserType interface but maybe it worth exporting BrowserCapabilities
8
+// and BrowserDetection as separate objects in future.
9
+
10
+/**
11
+ * Implements browser capabilities for lib-jitsi-meet.
12
+ */
13
+export default class BrowserCapabilities extends BrowserDetection {
14
+    /**
15
+     * Creates new BrowserCapabilities instance.
16
+     *
17
+     * @param {boolean} [isUsingIFrame] - True if Jitsi Meet is loaded in iframe
18
+     * and false otherwise.
19
+     * @param {Object} [browserInfo] - Information about the browser.
20
+     * @param {string} browserInfo.name - The name of the browser.
21
+     * @param {string} browserInfo.version - The version of the browser.
22
+     */
23
+    constructor(isUsingIFrame = false, browserInfo) {
24
+        super(browserInfo);
25
+
26
+        const browserCapabilities = capabilitiesDB[this.getName()] || [];
27
+        const capabilitiesByVersion = browserCapabilities.find(({ version }) =>
28
+            !version || !this.isVersionLessThan(version));
29
+
30
+        if (!capabilitiesByVersion || !capabilitiesByVersion.capabilities) {
31
+            this._capabilities = { isSupported: false };
32
+        } else if (isUsingIFrame) {
33
+            this._capabilities = {
34
+                ...capabilitiesByVersion.capabilities,
35
+                ...capabilitiesByVersion.iframeCapabilities
36
+            };
37
+        } else {
38
+            this._capabilities = capabilitiesByVersion.capabilities;
39
+        }
40
+
41
+        if (typeof this._capabilities.isSupported === 'undefined') {
42
+            // we have some capabilities but isSupported property is not filled.
43
+            this._capabilities.isSupported = true;
44
+        } else if (this._capabilities.isSupported === false) {
45
+            this._capabilities = { isSupported: false };
46
+        }
47
+    }
48
+
49
+    /**
50
+     * Tells whether or not the <tt>MediaStream/tt> is removed from
51
+     * the <tt>PeerConnection</tt> and disposed on video mute (in order to turn
52
+     * off the camera device).
53
+     * @return {boolean} <tt>true</tt> if the current browser supports this
54
+     * strategy or <tt>false</tt> otherwise.
55
+     */
56
+    doesVideoMuteByStreamRemove() {
57
+        return !(
58
+            this.isFirefox()
59
+            || this.isEdge()
60
+            || this.isSafariWithWebrtc()
61
+        );
62
+    }
63
+
64
+    /**
65
+     * Check whether or not the current browser support peer to peer connections
66
+     * @return {boolean} <tt>true</tt> if p2p is supported or <tt>false</tt>
67
+     * otherwise.
68
+     */
69
+    supportsP2P() {
70
+        return !this.isEdge();
71
+    }
72
+
73
+    /**
74
+     * Checks if current browser is a Safari and a version of Safari that
75
+     * supports native webrtc.
76
+     *
77
+     * @returns {boolean}
78
+     */
79
+    isSafariWithWebrtc() {
80
+        return this.isSafari()
81
+            && !this.isVersionLessThan('11');
82
+    }
83
+
84
+    /**
85
+     * Checks whether the browser is supported by Jitsi Meet.
86
+     *
87
+     * @returns {boolean}
88
+     */
89
+    isSupported() {
90
+        return this._capabilities.isSupported;
91
+    }
92
+
93
+    /**
94
+     * Checks if Temasys RTC plugin is used.
95
+     * @returns {boolean}
96
+     */
97
+    isTemasysPluginUsed() {
98
+        // Temasys do not support Microsoft Edge:
99
+        // http://support.temasys.com.sg/support/solutions/articles/
100
+        // 5000654345-can-the-temasys-webrtc-plugin-be-used-with-microsoft-edge-
101
+        return (
102
+            (this.isSafari()
103
+                && !this.isSafariWithWebrtc())
104
+            || (this.isIExplorer()
105
+                && this.isVersionLessThan('12'))
106
+        );
107
+    }
108
+
109
+    /**
110
+     * Checks if the current browser triggers 'onmute'/'onunmute' events when
111
+     * user's connection is interrupted and the video stops playback.
112
+     * @returns {*|boolean} 'true' if the event is supported or 'false'
113
+     * otherwise.
114
+     */
115
+    supportsVideoMuteOnConnInterrupted() {
116
+        return this.isChrome() || this.isElectron();
117
+    }
118
+
119
+
120
+    /**
121
+     * Checks whether the browser supports incoming audio.
122
+     *
123
+     * @returns {boolean}
124
+     */
125
+    supportsAudioIn() {
126
+        return this._capabilities.audioIn || false;
127
+    }
128
+
129
+    /**
130
+     * Checks whether the browser supports outgoing audio.
131
+     *
132
+     * @returns {boolean}
133
+     */
134
+    supportsAudioOut() {
135
+        return this._capabilities.audioOut || false;
136
+    }
137
+
138
+    /**
139
+     * Checks if the current browser reports upload and download bandwidth
140
+     * statistics.
141
+     * @return {boolean}
142
+     */
143
+    supportsBandwidthStatistics() {
144
+        // FIXME bandwidth stats are currently not implemented for FF on our
145
+        // side, but not sure if not possible ?
146
+        return !this.isFirefox() && !this.isEdge();
147
+    }
148
+
149
+    /**
150
+     * Checks if the current browser supports WebRTC datachannels.
151
+     * @return {boolean}
152
+     */
153
+    supportsDataChannels() {
154
+        // NOTE: Edge does not yet implement DataChannel.
155
+        return !this.isEdge();
156
+    }
157
+
158
+    /**
159
+     * Checks if the current browser reports round trip time statistics for
160
+     * the ICE candidate pair.
161
+     * @return {boolean}
162
+     */
163
+    supportsRTTStatistics() {
164
+        // Firefox does not seem to report RTT for ICE candidate pair:
165
+        // eslint-disable-next-line max-len
166
+        // https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
167
+        // It does report mozRTT for RTP streams, but at the time of this
168
+        // writing it's value does not make sense most of the time
169
+        // (is reported as 1):
170
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=1241066
171
+        // For Chrome and others we rely on 'googRtt'.
172
+        return !this.isFirefox() && !this.isEdge();
173
+    }
174
+
175
+    /**
176
+     * Checks whether the browser supports RTPSender.
177
+     *
178
+     * @returns {boolean}
179
+     */
180
+    supportsRtpSender() {
181
+        return this.isFirefox();
182
+    }
183
+
184
+    /**
185
+     * Checks whether the browser supports RTX.
186
+     *
187
+     * @returns {boolean}
188
+     */
189
+    supportsRtx() {
190
+        return !this.isFirefox();
191
+    }
192
+
193
+    /**
194
+     * Checks whether the browser supports screen sharing.
195
+     *
196
+     * @returns {boolean}
197
+     */
198
+    supportsScreenSharing() {
199
+        return this._capabilities.screenSharing || false;
200
+    }
201
+
202
+    /**
203
+     * Whether jitsi-meet supports simulcast on the current browser.
204
+     * @returns {boolean}
205
+     */
206
+    supportsSimulcast() {
207
+        return this.isChrome()
208
+            || this.isFirefox()
209
+            || this.isElectron()
210
+            || this.isNWJS()
211
+            || this.isReactNative();
212
+    }
213
+
214
+    /**
215
+     * Returns whether or not the current browser can support capturing video,
216
+     * be it camera or desktop, and displaying received video.
217
+     *
218
+     * @returns {boolean}
219
+     */
220
+    supportsVideo() {
221
+        // FIXME: Check if we can use supportsVideoOut and supportsVideoIn. I
222
+        // leave the old implementation here in order not to brake something.
223
+
224
+        // Currently Safari using webrtc/adapter does not support video due in
225
+        // part to Safari only supporting H264 and the bridge sending VP8.
226
+        return !this.isSafariWithWebrtc();
227
+    }
228
+
229
+    /**
230
+     * Checks whether the browser supports incomming video.
231
+     *
232
+     * @returns {boolean}
233
+     */
234
+    supportsVideoIn() {
235
+        return this._capabilities.videoIn || false;
236
+    }
237
+
238
+    /**
239
+     * Checks whether the browser supports incomming video.
240
+     *
241
+     * @returns {boolean}
242
+     */
243
+    supportsVideoOut() {
244
+        return this._capabilities.videoOut || false;
245
+    }
246
+
247
+    /**
248
+     * Checks if the browser uses plan B.
249
+     *
250
+     * @returns {boolean}
251
+     */
252
+    usesPlanB() {
253
+        return !this.usesUnifiedPlan();
254
+    }
255
+
256
+    /**
257
+     * Checks if the browser uses unified plan.
258
+     *
259
+     * @returns {boolean}
260
+     */
261
+    usesUnifiedPlan() {
262
+        return this.isFirefox();
263
+    }
264
+
265
+    /**
266
+     * Returns whether or not the current browser should be using the new
267
+     * getUserMedia flow, which utilizes the adapter shim. This method should
268
+     * be temporary and used while migrating all browsers to use adapter and
269
+     * the new getUserMedia.
270
+     *
271
+     * @returns {boolean}
272
+     */
273
+    usesNewGumFlow() {
274
+        return (this.isChrome()
275
+                && !this.isVersionLessThan('61'))
276
+            || this.isFirefox()
277
+            || this.isSafariWithWebrtc();
278
+
279
+    }
280
+}

+ 305
- 0
modules/browser/BrowserDetection.js View File

1
+import bowser from 'bowser';
2
+import { getLogger } from 'jitsi-meet-logger';
3
+
4
+import {
5
+    CHROME,
6
+    OPERA,
7
+    FIREFOX,
8
+    INTERNET_EXPLORER,
9
+    EDGE,
10
+    SAFARI,
11
+    NWJS,
12
+    ELECTRON,
13
+    REACT_NATIVE
14
+} from './browsers';
15
+
16
+const logger = getLogger(__filename);
17
+
18
+/**
19
+ * Maps the names of the browsers from bowser to the internal names defined in
20
+ * ./browsers.js
21
+ */
22
+const bowserNameToJitsiName = {
23
+    'Chrome': CHROME,
24
+    'Opera': OPERA,
25
+    'Firefox': FIREFOX,
26
+    'Internet Explorer': INTERNET_EXPLORER,
27
+    'Microsoft Edge': EDGE,
28
+    'Safari': SAFARI
29
+};
30
+
31
+/**
32
+ * Detects Electron environment.
33
+ *
34
+ * @returns {Object} - The name (ELECTRON) and version.
35
+ */
36
+function _detectElectron() {
37
+    const userAgent = navigator.userAgent;
38
+
39
+    if (userAgent.match(/Electron/)) {
40
+        const version = userAgent.match(/Electron\/([\d.]+)/)[1];
41
+
42
+        logger.info(`This appears to be Electron, ver: ${version}`);
43
+
44
+        return {
45
+            name: ELECTRON,
46
+            version
47
+        };
48
+    }
49
+}
50
+
51
+/**
52
+ * Detects NWJS environment.
53
+ *
54
+ * @returns {Object} - The name (NWJS) and version.
55
+ */
56
+function _detectNWJS() {
57
+    const userAgent = navigator.userAgent;
58
+
59
+    if (userAgent.match(/JitsiMeetNW/)) {
60
+        const version = userAgent.match(/JitsiMeetNW\/([\d.]+)/)[1];
61
+
62
+        logger.info(`This appears to be JitsiMeetNW, ver: ${version}`);
63
+
64
+        return {
65
+            name: NWJS,
66
+            version
67
+        };
68
+    }
69
+}
70
+
71
+/**
72
+ * Detects React Native environment.
73
+ * @returns {Object} - The name (REACT_NATIVE) and version
74
+ */
75
+function _detectReactNative() {
76
+    const match
77
+        = navigator.userAgent.match(/\b(react[ \t_-]*native)(?:\/(\S+))?/i);
78
+    let version;
79
+
80
+    // If we're remote debugging a React Native app, it may be treated as
81
+    // Chrome. Check navigator.product as well and always return some version
82
+    // even if we can't get the real one.
83
+
84
+    if (match || navigator.product === 'ReactNative') {
85
+        let name;
86
+
87
+        if (match && match.length > 2) {
88
+            name = match[1];
89
+            version = match[2];
90
+        }
91
+        name || (name = 'react-native');
92
+        version || (version = 'unknown');
93
+        logger.info(`This appears to be ${name}, ver: ${version}`);
94
+
95
+        return {
96
+            name: REACT_NATIVE,
97
+            version
98
+        };
99
+    }
100
+}
101
+
102
+/**
103
+ * Returns information about the current browser.
104
+ *
105
+ * @returns {Object} - The name and version of the browser.
106
+ */
107
+function _detect() {
108
+    let browserInfo;
109
+    const detectors = [
110
+        _detectReactNative,
111
+        _detectElectron,
112
+        _detectNWJS
113
+    ];
114
+
115
+    // Try all browser detectors
116
+    for (let i = 0; i < detectors.length; i++) {
117
+        browserInfo = detectors[i]();
118
+        if (browserInfo) {
119
+            return browserInfo;
120
+        }
121
+    }
122
+
123
+    const { name, version } = bowser;
124
+
125
+    if (name in bowserNameToJitsiName) {
126
+        return {
127
+            name: bowserNameToJitsiName[name],
128
+            version
129
+        };
130
+    }
131
+
132
+    logger.warn('Browser type defaults to Safari ver 1');
133
+
134
+    return {
135
+        name: SAFARI,
136
+        version: '1'
137
+    };
138
+}
139
+
140
+/**
141
+ * Implements browser detection.
142
+ */
143
+export default class BrowserDetection {
144
+    /**
145
+     * Creates new BrowserDetection instance.
146
+     *
147
+     * @param {Object} [browserInfo] - Information about the browser.
148
+     * @param {string} browserInfo.name - The name of the browser.
149
+     * @param {string} browserInfo.version - The version of the browser.
150
+     */
151
+    constructor(browserInfo = _detect()) {
152
+        const { name, version } = browserInfo;
153
+
154
+        this._name = name;
155
+        this._version = version;
156
+    }
157
+
158
+    /**
159
+     * Gets current browser name.
160
+     * @returns {string}
161
+     */
162
+    getName() {
163
+        return this._name;
164
+    }
165
+
166
+    /**
167
+     * Checks if current browser is Chrome.
168
+     * @returns {boolean}
169
+     */
170
+    isChrome() {
171
+        return this._name === CHROME;
172
+    }
173
+
174
+    /**
175
+     * Checks if current browser is Opera.
176
+     * @returns {boolean}
177
+     */
178
+    isOpera() {
179
+        return this._name === OPERA;
180
+    }
181
+
182
+    /**
183
+     * Checks if current browser is Firefox.
184
+     * @returns {boolean}
185
+     */
186
+    isFirefox() {
187
+        return this._name === FIREFOX;
188
+    }
189
+
190
+    /**
191
+     * Checks if current browser is Internet Explorer.
192
+     * @returns {boolean}
193
+     */
194
+    isIExplorer() {
195
+        return this._name === INTERNET_EXPLORER;
196
+    }
197
+
198
+    /**
199
+     * Checks if current browser is Microsoft Edge.
200
+     * @returns {boolean}
201
+     */
202
+    isEdge() {
203
+        return this._name === EDGE;
204
+    }
205
+
206
+    /**
207
+     * Checks if current browser is Safari.
208
+     * @returns {boolean}
209
+     */
210
+    isSafari() {
211
+        return this._name === SAFARI;
212
+    }
213
+
214
+    /**
215
+     * Checks if current environment is NWJS.
216
+     * @returns {boolean}
217
+     */
218
+    isNWJS() {
219
+        return this._name === NWJS;
220
+    }
221
+
222
+    /**
223
+     * Checks if current environment is Electron.
224
+     * @returns {boolean}
225
+     */
226
+    isElectron() {
227
+        return this._name === ELECTRON;
228
+    }
229
+
230
+    /**
231
+     * Checks if current environment is React Native.
232
+     * @returns {boolean}
233
+     */
234
+    isReactNative() {
235
+        return this._name === REACT_NATIVE;
236
+    }
237
+
238
+    /**
239
+     * Returns the version of the current browser.
240
+     * @returns {string}
241
+     */
242
+    getVersion() {
243
+        return this._version;
244
+    }
245
+
246
+    /**
247
+     * Compares the passed version with the current browser version.
248
+     * {@see https://github.com/lancedikson/bowser}
249
+     */
250
+    static compareVersions = bowser.compareVersions;
251
+
252
+    /**
253
+     * Compares the passed version with the current browser version.
254
+     *
255
+     * @param {string} version - The version to compare with.
256
+     * @returns {number|undefined} - Returns 0 if the version is equal to the
257
+     * current one, 1 if the version is greater than the current one, -1 if the
258
+     * version is lower than the current one and undefined if the current
259
+     * browser version is unknown.
260
+     */
261
+    compareVersion(version) {
262
+        if (this._version) {
263
+            return bowser.compareVersions([ version, this._version ]);
264
+        }
265
+    }
266
+
267
+    /**
268
+     * Compares the passed version with the current browser version.
269
+     *
270
+     * @param {string} version - The version to compare with.
271
+     * @returns {boolean|undefined} - Returns true if the current version is
272
+     * greater than the passed version and false otherwise.
273
+     */
274
+    isVersionGreaterThan(version) {
275
+        if (this._version) {
276
+            return this.compareVersion(version) === 1;
277
+        }
278
+    }
279
+
280
+    /**
281
+     * Compares the passed version with the current browser version.
282
+     *
283
+     * @param {string} version - The version to compare with.
284
+     * @returns {boolean|undefined} - Returns true if the current version is
285
+     * lower than the passed version and false otherwise.
286
+     */
287
+    isVersionLessThan(version) {
288
+        if (this._version) {
289
+            return this.compareVersion(version) === -1;
290
+        }
291
+    }
292
+
293
+    /**
294
+     * Compares the passed version with the current browser version.
295
+     *
296
+     * @param {string} version - The version to compare with.
297
+     * @returns {boolean|undefined} - Returns true if the current version is
298
+     * equal to the passed version and false otherwise.
299
+     */
300
+    isVersionEqualTo(version) {
301
+        if (this._version) {
302
+            return this.compareVersion(version) === 0;
303
+        }
304
+    }
305
+}

+ 66
- 0
modules/browser/ExternalBrowserCapabilities.js View File

1
+import BrowserCapabilities from './BrowserCapabilities';
2
+
3
+/**
4
+ * API for checking the capabilities for Jitsi Meet for the current or passed
5
+ * browser.
6
+ */
7
+export default class ExternalBrowserCapabilities {
8
+    /**
9
+     * Creates new ExternalBrowserCapabilities instance.
10
+     *
11
+     * @param {boolean} [isUsingIFrame] - True if Jitsi Meet is loaded in iframe
12
+     * and false otherwise.
13
+     * @param {Object} [browserInfo] - Information about the browser.
14
+     * @param {string} browserInfo.name - The name of the browser.
15
+     * @param {string} browserInfo.version - The version of the browser.
16
+     */
17
+    constructor(isUsingIFrame, browserInfo) {
18
+        this._capabilities
19
+            = new BrowserCapabilities(isUsingIFrame, browserInfo);
20
+    }
21
+
22
+    /**
23
+     * Checks whether the browser is supported by Jitsi Meet.
24
+     *
25
+     * @returns {boolean}
26
+     */
27
+    isSupported() {
28
+        return this._capabilities.isSupported();
29
+    }
30
+
31
+    /**
32
+     * Checks whether the browser supports incoming audio.
33
+     *
34
+     * @returns {boolean}
35
+     */
36
+    supportsAudioIn() {
37
+        return this._capabilities.supportsAudioIn();
38
+    }
39
+
40
+    /**
41
+     * Checks whether the browser supports outgoing audio.
42
+     *
43
+     * @returns {boolean}
44
+     */
45
+    supportsAudioOut() {
46
+        return this._capabilities.supportsAudioOut();
47
+    }
48
+
49
+    /**
50
+     * Checks whether the browser supports video.
51
+     *
52
+     * @returns {boolean}
53
+     */
54
+    supportsVideo() {
55
+        return this._capabilities.supportsVideo();
56
+    }
57
+
58
+    /**
59
+     * Checks whether the browser supports screen sharing.
60
+     *
61
+     * @returns {boolean}
62
+     */
63
+    supportsScreenSharing() {
64
+        return this._capabilities.supportsScreenSharing();
65
+    }
66
+}

+ 21
- 0
modules/browser/browsers.js View File

1
+// TODO: Maybe fix the values to 'Chrome', 'Internet Explorer', etc. Currently
2
+// this values needs to be as they are becuse they are going to analytics,
3
+// callstats, etc.
4
+
5
+export const CHROME = 'chrome';
6
+
7
+export const OPERA = 'opera';
8
+
9
+export const FIREFOX = 'firefox';
10
+
11
+export const INTERNET_EXPLORER = 'iexplorer';
12
+
13
+export const EDGE = 'edge';
14
+
15
+export const SAFARI = 'safari';
16
+
17
+export const NWJS = 'nwjs';
18
+
19
+export const ELECTRON = 'electron';
20
+
21
+export const REACT_NATIVE = 'react-native';

+ 44
- 0
modules/browser/capabilities-structure.md View File

1
+capabilities.json stores the capabilities of Jitsi Meet per browser per version of the browser. The JSON structure is the following:
2
+```
3
+{
4
+    <browser_name>: Array<CapabilitiesByVersion>
5
+}
6
+```
7
+
8
+If there is no entry for a browser, this browser is considered unsupported. The browser name should be compatible with the names returned by the bowser package.
9
+
10
+The `CapabilitiesByVersion` objects have the following structure:
11
+```
12
+{
13
+    version: <browser_version>,
14
+    capabilities: {
15
+        ...
16
+    },
17
+    iframeCapabilities: {
18
+        ...
19
+    }
20
+}
21
+```
22
+`CapabilitiesByVersion` objects are storing the capabilities for a range of versions. The range of versions for a `CapabilitiesByVersion` object is from the version filled in the previous `CapabilitiesByVersion` object to the version filled in the current object.  For example:
23
+```
24
+"Chrome": [
25
+    {
26
+        "version": "1.0.0.0",
27
+        capabilities: A
28
+    },
29
+    {
30
+        "version": "3.0.0.0",
31
+        capabilities: B
32
+    },
33
+    {
34
+        capabilities: C
35
+    }
36
+]
37
+```
38
+
39
+For versions of Chrome lower or equal to 1.0.0.0 will have capabilities A, versions between 1.0.0.0 and 3.0.0.0 will have capabilities B and versions greater than 3.0.0.0 will have capabilities C. The last element in the array doesn't have a version property because it stores information for any version that is greater than 3.0.0.0.
40
+
41
+The capabilities property of `CapabilitiesByVersion` object stores the capabilities for the use case where Jitsi Meet is not loaded in an iframe. For the use case when Jitsi Meet is loaded in an iframe the capabilities are calculated by `Object.assign(capabilities, iframeCapabilities)`.
42
+
43
+
44
+If the calculated capabilities are `undefined` this version is considered  unsupported.

+ 121
- 0
modules/browser/capabilities.json View File

1
+{
2
+    "safari": [
3
+        {"version": "6.3" },
4
+        {
5
+            "version": "10.2",
6
+            "capabilities": {
7
+                "audioIn": true,
8
+                "audioOut": true,
9
+                "videoIn": true,
10
+                "videoOut": true,
11
+                "screenSharing": true
12
+            }
13
+        },
14
+        {
15
+            "capabilities": {
16
+                "audioIn": true,
17
+                "audioOut": true,
18
+                "videoIn": false,
19
+                "videoOut": false,
20
+                "screenSharing": false
21
+            },
22
+            "iframeCapabilities": {
23
+                "isSupported": false
24
+            }
25
+        }
26
+    ],
27
+    "chrome":
28
+    [
29
+        {"version": "34" },
30
+        {
31
+            "capabilities": {
32
+                "audioIn": true,
33
+                "audioOut": true,
34
+                "videoIn": true,
35
+                "videoOut": true,
36
+                "screenSharing": true
37
+            }
38
+        }
39
+    ],
40
+    "opera":
41
+    [
42
+        {"version": "22" },
43
+        {
44
+            "capabilities": {
45
+                "audioIn": true,
46
+                "audioOut": true,
47
+                "videoIn": true,
48
+                "videoOut": true,
49
+                "screenSharing": false
50
+            }
51
+        }
52
+    ],
53
+    "firefox": [
54
+        {"version": "52.4" },
55
+        {
56
+            "capabilities": {
57
+                "audioIn": true,
58
+                "audioOut": true,
59
+                "videoIn": true,
60
+                "videoOut": true,
61
+                "screenSharing": true
62
+            }
63
+        }
64
+    ],
65
+    "iexplorer": [
66
+        {"version": "9" },
67
+        {
68
+            "capabilities": {
69
+                "audioIn": true,
70
+                "audioOut": true,
71
+                "videoIn": true,
72
+                "videoOut": true,
73
+                "screenSharing": true
74
+            }
75
+        }
76
+    ],
77
+    "edge": [
78
+        {
79
+            "capabilities": {
80
+                "audioIn": true,
81
+                "audioOut": true,
82
+                "videoIn": true,
83
+                "videoOut": true,
84
+                "screenSharing": false
85
+            }
86
+        }
87
+    ],
88
+    "nwjs": [
89
+        {
90
+            "capabilities": {
91
+                "audioIn": true,
92
+                "audioOut": true,
93
+                "videoIn": true,
94
+                "videoOut": true,
95
+                "screenSharing": true
96
+            }
97
+        }
98
+    ],
99
+    "electron": [
100
+        {
101
+            "capabilities": {
102
+                "audioIn": true,
103
+                "audioOut": true,
104
+                "videoIn": true,
105
+                "videoOut": true,
106
+                "screenSharing": true
107
+            }
108
+        }
109
+    ],
110
+    "react-native": [
111
+        {
112
+            "capabilities": {
113
+                "audioIn": true,
114
+                "audioOut": true,
115
+                "videoIn": true,
116
+                "videoOut": true,
117
+                "screenSharing": false
118
+            }
119
+        }
120
+    ]
121
+}

+ 7
- 0
modules/browser/index.js View File

1
+import BrowserCapabilities from './BrowserCapabilities';
2
+
3
+// Export for browser_capabilities bundle.
4
+export ExternalBrowserCapabilities from './ExternalBrowserCapabilities';
5
+
6
+export * as browsers from './browsers';
7
+export default new BrowserCapabilities();

+ 6
- 6
modules/connectivity/ParticipantConnectionStatus.js View File

3
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
3
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
4
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
4
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
5
 import * as MediaType from '../../service/RTC/MediaType';
5
 import * as MediaType from '../../service/RTC/MediaType';
6
-import RTCBrowserType from '../RTC/RTCBrowserType';
6
+import browser from '../browser';
7
 import RTCEvents from '../../service/RTC/RTCEvents';
7
 import RTCEvents from '../../service/RTC/RTCEvents';
8
 import Statistics from '../statistics/statistics';
8
 import Statistics from '../statistics/statistics';
9
 
9
 
117
         }
117
         }
118
 
118
 
119
         // Logic when isVideoTrackFrozen is supported
119
         // Logic when isVideoTrackFrozen is supported
120
-        if (RTCBrowserType.isVideoMuteOnConnInterruptedSupported()) {
120
+        if (browser.supportsVideoMuteOnConnInterrupted()) {
121
             if (!isVideoTrackFrozen) {
121
             if (!isVideoTrackFrozen) {
122
                 // If the video is playing we're good
122
                 // If the video is playing we're good
123
                 return ParticipantConnectionStatus.ACTIVE;
123
                 return ParticipantConnectionStatus.ACTIVE;
150
      * @private
150
      * @private
151
      */
151
      */
152
     static _getNewStateForP2PMode(isVideoMuted, isVideoTrackFrozen) {
152
     static _getNewStateForP2PMode(isVideoMuted, isVideoTrackFrozen) {
153
-        if (!RTCBrowserType.isVideoMuteOnConnInterruptedSupported()) {
153
+        if (!browser.supportsVideoMuteOnConnInterrupted()) {
154
             // There's no way to detect problems in P2P when there's no video
154
             // There's no way to detect problems in P2P when there's no video
155
             // track frozen detection...
155
             // track frozen detection...
156
             return ParticipantConnectionStatus.ACTIVE;
156
             return ParticipantConnectionStatus.ACTIVE;
298
         // On some browsers MediaStreamTrack trigger "onmute"/"onunmute"
298
         // On some browsers MediaStreamTrack trigger "onmute"/"onunmute"
299
         // events for video type tracks when they stop receiving data which is
299
         // events for video type tracks when they stop receiving data which is
300
         // often a sign that remote user is having connectivity issues
300
         // often a sign that remote user is having connectivity issues
301
-        if (RTCBrowserType.isVideoMuteOnConnInterruptedSupported()) {
301
+        if (browser.supportsVideoMuteOnConnInterrupted()) {
302
 
302
 
303
             this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);
303
             this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);
304
             this.rtc.addListener(
304
             this.rtc.addListener(
347
             RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,
347
             RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,
348
             this._onEndpointConnStatusChanged);
348
             this._onEndpointConnStatusChanged);
349
 
349
 
350
-        if (RTCBrowserType.isVideoMuteOnConnInterruptedSupported()) {
350
+        if (browser.supportsVideoMuteOnConnInterrupted()) {
351
             this.rtc.removeListener(
351
             this.rtc.removeListener(
352
                 RTCEvents.REMOTE_TRACK_MUTE,
352
                 RTCEvents.REMOTE_TRACK_MUTE,
353
                 this._onTrackRtcMuted);
353
                 this._onTrackRtcMuted);
520
      *
520
      *
521
      */
521
      */
522
     isVideoTrackFrozen(participant) {
522
     isVideoTrackFrozen(participant) {
523
-        if (!RTCBrowserType.isVideoMuteOnConnInterruptedSupported()) {
523
+        if (!browser.supportsVideoMuteOnConnInterrupted()) {
524
             return false;
524
             return false;
525
         }
525
         }
526
 
526
 

+ 2
- 2
modules/statistics/AnalyticsAdapter.js View File

5
     TYPE_UI
5
     TYPE_UI
6
 } from '../../service/statistics/AnalyticsEvents';
6
 } from '../../service/statistics/AnalyticsEvents';
7
 import { getLogger } from 'jitsi-meet-logger';
7
 import { getLogger } from 'jitsi-meet-logger';
8
-import RTCBrowserType from '../RTC/RTCBrowserType';
8
+import browser from '../browser';
9
 import Settings from '../settings/Settings';
9
 import Settings from '../settings/Settings';
10
 
10
 
11
 const MAX_CACHE_SIZE = 100;
11
 const MAX_CACHE_SIZE = 100;
97
         this.addPermanentProperties({
97
         this.addPermanentProperties({
98
             'callstats_name': Settings.callStatsUserName,
98
             'callstats_name': Settings.callStatsUserName,
99
             'user_agent': navigator.userAgent,
99
             'user_agent': navigator.userAgent,
100
-            'browser_name': RTCBrowserType.getBrowserName()
100
+            'browser_name': browser.getName()
101
         });
101
         });
102
     }
102
     }
103
 
103
 

+ 5
- 5
modules/statistics/AvgRTPStatsReporter.js View File

6
     from '../../service/connectivity/ConnectionQualityEvents';
6
     from '../../service/connectivity/ConnectionQualityEvents';
7
 import * as ConferenceEvents from '../../JitsiConferenceEvents';
7
 import * as ConferenceEvents from '../../JitsiConferenceEvents';
8
 import * as MediaType from '../../service/RTC/MediaType';
8
 import * as MediaType from '../../service/RTC/MediaType';
9
-import RTCBrowserType from '../RTC/RTCBrowserType';
9
+import browser from '../browser';
10
 import Statistics from './statistics';
10
 import Statistics from './statistics';
11
 import * as VideoType from '../../service/RTC/VideoType';
11
 import * as VideoType from '../../service/RTC/VideoType';
12
 
12
 
181
             return;
181
             return;
182
         }
182
         }
183
 
183
 
184
-        if (RTCBrowserType.supportsRTTStatistics()) {
184
+        if (browser.supportsRTTStatistics()) {
185
             if (data.transport && data.transport.length) {
185
             if (data.transport && data.transport.length) {
186
                 this._avgRTT.addNext(data.transport[0].rtt);
186
                 this._avgRTT.addNext(data.transport[0].rtt);
187
             }
187
             }
190
         this._sampleIdx += 1;
190
         this._sampleIdx += 1;
191
 
191
 
192
         if (this._sampleIdx >= this._n) {
192
         if (this._sampleIdx >= this._n) {
193
-            if (RTCBrowserType.supportsRTTStatistics()) {
193
+            if (browser.supportsRTTStatistics()) {
194
                 const conference = this._avgRtpStatsReporter._conference;
194
                 const conference = this._avgRtpStatsReporter._conference;
195
 
195
 
196
                 const batchReport = {
196
                 const batchReport = {
624
         this._avgVideoBitrateUp.addNext(bitrate.video.upload);
624
         this._avgVideoBitrateUp.addNext(bitrate.video.upload);
625
         this._avgVideoBitrateDown.addNext(bitrate.video.download);
625
         this._avgVideoBitrateDown.addNext(bitrate.video.download);
626
 
626
 
627
-        if (RTCBrowserType.supportsBandwidthStatistics()) {
627
+        if (browser.supportsBandwidthStatistics()) {
628
             this._avgBandwidthUp.addNext(bandwidth.upload);
628
             this._avgBandwidthUp.addNext(bandwidth.upload);
629
             this._avgBandwidthDown.addNext(bandwidth.download);
629
             this._avgBandwidthDown.addNext(bandwidth.download);
630
         }
630
         }
694
             this._avgVideoBitrateUp.appendReport(batchReport);
694
             this._avgVideoBitrateUp.appendReport(batchReport);
695
             this._avgVideoBitrateDown.appendReport(batchReport);
695
             this._avgVideoBitrateDown.appendReport(batchReport);
696
 
696
 
697
-            if (RTCBrowserType.supportsBandwidthStatistics()) {
697
+            if (browser.supportsBandwidthStatistics()) {
698
                 this._avgBandwidthUp.appendReport(batchReport);
698
                 this._avgBandwidthUp.appendReport(batchReport);
699
                 this._avgBandwidthDown.appendReport(batchReport);
699
                 this._avgBandwidthDown.appendReport(batchReport);
700
             }
700
             }

+ 4
- 4
modules/statistics/CallStats.js View File

1
 /* global callstats */
1
 /* global callstats */
2
 
2
 
3
-import RTCBrowserType from '../RTC/RTCBrowserType';
3
+import browser from '../browser';
4
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
4
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
5
 
5
 
6
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6
 const logger = require('jitsi-meet-logger').getLogger(__filename);
300
                 // NOTE it is not safe to log whole objects on react-native as
300
                 // NOTE it is not safe to log whole objects on react-native as
301
                 // those contain too many circular references and may crash
301
                 // those contain too many circular references and may crash
302
                 // the app.
302
                 // the app.
303
-                if (!RTCBrowserType.isReactNative()) {
303
+                if (!browser.isReactNative()) {
304
                     console && console.debug('reportError', pc, cs, type);
304
                     console && console.debug('reportError', pc, cs, type);
305
                 }
305
                 }
306
             } else {
306
             } else {
354
             // imports are only allowed at top-level, so we must use require
354
             // imports are only allowed at top-level, so we must use require
355
             // here. Sigh.
355
             // here. Sigh.
356
             const CallStatsBackend
356
             const CallStatsBackend
357
-                = RTCBrowserType.isReactNative()
357
+                = browser.isReactNative()
358
                     ? require('react-native-callstats/callstats')
358
                     ? require('react-native-callstats/callstats')
359
                     : callstats;
359
                     : callstats;
360
 
360
 
375
                 configParams = {
375
                 configParams = {
376
                     applicationVersion:
376
                     applicationVersion:
377
                         `${options.applicationName} (${
377
                         `${options.applicationName} (${
378
-                            RTCBrowserType.getBrowserName()})`
378
+                            browser.getName()})`
379
                 };
379
                 };
380
             }
380
             }
381
 
381
 

+ 2
- 2
modules/statistics/LocalStatsCollector.js View File

2
  * Provides statistics for the local stream.
2
  * Provides statistics for the local stream.
3
  */
3
  */
4
 
4
 
5
-import RTCBrowserType from '../RTC/RTCBrowserType';
5
+import browser from '../browser';
6
 
6
 
7
 /**
7
 /**
8
  * Size of the webaudio analyzer buffer.
8
  * Size of the webaudio analyzer buffer.
145
  * @returns {boolean}
145
  * @returns {boolean}
146
  */
146
  */
147
 LocalStatsCollector.isLocalStatsSupported = function() {
147
 LocalStatsCollector.isLocalStatsSupported = function() {
148
-    return Boolean(context && !RTCBrowserType.isTemasysPluginUsed());
148
+    return Boolean(context && !browser.isTemasysPluginUsed());
149
 };
149
 };

+ 34
- 34
modules/statistics/RTPStatsCollector.js View File

1
-import RTCBrowserType from '../RTC/RTCBrowserType';
1
+import browser, { browsers } from '../browser';
2
 import * as StatisticsEvents from '../../service/statistics/Events';
2
 import * as StatisticsEvents from '../../service/statistics/Events';
3
 
3
 
4
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
4
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6
 
6
 
7
 /* Whether we support the browser we are running into for logging statistics */
7
 /* Whether we support the browser we are running into for logging statistics */
8
-const browserSupported = RTCBrowserType.isChrome()
9
-        || RTCBrowserType.isOpera() || RTCBrowserType.isFirefox()
10
-        || RTCBrowserType.isNWJS() || RTCBrowserType.isElectron()
11
-        || RTCBrowserType.isTemasysPluginUsed() || RTCBrowserType.isEdge();
8
+const browserSupported = browser.isChrome()
9
+        || browser.isOpera() || browser.isFirefox()
10
+        || browser.isNWJS() || browser.isElectron()
11
+        || browser.isTemasysPluginUsed() || browser.isEdge();
12
 
12
 
13
 /**
13
 /**
14
  * The lib-jitsi-meet browser-agnostic names of the browser-specific keys
14
  * The lib-jitsi-meet browser-agnostic names of the browser-specific keys
15
- * reported by RTCPeerConnection#getStats mapped by RTCBrowserType.
15
+ * reported by RTCPeerConnection#getStats mapped by browser.
16
  */
16
  */
17
 const KEYS_BY_BROWSER_TYPE = {};
17
 const KEYS_BY_BROWSER_TYPE = {};
18
 
18
 
19
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
19
+KEYS_BY_BROWSER_TYPE[browsers.FIREFOX] = {
20
     'ssrc': 'ssrc',
20
     'ssrc': 'ssrc',
21
     'packetsReceived': 'packetsReceived',
21
     'packetsReceived': 'packetsReceived',
22
     'packetsLost': 'packetsLost',
22
     'packetsLost': 'packetsLost',
25
     'bytesSent': 'bytesSent',
25
     'bytesSent': 'bytesSent',
26
     'framerateMean': 'framerateMean'
26
     'framerateMean': 'framerateMean'
27
 };
27
 };
28
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME] = {
28
+KEYS_BY_BROWSER_TYPE[browsers.CHROME] = {
29
     'receiveBandwidth': 'googAvailableReceiveBandwidth',
29
     'receiveBandwidth': 'googAvailableReceiveBandwidth',
30
     'sendBandwidth': 'googAvailableSendBandwidth',
30
     'sendBandwidth': 'googAvailableSendBandwidth',
31
     'remoteAddress': 'googRemoteAddress',
31
     'remoteAddress': 'googRemoteAddress',
50
     'remoteCandidateType': 'googRemoteCandidateType',
50
     'remoteCandidateType': 'googRemoteCandidateType',
51
     'localCandidateType': 'googLocalCandidateType'
51
     'localCandidateType': 'googLocalCandidateType'
52
 };
52
 };
53
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_EDGE] = {
53
+KEYS_BY_BROWSER_TYPE[browsers.EDGE] = {
54
     'sendBandwidth': 'googAvailableSendBandwidth',
54
     'sendBandwidth': 'googAvailableSendBandwidth',
55
     'remoteAddress': 'remoteAddress',
55
     'remoteAddress': 'remoteAddress',
56
     'transportType': 'protocol',
56
     'transportType': 'protocol',
72
     'audioOutputLevel': 'audioLevel',
72
     'audioOutputLevel': 'audioLevel',
73
     'currentRoundTripTime': 'roundTripTime'
73
     'currentRoundTripTime': 'roundTripTime'
74
 };
74
 };
75
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_OPERA]
76
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
77
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_NWJS]
78
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
79
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_ELECTRON]
80
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
81
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_IEXPLORER]
82
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
83
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_SAFARI]
84
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
85
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_REACT_NATIVE]
86
-    = KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
75
+KEYS_BY_BROWSER_TYPE[browsers.OPERA]
76
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
77
+KEYS_BY_BROWSER_TYPE[browsers.NWJS]
78
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
79
+KEYS_BY_BROWSER_TYPE[browsers.ELECTRON]
80
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
81
+KEYS_BY_BROWSER_TYPE[browsers.IEXPLORER]
82
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
83
+KEYS_BY_BROWSER_TYPE[browsers.SAFARI]
84
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
85
+KEYS_BY_BROWSER_TYPE[browsers.REACT_NATIVE]
86
+    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];
87
 
87
 
88
 /**
88
 /**
89
  * Calculates packet lost percent using the number of lost packets and the
89
  * Calculates packet lost percent using the number of lost packets and the
210
         eventEmitter) {
210
         eventEmitter) {
211
     // StatsCollector depends entirely on the format of the reports returned by
211
     // StatsCollector depends entirely on the format of the reports returned by
212
     // RTCPeerConnection#getStats. Given that the value of
212
     // RTCPeerConnection#getStats. Given that the value of
213
-    // RTCBrowserType#getBrowserType() is very unlikely to change at runtime, it
213
+    // browser#getName() is very unlikely to change at runtime, it
214
     // makes sense to discover whether StatsCollector supports the executing
214
     // makes sense to discover whether StatsCollector supports the executing
215
     // browser as soon as possible. Otherwise, (1) getStatValue would have to
215
     // browser as soon as possible. Otherwise, (1) getStatValue would have to
216
     // needlessly check a "static" condition multiple times very very often and
216
     // needlessly check a "static" condition multiple times very very often and
218
     // reported multiple times very very often too late in the execution in some
218
     // reported multiple times very very often too late in the execution in some
219
     // totally unrelated callback.
219
     // totally unrelated callback.
220
     /**
220
     /**
221
-     * The RTCBrowserType supported by this StatsCollector. In other words, the
222
-     * RTCBrowserType of the browser which initialized this StatsCollector
221
+     * The browser type supported by this StatsCollector. In other words, the
222
+     * type of the browser which initialized this StatsCollector
223
      * instance.
223
      * instance.
224
      * @private
224
      * @private
225
      */
225
      */
226
-    this._browserType = RTCBrowserType.getBrowserType();
226
+    this._browserType = browser.getName();
227
     const keys = KEYS_BY_BROWSER_TYPE[this._browserType];
227
     const keys = KEYS_BY_BROWSER_TYPE[this._browserType];
228
 
228
 
229
     if (!keys) {
229
     if (!keys) {
386
     let itemStatByKey;
386
     let itemStatByKey;
387
 
387
 
388
     switch (this._browserType) {
388
     switch (this._browserType) {
389
-    case RTCBrowserType.RTC_BROWSER_CHROME:
390
-    case RTCBrowserType.RTC_BROWSER_OPERA:
391
-    case RTCBrowserType.RTC_BROWSER_NWJS:
392
-    case RTCBrowserType.RTC_BROWSER_ELECTRON:
389
+    case browsers.CHROME:
390
+    case browsers.OPERA:
391
+    case browsers.NWJS:
392
+    case browsers.ELECTRON:
393
         // TODO What about other types of browser which are based on Chrome such
393
         // TODO What about other types of browser which are based on Chrome such
394
         // as NW.js? Every time we want to support a new type browser we have to
394
         // as NW.js? Every time we want to support a new type browser we have to
395
         // go and add more conditions (here and in multiple other places).
395
         // go and add more conditions (here and in multiple other places).
399
         // retrieve the value associated with a specific key.
399
         // retrieve the value associated with a specific key.
400
         itemStatByKey = (item, key) => item.stat(key);
400
         itemStatByKey = (item, key) => item.stat(key);
401
         break;
401
         break;
402
-    case RTCBrowserType.RTC_BROWSER_REACT_NATIVE:
402
+    case browsers.REACT_NATIVE:
403
         // The implementation provided by react-native-webrtc follows the
403
         // The implementation provided by react-native-webrtc follows the
404
         // Objective-C WebRTC API: RTCStatsReport has a values property of type
404
         // Objective-C WebRTC API: RTCStatsReport has a values property of type
405
         // Array in which each element is a key-value pair.
405
         // Array in which each element is a key-value pair.
420
             return value;
420
             return value;
421
         };
421
         };
422
         break;
422
         break;
423
-    case RTCBrowserType.RTC_BROWSER_EDGE:
423
+    case browsers.EDGE:
424
         itemStatByKey = (item, key) => item[key];
424
         itemStatByKey = (item, key) => item[key];
425
         break;
425
         break;
426
     default:
426
     default:
569
 
569
 
570
         // NOTE: In Edge, stats with type "inboundrtp" and "outboundrtp" are
570
         // NOTE: In Edge, stats with type "inboundrtp" and "outboundrtp" are
571
         // completely useless, so ignore them.
571
         // completely useless, so ignore them.
572
-        if (RTCBrowserType.isEdge()
572
+        if (browser.isEdge()
573
             && (now.type === 'inboundrtp' || now.type === 'outboundrtp')) {
573
             && (now.type === 'inboundrtp' || now.type === 'outboundrtp')) {
574
             continue;
574
             continue;
575
         }
575
         }
596
         // In new W3 stats spec, type="track" has a remoteSource boolean
596
         // In new W3 stats spec, type="track" has a remoteSource boolean
597
         // property.
597
         // property.
598
         // Edge uses the new format, so skip this check.
598
         // Edge uses the new format, so skip this check.
599
-        if (!RTCBrowserType.isEdge()
599
+        if (!browser.isEdge()
600
                 && (now.isRemote === true || now.remoteSource === true)) {
600
                 && (now.isRemote === true || now.remoteSource === true)) {
601
             continue;
601
             continue;
602
         }
602
         }
918
             // In Edge the range is -100..0 (-100 == silence) measured in dB,
918
             // In Edge the range is -100..0 (-100 == silence) measured in dB,
919
             // so convert to linear. The levels are set to 0 for remote tracks,
919
             // so convert to linear. The levels are set to 0 for remote tracks,
920
             // so don't convert those, since 0 means "the maximum" in Edge.
920
             // so don't convert those, since 0 means "the maximum" in Edge.
921
-            if (RTCBrowserType.isEdge()) {
921
+            if (browser.isEdge()) {
922
                 audioLevel = audioLevel < 0 ? Math.pow(10, audioLevel / 20) : 0;
922
                 audioLevel = audioLevel < 0 ? Math.pow(10, audioLevel / 20) : 0;
923
 
923
 
924
             // TODO: Can't find specs about what this value really is, but it
924
             // TODO: Can't find specs about what this value really is, but it

+ 2
- 2
modules/statistics/statistics.js View File

6
 import LocalStats from './LocalStatsCollector';
6
 import LocalStats from './LocalStatsCollector';
7
 import RTPStats from './RTPStatsCollector';
7
 import RTPStats from './RTPStatsCollector';
8
 
8
 
9
-import RTCBrowserType from '../RTC/RTCBrowserType';
9
+import browser from '../browser';
10
 import Settings from '../settings/Settings';
10
 import Settings from '../settings/Settings';
11
 import ScriptUtil from '../util/ScriptUtil';
11
 import ScriptUtil from '../util/ScriptUtil';
12
 import JitsiTrackError from '../../JitsiTrackError';
12
 import JitsiTrackError from '../../JitsiTrackError';
164
             // requests to any third parties.
164
             // requests to any third parties.
165
             && (Statistics.disableThirdPartyRequests !== true);
165
             && (Statistics.disableThirdPartyRequests !== true);
166
     if (this.callStatsIntegrationEnabled) {
166
     if (this.callStatsIntegrationEnabled) {
167
-        if (RTCBrowserType.isReactNative()) {
167
+        if (browser.isReactNative()) {
168
             _initCallStatsBackend(this.options);
168
             _initCallStatsBackend(this.options);
169
         } else {
169
         } else {
170
             loadCallStatsAPI(this.options);
170
             loadCallStatsAPI(this.options);

+ 2
- 2
modules/xmpp/SDPUtil.js View File

2
 const logger = getLogger(__filename);
2
 const logger = getLogger(__filename);
3
 
3
 
4
 import RandomUtil from '../util/RandomUtil';
4
 import RandomUtil from '../util/RandomUtil';
5
-import RTCBrowserType from '../RTC/RTCBrowserType';
5
+import browser from '../browser';
6
 
6
 
7
 const SDPUtil = {
7
 const SDPUtil = {
8
     filterSpecialChars(text) {
8
     filterSpecialChars(text) {
408
 
408
 
409
         // use tcp candidates for FF
409
         // use tcp candidates for FF
410
 
410
 
411
-        if (RTCBrowserType.isFirefox() && protocol.toLowerCase() === 'ssltcp') {
411
+        if (browser.isFirefox() && protocol.toLowerCase() === 'ssltcp') {
412
             protocol = 'tcp';
412
             protocol = 'tcp';
413
         }
413
         }
414
 
414
 

+ 2
- 2
modules/xmpp/moderator.js View File

9
     = require('../../service/authentication/AuthenticationEvents');
9
     = require('../../service/authentication/AuthenticationEvents');
10
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
10
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
11
 
11
 
12
-import RTCBrowserType from '../RTC/RTCBrowserType';
12
+import browser from '../browser';
13
 import Settings from '../settings/Settings';
13
 import Settings from '../settings/Settings';
14
 
14
 
15
 /**
15
 /**
228
         break;
228
         break;
229
     }
229
     }
230
 
230
 
231
-    if (openSctp && !RTCBrowserType.supportsDataChannels()) {
231
+    if (openSctp && !browser.supportsDataChannels()) {
232
         openSctp = false;
232
         openSctp = false;
233
     }
233
     }
234
 
234
 

+ 3
- 3
modules/xmpp/xmpp.js View File

7
 import RandomUtil from '../util/RandomUtil';
7
 import RandomUtil from '../util/RandomUtil';
8
 import * as JitsiConnectionErrors from '../../JitsiConnectionErrors';
8
 import * as JitsiConnectionErrors from '../../JitsiConnectionErrors';
9
 import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
9
 import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
10
-import RTCBrowserType from '../RTC/RTCBrowserType';
10
+import browser from '../browser';
11
 import initEmuc from './strophe.emuc';
11
 import initEmuc from './strophe.emuc';
12
 import initJingle from './strophe.jingle';
12
 import initJingle from './strophe.jingle';
13
 import initStropheUtil from './strophe.util';
13
 import initStropheUtil from './strophe.util';
90
         this.caps.addFeature('urn:xmpp:jingle:apps:rtp:audio');
90
         this.caps.addFeature('urn:xmpp:jingle:apps:rtp:audio');
91
         this.caps.addFeature('urn:xmpp:jingle:apps:rtp:video');
91
         this.caps.addFeature('urn:xmpp:jingle:apps:rtp:video');
92
 
92
 
93
-        if (!this.options.disableRtx && RTCBrowserType.supportsRtx()) {
93
+        if (!this.options.disableRtx && browser.supportsRtx()) {
94
             this.caps.addFeature('urn:ietf:rfc:4588');
94
             this.caps.addFeature('urn:ietf:rfc:4588');
95
         }
95
         }
96
 
96
 
106
         // this.caps.addFeature('urn:ietf:rfc:5576'); // a=ssrc
106
         // this.caps.addFeature('urn:ietf:rfc:5576'); // a=ssrc
107
 
107
 
108
         // Enable Lipsync ?
108
         // Enable Lipsync ?
109
-        if (RTCBrowserType.isChrome() && this.options.enableLipSync !== false) {
109
+        if (browser.isChrome() && this.options.enableLipSync !== false) {
110
             logger.info('Lip-sync enabled !');
110
             logger.info('Lip-sync enabled !');
111
             this.caps.addFeature('http://jitsi.org/meet/lipsync');
111
             this.caps.addFeature('http://jitsi.org/meet/lipsync');
112
         }
112
         }

+ 638
- 469
package-lock.json
File diff suppressed because it is too large
View File


+ 1
- 0
package.json View File

34
     "babel-loader": "7.1.2",
34
     "babel-loader": "7.1.2",
35
     "babel-preset-env": "1.6.1",
35
     "babel-preset-env": "1.6.1",
36
     "babel-preset-stage-1": "6.24.1",
36
     "babel-preset-stage-1": "6.24.1",
37
+    "bowser": "1.9.1",
37
     "core-js": "2.5.1",
38
     "core-js": "2.5.1",
38
     "eslint": "4.12.1",
39
     "eslint": "4.12.1",
39
     "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#v0.1",
40
     "eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#v0.1",

+ 27
- 6
webpack.config.js View File

24
     }));
24
     }));
25
 }
25
 }
26
 
26
 
27
-module.exports = {
27
+const config = {
28
     devtool: 'source-map',
28
     devtool: 'source-map',
29
-    entry: {
30
-        'lib-jitsi-meet': './index.js'
31
-    },
32
     module: {
29
     module: {
33
         rules: [ {
30
         rules: [ {
34
             // Version this build of the lib-jitsi-meet library.
31
             // Version this build of the lib-jitsi-meet library.
72
     },
69
     },
73
     output: {
70
     output: {
74
         filename: `[name]${minimize ? '.min' : ''}.js`,
71
         filename: `[name]${minimize ? '.min' : ''}.js`,
75
-        library: 'JitsiMeetJS',
76
-        libraryTarget: 'umd',
77
         sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
72
         sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
78
     },
73
     },
79
     plugins
74
     plugins
80
 };
75
 };
76
+
77
+module.exports = [
78
+    Object.assign({}, config, {
79
+        entry: {
80
+            'lib-jitsi-meet': './index.js'
81
+        },
82
+        output: Object.assign({}, config.output, {
83
+            library: 'JitsiMeetJS',
84
+            libraryTarget: 'umd'
85
+        })
86
+    }),
87
+
88
+    // The Webpack configuration to bundle browser_capabilities.js (API for
89
+    // browser capabilities).
90
+    Object.assign({}, config, {
91
+        entry: {
92
+            'browser_capabilities':
93
+                './modules/browser/index.js'
94
+        },
95
+        output: Object.assign({}, config.output, {
96
+            library: 'BrowserCapabilities',
97
+            libraryExport: 'ExternalBrowserCapabilities',
98
+            libraryTarget: 'umd'
99
+        })
100
+    })
101
+];

Loading…
Cancel
Save