@webex/plugin-meetings 3.7.0 → 3.8.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotation/index.js +17 -0
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/join-forbidden-error.js +52 -0
- package/dist/common/errors/join-forbidden-error.js.map +1 -0
- package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/common/errors/multistream-not-supported-error.js +53 -0
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -0
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +69 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +4 -4
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +14 -3
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +35 -17
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +1 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +30 -16
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +167 -0
- package/dist/meeting/brbState.js.map +1 -0
- package/dist/meeting/in-meeting-actions.js +13 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1373 -1052
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +32 -11
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +1 -6
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +51 -29
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +103 -67
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +115 -45
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +6 -2
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +107 -55
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +2 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +1 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +39 -28
- package/dist/member/util.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +6 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMedia.js +30 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +40 -8
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +24 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +471 -140
- package/dist/reachability/index.js.map +1 -1
- package/dist/{rtcMetrics/constants.js → reachability/reachability.types.js} +1 -5
- package/dist/reachability/reachability.types.js.map +1 -0
- package/dist/reachability/request.js +21 -8
- package/dist/reachability/request.js.map +1 -1
- package/dist/recording-controller/enums.js +8 -4
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +18 -9
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +13 -9
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +15 -15
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +45 -79
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +3 -6
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/annotation/index.d.ts +5 -0
- package/dist/types/common/errors/join-forbidden-error.d.ts +15 -0
- package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/common/errors/multistream-not-supported-error.d.ts +17 -0
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +54 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/brbState.d.ts +54 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +12 -0
- package/dist/types/meeting/index.d.ts +86 -14
- package/dist/types/meeting/locusMediaRequest.d.ts +6 -3
- package/dist/types/meeting/request.d.ts +14 -3
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +30 -5
- package/dist/types/meetings/index.d.ts +20 -2
- package/dist/types/meetings/meetings.types.d.ts +8 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +7 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +6 -1
- package/dist/types/multistream/remoteMediaManager.d.ts +10 -1
- package/dist/types/multistream/sendSlotManager.d.ts +8 -1
- package/dist/types/reachability/clusterReachability.d.ts +1 -10
- package/dist/types/reachability/index.d.ts +83 -36
- package/dist/types/reachability/reachability.types.d.ts +64 -0
- package/dist/types/reachability/request.d.ts +5 -1
- package/dist/types/recording-controller/enums.d.ts +5 -2
- package/dist/types/recording-controller/index.d.ts +1 -0
- package/dist/types/recording-controller/util.d.ts +2 -1
- package/dist/types/roap/request.d.ts +1 -13
- package/dist/webinar/index.js +390 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +23 -22
- package/src/annotation/index.ts +16 -0
- package/src/common/errors/join-forbidden-error.ts +26 -0
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/common/errors/multistream-not-supported-error.ts +30 -0
- package/src/config.ts +2 -0
- package/src/constants.ts +62 -3
- package/src/index.ts +5 -3
- package/src/interpretation/index.ts +3 -3
- package/src/locus-info/index.ts +20 -3
- package/src/locus-info/selfUtils.ts +24 -6
- package/src/media/MediaConnectionAwaiter.ts +2 -0
- package/src/media/properties.ts +34 -13
- package/src/meeting/brbState.ts +169 -0
- package/src/meeting/in-meeting-actions.ts +25 -0
- package/src/meeting/index.ts +485 -88
- package/src/meeting/locusMediaRequest.ts +38 -12
- package/src/meeting/muteState.ts +1 -6
- package/src/meeting/request.ts +30 -12
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +32 -13
- package/src/meeting-info/meeting-info-v2.ts +83 -12
- package/src/meeting-info/utilv2.ts +17 -3
- package/src/meetings/index.ts +79 -20
- package/src/meetings/meetings.types.ts +10 -0
- package/src/meetings/util.ts +2 -1
- package/src/member/index.ts +9 -0
- package/src/member/types.ts +8 -0
- package/src/member/util.ts +34 -24
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +6 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/multistream/remoteMediaManager.ts +32 -10
- package/src/multistream/sendSlotManager.ts +31 -0
- package/src/reachability/clusterReachability.ts +5 -15
- package/src/reachability/index.ts +315 -75
- package/src/reachability/reachability.types.ts +85 -0
- package/src/reachability/request.ts +55 -31
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +28 -9
- package/src/roap/index.ts +14 -13
- package/src/roap/request.ts +30 -44
- package/src/roap/turnDiscovery.ts +2 -4
- package/src/webinar/index.ts +235 -9
- package/test/unit/spec/annotation/index.ts +46 -1
- package/test/unit/spec/interpretation/index.ts +39 -1
- package/test/unit/spec/locus-info/index.js +292 -60
- package/test/unit/spec/locus-info/selfConstant.js +7 -0
- package/test/unit/spec/locus-info/selfUtils.js +101 -1
- package/test/unit/spec/media/properties.ts +15 -0
- package/test/unit/spec/meeting/brbState.ts +114 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +15 -1
- package/test/unit/spec/meeting/index.js +908 -124
- package/test/unit/spec/meeting/locusMediaRequest.ts +111 -66
- package/test/unit/spec/meeting/muteState.js +0 -24
- package/test/unit/spec/meeting/request.js +3 -26
- package/test/unit/spec/meeting/utils.js +73 -28
- package/test/unit/spec/meeting-info/meetinginfov2.js +46 -4
- package/test/unit/spec/meeting-info/utilv2.js +26 -0
- package/test/unit/spec/meetings/index.js +172 -18
- package/test/unit/spec/meetings/utils.js +10 -0
- package/test/unit/spec/member/util.js +52 -11
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/multistream/remoteMediaManager.ts +397 -118
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/reachability/index.ts +391 -9
- package/test/unit/spec/reachability/request.js +48 -12
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/roap/index.ts +48 -1
- package/test/unit/spec/roap/request.ts +51 -109
- package/test/unit/spec/roap/turnDiscovery.ts +202 -147
- package/test/unit/spec/webinar/index.ts +509 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/rtcMetrics/constants.js.map +0 -1
- package/dist/rtcMetrics/index.js +0 -197
- package/dist/rtcMetrics/index.js.map +0 -1
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/rtcMetrics/constants.d.ts +0 -4
- package/dist/types/rtcMetrics/index.d.ts +0 -71
- package/src/common/errors/webinar-registration-error.ts +0 -27
package/src/meeting/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import jwtDecode from 'jwt-decode';
|
|
|
5
5
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
6
6
|
// @ts-ignore - Types not available for @webex/common
|
|
7
7
|
import {Defer} from '@webex/common';
|
|
8
|
+
import {safeSetTimeout, safeSetInterval} from '@webex/common-timers';
|
|
8
9
|
import {
|
|
9
10
|
ClientEvent,
|
|
10
11
|
ClientEventLeaveReason,
|
|
@@ -30,7 +31,6 @@ import {
|
|
|
30
31
|
} from '@webex/internal-media-core';
|
|
31
32
|
|
|
32
33
|
import {
|
|
33
|
-
getDevices,
|
|
34
34
|
LocalStream,
|
|
35
35
|
LocalCameraStream,
|
|
36
36
|
LocalDisplayStream,
|
|
@@ -121,6 +121,10 @@ import {
|
|
|
121
121
|
MEETING_PERMISSION_TOKEN_REFRESH_REASON,
|
|
122
122
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
|
|
123
123
|
NAMED_MEDIA_GROUP_TYPE_AUDIO,
|
|
124
|
+
WEBINAR_ERROR_WEBCAST,
|
|
125
|
+
WEBINAR_ERROR_REGISTRATION_ID,
|
|
126
|
+
JOIN_BEFORE_HOST,
|
|
127
|
+
REGISTRATION_ID_STATUS,
|
|
124
128
|
} from '../constants';
|
|
125
129
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
126
130
|
import ParameterError from '../common/errors/parameter';
|
|
@@ -128,7 +132,8 @@ import {
|
|
|
128
132
|
MeetingInfoV2PasswordError,
|
|
129
133
|
MeetingInfoV2CaptchaError,
|
|
130
134
|
MeetingInfoV2PolicyError,
|
|
131
|
-
|
|
135
|
+
MeetingInfoV2JoinWebinarError,
|
|
136
|
+
MeetingInfoV2JoinForbiddenError,
|
|
132
137
|
} from '../meeting-info/meeting-info-v2';
|
|
133
138
|
import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
|
134
139
|
import SendSlotManager from '../multistream/sendSlotManager';
|
|
@@ -157,7 +162,11 @@ import ControlsOptionsManager from '../controls-options-manager';
|
|
|
157
162
|
import PermissionError from '../common/errors/permission';
|
|
158
163
|
import {LocusMediaRequest} from './locusMediaRequest';
|
|
159
164
|
import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
|
|
160
|
-
import
|
|
165
|
+
import JoinWebinarError from '../common/errors/join-webinar-error';
|
|
166
|
+
import Member from '../member';
|
|
167
|
+
import {BrbState, createBrbState} from './brbState';
|
|
168
|
+
import MultistreamNotSupportedError from '../common/errors/multistream-not-supported-error';
|
|
169
|
+
import JoinForbiddenError from '../common/errors/join-forbidden-error';
|
|
161
170
|
|
|
162
171
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
|
163
172
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
|
@@ -232,6 +241,8 @@ export type CallStateForMetrics = {
|
|
|
232
241
|
sessionCorrelationId?: string;
|
|
233
242
|
joinTrigger?: string;
|
|
234
243
|
loginType?: string;
|
|
244
|
+
userNameInput?: string;
|
|
245
|
+
emailInput?: string;
|
|
235
246
|
};
|
|
236
247
|
|
|
237
248
|
export const MEDIA_UPDATE_TYPE = {
|
|
@@ -248,6 +259,7 @@ export enum ScreenShareFloorStatus {
|
|
|
248
259
|
|
|
249
260
|
type FetchMeetingInfoParams = {
|
|
250
261
|
password?: string;
|
|
262
|
+
registrationId?: string;
|
|
251
263
|
captchaCode?: string;
|
|
252
264
|
extraParams?: Record<string, any>;
|
|
253
265
|
sendCAevents?: boolean;
|
|
@@ -642,6 +654,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
642
654
|
turnServerUsed: boolean;
|
|
643
655
|
areVoiceaEventsSetup = false;
|
|
644
656
|
isMoveToInProgress = false;
|
|
657
|
+
registrationIdStatus: string;
|
|
658
|
+
brbState: BrbState;
|
|
645
659
|
|
|
646
660
|
voiceaListenerCallbacks: object = {
|
|
647
661
|
[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]: (payload: Transcription['languageOptions']) => {
|
|
@@ -702,6 +716,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
702
716
|
private iceCandidateErrors: Map<string, number>;
|
|
703
717
|
private iceCandidatesCount: number;
|
|
704
718
|
private rtcMetrics?: RtcMetrics;
|
|
719
|
+
private uploadLogsTimer?: ReturnType<typeof setTimeout>;
|
|
720
|
+
private logUploadIntervalIndex: number;
|
|
705
721
|
|
|
706
722
|
/**
|
|
707
723
|
* @param {Object} attrs
|
|
@@ -770,6 +786,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
770
786
|
);
|
|
771
787
|
this.callStateForMetrics.correlationId = this.id;
|
|
772
788
|
}
|
|
789
|
+
this.logUploadIntervalIndex = 0;
|
|
790
|
+
|
|
773
791
|
/**
|
|
774
792
|
* @instance
|
|
775
793
|
* @type {String}
|
|
@@ -843,7 +861,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
843
861
|
* @memberof Meeting
|
|
844
862
|
*/
|
|
845
863
|
// @ts-ignore
|
|
846
|
-
this.webinar = new Webinar({}, {parent: this.webex});
|
|
864
|
+
this.webinar = new Webinar({meetingId: this.id}, {parent: this.webex});
|
|
847
865
|
/**
|
|
848
866
|
* helper class for managing receive slots (for multistream media connections)
|
|
849
867
|
*/
|
|
@@ -1334,6 +1352,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1334
1352
|
*/
|
|
1335
1353
|
this.passwordStatus = PASSWORD_STATUS.UNKNOWN;
|
|
1336
1354
|
|
|
1355
|
+
/**
|
|
1356
|
+
* registrationId status. If it's REGISTRATIONID_STATUS.REQUIRED then verifyRegistrationId() needs to be called
|
|
1357
|
+
* with the correct registrationId before calling join()
|
|
1358
|
+
* @instance
|
|
1359
|
+
* @type {REGISTRATION_ID_STATUS}
|
|
1360
|
+
* @public
|
|
1361
|
+
* @memberof Meeting
|
|
1362
|
+
*/
|
|
1363
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.UNKNOWN;
|
|
1364
|
+
|
|
1337
1365
|
/**
|
|
1338
1366
|
* Information about required captcha. If null, then no captcha is required. status. If it's PASSWORD_STATUS.REQUIRED then verifyPassword() needs to be called
|
|
1339
1367
|
* with the correct password before calling join()
|
|
@@ -1601,6 +1629,38 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1601
1629
|
this.callStateForMetrics.correlationId = correlationId;
|
|
1602
1630
|
}
|
|
1603
1631
|
|
|
1632
|
+
/**
|
|
1633
|
+
* Getter - Returns callStateForMetrics.userNameInput
|
|
1634
|
+
* @returns {string}
|
|
1635
|
+
*/
|
|
1636
|
+
get userNameInput() {
|
|
1637
|
+
return this.callStateForMetrics?.userNameInput;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* Setter - sets callStateForMetrics.userNameInput
|
|
1642
|
+
* @param {string} userNameInput
|
|
1643
|
+
*/
|
|
1644
|
+
set userNameInput(userNameInput: string) {
|
|
1645
|
+
this.callStateForMetrics.userNameInput = userNameInput;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Getter - Returns callStateForMetrics.emailInput
|
|
1650
|
+
* @returns {string}
|
|
1651
|
+
*/
|
|
1652
|
+
get emailInput() {
|
|
1653
|
+
return this.callStateForMetrics?.emailInput;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Setter - sets callStateForMetrics.emailInput
|
|
1658
|
+
* @param {string} emailInput
|
|
1659
|
+
*/
|
|
1660
|
+
set emailInput(emailInput: string) {
|
|
1661
|
+
this.callStateForMetrics.emailInput = emailInput;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1604
1664
|
/**
|
|
1605
1665
|
* Getter - Returns callStateForMetrics.sessionCorrelationId
|
|
1606
1666
|
* @returns {string}
|
|
@@ -1646,6 +1706,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1646
1706
|
this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
|
|
1647
1707
|
}
|
|
1648
1708
|
|
|
1709
|
+
if (
|
|
1710
|
+
this.registrationIdStatus === REGISTRATION_ID_STATUS.REQUIRED ||
|
|
1711
|
+
this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED
|
|
1712
|
+
) {
|
|
1713
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.VERIFIED;
|
|
1714
|
+
} else {
|
|
1715
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.NOT_REQUIRED;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1649
1718
|
Trigger.trigger(
|
|
1650
1719
|
this,
|
|
1651
1720
|
{
|
|
@@ -1689,7 +1758,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1689
1758
|
* @private
|
|
1690
1759
|
*/
|
|
1691
1760
|
private prepForFetchMeetingInfo(
|
|
1692
|
-
{
|
|
1761
|
+
{
|
|
1762
|
+
password = null,
|
|
1763
|
+
registrationId = null,
|
|
1764
|
+
captchaCode = null,
|
|
1765
|
+
extraParams = {},
|
|
1766
|
+
}: FetchMeetingInfoParams,
|
|
1693
1767
|
caller: string
|
|
1694
1768
|
): Promise<void> {
|
|
1695
1769
|
// when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
|
|
@@ -1729,6 +1803,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1729
1803
|
captchaCode = null,
|
|
1730
1804
|
extraParams = {},
|
|
1731
1805
|
sendCAevents = false,
|
|
1806
|
+
registrationId = null,
|
|
1732
1807
|
}): Promise<void> {
|
|
1733
1808
|
try {
|
|
1734
1809
|
const captchaInfo = captchaCode
|
|
@@ -1744,7 +1819,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1744
1819
|
this.config.installedOrgID,
|
|
1745
1820
|
this.locusId,
|
|
1746
1821
|
extraParams,
|
|
1747
|
-
{meetingId: this.id, sendCAevents}
|
|
1822
|
+
{meetingId: this.id, sendCAevents},
|
|
1823
|
+
registrationId
|
|
1748
1824
|
);
|
|
1749
1825
|
|
|
1750
1826
|
this.parseMeetingInfo(info?.body, this.destination, info?.errors);
|
|
@@ -1762,15 +1838,35 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1762
1838
|
this.meetingInfo = err.meetingInfo;
|
|
1763
1839
|
}
|
|
1764
1840
|
throw new PermissionError();
|
|
1765
|
-
} else if (err instanceof
|
|
1841
|
+
} else if (err instanceof MeetingInfoV2JoinWebinarError) {
|
|
1766
1842
|
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
|
|
1843
|
+
if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
|
|
1844
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
|
|
1845
|
+
} else if (WEBINAR_ERROR_REGISTRATION_ID.includes(err.wbxAppApiCode)) {
|
|
1846
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATION_ID;
|
|
1847
|
+
}
|
|
1767
1848
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1768
1849
|
|
|
1769
1850
|
if (err.meetingInfo) {
|
|
1770
1851
|
this.meetingInfo = err.meetingInfo;
|
|
1771
1852
|
}
|
|
1853
|
+
this.requiredCaptcha = null;
|
|
1772
1854
|
|
|
1773
|
-
throw new
|
|
1855
|
+
throw new JoinWebinarError();
|
|
1856
|
+
} else if (err instanceof MeetingInfoV2JoinForbiddenError) {
|
|
1857
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.JOIN_FORBIDDEN;
|
|
1858
|
+
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1859
|
+
|
|
1860
|
+
if (err.meetingInfo) {
|
|
1861
|
+
this.meetingInfo = err.meetingInfo;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// Handle the case where user hasn't reached Join Before Host (JBH) time (error code 403003)
|
|
1865
|
+
if (JOIN_BEFORE_HOST === err.wbxAppApiCode) {
|
|
1866
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
throw new JoinForbiddenError(this.meetingInfoFailureReason, err);
|
|
1774
1870
|
} else if (err instanceof MeetingInfoV2PasswordError) {
|
|
1775
1871
|
LoggerProxy.logger.info(
|
|
1776
1872
|
// @ts-ignore
|
|
@@ -1799,9 +1895,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1799
1895
|
`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - captcha required (code=${err?.body?.code}).`
|
|
1800
1896
|
);
|
|
1801
1897
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1898
|
+
if (this.requiredCaptcha) {
|
|
1899
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA;
|
|
1900
|
+
} else if (err.isRegistrationIdRequired) {
|
|
1901
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID;
|
|
1902
|
+
} else {
|
|
1903
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD;
|
|
1904
|
+
}
|
|
1805
1905
|
|
|
1806
1906
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1807
1907
|
|
|
@@ -1809,6 +1909,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1809
1909
|
this.passwordStatus = PASSWORD_STATUS.REQUIRED;
|
|
1810
1910
|
}
|
|
1811
1911
|
|
|
1912
|
+
if (err.isRegistrationIdRequired) {
|
|
1913
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.REQUIRED;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1812
1916
|
this.requiredCaptcha = err.captchaInfo;
|
|
1813
1917
|
throw new CaptchaError();
|
|
1814
1918
|
} else {
|
|
@@ -1949,6 +2053,48 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1949
2053
|
});
|
|
1950
2054
|
}
|
|
1951
2055
|
|
|
2056
|
+
/**
|
|
2057
|
+
* Checks if the supplied registrationId is correct. It returns a promise with information whether the
|
|
2058
|
+
* registrationId and captcha code were correct or not.
|
|
2059
|
+
* @param {String | undefined} registrationId - can be undefined if only captcha was required
|
|
2060
|
+
* @param {String | undefined} captchaCode - can be undefined if captcha was not required by the server
|
|
2061
|
+
* @param {Boolean} sendCAevents - whether Call Analyzer events should be sent when fetching meeting information
|
|
2062
|
+
* @public
|
|
2063
|
+
* @memberof Meeting
|
|
2064
|
+
* @returns {Promise<{isRegistrationIdValid: boolean, requiredCaptcha: boolean, failureReason: MEETING_INFO_FAILURE_REASON}>}
|
|
2065
|
+
*/
|
|
2066
|
+
public verifyRegistrationId(registrationId: string, captchaCode: string, sendCAevents = false) {
|
|
2067
|
+
return this.fetchMeetingInfo({
|
|
2068
|
+
registrationId,
|
|
2069
|
+
captchaCode,
|
|
2070
|
+
sendCAevents,
|
|
2071
|
+
})
|
|
2072
|
+
.then(() => {
|
|
2073
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_SUCCESS);
|
|
2074
|
+
|
|
2075
|
+
return {
|
|
2076
|
+
isRegistrationIdValid: true,
|
|
2077
|
+
requiredCaptcha: null,
|
|
2078
|
+
failureReason: MEETING_INFO_FAILURE_REASON.NONE,
|
|
2079
|
+
};
|
|
2080
|
+
})
|
|
2081
|
+
.catch((error) => {
|
|
2082
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_ERROR);
|
|
2083
|
+
|
|
2084
|
+
if (error instanceof JoinWebinarError || error instanceof CaptchaError) {
|
|
2085
|
+
return {
|
|
2086
|
+
isRegistrationIdValid: this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED,
|
|
2087
|
+
requiredCaptcha: this.requiredCaptcha,
|
|
2088
|
+
failureReason:
|
|
2089
|
+
error instanceof JoinWebinarError
|
|
2090
|
+
? MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID
|
|
2091
|
+
: this.meetingInfoFailureReason,
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
throw error;
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
|
|
1952
2098
|
/**
|
|
1953
2099
|
* Refreshes the captcha. As a result the meeting will have new captcha id, image and audio.
|
|
1954
2100
|
* If the refresh operation fails, meeting remains with the old captcha properties.
|
|
@@ -2655,6 +2801,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2655
2801
|
});
|
|
2656
2802
|
|
|
2657
2803
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED, ({state}) => {
|
|
2804
|
+
this.webinar.updatePracticeSessionStatus(state);
|
|
2658
2805
|
Trigger.trigger(
|
|
2659
2806
|
this,
|
|
2660
2807
|
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
@@ -2728,6 +2875,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2728
2875
|
this.triggerAnnotationInfoEvent(contentShare, previousContentShare);
|
|
2729
2876
|
|
|
2730
2877
|
if (
|
|
2878
|
+
!payload.forceUpdate &&
|
|
2731
2879
|
contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
|
|
2732
2880
|
contentShare.disposition === previousContentShare?.disposition &&
|
|
2733
2881
|
contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
|
|
@@ -2774,7 +2922,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2774
2922
|
// It does not matter who requested to share the whiteboard, everyone gets the same view
|
|
2775
2923
|
else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
|
|
2776
2924
|
// WHITEBOARD - sharing whiteboard
|
|
2777
|
-
|
|
2925
|
+
// Webinar attendee should receive whiteboard as remote share
|
|
2926
|
+
newShareStatus =
|
|
2927
|
+
this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
|
|
2928
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
|
2929
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
2778
2930
|
}
|
|
2779
2931
|
// or if content share is either released or null and whiteboard share is either released or null, no one is sharing
|
|
2780
2932
|
else if (
|
|
@@ -2789,6 +2941,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2789
2941
|
LoggerProxy.logger.info(
|
|
2790
2942
|
`Meeting:index#setUpLocusInfoMediaInactiveListener --> this.shareStatus=${this.shareStatus} newShareStatus=${newShareStatus}`
|
|
2791
2943
|
);
|
|
2944
|
+
|
|
2792
2945
|
if (newShareStatus !== this.shareStatus) {
|
|
2793
2946
|
const oldShareStatus = this.shareStatus;
|
|
2794
2947
|
|
|
@@ -3046,7 +3199,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3046
3199
|
*/
|
|
3047
3200
|
private setUpLocusResourcesListener() {
|
|
3048
3201
|
this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_RESOURCES, (payload) => {
|
|
3049
|
-
|
|
3202
|
+
if (payload) {
|
|
3203
|
+
this.webinar.updateWebcastUrl(payload);
|
|
3204
|
+
Trigger.trigger(
|
|
3205
|
+
this,
|
|
3206
|
+
{
|
|
3207
|
+
file: 'meeting/index',
|
|
3208
|
+
function: 'setUpLocusInfoMeetingInfoListener',
|
|
3209
|
+
},
|
|
3210
|
+
EVENT_TRIGGERS.MEETING_RESOURCE_LINKS_UPDATE,
|
|
3211
|
+
{
|
|
3212
|
+
payload,
|
|
3213
|
+
}
|
|
3214
|
+
);
|
|
3215
|
+
}
|
|
3050
3216
|
});
|
|
3051
3217
|
}
|
|
3052
3218
|
|
|
@@ -3249,6 +3415,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3249
3415
|
options: {meetingId: this.id},
|
|
3250
3416
|
});
|
|
3251
3417
|
}
|
|
3418
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY, {
|
|
3419
|
+
correlation_id: this.correlationId,
|
|
3420
|
+
});
|
|
3252
3421
|
this.updateLLMConnection();
|
|
3253
3422
|
});
|
|
3254
3423
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
|
|
@@ -3272,6 +3441,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3272
3441
|
name: 'client.lobby.exited',
|
|
3273
3442
|
options: {meetingId: this.id},
|
|
3274
3443
|
});
|
|
3444
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY, {
|
|
3445
|
+
correlation_id: this.correlationId,
|
|
3446
|
+
});
|
|
3275
3447
|
}
|
|
3276
3448
|
this.rtcMetrics?.sendNextMetrics();
|
|
3277
3449
|
this.updateLLMConnection();
|
|
@@ -3293,6 +3465,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3293
3465
|
// The second on is if the audio is muted, we need to tell the statsAnalyzer when
|
|
3294
3466
|
// the audio is muted or the user is not willing to send media
|
|
3295
3467
|
this.locusInfo.on(LOCUSINFO.EVENTS.MEDIA_STATUS_CHANGE, (status) => {
|
|
3468
|
+
LoggerProxy.logger.info(
|
|
3469
|
+
'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE received, processing...'
|
|
3470
|
+
);
|
|
3471
|
+
|
|
3296
3472
|
if (this.statsAnalyzer) {
|
|
3297
3473
|
this.statsAnalyzer.updateMediaStatus({
|
|
3298
3474
|
actual: status,
|
|
@@ -3306,6 +3482,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3306
3482
|
receiveShare: this.mediaProperties.mediaDirection?.receiveShare,
|
|
3307
3483
|
},
|
|
3308
3484
|
});
|
|
3485
|
+
} else {
|
|
3486
|
+
LoggerProxy.logger.warn(
|
|
3487
|
+
'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE, statsAnalyzer is not available.'
|
|
3488
|
+
);
|
|
3309
3489
|
}
|
|
3310
3490
|
});
|
|
3311
3491
|
|
|
@@ -3350,6 +3530,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3350
3530
|
}
|
|
3351
3531
|
});
|
|
3352
3532
|
|
|
3533
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
|
|
3534
|
+
this.brbState?.handleServerBrbUpdate(payload?.brb?.enabled);
|
|
3535
|
+
Trigger.trigger(
|
|
3536
|
+
this,
|
|
3537
|
+
{
|
|
3538
|
+
file: 'meeting/index',
|
|
3539
|
+
function: 'setUpLocusInfoSelfListener',
|
|
3540
|
+
},
|
|
3541
|
+
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
|
3542
|
+
{
|
|
3543
|
+
payload,
|
|
3544
|
+
}
|
|
3545
|
+
);
|
|
3546
|
+
});
|
|
3547
|
+
|
|
3353
3548
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
|
|
3354
3549
|
const isModeratorOrCohost =
|
|
3355
3550
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
|
|
@@ -3359,6 +3554,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3359
3554
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR)
|
|
3360
3555
|
);
|
|
3361
3556
|
this.webinar.updateRoleChanged(payload);
|
|
3557
|
+
|
|
3362
3558
|
Trigger.trigger(
|
|
3363
3559
|
this,
|
|
3364
3560
|
{
|
|
@@ -3505,6 +3701,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3505
3701
|
emailAddress: string;
|
|
3506
3702
|
email: string;
|
|
3507
3703
|
phoneNumber: string;
|
|
3704
|
+
roles: Array<string>;
|
|
3508
3705
|
},
|
|
3509
3706
|
alertIfActive = true
|
|
3510
3707
|
) {
|
|
@@ -3552,6 +3749,35 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3552
3749
|
return this.members.admitMembers(memberIds, locusUrls);
|
|
3553
3750
|
}
|
|
3554
3751
|
|
|
3752
|
+
/**
|
|
3753
|
+
* Manages be right back status updates for the current participant.
|
|
3754
|
+
*
|
|
3755
|
+
* @param {boolean} enabled - Indicates whether the user enabled brb or not.
|
|
3756
|
+
* @returns {Promise<void>} resolves when the brb status is updated or does nothing if not in a multistream meeting.
|
|
3757
|
+
* @throws {Error} - Throws an error if the request fails.
|
|
3758
|
+
*/
|
|
3759
|
+
public async beRightBack(enabled: boolean): Promise<void> {
|
|
3760
|
+
if (!this.isMultistream) {
|
|
3761
|
+
const errorMessage = 'Meeting:index#beRightBack --> Not a multistream meeting';
|
|
3762
|
+
const error = new Error(errorMessage);
|
|
3763
|
+
|
|
3764
|
+
LoggerProxy.logger.error(error);
|
|
3765
|
+
|
|
3766
|
+
return Promise.reject(error);
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
3770
|
+
const errorMessage = 'Meeting:index#beRightBack --> WebRTC media connection is not defined';
|
|
3771
|
+
const error = new Error(errorMessage);
|
|
3772
|
+
|
|
3773
|
+
LoggerProxy.logger.error(error);
|
|
3774
|
+
|
|
3775
|
+
return Promise.reject(error);
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
return this.brbState.enable(enabled, this.sendSlotManager);
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3555
3781
|
/**
|
|
3556
3782
|
* Remove the member from the meeting, boot them
|
|
3557
3783
|
* @param {String} memberId
|
|
@@ -3761,6 +3987,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3761
3987
|
this.userDisplayHints,
|
|
3762
3988
|
this.selfUserPolicies
|
|
3763
3989
|
),
|
|
3990
|
+
isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
|
|
3991
|
+
this.userDisplayHints,
|
|
3992
|
+
this.selfUserPolicies
|
|
3993
|
+
),
|
|
3764
3994
|
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
|
|
3765
3995
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
|
|
3766
3996
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
|
|
@@ -3787,6 +4017,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3787
4017
|
this.userDisplayHints
|
|
3788
4018
|
),
|
|
3789
4019
|
canManageBreakout: MeetingUtil.canManageBreakout(this.userDisplayHints),
|
|
4020
|
+
canStartBreakout: MeetingUtil.canStartBreakout(this.userDisplayHints),
|
|
3790
4021
|
canBroadcastMessageToBreakout: MeetingUtil.canBroadcastMessageToBreakout(
|
|
3791
4022
|
this.userDisplayHints,
|
|
3792
4023
|
this.selfUserPolicies
|
|
@@ -3904,6 +4135,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3904
4135
|
requiredHints: [DISPLAY_HINTS.DISABLE_STAGE_VIEW],
|
|
3905
4136
|
displayHints: this.userDisplayHints,
|
|
3906
4137
|
}),
|
|
4138
|
+
isPracticeSessionOn: ControlsOptionsUtil.hasHints({
|
|
4139
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_ON],
|
|
4140
|
+
displayHints: this.userDisplayHints,
|
|
4141
|
+
}),
|
|
4142
|
+
isPracticeSessionOff: ControlsOptionsUtil.hasHints({
|
|
4143
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_OFF],
|
|
4144
|
+
displayHints: this.userDisplayHints,
|
|
4145
|
+
}),
|
|
4146
|
+
canStartPracticeSession: ControlsOptionsUtil.hasHints({
|
|
4147
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_START],
|
|
4148
|
+
displayHints: this.userDisplayHints,
|
|
4149
|
+
}),
|
|
4150
|
+
canStopPracticeSession: ControlsOptionsUtil.hasHints({
|
|
4151
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_STOP],
|
|
4152
|
+
displayHints: this.userDisplayHints,
|
|
4153
|
+
}),
|
|
3907
4154
|
canShareFile:
|
|
3908
4155
|
(ControlsOptionsUtil.hasHints({
|
|
3909
4156
|
requiredHints: [DISPLAY_HINTS.SHARE_FILE],
|
|
@@ -4060,6 +4307,66 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4060
4307
|
Trigger.trigger(this, options, EVENTS.REQUEST_UPLOAD_LOGS, this);
|
|
4061
4308
|
}
|
|
4062
4309
|
|
|
4310
|
+
/**
|
|
4311
|
+
* sets the timer for periodic log upload
|
|
4312
|
+
* @returns {void}
|
|
4313
|
+
*/
|
|
4314
|
+
private setLogUploadTimer() {
|
|
4315
|
+
// start with short timeouts and increase them later on so in case users have very long multi-hour meetings we don't get too fragmented logs
|
|
4316
|
+
const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
|
|
4317
|
+
|
|
4318
|
+
const delay =
|
|
4319
|
+
1000 *
|
|
4320
|
+
60 *
|
|
4321
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4322
|
+
this.config.logUploadIntervalMultiplicationFactor *
|
|
4323
|
+
LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
|
|
4324
|
+
|
|
4325
|
+
if (this.logUploadIntervalIndex < LOG_UPLOAD_INTERVALS.length - 1) {
|
|
4326
|
+
this.logUploadIntervalIndex += 1;
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
this.uploadLogsTimer = safeSetTimeout(() => {
|
|
4330
|
+
this.uploadLogsTimer = undefined;
|
|
4331
|
+
|
|
4332
|
+
this.uploadLogs();
|
|
4333
|
+
|
|
4334
|
+
// just as an extra precaution, to avoid uploading logs forever in case something goes wrong
|
|
4335
|
+
// and the page remains opened, we stop it if there is no media connection
|
|
4336
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
4337
|
+
return;
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
this.setLogUploadTimer();
|
|
4341
|
+
}, delay);
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4344
|
+
/**
|
|
4345
|
+
* Starts a periodic upload of logs
|
|
4346
|
+
*
|
|
4347
|
+
* @returns {undefined}
|
|
4348
|
+
*/
|
|
4349
|
+
public startPeriodicLogUpload() {
|
|
4350
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4351
|
+
if (this.config.logUploadIntervalMultiplicationFactor && !this.uploadLogsTimer) {
|
|
4352
|
+
this.logUploadIntervalIndex = 0;
|
|
4353
|
+
|
|
4354
|
+
this.setLogUploadTimer();
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
/**
|
|
4359
|
+
* Stops the periodic upload of logs
|
|
4360
|
+
*
|
|
4361
|
+
* @returns {undefined}
|
|
4362
|
+
*/
|
|
4363
|
+
public stopPeriodicLogUpload() {
|
|
4364
|
+
if (this.uploadLogsTimer) {
|
|
4365
|
+
clearTimeout(this.uploadLogsTimer);
|
|
4366
|
+
this.uploadLogsTimer = undefined;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4063
4370
|
/**
|
|
4064
4371
|
* Removes remote audio, video and share streams from class instance's mediaProperties
|
|
4065
4372
|
* @returns {undefined}
|
|
@@ -4449,11 +4756,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4449
4756
|
* Close the peer connections and remove them from the class.
|
|
4450
4757
|
* Cleanup any media connection related things.
|
|
4451
4758
|
*
|
|
4759
|
+
* @param {boolean} resetMuteStates whether to also reset the audio/video mute state information
|
|
4452
4760
|
* @returns {Promise}
|
|
4453
4761
|
* @public
|
|
4454
4762
|
* @memberof Meeting
|
|
4455
4763
|
*/
|
|
4456
|
-
public closePeerConnections() {
|
|
4764
|
+
public closePeerConnections(resetMuteStates = true) {
|
|
4457
4765
|
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4458
4766
|
if (this.remoteMediaManager) {
|
|
4459
4767
|
this.remoteMediaManager.stop();
|
|
@@ -4466,12 +4774,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4466
4774
|
|
|
4467
4775
|
this.receiveSlotManager.reset();
|
|
4468
4776
|
this.mediaProperties.webrtcMediaConnection.close();
|
|
4777
|
+
this.mediaProperties.unsetPeerConnection();
|
|
4469
4778
|
this.sendSlotManager.reset();
|
|
4470
4779
|
this.setNetworkStatus(undefined);
|
|
4471
4780
|
}
|
|
4472
4781
|
|
|
4473
|
-
|
|
4474
|
-
|
|
4782
|
+
if (resetMuteStates) {
|
|
4783
|
+
this.audio = null;
|
|
4784
|
+
this.video = null;
|
|
4785
|
+
}
|
|
4475
4786
|
|
|
4476
4787
|
return Promise.resolve();
|
|
4477
4788
|
}
|
|
@@ -4731,7 +5042,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4731
5042
|
* @param {Object} options - options to join with media
|
|
4732
5043
|
* @param {JoinOptions} [options.joinOptions] - see #join()
|
|
4733
5044
|
* @param {AddMediaOptions} [options.mediaOptions] - see #addMedia()
|
|
4734
|
-
* @returns {Promise} -- {join: see join(), media: see addMedia()}
|
|
5045
|
+
* @returns {Promise} -- {join: see join(), media: see addMedia(), multistreamEnabled: flag to indicate if we managed to join in multistream mode}
|
|
4735
5046
|
* @public
|
|
4736
5047
|
* @memberof Meeting
|
|
4737
5048
|
* @example
|
|
@@ -4771,8 +5082,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4771
5082
|
if (!joinResponse) {
|
|
4772
5083
|
// This is the 1st attempt or a retry after join request failed -> we need to do a join with TURN discovery
|
|
4773
5084
|
|
|
4774
|
-
// @ts-ignore
|
|
4775
|
-
joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
|
|
4776
5085
|
const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(
|
|
4777
5086
|
this,
|
|
4778
5087
|
true
|
|
@@ -4823,6 +5132,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4823
5132
|
return {
|
|
4824
5133
|
join: joinResponse,
|
|
4825
5134
|
media: mediaResponse,
|
|
5135
|
+
multistreamEnabled: this.isMultistream,
|
|
4826
5136
|
};
|
|
4827
5137
|
} catch (error) {
|
|
4828
5138
|
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
@@ -4831,7 +5141,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4831
5141
|
|
|
4832
5142
|
this.roap.abortTurnDiscovery();
|
|
4833
5143
|
|
|
4834
|
-
if
|
|
5144
|
+
// if this was the first attempt, let's do a retry
|
|
5145
|
+
let shouldRetry = !isRetry;
|
|
5146
|
+
|
|
5147
|
+
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
5148
|
+
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
5149
|
+
// so there is no point doing a retry
|
|
5150
|
+
shouldRetry = false;
|
|
5151
|
+
}
|
|
5152
|
+
|
|
5153
|
+
// we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
|
|
5154
|
+
if (joined && (isRetry || !shouldRetry)) {
|
|
4835
5155
|
try {
|
|
4836
5156
|
await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
|
|
4837
5157
|
} catch (e) {
|
|
@@ -4855,15 +5175,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4855
5175
|
}
|
|
4856
5176
|
);
|
|
4857
5177
|
|
|
4858
|
-
// if this was the first attempt, let's do a retry
|
|
4859
|
-
let shouldRetry = !isRetry;
|
|
4860
|
-
|
|
4861
|
-
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
4862
|
-
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
4863
|
-
// so there is no point doing a retry
|
|
4864
|
-
shouldRetry = false;
|
|
4865
|
-
}
|
|
4866
|
-
|
|
4867
5178
|
if (shouldRetry) {
|
|
4868
5179
|
LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
|
|
4869
5180
|
this.joinWithMediaRetryInfo.isRetry = true;
|
|
@@ -5119,7 +5430,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5119
5430
|
(this.config.receiveReactions || options.receiveReactions) &&
|
|
5120
5431
|
this.isReactionsSupported()
|
|
5121
5432
|
) {
|
|
5122
|
-
const
|
|
5433
|
+
const member = this.members.membersCollection.get(e.data.sender.participantId);
|
|
5434
|
+
if (!member) {
|
|
5435
|
+
// @ts-ignore -- fix type
|
|
5436
|
+
LoggerProxy.logger.warn(
|
|
5437
|
+
`Meeting:index#processRelayEvent --> Skipping handling of ${REACTION_RELAY_TYPES.REACTION} for ${this.id}. participantId ${e.data.sender.participantId} does not exist in membersCollection.`
|
|
5438
|
+
);
|
|
5439
|
+
break;
|
|
5440
|
+
}
|
|
5441
|
+
|
|
5442
|
+
const {name} = member;
|
|
5123
5443
|
const processedReaction: ProcessedReaction = {
|
|
5124
5444
|
reaction: e.data.reaction,
|
|
5125
5445
|
sender: {
|
|
@@ -5173,6 +5493,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5173
5493
|
this.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]
|
|
5174
5494
|
);
|
|
5175
5495
|
|
|
5496
|
+
// @ts-ignore
|
|
5497
|
+
this.webex.internal.voicea.deregisterEvents();
|
|
5498
|
+
|
|
5176
5499
|
this.areVoiceaEventsSetup = false;
|
|
5177
5500
|
this.triggerStopReceivingTranscriptionEvent();
|
|
5178
5501
|
}
|
|
@@ -5283,16 +5606,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5283
5606
|
this.meetingFiniteStateMachine.reset();
|
|
5284
5607
|
}
|
|
5285
5608
|
|
|
5286
|
-
//
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5609
|
+
// send client.call.initiated unless told not to
|
|
5610
|
+
if (options.sendCallInitiated !== false) {
|
|
5611
|
+
// @ts-ignore
|
|
5612
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5613
|
+
name: 'client.call.initiated',
|
|
5614
|
+
payload: {
|
|
5615
|
+
trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
|
|
5616
|
+
isRoapCallEnabled: true,
|
|
5617
|
+
pstnAudioType: options?.pstnAudioType,
|
|
5618
|
+
},
|
|
5619
|
+
options: {meetingId: this.id},
|
|
5620
|
+
});
|
|
5621
|
+
}
|
|
5296
5622
|
|
|
5297
5623
|
LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
|
|
5298
5624
|
|
|
@@ -5480,23 +5806,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5480
5806
|
*/
|
|
5481
5807
|
async updateLLMConnection() {
|
|
5482
5808
|
// @ts-ignore - Fix type
|
|
5483
|
-
const {url, info: {datachannelUrl} = {}} = this.locusInfo;
|
|
5809
|
+
const {url, info: {datachannelUrl, practiceSessionDatachannelUrl} = {}} = this.locusInfo;
|
|
5484
5810
|
|
|
5485
5811
|
const isJoined = this.isJoined();
|
|
5486
5812
|
|
|
5813
|
+
// webinar panelist should use new data channel in practice session
|
|
5814
|
+
const dataChannelUrl =
|
|
5815
|
+
this.webinar.isJoinPracticeSessionDataChannel() && practiceSessionDatachannelUrl
|
|
5816
|
+
? practiceSessionDatachannelUrl
|
|
5817
|
+
: datachannelUrl;
|
|
5818
|
+
|
|
5487
5819
|
// @ts-ignore - Fix type
|
|
5488
5820
|
if (this.webex.internal.llm.isConnected()) {
|
|
5489
5821
|
if (
|
|
5490
5822
|
// @ts-ignore - Fix type
|
|
5491
5823
|
url === this.webex.internal.llm.getLocusUrl() &&
|
|
5492
5824
|
// @ts-ignore - Fix type
|
|
5493
|
-
|
|
5825
|
+
dataChannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
|
|
5494
5826
|
isJoined
|
|
5495
5827
|
) {
|
|
5496
5828
|
return undefined;
|
|
5497
5829
|
}
|
|
5498
5830
|
// @ts-ignore - Fix type
|
|
5499
|
-
await this.webex.internal.llm.disconnectLLM(
|
|
5831
|
+
await this.webex.internal.llm.disconnectLLM(
|
|
5832
|
+
isJoined
|
|
5833
|
+
? {
|
|
5834
|
+
code: 3050,
|
|
5835
|
+
reason: 'done (permanent)',
|
|
5836
|
+
}
|
|
5837
|
+
: undefined
|
|
5838
|
+
);
|
|
5500
5839
|
// @ts-ignore - Fix type
|
|
5501
5840
|
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
5502
5841
|
}
|
|
@@ -5507,7 +5846,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5507
5846
|
|
|
5508
5847
|
// @ts-ignore - Fix type
|
|
5509
5848
|
return this.webex.internal.llm
|
|
5510
|
-
.registerAndConnect(url,
|
|
5849
|
+
.registerAndConnect(url, dataChannelUrl)
|
|
5511
5850
|
.then((registerAndConnectResult) => {
|
|
5512
5851
|
// @ts-ignore - Fix type
|
|
5513
5852
|
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
@@ -5877,8 +6216,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5877
6216
|
* @returns {undefined}
|
|
5878
6217
|
*/
|
|
5879
6218
|
public roapMessageReceived = (roapMessage: RoapMessage) => {
|
|
5880
|
-
const mediaServer =
|
|
5881
|
-
|
|
6219
|
+
const mediaServer =
|
|
6220
|
+
roapMessage.messageType === 'ANSWER'
|
|
6221
|
+
? MeetingsUtil.getMediaServer(roapMessage.sdp)
|
|
6222
|
+
: undefined;
|
|
6223
|
+
|
|
6224
|
+
if (this.isMultistream && mediaServer && mediaServer !== 'homer') {
|
|
6225
|
+
throw new MultistreamNotSupportedError(
|
|
6226
|
+
`Client asked for multistream backend (Homer), but got ${mediaServer} instead`
|
|
6227
|
+
);
|
|
6228
|
+
}
|
|
5882
6229
|
this.mediaProperties.webrtcMediaConnection.roapMessageReceived(roapMessage);
|
|
5883
6230
|
|
|
5884
6231
|
if (mediaServer) {
|
|
@@ -6001,16 +6348,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6001
6348
|
logText: `${LOG_HEADER} Roap Offer`,
|
|
6002
6349
|
}
|
|
6003
6350
|
).catch((error) => {
|
|
6351
|
+
const multistreamNotSupported = error instanceof MultistreamNotSupportedError;
|
|
6352
|
+
|
|
6004
6353
|
// @ts-ignore
|
|
6005
6354
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6006
6355
|
name: 'client.media-engine.remote-sdp-received',
|
|
6007
6356
|
payload: {
|
|
6008
|
-
canProceed:
|
|
6357
|
+
canProceed: multistreamNotSupported,
|
|
6009
6358
|
errors: [
|
|
6010
6359
|
// @ts-ignore
|
|
6011
6360
|
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
6012
6361
|
{
|
|
6013
|
-
clientErrorCode:
|
|
6362
|
+
clientErrorCode: multistreamNotSupported
|
|
6363
|
+
? CALL_DIAGNOSTIC_CONFIG.MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE
|
|
6364
|
+
: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
6014
6365
|
}
|
|
6015
6366
|
),
|
|
6016
6367
|
],
|
|
@@ -6018,7 +6369,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6018
6369
|
options: {meetingId: this.id, rawError: error},
|
|
6019
6370
|
});
|
|
6020
6371
|
|
|
6021
|
-
this.deferSDPAnswer.reject(
|
|
6372
|
+
this.deferSDPAnswer.reject(error);
|
|
6022
6373
|
clearTimeout(this.sdpResponseTimer);
|
|
6023
6374
|
this.sdpResponseTimer = undefined;
|
|
6024
6375
|
});
|
|
@@ -6346,6 +6697,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6346
6697
|
this.webex.meetings.geoHintInfo?.clientAddress ||
|
|
6347
6698
|
options.data.intervalMetadata.peerReflexiveIP ||
|
|
6348
6699
|
MQA_STATS.DEFAULT_IP;
|
|
6700
|
+
|
|
6701
|
+
const {members} = this.getMembers().membersCollection;
|
|
6702
|
+
|
|
6703
|
+
// Count members that are in the meeting
|
|
6704
|
+
options.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
|
6705
|
+
(member: Member) => member.isInMeeting
|
|
6706
|
+
).length;
|
|
6707
|
+
|
|
6349
6708
|
// @ts-ignore
|
|
6350
6709
|
this.webex.internal.newMetrics.submitMQE({
|
|
6351
6710
|
name: 'client.mediaquality.event',
|
|
@@ -6477,6 +6836,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6477
6836
|
new RtcMetrics(this.webex, {meetingId: this.id}, this.correlationId)
|
|
6478
6837
|
: undefined;
|
|
6479
6838
|
|
|
6839
|
+
// ongoing reachability checks slow down new media connections especially on Firefox, so we stop them
|
|
6840
|
+
this.getWebexObject().meetings.reachability.stopReachability();
|
|
6841
|
+
|
|
6480
6842
|
const mc = Media.createMediaConnection(
|
|
6481
6843
|
this.isMultistream,
|
|
6482
6844
|
this.getMediaConnectionDebugId(),
|
|
@@ -6677,32 +7039,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6677
7039
|
}
|
|
6678
7040
|
}
|
|
6679
7041
|
|
|
6680
|
-
/**
|
|
6681
|
-
* Handles device logging
|
|
6682
|
-
*
|
|
6683
|
-
* @private
|
|
6684
|
-
* @static
|
|
6685
|
-
* @param {boolean} isAudioEnabled
|
|
6686
|
-
* @param {boolean} isVideoEnabled
|
|
6687
|
-
* @returns {Promise<void>}
|
|
6688
|
-
*/
|
|
6689
|
-
|
|
6690
|
-
private static async handleDeviceLogging(isAudioEnabled, isVideoEnabled): Promise<void> {
|
|
6691
|
-
try {
|
|
6692
|
-
let devices = [];
|
|
6693
|
-
if (isVideoEnabled && isAudioEnabled) {
|
|
6694
|
-
devices = await getDevices();
|
|
6695
|
-
} else if (isVideoEnabled) {
|
|
6696
|
-
devices = await getDevices(Media.DeviceKind.VIDEO_INPUT);
|
|
6697
|
-
} else if (isAudioEnabled) {
|
|
6698
|
-
devices = await getDevices(Media.DeviceKind.AUDIO_INPUT);
|
|
6699
|
-
}
|
|
6700
|
-
MeetingUtil.handleDeviceLogging(devices);
|
|
6701
|
-
} catch {
|
|
6702
|
-
// getDevices may fail if we don't have browser permissions, that's ok, we still can have a media connection
|
|
6703
|
-
}
|
|
6704
|
-
}
|
|
6705
|
-
|
|
6706
7042
|
/**
|
|
6707
7043
|
* Returns a promise. This promise is created once the local sdp offer has been successfully created and is resolved
|
|
6708
7044
|
* once the remote sdp answer has been received.
|
|
@@ -6926,7 +7262,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6926
7262
|
|
|
6927
7263
|
const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
|
|
6928
7264
|
|
|
6929
|
-
LoggerProxy.logger.info(
|
|
7265
|
+
LoggerProxy.logger.info(
|
|
7266
|
+
`${LOG_HEADER} media connection created this.isMultistream=${this.isMultistream}`
|
|
7267
|
+
);
|
|
6930
7268
|
|
|
6931
7269
|
if (this.isMultistream) {
|
|
6932
7270
|
this.remoteMediaManager = new RemoteMediaManager(
|
|
@@ -7004,6 +7342,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7004
7342
|
}
|
|
7005
7343
|
}
|
|
7006
7344
|
|
|
7345
|
+
/**
|
|
7346
|
+
* Cleans up stats analyzer, peer connection and other things before
|
|
7347
|
+
* we can create a new transcoded media connection
|
|
7348
|
+
*
|
|
7349
|
+
* @private
|
|
7350
|
+
* @returns {Promise<void>}
|
|
7351
|
+
*/
|
|
7352
|
+
private async downgradeFromMultistreamToTranscoded(): Promise<void> {
|
|
7353
|
+
if (this.statsAnalyzer) {
|
|
7354
|
+
await this.statsAnalyzer.stopAnalyzer();
|
|
7355
|
+
}
|
|
7356
|
+
this.statsAnalyzer = null;
|
|
7357
|
+
|
|
7358
|
+
this.isMultistream = false;
|
|
7359
|
+
|
|
7360
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
7361
|
+
// close peer connection, but don't reset mute state information, because we will want to use it on the retry
|
|
7362
|
+
this.closePeerConnections(false);
|
|
7363
|
+
|
|
7364
|
+
this.mediaProperties.unsetPeerConnection();
|
|
7365
|
+
}
|
|
7366
|
+
|
|
7367
|
+
this.locusMediaRequest?.downgradeFromMultistreamToTranscoded();
|
|
7368
|
+
|
|
7369
|
+
this.createStatsAnalyzer();
|
|
7370
|
+
}
|
|
7371
|
+
|
|
7007
7372
|
/**
|
|
7008
7373
|
* Sends stats report, closes peer connection and cleans up any media connection
|
|
7009
7374
|
* related things before trying to establish media connection again with turn server
|
|
@@ -7190,6 +7555,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7190
7555
|
|
|
7191
7556
|
this.audio = createMuteState(AUDIO, this, audioEnabled);
|
|
7192
7557
|
this.video = createMuteState(VIDEO, this, videoEnabled);
|
|
7558
|
+
this.brbState = createBrbState(this, false);
|
|
7193
7559
|
|
|
7194
7560
|
try {
|
|
7195
7561
|
await this.setUpLocalStreamReferences(localStreams);
|
|
@@ -7198,19 +7564,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7198
7564
|
|
|
7199
7565
|
this.createStatsAnalyzer();
|
|
7200
7566
|
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7567
|
+
try {
|
|
7568
|
+
await this.establishMediaConnection(
|
|
7569
|
+
remoteMediaManagerConfig,
|
|
7570
|
+
bundlePolicy,
|
|
7571
|
+
forceTurnDiscovery,
|
|
7572
|
+
turnServerInfo
|
|
7573
|
+
);
|
|
7574
|
+
} catch (error) {
|
|
7575
|
+
if (error instanceof MultistreamNotSupportedError) {
|
|
7576
|
+
LoggerProxy.logger.warn(
|
|
7577
|
+
`${LOG_HEADER} we asked for multistream backend (Homer), but got transcoded backend, recreating media connection...`
|
|
7578
|
+
);
|
|
7207
7579
|
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7580
|
+
await this.downgradeFromMultistreamToTranscoded();
|
|
7581
|
+
|
|
7582
|
+
// Establish new media connection with forced TURN discovery
|
|
7583
|
+
// We need to do TURN discovery again, because backend will be creating a new confluence, so it might land on a different node or cluster
|
|
7584
|
+
await this.establishMediaConnection(
|
|
7585
|
+
remoteMediaManagerConfig,
|
|
7586
|
+
bundlePolicy,
|
|
7587
|
+
true,
|
|
7588
|
+
undefined
|
|
7589
|
+
);
|
|
7590
|
+
} else {
|
|
7591
|
+
throw error;
|
|
7592
|
+
}
|
|
7212
7593
|
}
|
|
7213
7594
|
|
|
7595
|
+
LoggerProxy.logger.info(`${LOG_HEADER} media connected, finalizing...`);
|
|
7596
|
+
|
|
7214
7597
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
7215
7598
|
await this.enqueueScreenShareFloorRequest();
|
|
7216
7599
|
}
|
|
@@ -7247,6 +7630,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7247
7630
|
|
|
7248
7631
|
// We can log ReceiveSlot SSRCs only after the SDP exchange, so doing it here:
|
|
7249
7632
|
this.remoteMediaManager?.logAllReceiveSlots();
|
|
7633
|
+
this.startPeriodicLogUpload();
|
|
7250
7634
|
} catch (error) {
|
|
7251
7635
|
LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);
|
|
7252
7636
|
|
|
@@ -8179,7 +8563,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8179
8563
|
if (layoutType) {
|
|
8180
8564
|
if (!LAYOUT_TYPES.includes(layoutType)) {
|
|
8181
8565
|
return this.rejectWithErrorLog(
|
|
8182
|
-
|
|
8566
|
+
`Meeting:index#changeVideoLayout --> cannot change video layout, invalid layoutType "${layoutType}" received.`
|
|
8183
8567
|
);
|
|
8184
8568
|
}
|
|
8185
8569
|
|
|
@@ -8335,6 +8719,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8335
8719
|
correlationId: this.correlationId,
|
|
8336
8720
|
muted,
|
|
8337
8721
|
encoderImplementation: this.statsAnalyzer?.shareVideoEncoderImplementation,
|
|
8722
|
+
// TypeScript 4 does not recognize the `displaySurface` property. Instead of upgrading the
|
|
8723
|
+
// SDK to TypeScript 5, which may affect other packages, use bracket notation for now, since
|
|
8724
|
+
// all we're doing here is adding metrics.
|
|
8725
|
+
// eslint-disable-next-line dot-notation
|
|
8726
|
+
displaySurface: this.mediaProperties?.shareVideoStream?.getSettings()['displaySurface'],
|
|
8727
|
+
isMultistream: this.isMultistream,
|
|
8338
8728
|
});
|
|
8339
8729
|
};
|
|
8340
8730
|
|
|
@@ -8537,6 +8927,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8537
8927
|
this.stopTranscription();
|
|
8538
8928
|
this.transcription = undefined;
|
|
8539
8929
|
}
|
|
8930
|
+
|
|
8931
|
+
this.annotation.deregisterEvents();
|
|
8932
|
+
|
|
8933
|
+
// @ts-ignore - fix types
|
|
8934
|
+
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
8540
8935
|
};
|
|
8541
8936
|
|
|
8542
8937
|
/**
|
|
@@ -8574,10 +8969,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8574
8969
|
|
|
8575
8970
|
return;
|
|
8576
8971
|
}
|
|
8577
|
-
|
|
8972
|
+
|
|
8578
8973
|
const keepAliveInterval = (this.joinedWith.keepAliveSecs - 1) * 750; // taken from UCF
|
|
8579
8974
|
|
|
8580
8975
|
this.keepAliveTimerId = setInterval(() => {
|
|
8976
|
+
const {keepAliveUrl} = this.joinedWith;
|
|
8977
|
+
|
|
8581
8978
|
this.meetingRequest.keepAlive({keepAliveUrl}).catch((error) => {
|
|
8582
8979
|
LoggerProxy.logger.warn(
|
|
8583
8980
|
`Meeting:index#startKeepAlive --> Stopping sending keepAlives to ${keepAliveUrl} after error ${error}`
|