Sfoglia il codice sorgente

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

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

+ 4
- 0
.eslintignore Vedi File

@@ -3,6 +3,10 @@ lib-jitsi-meet.js
3 3
 lib-jitsi-meet.js.map
4 4
 lib-jitsi-meet.min.js
5 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 11
 # Third-party source code which we (1) do not want to modify or (2) try to
8 12
 # modify as little as possible.

+ 1
- 0
.gitignore Vedi File

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

+ 4
- 4
JitsiConference.js Vedi File

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

+ 9
- 6
JitsiMediaDevices.js Vedi File

@@ -1,11 +1,13 @@
1 1
 import EventEmitter from 'events';
2
-import * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';
2
+
3 3
 import * as MediaType from './service/RTC/MediaType';
4
+import browser from './modules/browser';
4 5
 import RTC from './modules/RTC/RTC';
5
-import RTCBrowserType from './modules/RTC/RTCBrowserType';
6 6
 import RTCEvents from './service/RTC/RTCEvents';
7 7
 import Statistics from './modules/statistics/statistics';
8 8
 
9
+import * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';
10
+
9 11
 const eventEmitter = new EventEmitter();
10 12
 
11 13
 /**
@@ -80,7 +82,7 @@ const JitsiMediaDevices = {
80 82
      * @returns {boolean}
81 83
      */
82 84
     isMultipleAudioInputSupported() {
83
-        return !RTCBrowserType.isFirefox();
85
+        return !browser.isFirefox();
84 86
     },
85 87
 
86 88
     /**
@@ -147,9 +149,10 @@ const JitsiMediaDevices = {
147 149
      * @returns {boolean}
148 150
      */
149 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 Vedi File

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

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

@@ -0,0 +1,44 @@
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 Vedi File

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

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

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

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

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

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

@@ -1,557 +0,0 @@
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 Vedi File

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

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

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

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

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

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

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

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

@@ -17,7 +17,7 @@ It also implements Plan-B for multi-stream.
17 17
 
18 18
 * BUNDLE is assumed (single transport for all the local and remote media streams).
19 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 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 22
 * If the app calls `createAnswer()`, mangles SSRC values, and applies by calling `setLocalDescription()`, those new SSRC values are currently ignored.
23 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 Vedi File

@@ -0,0 +1,280 @@
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 Vedi File

@@ -0,0 +1,305 @@
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 Vedi File

@@ -0,0 +1,66 @@
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 Vedi File

@@ -0,0 +1,21 @@
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 Vedi File

@@ -0,0 +1,44 @@
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 Vedi File

@@ -0,0 +1,121 @@
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 Vedi File

@@ -0,0 +1,7 @@
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 Vedi File

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

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

@@ -5,7 +5,7 @@ import {
5 5
     TYPE_UI
6 6
 } from '../../service/statistics/AnalyticsEvents';
7 7
 import { getLogger } from 'jitsi-meet-logger';
8
-import RTCBrowserType from '../RTC/RTCBrowserType';
8
+import browser from '../browser';
9 9
 import Settings from '../settings/Settings';
10 10
 
11 11
 const MAX_CACHE_SIZE = 100;
