Parcourir la source

fix(SS): Set min and max frame rate to 5 (#711)

* fix(SS): Set min and max frame rate to 5

* fix(SS): Set min and max frame rate for the new GUM flow.

* feat(ss_framerate): Add config option for min/max frame rate.

* doc(RTCUtils): Fix params format.

* fix(SS_framerate): Don't pass undefined constraints.

* fix(SS_constraints): Handle  chromeMediaSourceId === undefined.
dev1
hristoterezov il y a 7 ans
Parent
révision
0dcc8a0491
2 fichiers modifiés avec 173 ajouts et 59 suppressions
  1. 118
    37
      modules/RTC/RTCUtils.js
  2. 55
    22
      modules/RTC/ScreenObtainer.js

+ 118
- 37
modules/RTC/RTCUtils.js Voir le fichier

@@ -83,6 +83,11 @@ const devices = {
83 83
     video: false
84 84
 };
85 85
 
86
+/**
87
+ * The default frame rate for Screen Sharing.
88
+ */
89
+const SS_DEFAULT_FRAME_RATE = 5;
90
+
86 91
 // Currently audio output device change is supported only in Chrome and
87 92
 // default output always has 'default' device ID
88 93
 let audioOutputDeviceId = 'default'; // default device
@@ -203,10 +208,15 @@ function setResolutionConstraints(
203 208
  * @param {string} options.micDeviceId
204 209
  * @param {CameraFacingMode} options.facingMode
205 210
  * @param {bool} firefox_fake_device
211
+ * @param {Object} options.frameRate - used only for dekstop sharing.
212
+ * @param {Object} options.frameRate.min - Minimum fps
213
+ * @param {Object} options.frameRate.max - Maximum fps
206 214
  */
207
-function getConstraints(um, options) {
208
-    const constraints = { audio: false,
209
-        video: false };
215
+function getConstraints(um, options = {}) {
216
+    const constraints = {
217
+        audio: false,
218
+        video: false
219
+    };
210 220
 
211 221
     // Don't mix new and old style settings for Chromium as this leads
212 222
     // to TypeError in new Chromium versions. @see
@@ -316,14 +326,13 @@ function getConstraints(um, options) {
316 326
     if (um.indexOf('screen') >= 0) {
317 327
         if (browser.isChrome()) {
318 328
             constraints.video = {
319
-                mandatory: {
320
-                    chromeMediaSource: 'screen',
321
-                    maxWidth: window.screen.width,
322
-                    maxHeight: window.screen.height,
323
-                    maxFrameRate: 3
324
-                },
329
+                mandatory: getSSConstraints({
330
+                    ...options,
331
+                    source: 'screen'
332
+                }),
325 333
                 optional: []
326 334
             };
335
+
327 336
         } else if (browser.isTemasysPluginUsed()) {
328 337
             constraints.video = {
329 338
                 optional: [
@@ -335,7 +344,11 @@ function getConstraints(um, options) {
335 344
         } else if (browser.isFirefox()) {
336 345
             constraints.video = {
337 346
                 mozMediaSource: 'window',
338
-                mediaSource: 'window'
347
+                mediaSource: 'window',
348
+                frameRate: options.frameRate || {
349
+                    min: SS_DEFAULT_FRAME_RATE,
350
+                    max: SS_DEFAULT_FRAME_RATE
351
+                }
339 352
             };
340 353
 
341 354
         } else {
@@ -349,13 +362,10 @@ function getConstraints(um, options) {
349 362
     }
350 363
     if (um.indexOf('desktop') >= 0) {
351 364
         constraints.video = {
352
-            mandatory: {
353
-                chromeMediaSource: 'desktop',
354
-                chromeMediaSourceId: options.desktopStream,
355
-                maxWidth: window.screen.width,
356
-                maxHeight: window.screen.height,
357
-                maxFrameRate: 3
358
-            },
365
+            mandatory: getSSConstraints({
366
+                ...options,
367
+                source: 'desktop'
368
+            }),
359 369
             optional: []
360 370
         };
361 371
     }
@@ -399,6 +409,9 @@ function getConstraints(um, options) {
399 409
  * pointing to.
400 410
  * @param {string} options.micDeviceId - The device id for the audio capture
401 411
  * device to get audio from.
412
+ * @param {Object} options.frameRate - used only for dekstop sharing.
413
+ * @param {Object} options.frameRate.min - Minimum fps
414
+ * @param {Object} options.frameRate.max - Maximum fps
402 415
  * @private
403 416
  * @returns {Object}
404 417
  */
@@ -460,19 +473,57 @@ function newGetConstraints(um = [], options = {}) {
460 473
         }
461 474
 
462 475
         constraints.video = {
463
-            mandatory: {
464
-                chromeMediaSource: 'desktop',
465
-                chromeMediaSourceId: options.desktopStream,
466
-                maxWidth: window.screen.width,
467
-                maxHeight: window.screen.height,
468
-                maxFrameRate: 3
469
-            }
476
+            mandatory: getSSConstraints({
477
+                ...options,
478
+                source: 'desktop'
479
+            })
470 480
         };
471 481
     }
472 482
 
473 483
     return constraints;
474 484
 }
475 485
 
486
+/**
487
+ * Generates GUM constraints for screen sharing.
488
+ *
489
+ * @param {Object} options - The options passed to
490
+ * <tt>obtainAudioAndVideoPermissions</tt>.
491
+ * @returns {Object} - GUM constraints.
492
+ *
493
+ * TODO: Currently only the new GUM flow and Chrome is using the method. We
494
+ * should make it work for all use cases.
495
+ */
496
+function getSSConstraints(options = {}) {
497
+    const {
498
+        desktopStream,
499
+        frameRate = {
500
+            min: SS_DEFAULT_FRAME_RATE,
501
+            max: SS_DEFAULT_FRAME_RATE
502
+        }
503
+    } = options;
504
+    const { max, min } = frameRate;
505
+
506
+    const constraints = {
507
+        chromeMediaSource: options.source,
508
+        maxWidth: window.screen.width,
509
+        maxHeight: window.screen.height
510
+    };
511
+
512
+    if (typeof min === 'number') {
513
+        constraints.minFrameRate = min;
514
+    }
515
+
516
+    if (typeof max === 'number') {
517
+        constraints.maxFrameRate = max;
518
+    }
519
+
520
+    if (typeof desktopStream !== 'undefined') {
521
+        constraints.chromeMediaSourceId = desktopStream;
522
+    }
523
+
524
+    return constraints;
525
+}
526
+
476 527
 /**
477 528
  * Sets the availbale devices based on the options we requested and the
478 529
  * streams we received.
@@ -1172,6 +1223,9 @@ class RTCUtils extends Listenable {
1172 1223
     * @param {string} options.desktopStream
1173 1224
     * @param {string} options.cameraDeviceId
1174 1225
     * @param {string} options.micDeviceId
1226
+    * @param {Object} options.frameRate - used only for dekstop sharing.
1227
+    * @param {Object} options.frameRate.min - Minimum fps
1228
+    * @param {Object} options.frameRate.max - Maximum fps
1175 1229
     * @returns {Promise} Returns a media stream on success or a JitsiTrackError
1176 1230
     * on failure.
1177 1231
     **/
@@ -1271,28 +1325,35 @@ class RTCUtils extends Listenable {
1271 1325
      * logic compared to use screenObtainer versus normal device capture logic
1272 1326
      * in RTCUtils#_newGetUserMediaWithConstraints.
1273 1327
      *
1274
-     * @param {Object} desktopSharingExtensionExternalInstallation
1275
-     * @param {string[]} desktopSharingSources
1328
+     * @param {Object} options
1329
+     * @param {Object} options.desktopSharingExtensionExternalInstallation
1330
+     * @param {string[]} options.desktopSharingSources
1331
+     * @param {Object} options.gumOptions.frameRate
1332
+     * @param {Object} options.gumOptions.frameRate.min - Minimum fps
1333
+     * @param {Object} options.gumOptions.frameRate.max - Maximum fps
1276 1334
      * @returns {Promise} A promise which will be resolved with an object whic
1277 1335
      * contains the acquired display stream. If desktop sharing is not supported
1278 1336
      * then a rejected promise will be returned.
1279 1337
      */
1280
-    _newGetDesktopMedia(
1281
-            desktopSharingExtensionExternalInstallation,
1282
-            desktopSharingSources) {
1338
+    _newGetDesktopMedia(options) {
1283 1339
         if (!screenObtainer.isSupported() || !browser.supportsVideo()) {
1284 1340
             return Promise.reject(
1285 1341
                 new Error('Desktop sharing is not supported!'));
1286 1342
         }
1287 1343
 
1288
-        const desktopSharingOptions = {
1289
-            ...desktopSharingExtensionExternalInstallation,
1290
-            desktopSharingSources
1291
-        };
1344
+        const {
1345
+            desktopSharingExtensionExternalInstallation,
1346
+            desktopSharingSources,
1347
+            gumOptions
1348
+        } = options;
1292 1349
 
1293 1350
         return new Promise((resolve, reject) => {
1294 1351
             screenObtainer.obtainStream(
1295
-                desktopSharingOptions,
1352
+                {
1353
+                    ...desktopSharingExtensionExternalInstallation,
1354
+                    desktopSharingSources,
1355
+                    gumOptions
1356
+                },
1296 1357
                 stream => {
1297 1358
                     resolve(stream);
1298 1359
                 },
@@ -1315,6 +1376,9 @@ class RTCUtils extends Listenable {
1315 1376
      * Promise, otherwise JitsiTrack objects will be returned.
1316 1377
      * @param {string} options.cameraDeviceId
1317 1378
      * @param {string} options.micDeviceId
1379
+     * @param {Object} options.desktopSharingFrameRate
1380
+     * @param {Object} options.desktopSharingFrameRate.min - Minimum fps
1381
+     * @param {Object} options.desktopSharingFrameRate.max - Maximum fps
1318 1382
      * @returns {*} Promise object that will receive the new JitsiTracks
1319 1383
      */
1320 1384
     obtainAudioAndVideoPermissions(options = {}) {
@@ -1483,7 +1547,10 @@ class RTCUtils extends Listenable {
1483 1547
     _parseDesktopSharingOptions(options) {
1484 1548
         return {
1485 1549
             ...options.desktopSharingExtensionExternalInstallation,
1486
-            desktopSharingSources: options.desktopSharingSources
1550
+            desktopSharingSources: options.desktopSharingSources,
1551
+            gumOptions: {
1552
+                frameRate: options.desktopSharingFrameRate
1553
+            }
1487 1554
         };
1488 1555
     }
1489 1556
 
@@ -1495,6 +1562,9 @@ class RTCUtils extends Listenable {
1495 1562
      * relevant constraints.
1496 1563
      * @param {string[]} options.devices - The types of media to capture. Valid
1497 1564
      * values are "desktop", "audio", and "video".
1565
+     * @param {Object} options.desktopSharingFrameRate
1566
+     * @param {Object} options.desktopSharingFrameRate.min - Minimum fps
1567
+     * @param {Object} options.desktopSharingFrameRate.max - Maximum fps
1498 1568
      * @returns {Promise} The promise, when successful, will return an array of
1499 1569
      * meta data for the requested device type, which includes the stream and
1500 1570
      * track. If an error occurs, it will be deferred to the caller for
@@ -1519,10 +1589,21 @@ class RTCUtils extends Listenable {
1519 1589
             const umDevices = options.devices || [];
1520 1590
             const isDesktopDeviceRequsted = umDevices.indexOf('desktop') !== -1;
1521 1591
 
1592
+            const {
1593
+                desktopSharingExtensionExternalInstallation,
1594
+                desktopSharingSources,
1595
+                desktopSharingFrameRate
1596
+            } = options;
1597
+
1522 1598
             return isDesktopDeviceRequsted
1523 1599
                 ? this._newGetDesktopMedia(
1524
-                    options.desktopSharingExtensionExternalInstallation,
1525
-                    options.desktopSharingSources)
1600
+                    {
1601
+                        desktopSharingExtensionExternalInstallation,
1602
+                        desktopSharingSources,
1603
+                        gumOptions: {
1604
+                            frameRate: desktopSharingFrameRate
1605
+                        }
1606
+                    })
1526 1607
                 : Promise.resolve();
1527 1608
         }.bind(this);
1528 1609
 

+ 55
- 22
modules/RTC/ScreenObtainer.js Voir le fichier

@@ -116,8 +116,7 @@ const ScreenObtainer = {
116 116
         desktopSharingFirefoxDisabled: false,
117 117
         desktopSharingFirefoxExtId: null
118 118
     }, gum) {
119
-        // eslint-disable-next-line no-param-reassign
120
-        this.options = options = options || {};
119
+        this.options = options;
121 120
         gumFunction = gum;
122 121
 
123 122
         this.obtainStream
@@ -277,7 +276,7 @@ const ScreenObtainer = {
277 276
         }
278 277
 
279 278
         if (!extensionRequired || firefoxExtInstalled === true) {
280
-            obtainWebRTCScreen(options, callback, errorCallback);
279
+            obtainWebRTCScreen(options.gumOptions, callback, errorCallback);
281 280
 
282 281
             return;
283 282
         }
@@ -330,17 +329,21 @@ const ScreenObtainer = {
330 329
     obtainScreenOnElectron(options = {}, onSuccess, onFailure) {
331 330
         if (window.JitsiMeetScreenObtainer
332 331
             && window.JitsiMeetScreenObtainer.openDesktopPicker) {
332
+            const { desktopSharingSources, gumOptions } = options;
333
+
333 334
             window.JitsiMeetScreenObtainer.openDesktopPicker(
334 335
                 {
335
-                    desktopSharingSources:
336
-                        options.desktopSharingSources
337
-                            || this.options.desktopSharingChromeSources
336
+                    desktopSharingSources: desktopSharingSources
337
+                        || this.options.desktopSharingChromeSources
338 338
                 },
339 339
                 (streamId, streamType) =>
340 340
                     onGetStreamResponse(
341 341
                         {
342
-                            streamId,
343
-                            streamType
342
+                            response: {
343
+                                streamId,
344
+                                streamType
345
+                            },
346
+                            gumOptions
344 347
                         },
345 348
                         onSuccess,
346 349
                         onFailure
@@ -375,16 +378,20 @@ const ScreenObtainer = {
375 378
             desktopSharingChromeSources
376 379
         } = this.options;
377 380
 
378
-        const gumOptions = {
381
+        const {
382
+            gumOptions
383
+        } = options;
384
+
385
+        const doGetStreamFromExtensionOptions = {
379 386
             desktopSharingChromeExtId,
380 387
             desktopSharingChromeSources:
381
-                options.desktopSharingSources
382
-                    || desktopSharingChromeSources
388
+                options.desktopSharingSources || desktopSharingChromeSources,
389
+            gumOptions
383 390
         };
384 391
 
385 392
         if (chromeExtInstalled) {
386 393
             doGetStreamFromExtension(
387
-                gumOptions,
394
+                doGetStreamFromExtensionOptions,
388 395
                 streamCallback,
389 396
                 failCallback);
390 397
         } else {
@@ -420,7 +427,7 @@ const ScreenObtainer = {
420 427
                         waitForExtensionAfterInstall(this.options, 200, 10)
421 428
                             .then(() => {
422 429
                                 doGetStreamFromExtension(
423
-                                    gumOptions,
430
+                                    doGetStreamFromExtensionOptions,
424 431
                                     streamCallback,
425 432
                                     failCallback);
426 433
                             })
@@ -514,7 +521,8 @@ function obtainWebRTCScreen(options, streamCallback, failCallback) {
514 521
     gumFunction(
515 522
         [ 'screen' ],
516 523
         stream => streamCallback({ stream }),
517
-        failCallback
524
+        failCallback,
525
+        options
518 526
     );
519 527
 }
520 528
 
@@ -618,13 +626,19 @@ function checkChromeExtInstalled(callback, options) {
618 626
  * @param failCallback
619 627
  */
620 628
 function doGetStreamFromExtension(options, streamCallback, failCallback) {
629
+    const {
630
+        desktopSharingChromeSources,
631
+        desktopSharingChromeExtId,
632
+        gumOptions
633
+    } = options;
634
+
621 635
     // Sends 'getStream' msg to the extension.
622 636
     // Extension id must be defined in the config.
623 637
     chrome.runtime.sendMessage(
624
-        options.desktopSharingChromeExtId,
638
+        desktopSharingChromeExtId,
625 639
         {
626 640
             getStream: true,
627
-            sources: options.desktopSharingChromeSources
641
+            sources: desktopSharingChromeSources
628 642
         },
629 643
         response => {
630 644
             if (!response) {
@@ -640,7 +654,14 @@ function doGetStreamFromExtension(options, streamCallback, failCallback) {
640 654
                 return;
641 655
             }
642 656
             logger.log('Response from extension: ', response);
643
-            onGetStreamResponse(response, streamCallback, failCallback);
657
+            onGetStreamResponse(
658
+                {
659
+                    response,
660
+                    gumOptions
661
+                },
662
+                streamCallback,
663
+                failCallback
664
+            );
644 665
         }
645 666
     );
646 667
 }
@@ -720,16 +741,25 @@ function waitForExtensionAfterInstall(options, waitInterval, retries) {
720 741
 /**
721 742
  * Handles response from external application / extension and calls GUM to
722 743
  * receive the desktop streams or reports error.
723
- * @param {object} response
724
- * @param {string} response.streamId - the streamId for the desktop stream
725
- * @param {string} response.error - error to be reported.
744
+ * @param {object} options
745
+ * @param {object} options.response
746
+ * @param {string} options.response.streamId - the streamId for the desktop
747
+ * stream.
748
+ * @param {string} options.response.error - error to be reported.
749
+ * @param {object} options.gumOptions - options passed to GUM.
726 750
  * @param {Function} onSuccess - callback for success.
727 751
  * @param {Function} onFailure - callback for failure.
752
+ * @param {object} gumOptions - options passed to GUM.
728 753
  */
729 754
 function onGetStreamResponse(
730
-        { streamId, streamType, error },
755
+        options = {
756
+            response: {},
757
+            gumOptions: {}
758
+        },
731 759
         onSuccess,
732 760
         onFailure) {
761
+    const { streamId, streamType, error } = options.response || {};
762
+
733 763
     if (streamId) {
734 764
         gumFunction(
735 765
             [ 'desktop' ],
@@ -739,7 +769,10 @@ function onGetStreamResponse(
739 769
                 sourceType: streamType
740 770
             }),
741 771
             onFailure,
742
-            { desktopStream: streamId });
772
+            {
773
+                desktopStream: streamId,
774
+                ...options.gumOptions
775
+            });
743 776
     } else {
744 777
         // As noted in Chrome Desktop Capture API:
745 778
         // If user didn't select any source (i.e. canceled the prompt)

Chargement…
Annuler
Enregistrer