@webex/plugin-meetings 3.7.0 → 3.8.0-next.1
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 +2 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +68 -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 +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 +1335 -1052
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +11 -6
- 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/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 +461 -136
- 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 +1 -0
- package/dist/types/constants.d.ts +53 -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 +64 -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/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 +1 -0
- package/src/constants.ts +61 -3
- package/src/index.ts +5 -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 +451 -88
- package/src/meeting/locusMediaRequest.ts +11 -8
- 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/sendSlotManager.ts +31 -0
- package/src/reachability/clusterReachability.ts +5 -15
- package/src/reachability/index.ts +311 -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 +20 -5
- 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/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 +860 -110
- package/test/unit/spec/meeting/locusMediaRequest.ts +18 -11
- 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 +159 -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/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/reachability/index.ts +383 -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 +504 -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';
|
|
@@ -248,6 +257,7 @@ export enum ScreenShareFloorStatus {
|
|
|
248
257
|
|
|
249
258
|
type FetchMeetingInfoParams = {
|
|
250
259
|
password?: string;
|
|
260
|
+
registrationId?: string;
|
|
251
261
|
captchaCode?: string;
|
|
252
262
|
extraParams?: Record<string, any>;
|
|
253
263
|
sendCAevents?: boolean;
|
|
@@ -642,6 +652,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
642
652
|
turnServerUsed: boolean;
|
|
643
653
|
areVoiceaEventsSetup = false;
|
|
644
654
|
isMoveToInProgress = false;
|
|
655
|
+
registrationIdStatus: string;
|
|
656
|
+
brbState: BrbState;
|
|
645
657
|
|
|
646
658
|
voiceaListenerCallbacks: object = {
|
|
647
659
|
[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]: (payload: Transcription['languageOptions']) => {
|
|
@@ -702,6 +714,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
702
714
|
private iceCandidateErrors: Map<string, number>;
|
|
703
715
|
private iceCandidatesCount: number;
|
|
704
716
|
private rtcMetrics?: RtcMetrics;
|
|
717
|
+
private uploadLogsTimer?: ReturnType<typeof setTimeout>;
|
|
718
|
+
private logUploadIntervalIndex: number;
|
|
705
719
|
|
|
706
720
|
/**
|
|
707
721
|
* @param {Object} attrs
|
|
@@ -770,6 +784,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
770
784
|
);
|
|
771
785
|
this.callStateForMetrics.correlationId = this.id;
|
|
772
786
|
}
|
|
787
|
+
this.logUploadIntervalIndex = 0;
|
|
788
|
+
|
|
773
789
|
/**
|
|
774
790
|
* @instance
|
|
775
791
|
* @type {String}
|
|
@@ -843,7 +859,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
843
859
|
* @memberof Meeting
|
|
844
860
|
*/
|
|
845
861
|
// @ts-ignore
|
|
846
|
-
this.webinar = new Webinar({}, {parent: this.webex});
|
|
862
|
+
this.webinar = new Webinar({meetingId: this.id}, {parent: this.webex});
|
|
847
863
|
/**
|
|
848
864
|
* helper class for managing receive slots (for multistream media connections)
|
|
849
865
|
*/
|
|
@@ -1334,6 +1350,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1334
1350
|
*/
|
|
1335
1351
|
this.passwordStatus = PASSWORD_STATUS.UNKNOWN;
|
|
1336
1352
|
|
|
1353
|
+
/**
|
|
1354
|
+
* registrationId status. If it's REGISTRATIONID_STATUS.REQUIRED then verifyRegistrationId() needs to be called
|
|
1355
|
+
* with the correct registrationId before calling join()
|
|
1356
|
+
* @instance
|
|
1357
|
+
* @type {REGISTRATION_ID_STATUS}
|
|
1358
|
+
* @public
|
|
1359
|
+
* @memberof Meeting
|
|
1360
|
+
*/
|
|
1361
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.UNKNOWN;
|
|
1362
|
+
|
|
1337
1363
|
/**
|
|
1338
1364
|
* 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
1365
|
* with the correct password before calling join()
|
|
@@ -1646,6 +1672,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1646
1672
|
this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
|
|
1647
1673
|
}
|
|
1648
1674
|
|
|
1675
|
+
if (
|
|
1676
|
+
this.registrationIdStatus === REGISTRATION_ID_STATUS.REQUIRED ||
|
|
1677
|
+
this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED
|
|
1678
|
+
) {
|
|
1679
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.VERIFIED;
|
|
1680
|
+
} else {
|
|
1681
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.NOT_REQUIRED;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1649
1684
|
Trigger.trigger(
|
|
1650
1685
|
this,
|
|
1651
1686
|
{
|
|
@@ -1689,7 +1724,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1689
1724
|
* @private
|
|
1690
1725
|
*/
|
|
1691
1726
|
private prepForFetchMeetingInfo(
|
|
1692
|
-
{
|
|
1727
|
+
{
|
|
1728
|
+
password = null,
|
|
1729
|
+
registrationId = null,
|
|
1730
|
+
captchaCode = null,
|
|
1731
|
+
extraParams = {},
|
|
1732
|
+
}: FetchMeetingInfoParams,
|
|
1693
1733
|
caller: string
|
|
1694
1734
|
): Promise<void> {
|
|
1695
1735
|
// 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 +1769,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1729
1769
|
captchaCode = null,
|
|
1730
1770
|
extraParams = {},
|
|
1731
1771
|
sendCAevents = false,
|
|
1772
|
+
registrationId = null,
|
|
1732
1773
|
}): Promise<void> {
|
|
1733
1774
|
try {
|
|
1734
1775
|
const captchaInfo = captchaCode
|
|
@@ -1744,7 +1785,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1744
1785
|
this.config.installedOrgID,
|
|
1745
1786
|
this.locusId,
|
|
1746
1787
|
extraParams,
|
|
1747
|
-
{meetingId: this.id, sendCAevents}
|
|
1788
|
+
{meetingId: this.id, sendCAevents},
|
|
1789
|
+
registrationId
|
|
1748
1790
|
);
|
|
1749
1791
|
|
|
1750
1792
|
this.parseMeetingInfo(info?.body, this.destination, info?.errors);
|
|
@@ -1762,15 +1804,35 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1762
1804
|
this.meetingInfo = err.meetingInfo;
|
|
1763
1805
|
}
|
|
1764
1806
|
throw new PermissionError();
|
|
1765
|
-
} else if (err instanceof
|
|
1807
|
+
} else if (err instanceof MeetingInfoV2JoinWebinarError) {
|
|
1766
1808
|
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
|
|
1809
|
+
if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
|
|
1810
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
|
|
1811
|
+
} else if (WEBINAR_ERROR_REGISTRATION_ID.includes(err.wbxAppApiCode)) {
|
|
1812
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATION_ID;
|
|
1813
|
+
}
|
|
1814
|
+
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1815
|
+
|
|
1816
|
+
if (err.meetingInfo) {
|
|
1817
|
+
this.meetingInfo = err.meetingInfo;
|
|
1818
|
+
}
|
|
1819
|
+
this.requiredCaptcha = null;
|
|
1820
|
+
|
|
1821
|
+
throw new JoinWebinarError();
|
|
1822
|
+
} else if (err instanceof MeetingInfoV2JoinForbiddenError) {
|
|
1823
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.JOIN_FORBIDDEN;
|
|
1767
1824
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1768
1825
|
|
|
1769
1826
|
if (err.meetingInfo) {
|
|
1770
1827
|
this.meetingInfo = err.meetingInfo;
|
|
1771
1828
|
}
|
|
1772
1829
|
|
|
1773
|
-
|
|
1830
|
+
// Handle the case where user hasn't reached Join Before Host (JBH) time (error code 403003)
|
|
1831
|
+
if (JOIN_BEFORE_HOST === err.wbxAppApiCode) {
|
|
1832
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
throw new JoinForbiddenError(this.meetingInfoFailureReason, err);
|
|
1774
1836
|
} else if (err instanceof MeetingInfoV2PasswordError) {
|
|
1775
1837
|
LoggerProxy.logger.info(
|
|
1776
1838
|
// @ts-ignore
|
|
@@ -1799,9 +1861,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1799
1861
|
`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - captcha required (code=${err?.body?.code}).`
|
|
1800
1862
|
);
|
|
1801
1863
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1864
|
+
if (this.requiredCaptcha) {
|
|
1865
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA;
|
|
1866
|
+
} else if (err.isRegistrationIdRequired) {
|
|
1867
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID;
|
|
1868
|
+
} else {
|
|
1869
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD;
|
|
1870
|
+
}
|
|
1805
1871
|
|
|
1806
1872
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1807
1873
|
|
|
@@ -1809,6 +1875,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1809
1875
|
this.passwordStatus = PASSWORD_STATUS.REQUIRED;
|
|
1810
1876
|
}
|
|
1811
1877
|
|
|
1878
|
+
if (err.isRegistrationIdRequired) {
|
|
1879
|
+
this.registrationIdStatus = REGISTRATION_ID_STATUS.REQUIRED;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1812
1882
|
this.requiredCaptcha = err.captchaInfo;
|
|
1813
1883
|
throw new CaptchaError();
|
|
1814
1884
|
} else {
|
|
@@ -1949,6 +2019,48 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1949
2019
|
});
|
|
1950
2020
|
}
|
|
1951
2021
|
|
|
2022
|
+
/**
|
|
2023
|
+
* Checks if the supplied registrationId is correct. It returns a promise with information whether the
|
|
2024
|
+
* registrationId and captcha code were correct or not.
|
|
2025
|
+
* @param {String | undefined} registrationId - can be undefined if only captcha was required
|
|
2026
|
+
* @param {String | undefined} captchaCode - can be undefined if captcha was not required by the server
|
|
2027
|
+
* @param {Boolean} sendCAevents - whether Call Analyzer events should be sent when fetching meeting information
|
|
2028
|
+
* @public
|
|
2029
|
+
* @memberof Meeting
|
|
2030
|
+
* @returns {Promise<{isRegistrationIdValid: boolean, requiredCaptcha: boolean, failureReason: MEETING_INFO_FAILURE_REASON}>}
|
|
2031
|
+
*/
|
|
2032
|
+
public verifyRegistrationId(registrationId: string, captchaCode: string, sendCAevents = false) {
|
|
2033
|
+
return this.fetchMeetingInfo({
|
|
2034
|
+
registrationId,
|
|
2035
|
+
captchaCode,
|
|
2036
|
+
sendCAevents,
|
|
2037
|
+
})
|
|
2038
|
+
.then(() => {
|
|
2039
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_SUCCESS);
|
|
2040
|
+
|
|
2041
|
+
return {
|
|
2042
|
+
isRegistrationIdValid: true,
|
|
2043
|
+
requiredCaptcha: null,
|
|
2044
|
+
failureReason: MEETING_INFO_FAILURE_REASON.NONE,
|
|
2045
|
+
};
|
|
2046
|
+
})
|
|
2047
|
+
.catch((error) => {
|
|
2048
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_ERROR);
|
|
2049
|
+
|
|
2050
|
+
if (error instanceof JoinWebinarError || error instanceof CaptchaError) {
|
|
2051
|
+
return {
|
|
2052
|
+
isRegistrationIdValid: this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED,
|
|
2053
|
+
requiredCaptcha: this.requiredCaptcha,
|
|
2054
|
+
failureReason:
|
|
2055
|
+
error instanceof JoinWebinarError
|
|
2056
|
+
? MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID
|
|
2057
|
+
: this.meetingInfoFailureReason,
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
throw error;
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
|
|
1952
2064
|
/**
|
|
1953
2065
|
* Refreshes the captcha. As a result the meeting will have new captcha id, image and audio.
|
|
1954
2066
|
* If the refresh operation fails, meeting remains with the old captcha properties.
|
|
@@ -2655,6 +2767,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2655
2767
|
});
|
|
2656
2768
|
|
|
2657
2769
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED, ({state}) => {
|
|
2770
|
+
this.webinar.updatePracticeSessionStatus(state);
|
|
2658
2771
|
Trigger.trigger(
|
|
2659
2772
|
this,
|
|
2660
2773
|
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
@@ -2728,6 +2841,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2728
2841
|
this.triggerAnnotationInfoEvent(contentShare, previousContentShare);
|
|
2729
2842
|
|
|
2730
2843
|
if (
|
|
2844
|
+
!payload.forceUpdate &&
|
|
2731
2845
|
contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
|
|
2732
2846
|
contentShare.disposition === previousContentShare?.disposition &&
|
|
2733
2847
|
contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
|
|
@@ -2774,7 +2888,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2774
2888
|
// It does not matter who requested to share the whiteboard, everyone gets the same view
|
|
2775
2889
|
else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
|
|
2776
2890
|
// WHITEBOARD - sharing whiteboard
|
|
2777
|
-
|
|
2891
|
+
// Webinar attendee should receive whiteboard as remote share
|
|
2892
|
+
newShareStatus =
|
|
2893
|
+
this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
|
|
2894
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
|
2895
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
2778
2896
|
}
|
|
2779
2897
|
// or if content share is either released or null and whiteboard share is either released or null, no one is sharing
|
|
2780
2898
|
else if (
|
|
@@ -2789,6 +2907,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2789
2907
|
LoggerProxy.logger.info(
|
|
2790
2908
|
`Meeting:index#setUpLocusInfoMediaInactiveListener --> this.shareStatus=${this.shareStatus} newShareStatus=${newShareStatus}`
|
|
2791
2909
|
);
|
|
2910
|
+
|
|
2792
2911
|
if (newShareStatus !== this.shareStatus) {
|
|
2793
2912
|
const oldShareStatus = this.shareStatus;
|
|
2794
2913
|
|
|
@@ -3046,7 +3165,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3046
3165
|
*/
|
|
3047
3166
|
private setUpLocusResourcesListener() {
|
|
3048
3167
|
this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_RESOURCES, (payload) => {
|
|
3049
|
-
|
|
3168
|
+
if (payload) {
|
|
3169
|
+
this.webinar.updateWebcastUrl(payload);
|
|
3170
|
+
Trigger.trigger(
|
|
3171
|
+
this,
|
|
3172
|
+
{
|
|
3173
|
+
file: 'meeting/index',
|
|
3174
|
+
function: 'setUpLocusInfoMeetingInfoListener',
|
|
3175
|
+
},
|
|
3176
|
+
EVENT_TRIGGERS.MEETING_RESOURCE_LINKS_UPDATE,
|
|
3177
|
+
{
|
|
3178
|
+
payload,
|
|
3179
|
+
}
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3050
3182
|
});
|
|
3051
3183
|
}
|
|
3052
3184
|
|
|
@@ -3249,6 +3381,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3249
3381
|
options: {meetingId: this.id},
|
|
3250
3382
|
});
|
|
3251
3383
|
}
|
|
3384
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY, {
|
|
3385
|
+
correlation_id: this.correlationId,
|
|
3386
|
+
});
|
|
3252
3387
|
this.updateLLMConnection();
|
|
3253
3388
|
});
|
|
3254
3389
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
|
|
@@ -3272,6 +3407,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3272
3407
|
name: 'client.lobby.exited',
|
|
3273
3408
|
options: {meetingId: this.id},
|
|
3274
3409
|
});
|
|
3410
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY, {
|
|
3411
|
+
correlation_id: this.correlationId,
|
|
3412
|
+
});
|
|
3275
3413
|
}
|
|
3276
3414
|
this.rtcMetrics?.sendNextMetrics();
|
|
3277
3415
|
this.updateLLMConnection();
|
|
@@ -3293,6 +3431,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3293
3431
|
// The second on is if the audio is muted, we need to tell the statsAnalyzer when
|
|
3294
3432
|
// the audio is muted or the user is not willing to send media
|
|
3295
3433
|
this.locusInfo.on(LOCUSINFO.EVENTS.MEDIA_STATUS_CHANGE, (status) => {
|
|
3434
|
+
LoggerProxy.logger.info(
|
|
3435
|
+
'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE received, processing...'
|
|
3436
|
+
);
|
|
3437
|
+
|
|
3296
3438
|
if (this.statsAnalyzer) {
|
|
3297
3439
|
this.statsAnalyzer.updateMediaStatus({
|
|
3298
3440
|
actual: status,
|
|
@@ -3306,6 +3448,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3306
3448
|
receiveShare: this.mediaProperties.mediaDirection?.receiveShare,
|
|
3307
3449
|
},
|
|
3308
3450
|
});
|
|
3451
|
+
} else {
|
|
3452
|
+
LoggerProxy.logger.warn(
|
|
3453
|
+
'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE, statsAnalyzer is not available.'
|
|
3454
|
+
);
|
|
3309
3455
|
}
|
|
3310
3456
|
});
|
|
3311
3457
|
|
|
@@ -3350,6 +3496,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3350
3496
|
}
|
|
3351
3497
|
});
|
|
3352
3498
|
|
|
3499
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
|
|
3500
|
+
this.brbState?.handleServerBrbUpdate(payload?.brb?.enabled);
|
|
3501
|
+
Trigger.trigger(
|
|
3502
|
+
this,
|
|
3503
|
+
{
|
|
3504
|
+
file: 'meeting/index',
|
|
3505
|
+
function: 'setUpLocusInfoSelfListener',
|
|
3506
|
+
},
|
|
3507
|
+
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
|
3508
|
+
{
|
|
3509
|
+
payload,
|
|
3510
|
+
}
|
|
3511
|
+
);
|
|
3512
|
+
});
|
|
3513
|
+
|
|
3353
3514
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
|
|
3354
3515
|
const isModeratorOrCohost =
|
|
3355
3516
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
|
|
@@ -3359,6 +3520,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3359
3520
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR)
|
|
3360
3521
|
);
|
|
3361
3522
|
this.webinar.updateRoleChanged(payload);
|
|
3523
|
+
|
|
3362
3524
|
Trigger.trigger(
|
|
3363
3525
|
this,
|
|
3364
3526
|
{
|
|
@@ -3505,6 +3667,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3505
3667
|
emailAddress: string;
|
|
3506
3668
|
email: string;
|
|
3507
3669
|
phoneNumber: string;
|
|
3670
|
+
roles: Array<string>;
|
|
3508
3671
|
},
|
|
3509
3672
|
alertIfActive = true
|
|
3510
3673
|
) {
|
|
@@ -3552,6 +3715,35 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3552
3715
|
return this.members.admitMembers(memberIds, locusUrls);
|
|
3553
3716
|
}
|
|
3554
3717
|
|
|
3718
|
+
/**
|
|
3719
|
+
* Manages be right back status updates for the current participant.
|
|
3720
|
+
*
|
|
3721
|
+
* @param {boolean} enabled - Indicates whether the user enabled brb or not.
|
|
3722
|
+
* @returns {Promise<void>} resolves when the brb status is updated or does nothing if not in a multistream meeting.
|
|
3723
|
+
* @throws {Error} - Throws an error if the request fails.
|
|
3724
|
+
*/
|
|
3725
|
+
public async beRightBack(enabled: boolean): Promise<void> {
|
|
3726
|
+
if (!this.isMultistream) {
|
|
3727
|
+
const errorMessage = 'Meeting:index#beRightBack --> Not a multistream meeting';
|
|
3728
|
+
const error = new Error(errorMessage);
|
|
3729
|
+
|
|
3730
|
+
LoggerProxy.logger.error(error);
|
|
3731
|
+
|
|
3732
|
+
return Promise.reject(error);
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
3736
|
+
const errorMessage = 'Meeting:index#beRightBack --> WebRTC media connection is not defined';
|
|
3737
|
+
const error = new Error(errorMessage);
|
|
3738
|
+
|
|
3739
|
+
LoggerProxy.logger.error(error);
|
|
3740
|
+
|
|
3741
|
+
return Promise.reject(error);
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
return this.brbState.enable(enabled, this.sendSlotManager);
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3555
3747
|
/**
|
|
3556
3748
|
* Remove the member from the meeting, boot them
|
|
3557
3749
|
* @param {String} memberId
|
|
@@ -3761,6 +3953,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3761
3953
|
this.userDisplayHints,
|
|
3762
3954
|
this.selfUserPolicies
|
|
3763
3955
|
),
|
|
3956
|
+
isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
|
|
3957
|
+
this.userDisplayHints,
|
|
3958
|
+
this.selfUserPolicies
|
|
3959
|
+
),
|
|
3764
3960
|
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
|
|
3765
3961
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
|
|
3766
3962
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
|
|
@@ -3787,6 +3983,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3787
3983
|
this.userDisplayHints
|
|
3788
3984
|
),
|
|
3789
3985
|
canManageBreakout: MeetingUtil.canManageBreakout(this.userDisplayHints),
|
|
3986
|
+
canStartBreakout: MeetingUtil.canStartBreakout(this.userDisplayHints),
|
|
3790
3987
|
canBroadcastMessageToBreakout: MeetingUtil.canBroadcastMessageToBreakout(
|
|
3791
3988
|
this.userDisplayHints,
|
|
3792
3989
|
this.selfUserPolicies
|
|
@@ -3904,6 +4101,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3904
4101
|
requiredHints: [DISPLAY_HINTS.DISABLE_STAGE_VIEW],
|
|
3905
4102
|
displayHints: this.userDisplayHints,
|
|
3906
4103
|
}),
|
|
4104
|
+
isPracticeSessionOn: ControlsOptionsUtil.hasHints({
|
|
4105
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_ON],
|
|
4106
|
+
displayHints: this.userDisplayHints,
|
|
4107
|
+
}),
|
|
4108
|
+
isPracticeSessionOff: ControlsOptionsUtil.hasHints({
|
|
4109
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_OFF],
|
|
4110
|
+
displayHints: this.userDisplayHints,
|
|
4111
|
+
}),
|
|
4112
|
+
canStartPracticeSession: ControlsOptionsUtil.hasHints({
|
|
4113
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_START],
|
|
4114
|
+
displayHints: this.userDisplayHints,
|
|
4115
|
+
}),
|
|
4116
|
+
canStopPracticeSession: ControlsOptionsUtil.hasHints({
|
|
4117
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_STOP],
|
|
4118
|
+
displayHints: this.userDisplayHints,
|
|
4119
|
+
}),
|
|
3907
4120
|
canShareFile:
|
|
3908
4121
|
(ControlsOptionsUtil.hasHints({
|
|
3909
4122
|
requiredHints: [DISPLAY_HINTS.SHARE_FILE],
|
|
@@ -4060,6 +4273,66 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4060
4273
|
Trigger.trigger(this, options, EVENTS.REQUEST_UPLOAD_LOGS, this);
|
|
4061
4274
|
}
|
|
4062
4275
|
|
|
4276
|
+
/**
|
|
4277
|
+
* sets the timer for periodic log upload
|
|
4278
|
+
* @returns {void}
|
|
4279
|
+
*/
|
|
4280
|
+
private setLogUploadTimer() {
|
|
4281
|
+
// 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
|
|
4282
|
+
const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
|
|
4283
|
+
|
|
4284
|
+
const delay =
|
|
4285
|
+
1000 *
|
|
4286
|
+
60 *
|
|
4287
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4288
|
+
this.config.logUploadIntervalMultiplicationFactor *
|
|
4289
|
+
LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
|
|
4290
|
+
|
|
4291
|
+
if (this.logUploadIntervalIndex < LOG_UPLOAD_INTERVALS.length - 1) {
|
|
4292
|
+
this.logUploadIntervalIndex += 1;
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
this.uploadLogsTimer = safeSetTimeout(() => {
|
|
4296
|
+
this.uploadLogsTimer = undefined;
|
|
4297
|
+
|
|
4298
|
+
this.uploadLogs();
|
|
4299
|
+
|
|
4300
|
+
// just as an extra precaution, to avoid uploading logs forever in case something goes wrong
|
|
4301
|
+
// and the page remains opened, we stop it if there is no media connection
|
|
4302
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
4303
|
+
return;
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
this.setLogUploadTimer();
|
|
4307
|
+
}, delay);
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
/**
|
|
4311
|
+
* Starts a periodic upload of logs
|
|
4312
|
+
*
|
|
4313
|
+
* @returns {undefined}
|
|
4314
|
+
*/
|
|
4315
|
+
public startPeriodicLogUpload() {
|
|
4316
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4317
|
+
if (this.config.logUploadIntervalMultiplicationFactor && !this.uploadLogsTimer) {
|
|
4318
|
+
this.logUploadIntervalIndex = 0;
|
|
4319
|
+
|
|
4320
|
+
this.setLogUploadTimer();
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
|
|
4324
|
+
/**
|
|
4325
|
+
* Stops the periodic upload of logs
|
|
4326
|
+
*
|
|
4327
|
+
* @returns {undefined}
|
|
4328
|
+
*/
|
|
4329
|
+
public stopPeriodicLogUpload() {
|
|
4330
|
+
if (this.uploadLogsTimer) {
|
|
4331
|
+
clearTimeout(this.uploadLogsTimer);
|
|
4332
|
+
this.uploadLogsTimer = undefined;
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4063
4336
|
/**
|
|
4064
4337
|
* Removes remote audio, video and share streams from class instance's mediaProperties
|
|
4065
4338
|
* @returns {undefined}
|
|
@@ -4449,11 +4722,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4449
4722
|
* Close the peer connections and remove them from the class.
|
|
4450
4723
|
* Cleanup any media connection related things.
|
|
4451
4724
|
*
|
|
4725
|
+
* @param {boolean} resetMuteStates whether to also reset the audio/video mute state information
|
|
4452
4726
|
* @returns {Promise}
|
|
4453
4727
|
* @public
|
|
4454
4728
|
* @memberof Meeting
|
|
4455
4729
|
*/
|
|
4456
|
-
public closePeerConnections() {
|
|
4730
|
+
public closePeerConnections(resetMuteStates = true) {
|
|
4457
4731
|
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4458
4732
|
if (this.remoteMediaManager) {
|
|
4459
4733
|
this.remoteMediaManager.stop();
|
|
@@ -4466,12 +4740,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4466
4740
|
|
|
4467
4741
|
this.receiveSlotManager.reset();
|
|
4468
4742
|
this.mediaProperties.webrtcMediaConnection.close();
|
|
4743
|
+
this.mediaProperties.unsetPeerConnection();
|
|
4469
4744
|
this.sendSlotManager.reset();
|
|
4470
4745
|
this.setNetworkStatus(undefined);
|
|
4471
4746
|
}
|
|
4472
4747
|
|
|
4473
|
-
|
|
4474
|
-
|
|
4748
|
+
if (resetMuteStates) {
|
|
4749
|
+
this.audio = null;
|
|
4750
|
+
this.video = null;
|
|
4751
|
+
}
|
|
4475
4752
|
|
|
4476
4753
|
return Promise.resolve();
|
|
4477
4754
|
}
|
|
@@ -4731,7 +5008,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4731
5008
|
* @param {Object} options - options to join with media
|
|
4732
5009
|
* @param {JoinOptions} [options.joinOptions] - see #join()
|
|
4733
5010
|
* @param {AddMediaOptions} [options.mediaOptions] - see #addMedia()
|
|
4734
|
-
* @returns {Promise} -- {join: see join(), media: see addMedia()}
|
|
5011
|
+
* @returns {Promise} -- {join: see join(), media: see addMedia(), multistreamEnabled: flag to indicate if we managed to join in multistream mode}
|
|
4735
5012
|
* @public
|
|
4736
5013
|
* @memberof Meeting
|
|
4737
5014
|
* @example
|
|
@@ -4771,8 +5048,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4771
5048
|
if (!joinResponse) {
|
|
4772
5049
|
// This is the 1st attempt or a retry after join request failed -> we need to do a join with TURN discovery
|
|
4773
5050
|
|
|
4774
|
-
// @ts-ignore
|
|
4775
|
-
joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
|
|
4776
5051
|
const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(
|
|
4777
5052
|
this,
|
|
4778
5053
|
true
|
|
@@ -4823,6 +5098,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4823
5098
|
return {
|
|
4824
5099
|
join: joinResponse,
|
|
4825
5100
|
media: mediaResponse,
|
|
5101
|
+
multistreamEnabled: this.isMultistream,
|
|
4826
5102
|
};
|
|
4827
5103
|
} catch (error) {
|
|
4828
5104
|
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
@@ -4831,7 +5107,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4831
5107
|
|
|
4832
5108
|
this.roap.abortTurnDiscovery();
|
|
4833
5109
|
|
|
4834
|
-
if
|
|
5110
|
+
// if this was the first attempt, let's do a retry
|
|
5111
|
+
let shouldRetry = !isRetry;
|
|
5112
|
+
|
|
5113
|
+
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
5114
|
+
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
5115
|
+
// so there is no point doing a retry
|
|
5116
|
+
shouldRetry = false;
|
|
5117
|
+
}
|
|
5118
|
+
|
|
5119
|
+
// we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
|
|
5120
|
+
if (joined && (isRetry || !shouldRetry)) {
|
|
4835
5121
|
try {
|
|
4836
5122
|
await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
|
|
4837
5123
|
} catch (e) {
|
|
@@ -4855,15 +5141,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4855
5141
|
}
|
|
4856
5142
|
);
|
|
4857
5143
|
|
|
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
5144
|
if (shouldRetry) {
|
|
4868
5145
|
LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
|
|
4869
5146
|
this.joinWithMediaRetryInfo.isRetry = true;
|
|
@@ -5119,7 +5396,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5119
5396
|
(this.config.receiveReactions || options.receiveReactions) &&
|
|
5120
5397
|
this.isReactionsSupported()
|
|
5121
5398
|
) {
|
|
5122
|
-
const
|
|
5399
|
+
const member = this.members.membersCollection.get(e.data.sender.participantId);
|
|
5400
|
+
if (!member) {
|
|
5401
|
+
// @ts-ignore -- fix type
|
|
5402
|
+
LoggerProxy.logger.warn(
|
|
5403
|
+
`Meeting:index#processRelayEvent --> Skipping handling of ${REACTION_RELAY_TYPES.REACTION} for ${this.id}. participantId ${e.data.sender.participantId} does not exist in membersCollection.`
|
|
5404
|
+
);
|
|
5405
|
+
break;
|
|
5406
|
+
}
|
|
5407
|
+
|
|
5408
|
+
const {name} = member;
|
|
5123
5409
|
const processedReaction: ProcessedReaction = {
|
|
5124
5410
|
reaction: e.data.reaction,
|
|
5125
5411
|
sender: {
|
|
@@ -5173,6 +5459,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5173
5459
|
this.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]
|
|
5174
5460
|
);
|
|
5175
5461
|
|
|
5462
|
+
// @ts-ignore
|
|
5463
|
+
this.webex.internal.voicea.deregisterEvents();
|
|
5464
|
+
|
|
5176
5465
|
this.areVoiceaEventsSetup = false;
|
|
5177
5466
|
this.triggerStopReceivingTranscriptionEvent();
|
|
5178
5467
|
}
|
|
@@ -5283,16 +5572,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5283
5572
|
this.meetingFiniteStateMachine.reset();
|
|
5284
5573
|
}
|
|
5285
5574
|
|
|
5286
|
-
//
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5575
|
+
// send client.call.initiated unless told not to
|
|
5576
|
+
if (options.sendCallInitiated !== false) {
|
|
5577
|
+
// @ts-ignore
|
|
5578
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5579
|
+
name: 'client.call.initiated',
|
|
5580
|
+
payload: {
|
|
5581
|
+
trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
|
|
5582
|
+
isRoapCallEnabled: true,
|
|
5583
|
+
pstnAudioType: options?.pstnAudioType,
|
|
5584
|
+
},
|
|
5585
|
+
options: {meetingId: this.id},
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5296
5588
|
|
|
5297
5589
|
LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
|
|
5298
5590
|
|
|
@@ -5480,23 +5772,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5480
5772
|
*/
|
|
5481
5773
|
async updateLLMConnection() {
|
|
5482
5774
|
// @ts-ignore - Fix type
|
|
5483
|
-
const {url, info: {datachannelUrl} = {}} = this.locusInfo;
|
|
5775
|
+
const {url, info: {datachannelUrl, practiceSessionDatachannelUrl} = {}} = this.locusInfo;
|
|
5484
5776
|
|
|
5485
5777
|
const isJoined = this.isJoined();
|
|
5486
5778
|
|
|
5779
|
+
// webinar panelist should use new data channel in practice session
|
|
5780
|
+
const dataChannelUrl =
|
|
5781
|
+
this.webinar.isJoinPracticeSessionDataChannel() && practiceSessionDatachannelUrl
|
|
5782
|
+
? practiceSessionDatachannelUrl
|
|
5783
|
+
: datachannelUrl;
|
|
5784
|
+
|
|
5487
5785
|
// @ts-ignore - Fix type
|
|
5488
5786
|
if (this.webex.internal.llm.isConnected()) {
|
|
5489
5787
|
if (
|
|
5490
5788
|
// @ts-ignore - Fix type
|
|
5491
5789
|
url === this.webex.internal.llm.getLocusUrl() &&
|
|
5492
5790
|
// @ts-ignore - Fix type
|
|
5493
|
-
|
|
5791
|
+
dataChannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
|
|
5494
5792
|
isJoined
|
|
5495
5793
|
) {
|
|
5496
5794
|
return undefined;
|
|
5497
5795
|
}
|
|
5498
5796
|
// @ts-ignore - Fix type
|
|
5499
|
-
await this.webex.internal.llm.disconnectLLM(
|
|
5797
|
+
await this.webex.internal.llm.disconnectLLM(
|
|
5798
|
+
isJoined
|
|
5799
|
+
? {
|
|
5800
|
+
code: 3050,
|
|
5801
|
+
reason: 'done (permanent)',
|
|
5802
|
+
}
|
|
5803
|
+
: undefined
|
|
5804
|
+
);
|
|
5500
5805
|
// @ts-ignore - Fix type
|
|
5501
5806
|
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
5502
5807
|
}
|
|
@@ -5507,7 +5812,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5507
5812
|
|
|
5508
5813
|
// @ts-ignore - Fix type
|
|
5509
5814
|
return this.webex.internal.llm
|
|
5510
|
-
.registerAndConnect(url,
|
|
5815
|
+
.registerAndConnect(url, dataChannelUrl)
|
|
5511
5816
|
.then((registerAndConnectResult) => {
|
|
5512
5817
|
// @ts-ignore - Fix type
|
|
5513
5818
|
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
@@ -5877,8 +6182,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5877
6182
|
* @returns {undefined}
|
|
5878
6183
|
*/
|
|
5879
6184
|
public roapMessageReceived = (roapMessage: RoapMessage) => {
|
|
5880
|
-
const mediaServer =
|
|
5881
|
-
|
|
6185
|
+
const mediaServer =
|
|
6186
|
+
roapMessage.messageType === 'ANSWER'
|
|
6187
|
+
? MeetingsUtil.getMediaServer(roapMessage.sdp)
|
|
6188
|
+
: undefined;
|
|
6189
|
+
|
|
6190
|
+
if (this.isMultistream && mediaServer && mediaServer !== 'homer') {
|
|
6191
|
+
throw new MultistreamNotSupportedError(
|
|
6192
|
+
`Client asked for multistream backend (Homer), but got ${mediaServer} instead`
|
|
6193
|
+
);
|
|
6194
|
+
}
|
|
5882
6195
|
this.mediaProperties.webrtcMediaConnection.roapMessageReceived(roapMessage);
|
|
5883
6196
|
|
|
5884
6197
|
if (mediaServer) {
|
|
@@ -6001,16 +6314,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6001
6314
|
logText: `${LOG_HEADER} Roap Offer`,
|
|
6002
6315
|
}
|
|
6003
6316
|
).catch((error) => {
|
|
6317
|
+
const multistreamNotSupported = error instanceof MultistreamNotSupportedError;
|
|
6318
|
+
|
|
6004
6319
|
// @ts-ignore
|
|
6005
6320
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6006
6321
|
name: 'client.media-engine.remote-sdp-received',
|
|
6007
6322
|
payload: {
|
|
6008
|
-
canProceed:
|
|
6323
|
+
canProceed: multistreamNotSupported,
|
|
6009
6324
|
errors: [
|
|
6010
6325
|
// @ts-ignore
|
|
6011
6326
|
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
6012
6327
|
{
|
|
6013
|
-
clientErrorCode:
|
|
6328
|
+
clientErrorCode: multistreamNotSupported
|
|
6329
|
+
? CALL_DIAGNOSTIC_CONFIG.MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE
|
|
6330
|
+
: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
6014
6331
|
}
|
|
6015
6332
|
),
|
|
6016
6333
|
],
|
|
@@ -6018,7 +6335,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6018
6335
|
options: {meetingId: this.id, rawError: error},
|
|
6019
6336
|
});
|
|
6020
6337
|
|
|
6021
|
-
this.deferSDPAnswer.reject(
|
|
6338
|
+
this.deferSDPAnswer.reject(error);
|
|
6022
6339
|
clearTimeout(this.sdpResponseTimer);
|
|
6023
6340
|
this.sdpResponseTimer = undefined;
|
|
6024
6341
|
});
|
|
@@ -6346,6 +6663,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6346
6663
|
this.webex.meetings.geoHintInfo?.clientAddress ||
|
|
6347
6664
|
options.data.intervalMetadata.peerReflexiveIP ||
|
|
6348
6665
|
MQA_STATS.DEFAULT_IP;
|
|
6666
|
+
|
|
6667
|
+
const {members} = this.getMembers().membersCollection;
|
|
6668
|
+
|
|
6669
|
+
// Count members that are in the meeting
|
|
6670
|
+
options.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
|
6671
|
+
(member: Member) => member.isInMeeting
|
|
6672
|
+
).length;
|
|
6673
|
+
|
|
6349
6674
|
// @ts-ignore
|
|
6350
6675
|
this.webex.internal.newMetrics.submitMQE({
|
|
6351
6676
|
name: 'client.mediaquality.event',
|
|
@@ -6477,6 +6802,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6477
6802
|
new RtcMetrics(this.webex, {meetingId: this.id}, this.correlationId)
|
|
6478
6803
|
: undefined;
|
|
6479
6804
|
|
|
6805
|
+
// ongoing reachability checks slow down new media connections especially on Firefox, so we stop them
|
|
6806
|
+
this.getWebexObject().meetings.reachability.stopReachability();
|
|
6807
|
+
|
|
6480
6808
|
const mc = Media.createMediaConnection(
|
|
6481
6809
|
this.isMultistream,
|
|
6482
6810
|
this.getMediaConnectionDebugId(),
|
|
@@ -6677,32 +7005,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6677
7005
|
}
|
|
6678
7006
|
}
|
|
6679
7007
|
|
|
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
7008
|
/**
|
|
6707
7009
|
* Returns a promise. This promise is created once the local sdp offer has been successfully created and is resolved
|
|
6708
7010
|
* once the remote sdp answer has been received.
|
|
@@ -6926,7 +7228,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6926
7228
|
|
|
6927
7229
|
const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
|
|
6928
7230
|
|
|
6929
|
-
LoggerProxy.logger.info(
|
|
7231
|
+
LoggerProxy.logger.info(
|
|
7232
|
+
`${LOG_HEADER} media connection created this.isMultistream=${this.isMultistream}`
|
|
7233
|
+
);
|
|
6930
7234
|
|
|
6931
7235
|
if (this.isMultistream) {
|
|
6932
7236
|
this.remoteMediaManager = new RemoteMediaManager(
|
|
@@ -7004,6 +7308,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7004
7308
|
}
|
|
7005
7309
|
}
|
|
7006
7310
|
|
|
7311
|
+
/**
|
|
7312
|
+
* Cleans up stats analyzer, peer connection and other things before
|
|
7313
|
+
* we can create a new transcoded media connection
|
|
7314
|
+
*
|
|
7315
|
+
* @private
|
|
7316
|
+
* @returns {Promise<void>}
|
|
7317
|
+
*/
|
|
7318
|
+
private async downgradeFromMultistreamToTranscoded(): Promise<void> {
|
|
7319
|
+
if (this.statsAnalyzer) {
|
|
7320
|
+
await this.statsAnalyzer.stopAnalyzer();
|
|
7321
|
+
}
|
|
7322
|
+
this.statsAnalyzer = null;
|
|
7323
|
+
|
|
7324
|
+
this.isMultistream = false;
|
|
7325
|
+
|
|
7326
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
7327
|
+
// close peer connection, but don't reset mute state information, because we will want to use it on the retry
|
|
7328
|
+
this.closePeerConnections(false);
|
|
7329
|
+
|
|
7330
|
+
this.mediaProperties.unsetPeerConnection();
|
|
7331
|
+
}
|
|
7332
|
+
|
|
7333
|
+
this.locusMediaRequest?.downgradeFromMultistreamToTranscoded();
|
|
7334
|
+
|
|
7335
|
+
this.createStatsAnalyzer();
|
|
7336
|
+
}
|
|
7337
|
+
|
|
7007
7338
|
/**
|
|
7008
7339
|
* Sends stats report, closes peer connection and cleans up any media connection
|
|
7009
7340
|
* related things before trying to establish media connection again with turn server
|
|
@@ -7190,6 +7521,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7190
7521
|
|
|
7191
7522
|
this.audio = createMuteState(AUDIO, this, audioEnabled);
|
|
7192
7523
|
this.video = createMuteState(VIDEO, this, videoEnabled);
|
|
7524
|
+
this.brbState = createBrbState(this, false);
|
|
7193
7525
|
|
|
7194
7526
|
try {
|
|
7195
7527
|
await this.setUpLocalStreamReferences(localStreams);
|
|
@@ -7198,19 +7530,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7198
7530
|
|
|
7199
7531
|
this.createStatsAnalyzer();
|
|
7200
7532
|
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7533
|
+
try {
|
|
7534
|
+
await this.establishMediaConnection(
|
|
7535
|
+
remoteMediaManagerConfig,
|
|
7536
|
+
bundlePolicy,
|
|
7537
|
+
forceTurnDiscovery,
|
|
7538
|
+
turnServerInfo
|
|
7539
|
+
);
|
|
7540
|
+
} catch (error) {
|
|
7541
|
+
if (error instanceof MultistreamNotSupportedError) {
|
|
7542
|
+
LoggerProxy.logger.warn(
|
|
7543
|
+
`${LOG_HEADER} we asked for multistream backend (Homer), but got transcoded backend, recreating media connection...`
|
|
7544
|
+
);
|
|
7207
7545
|
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7546
|
+
await this.downgradeFromMultistreamToTranscoded();
|
|
7547
|
+
|
|
7548
|
+
// Establish new media connection with forced TURN discovery
|
|
7549
|
+
// 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
|
|
7550
|
+
await this.establishMediaConnection(
|
|
7551
|
+
remoteMediaManagerConfig,
|
|
7552
|
+
bundlePolicy,
|
|
7553
|
+
true,
|
|
7554
|
+
undefined
|
|
7555
|
+
);
|
|
7556
|
+
} else {
|
|
7557
|
+
throw error;
|
|
7558
|
+
}
|
|
7212
7559
|
}
|
|
7213
7560
|
|
|
7561
|
+
LoggerProxy.logger.info(`${LOG_HEADER} media connected, finalizing...`);
|
|
7562
|
+
|
|
7214
7563
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
7215
7564
|
await this.enqueueScreenShareFloorRequest();
|
|
7216
7565
|
}
|
|
@@ -7247,6 +7596,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7247
7596
|
|
|
7248
7597
|
// We can log ReceiveSlot SSRCs only after the SDP exchange, so doing it here:
|
|
7249
7598
|
this.remoteMediaManager?.logAllReceiveSlots();
|
|
7599
|
+
this.startPeriodicLogUpload();
|
|
7250
7600
|
} catch (error) {
|
|
7251
7601
|
LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);
|
|
7252
7602
|
|
|
@@ -8179,7 +8529,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8179
8529
|
if (layoutType) {
|
|
8180
8530
|
if (!LAYOUT_TYPES.includes(layoutType)) {
|
|
8181
8531
|
return this.rejectWithErrorLog(
|
|
8182
|
-
|
|
8532
|
+
`Meeting:index#changeVideoLayout --> cannot change video layout, invalid layoutType "${layoutType}" received.`
|
|
8183
8533
|
);
|
|
8184
8534
|
}
|
|
8185
8535
|
|
|
@@ -8335,6 +8685,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8335
8685
|
correlationId: this.correlationId,
|
|
8336
8686
|
muted,
|
|
8337
8687
|
encoderImplementation: this.statsAnalyzer?.shareVideoEncoderImplementation,
|
|
8688
|
+
// TypeScript 4 does not recognize the `displaySurface` property. Instead of upgrading the
|
|
8689
|
+
// SDK to TypeScript 5, which may affect other packages, use bracket notation for now, since
|
|
8690
|
+
// all we're doing here is adding metrics.
|
|
8691
|
+
// eslint-disable-next-line dot-notation
|
|
8692
|
+
displaySurface: this.mediaProperties?.shareVideoStream?.getSettings()['displaySurface'],
|
|
8693
|
+
isMultistream: this.isMultistream,
|
|
8338
8694
|
});
|
|
8339
8695
|
};
|
|
8340
8696
|
|
|
@@ -8537,6 +8893,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8537
8893
|
this.stopTranscription();
|
|
8538
8894
|
this.transcription = undefined;
|
|
8539
8895
|
}
|
|
8896
|
+
|
|
8897
|
+
this.annotation.deregisterEvents();
|
|
8898
|
+
|
|
8899
|
+
// @ts-ignore - fix types
|
|
8900
|
+
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
8540
8901
|
};
|
|
8541
8902
|
|
|
8542
8903
|
/**
|
|
@@ -8574,10 +8935,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8574
8935
|
|
|
8575
8936
|
return;
|
|
8576
8937
|
}
|
|
8577
|
-
|
|
8938
|
+
|
|
8578
8939
|
const keepAliveInterval = (this.joinedWith.keepAliveSecs - 1) * 750; // taken from UCF
|
|
8579
8940
|
|
|
8580
8941
|
this.keepAliveTimerId = setInterval(() => {
|
|
8942
|
+
const {keepAliveUrl} = this.joinedWith;
|
|
8943
|
+
|
|
8581
8944
|
this.meetingRequest.keepAlive({keepAliveUrl}).catch((error) => {
|
|
8582
8945
|
LoggerProxy.logger.warn(
|
|
8583
8946
|
`Meeting:index#startKeepAlive --> Stopping sending keepAlives to ${keepAliveUrl} after error ${error}`
|