@webex/plugin-meetings 3.7.0-next.9 → 3.7.0-wxcc.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 +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +46 -5
- 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 +30 -17
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +960 -832
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +9 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +16 -16
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +96 -33
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +1 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +103 -54
- 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/metrics/constants.js +3 -2
- 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/roap/index.js +10 -8
- package/dist/roap/index.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/constants.d.ts +38 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +19 -12
- package/dist/types/meeting/locusMediaRequest.d.ts +4 -0
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +1 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +27 -4
- package/dist/types/meetings/index.d.ts +16 -1
- 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/metrics/constants.d.ts +2 -1
- package/dist/types/multistream/sendSlotManager.d.ts +8 -1
- package/dist/webinar/index.js +354 -3
- 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 -1
- package/src/constants.ts +43 -3
- package/src/index.ts +5 -3
- package/src/locus-info/index.ts +20 -3
- package/src/locus-info/selfUtils.ts +19 -6
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +259 -80
- package/src/meeting/locusMediaRequest.ts +7 -0
- package/src/meeting/request.ts +26 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +8 -10
- package/src/meeting-info/meeting-info-v2.ts +74 -11
- package/src/meeting-info/utilv2.ts +3 -1
- package/src/meetings/index.ts +73 -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/metrics/constants.ts +2 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/multistream/sendSlotManager.ts +31 -0
- package/src/roap/index.ts +10 -8
- package/src/webinar/index.ts +197 -3
- 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 +91 -1
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +722 -105
- package/test/unit/spec/meeting/utils.js +22 -19
- package/test/unit/spec/meeting-info/meetinginfov2.js +46 -4
- package/test/unit/spec/meeting-info/utilv2.js +17 -0
- package/test/unit/spec/meetings/index.js +150 -13
- package/test/unit/spec/meetings/utils.js +10 -0
- package/test/unit/spec/member/util.js +52 -11
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/roap/index.ts +47 -0
- package/test/unit/spec/webinar/index.ts +457 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/src/common/errors/webinar-registration-error.ts +0 -27
package/src/meeting/index.ts
CHANGED
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
} from '@webex/internal-media-core';
|
|
32
32
|
|
|
33
33
|
import {
|
|
34
|
-
getDevices,
|
|
35
34
|
LocalStream,
|
|
36
35
|
LocalCameraStream,
|
|
37
36
|
LocalDisplayStream,
|
|
@@ -122,6 +121,9 @@ import {
|
|
|
122
121
|
MEETING_PERMISSION_TOKEN_REFRESH_REASON,
|
|
123
122
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
|
|
124
123
|
NAMED_MEDIA_GROUP_TYPE_AUDIO,
|
|
124
|
+
WEBINAR_ERROR_WEBCAST,
|
|
125
|
+
WEBINAR_ERROR_REGISTRATIONID,
|
|
126
|
+
JOIN_BEFORE_HOST,
|
|
125
127
|
} from '../constants';
|
|
126
128
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
127
129
|
import ParameterError from '../common/errors/parameter';
|
|
@@ -129,7 +131,8 @@ import {
|
|
|
129
131
|
MeetingInfoV2PasswordError,
|
|
130
132
|
MeetingInfoV2CaptchaError,
|
|
131
133
|
MeetingInfoV2PolicyError,
|
|
132
|
-
|
|
134
|
+
MeetingInfoV2JoinWebinarError,
|
|
135
|
+
MeetingInfoV2JoinForbiddenError,
|
|
133
136
|
} from '../meeting-info/meeting-info-v2';
|
|
134
137
|
import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
|
135
138
|
import SendSlotManager from '../multistream/sendSlotManager';
|
|
@@ -158,7 +161,10 @@ import ControlsOptionsManager from '../controls-options-manager';
|
|
|
158
161
|
import PermissionError from '../common/errors/permission';
|
|
159
162
|
import {LocusMediaRequest} from './locusMediaRequest';
|
|
160
163
|
import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
|
|
161
|
-
import
|
|
164
|
+
import JoinWebinarError from '../common/errors/join-webinar-error';
|
|
165
|
+
import Member from '../member';
|
|
166
|
+
import MultistreamNotSupportedError from '../common/errors/multistream-not-supported-error';
|
|
167
|
+
import JoinForbiddenError from '../common/errors/join-forbidden-error';
|
|
162
168
|
|
|
163
169
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
|
164
170
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
|
@@ -848,7 +854,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
848
854
|
* @memberof Meeting
|
|
849
855
|
*/
|
|
850
856
|
// @ts-ignore
|
|
851
|
-
this.webinar = new Webinar({}, {parent: this.webex});
|
|
857
|
+
this.webinar = new Webinar({meetingId: this.id}, {parent: this.webex});
|
|
852
858
|
/**
|
|
853
859
|
* helper class for managing receive slots (for multistream media connections)
|
|
854
860
|
*/
|
|
@@ -1767,15 +1773,34 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1767
1773
|
this.meetingInfo = err.meetingInfo;
|
|
1768
1774
|
}
|
|
1769
1775
|
throw new PermissionError();
|
|
1770
|
-
} else if (err instanceof
|
|
1776
|
+
} else if (err instanceof MeetingInfoV2JoinWebinarError) {
|
|
1771
1777
|
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
|
|
1778
|
+
if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
|
|
1779
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
|
|
1780
|
+
} else if (WEBINAR_ERROR_REGISTRATIONID.includes(err.wbxAppApiCode)) {
|
|
1781
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID;
|
|
1782
|
+
}
|
|
1772
1783
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1773
1784
|
|
|
1774
1785
|
if (err.meetingInfo) {
|
|
1775
1786
|
this.meetingInfo = err.meetingInfo;
|
|
1776
1787
|
}
|
|
1777
1788
|
|
|
1778
|
-
throw new
|
|
1789
|
+
throw new JoinWebinarError();
|
|
1790
|
+
} else if (err instanceof MeetingInfoV2JoinForbiddenError) {
|
|
1791
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.JOIN_FORBIDDEN;
|
|
1792
|
+
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1793
|
+
|
|
1794
|
+
if (err.meetingInfo) {
|
|
1795
|
+
this.meetingInfo = err.meetingInfo;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// Handle the case where user hasn't reached Join Before Host (JBH) time (error code 403003)
|
|
1799
|
+
if (JOIN_BEFORE_HOST === err.wbxAppApiCode) {
|
|
1800
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
throw new JoinForbiddenError(this.meetingInfoFailureReason, err);
|
|
1779
1804
|
} else if (err instanceof MeetingInfoV2PasswordError) {
|
|
1780
1805
|
LoggerProxy.logger.info(
|
|
1781
1806
|
// @ts-ignore
|
|
@@ -2734,6 +2759,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2734
2759
|
this.triggerAnnotationInfoEvent(contentShare, previousContentShare);
|
|
2735
2760
|
|
|
2736
2761
|
if (
|
|
2762
|
+
!payload.forceUpdate &&
|
|
2737
2763
|
contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
|
|
2738
2764
|
contentShare.disposition === previousContentShare?.disposition &&
|
|
2739
2765
|
contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
|
|
@@ -2780,7 +2806,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2780
2806
|
// It does not matter who requested to share the whiteboard, everyone gets the same view
|
|
2781
2807
|
else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
|
|
2782
2808
|
// WHITEBOARD - sharing whiteboard
|
|
2783
|
-
|
|
2809
|
+
// Webinar attendee should receive whiteboard as remote share
|
|
2810
|
+
newShareStatus =
|
|
2811
|
+
this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
|
|
2812
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
|
2813
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
2784
2814
|
}
|
|
2785
2815
|
// or if content share is either released or null and whiteboard share is either released or null, no one is sharing
|
|
2786
2816
|
else if (
|
|
@@ -2795,6 +2825,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2795
2825
|
LoggerProxy.logger.info(
|
|
2796
2826
|
`Meeting:index#setUpLocusInfoMediaInactiveListener --> this.shareStatus=${this.shareStatus} newShareStatus=${newShareStatus}`
|
|
2797
2827
|
);
|
|
2828
|
+
|
|
2798
2829
|
if (newShareStatus !== this.shareStatus) {
|
|
2799
2830
|
const oldShareStatus = this.shareStatus;
|
|
2800
2831
|
|
|
@@ -3052,7 +3083,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3052
3083
|
*/
|
|
3053
3084
|
private setUpLocusResourcesListener() {
|
|
3054
3085
|
this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_RESOURCES, (payload) => {
|
|
3055
|
-
|
|
3086
|
+
if (payload) {
|
|
3087
|
+
this.webinar.updateWebcastUrl(payload);
|
|
3088
|
+
Trigger.trigger(
|
|
3089
|
+
this,
|
|
3090
|
+
{
|
|
3091
|
+
file: 'meeting/index',
|
|
3092
|
+
function: 'setUpLocusInfoMeetingInfoListener',
|
|
3093
|
+
},
|
|
3094
|
+
EVENT_TRIGGERS.MEETING_RESOURCE_LINKS_UPDATE,
|
|
3095
|
+
{
|
|
3096
|
+
payload,
|
|
3097
|
+
}
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3056
3100
|
});
|
|
3057
3101
|
}
|
|
3058
3102
|
|
|
@@ -3362,6 +3406,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3362
3406
|
}
|
|
3363
3407
|
});
|
|
3364
3408
|
|
|
3409
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
|
|
3410
|
+
Trigger.trigger(
|
|
3411
|
+
this,
|
|
3412
|
+
{
|
|
3413
|
+
file: 'meeting/index',
|
|
3414
|
+
function: 'setUpLocusInfoSelfListener',
|
|
3415
|
+
},
|
|
3416
|
+
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
|
3417
|
+
{
|
|
3418
|
+
payload,
|
|
3419
|
+
}
|
|
3420
|
+
);
|
|
3421
|
+
});
|
|
3422
|
+
|
|
3365
3423
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
|
|
3366
3424
|
const isModeratorOrCohost =
|
|
3367
3425
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
|
|
@@ -3371,6 +3429,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3371
3429
|
payload.newRoles?.includes(SELF_ROLES.MODERATOR)
|
|
3372
3430
|
);
|
|
3373
3431
|
this.webinar.updateRoleChanged(payload);
|
|
3432
|
+
|
|
3374
3433
|
Trigger.trigger(
|
|
3375
3434
|
this,
|
|
3376
3435
|
{
|
|
@@ -3565,6 +3624,50 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3565
3624
|
return this.members.admitMembers(memberIds, locusUrls);
|
|
3566
3625
|
}
|
|
3567
3626
|
|
|
3627
|
+
/**
|
|
3628
|
+
* Manages be right back status updates for the current participant.
|
|
3629
|
+
*
|
|
3630
|
+
* @param {boolean} enabled - Indicates whether the user enabled brb or not.
|
|
3631
|
+
* @returns {Promise<void>} resolves when the brb status is updated or does nothing if not in a multistream meeting.
|
|
3632
|
+
* @throws {Error} - Throws an error if the request fails.
|
|
3633
|
+
*/
|
|
3634
|
+
public async beRightBack(enabled: boolean): Promise<void> {
|
|
3635
|
+
if (!this.isMultistream) {
|
|
3636
|
+
const errorMessage = 'Meeting:index#beRightBack --> Not a multistream meeting';
|
|
3637
|
+
const error = new Error(errorMessage);
|
|
3638
|
+
|
|
3639
|
+
LoggerProxy.logger.error(error);
|
|
3640
|
+
|
|
3641
|
+
return Promise.reject(error);
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
3645
|
+
const errorMessage = 'Meeting:index#beRightBack --> WebRTC media connection is not defined';
|
|
3646
|
+
const error = new Error(errorMessage);
|
|
3647
|
+
|
|
3648
|
+
LoggerProxy.logger.error(error);
|
|
3649
|
+
|
|
3650
|
+
return Promise.reject(error);
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
// this logic should be applied only to multistream meetings
|
|
3654
|
+
return this.meetingRequest
|
|
3655
|
+
.setBrb({
|
|
3656
|
+
enabled,
|
|
3657
|
+
locusUrl: this.locusUrl,
|
|
3658
|
+
deviceUrl: this.deviceUrl,
|
|
3659
|
+
selfId: this.selfId,
|
|
3660
|
+
})
|
|
3661
|
+
.then(() => {
|
|
3662
|
+
this.sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
|
|
3663
|
+
})
|
|
3664
|
+
.catch((error) => {
|
|
3665
|
+
LoggerProxy.logger.error('Meeting:index#beRightBack --> Error ', error);
|
|
3666
|
+
|
|
3667
|
+
return Promise.reject(error);
|
|
3668
|
+
});
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3568
3671
|
/**
|
|
3569
3672
|
* Remove the member from the meeting, boot them
|
|
3570
3673
|
* @param {String} memberId
|
|
@@ -3804,6 +3907,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3804
3907
|
this.userDisplayHints
|
|
3805
3908
|
),
|
|
3806
3909
|
canManageBreakout: MeetingUtil.canManageBreakout(this.userDisplayHints),
|
|
3910
|
+
canStartBreakout: MeetingUtil.canStartBreakout(this.userDisplayHints),
|
|
3807
3911
|
canBroadcastMessageToBreakout: MeetingUtil.canBroadcastMessageToBreakout(
|
|
3808
3912
|
this.userDisplayHints,
|
|
3809
3913
|
this.selfUserPolicies
|
|
@@ -4099,10 +4203,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4099
4203
|
*/
|
|
4100
4204
|
private setLogUploadTimer() {
|
|
4101
4205
|
// 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
|
|
4102
|
-
const LOG_UPLOAD_INTERVALS = [0.1,
|
|
4206
|
+
const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
|
|
4103
4207
|
|
|
4104
4208
|
const delay =
|
|
4105
4209
|
1000 *
|
|
4210
|
+
60 *
|
|
4106
4211
|
// @ts-ignore - config coming from registerPlugin
|
|
4107
4212
|
this.config.logUploadIntervalMultiplicationFactor *
|
|
4108
4213
|
LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
|
|
@@ -4541,11 +4646,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4541
4646
|
* Close the peer connections and remove them from the class.
|
|
4542
4647
|
* Cleanup any media connection related things.
|
|
4543
4648
|
*
|
|
4649
|
+
* @param {boolean} resetMuteStates whether to also reset the audio/video mute state information
|
|
4544
4650
|
* @returns {Promise}
|
|
4545
4651
|
* @public
|
|
4546
4652
|
* @memberof Meeting
|
|
4547
4653
|
*/
|
|
4548
|
-
public closePeerConnections() {
|
|
4654
|
+
public closePeerConnections(resetMuteStates = true) {
|
|
4549
4655
|
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4550
4656
|
if (this.remoteMediaManager) {
|
|
4551
4657
|
this.remoteMediaManager.stop();
|
|
@@ -4558,12 +4664,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4558
4664
|
|
|
4559
4665
|
this.receiveSlotManager.reset();
|
|
4560
4666
|
this.mediaProperties.webrtcMediaConnection.close();
|
|
4667
|
+
this.mediaProperties.unsetPeerConnection();
|
|
4561
4668
|
this.sendSlotManager.reset();
|
|
4562
4669
|
this.setNetworkStatus(undefined);
|
|
4563
4670
|
}
|
|
4564
4671
|
|
|
4565
|
-
|
|
4566
|
-
|
|
4672
|
+
if (resetMuteStates) {
|
|
4673
|
+
this.audio = null;
|
|
4674
|
+
this.video = null;
|
|
4675
|
+
}
|
|
4567
4676
|
|
|
4568
4677
|
return Promise.resolve();
|
|
4569
4678
|
}
|
|
@@ -4823,7 +4932,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4823
4932
|
* @param {Object} options - options to join with media
|
|
4824
4933
|
* @param {JoinOptions} [options.joinOptions] - see #join()
|
|
4825
4934
|
* @param {AddMediaOptions} [options.mediaOptions] - see #addMedia()
|
|
4826
|
-
* @returns {Promise} -- {join: see join(), media: see addMedia()}
|
|
4935
|
+
* @returns {Promise} -- {join: see join(), media: see addMedia(), multistreamEnabled: flag to indicate if we managed to join in multistream mode}
|
|
4827
4936
|
* @public
|
|
4828
4937
|
* @memberof Meeting
|
|
4829
4938
|
* @example
|
|
@@ -4913,6 +5022,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4913
5022
|
return {
|
|
4914
5023
|
join: joinResponse,
|
|
4915
5024
|
media: mediaResponse,
|
|
5025
|
+
multistreamEnabled: this.isMultistream,
|
|
4916
5026
|
};
|
|
4917
5027
|
} catch (error) {
|
|
4918
5028
|
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
@@ -4921,7 +5031,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4921
5031
|
|
|
4922
5032
|
this.roap.abortTurnDiscovery();
|
|
4923
5033
|
|
|
4924
|
-
if
|
|
5034
|
+
// if this was the first attempt, let's do a retry
|
|
5035
|
+
let shouldRetry = !isRetry;
|
|
5036
|
+
|
|
5037
|
+
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
5038
|
+
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
5039
|
+
// so there is no point doing a retry
|
|
5040
|
+
shouldRetry = false;
|
|
5041
|
+
}
|
|
5042
|
+
|
|
5043
|
+
// we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
|
|
5044
|
+
if (joined && (isRetry || !shouldRetry)) {
|
|
4925
5045
|
try {
|
|
4926
5046
|
await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
|
|
4927
5047
|
} catch (e) {
|
|
@@ -4945,15 +5065,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4945
5065
|
}
|
|
4946
5066
|
);
|
|
4947
5067
|
|
|
4948
|
-
// if this was the first attempt, let's do a retry
|
|
4949
|
-
let shouldRetry = !isRetry;
|
|
4950
|
-
|
|
4951
|
-
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
4952
|
-
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
4953
|
-
// so there is no point doing a retry
|
|
4954
|
-
shouldRetry = false;
|
|
4955
|
-
}
|
|
4956
|
-
|
|
4957
5068
|
if (shouldRetry) {
|
|
4958
5069
|
LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
|
|
4959
5070
|
this.joinWithMediaRetryInfo.isRetry = true;
|
|
@@ -5209,7 +5320,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5209
5320
|
(this.config.receiveReactions || options.receiveReactions) &&
|
|
5210
5321
|
this.isReactionsSupported()
|
|
5211
5322
|
) {
|
|
5212
|
-
const
|
|
5323
|
+
const member = this.members.membersCollection.get(e.data.sender.participantId);
|
|
5324
|
+
if (!member) {
|
|
5325
|
+
// @ts-ignore -- fix type
|
|
5326
|
+
LoggerProxy.logger.warn(
|
|
5327
|
+
`Meeting:index#processRelayEvent --> Skipping handling of ${REACTION_RELAY_TYPES.REACTION} for ${this.id}. participantId ${e.data.sender.participantId} does not exist in membersCollection.`
|
|
5328
|
+
);
|
|
5329
|
+
break;
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5332
|
+
const {name} = member;
|
|
5213
5333
|
const processedReaction: ProcessedReaction = {
|
|
5214
5334
|
reaction: e.data.reaction,
|
|
5215
5335
|
sender: {
|
|
@@ -5263,6 +5383,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5263
5383
|
this.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]
|
|
5264
5384
|
);
|
|
5265
5385
|
|
|
5386
|
+
// @ts-ignore
|
|
5387
|
+
this.webex.internal.voicea.deregisterEvents();
|
|
5388
|
+
|
|
5266
5389
|
this.areVoiceaEventsSetup = false;
|
|
5267
5390
|
this.triggerStopReceivingTranscriptionEvent();
|
|
5268
5391
|
}
|
|
@@ -5373,16 +5496,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5373
5496
|
this.meetingFiniteStateMachine.reset();
|
|
5374
5497
|
}
|
|
5375
5498
|
|
|
5376
|
-
//
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5499
|
+
// send client.call.initiated unless told not to
|
|
5500
|
+
if (options.sendCallInitiated !== false) {
|
|
5501
|
+
// @ts-ignore
|
|
5502
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5503
|
+
name: 'client.call.initiated',
|
|
5504
|
+
payload: {
|
|
5505
|
+
trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
|
|
5506
|
+
isRoapCallEnabled: true,
|
|
5507
|
+
pstnAudioType: options?.pstnAudioType,
|
|
5508
|
+
},
|
|
5509
|
+
options: {meetingId: this.id},
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5386
5512
|
|
|
5387
5513
|
LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
|
|
5388
5514
|
|
|
@@ -5570,17 +5696,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5570
5696
|
*/
|
|
5571
5697
|
async updateLLMConnection() {
|
|
5572
5698
|
// @ts-ignore - Fix type
|
|
5573
|
-
const {url, info: {datachannelUrl} = {}} = this.locusInfo;
|
|
5699
|
+
const {url, info: {datachannelUrl, practiceSessionDatachannelUrl} = {}} = this.locusInfo;
|
|
5574
5700
|
|
|
5575
5701
|
const isJoined = this.isJoined();
|
|
5576
5702
|
|
|
5703
|
+
// webinar panelist should use new data channel in practice session
|
|
5704
|
+
const dataChannelUrl =
|
|
5705
|
+
this.webinar.isJoinPracticeSessionDataChannel() && practiceSessionDatachannelUrl
|
|
5706
|
+
? practiceSessionDatachannelUrl
|
|
5707
|
+
: datachannelUrl;
|
|
5708
|
+
|
|
5577
5709
|
// @ts-ignore - Fix type
|
|
5578
5710
|
if (this.webex.internal.llm.isConnected()) {
|
|
5579
5711
|
if (
|
|
5580
5712
|
// @ts-ignore - Fix type
|
|
5581
5713
|
url === this.webex.internal.llm.getLocusUrl() &&
|
|
5582
5714
|
// @ts-ignore - Fix type
|
|
5583
|
-
|
|
5715
|
+
dataChannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
|
|
5584
5716
|
isJoined
|
|
5585
5717
|
) {
|
|
5586
5718
|
return undefined;
|
|
@@ -5597,7 +5729,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5597
5729
|
|
|
5598
5730
|
// @ts-ignore - Fix type
|
|
5599
5731
|
return this.webex.internal.llm
|
|
5600
|
-
.registerAndConnect(url,
|
|
5732
|
+
.registerAndConnect(url, dataChannelUrl)
|
|
5601
5733
|
.then((registerAndConnectResult) => {
|
|
5602
5734
|
// @ts-ignore - Fix type
|
|
5603
5735
|
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
@@ -5969,6 +6101,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5969
6101
|
public roapMessageReceived = (roapMessage: RoapMessage) => {
|
|
5970
6102
|
const mediaServer = MeetingsUtil.getMediaServer(roapMessage.sdp);
|
|
5971
6103
|
|
|
6104
|
+
if (this.isMultistream && mediaServer !== 'homer') {
|
|
6105
|
+
throw new MultistreamNotSupportedError(
|
|
6106
|
+
`Client asked for multistream backend (Homer), but got ${mediaServer} instead`
|
|
6107
|
+
);
|
|
6108
|
+
}
|
|
5972
6109
|
this.mediaProperties.webrtcMediaConnection.roapMessageReceived(roapMessage);
|
|
5973
6110
|
|
|
5974
6111
|
if (mediaServer) {
|
|
@@ -6091,16 +6228,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6091
6228
|
logText: `${LOG_HEADER} Roap Offer`,
|
|
6092
6229
|
}
|
|
6093
6230
|
).catch((error) => {
|
|
6231
|
+
const multistreamNotSupported = error instanceof MultistreamNotSupportedError;
|
|
6232
|
+
|
|
6094
6233
|
// @ts-ignore
|
|
6095
6234
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6096
6235
|
name: 'client.media-engine.remote-sdp-received',
|
|
6097
6236
|
payload: {
|
|
6098
|
-
canProceed:
|
|
6237
|
+
canProceed: multistreamNotSupported,
|
|
6099
6238
|
errors: [
|
|
6100
6239
|
// @ts-ignore
|
|
6101
6240
|
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
6102
6241
|
{
|
|
6103
|
-
clientErrorCode:
|
|
6242
|
+
clientErrorCode: multistreamNotSupported
|
|
6243
|
+
? CALL_DIAGNOSTIC_CONFIG.MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE
|
|
6244
|
+
: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
6104
6245
|
}
|
|
6105
6246
|
),
|
|
6106
6247
|
],
|
|
@@ -6108,7 +6249,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6108
6249
|
options: {meetingId: this.id, rawError: error},
|
|
6109
6250
|
});
|
|
6110
6251
|
|
|
6111
|
-
this.deferSDPAnswer.reject(
|
|
6252
|
+
this.deferSDPAnswer.reject(error);
|
|
6112
6253
|
clearTimeout(this.sdpResponseTimer);
|
|
6113
6254
|
this.sdpResponseTimer = undefined;
|
|
6114
6255
|
});
|
|
@@ -6436,6 +6577,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6436
6577
|
this.webex.meetings.geoHintInfo?.clientAddress ||
|
|
6437
6578
|
options.data.intervalMetadata.peerReflexiveIP ||
|
|
6438
6579
|
MQA_STATS.DEFAULT_IP;
|
|
6580
|
+
|
|
6581
|
+
const {members} = this.getMembers().membersCollection;
|
|
6582
|
+
|
|
6583
|
+
// Count members that are in the meeting
|
|
6584
|
+
options.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
|
6585
|
+
(member: Member) => member.isInMeeting
|
|
6586
|
+
).length;
|
|
6587
|
+
|
|
6439
6588
|
// @ts-ignore
|
|
6440
6589
|
this.webex.internal.newMetrics.submitMQE({
|
|
6441
6590
|
name: 'client.mediaquality.event',
|
|
@@ -6767,32 +6916,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6767
6916
|
}
|
|
6768
6917
|
}
|
|
6769
6918
|
|
|
6770
|
-
/**
|
|
6771
|
-
* Handles device logging
|
|
6772
|
-
*
|
|
6773
|
-
* @private
|
|
6774
|
-
* @static
|
|
6775
|
-
* @param {boolean} isAudioEnabled
|
|
6776
|
-
* @param {boolean} isVideoEnabled
|
|
6777
|
-
* @returns {Promise<void>}
|
|
6778
|
-
*/
|
|
6779
|
-
|
|
6780
|
-
private static async handleDeviceLogging(isAudioEnabled, isVideoEnabled): Promise<void> {
|
|
6781
|
-
try {
|
|
6782
|
-
let devices = [];
|
|
6783
|
-
if (isVideoEnabled && isAudioEnabled) {
|
|
6784
|
-
devices = await getDevices();
|
|
6785
|
-
} else if (isVideoEnabled) {
|
|
6786
|
-
devices = await getDevices(Media.DeviceKind.VIDEO_INPUT);
|
|
6787
|
-
} else if (isAudioEnabled) {
|
|
6788
|
-
devices = await getDevices(Media.DeviceKind.AUDIO_INPUT);
|
|
6789
|
-
}
|
|
6790
|
-
MeetingUtil.handleDeviceLogging(devices);
|
|
6791
|
-
} catch {
|
|
6792
|
-
// getDevices may fail if we don't have browser permissions, that's ok, we still can have a media connection
|
|
6793
|
-
}
|
|
6794
|
-
}
|
|
6795
|
-
|
|
6796
6919
|
/**
|
|
6797
6920
|
* Returns a promise. This promise is created once the local sdp offer has been successfully created and is resolved
|
|
6798
6921
|
* once the remote sdp answer has been received.
|
|
@@ -7016,7 +7139,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7016
7139
|
|
|
7017
7140
|
const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
|
|
7018
7141
|
|
|
7019
|
-
LoggerProxy.logger.info(
|
|
7142
|
+
LoggerProxy.logger.info(
|
|
7143
|
+
`${LOG_HEADER} media connection created this.isMultistream=${this.isMultistream}`
|
|
7144
|
+
);
|
|
7020
7145
|
|
|
7021
7146
|
if (this.isMultistream) {
|
|
7022
7147
|
this.remoteMediaManager = new RemoteMediaManager(
|
|
@@ -7094,6 +7219,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7094
7219
|
}
|
|
7095
7220
|
}
|
|
7096
7221
|
|
|
7222
|
+
/**
|
|
7223
|
+
* Cleans up stats analyzer, peer connection and other things before
|
|
7224
|
+
* we can create a new transcoded media connection
|
|
7225
|
+
*
|
|
7226
|
+
* @private
|
|
7227
|
+
* @returns {Promise<void>}
|
|
7228
|
+
*/
|
|
7229
|
+
private async downgradeFromMultistreamToTranscoded(): Promise<void> {
|
|
7230
|
+
if (this.statsAnalyzer) {
|
|
7231
|
+
await this.statsAnalyzer.stopAnalyzer();
|
|
7232
|
+
}
|
|
7233
|
+
this.statsAnalyzer = null;
|
|
7234
|
+
|
|
7235
|
+
this.isMultistream = false;
|
|
7236
|
+
|
|
7237
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
7238
|
+
// close peer connection, but don't reset mute state information, because we will want to use it on the retry
|
|
7239
|
+
this.closePeerConnections(false);
|
|
7240
|
+
|
|
7241
|
+
this.mediaProperties.unsetPeerConnection();
|
|
7242
|
+
}
|
|
7243
|
+
|
|
7244
|
+
this.locusMediaRequest?.downgradeFromMultistreamToTranscoded();
|
|
7245
|
+
|
|
7246
|
+
this.createStatsAnalyzer();
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7097
7249
|
/**
|
|
7098
7250
|
* Sends stats report, closes peer connection and cleans up any media connection
|
|
7099
7251
|
* related things before trying to establish media connection again with turn server
|
|
@@ -7288,19 +7440,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7288
7440
|
|
|
7289
7441
|
this.createStatsAnalyzer();
|
|
7290
7442
|
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7443
|
+
try {
|
|
7444
|
+
await this.establishMediaConnection(
|
|
7445
|
+
remoteMediaManagerConfig,
|
|
7446
|
+
bundlePolicy,
|
|
7447
|
+
forceTurnDiscovery,
|
|
7448
|
+
turnServerInfo
|
|
7449
|
+
);
|
|
7450
|
+
} catch (error) {
|
|
7451
|
+
if (error instanceof MultistreamNotSupportedError) {
|
|
7452
|
+
LoggerProxy.logger.warn(
|
|
7453
|
+
`${LOG_HEADER} we asked for multistream backend (Homer), but got transcoded backend, recreating media connection...`
|
|
7454
|
+
);
|
|
7297
7455
|
|
|
7298
|
-
|
|
7299
|
-
await Meeting.handleDeviceLogging(audioEnabled, videoEnabled);
|
|
7300
|
-
} else {
|
|
7301
|
-
LoggerProxy.logger.info(`${LOG_HEADER} device logging not required`);
|
|
7302
|
-
}
|
|
7456
|
+
await this.downgradeFromMultistreamToTranscoded();
|
|
7303
7457
|
|
|
7458
|
+
// Establish new media connection with forced TURN discovery
|
|
7459
|
+
// 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
|
|
7460
|
+
await this.establishMediaConnection(
|
|
7461
|
+
remoteMediaManagerConfig,
|
|
7462
|
+
bundlePolicy,
|
|
7463
|
+
true,
|
|
7464
|
+
undefined
|
|
7465
|
+
);
|
|
7466
|
+
} else {
|
|
7467
|
+
throw error;
|
|
7468
|
+
}
|
|
7469
|
+
}
|
|
7304
7470
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
7305
7471
|
await this.enqueueScreenShareFloorRequest();
|
|
7306
7472
|
}
|
|
@@ -8270,7 +8436,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8270
8436
|
if (layoutType) {
|
|
8271
8437
|
if (!LAYOUT_TYPES.includes(layoutType)) {
|
|
8272
8438
|
return this.rejectWithErrorLog(
|
|
8273
|
-
|
|
8439
|
+
`Meeting:index#changeVideoLayout --> cannot change video layout, invalid layoutType "${layoutType}" received.`
|
|
8274
8440
|
);
|
|
8275
8441
|
}
|
|
8276
8442
|
|
|
@@ -8426,6 +8592,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8426
8592
|
correlationId: this.correlationId,
|
|
8427
8593
|
muted,
|
|
8428
8594
|
encoderImplementation: this.statsAnalyzer?.shareVideoEncoderImplementation,
|
|
8595
|
+
// TypeScript 4 does not recognize the `displaySurface` property. Instead of upgrading the
|
|
8596
|
+
// SDK to TypeScript 5, which may affect other packages, use bracket notation for now, since
|
|
8597
|
+
// all we're doing here is adding metrics.
|
|
8598
|
+
// eslint-disable-next-line dot-notation
|
|
8599
|
+
displaySurface: this.mediaProperties?.shareVideoStream?.getSettings()['displaySurface'],
|
|
8600
|
+
isMultistream: this.isMultistream,
|
|
8429
8601
|
});
|
|
8430
8602
|
};
|
|
8431
8603
|
|
|
@@ -8628,6 +8800,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8628
8800
|
this.stopTranscription();
|
|
8629
8801
|
this.transcription = undefined;
|
|
8630
8802
|
}
|
|
8803
|
+
|
|
8804
|
+
this.annotation.deregisterEvents();
|
|
8805
|
+
|
|
8806
|
+
// @ts-ignore - fix types
|
|
8807
|
+
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
8631
8808
|
};
|
|
8632
8809
|
|
|
8633
8810
|
/**
|
|
@@ -8665,10 +8842,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8665
8842
|
|
|
8666
8843
|
return;
|
|
8667
8844
|
}
|
|
8668
|
-
|
|
8845
|
+
|
|
8669
8846
|
const keepAliveInterval = (this.joinedWith.keepAliveSecs - 1) * 750; // taken from UCF
|
|
8670
8847
|
|
|
8671
8848
|
this.keepAliveTimerId = setInterval(() => {
|
|
8849
|
+
const {keepAliveUrl} = this.joinedWith;
|
|
8850
|
+
|
|
8672
8851
|
this.meetingRequest.keepAlive({keepAliveUrl}).catch((error) => {
|
|
8673
8852
|
LoggerProxy.logger.warn(
|
|
8674
8853
|
`Meeting:index#startKeepAlive --> Stopping sending keepAlives to ${keepAliveUrl} after error ${error}`
|
|
@@ -342,4 +342,11 @@ export class LocusMediaRequest extends WebexPlugin {
|
|
|
342
342
|
public isConfluenceCreated() {
|
|
343
343
|
return this.confluenceState === 'created';
|
|
344
344
|
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* This method needs to be called when we downgrade from multistream to transcoded connection.
|
|
348
|
+
*/
|
|
349
|
+
public downgradeFromMultistreamToTranscoded() {
|
|
350
|
+
this.config.preferTranscoding = true;
|
|
351
|
+
}
|
|
345
352
|
}
|
package/src/meeting/request.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
_SLIDES_,
|
|
28
28
|
ANNOTATION,
|
|
29
29
|
} from '../constants';
|
|
30
|
-
import {SendReactionOptions, ToggleReactionsOptions} from './request.type';
|
|
30
|
+
import {SendReactionOptions, BrbOptions, ToggleReactionsOptions} from './request.type';
|
|
31
31
|
import MeetingUtil from './util';
|
|
32
32
|
import {AnnotationInfo} from '../annotation/annotation.types';
|
|
33
33
|
import {ClientMediaPreferences} from '../reachability/reachability.types';
|
|
@@ -909,4 +909,29 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
909
909
|
uri: locusUrl,
|
|
910
910
|
});
|
|
911
911
|
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Sends a request to set be right back status.
|
|
915
|
+
*
|
|
916
|
+
* @param {Object} options - The options for brb request.
|
|
917
|
+
* @param {boolean} options.enabled - Whether brb status is enabled.
|
|
918
|
+
* @param {string} options.locusUrl - The URL of the locus.
|
|
919
|
+
* @param {string} options.deviceUrl - The URL of the device.
|
|
920
|
+
* @param {string} options.selfId - The ID of the participant.
|
|
921
|
+
* @returns {Promise}
|
|
922
|
+
*/
|
|
923
|
+
setBrb({enabled, locusUrl, deviceUrl, selfId}: BrbOptions) {
|
|
924
|
+
const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${CONTROLS}`;
|
|
925
|
+
|
|
926
|
+
return this.locusDeltaRequest({
|
|
927
|
+
method: HTTP_VERBS.PATCH,
|
|
928
|
+
uri,
|
|
929
|
+
body: {
|
|
930
|
+
brb: {
|
|
931
|
+
enabled,
|
|
932
|
+
deviceUrl,
|
|
933
|
+
},
|
|
934
|
+
},
|
|
935
|
+
});
|
|
936
|
+
}
|
|
912
937
|
}
|