浏览代码

feat(conference): Enable forced reload of client on bridge failure.

* feat(conference): Enable forced reload of client on bridge failure.
Force the client to reload when the bridge that is handling the media goes down.
This mitigates issues seen on the bridge because of a client re-joining the call with the same endpointId, BWE issues, etc.
This behavior is configurable through 'enableForcedReload' setting in config.js.
The client skips the pre-join page when the page reloads.

* squash: refactor the restart logic.

* squash: fix description

* squash: dispatch conferenceWillLeave action before reload.
master
Jaya Allamsetty 4 年前
父节点
当前提交
0138f23755
没有帐户链接到提交者的电子邮件

+ 6
- 0
config.js 查看文件

325
     // TCC sequence numbers starting from 0.
325
     // TCC sequence numbers starting from 0.
326
     // enableIceRestart: false,
326
     // enableIceRestart: false,
327
 
327
 
328
+    // Enables forced reload of the client when the call is migrated as a result of
329
+    // the bridge going down. Currently enabled by default as call migration through
330
+    // session-terminate is causing siganling issues when Octo is enabled.
331
+    // enableForcedReload: true,
332
+
328
     // Use TURN/UDP servers for the jitsi-videobridge connection (by default
333
     // Use TURN/UDP servers for the jitsi-videobridge connection (by default
329
     // we filter out TURN/UDP because it is usually not needed since the
334
     // we filter out TURN/UDP because it is usually not needed since the
330
     // bridge itself is reachable via UDP)
335
     // bridge itself is reachable via UDP)
732
     //     'dialog.reservationError',
737
     //     'dialog.reservationError',
733
     //     'dialog.serviceUnavailable', // shown when server is not reachable
738
     //     'dialog.serviceUnavailable', // shown when server is not reachable
734
     //     'dialog.sessTerminated', // shown when there is a failed conference session
739
     //     'dialog.sessTerminated', // shown when there is a failed conference session
740
+    //     'dialog.sessionRestarted', // show when a client reload is initiated because of bridge migration
735
     //     'dialog.tokenAuthFailed', // show when an invalid jwt is used
741
     //     'dialog.tokenAuthFailed', // show when an invalid jwt is used
736
     //     'dialog.transcribing', // transcribing notifications (pending, off)
742
     //     'dialog.transcribing', // transcribing notifications (pending, off)
737
     //     'dialOut.statusMessage', // shown when dial out status is updated.
743
     //     'dialOut.statusMessage', // shown when dial out status is updated.

+ 1
- 0
lang/main.json 查看文件

280
         "sendPrivateMessageTitle": "Send privately?",
280
         "sendPrivateMessageTitle": "Send privately?",
281
         "serviceUnavailable": "Service unavailable",
281
         "serviceUnavailable": "Service unavailable",
282
         "sessTerminated": "Call terminated",
282
         "sessTerminated": "Call terminated",
283
+        "sessionRestarted": "Call restarted by the bridge",
283
         "Share": "Share",
284
         "Share": "Share",
284
         "shareVideoLinkError": "Please provide a correct youtube link.",
285
         "shareVideoLinkError": "Please provide a correct youtube link.",
285
         "shareVideoTitle": "Share a video",
286
         "shareVideoTitle": "Share a video",

+ 10
- 10
react/features/base/conference/actionTypes.js 查看文件

42
  */
42
  */
43
 export const CONFERENCE_LEFT = 'CONFERENCE_LEFT';
43
 export const CONFERENCE_LEFT = 'CONFERENCE_LEFT';
44
 
44
 
45
-/**
46
- * The type of (redux) action which signals that an uuid for a conference has been set.
47
- *
48
- * {
49
- *     type: CONFERENCE_UNIQUE_ID_SET,
50
- *     conference: JitsiConference
51
- * }
52
- */
53
-export const CONFERENCE_UNIQUE_ID_SET = 'CONFERENCE_UNIQUE_ID_SET';
54
-
55
 /**
45
 /**
56
  * The type of (redux) action, which indicates conference subject changes.
46
  * The type of (redux) action, which indicates conference subject changes.
57
  *
47
  *
72
 */
62
 */
73
 export const CONFERENCE_TIMESTAMP_CHANGED = 'CONFERENCE_TIMESTAMP_CHANGED';
63
 export const CONFERENCE_TIMESTAMP_CHANGED = 'CONFERENCE_TIMESTAMP_CHANGED';
74
 
64
 
