Explorar el Código

fix(quality-control): Send the new receiver constraints on state changes.

The client now listens for changes to lastN, selectedEndpoints and maxReceiverVideoQuality in redux to trigger sending  bridge message in the new format. This fixes an issue where the stage view <-> tile view changes prompt two receiver constraints messages to be sent, first with the maxHeight update and then with the selected endpoints update.
j8
Jaya Allamsetty hace 4 años
padre
commit
91197bc69f
No account linked to committer's email address

+ 8
- 26
react/features/base/lastn/middleware.js Ver fichero

@@ -18,13 +18,12 @@ import {
18 18
 import { MiddlewareRegistry } from '../redux';
19 19
 import { isLocalVideoTrackDesktop } from '../tracks/functions';
20 20
 
21
-import { SET_LAST_N } from './actionTypes';
21
+import { setLastN } from './actions';
22 22
 import { limitLastN } from './functions';
23 23
 import logger from './logger';
24 24
 
25 25
 declare var APP: Object;
26 26
 
27
-
28 27
 MiddlewareRegistry.register(store => next => action => {
29 28
     const result = next(action);
30 29
 
@@ -41,12 +40,6 @@ MiddlewareRegistry.register(store => next => action => {
41 40
     case SET_TILE_VIEW:
42 41
         _updateLastN(store);
43 42
         break;
44
-    case SET_LAST_N: {
45
-        const { lastN } = action;
46
-
47
-        _updateLastN(store, lastN);
48
-        break;
49
-    }
50 43
     }
51 44
 
52 45
     return result;
@@ -56,11 +49,10 @@ MiddlewareRegistry.register(store => next => action => {
56 49
  * Updates the last N value in the conference based on the current state of the redux store.
57 50
  *
58 51
  * @param {Store} store - The redux store.
59
- * @param {number} value - The last-n value to be set.
60 52
  * @private
61 53
  * @returns {void}
62 54
  */
63
-function _updateLastN({ getState }, value = null) {
55
+function _updateLastN({ dispatch, getState }) {
64 56
     const state = getState();
65 57
     const { conference } = state['features/base/conference'];
66 58
     const { enabled: audioOnly } = state['features/base/audio-only'];
@@ -77,12 +69,11 @@ function _updateLastN({ getState }, value = null) {
77 69
     }
78 70
 
79 71
     // 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
+    // 1. The last-n value in redux.
73
+    // 2. The last-n value from 'startLastN' if it is specified in config.js
74
+    // 3. The last-n value from 'channelLastN' if specified in config.js.
75
+    // 4. -1 as the default value.
76
+    let lastNSelected = lastN || (config.startLastN ?? (config.channelLastN ?? -1));
86 77
 
87 78
     // Apply last N limit based on the # of participants and config settings.
88 79
     const limitedLastN = limitLastN(participantCount, lastNLimits);
@@ -111,15 +102,6 @@ function _updateLastN({ getState }, value = null) {
111 102
         lastNSelected = 1;
112 103
     }
113 104
 
114
-    if (conference.getLastN() === lastNSelected) {
115
-        return;
116
-    }
117
-
118 105
     logger.info(`Setting last N to: ${lastNSelected}`);
119
-
120
-    try {
121
-        conference.setLastN(lastNSelected);
122
-    } catch (err) {
123
-        logger.error(`Failed to set lastN: ${err}`);
124
-    }
106
+    dispatch(setLastN(lastNSelected));
125 107
 }

+ 5
- 24
react/features/large-video/actions.any.js Ver fichero

@@ -2,15 +2,9 @@
2 2
 
3 3
 import type { Dispatch } from 'redux';
4 4
 
5
-import {
6
-    createSelectParticipantFailedEvent,
7
-    sendAnalytics
8
-} from '../analytics';
9
-import { _handleParticipantError } from '../base/conference';
10 5
 import { MEDIA_TYPE } from '../base/media';
11 6
 import { getParticipants } from '../base/participants';
12
-import { reportError } from '../base/util';
13
-import { shouldDisplayTileView } from '../video-layout';
7
+import { selectEndpoints, shouldDisplayTileView } from '../video-layout';
14 8
 
15 9
 import {
16 10
     SELECT_LARGE_VIDEO_PARTICIPANT,
@@ -25,24 +19,11 @@ import {
25 19
 export function selectParticipant() {
26 20
     return (dispatch: Dispatch<any>, getState: Function) => {
27 21
         const state = getState();
28
-        const { conference } = state['features/base/conference'];
29
-
30
-        if (conference) {
31
-            const ids = shouldDisplayTileView(state)
32
-                ? getParticipants(state).map(participant => participant.id)
33
-                : [ state['features/large-video'].participantId ];
34
-
35
-            try {
36
-                conference.selectParticipants(ids);
37
-            } catch (err) {
38
-                _handleParticipantError(err);
22
+        const ids = shouldDisplayTileView(state)
23
+            ? getParticipants(state).map(participant => participant.id)
24
+            : [ state['features/large-video'].participantId ];
39 25
 
40
-                sendAnalytics(createSelectParticipantFailedEvent(err));
41
-
42
-                reportError(
43
-                    err, `Failed to select participants ${ids.toString()}`);
44
-            }
45
-        }
26
+        dispatch(selectEndpoints(ids));
46 27
     };
47 28
 }
48 29
 

+ 6
- 0
react/features/video-layout/actionTypes.js Ver fichero

@@ -10,6 +10,12 @@
10 10
 export const SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED
11 11
     = 'SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED';
12 12
 
13
+/**
14
+ * The type of the action which sets the list of the endpoints to be selected for video forwarding
15
+ * from the bridge.
16
+ */
17
+export const SELECT_ENDPOINTS = 'SELECT_ENDPOINTS';
18
+
13 19
 /**
14 20
  * The type of the action which enables or disables the feature for showing
15 21
  * video thumbnails in a two-axis tile view.

+ 18
- 0
react/features/video-layout/actions.js Ver fichero

@@ -4,10 +4,28 @@ import type { Dispatch } from 'redux';
4 4
 
5 5
 import {
6 6
     SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
7
+    SELECT_ENDPOINTS,
7 8
     SET_TILE_VIEW
8 9
 } from './actionTypes';
9 10
 import { shouldDisplayTileView } from './functions';
10 11
 
12
+/**
13
+ * Creates a (redux) action which signals that a new set of remote endpoints need to be selected.
14
+ *
15
+ * @param {Array<string>} participantIds - The remote participants that are currently selected
16
+ * for video forwarding from the bridge.
17
+ * @returns {{
18
+ *      type: SELECT_ENDPOINTS,
19
+ *      particpantsIds: Array<string>
20
+ * }}
21
+ */
22
+export function selectEndpoints(participantIds: Array<string>) {
23
+    return {
24
+        type: SELECT_ENDPOINTS,
25
+        participantIds
26
+    };
27
+}
28
+
11 29
 /**
12 30
  * Creates a (redux) action which signals that the list of known remote participants
13 31
  * with screen shares has changed.

+ 8
- 0
react/features/video-layout/reducer.js Ver fichero

@@ -4,6 +4,7 @@ import { ReducerRegistry } from '../base/redux';
4 4
 
5 5
 import {
6 6
     SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
7
+    SELECT_ENDPOINTS,
7 8
     SET_TILE_VIEW
8 9
 } from './actionTypes';
9 10
 
@@ -34,6 +35,13 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
34 35
         };
35 36
     }
36 37
 
38
+    case SELECT_ENDPOINTS: {
39
+        return {
40
+            ...state,
41
+            selectedEndpoints: action.participantIds
42
+        };
43
+    }
44
+
37 45
     case SET_TILE_VIEW:
38 46
         return {
39 47
             ...state,

+ 4
- 178
react/features/video-quality/middleware.js Ver fichero

@@ -1,21 +1,13 @@
1 1
 // @flow
2 2
 
3
-import {
4
-    CONFERENCE_JOINED,
5
-    DATA_CHANNEL_OPENED
6
-} from '../base/conference';
3
+import { CONFERENCE_JOINED } from '../base/conference';
7 4
 import { SET_CONFIG } from '../base/config';
8
-import { getParticipantCount } from '../base/participants';
9
-import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
10
-import { shouldDisplayTileView } from '../video-layout';
5
+import { MiddlewareRegistry } from '../base/redux';
11 6
 
12
-import { setPreferredVideoQuality, setMaxReceiverVideoQuality } from './actions';
13
-import { VIDEO_QUALITY_LEVELS } from './constants';
14
-import { getReceiverVideoQualityLevel } from './functions';
7
+import { setPreferredVideoQuality } from './actions';
15 8
 import logger from './logger';
16
-import { getMinHeightForQualityLvlMap } from './selector';
17 9
 
18
-declare var APP: Object;
10
+import './subscriber';
19 11
 
20 12
 /**
21 13
  * Implements the middleware of the feature video-quality.
@@ -24,10 +16,6 @@ declare var APP: Object;
24 16
  * @returns {Function}
25 17
  */
26 18
 MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
27
-    if (action.type === DATA_CHANNEL_OPENED) {
28
-        return _syncReceiveVideoQuality(getState, next, action);
29
-    }
30
-
31 19
     const result = next(action);
32 20
 
33 21
     switch (action.type) {
@@ -57,165 +45,3 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
57 45
 
58 46
     return result;
59 47
 });
60
-
61
-/**
62
- * Implements a state listener in order to calculate max receiver video quality.
63
- */
64
-StateListenerRegistry.register(
65
-    /* selector */ state => {
66
-        const { reducedUI } = state['features/base/responsive-ui'];
67
-        const _shouldDisplayTileView = shouldDisplayTileView(state);
68
-        const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
69
-        const participantCount = getParticipantCount(state);
70
-
71
-        return {
72
-            displayTileView: _shouldDisplayTileView,
73
-            participantCount,
74
-            reducedUI,
75
-            thumbnailHeight: thumbnailSize?.height
76
-        };
77
-    },
78
-    /* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
79
-        const state = getState();
80
-        const { maxReceiverVideoQuality } = state['features/video-quality'];
81
-        const { maxFullResolutionParticipants = 2 } = state['features/base/config'];
82
-
83
-        let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.ULTRA;
84
-
85
-        if (reducedUI) {
86
-            newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
87
-        } else if (displayTileView && !Number.isNaN(thumbnailHeight)) {
88
-            newMaxRecvVideoQuality = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
89
-
90
-            // Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
91
-            if (maxReceiverVideoQuality !== newMaxRecvVideoQuality && maxFullResolutionParticipants !== -1) {
92
-                const override
93
-                    = participantCount > maxFullResolutionParticipants
94
-                        && newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
95
-
96
-                logger.info(`Video quality level for thumbnail height: ${thumbnailHeight}, `
97
-                    + `is: ${newMaxRecvVideoQuality}, `
98
-                    + `override: ${String(override)}, `
99
-                    + `max full res N: ${maxFullResolutionParticipants}`);
100
-
101
-                if (override) {
102
-                    newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
103
-                }
104
-            }
105
-        }
106
-
107
-        if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {
108
-            dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
109
-        }
110
-    }, {
111
-        deepEquals: true
112
-    });
113
-
114
-/**
115
- * Helper function for updating the preferred receiver video constraint, based
116
- * on the user preference and the internal maximum.
117
- *
118
- * @param {JitsiConference} conference - The JitsiConference instance for the
119
- * current call.
120
- * @param {number} preferred - The user preferred max frame height.
121
- * @param {number} max - The maximum frame height the application should
122
- * receive.
123
- * @returns {void}
124
- */
125
-function _setReceiverVideoConstraint(conference, preferred, max) {
126
-    if (conference) {
127
-        const value = Math.min(preferred, max);
128
-
129
-        conference.setReceiverVideoConstraint(value);
130
-        logger.info(`setReceiverVideoConstraint: ${value}`);
131
-    }
132
-}
133
-
134
-/**
135
- * Helper function for updating the preferred sender video constraint, based
136
- * on the user preference.
137
- *
138
- * @param {JitsiConference} conference - The JitsiConference instance for the
139
- * current call.
140
- * @param {number} preferred - The user preferred max frame height.
141
- * @returns {void}
142
- */
143
-function _setSenderVideoConstraint(conference, preferred) {
144
-    if (conference) {
145
-        conference.setSenderVideoConstraint(preferred)
146
-            .catch(err => {
147
-                logger.error(`Changing sender resolution to ${preferred} failed - ${err} `);
148
-            });
149
-    }
150
-}
151
-
152
-/**
153
- * Sets the maximum receive video quality.
154
- *
155
- * @param {Function} getState - The redux function which returns the current redux state.
156
- * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
157
- * specified {@code action} to the specified {@code store}.
158
- * @param {Action} action - The redux action {@code DATA_CHANNEL_STATUS_CHANGED}
159
- * which is being dispatched in the specified {@code store}.
160
- * @private
161
- * @returns {Object} The value returned by {@code next(action)}.
162
- */
163
-function _syncReceiveVideoQuality(getState, next, action) {
164
-    const state = getState();
165
-    const {
166
-        conference
167
-    } = state['features/base/conference'];
168
-    const {
169
-        maxReceiverVideoQuality,
170
-        preferredVideoQuality
171
-    } = state['features/video-quality'];
172
-
173
-    _setReceiverVideoConstraint(
174
-        conference,
175
-        preferredVideoQuality,
176
-        maxReceiverVideoQuality);
177
-
178
-    return next(action);
179
-}
180
-
181
-
182
-/**
183
- * Registers a change handler for state['features/base/conference'] to update
184
- * the preferred video quality levels based on user preferred and internal
185
- * settings.
186
- */
187
-StateListenerRegistry.register(
188
-    /* selector */ state => {
189
-        const { conference } = state['features/base/conference'];
190
-        const {
191
-            maxReceiverVideoQuality,
192
-            preferredVideoQuality
193
-        } = state['features/video-quality'];
194
-
195
-        return {
196
-            conference,
197
-            maxReceiverVideoQuality,
198
-            preferredVideoQuality
199
-        };
200
-    },
201
-    /* listener */ (currentState, store, previousState = {}) => {
202
-        const {
203
-            conference,
204
-            maxReceiverVideoQuality,
205
-            preferredVideoQuality
206
-        } = currentState;
207
-        const changedConference = conference !== previousState.conference;
208
-        const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
209
-        const changedMaxVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
210
-
211
-        if (changedConference || changedPreferredVideoQuality || changedMaxVideoQuality) {
212
-            _setReceiverVideoConstraint(conference, preferredVideoQuality, maxReceiverVideoQuality);
213
-        }
214
-        if (changedConference || changedPreferredVideoQuality) {
215
-            _setSenderVideoConstraint(conference, preferredVideoQuality);
216
-        }
217
-
218
-        if (typeof APP !== 'undefined' && changedPreferredVideoQuality) {
219
-            APP.API.notifyVideoQualityChanged(preferredVideoQuality);
220
-        }
221
-    });

+ 190
- 0
react/features/video-quality/subscriber.js Ver fichero

@@ -0,0 +1,190 @@
1
+// @flow
2
+
3
+import debounce from 'lodash/debounce';
4
+
5
+import { _handleParticipantError } from '../base/conference';
6
+import { getParticipantCount } from '../base/participants';
7
+import { StateListenerRegistry } from '../base/redux';
8
+import { reportError } from '../base/util';
9
+import { shouldDisplayTileView } from '../video-layout';
10
+
11
+import { setMaxReceiverVideoQuality } from './actions';
12
+import { VIDEO_QUALITY_LEVELS } from './constants';
13
+import { getReceiverVideoQualityLevel } from './functions';
14
+import logger from './logger';
15
+import { getMinHeightForQualityLvlMap } from './selector';
16
+
17
+declare var APP: Object;
18
+
19
+/**
20
+ * StateListenerRegistry provides a reliable way of detecting changes to selected
21
+ * endpoints state and dispatching additional actions. The listener is debounced
22
+ * so that the client doesn't end up sending too many bridge messages when the user is
23
+ * scrolling through the thumbnails prompting updates to the selected endpoints.
24
+ */
25
+StateListenerRegistry.register(
26
+    /* selector */ state => state['features/video-layout'].selectedEndpoints,
27
+    /* listener */ debounce((selectedEndpoints, store) => {
28
+        _updateReceiverVideoConstraints(store);
29
+    }, 1000));
30
+
31
+/**
32
+ * StateListenerRegistry provides a reliable way of detecting changes to
33
+ * lastn state and dispatching additional actions.
34
+ */
35
+StateListenerRegistry.register(
36
+    /* selector */ state => state['features/base/lastn'].lastN,
37
+    /* listener */ (lastN, store) => {
38
+        _updateReceiverVideoConstraints(store);
39
+    });
40
+
41
+/**
42
+ * StateListenerRegistry provides a reliable way of detecting changes to
43
+ * maxReceiverVideoQuality and preferredVideoQuality state and dispatching additional actions.
44
+ */
45
+StateListenerRegistry.register(
46
+    /* selector */ state => {
47
+        const {
48
+            maxReceiverVideoQuality,
49
+            preferredVideoQuality
50
+        } = state['features/video-quality'];
51
+
52
+        return {
53
+            maxReceiverVideoQuality,
54
+            preferredVideoQuality
55
+        };
56
+    },
57
+    /* listener */ (currentState, store, previousState = {}) => {
58
+        const { maxReceiverVideoQuality, preferredVideoQuality } = currentState;
59
+        const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
60
+        const changedReceiverVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
61
+
62
+        if (changedPreferredVideoQuality) {
63
+            _setSenderVideoConstraint(preferredVideoQuality, store);
64
+            typeof APP !== 'undefined' && APP.API.notifyVideoQualityChanged(preferredVideoQuality);
65
+        }
66
+        changedReceiverVideoQuality && _updateReceiverVideoConstraints(store);
67
+    });
68
+
69
+/**
70
+ * Implements a state listener in order to calculate max receiver video quality.
71
+ */
72
+StateListenerRegistry.register(
73
+    /* selector */ state => {
74
+        const { reducedUI } = state['features/base/responsive-ui'];
75
+        const _shouldDisplayTileView = shouldDisplayTileView(state);
76
+        const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
77
+        const participantCount = getParticipantCount(state);
78
+
79
+        return {
80
+            displayTileView: _shouldDisplayTileView,
81
+            participantCount,
82
+            reducedUI,
83
+            thumbnailHeight: thumbnailSize?.height
84
+        };
85
+    },
86
+    /* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
87
+        const state = getState();
88
+        const { maxReceiverVideoQuality } = state['features/video-quality'];
89
+        const { maxFullResolutionParticipants = 2 } = state['features/base/config'];
90
+
91
+        let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.ULTRA;
92
+
93
+        if (reducedUI) {
94
+            newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
95
+        } else if (displayTileView && !Number.isNaN(thumbnailHeight)) {
96
+            newMaxRecvVideoQuality = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
97
+
98
+            // Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
99
+            if (maxReceiverVideoQuality !== newMaxRecvVideoQuality && maxFullResolutionParticipants !== -1) {
100
+                const override
101
+                    = participantCount > maxFullResolutionParticipants
102
+                        && newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
103
+
104
+                logger.info(`Video quality level for thumbnail height: ${thumbnailHeight}, `
105
+                    + `is: ${newMaxRecvVideoQuality}, `
106
+                    + `override: ${String(override)}, `
107
+                    + `max full res N: ${maxFullResolutionParticipants}`);
108
+
109
+                if (override) {
110
+                    newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
111
+                }
112
+            }
113
+        }
114
+
115
+        if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {
116
+            dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
117
+        }
118
+    }, {
119
+        deepEquals: true
120
+    });
121
+
122
+/**
123
+ * Helper function for updating the preferred sender video constraint, based on the user preference.
124
+ *
125
+ * @param {number} preferred - The user preferred max frame height.
126
+ * @returns {void}
127
+ */
128
+function _setSenderVideoConstraint(preferred, { getState }) {
129
+    const state = getState();
130
+    const { conference } = state['features/base/conference'];
131
+
132
+    if (!conference) {
133
+        return;
134
+    }
135
+
136
+    logger.info(`Setting sender resolution to ${preferred}`);
137
+    conference.setSenderVideoConstraint(preferred)
138
+        .catch(error => {
139
+            _handleParticipantError(error);
140
+            reportError(error, `Changing sender resolution to ${preferred} failed.`);
141
+        });
142
+}
143
+
144
+/**
145
+ * Private helper to calculate the receiver video constraints and set them on the bridge channel.
146
+ *
147
+ * @param {*} store - The redux store.
148
+ * @returns {void}
149
+ */
150
+function _updateReceiverVideoConstraints({ getState }) {
151
+    const state = getState();
152
+    const { conference } = state['features/base/conference'];
153
+
154
+    if (!conference) {
155
+        return;
156
+    }
157
+    const { lastN } = state['features/base/lastn'];
158
+    const { maxReceiverVideoQuality, preferredVideoQuality } = state['features/video-quality'];
159
+    const { selectedEndpoints } = state['features/video-layout'];
160
+    const maxFrameHeight = Math.min(maxReceiverVideoQuality, preferredVideoQuality);
161
+    const receiverConstraints = {
162
+        constraints: {},
163
+        defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.LOW },
164
+        lastN,
165
+        onStageEndpoints: [],
166
+        selectedEndpoints: []
167
+    };
168
+
169
+    if (!selectedEndpoints?.length) {
170
+        return;
171
+    }
172
+
173
+    // Stage view.
174
+    if (selectedEndpoints?.length === 1) {
175
+        receiverConstraints.constraints[selectedEndpoints[0]] = { 'maxHeight': maxFrameHeight };
176
+        receiverConstraints.onStageEndpoints = selectedEndpoints;
177
+
178
+    // Tile view.
179
+    } else {
180
+        receiverConstraints.defaultConstraints = { 'maxHeight': maxFrameHeight };
181
+    }
182
+
183
+    logger.info(`Setting receiver video constraints to ${JSON.stringify(receiverConstraints)}`);
184
+    try {
185
+        conference.setReceiverConstraints(receiverConstraints);
186
+    } catch (error) {
187
+        _handleParticipantError(error);
188
+        reportError(error, `Failed to set receiver video constraints ${JSON.stringify(receiverConstraints)}`);
189
+    }
190
+}

Loading…
Cancelar
Guardar