Переглянути джерело

Change layer suspension to use parameters in RTPSender (#786)

* Change layer suspension to use parameters in RTPSender

We no longer suspend unused simulcast layers via a bandwidth cap in the
SDP, instead we'll use the new parameters in RTPSender to enable and
disable streams explicitly.  The main advantage here is the RTPSender
method ramps up immediately when we re-enable the layers (as opposed to
the SDP bandwidth cap which took 30+ seconds).

* Fix linter issues
dev1
bbaldino 7 роки тому
джерело
коміт
a1b6f1b8b7
Аккаунт користувача з таким Email не знайдено
2 змінених файлів з 41 додано та 127 видалено
  1. 0
    88
      modules/RTC/BandwidthLimiter.js
  2. 41
    39
      modules/RTC/TraceablePeerConnection.js

+ 0
- 88
modules/RTC/BandwidthLimiter.js Переглянути файл

@@ -1,88 +0,0 @@
1
-/* global __filename */
2
-
3
-import { getLogger } from 'jitsi-meet-logger';
4
-import transform from 'sdp-transform';
5
-
6
-const logger = getLogger(__filename);
7
-
8
-/**
9
- * This will save a bandwidth limit (per mline) that should be used
10
- * and then, when given an SDP, will enforce that limit.
11
- * Note that this will affect *outgoing* bandwidth usage, but the SDP
12
- * it must modify to implement that is the *remote* description
13
- */
14
-export default class BandwidthLimiter {
15
-
16
-    /**
17
-     * Create a new BandwidthLimiter
18
-     */
19
-    constructor() {
20
-        /**
21
-         * @type {Map<String, Number>}
22
-         * Map of mline media type to a bandwidth limit (in kbps).
23
-         * Any mlines present in this map will have the associated
24
-         * bandwidth limit (or 'null' for no limit) enforced, meaning
25
-         * that it will potentially overwrite a limit already set in
26
-         * the given sdp.  However, if an mline is NOT present in
27
-         * this map, any limit in the given sdp will not be touched.
28
-         */
29
-        this._bandwidthLimits = new Map();
30
-    }
31
-
32
-    /**
33
-     * Set a bandwidth limit for given mline.  If limitKbps is null,
34
-     * the limit will be removed
35
-     * @param {String} mediaType the mline media type to set the
36
-     * bandwidth limit on
37
-     * @param {Number} limitKbps the bandwidth limit, in kbps
38
-     */
39
-    setBandwidthLimit(mediaType, limitKbps) {
40
-        this._bandwidthLimits.set(mediaType, limitKbps);
41
-    }
42
-
43
-    /**
44
-     * Get the current bandwidth limit
45
-     * @param {String} mediaType the mline media type for which to get the
46
-     * bandwidth limit
47
-     * @return {Number} the bandwidth limit in kbps if it exists, undefined
48
-     * otherwise
49
-     */
50
-    getBandwidthLimit(mediaType) {
51
-        return this._bandwidthLimits.get(mediaType);
52
-    }
53
-
54
-    /**
55
-     * Enforce any configured bandwidth limits (or lack thereof) in the given
56
-     * sdp
57
-     * @param {String} sdp the session description
58
-     * @returns {String} a potentially modified session description
59
-     * with any configured bandwidth limits set
60
-     */
61
-    enforceBandwithLimit(sdp) {
62
-        logger.debug('Enforcing any configured bandwidth limits');
63
-        const desc = transform.parse(sdp);
64
-
65
-        desc.media.forEach(mLine => {
66
-            const limitKbps = this._bandwidthLimits.get(mLine.type);
67
-
68
-            if (typeof limitKbps !== 'undefined') {
69
-                if (limitKbps === null) {
70
-                    logger.debug(
71
-                        `Removing bandwidth limit for mline ${mLine.type}`);
72
-                    delete mLine.bandwidth;
73
-                } else {
74
-                    logger.debug(`Enforcing limit ${limitKbps}kbps`
75
-                        + ` for mline ${mLine.type}`);
76
-                    mLine.bandwidth = [
77
-                        {
78
-                            type: 'AS',
79
-                            limit: limitKbps
80
-                        }
81
-                    ];
82
-                }
83
-            }
84
-        });
85
-
86
-        return transform.write(desc);
87
-    }
88
-}

+ 41
- 39
modules/RTC/TraceablePeerConnection.js Переглянути файл

@@ -6,7 +6,6 @@ import transform from 'sdp-transform';
6 6
 import * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
7 7
 import JitsiRemoteTrack from './JitsiRemoteTrack';
8 8
 import * as MediaType from '../../service/RTC/MediaType';
9
-import BandwidthLimiter from './BandwidthLimiter';
10 9
 import LocalSdpMunger from './LocalSdpMunger';
11 10
 import RTC from './RTC';
12 11
 import RTCUtils from './RTCUtils';
@@ -208,8 +207,6 @@ export default function TraceablePeerConnection(
208 207
      */
209 208
     this.localSdpMunger = new LocalSdpMunger(this);
210 209
 
211
-    this.bandwidthLimiter = new BandwidthLimiter();
212
-
213 210
     /**
214 211
      * TracablePeerConnection uses RTC's eventEmitter
215 212
      * @type {EventEmitter}
@@ -217,12 +214,6 @@ export default function TraceablePeerConnection(
217 214
     this.eventEmitter = rtc.eventEmitter;
218 215
     this.rtxModifier = new RtxModifier();
219 216
 
220
-    /**
221
-     * Whether or not this endpoint has been selected
222
-     * by a remote participant (via the bridge)
223
-     */
224
-    this.isSelected = true;
225
-
226 217
     // override as desired
227 218
     this.trace = (what, info) => {
228 219
         logger.debug(what, info);
@@ -1036,20 +1027,6 @@ function extractSSRCMap(desc) {
1036 1027
     return ssrcMap;
1037 1028
 }
1038 1029
 
1039
-/**
1040
- * Get the bitrate cap we should enforce for video given whether or not
1041
- * we are selected
1042
- * @param {boolean} isSelected whether or not we (the local endpoint) is
1043
- * selected by any other endpoints (meaning its HD stream is in use)
1044
- * @return {Number} the bitrate cap in kbps, or null if there should be
1045
- * no cap
1046
- */
1047
-function getSuspensionBitrateKbps(isSelected) {
1048
-    // eslint-disable-next-line max-len
1049
-    // https://codesearch.chromium.org/chromium/src/third_party/webrtc/media/engine/simulcast.cc?l=55&rcl=28deb90728c06a35d8847d2aeda2fc1aee105c5e
1050
-    return isSelected ? null : 200;
1051
-}
1052
-
1053 1030
 /**
1054 1031
  * Takes a SessionDescription object and returns a "normalized" version.
1055 1032
  * Currently it only takes care of ordering the a=ssrc lines.
@@ -1859,21 +1836,6 @@ TraceablePeerConnection.prototype.setRemoteDescription = function(
1859 1836
         });
1860 1837
     }
1861 1838
 
1862
-    if (this.options.enableLayerSuspension) {
1863
-        logger.debug('Layer suspension enabled,'
1864
-            + `currently selected? ${this.isSelected}`);
1865
-        const bitrateCapKbps = getSuspensionBitrateKbps(this.isSelected);
1866
-
1867
-        this.bandwidthLimiter.setBandwidthLimit('video', bitrateCapKbps);
1868
-        logger.debug(`Layer suspension got bitrate cap of ${bitrateCapKbps}`);
1869
-        description.sdp
1870
-            = this.bandwidthLimiter.enforceBandwithLimit(description.sdp);
1871
-        this.trace(
1872
-            'setRemoteDescription::postTransform '
1873
-            + '(layer suspension bitrate cap)',
1874
-            dumpSDP(description));
1875
-    }
1876
-
1877 1839
     // If the browser uses unified plan, transform to it first
1878 1840
     if (browser.usesUnifiedPlan()) {
1879 1841
         // eslint-disable-next-line no-param-reassign
@@ -2488,12 +2450,52 @@ TraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function(track) {
2488 2450
     return ssrcInfo;
2489 2451
 };
2490 2452
 
2453
+const handleLayerSuspension = function(peerConnection, isSelected) {
2454
+    const videoSender = peerConnection.getSenders()
2455
+        .find(sender => sender.track.kind === 'video');
2456
+
2457
+    if (!videoSender) {
2458
+        logger.warn('handleLayerSuspension unable to find video sender');
2459
+
2460
+        return;
2461
+    }
2462
+    if (!videoSender.getParameters) {
2463
+        logger.debug('Browser doesn\'t support RTPSender parameters');
2464
+
2465
+        return;
2466
+    }
2467
+    const parameters = videoSender.getParameters();
2468
+
2469
+    if (isSelected) {
2470
+        logger.debug('Currently selected, enabling all sim layers');
2471
+
2472
+        // Make sure all encodings are enabled
2473
+        parameters.encodings.forEach(e => {
2474
+            e.active = true;
2475
+        });
2476
+    } else {
2477
+        logger.debug('Not currently selected, disabling upper layers');
2478
+
2479
+        // Turn off the upper simulcast layers
2480
+        [ 1, 2 ].forEach(simIndex => {
2481
+            if (parameters.encodings[simIndex]) {
2482
+                parameters.encodings[simIndex].active = false;
2483
+            }
2484
+        });
2485
+    }
2486
+    videoSender.setParameters(parameters);
2487
+};
2488
+
2491 2489
 /**
2492 2490
  * Set whether or not the endpoint is 'selected' by other endpoints, meaning
2493 2491
  * it appears on their main stage
2494 2492
  */
2495 2493
 TraceablePeerConnection.prototype.setIsSelected = function(isSelected) {
2496
-    this.isSelected = isSelected;
2494
+    if (this.options.enableLayerSuspension) {
2495
+        logger.debug('Layer suspension enabled,'
2496
+            + `currently selected? ${isSelected}`);
2497
+        handleLayerSuspension(this.peerconnection, isSelected);
2498
+    }
2497 2499
 };
2498 2500
 
2499 2501
 /**

Завантаження…
Відмінити
Зберегти