Browse Source

feat(last-n): Implement startLastN and make last-n configurable through UI. (#9093)

master
Jaya Allamsetty 4 years ago
parent
commit
1898e4a768
No account linked to committer's email address

+ 5
- 0
config.js View File

228
     // Default value for the channel "last N" attribute. -1 for unlimited.
228
     // Default value for the channel "last N" attribute. -1 for unlimited.
229
     channelLastN: -1,
229
     channelLastN: -1,
230
 
230
 
231
+    // Provides a way for the lastN value to be controlled through the UI.
232
+    // When startLastN is present, conference starts with a last-n value of startLastN and channelLastN
233
+    // value will be used when the quality level is selected using "Manage Video Quality" slider.
234
+    // startLastN: 1,
235
+
231
     // Provides a way to use different "last N" values based on the number of participants in the conference.
236
     // Provides a way to use different "last N" values based on the number of participants in the conference.
232
     // The keys in an Object represent number of participants and the values are "last N" to be used when number of
237
     // The keys in an Object represent number of participants and the values are "last N" to be used when number of
233
     // participants gets to or above the number.
238
     // participants gets to or above the number.

+ 1
- 0
react/features/base/config/configWhitelist.js View File

153
     'resolution',
153
     'resolution',
154
     'startAudioMuted',
154
     'startAudioMuted',
155
     'startAudioOnly',
155
     'startAudioOnly',
156
+    'startLastN',
156
     'startScreenSharing',
157
     'startScreenSharing',
157
     'startSilent',
158
     'startSilent',
158
     'startVideoMuted',
159
     'startVideoMuted',

+ 11
- 0
react/features/base/lastn/actionTypes.js View File

1
+// @flow
2
+
3
+/**
4
+ * The type of (redux) action which sets the last-n for the conference.
5
+ *
6
+ * {
7
+ *     type: SET_LAST_N,
8
+ *     lastN: number
9
+ * }
10
+ */
11
+export const SET_LAST_N = 'SET_LAST_N';

+ 19
- 0
react/features/base/lastn/actions.js View File

1
+// @flow
2
+
3
+import { SET_LAST_N } from './actionTypes';
4
+
5
+/**
6
+ * Sets the last-n, i.e., the number of remote videos to be requested from the bridge for the conference.
7
+ *
8
+ * @param {number} lastN - The number of remote videos to be requested.
9
+ * @returns {{
10
+ *     type: SET_LAST_N,
11
+ *     lastN: number
12
+ * }}
13
+ */
14
+export function setLastN(lastN: number) {
15
+    return {
16
+        type: SET_LAST_N,
17
+        lastN
18
+    };
19
+}

+ 25
- 0
react/features/base/lastn/functions.js View File

1
+import { VIDEO_QUALITY_LEVELS } from '../../video-quality/constants';
2
+
3
+/**
4
+ * Determines the lastN value to be used for the conference based on the video quality selected.
5
+ *
6
+ * @param {string} qualityLevel - Quality level (height) selected.
7
+ * @param {number} channelLastN - LastN value set for the whole conference.
8
+ * @returns {number} LastN value applicable to the quality level specified.
9
+ */
10
+export function getLastNForQualityLevel(qualityLevel, channelLastN) {
11
+    let lastN = channelLastN;
12
+
13
+    const videoQualityLevels = Object.values(VIDEO_QUALITY_LEVELS);
14
+
15
+    for (const lvl in videoQualityLevels) {
16
+        if (videoQualityLevels.hasOwnProperty(lvl)
17
+            && qualityLevel === videoQualityLevels[lvl]
18
+            && lvl > 1) {
19
+            lastN = Math.floor(channelLastN / Math.pow(2, lvl - 1));
20
+        }
21
+    }
22
+
23
+    return lastN;
24
+}
25
+
1
 /**
26
 /**
2
  * Checks if the given Object is a correct last N limit mapping, coverts both keys and values to numbers and sorts
27
  * Checks if the given Object is a correct last N limit mapping, coverts both keys and values to numbers and sorts
3
  * the keys in ascending order.
28
  * the keys in ascending order.

+ 3
- 0
react/features/base/lastn/index.js View File

1
+export * from './actions';
2
+export * from './actionTypes';
3
+export * from './functions';

+ 26
- 12
react/features/base/lastn/middleware.js View File

18
 import { MiddlewareRegistry } from '../redux';
18
 import { MiddlewareRegistry } from '../redux';
19
 import { isLocalVideoTrackDesktop } from '../tracks/functions';
19
 import { isLocalVideoTrackDesktop } from '../tracks/functions';
20
 
20
 
21
+import { SET_LAST_N } from './actionTypes';
21
 import { limitLastN } from './functions';
22
 import { limitLastN } from './functions';
22
 import logger from './logger';
23
 import logger from './logger';
23
 
24
 
40
     case SET_TILE_VIEW:
41
     case SET_TILE_VIEW:
41
         _updateLastN(store);
42
         _updateLastN(store);
42
         break;
43
         break;
44
+    case SET_LAST_N: {
45
+        const { lastN } = action;
46
+
47
+        _updateLastN(store, lastN);
48
+        break;
49
+    }
43
     }
50
     }
44
 
51
 
45
     return result;
52
     return result;
49
  * Updates the last N value in the conference based on the current state of the redux store.
56
  * Updates the last N value in the conference based on the current state of the redux store.
50
  *
57
  *
51
  * @param {Store} store - The redux store.
58
  * @param {Store} store - The redux store.
59
+ * @param {number} value - The last-n value to be set.
52
  * @private
60
  * @private
53
  * @returns {void}
61
  * @returns {void}
54
  */
62
  */
55
-function _updateLastN({ getState }) {
63
+function _updateLastN({ getState }, value = null) {
56
     const state = getState();
64
     const state = getState();
57
     const { conference } = state['features/base/conference'];
65
     const { conference } = state['features/base/conference'];
58
     const { enabled: audioOnly } = state['features/base/audio-only'];
66
     const { enabled: audioOnly } = state['features/base/audio-only'];
59
     const { appState } = state['features/background'] || {};
67
     const { appState } = state['features/background'] || {};
60
     const { enabled: filmStripEnabled } = state['features/filmstrip'];
68
     const { enabled: filmStripEnabled } = state['features/filmstrip'];
61
     const config = state['features/base/config'];
69
     const config = state['features/base/config'];
62
-    const { lastNLimits } = state['features/base/lastn'];
70
+    const { lastNLimits, lastN } = state['features/base/lastn'];
63
     const participantCount = getParticipantCount(state);
71
     const participantCount = getParticipantCount(state);
64
 
72
 
65
     if (!conference) {
73
     if (!conference) {
68
         return;
76
         return;
69
     }
77
     }
70
 
78
 
71
-    let lastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN;
79
+    // Select the lastN value based on the following preference order.
80
+    // 1. The value passed to the setLastN action that is dispatched.
81
+    // 2. The last-n value in redux.
82
+    // 3. The last-n value from 'startLastN' if it is specified in config.js
83
+    // 4. The last-n value from 'channelLastN' if specified in config.js.
84
+    // 5. -1 as the default value.
85
+    let lastNSelected = value || lastN || (config.startLastN ?? (config.channelLastN ?? -1));
72
 
86
 
73
-    // Apply last N limit based on the # of participants and channelLastN settings.
87
+    // Apply last N limit based on the # of participants and config settings.
74
     const limitedLastN = limitLastN(participantCount, lastNLimits);
88
     const limitedLastN = limitLastN(participantCount, lastNLimits);
75
 
89
 
76
     if (limitedLastN !== undefined) {
90
     if (limitedLastN !== undefined) {
77
-        lastN = lastN === -1 ? limitedLastN : Math.min(limitedLastN, lastN);
91
+        lastNSelected = lastNSelected === -1 ? limitedLastN : Math.min(limitedLastN, lastNSelected);
78
     }
92
     }
79
 
93
 
80
     if (typeof appState !== 'undefined' && appState !== 'active') {
94
     if (typeof appState !== 'undefined' && appState !== 'active') {
81
-        lastN = isLocalVideoTrackDesktop(state) ? 1 : 0;
95
+        lastNSelected = isLocalVideoTrackDesktop(state) ? 1 : 0;
82
     } else if (audioOnly) {
96
     } else if (audioOnly) {
83
         const { remoteScreenShares, tileViewEnabled } = state['features/video-layout'];
97
         const { remoteScreenShares, tileViewEnabled } = state['features/video-layout'];
84
         const largeVideoParticipantId = state['features/large-video'].participantId;
98
         const largeVideoParticipantId = state['features/large-video'].participantId;
89
         // view since we make an exception only for screenshare when in audio-only mode. If the user unpins
103
         // view since we make an exception only for screenshare when in audio-only mode. If the user unpins
90
         // the screenshare, lastN will be set to 0 here. It will be set to 1 if screenshare has been auto pinned.
104
         // the screenshare, lastN will be set to 0 here. It will be set to 1 if screenshare has been auto pinned.
91
         if (!tileViewEnabled && largeVideoParticipant && !largeVideoParticipant.local) {
105
         if (!tileViewEnabled && largeVideoParticipant && !largeVideoParticipant.local) {
92
-            lastN = (remoteScreenShares || []).includes(largeVideoParticipantId) ? 1 : 0;
106
+            lastNSelected = (remoteScreenShares || []).includes(largeVideoParticipantId) ? 1 : 0;
93
         } else {
107
         } else {
94
-            lastN = 0;
108
+            lastNSelected = 0;
95
         }
109
         }
96
     } else if (!filmStripEnabled) {
110
     } else if (!filmStripEnabled) {
97
-        lastN = 1;
111
+        lastNSelected = 1;
98
     }
112
     }
99
 
113
 
100
-    if (conference.getLastN() === lastN) {
114
+    if (conference.getLastN() === lastNSelected) {
101
         return;
115
         return;
102
     }
116
     }
103
 
117
 
104
-    logger.info(`Setting last N to: ${lastN}`);
118
+    logger.info(`Setting last N to: ${lastNSelected}`);
105
 
119
 
106
     try {
120
     try {
107
-        conference.setLastN(lastN);
121
+        conference.setLastN(lastNSelected);
108
     } catch (err) {
122
     } catch (err) {
109
         logger.error(`Failed to set lastN: ${err}`);
123
         logger.error(`Failed to set lastN: ${err}`);
110
     }
124
     }

+ 9
- 0
react/features/base/lastn/reducer.js View File

3
 } from '../config';
3
 } from '../config';
4
 import { ReducerRegistry, set } from '../redux';
4
 import { ReducerRegistry, set } from '../redux';
5
 
5
 
6
+import { SET_LAST_N } from './actionTypes';
6
 import { validateLastNLimits } from './functions';
7
 import { validateLastNLimits } from './functions';
7
 
8
 
8
 ReducerRegistry.register('features/base/lastn', (state = { }, action) => {
9
 ReducerRegistry.register('features/base/lastn', (state = { }, action) => {
9
     switch (action.type) {
10
     switch (action.type) {
10
     case SET_CONFIG:
11
     case SET_CONFIG:
11
         return _setConfig(state, action);
12
         return _setConfig(state, action);
13
+    case SET_LAST_N: {
14
+        const { lastN } = action;
15
+
16
+        return {
17
+            ...state,
18
+            lastN
19
+        };
20
+    }
12
     }
21
     }
13
 
22
 
14
     return state;
23
     return state;

+ 18
- 2
react/features/video-quality/components/VideoQualitySlider.web.js View File

6
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
6
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
7
 import { setAudioOnly } from '../../base/audio-only';
7
 import { setAudioOnly } from '../../base/audio-only';
8
 import { translate } from '../../base/i18n';
8
 import { translate } from '../../base/i18n';
9
+import { setLastN, getLastNForQualityLevel } from '../../base/lastn';
9
 import { connect } from '../../base/redux';
10
 import { connect } from '../../base/redux';
10
 import { setPreferredVideoQuality } from '../actions';
11
 import { setPreferredVideoQuality } from '../actions';
11
-import { VIDEO_QUALITY_LEVELS } from '../constants';
12
+import { DEFAULT_LAST_N, VIDEO_QUALITY_LEVELS } from '../constants';
12
 import logger from '../logger';
13
 import logger from '../logger';
13
 
14
 
14
 const {
15
 const {
44
      */
45
      */
45
     _audioOnly: Boolean,
46
     _audioOnly: Boolean,
46
 
47
 
48
+    /**
49
+     * The channelLastN value configured for the conference.
50
+     */
51
+    _channelLastN: Number,
52
+
47
     /**
53
     /**
48
      * Whether or not the conference is in peer to peer mode.
54
      * Whether or not the conference is in peer to peer mode.
49
      */
55
      */
342
      */
348
      */
343
     _setPreferredVideoQuality(qualityLevel) {
349
     _setPreferredVideoQuality(qualityLevel) {
344
         this.props.dispatch(setPreferredVideoQuality(qualityLevel));
350
         this.props.dispatch(setPreferredVideoQuality(qualityLevel));
345
-
346
         if (this.props._audioOnly) {
351
         if (this.props._audioOnly) {
347
             this.props.dispatch(setAudioOnly(false));
352
             this.props.dispatch(setAudioOnly(false));
348
         }
353
         }
354
+
355
+        // Determine the lastN value based on the quality setting.
356
+        let { _channelLastN = DEFAULT_LAST_N } = this.props;
357
+
358
+        _channelLastN = _channelLastN === -1 ? DEFAULT_LAST_N : _channelLastN;
359
+        const lastN = getLastNForQualityLevel(qualityLevel, _channelLastN);
360
+
361
+        // Set the lastN for the conference.
362
+        this.props.dispatch(setLastN(lastN));
349
     }
363
     }
350
 }
364
 }
351
 
365
 
361
     const { enabled: audioOnly } = state['features/base/audio-only'];
375
     const { enabled: audioOnly } = state['features/base/audio-only'];
362
     const { p2p } = state['features/base/conference'];
376
     const { p2p } = state['features/base/conference'];
363
     const { preferredVideoQuality } = state['features/video-quality'];
377
     const { preferredVideoQuality } = state['features/video-quality'];
378
+    const { channelLastN } = state['features/base/config'];
364
 
379
 
365
     return {
380
     return {
366
         _audioOnly: audioOnly,
381
         _audioOnly: audioOnly,
382
+        _channelLastN: channelLastN,
367
         _p2p: p2p,
383
         _p2p: p2p,
368
         _sendrecvVideoQuality: preferredVideoQuality
384
         _sendrecvVideoQuality: preferredVideoQuality
369
     };
385
     };

+ 6
- 0
react/features/video-quality/constants.js View File

1
+/**
2
+ * Default last-n value used to be used for "HD" video quality setting when no channelLastN value is specified.
3
+ * @type {number}
4
+ */
5
+export const DEFAULT_LAST_N = 20;
6
+
1
 /**
7
 /**
2
  * The supported remote video resolutions. The values are currently based on
8
  * The supported remote video resolutions. The values are currently based on
3
  * available simulcast layers.
9
  * available simulcast layers.

Loading…
Cancel
Save