@@ -97,7 +97,7 @@ class AnalyticsAdapter {
97 97
         this.addPermanentProperties({
98 98
             'callstats_name': Settings.callStatsUserName,
99 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 Vedi File

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

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

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

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

@@ -2,7 +2,7 @@
2 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 8
  * Size of the webaudio analyzer buffer.
@@ -145,5 +145,5 @@ LocalStatsCollector.prototype.stop = function() {
145 145
  * @returns {boolean}
146 146
  */
147 147
 LocalStatsCollector.isLocalStatsSupported = function() {
148
-    return Boolean(context && !RTCBrowserType.isTemasysPluginUsed());
148
+    return Boolean(context && !browser.isTemasysPluginUsed());
149 149
 };

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

@@ -1,22 +1,22 @@
1
-import RTCBrowserType from '../RTC/RTCBrowserType';
1
+import browser, { browsers } from '../browser';
2 2
 import * as StatisticsEvents from '../../service/statistics/Events';
3 3
 
4 4
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
5 5
 const logger = require('jitsi-meet-logger').getLogger(__filename);
6 6
 
7 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 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 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 20
     'ssrc': 'ssrc',
21 21
     'packetsReceived': 'packetsReceived',
22 22
     'packetsLost': 'packetsLost',
@@ -25,7 +25,7 @@ KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
25 25
     'bytesSent': 'bytesSent',
26 26
     'framerateMean': 'framerateMean'
27 27
 };
28
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME] = {
28
+KEYS_BY_BROWSER_TYPE[browsers.CHROME] = {
29 29
     'receiveBandwidth': 'googAvailableReceiveBandwidth',
30 30
     'sendBandwidth': 'googAvailableSendBandwidth',
31 31
     'remoteAddress': 'googRemoteAddress',
@@ -50,7 +50,7 @@ KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME] = {
50 50
     'remoteCandidateType': 'googRemoteCandidateType',
51 51
     'localCandidateType': 'googLocalCandidateType'
52 52
 };
53
-KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_EDGE] = {
53
+KEYS_BY_BROWSER_TYPE[browsers.EDGE] = {
54 54
     'sendBandwidth': 'googAvailableSendBandwidth',
55 55
     'remoteAddress': 'remoteAddress',
56 56
     'transportType': 'protocol',
@@ -72,18 +72,18 @@ KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_EDGE] = {
72 72
     'audioOutputLevel': 'audioLevel',
73 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 89
  * Calculates packet lost percent using the number of lost packets and the
@@ -210,7 +210,7 @@ export default function StatsCollector(
210 210
         eventEmitter) {
211 211
     // StatsCollector depends entirely on the format of the reports returned by
212 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 214
     // makes sense to discover whether StatsCollector supports the executing
215 215
     // browser as soon as possible. Otherwise, (1) getStatValue would have to
216 216
     // needlessly check a "static" condition multiple times very very often and
@@ -218,12 +218,12 @@ export default function StatsCollector(
218 218
     // reported multiple times very very often too late in the execution in some
219 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 223
      * instance.
224 224
      * @private
225 225
      */
226
-    this._browserType = RTCBrowserType.getBrowserType();
226
+    this._browserType = browser.getName();
227 227
     const keys = KEYS_BY_BROWSER_TYPE[this._browserType];
228 228
 
229 229
     if (!keys) {
@@ -386,10 +386,10 @@ StatsCollector.prototype._defineGetStatValueMethod = function(keys) {
386 386
     let itemStatByKey;
387 387
 
388 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 393
         // TODO What about other types of browser which are based on Chrome such
394 394
         // as NW.js? Every time we want to support a new type browser we have to
395 395
         // go and add more conditions (here and in multiple other places).
@@ -399,7 +399,7 @@ StatsCollector.prototype._defineGetStatValueMethod = function(keys) {
399 399
         // retrieve the value associated with a specific key.
400 400
         itemStatByKey = (item, key) => item.stat(key);
401 401
         break;
402
-    case RTCBrowserType.RTC_BROWSER_REACT_NATIVE:
402
+    case browsers.REACT_NATIVE:
403 403
         // The implementation provided by react-native-webrtc follows the
404 404
         // Objective-C WebRTC API: RTCStatsReport has a values property of type
405 405
         // Array in which each element is a key-value pair.
@@ -420,7 +420,7 @@ StatsCollector.prototype._defineGetStatValueMethod = function(keys) {
420 420
             return value;
421 421
         };
422 422
         break;
423
-    case RTCBrowserType.RTC_BROWSER_EDGE:
423
+    case browsers.EDGE:
424 424
         itemStatByKey = (item, key) => item[key];
425 425
         break;
426 426
     default:
@@ -569,7 +569,7 @@ StatsCollector.prototype.processStatsReport = function() {
569 569
 
570 570
         // NOTE: In Edge, stats with type "inboundrtp" and "outboundrtp" are
571 571
         // completely useless, so ignore them.
572
-        if (RTCBrowserType.isEdge()
572
+        if (browser.isEdge()
573 573
             && (now.type === 'inboundrtp' || now.type === 'outboundrtp')) {
574 574
             continue;
575 575
         }
@@ -596,7 +596,7 @@ StatsCollector.prototype.processStatsReport = function() {
596 596
         // In new W3 stats spec, type="track" has a remoteSource boolean
597 597
         // property.
598 598
         // Edge uses the new format, so skip this check.
599
-        if (!RTCBrowserType.isEdge()
599
+        if (!browser.isEdge()
600 600
                 && (now.isRemote === true || now.remoteSource === true)) {
601 601
             continue;
602 602
         }
@@ -918,7 +918,7 @@ StatsCollector.prototype.processAudioLevelReport = function() {
918 918
             // In Edge the range is -100..0 (-100 == silence) measured in dB,
919 919
             // so convert to linear. The levels are set to 0 for remote tracks,
920 920
             // so don't convert those, since 0 means "the maximum" in Edge.
921
-            if (RTCBrowserType.isEdge()) {
921
+            if (browser.isEdge()) {
922 922
                 audioLevel = audioLevel < 0 ? Math.pow(10, audioLevel / 20) : 0;
923 923
 
924 924
             // TODO: Can't find specs about what this value really is, but it

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

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

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

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

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

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

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

@@ -7,7 +7,7 @@ import 'strophejs-plugin-disco';
7 7
 import RandomUtil from '../util/RandomUtil';
8 8
 import * as JitsiConnectionErrors from '../../JitsiConnectionErrors';
9 9
 import * as JitsiConnectionEvents from '../../JitsiConnectionEvents';
10
-import RTCBrowserType from '../RTC/RTCBrowserType';
10
+import browser from '../browser';
11 11
 import initEmuc from './strophe.emuc';
12 12
 import initJingle from './strophe.jingle';
13 13
 import initStropheUtil from './strophe.util';
@@ -90,7 +90,7 @@ export default class XMPP extends Listenable {
90 90
         this.caps.addFeature('urn:xmpp:jingle:apps:rtp:audio');
91 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 94
             this.caps.addFeature('urn:ietf:rfc:4588');
95 95
         }
96 96
 
@@ -106,7 +106,7 @@ export default class XMPP extends Listenable {
106 106
         // this.caps.addFeature('urn:ietf:rfc:5576'); // a=ssrc
107 107
 
108 108
         // Enable Lipsync ?
109
-        if (RTCBrowserType.isChrome() && this.options.enableLipSync !== false) {
109
+        if (browser.isChrome() && this.options.enableLipSync !== false) {
110 110
             logger.info('Lip-sync enabled !');
111 111
             this.caps.addFeature('http://jitsi.org/meet/lipsync');
112 112
         }

+ 638
- 469
package-lock.json
File diff soppresso perché troppo grande
Vedi File


+ 1
- 0
package.json Vedi File

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

+ 27
- 6
webpack.config.js Vedi File

@@ -24,11 +24,8 @@ if (minimize) {
24 24
     }));
25 25
 }
26 26
 
27
-module.exports = {
27
+const config = {
28 28
     devtool: 'source-map',
29
-    entry: {
30
-        'lib-jitsi-meet': './index.js'
31
-    },
32 29
     module: {
33 30
         rules: [ {
34 31
             // Version this build of the lib-jitsi-meet library.
@@ -72,9 +69,33 @@ module.exports = {
72 69
     },
73 70
     output: {
74 71
         filename: `[name]${minimize ? '.min' : ''}.js`,
75
-        library: 'JitsiMeetJS',
76
-        libraryTarget: 'umd',
77 72
         sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
78 73
     },
79 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…
Annulla
Salva