65
+/**
66
+ * The type of (redux) action which signals that an uuid for a conference has been set.
67
+ *
68
+ * {
69
+ *     type: CONFERENCE_UNIQUE_ID_SET,
70
+ *     conference: JitsiConference
71
+ * }
72
+ */
73
+export const CONFERENCE_UNIQUE_ID_SET = 'CONFERENCE_UNIQUE_ID_SET';
74
+
75
 /**
75
 /**
76
  * The type of (redux) action which signals that a specific conference will be
76
  * The type of (redux) action which signals that a specific conference will be
77
  * joined.
77
  * joined.

+ 16
- 16
react/features/base/conference/actions.js 查看文件

296
     };
296
     };
297
 }
297
 }
298
 
298
 
299
-/**
300
- * Signals that the unique identifier for conference has been set.
301
- *
302
- * @param {JitsiConference} conference - The JitsiConference instance, where the uuid has been set.
303
- * @returns {{
304
-    *   type: CONFERENCE_UNIQUE_ID_SET,
305
-    *   conference: JitsiConference,
306
-    * }}
307
-    */
308
-export function conferenceUniqueIdSet(conference: Object) {
309
-    return {
310
-        type: CONFERENCE_UNIQUE_ID_SET,
311
-        conference
312
-    };
313
-}
314
-
315
 /**
299
 /**
316
  * Signals that the conference subject has been changed.
300
  * Signals that the conference subject has been changed.
317
  *
301
  *
344
     };
328
     };
345
 }
329
 }
346
 
330
 
331
+/**
332
+* Signals that the unique identifier for conference has been set.
333
+*
334
+* @param {JitsiConference} conference - The JitsiConference instance, where the uuid has been set.
335
+* @returns {{
336
+*   type: CONFERENCE_UNIQUE_ID_SET,
337
+*   conference: JitsiConference,
338
+* }}
339
+*/
340
+export function conferenceUniqueIdSet(conference: Object) {
341
+    return {
342
+        type: CONFERENCE_UNIQUE_ID_SET,
343
+        conference
344
+    };
345
+}
346
+
347
 /**
347
 /**
348
  * Adds any existing local tracks to a specific conference before the conference
348
  * Adds any existing local tracks to a specific conference before the conference
349
  * is joined. Then signals the intention of the application to have the local
349
  * is joined. Then signals the intention of the application to have the local

+ 26
- 14
react/features/base/conference/middleware.any.js 查看文件

7
     createPinnedEvent,
7
     createPinnedEvent,
8
     sendAnalytics
8
     sendAnalytics
9
 } from '../../analytics';
9
 } from '../../analytics';
10
+import { reloadNow } from '../../app/actions';
10
 import { openDisplayNamePrompt } from '../../display-name';
11
 import { openDisplayNamePrompt } from '../../display-name';
11
 import { showErrorNotification } from '../../notifications';
12
 import { showErrorNotification } from '../../notifications';
12
 import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection';
13
 import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection';
117
 function _conferenceFailed({ dispatch, getState }, next, action) {
118
 function _conferenceFailed({ dispatch, getState }, next, action) {
118
     const result = next(action);
119
     const result = next(action);
119
     const { conference, error } = action;
120
     const { conference, error } = action;
121
+    const { enableForcedReload } = getState()['features/base/config'];
120
 
122
 
121
     // Handle specific failure reasons.
123
     // Handle specific failure reasons.
122
     switch (error.name) {
124
     switch (error.name) {
130
 
132
 
131
         break;
133
         break;
132
     }
134
     }
135
+    case JitsiConferenceErrors.CONFERENCE_RESTARTED: {
136
+        if (enableForcedReload) {
137
+            dispatch(showErrorNotification({
138
+                description: 'Restart initiated because of a bridge failure',
139
+                titleKey: 'dialog.sessionRestarted'
140
+            }));
141
+        }
142
+
143
+        break;
144
+    }
133
     case JitsiConferenceErrors.CONNECTION_ERROR: {
145
     case JitsiConferenceErrors.CONNECTION_ERROR: {
134
         const [ msg ] = error.params;
146
         const [ msg ] = error.params;
135
 
147
 
147
         break;
159
         break;
148
     }
160
     }
149
 
161
 
150
-    // FIXME: Workaround for the web version. Currently, the creation of the
151
-    // conference is handled by /conference.js and appropriate failure handlers
152
-    // are set there.
153
-    if (typeof APP !== 'undefined') {
154
-        if (typeof beforeUnloadHandler !== 'undefined') {
155
-            window.removeEventListener('beforeunload', beforeUnloadHandler);
156
-            beforeUnloadHandler = undefined;
157
-        }
158
-
159
-        return result;
160
-    }
161
-
162
-    // XXX After next(action), it is clear whether the error is recoverable.
163
-    !error.recoverable
162
+    if (typeof APP === 'undefined') {
163
+        !error.recoverable
164
         && conference
164
         && conference
165
         && conference.leave().catch(reason => {
165
         && conference.leave().catch(reason => {
166
             // Even though we don't care too much about the failure, it may be
166
             // Even though we don't care too much about the failure, it may be
167
             // good to know that it happen, so log it (on the info level).
167
             // good to know that it happen, so log it (on the info level).
168
             logger.info('JitsiConference.leave() rejected with:', reason);
168
             logger.info('JitsiConference.leave() rejected with:', reason);
169
         });
169
         });
170
+    } else if (typeof beforeUnloadHandler !== 'undefined') {
171
+        // FIXME: Workaround for the web version. Currently, the creation of the
172
+        // conference is handled by /conference.js and appropriate failure handlers
173
+        // are set there.
174
+        window.removeEventListener('beforeunload', beforeUnloadHandler);
175
+        beforeUnloadHandler = undefined;
176
+    }
177
+
178
+    if (enableForcedReload && error?.name === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
179
+        dispatch(conferenceWillLeave(conference));
180
+        dispatch(reloadNow());
181
+    }
170
 
182
 
171
     return result;
183
     return result;
172
 }
184
 }

+ 21
- 1
react/features/base/conference/middleware.web.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
 import UIEvents from '../../../../service/UI/UIEvents';
3
 import UIEvents from '../../../../service/UI/UIEvents';
4
+import { setPrejoinPageVisibility, setSkipPrejoinOnReload } from '../../prejoin';
5
+import { JitsiConferenceErrors } from '../lib-jitsi-meet';
4
 import { MiddlewareRegistry } from '../redux';
6
 import { MiddlewareRegistry } from '../redux';
5
 import { TOGGLE_SCREENSHARING } from '../tracks/actionTypes';
7
 import { TOGGLE_SCREENSHARING } from '../tracks/actionTypes';
6
 
8
 
9
+import { CONFERENCE_FAILED, CONFERENCE_JOINED } from './actionTypes';
7
 import './middleware.any';
10
 import './middleware.any';
8
 
11
 
9
 declare var APP: Object;
12
 declare var APP: Object;
10
 
13
 
11
-MiddlewareRegistry.register((/* store */) => next => action => {
14
+MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
15
+    const { enableForcedReload } = getState()['features/base/config'];
16
+
12
     switch (action.type) {
17
     switch (action.type) {
18
+    case CONFERENCE_JOINED: {
19
+        if (enableForcedReload) {
20
+            dispatch(setPrejoinPageVisibility(false));
21
+            dispatch(setSkipPrejoinOnReload(false));
22
+        }
23
+
24
+        break;
25
+    }
26
+    case CONFERENCE_FAILED: {
27
+        enableForcedReload
28
+            && action.error?.name === JitsiConferenceErrors.CONFERENCE_RESTARTED
29
+            && dispatch(setSkipPrejoinOnReload(true));
30
+
31
+        break;
32
+    }
13
     case TOGGLE_SCREENSHARING: {
33
     case TOGGLE_SCREENSHARING: {
14
         if (typeof APP === 'object') {
34
         if (typeof APP === 'object') {
15
             APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
35
             APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);

+ 5
- 0
react/features/prejoin/actionTypes.js 查看文件

19
  */
19
  */
20
 export const SET_SKIP_PREJOIN = 'SET_SKIP_PREJOIN';
20
 export const SET_SKIP_PREJOIN = 'SET_SKIP_PREJOIN';
21
 
21
 
22
+/**
23
+ * Action type to set the visiblity of the prejoin page when client is forcefully reloaded.
24
+ */
25
+export const SET_SKIP_PREJOIN_RELOAD = 'SET_SKIP_PREJOIN_RELOAD';
26
+
22
 /**
27
 /**
23
  * Action type used to set the mandatory stance of the prejoin display name.
28
  * Action type used to set the mandatory stance of the prejoin display name.
24
  */
29
  */

+ 15
- 0
react/features/prejoin/actions.js 查看文件

26
     SET_DIALOUT_STATUS,
26
     SET_DIALOUT_STATUS,
27
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
27
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
28
     SET_SKIP_PREJOIN,
28
     SET_SKIP_PREJOIN,
29
+    SET_SKIP_PREJOIN_RELOAD,
29
     SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
30
     SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
30
     SET_PRECALL_TEST_RESULTS,
31
     SET_PRECALL_TEST_RESULTS,
31
     SET_PREJOIN_DEVICE_ERRORS,
32
     SET_PREJOIN_DEVICE_ERRORS,
418
     };
419
     };
419
 }
420
 }
420
 
421
 
422
+/**
423
+ * Sets the visibility of the prejoin page when a client reload
424
+ * is triggered as a result of call migration initiated by Jicofo.
425
+ *
426
+ * @param {boolean} value - The visibility value.
427
+ * @returns {Object}
428
+ */
429
+export function setSkipPrejoinOnReload(value: boolean) {
430
+    return {
431
+        type: SET_SKIP_PREJOIN_RELOAD,
432
+        value
433
+    };
434
+}
435
+
421
 /**
436
 /**
422
  * Action used to set the visiblitiy of the 'JoinByPhoneDialog'.
437
  * Action used to set the visiblitiy of the 'JoinByPhoneDialog'.
423
  *
438
  *

+ 2
- 1
react/features/prejoin/functions.js 查看文件

149
 export function isPrejoinPageEnabled(state: Object): boolean {
149
 export function isPrejoinPageEnabled(state: Object): boolean {
150
     return navigator.product !== 'ReactNative'
150
     return navigator.product !== 'ReactNative'
151
         && state['features/base/config'].prejoinPageEnabled
151
         && state['features/base/config'].prejoinPageEnabled
152
-        && !state['features/base/settings'].userSelectedSkipPrejoin;
152
+        && !state['features/base/settings'].userSelectedSkipPrejoin
153
+        && !(state['features/base/config'].enableForcedReload && state['features/prejoin'].skipPrejoinOnReload);
153
 }
154
 }
154
 
155
 
155
 /**
156
 /**

+ 24
- 2
react/features/prejoin/reducer.js 查看文件

1
-import { ReducerRegistry } from '../base/redux';
1
+import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
2
 
2
 
3
 import {
3
 import {
4
     SET_DEVICE_STATUS,
4
     SET_DEVICE_STATUS,
10
     SET_PREJOIN_DEVICE_ERRORS,
10
     SET_PREJOIN_DEVICE_ERRORS,
11
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
11
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
12
     SET_PREJOIN_PAGE_VISIBILITY,
12
     SET_PREJOIN_PAGE_VISIBILITY,
13
-    SET_SKIP_PREJOIN
13
+    SET_SKIP_PREJOIN,
14
+    SET_SKIP_PREJOIN_RELOAD
14
 } from './actionTypes';
15
 } from './actionTypes';
15
 
16
 
16
 const DEFAULT_STATE = {
17
 const DEFAULT_STATE = {
28
     name: '',
29
     name: '',
29
     rawError: '',
30
     rawError: '',
30
     showPrejoin: true,
31
     showPrejoin: true,
32
+    skipPrejoinOnReload: false,
31
     showJoinByPhoneDialog: false,
33
     showJoinByPhoneDialog: false,
32
     userSelectedSkipPrejoin: false
34
     userSelectedSkipPrejoin: false
33
 };
35
 };
34
 
36
 
37
+/**
38
+ * The name of the redux store/state property which is the root of the redux
39
+ * state of the feature {@code prejoin}.
40
+ */
41
+const STORE_NAME = 'features/prejoin';
42
+
43
+/**
44
+ * Sets up the persistence of the feature {@code prejoin}.
45
+ */
46
+PersistenceRegistry.register(STORE_NAME, {
47
+    skipPrejoinOnReload: true
48
+}, DEFAULT_STATE);
49
+
35
 /**
50
 /**
36
  * Listen for actions that mutate the prejoin state
51
  * Listen for actions that mutate the prejoin state
37
  */
52
  */
46
             };
61
             };
47
         }
62
         }
48
 
63
 
64
+        case SET_SKIP_PREJOIN_RELOAD: {
65
+            return {
66
+                ...state,
67
+                skipPrejoinOnReload: action.value
68
+            };
69
+        }
70
+
49
         case SET_PRECALL_TEST_RESULTS:
71
         case SET_PRECALL_TEST_RESULTS:
50
             return {
72
             return {
51
                 ...state,
73
                 ...state,

正在加载...
取消
保存