@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/browser-detection.js.map +1 -1
- package/dist/common/collection.js.map +1 -1
- package/dist/common/config.js.map +1 -1
- package/dist/common/errors/captcha-error.js +7 -0
- package/dist/common/errors/captcha-error.js.map +1 -1
- package/dist/common/errors/intent-to-join.js +8 -0
- package/dist/common/errors/intent-to-join.js.map +1 -1
- package/dist/common/errors/join-meeting.js +8 -0
- package/dist/common/errors/join-meeting.js.map +1 -1
- package/dist/common/errors/media.js +7 -0
- package/dist/common/errors/media.js.map +1 -1
- package/dist/common/errors/parameter.js.map +1 -1
- package/dist/common/errors/password-error.js +7 -0
- package/dist/common/errors/password-error.js.map +1 -1
- package/dist/common/errors/permission.js +7 -0
- package/dist/common/errors/permission.js.map +1 -1
- package/dist/common/errors/reconnection-in-progress.js.map +1 -1
- package/dist/common/errors/reconnection.js +7 -0
- package/dist/common/errors/reconnection.js.map +1 -1
- package/dist/common/errors/stats.js +7 -0
- package/dist/common/errors/stats.js.map +1 -1
- package/dist/common/errors/webex-errors.js +5 -29
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/errors/webex-meetings-error.js +5 -2
- package/dist/common/errors/webex-meetings-error.js.map +1 -1
- package/dist/common/events/events-scope.js.map +1 -1
- package/dist/common/events/events.js.map +1 -1
- package/dist/common/events/trigger-proxy.js.map +1 -1
- package/dist/common/events/util.js.map +1 -1
- package/dist/common/logs/logger-config.js.map +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +3 -0
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +15 -74
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
- package/dist/locus-info/fullState.js.map +1 -1
- package/dist/locus-info/hostUtils.js.map +1 -1
- package/dist/locus-info/index.js +43 -5
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js +4 -0
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +12 -3
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +71 -210
- package/dist/media/index.js.map +1 -1
- package/dist/media/internal-media-core-wrapper.js +22 -0
- package/dist/media/internal-media-core-wrapper.js.map +1 -0
- package/dist/media/properties.js +32 -25
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js +0 -27
- package/dist/media/util.js.map +1 -1
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/effectsState.js +8 -1
- package/dist/meeting/effectsState.js.map +1 -1
- package/dist/meeting/index.js +1116 -613
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +6 -0
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +55 -24
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/state.js.map +1 -1
- package/dist/meeting/util.js +5 -44
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/collection.js +4 -1
- package/dist/meeting-info/collection.js.map +1 -1
- package/dist/meeting-info/index.js +5 -0
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +14 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/request.js +3 -0
- package/dist/meeting-info/request.js.map +1 -1
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +4 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +136 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +4 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +24 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +30 -7
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +2 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +1 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +82 -1
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +19 -9
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/config.js.map +1 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +8 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +133 -0
- package/dist/multistream/mediaRequestManager.js.map +1 -0
- package/dist/multistream/multistreamMedia.js +116 -0
- package/dist/multistream/multistreamMedia.js.map +1 -0
- package/dist/multistream/receiveSlot.js +209 -0
- package/dist/multistream/receiveSlot.js.map +1 -0
- package/dist/multistream/receiveSlotManager.js +195 -0
- package/dist/multistream/receiveSlotManager.js.map +1 -0
- package/dist/multistream/remoteMedia.js +289 -0
- package/dist/multistream/remoteMedia.js.map +1 -0
- package/dist/multistream/remoteMediaGroup.js +243 -0
- package/dist/multistream/remoteMediaGroup.js.map +1 -0
- package/dist/multistream/remoteMediaManager.js +1113 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -0
- package/dist/networkQualityMonitor/index.js +10 -2
- package/dist/networkQualityMonitor/index.js.map +1 -1
- package/dist/personal-meeting-room/index.js +11 -0
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/personal-meeting-room/request.js +2 -1
- package/dist/personal-meeting-room/request.js.map +1 -1
- package/dist/personal-meeting-room/util.js.map +1 -1
- package/dist/reachability/index.js +17 -7
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +1 -0
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +130 -132
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +58 -231
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +7 -116
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +20 -6
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/global.js +2 -0
- package/dist/statsAnalyzer/global.js.map +1 -1
- package/dist/statsAnalyzer/index.js +58 -37
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +9 -3
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/transcription/index.js +10 -3
- package/dist/transcription/index.js.map +1 -1
- package/package.json +21 -20
- package/src/common/{browser-detection.js → browser-detection.ts} +1 -1
- package/src/common/collection.ts +6 -6
- package/src/common/{config.js → config.ts} +1 -1
- package/src/common/errors/{captcha-error.js → captcha-error.ts} +5 -1
- package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +6 -1
- package/src/common/errors/{join-meeting.js → join-meeting.ts} +6 -1
- package/src/common/errors/{media.js → media.ts} +5 -1
- package/src/common/errors/parameter.ts +3 -2
- package/src/common/errors/{password-error.js → password-error.ts} +5 -1
- package/src/common/errors/{permission.js → permission.ts} +5 -1
- package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
- package/src/common/errors/{reconnection.js → reconnection.ts} +5 -1
- package/src/common/errors/{stats.js → stats.ts} +5 -1
- package/src/common/errors/{webex-errors.js → webex-errors.ts} +1 -20
- package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +3 -1
- package/src/common/events/{events-scope.js → events-scope.ts} +1 -1
- package/src/common/events/{events.js → events.ts} +0 -0
- package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +1 -2
- package/src/common/events/{util.js → util.ts} +1 -1
- package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
- package/src/common/logs/{logger-proxy.js → logger-proxy.ts} +1 -1
- package/src/common/logs/{request.js → request.ts} +12 -2
- package/src/common/queue.ts +1 -2
- package/src/{config.js → config.ts} +2 -0
- package/src/constants.ts +139 -179
- package/src/locus-info/{controlsUtils.js → controlsUtils.ts} +4 -4
- package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
- package/src/locus-info/{fullState.js → fullState.ts} +1 -1
- package/src/locus-info/{hostUtils.js → hostUtils.ts} +5 -5
- package/src/locus-info/{index.js → index.ts} +67 -32
- package/src/locus-info/{infoUtils.js → infoUtils.ts} +7 -4
- package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +13 -13
- package/src/locus-info/{parser.js → parser.ts} +22 -12
- package/src/locus-info/{selfUtils.js → selfUtils.ts} +17 -19
- package/src/media/{index.js → index.ts} +130 -205
- package/src/media/internal-media-core-wrapper.ts +9 -0
- package/src/media/{properties.js → properties.ts} +35 -29
- package/src/media/util.ts +16 -0
- package/src/mediaQualityMetrics/{config.js → config.ts} +1 -1
- package/src/meeting/{effectsState.js → effectsState.ts} +12 -6
- package/src/meeting/{index.js → index.ts} +961 -474
- package/src/meeting/{muteState.js → muteState.ts} +16 -11
- package/src/meeting/{request.js → request.ts} +125 -36
- package/src/meeting/{state.js → state.ts} +6 -6
- package/src/meeting/{util.js → util.ts} +9 -51
- package/src/meeting-info/{collection.js → collection.ts} +4 -1
- package/src/meeting-info/{index.js → index.ts} +10 -6
- package/src/meeting-info/{meeting-info-v2.js → meeting-info-v2.ts} +28 -10
- package/src/meeting-info/{request.js → request.ts} +6 -2
- package/src/meeting-info/{util.js → util.ts} +6 -5
- package/src/meeting-info/{utilv2.js → utilv2.ts} +8 -7
- package/src/meetings/{collection.js → collection.ts} +5 -2
- package/src/meetings/{index.js → index.ts} +118 -22
- package/src/meetings/{request.js → request.ts} +6 -1
- package/src/meetings/{util.js → util.ts} +28 -5
- package/src/member/{index.js → index.ts} +46 -15
- package/src/member/{util.js → util.ts} +17 -16
- package/src/members/{collection.js → collection.ts} +2 -1
- package/src/members/{index.js → index.ts} +94 -26
- package/src/members/{request.js → request.ts} +16 -5
- package/src/members/{util.js → util.ts} +7 -7
- package/src/metrics/{config.js → config.ts} +0 -2
- package/src/metrics/{constants.js → constants.ts} +0 -0
- package/src/metrics/{index.js → index.ts} +27 -8
- package/src/multistream/mediaRequestManager.ts +166 -0
- package/src/multistream/multistreamMedia.ts +92 -0
- package/src/multistream/receiveSlot.ts +141 -0
- package/src/multistream/receiveSlotManager.ts +142 -0
- package/src/multistream/remoteMedia.ts +228 -0
- package/src/multistream/remoteMediaGroup.ts +224 -0
- package/src/multistream/remoteMediaManager.ts +911 -0
- package/src/networkQualityMonitor/{index.js → index.ts} +18 -3
- package/src/personal-meeting-room/{index.js → index.ts} +17 -4
- package/src/personal-meeting-room/{request.js → request.ts} +3 -1
- package/src/personal-meeting-room/{util.js → util.ts} +1 -1
- package/src/reachability/{index.js → index.ts} +28 -17
- package/src/reachability/request.ts +4 -2
- package/src/reconnection-manager/{index.js → index.ts} +81 -65
- package/src/roap/index.ts +229 -0
- package/src/roap/{request.js → request.ts} +15 -74
- package/src/roap/turnDiscovery.ts +26 -11
- package/src/statsAnalyzer/{global.js → global.ts} +2 -0
- package/src/statsAnalyzer/{index.js → index.ts} +66 -61
- package/src/statsAnalyzer/{mqaUtil.js → mqaUtil.ts} +6 -1
- package/src/transcription/{index.js → index.ts} +16 -11
- package/test/integration/spec/journey.js +1 -1
- package/test/integration/spec/space-meeting.js +1 -2
- package/test/unit/spec/locus-info/infoUtils.js +17 -1
- package/test/unit/spec/media/index.ts +207 -0
- package/test/unit/spec/media/properties.ts +73 -82
- package/test/unit/spec/meeting/effectsState.js +1 -3
- package/test/unit/spec/meeting/index.js +585 -245
- package/test/unit/spec/meeting/muteState.js +7 -0
- package/test/unit/spec/meeting/utils.js +63 -2
- package/test/unit/spec/meetings/index.js +0 -4
- package/test/unit/spec/members/index.js +164 -2
- package/test/unit/spec/multistream/mediaRequestManager.ts +515 -0
- package/test/unit/spec/multistream/receiveSlot.ts +104 -0
- package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
- package/test/unit/spec/multistream/remoteMedia.ts +225 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +1309 -0
- package/test/unit/spec/reconnection-manager/index.js +68 -2
- package/test/unit/spec/roap/index.ts +63 -35
- package/test/unit/spec/stats-analyzer/index.js +19 -22
- package/dist/peer-connection-manager/index.js +0 -794
- package/dist/peer-connection-manager/index.js.map +0 -1
- package/dist/peer-connection-manager/util.js +0 -124
- package/dist/peer-connection-manager/util.js.map +0 -1
- package/dist/roap/collection.js +0 -73
- package/dist/roap/collection.js.map +0 -1
- package/dist/roap/handler.js +0 -337
- package/dist/roap/handler.js.map +0 -1
- package/dist/roap/state.js +0 -164
- package/dist/roap/state.js.map +0 -1
- package/dist/roap/util.js +0 -102
- package/dist/roap/util.js.map +0 -1
- package/src/media/util.js +0 -38
- package/src/peer-connection-manager/index.js +0 -723
- package/src/peer-connection-manager/util.ts +0 -117
- package/src/roap/collection.js +0 -63
- package/src/roap/handler.js +0 -252
- package/src/roap/index.js +0 -380
- package/src/roap/state.js +0 -149
- package/src/roap/util.js +0 -93
- package/test/unit/spec/peerconnection-manager/index.js +0 -188
- package/test/unit/spec/peerconnection-manager/utils.js +0 -48
- package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
- package/test/unit/spec/roap/util.js +0 -30
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import uuid from 'uuid';
|
|
2
|
-
import {cloneDeep, isEqual, pick, isString} from 'lodash';
|
|
2
|
+
import {cloneDeep, isEqual, pick, isString, defer} from 'lodash';
|
|
3
|
+
// @ts-ignore - Fix this
|
|
3
4
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
4
|
-
import {Media as WebRTCMedia} from '@webex/internal-media-core';
|
|
5
|
+
import {Media as WebRTCMedia, MediaConnection as MC} from '@webex/internal-media-core';
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
MeetingNotActiveError, createMeetingsError, UserInLobbyError,
|
|
8
|
-
NoMediaEstablishedYetError, UserNotJoinedError
|
|
9
|
+
NoMediaEstablishedYetError, UserNotJoinedError
|
|
9
10
|
} from '../common/errors/webex-errors';
|
|
10
11
|
import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
|
|
11
12
|
import NetworkQualityMonitor from '../networkQualityMonitor';
|
|
@@ -18,9 +19,8 @@ import MeetingStateMachine from '../meeting/state';
|
|
|
18
19
|
import createMuteState from '../meeting/muteState';
|
|
19
20
|
import createEffectsState from '../meeting/effectsState';
|
|
20
21
|
import LocusInfo from '../locus-info';
|
|
21
|
-
import PeerConnectionManager from '../peer-connection-manager';
|
|
22
22
|
import Metrics from '../metrics';
|
|
23
|
-
import {trigger, mediaType, eventType} from '../metrics/config';
|
|
23
|
+
import {trigger, mediaType, error as MetricsError, eventType} from '../metrics/config';
|
|
24
24
|
import ReconnectionManager from '../reconnection-manager';
|
|
25
25
|
import MeetingRequest from '../meeting/request';
|
|
26
26
|
import Members from '../members/index';
|
|
@@ -44,7 +44,6 @@ import {
|
|
|
44
44
|
FLOOR_ACTION,
|
|
45
45
|
FULL_STATE,
|
|
46
46
|
LAYOUT_TYPES,
|
|
47
|
-
LIVE,
|
|
48
47
|
LOCUSINFO,
|
|
49
48
|
MEETING_INFO_FAILURE_REASON,
|
|
50
49
|
MEETING_REMOVED_REASON,
|
|
@@ -60,9 +59,6 @@ import {
|
|
|
60
59
|
PSTN_STATUS,
|
|
61
60
|
QUALITY_LEVELS,
|
|
62
61
|
RECORDING_STATE,
|
|
63
|
-
ROAP_SEQ_PRE,
|
|
64
|
-
SDP,
|
|
65
|
-
SENDRECV,
|
|
66
62
|
SHARE_STATUS,
|
|
67
63
|
SHARE_STOPPED_REASON,
|
|
68
64
|
VIDEO_RESOLUTIONS,
|
|
@@ -75,14 +71,17 @@ import ParameterError from '../common/errors/parameter';
|
|
|
75
71
|
import MediaError from '../common/errors/media';
|
|
76
72
|
import {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError} from '../meeting-info/meeting-info-v2';
|
|
77
73
|
import BrowserDetection from '../common/browser-detection';
|
|
78
|
-
import
|
|
74
|
+
import {ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
|
75
|
+
import {MediaRequestManager} from '../multistream/mediaRequestManager';
|
|
76
|
+
import {RemoteMediaManager, Event as RemoteMediaManagerEvent} from '../multistream/remoteMediaManager';
|
|
77
|
+
import {MultistreamMedia} from '../multistream/multistreamMedia';
|
|
79
78
|
|
|
80
79
|
import InMeetingActions from './in-meeting-actions';
|
|
81
80
|
|
|
82
81
|
|
|
83
82
|
const {isBrowser} = BrowserDetection();
|
|
84
83
|
|
|
85
|
-
const logRequest = (request, {header = '', success = '', failure = ''}) => {
|
|
84
|
+
const logRequest = (request: any, { header = '', success = '', failure = '' }) => {
|
|
86
85
|
LoggerProxy.logger.info(header);
|
|
87
86
|
|
|
88
87
|
return request
|
|
@@ -143,6 +142,7 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
143
142
|
* @property {String} [meetingQuality.local]
|
|
144
143
|
* @property {String} [meetingQuality.remote]
|
|
145
144
|
* @property {Boolean} [rejoin]
|
|
145
|
+
* @property {Boolean} [enableMultistream]
|
|
146
146
|
*/
|
|
147
147
|
|
|
148
148
|
/**
|
|
@@ -392,6 +392,92 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
392
392
|
* @class Meeting
|
|
393
393
|
*/
|
|
394
394
|
export default class Meeting extends StatelessWebexPlugin {
|
|
395
|
+
attrs: any;
|
|
396
|
+
audio: any;
|
|
397
|
+
conversationUrl: string;
|
|
398
|
+
correlationId: string;
|
|
399
|
+
destination: string;
|
|
400
|
+
destinationType: string;
|
|
401
|
+
deviceUrl: string;
|
|
402
|
+
effects: any;
|
|
403
|
+
hostId: string;
|
|
404
|
+
id: string;
|
|
405
|
+
isMultistream: boolean;
|
|
406
|
+
locusUrl: string;
|
|
407
|
+
mediaConnections: any[];
|
|
408
|
+
mediaId?: string;
|
|
409
|
+
meetingFiniteStateMachine: any;
|
|
410
|
+
meetingInfo: object;
|
|
411
|
+
meetingRequest: any;
|
|
412
|
+
members: Members;
|
|
413
|
+
options: object;
|
|
414
|
+
orgId: string;
|
|
415
|
+
owner: string;
|
|
416
|
+
partner: any;
|
|
417
|
+
policy: string;
|
|
418
|
+
reconnectionManager: ReconnectionManager;
|
|
419
|
+
resource: string;
|
|
420
|
+
roap: Roap;
|
|
421
|
+
roapSeq: number;
|
|
422
|
+
sipUri: string;
|
|
423
|
+
type: string;
|
|
424
|
+
userId: string;
|
|
425
|
+
video: any;
|
|
426
|
+
callEvents: any[];
|
|
427
|
+
deferJoin: Promise<any>;
|
|
428
|
+
dialInDeviceStatus: string;
|
|
429
|
+
dialInUrl: string;
|
|
430
|
+
dialOutDeviceStatus: string;
|
|
431
|
+
dialOutUrl: string;
|
|
432
|
+
fetchMeetingInfoTimeoutId: NodeJS.Timeout;
|
|
433
|
+
floorGrantPending: boolean;
|
|
434
|
+
hasJoinedOnce: boolean;
|
|
435
|
+
hasWebsocketConnected: boolean;
|
|
436
|
+
inMeetingActions: InMeetingActions;
|
|
437
|
+
isLocalShareLive: boolean;
|
|
438
|
+
isRoapInProgress: boolean;
|
|
439
|
+
isSharing: boolean;
|
|
440
|
+
keepAliveTimerId: NodeJS.Timeout;
|
|
441
|
+
lastVideoLayoutInfo: any;
|
|
442
|
+
locusInfo: any;
|
|
443
|
+
media: MultistreamMedia;
|
|
444
|
+
mediaProperties: MediaProperties;
|
|
445
|
+
mediaRequestManagers: {
|
|
446
|
+
audio: MediaRequestManager;
|
|
447
|
+
video: MediaRequestManager;
|
|
448
|
+
};
|
|
449
|
+
meetingInfoFailureReason: string;
|
|
450
|
+
networkQualityMonitor: NetworkQualityMonitor;
|
|
451
|
+
networkStatus: string;
|
|
452
|
+
passwordStatus: string;
|
|
453
|
+
queuedMediaUpdates: any[];
|
|
454
|
+
recording: any;
|
|
455
|
+
remoteMediaManager: RemoteMediaManager | null;
|
|
456
|
+
requiredCaptcha: any;
|
|
457
|
+
receiveSlotManager: ReceiveSlotManager;
|
|
458
|
+
shareStatus: string;
|
|
459
|
+
statsAnalyzer: StatsAnalyzer;
|
|
460
|
+
transcription: Transcription;
|
|
461
|
+
updateMediaConnections: (mediaConnections: any[]) => void;
|
|
462
|
+
endCallInitiateJoinReq: any;
|
|
463
|
+
endJoinReqResp: any;
|
|
464
|
+
endLocalSDPGenRemoteSDPRecvDelay: any;
|
|
465
|
+
joinedWith: any;
|
|
466
|
+
locusId: any;
|
|
467
|
+
startCallInitiateJoinReq: any;
|
|
468
|
+
startJoinReqResp: any;
|
|
469
|
+
startLocalSDPGenRemoteSDPRecvDelay: any;
|
|
470
|
+
wirelessShare: any;
|
|
471
|
+
guest: any;
|
|
472
|
+
meetingJoinUrl: any;
|
|
473
|
+
meetingNumber: any;
|
|
474
|
+
meetingState: any;
|
|
475
|
+
permissionToken: any;
|
|
476
|
+
resourceId: any;
|
|
477
|
+
resourceUrl: string;
|
|
478
|
+
selfId: string;
|
|
479
|
+
state: any;
|
|
480
|
+
|
|
395
481
|
namespace = MEETINGS;
|
|
396
482
|
|
|
397
483
|
/**
|
|
@@ -400,7 +486,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
400
486
|
* @constructor
|
|
401
487
|
* @memberof Meeting
|
|
402
488
|
*/
|
|
403
|
-
constructor(attrs, options) {
|
|
489
|
+
constructor(attrs: any, options: object) {
|
|
404
490
|
super({}, options);
|
|
405
491
|
/**
|
|
406
492
|
* @instance
|
|
@@ -468,15 +554,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
468
554
|
* @memberof Meeting
|
|
469
555
|
*/
|
|
470
556
|
this.deviceUrl = attrs.deviceUrl;
|
|
471
|
-
/**
|
|
472
|
-
* @description set you -1 as default values is 0 (used to idenfify if 1st roap request was sent)
|
|
473
|
-
* @instance
|
|
474
|
-
* @type {Number}
|
|
475
|
-
* @readonly
|
|
476
|
-
* @private
|
|
477
|
-
* @memberof Meeting
|
|
478
|
-
*/
|
|
479
|
-
this.roapSeq = ROAP_SEQ_PRE;
|
|
480
557
|
/**
|
|
481
558
|
* @instance
|
|
482
559
|
* @type {Object}
|
|
@@ -486,13 +563,44 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
486
563
|
*/
|
|
487
564
|
// TODO: needs to be defined as a class
|
|
488
565
|
this.meetingInfo = {};
|
|
566
|
+
/**
|
|
567
|
+
* helper class for managing receive slots (for multistream media connections)
|
|
568
|
+
*/
|
|
569
|
+
this.receiveSlotManager = new ReceiveSlotManager(this);
|
|
570
|
+
/**
|
|
571
|
+
* Helper class for managing media requests for main video (for multistream media connections)
|
|
572
|
+
* All media requests sent out for main video for this meeting have to go through it.
|
|
573
|
+
*/
|
|
574
|
+
this.mediaRequestManagers = {
|
|
575
|
+
audio: new MediaRequestManager((mediaRequests) => {
|
|
576
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
577
|
+
LoggerProxy.logger.warn('Meeting:index#mediaRequestManager --> trying to send audio media request before media connection was created');
|
|
578
|
+
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(MC.MediaType.AudioMain, mediaRequests);
|
|
582
|
+
}),
|
|
583
|
+
video: new MediaRequestManager((mediaRequests) => {
|
|
584
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
585
|
+
LoggerProxy.logger.warn('Meeting:index#mediaRequestManager --> trying to send video media request before media connection was created');
|
|
586
|
+
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(MC.MediaType.VideoMain, mediaRequests);
|
|
590
|
+
})
|
|
591
|
+
};
|
|
489
592
|
/**
|
|
490
593
|
* @instance
|
|
491
594
|
* @type {Members}
|
|
492
595
|
* @public
|
|
493
596
|
* @memberof Meeting
|
|
494
597
|
*/
|
|
495
|
-
this.members = new Members({
|
|
598
|
+
this.members = new Members({
|
|
599
|
+
locusUrl: (attrs.locus && attrs.locus.url),
|
|
600
|
+
receiveSlotManager: this.receiveSlotManager,
|
|
601
|
+
mediaRequestManagers: this.mediaRequestManagers,
|
|
602
|
+
// @ts-ignore - Fix type
|
|
603
|
+
}, {parent: this.webex});
|
|
496
604
|
/**
|
|
497
605
|
* @instance
|
|
498
606
|
* @type {Roap}
|
|
@@ -500,7 +608,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
500
608
|
* @private
|
|
501
609
|
* @memberof Meeting
|
|
502
610
|
*/
|
|
611
|
+
// @ts-ignore - Fix type
|
|
503
612
|
this.roap = new Roap({}, {parent: this.webex});
|
|
613
|
+
/**
|
|
614
|
+
* indicates if an SDP exchange is happening
|
|
615
|
+
*
|
|
616
|
+
* @instance
|
|
617
|
+
* @type {Boolean}
|
|
618
|
+
* @readonly
|
|
619
|
+
* @private
|
|
620
|
+
* @memberof Meeting
|
|
621
|
+
*/
|
|
622
|
+
this.isRoapInProgress = false;
|
|
504
623
|
/**
|
|
505
624
|
* created later
|
|
506
625
|
* @instance
|
|
@@ -638,6 +757,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
638
757
|
*/
|
|
639
758
|
this.mediaConnections = null;
|
|
640
759
|
|
|
760
|
+
/**
|
|
761
|
+
* If true, then media is sent over multiple separate streams.
|
|
762
|
+
* If false, then media is transcoded by the server into a single stream.
|
|
763
|
+
*/
|
|
764
|
+
this.isMultistream = false;
|
|
641
765
|
/**
|
|
642
766
|
* Fetching meeting info can be done randomly 2-5 mins before meeting start
|
|
643
767
|
* In case it is done before the timer expires, this timeout id is reset to cancel the timer.
|
|
@@ -656,7 +780,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
656
780
|
* @private
|
|
657
781
|
* @memberof Meeting
|
|
658
782
|
*/
|
|
659
|
-
this.updateMediaConnections = (mediaConnections) => {
|
|
783
|
+
this.updateMediaConnections = (mediaConnections: any[]) => {
|
|
660
784
|
if (!isEqual(this.mediaConnections, mediaConnections)) {
|
|
661
785
|
// grab last/latest item in the new mediaConnections information
|
|
662
786
|
this.mediaConnections = mediaConnections.slice(-1);
|
|
@@ -689,35 +813,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
689
813
|
this.isSharing = false;
|
|
690
814
|
/**
|
|
691
815
|
* @instance
|
|
692
|
-
* @type {
|
|
816
|
+
* @type {string}
|
|
693
817
|
* @readonly
|
|
694
818
|
* @public
|
|
695
819
|
* @memberof Meeting
|
|
696
820
|
*/
|
|
697
821
|
this.shareStatus = SHARE_STATUS.NO_SHARE;
|
|
698
|
-
/**
|
|
699
|
-
* @instance
|
|
700
|
-
* @type {Boolean}
|
|
701
|
-
* @readonly
|
|
702
|
-
* @private
|
|
703
|
-
* @memberof Meeting
|
|
704
|
-
*/
|
|
705
|
-
Object.defineProperty(this, 'isLocalShareLive', {
|
|
706
|
-
get: () => {
|
|
707
|
-
const {shareTransceiver} = this.mediaProperties.peerConnection;
|
|
708
|
-
const shareDirection = shareTransceiver?.direction;
|
|
709
|
-
const trackReadyState = shareTransceiver?.sender?.track?.readyState;
|
|
710
|
-
const activeShare = trackReadyState === LIVE;
|
|
711
|
-
const offersToSendData = shareDirection === SENDRECV;
|
|
712
|
-
|
|
713
|
-
if (activeShare && offersToSendData) {
|
|
714
|
-
return true;
|
|
715
|
-
}
|
|
716
822
|
|
|
717
|
-
return false;
|
|
718
|
-
},
|
|
719
|
-
configurable: true
|
|
720
|
-
});
|
|
721
823
|
/**
|
|
722
824
|
* @instance
|
|
723
825
|
* @type {Array}
|
|
@@ -729,7 +831,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
729
831
|
/**
|
|
730
832
|
* There is a pending floor requested by the user
|
|
731
833
|
* @instance
|
|
732
|
-
* @type {
|
|
834
|
+
* @type {boolean}
|
|
733
835
|
* @private
|
|
734
836
|
* @memberof Meeting
|
|
735
837
|
*/
|
|
@@ -797,6 +899,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
797
899
|
* @private
|
|
798
900
|
* @memberof Meeting
|
|
799
901
|
*/
|
|
902
|
+
// @ts-ignore - Fix type
|
|
800
903
|
this.locusInfo = new LocusInfo(this.updateMeetingObject.bind(this), this.webex, this.id);
|
|
801
904
|
// We had to add listeners first before setting up the locus instance
|
|
802
905
|
/**
|
|
@@ -824,6 +927,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
824
927
|
* @private
|
|
825
928
|
* @memberof Meeting
|
|
826
929
|
*/
|
|
930
|
+
// @ts-ignore - Fix type
|
|
827
931
|
this.hasWebsocketConnected = this.webex.internal.mercury.connected;
|
|
828
932
|
|
|
829
933
|
/**
|
|
@@ -899,6 +1003,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
899
1003
|
this.setUpLocusInfoListeners();
|
|
900
1004
|
this.locusInfo.init(attrs.locus ? attrs.locus : {});
|
|
901
1005
|
this.hasJoinedOnce = false;
|
|
1006
|
+
|
|
1007
|
+
this.media = new MultistreamMedia(this);
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* helper class for managing remote streams
|
|
1011
|
+
*/
|
|
1012
|
+
this.remoteMediaManager = null;
|
|
902
1013
|
}
|
|
903
1014
|
|
|
904
1015
|
/**
|
|
@@ -910,9 +1021,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
910
1021
|
* @memberof Meeting
|
|
911
1022
|
* @returns {Promise}
|
|
912
1023
|
*/
|
|
913
|
-
async fetchMeetingInfo({
|
|
914
|
-
password = null, captchaCode = null
|
|
915
|
-
}) {
|
|
1024
|
+
public async fetchMeetingInfo({ password = null, captchaCode = null }: { password?: string; captchaCode?: string }) {
|
|
916
1025
|
// when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
|
|
917
1026
|
if (this.fetchMeetingInfoTimeoutId) {
|
|
918
1027
|
clearTimeout(this.fetchMeetingInfoTimeoutId);
|
|
@@ -954,6 +1063,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
954
1063
|
}
|
|
955
1064
|
catch (err) {
|
|
956
1065
|
if (err instanceof MeetingInfoV2PasswordError) {
|
|
1066
|
+
// @ts-ignore
|
|
957
1067
|
LoggerProxy.logger.info(`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - password required (code=${err?.body?.code}).`);
|
|
958
1068
|
|
|
959
1069
|
// when wbxappapi requires password it still populates partial meeting info in the response
|
|
@@ -972,6 +1082,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
972
1082
|
throw (new PasswordError());
|
|
973
1083
|
}
|
|
974
1084
|
else if (err instanceof MeetingInfoV2CaptchaError) {
|
|
1085
|
+
// @ts-ignore
|
|
975
1086
|
LoggerProxy.logger.info(`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - captcha required (code=${err?.body?.code}).`);
|
|
976
1087
|
|
|
977
1088
|
this.meetingInfoFailureReason = (this.requiredCaptcha) ?
|
|
@@ -1001,7 +1112,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1001
1112
|
* @memberof Meeting
|
|
1002
1113
|
* @returns {Promise<{isPasswordValid: boolean, requiredCaptcha: boolean, failureReason: MEETING_INFO_FAILURE_REASON}>}
|
|
1003
1114
|
*/
|
|
1004
|
-
verifyPassword(password, captchaCode) {
|
|
1115
|
+
public verifyPassword(password: string, captchaCode: string) {
|
|
1005
1116
|
return this.fetchMeetingInfo({
|
|
1006
1117
|
password, captchaCode
|
|
1007
1118
|
})
|
|
@@ -1031,7 +1142,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1031
1142
|
* @memberof Meeting
|
|
1032
1143
|
* @returns {Promise}
|
|
1033
1144
|
*/
|
|
1034
|
-
refreshCaptcha() {
|
|
1145
|
+
public refreshCaptcha() {
|
|
1035
1146
|
if (!this.requiredCaptcha) {
|
|
1036
1147
|
return Promise.reject(new Error('There is no captcha to refresh'));
|
|
1037
1148
|
}
|
|
@@ -1061,7 +1172,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1061
1172
|
* @private
|
|
1062
1173
|
* @memberof Meeting
|
|
1063
1174
|
*/
|
|
1064
|
-
setUpLocusInfoListeners() {
|
|
1175
|
+
private setUpLocusInfoListeners() {
|
|
1065
1176
|
// meeting update listeners
|
|
1066
1177
|
this.setUpLocusInfoSelfListener();
|
|
1067
1178
|
this.setUpLocusInfoMeetingListener();
|
|
@@ -1079,14 +1190,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1079
1190
|
this.setUpLocusInfoMediaInactiveListener();
|
|
1080
1191
|
}
|
|
1081
1192
|
|
|
1082
|
-
|
|
1083
1193
|
/**
|
|
1084
1194
|
* Set up the locus info listener for meetings disconnected due to inactivity
|
|
1085
1195
|
* @returns {undefined}
|
|
1086
1196
|
* @private
|
|
1087
1197
|
* @memberof Meeting
|
|
1088
1198
|
*/
|
|
1089
|
-
setUpLocusInfoMediaInactiveListener() {
|
|
1199
|
+
private setUpLocusInfoMediaInactiveListener() {
|
|
1090
1200
|
// User gets kicked off the meeting due to inactivity or user did a refresh
|
|
1091
1201
|
this.locusInfo.on(EVENTS.DISCONNECT_DUE_TO_INACTIVITY, (res) => {
|
|
1092
1202
|
// https:// jira-eng-gpk2.cisco.com/jira/browse/SPARK-240520
|
|
@@ -1114,6 +1224,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1114
1224
|
|
|
1115
1225
|
LoggerProxy.logger.error(`Meeting:index#setUpLocusInfoMediaInactiveListener --> Meeting disconnected due to inactivity: ${res.reason}`);
|
|
1116
1226
|
|
|
1227
|
+
// @ts-ignore - config coming from registerPlugin
|
|
1117
1228
|
if (this.config.reconnection.autoRejoin) {
|
|
1118
1229
|
this.reconnect();
|
|
1119
1230
|
}
|
|
@@ -1137,7 +1248,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1137
1248
|
* @private
|
|
1138
1249
|
* @memberof Meeting
|
|
1139
1250
|
*/
|
|
1140
|
-
setUpLocusInfoAssignHostListener() {
|
|
1251
|
+
private setUpLocusInfoAssignHostListener() {
|
|
1141
1252
|
this.locusInfo.on(EVENTS.LOCUS_INFO_CAN_ASSIGN_HOST, (payload) => {
|
|
1142
1253
|
const changed = this.inMeetingActions.set({
|
|
1143
1254
|
canAssignHost: payload.canAssignHost,
|
|
@@ -1163,7 +1274,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1163
1274
|
* @private
|
|
1164
1275
|
* @memberof Meeting
|
|
1165
1276
|
*/
|
|
1166
|
-
setUpLocusFullStateListener() {
|
|
1277
|
+
private setUpLocusFullStateListener() {
|
|
1167
1278
|
this.locusInfo.on(LOCUSINFO.EVENTS.FULL_STATE_MEETING_STATE_CHANGE, (payload) => {
|
|
1168
1279
|
Trigger.trigger(
|
|
1169
1280
|
this,
|
|
@@ -1194,7 +1305,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1194
1305
|
* @returns {Object}
|
|
1195
1306
|
* @memberof Meeting
|
|
1196
1307
|
*/
|
|
1197
|
-
getAnalyzerMetricsPrePayload(options
|
|
1308
|
+
getAnalyzerMetricsPrePayload(options: {
|
|
1309
|
+
event: string;
|
|
1310
|
+
trackingId: string;
|
|
1311
|
+
locus: object;
|
|
1312
|
+
mediaConnections: Array<any>;
|
|
1313
|
+
errors: object;
|
|
1314
|
+
} | any) {
|
|
1198
1315
|
if (options) {
|
|
1199
1316
|
const {
|
|
1200
1317
|
event,
|
|
@@ -1208,11 +1325,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1208
1325
|
return null;
|
|
1209
1326
|
}
|
|
1210
1327
|
|
|
1211
|
-
const identifiers = {
|
|
1328
|
+
const identifiers: any = {
|
|
1212
1329
|
correlationId: this.correlationId,
|
|
1213
1330
|
userId: this.userId,
|
|
1214
1331
|
deviceId: this.deviceUrl,
|
|
1215
1332
|
orgId: this.orgId,
|
|
1333
|
+
// @ts-ignore fix type
|
|
1216
1334
|
locusUrl: this.webex.internal.services.get('locus')
|
|
1217
1335
|
};
|
|
1218
1336
|
|
|
@@ -1334,12 +1452,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1334
1452
|
* @private
|
|
1335
1453
|
* @memberof Meeting
|
|
1336
1454
|
*/
|
|
1337
|
-
sendCallAnalyzerMetrics(options) {
|
|
1455
|
+
private sendCallAnalyzerMetrics(options: { event: string; trackingId: string; locus: object; errors: object }) {
|
|
1338
1456
|
const payload = this.getAnalyzerMetricsPrePayload({
|
|
1457
|
+
// @ts-ignore - config coming from registerPlugin
|
|
1339
1458
|
...pick(this.config.metrics, ['clientType', 'subClientType']),
|
|
1340
1459
|
...options
|
|
1341
1460
|
});
|
|
1342
1461
|
|
|
1462
|
+
// @ts-ignore - fix type
|
|
1343
1463
|
return this.webex.internal.metrics.submitCallDiagnosticEvents(payload);
|
|
1344
1464
|
}
|
|
1345
1465
|
|
|
@@ -1353,13 +1473,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1353
1473
|
* @private
|
|
1354
1474
|
* @memberof Meeting
|
|
1355
1475
|
*/
|
|
1356
|
-
sendMediaQualityAnalyzerMetrics(options) {
|
|
1476
|
+
private sendMediaQualityAnalyzerMetrics(options: { event: string; trackingId: string; locus: object }) {
|
|
1357
1477
|
const payload = this.getAnalyzerMetricsPrePayload({
|
|
1358
1478
|
type: MQA_STATS.CA_TYPE,
|
|
1479
|
+
// @ts-ignore - config coming from registerPlugin
|
|
1359
1480
|
...pick(this.config.metrics, ['clientType', 'subClientType']),
|
|
1360
1481
|
...options
|
|
1361
1482
|
});
|
|
1362
1483
|
|
|
1484
|
+
// @ts-ignore
|
|
1363
1485
|
return this.webex.internal.metrics.submitCallDiagnosticEvents(payload);
|
|
1364
1486
|
}
|
|
1365
1487
|
|
|
@@ -1370,7 +1492,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1370
1492
|
* @returns {undefined}
|
|
1371
1493
|
* @memberof Meeting
|
|
1372
1494
|
*/
|
|
1373
|
-
setNetworkStatus(networkStatus) {
|
|
1495
|
+
private setNetworkStatus(networkStatus: string) {
|
|
1374
1496
|
if (networkStatus === NETWORK_STATUS.DISCONNECTED) {
|
|
1375
1497
|
Trigger.trigger(
|
|
1376
1498
|
this,
|
|
@@ -1403,14 +1525,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1403
1525
|
* @private
|
|
1404
1526
|
* @memberof Meeting
|
|
1405
1527
|
*/
|
|
1406
|
-
setUpLocusSelfListener() {
|
|
1528
|
+
private setUpLocusSelfListener() {
|
|
1407
1529
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_SELF, (payload) => {
|
|
1408
1530
|
this.members.locusSelfUpdate(payload);
|
|
1409
1531
|
this.pstnUpdate(payload);
|
|
1410
1532
|
|
|
1411
1533
|
// If user moved to a JOINED state and there is a pending floor grant trigger it
|
|
1412
1534
|
if (this.floorGrantPending && payload.newSelf.state === MEETING_STATE.STATES.JOINED) {
|
|
1413
|
-
this.
|
|
1535
|
+
this.requestScreenShareFloor()
|
|
1414
1536
|
.then(() => { this.floorGrantPending = false; });
|
|
1415
1537
|
}
|
|
1416
1538
|
});
|
|
@@ -1423,7 +1545,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1423
1545
|
* @private
|
|
1424
1546
|
* @memberof Meeting
|
|
1425
1547
|
*/
|
|
1426
|
-
pstnUpdate(payload) {
|
|
1548
|
+
private pstnUpdate(payload: any) {
|
|
1427
1549
|
if (this.locusInfo.self) {
|
|
1428
1550
|
const dialInPstnDevice = payload.newSelf?.pstnDevices.find((device) => device.url === this.dialInUrl);
|
|
1429
1551
|
const dialOutPstnDevice = payload.newSelf?.pstnDevices.find((device) => device.url === this.dialOutUrl);
|
|
@@ -1478,7 +1600,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1478
1600
|
* @private
|
|
1479
1601
|
* @memberof Meeting
|
|
1480
1602
|
*/
|
|
1481
|
-
setUpLocusHostListener() {
|
|
1603
|
+
private setUpLocusHostListener() {
|
|
1482
1604
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_HOST, (payload) => {
|
|
1483
1605
|
this.members.locusHostUpdate(payload);
|
|
1484
1606
|
});
|
|
@@ -1492,13 +1614,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1492
1614
|
* @private
|
|
1493
1615
|
* @memberof Meeting
|
|
1494
1616
|
*/
|
|
1495
|
-
setUpLocusParticipantsListener() {
|
|
1617
|
+
private setUpLocusParticipantsListener() {
|
|
1496
1618
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS, (payload) => {
|
|
1497
1619
|
this.members.locusParticipantsUpdate(payload);
|
|
1498
1620
|
});
|
|
1499
1621
|
}
|
|
1500
1622
|
|
|
1501
|
-
|
|
1502
1623
|
/**
|
|
1503
1624
|
* Set up the locus info recording update listener
|
|
1504
1625
|
* update recording value for the meeting
|
|
@@ -1517,7 +1638,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1517
1638
|
* @private
|
|
1518
1639
|
* @memberof Meeting
|
|
1519
1640
|
*/
|
|
1520
|
-
|
|
1641
|
+
private setupLocusControlsListener() {
|
|
1521
1642
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_RECORDING_UPDATED,
|
|
1522
1643
|
({state, modifiedBy, lastModified}) => {
|
|
1523
1644
|
let event;
|
|
@@ -1573,6 +1694,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1573
1694
|
|
|
1574
1695
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIBE_UPDATED,
|
|
1575
1696
|
({caption, transcribing}) => {
|
|
1697
|
+
|
|
1698
|
+
// @ts-ignore - config coming from registerPlugin
|
|
1576
1699
|
if (transcribing && this.transcription && this.config.receiveTranscription) {
|
|
1577
1700
|
this.receiveTranscription();
|
|
1578
1701
|
}
|
|
@@ -1611,7 +1734,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1611
1734
|
* @private
|
|
1612
1735
|
* @memberof Meeting
|
|
1613
1736
|
*/
|
|
1614
|
-
|
|
1737
|
+
private setUpLocusMediaSharesListener() {
|
|
1615
1738
|
// Will get triggered on local and remote share
|
|
1616
1739
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, (payload) => {
|
|
1617
1740
|
const {content: contentShare, whiteboard: whiteboardShare} = payload.current;
|
|
@@ -1699,7 +1822,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1699
1822
|
this,
|
|
1700
1823
|
{
|
|
1701
1824
|
file: 'meeting/index',
|
|
1702
|
-
function: '
|
|
1825
|
+
function: 'localShare'
|
|
1703
1826
|
},
|
|
1704
1827
|
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
|
|
1705
1828
|
{
|
|
@@ -1842,7 +1965,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1842
1965
|
* @private
|
|
1843
1966
|
* @memberof Meeting
|
|
1844
1967
|
*/
|
|
1845
|
-
setUpLocusUrlListener() {
|
|
1968
|
+
private setUpLocusUrlListener() {
|
|
1846
1969
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_URL, (payload) => {
|
|
1847
1970
|
this.members.locusUrlUpdate(payload);
|
|
1848
1971
|
this.locusUrl = payload;
|
|
@@ -1856,7 +1979,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1856
1979
|
* @private
|
|
1857
1980
|
* @memberof meeting
|
|
1858
1981
|
*/
|
|
1859
|
-
setUpLocusInfoMeetingInfoListener() {
|
|
1982
|
+
private setUpLocusInfoMeetingInfoListener() {
|
|
1860
1983
|
this.locusInfo.on(LOCUSINFO.EVENTS.MEETING_LOCKED, (payload) => {
|
|
1861
1984
|
if (payload) {
|
|
1862
1985
|
Trigger.trigger(
|
|
@@ -1924,17 +2047,34 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1924
2047
|
this.inMeetingActions.get()
|
|
1925
2048
|
);
|
|
1926
2049
|
}
|
|
2050
|
+
|
|
2051
|
+
this.handleDataChannelUrlChange(payload.info.datachannelUrl);
|
|
1927
2052
|
}
|
|
1928
2053
|
});
|
|
1929
2054
|
}
|
|
1930
2055
|
|
|
2056
|
+
/**
|
|
2057
|
+
* Handles a data channel URL change
|
|
2058
|
+
* @param {String} datachannelUrl
|
|
2059
|
+
* @returns {void}
|
|
2060
|
+
*/
|
|
2061
|
+
handleDataChannelUrlChange(datachannelUrl) {
|
|
2062
|
+
if (datachannelUrl && this.config.enableAutomaticLLM) {
|
|
2063
|
+
// Defer this as updateLLMConnection relies upon this.locusInfo.url which is only set
|
|
2064
|
+
// after the MEETING_INFO_UPDATED callback finishes
|
|
2065
|
+
defer(() => {
|
|
2066
|
+
this.updateLLMConnection();
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
|
|
1931
2071
|
/**
|
|
1932
2072
|
* Set up the locus info embedded apps listener
|
|
1933
2073
|
* @returns {undefined}
|
|
1934
2074
|
* @private
|
|
1935
2075
|
* @memberof meeting
|
|
1936
2076
|
*/
|
|
1937
|
-
setUpLocusEmbeddedAppsListener() {
|
|
2077
|
+
private setUpLocusEmbeddedAppsListener() {
|
|
1938
2078
|
this.locusInfo.on(LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED, (embeddedApps) => {
|
|
1939
2079
|
if (embeddedApps) {
|
|
1940
2080
|
Trigger.trigger(
|
|
@@ -1956,7 +2096,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1956
2096
|
* @private
|
|
1957
2097
|
* @memberof Meeting
|
|
1958
2098
|
*/
|
|
1959
|
-
setUpLocusInfoSelfListener() {
|
|
2099
|
+
private setUpLocusInfoSelfListener() {
|
|
1960
2100
|
this.locusInfo.on(LOCUSINFO.EVENTS.LOCAL_UNMUTE_REQUIRED, (payload) => {
|
|
1961
2101
|
if (this.audio) {
|
|
1962
2102
|
this.audio.handleServerLocalUnmuteRequired(this);
|
|
@@ -2054,6 +2194,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2054
2194
|
}
|
|
2055
2195
|
});
|
|
2056
2196
|
|
|
2197
|
+
// @ts-ignore - check if MEDIA_INACTIVITY exists
|
|
2057
2198
|
this.locusInfo.on(LOCUSINFO.EVENTS.MEDIA_INACTIVITY, () => {
|
|
2058
2199
|
Metrics.sendBehavioralMetric(
|
|
2059
2200
|
BEHAVIORAL_METRICS.MEETING_MEDIA_INACTIVE,
|
|
@@ -2120,7 +2261,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2120
2261
|
* @private
|
|
2121
2262
|
* @memberof Meeting
|
|
2122
2263
|
*/
|
|
2123
|
-
setUpLocusInfoMeetingListener() {
|
|
2264
|
+
private setUpLocusInfoMeetingListener() {
|
|
2124
2265
|
this.locusInfo.on(EVENTS.REMOTE_RESPONSE, (payload) => {
|
|
2125
2266
|
this.meetingFiniteStateMachine.remote(payload);
|
|
2126
2267
|
|
|
@@ -2128,6 +2269,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2128
2269
|
this.leave({reason: payload.reason}).then(() => {
|
|
2129
2270
|
LoggerProxy.logger.info('Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Attempting to leave meeting.');
|
|
2130
2271
|
}).catch((error) => {
|
|
2272
|
+
// @ts-ignore
|
|
2131
2273
|
LoggerProxy.logger.error(`Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Issue with leave for meeting, meeting still in collection: ${this.meeting}, error: ${error}`);
|
|
2132
2274
|
});
|
|
2133
2275
|
}
|
|
@@ -2156,6 +2298,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2156
2298
|
this.leave({reason: payload.reason}).then(() => {
|
|
2157
2299
|
LoggerProxy.logger.warn('Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. The meeting has been left, but has not been destroyed, you should see a later event for leave.');
|
|
2158
2300
|
}).catch((error) => {
|
|
2301
|
+
// @ts-ignore
|
|
2159
2302
|
LoggerProxy.logger.error(`Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. Issue with leave for meeting, meeting still in collection: ${this.meeting}, error: ${error}`);
|
|
2160
2303
|
});
|
|
2161
2304
|
}
|
|
@@ -2187,7 +2330,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2187
2330
|
* @memberof Meeting
|
|
2188
2331
|
* // TODO: is this function necessary?
|
|
2189
2332
|
*/
|
|
2190
|
-
updateMeetingObject(object) {
|
|
2333
|
+
private updateMeetingObject(object: object) {
|
|
2191
2334
|
// Validate if these are valid meeting object property
|
|
2192
2335
|
// TODO: add a check to make sure the value passed in the constructor
|
|
2193
2336
|
// is not changed by any delta event
|
|
@@ -2209,7 +2352,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2209
2352
|
* @public
|
|
2210
2353
|
* @memberof Meeting
|
|
2211
2354
|
*/
|
|
2212
|
-
invite(
|
|
2355
|
+
public invite(
|
|
2356
|
+
invitee: {
|
|
2357
|
+
emailAddress: string;
|
|
2358
|
+
email: string;
|
|
2359
|
+
phoneNumber: string;
|
|
2360
|
+
},
|
|
2361
|
+
alertIfActive: boolean = true
|
|
2362
|
+
) {
|
|
2213
2363
|
return this.members.addMember(invitee, alertIfActive);
|
|
2214
2364
|
}
|
|
2215
2365
|
|
|
@@ -2221,7 +2371,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2221
2371
|
* @public
|
|
2222
2372
|
* @memberof Meeting
|
|
2223
2373
|
*/
|
|
2224
|
-
cancelPhoneInvite(invitee) {
|
|
2374
|
+
public cancelPhoneInvite(invitee: { phoneNumber: string }) {
|
|
2225
2375
|
return this.members.cancelPhoneInvite(invitee);
|
|
2226
2376
|
}
|
|
2227
2377
|
|
|
@@ -2232,7 +2382,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2232
2382
|
* @public
|
|
2233
2383
|
* @memberof Meeting
|
|
2234
2384
|
*/
|
|
2235
|
-
admit(memberIds) {
|
|
2385
|
+
public admit(memberIds: Array<any>) {
|
|
2236
2386
|
return this.members.admitMembers(memberIds);
|
|
2237
2387
|
}
|
|
2238
2388
|
|
|
@@ -2243,7 +2393,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2243
2393
|
* @public
|
|
2244
2394
|
* @memberof Meeting
|
|
2245
2395
|
*/
|
|
2246
|
-
remove(memberId) {
|
|
2396
|
+
public remove(memberId: string) {
|
|
2247
2397
|
return this.members.removeMember(memberId);
|
|
2248
2398
|
}
|
|
2249
2399
|
|
|
@@ -2255,7 +2405,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2255
2405
|
* @public
|
|
2256
2406
|
* @memberof Meeting
|
|
2257
2407
|
*/
|
|
2258
|
-
mute(memberId, mute = true) {
|
|
2408
|
+
public mute(memberId: string, mute: boolean = true) {
|
|
2259
2409
|
return this.members.muteMember(memberId, mute);
|
|
2260
2410
|
}
|
|
2261
2411
|
|
|
@@ -2267,7 +2417,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2267
2417
|
* @public
|
|
2268
2418
|
* @memberof Meeting
|
|
2269
2419
|
*/
|
|
2270
|
-
transfer(memberId, moderator = true) {
|
|
2420
|
+
public transfer(memberId: string, moderator: boolean = true) {
|
|
2271
2421
|
return this.members.transferHostToMember(memberId, moderator);
|
|
2272
2422
|
}
|
|
2273
2423
|
|
|
@@ -2277,7 +2427,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2277
2427
|
* @public
|
|
2278
2428
|
* @memberof Meeting
|
|
2279
2429
|
*/
|
|
2280
|
-
getMembers() {
|
|
2430
|
+
public getMembers() {
|
|
2281
2431
|
return this.members;
|
|
2282
2432
|
}
|
|
2283
2433
|
|
|
@@ -2287,7 +2437,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2287
2437
|
* @public
|
|
2288
2438
|
* @memberof Meeting
|
|
2289
2439
|
*/
|
|
2290
|
-
isAudioConnected() {
|
|
2440
|
+
public isAudioConnected() {
|
|
2291
2441
|
return !!this.audio;
|
|
2292
2442
|
}
|
|
2293
2443
|
|
|
@@ -2297,7 +2447,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2297
2447
|
* @public
|
|
2298
2448
|
* @memberof Meeting
|
|
2299
2449
|
*/
|
|
2300
|
-
isAudioMuted() {
|
|
2450
|
+
public isAudioMuted() {
|
|
2301
2451
|
return this.audio && this.audio.isMuted();
|
|
2302
2452
|
}
|
|
2303
2453
|
|
|
@@ -2307,7 +2457,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2307
2457
|
* @public
|
|
2308
2458
|
* @memberof Meeting
|
|
2309
2459
|
*/
|
|
2310
|
-
isAudioSelf() {
|
|
2460
|
+
public isAudioSelf() {
|
|
2311
2461
|
return this.audio && this.audio.isSelf();
|
|
2312
2462
|
}
|
|
2313
2463
|
|
|
@@ -2317,7 +2467,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2317
2467
|
* @public
|
|
2318
2468
|
* @memberof Meeting
|
|
2319
2469
|
*/
|
|
2320
|
-
isVideoConnected() {
|
|
2470
|
+
public isVideoConnected() {
|
|
2321
2471
|
return !!this.video;
|
|
2322
2472
|
}
|
|
2323
2473
|
|
|
@@ -2327,7 +2477,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2327
2477
|
* @public
|
|
2328
2478
|
* @memberof Meeting
|
|
2329
2479
|
*/
|
|
2330
|
-
isVideoMuted() {
|
|
2480
|
+
public isVideoMuted() {
|
|
2331
2481
|
return this.video && this.video.isMuted();
|
|
2332
2482
|
}
|
|
2333
2483
|
|
|
@@ -2337,7 +2487,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2337
2487
|
* @public
|
|
2338
2488
|
* @memberof Meeting
|
|
2339
2489
|
*/
|
|
2340
|
-
isVideoSelf() {
|
|
2490
|
+
public isVideoSelf() {
|
|
2341
2491
|
return this.video && this.video.isSelf();
|
|
2342
2492
|
}
|
|
2343
2493
|
|
|
@@ -2354,7 +2504,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2354
2504
|
* @private
|
|
2355
2505
|
* @memberof Meeting
|
|
2356
2506
|
*/
|
|
2357
|
-
parseMeetingInfo(
|
|
2507
|
+
parseMeetingInfo(
|
|
2508
|
+
meetingInfo: {
|
|
2509
|
+
body: {
|
|
2510
|
+
conversationUrl: string;
|
|
2511
|
+
locusUrl: string;
|
|
2512
|
+
sipUri: string;
|
|
2513
|
+
owner: object;
|
|
2514
|
+
};
|
|
2515
|
+
} | any,
|
|
2516
|
+
destination: object | string | null = null
|
|
2517
|
+
) {
|
|
2358
2518
|
const webexMeetingInfo = meetingInfo?.body;
|
|
2359
2519
|
// We try to use as much info from Locus meeting object, stored in destination
|
|
2360
2520
|
|
|
@@ -2368,7 +2528,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2368
2528
|
if (locusMeetingObject || (webexMeetingInfo && !(meetingInfo?.errors && meetingInfo?.errors.length > 0))) {
|
|
2369
2529
|
this.conversationUrl = locusMeetingObject?.conversationUrl || webexMeetingInfo?.conversationUrl || this.conversationUrl;
|
|
2370
2530
|
this.locusUrl = locusMeetingObject?.url || webexMeetingInfo?.locusUrl || this.locusUrl;
|
|
2531
|
+
// @ts-ignore - config coming from registerPlugin
|
|
2371
2532
|
this.setSipUri(this.config.experimental.enableUnifiedMeetings ? locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipUrl : locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipMeetingUri || this.sipUri);
|
|
2533
|
+
// @ts-ignore - config coming from registerPlugin
|
|
2372
2534
|
if (this.config.experimental.enableUnifiedMeetings) {
|
|
2373
2535
|
this.meetingNumber = locusMeetingObject?.info.webExMeetingId || webexMeetingInfo?.meetingNumber;
|
|
2374
2536
|
this.meetingJoinUrl = webexMeetingInfo?.meetingJoinUrl;
|
|
@@ -2388,7 +2550,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2388
2550
|
* @private
|
|
2389
2551
|
* @memberof Meeting
|
|
2390
2552
|
*/
|
|
2391
|
-
parseLocus(locus) {
|
|
2553
|
+
private parseLocus(locus: { url: string; participants: Array<any>; self: object }) {
|
|
2392
2554
|
if (locus) {
|
|
2393
2555
|
this.locusUrl = locus.url;
|
|
2394
2556
|
// TODO: move this to parse participants module
|
|
@@ -2415,24 +2577,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2415
2577
|
* @private
|
|
2416
2578
|
* @memberof Meeting
|
|
2417
2579
|
*/
|
|
2418
|
-
setSipUri(sipUri) {
|
|
2580
|
+
setSipUri(sipUri: string) {
|
|
2419
2581
|
// This can be tel no, device id or a sip uri, user Id
|
|
2420
2582
|
this.sipUri = sipUri;
|
|
2421
2583
|
}
|
|
2422
2584
|
|
|
2423
|
-
/**
|
|
2424
|
-
* Set the roap seq on the class instance
|
|
2425
|
-
* @param {Number} seq
|
|
2426
|
-
* @returns {undefined}
|
|
2427
|
-
* @private
|
|
2428
|
-
* @memberof Meeting
|
|
2429
|
-
*/
|
|
2430
|
-
setRoapSeq(seq) {
|
|
2431
|
-
if (seq >= 0) {
|
|
2432
|
-
this.roapSeq = seq;
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
|
|
2436
2585
|
/**
|
|
2437
2586
|
* Set the locus info the class instance
|
|
2438
2587
|
* @param {Object} locus
|
|
@@ -2446,8 +2595,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2446
2595
|
* @private
|
|
2447
2596
|
* @memberof Meeting
|
|
2448
2597
|
*/
|
|
2449
|
-
setLocus(locus
|
|
2450
|
-
|
|
2598
|
+
private setLocus(locus: {
|
|
2599
|
+
mediaConnections: Array<any>;
|
|
2600
|
+
locusUrl: string;
|
|
2601
|
+
locusId: string;
|
|
2602
|
+
mediaId: string;
|
|
2603
|
+
host: object;
|
|
2604
|
+
} | any) {
|
|
2605
|
+
const mtgLocus: any = locus.locus || locus;
|
|
2451
2606
|
|
|
2452
2607
|
// LocusInfo object saves the locus object
|
|
2453
2608
|
// this.locus = mtgLocus;
|
|
@@ -2460,111 +2615,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2460
2615
|
this.locusInfo.initialSetup(mtgLocus);
|
|
2461
2616
|
}
|
|
2462
2617
|
|
|
2463
|
-
/**
|
|
2464
|
-
* Sets the remote stream on the class instance and emits and
|
|
2465
|
-
* event to developers
|
|
2466
|
-
* @param {Object} pc The remote stream peer connection
|
|
2467
|
-
* @returns {undefined}
|
|
2468
|
-
* @public
|
|
2469
|
-
* @memberof Meeting
|
|
2470
|
-
*/
|
|
2471
|
-
setRemoteStream(pc) {
|
|
2472
|
-
if (!pc) {
|
|
2473
|
-
return;
|
|
2474
|
-
}
|
|
2475
|
-
// eslint-disable-next-line no-param-reassign
|
|
2476
|
-
pc.ontrack = (event) => {
|
|
2477
|
-
// eslint-disable-next-line no-warning-comments
|
|
2478
|
-
// TODO: It's possible for media to not be present
|
|
2479
|
-
// so we might need to either
|
|
2480
|
-
// A) wait until we have media flowing
|
|
2481
|
-
// B) trigger a second event when video is flowing
|
|
2482
|
-
LoggerProxy.logger.log(`Meeting:index#setRemoteStream --> ontrack event received for peerConnection: ${event}`);
|
|
2483
|
-
|
|
2484
|
-
const MEDIA_ID = {
|
|
2485
|
-
AUDIO_TRACK: '0',
|
|
2486
|
-
VIDEO_TRACK: '1',
|
|
2487
|
-
SHARE_TRACK: '2'
|
|
2488
|
-
};
|
|
2489
|
-
let eventType = null;
|
|
2490
|
-
const mediaTrack = event.track;
|
|
2491
|
-
let trackMediaID = null;
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
// In case of safari some time the transceiver is not present for specific os version
|
|
2495
|
-
// sdk tries to determine the transceive using the track id present
|
|
2496
|
-
if (event.transceiver && event.transceiver.mid) {
|
|
2497
|
-
trackMediaID = event.transceiver.mid;
|
|
2498
|
-
}
|
|
2499
|
-
else {
|
|
2500
|
-
const {audioTransceiver, videoTransceiver, shareTransceiver} = event.target;
|
|
2501
|
-
|
|
2502
|
-
// audio kind indicates its a audio stream
|
|
2503
|
-
if (mediaTrack.id === audioTransceiver.receiver.track.id) {
|
|
2504
|
-
trackMediaID = '0';
|
|
2505
|
-
}
|
|
2506
|
-
else
|
|
2507
|
-
if (mediaTrack.id === videoTransceiver.receiver.track.id) {
|
|
2508
|
-
trackMediaID = '1';
|
|
2509
|
-
}
|
|
2510
|
-
else
|
|
2511
|
-
if (mediaTrack.id === shareTransceiver.receiver.track.id) {
|
|
2512
|
-
trackMediaID = '2';
|
|
2513
|
-
}
|
|
2514
|
-
else {
|
|
2515
|
-
trackMediaID = null;
|
|
2516
|
-
Metrics.sendBehavioralMetric(
|
|
2517
|
-
BEHAVIORAL_METRICS.MUTE_AUDIO_FAILURE,
|
|
2518
|
-
{
|
|
2519
|
-
correlation_id: this.correlationId,
|
|
2520
|
-
locus_id: this.locusUrl.split('/').pop()
|
|
2521
|
-
}
|
|
2522
|
-
);
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
switch (trackMediaID) {
|
|
2528
|
-
case MEDIA_ID.AUDIO_TRACK:
|
|
2529
|
-
eventType = EVENT_TYPES.REMOTE_AUDIO;
|
|
2530
|
-
this.mediaProperties.setRemoteAudioTrack(mediaTrack);
|
|
2531
|
-
break;
|
|
2532
|
-
case MEDIA_ID.VIDEO_TRACK:
|
|
2533
|
-
eventType = EVENT_TYPES.REMOTE_VIDEO;
|
|
2534
|
-
this.mediaProperties.setRemoteVideoTrack(mediaTrack);
|
|
2535
|
-
break;
|
|
2536
|
-
case MEDIA_ID.SHARE_TRACK:
|
|
2537
|
-
if (event.track) {
|
|
2538
|
-
eventType = EVENT_TYPES.REMOTE_SHARE;
|
|
2539
|
-
this.mediaProperties.setRemoteShare(mediaTrack);
|
|
2540
|
-
}
|
|
2541
|
-
break;
|
|
2542
|
-
default: {
|
|
2543
|
-
LoggerProxy.logger.log('Meeting:index#setRemoteStream --> no matching media track id');
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
// start stats here the stats are coming null if you dont receive streams
|
|
2548
|
-
|
|
2549
|
-
this.statsAnalyzer.startAnalyzer(this.mediaProperties.peerConnection);
|
|
2550
|
-
|
|
2551
|
-
if (eventType && mediaTrack) {
|
|
2552
|
-
Trigger.trigger(
|
|
2553
|
-
this,
|
|
2554
|
-
{
|
|
2555
|
-
file: 'meeting/index',
|
|
2556
|
-
function: 'setRemoteStream:pc.ontrack'
|
|
2557
|
-
},
|
|
2558
|
-
EVENT_TRIGGERS.MEDIA_READY,
|
|
2559
|
-
{
|
|
2560
|
-
type: eventType,
|
|
2561
|
-
stream: MediaUtil.createMediaStream([mediaTrack])
|
|
2562
|
-
}
|
|
2563
|
-
);
|
|
2564
|
-
}
|
|
2565
|
-
};
|
|
2566
|
-
}
|
|
2567
|
-
|
|
2568
2618
|
/**
|
|
2569
2619
|
* Upload logs for the current meeting
|
|
2570
2620
|
* @param {object} options file name and function name
|
|
@@ -2572,7 +2622,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2572
2622
|
* @public
|
|
2573
2623
|
* @memberof Meeting
|
|
2574
2624
|
*/
|
|
2575
|
-
uploadLogs(options = {file: 'meeting/index', function: 'uploadLogs'}) {
|
|
2625
|
+
public uploadLogs(options: object = { file: 'meeting/index', function: 'uploadLogs' }) {
|
|
2576
2626
|
Trigger.trigger(
|
|
2577
2627
|
this,
|
|
2578
2628
|
options,
|
|
@@ -2581,7 +2631,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2581
2631
|
);
|
|
2582
2632
|
}
|
|
2583
2633
|
|
|
2584
|
-
|
|
2585
2634
|
/**
|
|
2586
2635
|
* Removes remote audio and video stream on the class instance and triggers an event
|
|
2587
2636
|
* to developers
|
|
@@ -2590,7 +2639,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2590
2639
|
* @memberof Meeting
|
|
2591
2640
|
* @deprecated after v1.89.3
|
|
2592
2641
|
*/
|
|
2593
|
-
unsetRemoteStream() {
|
|
2642
|
+
public unsetRemoteStream() {
|
|
2594
2643
|
LoggerProxy.logger.warn('Meeting:index#unsetRemoteStream --> [DEPRECATION WARNING]: unsetRemoteStream has been deprecated after v1.89.3');
|
|
2595
2644
|
this.mediaProperties.unsetRemoteMedia();
|
|
2596
2645
|
}
|
|
@@ -2611,7 +2660,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2611
2660
|
* @memberof Meeting
|
|
2612
2661
|
* @deprecated after v1.89.3
|
|
2613
2662
|
*/
|
|
2614
|
-
closeRemoteStream() {
|
|
2663
|
+
public closeRemoteStream() {
|
|
2615
2664
|
LoggerProxy.logger.warn('Meeting:index#closeRemoteStream --> [DEPRECATION WARNING]: closeRemoteStream has been deprecated after v1.89.3');
|
|
2616
2665
|
this.closeRemoteTracks();
|
|
2617
2666
|
}
|
|
@@ -2635,7 +2684,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2635
2684
|
* @returns {void}
|
|
2636
2685
|
* @inner
|
|
2637
2686
|
*/
|
|
2638
|
-
const triggerMediaStoppedEvent = (mediaType) => {
|
|
2687
|
+
const triggerMediaStoppedEvent = (mediaType: string) => {
|
|
2639
2688
|
Trigger.trigger(
|
|
2640
2689
|
this,
|
|
2641
2690
|
{
|
|
@@ -2657,7 +2706,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2657
2706
|
* @inner
|
|
2658
2707
|
*/
|
|
2659
2708
|
// eslint-disable-next-line arrow-body-style
|
|
2660
|
-
const stopTrack = (track, type) => {
|
|
2709
|
+
const stopTrack = (track: MediaStreamTrack, type: string) => {
|
|
2661
2710
|
return Media.stopTracks(track)
|
|
2662
2711
|
.then(() => {
|
|
2663
2712
|
const isTrackStopped = track && track.readyState === ENDED;
|
|
@@ -2685,7 +2734,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2685
2734
|
* @private
|
|
2686
2735
|
* @memberof Meeting
|
|
2687
2736
|
*/
|
|
2688
|
-
sendLocalMediaReadyEvent() {
|
|
2737
|
+
private sendLocalMediaReadyEvent() {
|
|
2689
2738
|
Trigger.trigger(
|
|
2690
2739
|
this,
|
|
2691
2740
|
{
|
|
@@ -2708,7 +2757,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2708
2757
|
* @private
|
|
2709
2758
|
* @memberof Meeting
|
|
2710
2759
|
*/
|
|
2711
|
-
setLocalAudioTrack(audioTrack, emitEvent = true) {
|
|
2760
|
+
private setLocalAudioTrack(audioTrack: MediaStreamTrack, emitEvent: boolean = true) {
|
|
2712
2761
|
if (audioTrack) {
|
|
2713
2762
|
const settings = audioTrack.getSettings();
|
|
2714
2763
|
|
|
@@ -2735,7 +2784,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2735
2784
|
* @private
|
|
2736
2785
|
* @memberof Meeting
|
|
2737
2786
|
*/
|
|
2738
|
-
setLocalVideoTrack(videoTrack, emitEvent = true) {
|
|
2787
|
+
private setLocalVideoTrack(videoTrack: MediaStreamTrack, emitEvent: boolean = true) {
|
|
2739
2788
|
if (videoTrack) {
|
|
2740
2789
|
const {
|
|
2741
2790
|
aspectRatio, frameRate, height, width, deviceId
|
|
@@ -2775,7 +2824,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2775
2824
|
* @public
|
|
2776
2825
|
* @memberof Meeting
|
|
2777
2826
|
*/
|
|
2778
|
-
setLocalTracks(localStream) {
|
|
2827
|
+
public setLocalTracks(localStream: any) {
|
|
2779
2828
|
if (localStream) {
|
|
2780
2829
|
const {audioTrack, videoTrack} = MeetingUtil.getTrack(localStream);
|
|
2781
2830
|
|
|
@@ -2788,12 +2837,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2788
2837
|
|
|
2789
2838
|
/**
|
|
2790
2839
|
* Sets the local media stream on the class and emits an event to the developer
|
|
2791
|
-
* @param {
|
|
2840
|
+
* @param {MediaStream} localShare the local media stream
|
|
2792
2841
|
* @returns {undefined}
|
|
2793
2842
|
* @public
|
|
2794
2843
|
* @memberof Meeting
|
|
2795
2844
|
*/
|
|
2796
|
-
setLocalShareTrack(localShare) {
|
|
2845
|
+
public setLocalShareTrack(localShare: MediaStream) {
|
|
2797
2846
|
let settings = null;
|
|
2798
2847
|
|
|
2799
2848
|
if (localShare) {
|
|
@@ -2837,7 +2886,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2837
2886
|
* @public
|
|
2838
2887
|
* @memberof Meeting
|
|
2839
2888
|
*/
|
|
2840
|
-
closeLocalStream() {
|
|
2889
|
+
public closeLocalStream() {
|
|
2841
2890
|
const {audioTrack, videoTrack} = this.mediaProperties;
|
|
2842
2891
|
|
|
2843
2892
|
return Media.stopTracks(audioTrack)
|
|
@@ -2872,7 +2921,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2872
2921
|
* @public
|
|
2873
2922
|
* @memberof Meeting
|
|
2874
2923
|
*/
|
|
2875
|
-
closeLocalShare() {
|
|
2924
|
+
public closeLocalShare() {
|
|
2876
2925
|
const track = this.mediaProperties.shareTrack;
|
|
2877
2926
|
|
|
2878
2927
|
return Media.stopTracks(track).then(() => {
|
|
@@ -2901,7 +2950,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2901
2950
|
* @public
|
|
2902
2951
|
* @memberof Meeting
|
|
2903
2952
|
*/
|
|
2904
|
-
unsetLocalVideoTrack() {
|
|
2953
|
+
public unsetLocalVideoTrack() {
|
|
2905
2954
|
this.mediaProperties.unsetLocalVideoTrack();
|
|
2906
2955
|
}
|
|
2907
2956
|
|
|
@@ -2911,7 +2960,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2911
2960
|
* @public
|
|
2912
2961
|
* @memberof Meeting
|
|
2913
2962
|
*/
|
|
2914
|
-
unsetLocalShareTrack() {
|
|
2963
|
+
public unsetLocalShareTrack() {
|
|
2915
2964
|
this.mediaProperties.unsetLocalShareTrack();
|
|
2916
2965
|
}
|
|
2917
2966
|
|
|
@@ -2921,9 +2970,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2921
2970
|
* @public
|
|
2922
2971
|
* @memberof Meeting
|
|
2923
2972
|
*/
|
|
2924
|
-
setMercuryListener() {
|
|
2973
|
+
public setMercuryListener() {
|
|
2925
2974
|
// Client will have a socket manager and handle reconnecting to mercury, when we reconnect to mercury
|
|
2926
2975
|
// if the meeting has active peer connections, it should try to reconnect.
|
|
2976
|
+
// @ts-ignore
|
|
2927
2977
|
this.webex.internal.mercury.on(ONLINE, () => {
|
|
2928
2978
|
LoggerProxy.logger.info('Meeting:index#setMercuryListener --> Web socket online');
|
|
2929
2979
|
|
|
@@ -2943,6 +2993,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2943
2993
|
this.hasWebsocketConnected = true;
|
|
2944
2994
|
});
|
|
2945
2995
|
|
|
2996
|
+
// @ts-ignore
|
|
2946
2997
|
this.webex.internal.mercury.on(OFFLINE, () => {
|
|
2947
2998
|
LoggerProxy.logger.error('Meeting:index#setMercuryListener --> Web socket offline');
|
|
2948
2999
|
Metrics.postEvent({
|
|
@@ -2959,27 +3010,41 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2959
3010
|
}
|
|
2960
3011
|
|
|
2961
3012
|
/**
|
|
2962
|
-
* Close the peer connections and remove them from the class.
|
|
2963
|
-
*
|
|
2964
|
-
*
|
|
3013
|
+
* Close the peer connections and remove them from the class.
|
|
3014
|
+
* Cleanup any media connection related things.
|
|
3015
|
+
*
|
|
3016
|
+
* @returns {Promise}
|
|
2965
3017
|
* @public
|
|
2966
3018
|
* @memberof Meeting
|
|
2967
3019
|
*/
|
|
2968
|
-
closePeerConnections() {
|
|
2969
|
-
|
|
3020
|
+
public closePeerConnections() {
|
|
3021
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
3022
|
+
if (this.remoteMediaManager) {
|
|
3023
|
+
this.remoteMediaManager.stop();
|
|
3024
|
+
this.remoteMediaManager = null;
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
Object.values(this.mediaRequestManagers).forEach((mediaRequestManager) => mediaRequestManager.reset());
|
|
3028
|
+
|
|
3029
|
+
this.receiveSlotManager.reset();
|
|
3030
|
+
this.mediaProperties.webrtcMediaConnection.close();
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
return Promise.resolve();
|
|
2970
3034
|
}
|
|
2971
3035
|
|
|
2972
3036
|
/**
|
|
2973
3037
|
* Unsets the peer connections on the class
|
|
2974
3038
|
* warning DO NOT CALL WITHOUT CLOSING PEER CONNECTIONS FIRST
|
|
2975
|
-
* @param {PeerConnection} peerConnection
|
|
2976
3039
|
* @returns {undefined}
|
|
2977
3040
|
* @public
|
|
2978
3041
|
* @memberof Meeting
|
|
2979
3042
|
*/
|
|
2980
|
-
unsetPeerConnections() {
|
|
3043
|
+
public unsetPeerConnections() {
|
|
2981
3044
|
this.mediaProperties.unsetPeerConnection();
|
|
3045
|
+
// @ts-ignore - config coming from registerPlugin
|
|
2982
3046
|
if (this.config.reconnection.detection) {
|
|
3047
|
+
// @ts-ignore
|
|
2983
3048
|
this.webex.internal.mercury.off(ONLINE);
|
|
2984
3049
|
}
|
|
2985
3050
|
}
|
|
@@ -2991,7 +3056,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2991
3056
|
* @private
|
|
2992
3057
|
* @memberof Meeting
|
|
2993
3058
|
*/
|
|
2994
|
-
setCorrelationId(id) {
|
|
3059
|
+
private setCorrelationId(id: string) {
|
|
2995
3060
|
this.correlationId = id;
|
|
2996
3061
|
}
|
|
2997
3062
|
|
|
@@ -3001,11 +3066,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3001
3066
|
* @public
|
|
3002
3067
|
* @memberof Meeting
|
|
3003
3068
|
*/
|
|
3004
|
-
muteAudio() {
|
|
3069
|
+
public muteAudio() {
|
|
3005
3070
|
if (!MeetingUtil.isUserInJoinedState(this.locusInfo)) {
|
|
3006
3071
|
return Promise.reject(new UserNotJoinedError());
|
|
3007
3072
|
}
|
|
3008
3073
|
|
|
3074
|
+
// @ts-ignore
|
|
3009
3075
|
if (!this.mediaId) {
|
|
3010
3076
|
// Happens when addMedia and mute are triggered in succession
|
|
3011
3077
|
return Promise.reject(new NoMediaEstablishedYetError());
|
|
@@ -3052,11 +3118,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3052
3118
|
* @public
|
|
3053
3119
|
* @memberof Meeting
|
|
3054
3120
|
*/
|
|
3055
|
-
unmuteAudio() {
|
|
3121
|
+
public unmuteAudio() {
|
|
3056
3122
|
if (!MeetingUtil.isUserInJoinedState(this.locusInfo)) {
|
|
3057
3123
|
return Promise.reject(new UserNotJoinedError());
|
|
3058
3124
|
}
|
|
3059
3125
|
|
|
3126
|
+
// @ts-ignore
|
|
3060
3127
|
if (!this.mediaId) {
|
|
3061
3128
|
// Happens when addMedia and mute are triggered in succession
|
|
3062
3129
|
return Promise.reject(new NoMediaEstablishedYetError());
|
|
@@ -3103,11 +3170,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3103
3170
|
* @public
|
|
3104
3171
|
* @memberof Meeting
|
|
3105
3172
|
*/
|
|
3106
|
-
muteVideo() {
|
|
3173
|
+
public muteVideo() {
|
|
3107
3174
|
if (!MeetingUtil.isUserInJoinedState(this.locusInfo)) {
|
|
3108
3175
|
return Promise.reject(new UserNotJoinedError());
|
|
3109
3176
|
}
|
|
3110
3177
|
|
|
3178
|
+
// @ts-ignore
|
|
3111
3179
|
if (!this.mediaId) {
|
|
3112
3180
|
// Happens when addMedia and mute are triggered in succession
|
|
3113
3181
|
return Promise.reject(new NoMediaEstablishedYetError());
|
|
@@ -3153,11 +3221,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3153
3221
|
* @public
|
|
3154
3222
|
* @memberof Meeting
|
|
3155
3223
|
*/
|
|
3156
|
-
unmuteVideo() {
|
|
3224
|
+
public unmuteVideo() {
|
|
3157
3225
|
if (!MeetingUtil.isUserInJoinedState(this.locusInfo)) {
|
|
3158
3226
|
return Promise.reject(new UserNotJoinedError());
|
|
3159
3227
|
}
|
|
3160
3228
|
|
|
3229
|
+
// @ts-ignore
|
|
3161
3230
|
if (!this.mediaId) {
|
|
3162
3231
|
// Happens when addMedia and mute are triggered in succession
|
|
3163
3232
|
return Promise.reject(new NoMediaEstablishedYetError());
|
|
@@ -3222,7 +3291,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3222
3291
|
* video: 'videoDeviceId'
|
|
3223
3292
|
* }})
|
|
3224
3293
|
*/
|
|
3225
|
-
joinWithMedia(
|
|
3294
|
+
public joinWithMedia(
|
|
3295
|
+
options: {
|
|
3296
|
+
joinOptions?: any;
|
|
3297
|
+
mediaSettings: any;
|
|
3298
|
+
audioVideoOptions?: any;
|
|
3299
|
+
} = {} as any
|
|
3300
|
+
) {
|
|
3226
3301
|
// TODO: add validations for parameters
|
|
3227
3302
|
const {mediaSettings, joinOptions, audioVideoOptions} = options;
|
|
3228
3303
|
|
|
@@ -3266,19 +3341,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3266
3341
|
* @public
|
|
3267
3342
|
* @memberof Meeting
|
|
3268
3343
|
*/
|
|
3269
|
-
reconnect(options) {
|
|
3344
|
+
public reconnect(options?: object) {
|
|
3270
3345
|
LoggerProxy.logger.log(`Meeting:index#reconnect --> attempting to reconnect meeting ${this.id}`);
|
|
3271
3346
|
|
|
3272
3347
|
if (!this.reconnectionManager || !this.reconnectionManager.reconnect) {
|
|
3273
3348
|
return Promise.reject(new ParameterError('Cannot reconnect, ReconnectionManager must first be defined.'));
|
|
3274
3349
|
}
|
|
3275
3350
|
|
|
3351
|
+
// @ts-ignore - currentMediaStatus coming from SelfUtil
|
|
3276
3352
|
if (!MeetingUtil.isMediaEstablished(this.currentMediaStatus)) {
|
|
3277
3353
|
return Promise.reject(new ParameterError('Cannot reconnect, Media has not established to reconnect'));
|
|
3278
3354
|
}
|
|
3279
3355
|
|
|
3280
3356
|
try {
|
|
3281
3357
|
LoggerProxy.logger.info('Meeting:index#reconnect --> Validating reconnect ability.');
|
|
3358
|
+
// @ts-ignore
|
|
3282
3359
|
this.reconnectionManager.validate();
|
|
3283
3360
|
}
|
|
3284
3361
|
catch (error) {
|
|
@@ -3302,7 +3379,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3302
3379
|
EVENT_TRIGGERS.MEETING_RECONNECTION_STARTING
|
|
3303
3380
|
);
|
|
3304
3381
|
|
|
3305
|
-
|
|
3306
3382
|
return this.reconnectionManager
|
|
3307
3383
|
.reconnect(options)
|
|
3308
3384
|
.then(() => {
|
|
@@ -3375,7 +3451,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3375
3451
|
* @private
|
|
3376
3452
|
* @returns {void}
|
|
3377
3453
|
*/
|
|
3378
|
-
monitorTranscriptionSocketConnection() {
|
|
3454
|
+
private monitorTranscriptionSocketConnection() {
|
|
3379
3455
|
this.transcription.onCloseSocket((event) => {
|
|
3380
3456
|
LoggerProxy.logger.info(
|
|
3381
3457
|
`Meeting:index#onCloseSocket -->
|
|
@@ -3413,7 +3489,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3413
3489
|
* @private
|
|
3414
3490
|
* @returns {Promise<void>} a promise to open the WebSocket connection
|
|
3415
3491
|
*/
|
|
3416
|
-
async receiveTranscription() {
|
|
3492
|
+
private async receiveTranscription() {
|
|
3417
3493
|
LoggerProxy.logger.info(
|
|
3418
3494
|
`Meeting:index#receiveTranscription -->
|
|
3419
3495
|
Attempting to generate a web socket url.`
|
|
@@ -3421,6 +3497,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3421
3497
|
|
|
3422
3498
|
try {
|
|
3423
3499
|
const {datachannelUrl} = this.locusInfo.info;
|
|
3500
|
+
// @ts-ignore - fix type
|
|
3424
3501
|
const {body: {webSocketUrl}} = await this.request({
|
|
3425
3502
|
method: HTTP_VERBS.POST,
|
|
3426
3503
|
uri: datachannelUrl,
|
|
@@ -3434,6 +3511,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3434
3511
|
|
|
3435
3512
|
this.transcription = new Transcription(
|
|
3436
3513
|
webSocketUrl,
|
|
3514
|
+
// @ts-ignore - fix type
|
|
3437
3515
|
this.webex.sessionId,
|
|
3438
3516
|
this.members,
|
|
3439
3517
|
);
|
|
@@ -3457,6 +3535,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3457
3535
|
});
|
|
3458
3536
|
|
|
3459
3537
|
this.monitorTranscriptionSocketConnection();
|
|
3538
|
+
// @ts-ignore - fix type
|
|
3460
3539
|
this.transcription.connect(this.webex.credentials.supertoken.access_token);
|
|
3461
3540
|
}
|
|
3462
3541
|
catch (error) {
|
|
@@ -3489,12 +3568,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3489
3568
|
* @private
|
|
3490
3569
|
* @returns{void}
|
|
3491
3570
|
*/
|
|
3492
|
-
triggerStopReceivingTranscriptionEvent() {
|
|
3571
|
+
private triggerStopReceivingTranscriptionEvent() {
|
|
3493
3572
|
LoggerProxy.logger.info(`
|
|
3494
3573
|
Meeting:index#stopReceivingTranscription -->
|
|
3495
3574
|
closed transcription LLM web socket connection successfully.`);
|
|
3496
3575
|
|
|
3497
|
-
|
|
3498
3576
|
Trigger.trigger(
|
|
3499
3577
|
this,
|
|
3500
3578
|
{
|
|
@@ -3517,7 +3595,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3517
3595
|
* if joining as host on second loop, pass pin and pass moderator if joining as guest on second loop
|
|
3518
3596
|
* Scenario D: Joining any other way (sip, pstn, conversationUrl, link just need to specify resourceId)
|
|
3519
3597
|
*/
|
|
3520
|
-
join(options = {}) {
|
|
3598
|
+
public join(options: any = {}) {
|
|
3599
|
+
// @ts-ignore - fix type
|
|
3521
3600
|
if (!this.webex.meetings.registered) {
|
|
3522
3601
|
const errorMessage = 'Meeting:index#join --> Device not registered';
|
|
3523
3602
|
const error = new Error(errorMessage);
|
|
@@ -3623,6 +3702,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3623
3702
|
}
|
|
3624
3703
|
}
|
|
3625
3704
|
|
|
3705
|
+
this.isMultistream = !!options.enableMultistream;
|
|
3706
|
+
|
|
3626
3707
|
return MeetingUtil.joinMeetingOptions(this, options)
|
|
3627
3708
|
.then((join) => {
|
|
3628
3709
|
this.meetingFiniteStateMachine.join();
|
|
@@ -3641,8 +3722,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3641
3722
|
);
|
|
3642
3723
|
|
|
3643
3724
|
return join;
|
|
3644
|
-
})
|
|
3725
|
+
})
|
|
3726
|
+
.then(async (join) => {
|
|
3727
|
+
if (this.config.enableAutomaticLLM) {
|
|
3728
|
+
await this.updateLLMConnection();
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
return join;
|
|
3732
|
+
})
|
|
3733
|
+
.then(async (join) => {
|
|
3645
3734
|
if (isBrowser) {
|
|
3735
|
+
// @ts-ignore - config coming from registerPlugin
|
|
3646
3736
|
if (this.config.receiveTranscription || options.receiveTranscription) {
|
|
3647
3737
|
if (this.isTranscriptionSupported()) {
|
|
3648
3738
|
await this.receiveTranscription();
|
|
@@ -3654,7 +3744,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3654
3744
|
LoggerProxy.logger.error('Meeting:index#join --> Receving transcription is not supported on this platform');
|
|
3655
3745
|
}
|
|
3656
3746
|
|
|
3657
|
-
|
|
3658
3747
|
return join;
|
|
3659
3748
|
})
|
|
3660
3749
|
.catch((error) => {
|
|
@@ -3700,6 +3789,31 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3700
3789
|
});
|
|
3701
3790
|
}
|
|
3702
3791
|
|
|
3792
|
+
/**
|
|
3793
|
+
* Connects to low latency mercury and reconnects if the address has changed
|
|
3794
|
+
* It will also disconnect if called when the meeting has ended
|
|
3795
|
+
* @param {String} datachannelUrl
|
|
3796
|
+
* @returns {Promise}
|
|
3797
|
+
*/
|
|
3798
|
+
async updateLLMConnection() {
|
|
3799
|
+
const {url, info: {datachannelUrl} = {}} = this.locusInfo;
|
|
3800
|
+
|
|
3801
|
+
const isJoined = this.joinedWith && this.joinedWith.state === 'JOINED';
|
|
3802
|
+
|
|
3803
|
+
if (this.webex.internal.llm.isConnected()) {
|
|
3804
|
+
if (url === this.webex.internal.llm.getLocusUrl() && isJoined) {
|
|
3805
|
+
return undefined;
|
|
3806
|
+
}
|
|
3807
|
+
await this.webex.internal.llm.disconnectLLM();
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
if (!isJoined) {
|
|
3811
|
+
return undefined;
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3814
|
+
return this.webex.internal.llm.registerAndConnect(url, datachannelUrl);
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3703
3817
|
/**
|
|
3704
3818
|
* Use phone for meeting audio
|
|
3705
3819
|
* @param {String} phoneNumber If provided, it will dial-out using this number. If not provided, dial-in will be used
|
|
@@ -3707,7 +3821,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3707
3821
|
* @public
|
|
3708
3822
|
* @memberof Meeting
|
|
3709
3823
|
*/
|
|
3710
|
-
usePhoneAudio(phoneNumber) {
|
|
3824
|
+
public usePhoneAudio(phoneNumber: string) {
|
|
3711
3825
|
if (!phoneNumber) {
|
|
3712
3826
|
return this.dialInPstn();
|
|
3713
3827
|
}
|
|
@@ -3722,7 +3836,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3722
3836
|
* @private
|
|
3723
3837
|
* @memberof Meeting
|
|
3724
3838
|
*/
|
|
3725
|
-
isPhoneProvisioned(pstnStatus) {
|
|
3839
|
+
private isPhoneProvisioned(pstnStatus: string) {
|
|
3726
3840
|
return [PSTN_STATUS.JOINED, PSTN_STATUS.CONNECTED, PSTN_STATUS.SUCCESS].includes(pstnStatus);
|
|
3727
3841
|
}
|
|
3728
3842
|
|
|
@@ -3732,7 +3846,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3732
3846
|
* @private
|
|
3733
3847
|
* @memberof Meeting
|
|
3734
3848
|
*/
|
|
3735
|
-
dialInPstn() {
|
|
3849
|
+
private dialInPstn() {
|
|
3736
3850
|
if (this.isPhoneProvisioned(this.dialInDeviceStatus)) return Promise.resolve(); // prevent multiple dial in devices from being provisioned
|
|
3737
3851
|
|
|
3738
3852
|
const {correlationId, locusUrl} = this;
|
|
@@ -3770,7 +3884,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3770
3884
|
* @private
|
|
3771
3885
|
* @memberof Meeting
|
|
3772
3886
|
*/
|
|
3773
|
-
dialOutPstn(phoneNumber) {
|
|
3887
|
+
private dialOutPstn(phoneNumber: string) {
|
|
3774
3888
|
if (this.isPhoneProvisioned(this.dialOutDeviceStatus)) return Promise.resolve(); // prevent multiple dial out devices from being provisioned
|
|
3775
3889
|
|
|
3776
3890
|
const {correlationId, locusUrl} = this;
|
|
@@ -3809,7 +3923,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3809
3923
|
* @memberof Meeting
|
|
3810
3924
|
* @returns {Promise}
|
|
3811
3925
|
*/
|
|
3812
|
-
disconnectPhoneAudio() {
|
|
3926
|
+
public disconnectPhoneAudio() {
|
|
3813
3927
|
return Promise.all([
|
|
3814
3928
|
this.isPhoneProvisioned(this.dialInDeviceStatus) ?
|
|
3815
3929
|
MeetingUtil.disconnectPhoneAudio(this, this.dialInUrl) :
|
|
@@ -3827,7 +3941,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3827
3941
|
* @public
|
|
3828
3942
|
* @memberof Meeting
|
|
3829
3943
|
*/
|
|
3830
|
-
moveTo(resourceId) {
|
|
3944
|
+
public moveTo(resourceId: string) {
|
|
3831
3945
|
if (!resourceId) {
|
|
3832
3946
|
throw new ParameterError('Cannot move call without a resourceId.');
|
|
3833
3947
|
}
|
|
@@ -3862,7 +3976,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3862
3976
|
|
|
3863
3977
|
try {
|
|
3864
3978
|
if (this.isSharing) {
|
|
3865
|
-
await this.
|
|
3979
|
+
await this.releaseScreenShareFloor();
|
|
3866
3980
|
}
|
|
3867
3981
|
const mediaSettings = {
|
|
3868
3982
|
mediaDirection: {
|
|
@@ -3887,6 +4001,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3887
4001
|
|
|
3888
4002
|
// when a move to is intiated by the client , Locus delets the existing media node from the server as soon the DX answers the meeting
|
|
3889
4003
|
// once the DX answers we establish connection back the media server with only receiveShare enabled
|
|
4004
|
+
// @ts-ignore - reconnectMedia does not accept any argument
|
|
3890
4005
|
await this.reconnectionManager.reconnectMedia(mediaSettings)
|
|
3891
4006
|
.then(() => {
|
|
3892
4007
|
Metrics.sendBehavioralMetric(
|
|
@@ -3936,7 +4051,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3936
4051
|
* @public
|
|
3937
4052
|
* @memberof Meeting
|
|
3938
4053
|
*/
|
|
3939
|
-
moveFrom(resourceId) {
|
|
4054
|
+
public moveFrom(resourceId: string) {
|
|
3940
4055
|
// On moveFrom ask the developer to re capture it moveFrom then updateMedia
|
|
3941
4056
|
if (!resourceId) {
|
|
3942
4057
|
throw new ParameterError('Cannot move call without a resourceId.');
|
|
@@ -3974,6 +4089,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3974
4089
|
|
|
3975
4090
|
/**
|
|
3976
4091
|
* Get local media streams based on options passed
|
|
4092
|
+
*
|
|
4093
|
+
* NOTE: this method can only be used with transcoded meetings, not with multistream meetings
|
|
4094
|
+
*
|
|
3977
4095
|
* @param {MediaDirection} mediaDirection A configurable options object for joining a meeting
|
|
3978
4096
|
* @param {AudioVideo} [audioVideo] audio/video object to set audioinput and videoinput devices, see #Media.getUserMedia
|
|
3979
4097
|
* @param {SharePreferences} [sharePreferences] audio/video object to set audioinput and videoinput devices, see #Media.getUserMedia
|
|
@@ -3983,10 +4101,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3983
4101
|
* @memberof Meeting
|
|
3984
4102
|
*/
|
|
3985
4103
|
getMediaStreams = (
|
|
3986
|
-
mediaDirection,
|
|
4104
|
+
mediaDirection: any,
|
|
3987
4105
|
// This return an OBJECT {video: {height, widght}}
|
|
3988
|
-
audioVideo = VIDEO_RESOLUTIONS[this.mediaProperties.localQualityLevel],
|
|
3989
|
-
sharePreferences
|
|
4106
|
+
audioVideo: any = VIDEO_RESOLUTIONS[this.mediaProperties.localQualityLevel],
|
|
4107
|
+
sharePreferences?: any
|
|
3990
4108
|
) => {
|
|
3991
4109
|
if (
|
|
3992
4110
|
mediaDirection &&
|
|
@@ -4063,6 +4181,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4063
4181
|
},
|
|
4064
4182
|
audioVideo,
|
|
4065
4183
|
sharePreferences,
|
|
4184
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4066
4185
|
this.config
|
|
4067
4186
|
)
|
|
4068
4187
|
.catch((error) => {
|
|
@@ -4100,7 +4219,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4100
4219
|
* @returns {Object}
|
|
4101
4220
|
* @memberof Meetings
|
|
4102
4221
|
*/
|
|
4103
|
-
getSupportedDevices = ({sendAudio = true, sendVideo = true}) => Media.getSupportedDevice({sendAudio, sendVideo});
|
|
4222
|
+
getSupportedDevices = ({ sendAudio = true, sendVideo = true }: { sendAudio: boolean; sendVideo: boolean }) => Media.getSupportedDevice({ sendAudio, sendVideo });
|
|
4104
4223
|
|
|
4105
4224
|
/**
|
|
4106
4225
|
* Get the devices from the Media module
|
|
@@ -4109,6 +4228,344 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4109
4228
|
*/
|
|
4110
4229
|
getDevices = () => Media.getDevices();
|
|
4111
4230
|
|
|
4231
|
+
/**
|
|
4232
|
+
* Handles ROAP_FAILURE event from the webrtc media connection
|
|
4233
|
+
*
|
|
4234
|
+
* @param {Error} error
|
|
4235
|
+
* @returns {void}
|
|
4236
|
+
*/
|
|
4237
|
+
handleRoapFailure = (error) => {
|
|
4238
|
+
const sendBehavioralMetric = (metricName, error, correlationId) => {
|
|
4239
|
+
const data = {
|
|
4240
|
+
code: error.code,
|
|
4241
|
+
correlation_id: correlationId,
|
|
4242
|
+
reason: error.message,
|
|
4243
|
+
stack: error.stack
|
|
4244
|
+
};
|
|
4245
|
+
const metadata = {
|
|
4246
|
+
type: error.cause?.name || error.name
|
|
4247
|
+
};
|
|
4248
|
+
|
|
4249
|
+
Metrics.sendBehavioralMetric(metricName, data, metadata);
|
|
4250
|
+
};
|
|
4251
|
+
|
|
4252
|
+
|
|
4253
|
+
if (error instanceof MC.Errors.SdpOfferCreationError) {
|
|
4254
|
+
sendBehavioralMetric(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, error, this.id);
|
|
4255
|
+
|
|
4256
|
+
Metrics.postEvent({
|
|
4257
|
+
event: eventType.LOCAL_SDP_GENERATED,
|
|
4258
|
+
meetingId: this.id,
|
|
4259
|
+
data: {
|
|
4260
|
+
canProceed: false,
|
|
4261
|
+
errors: [
|
|
4262
|
+
Metrics.generateErrorPayload(2001, true, MetricsError.name.MEDIA_ENGINE, undefined)]
|
|
4263
|
+
}
|
|
4264
|
+
});
|
|
4265
|
+
}
|
|
4266
|
+
else if ((error instanceof MC.Errors.SdpOfferHandlingError) || (error instanceof MC.Errors.SdpAnswerHandlingError)) {
|
|
4267
|
+
sendBehavioralMetric(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, error, this.id);
|
|
4268
|
+
|
|
4269
|
+
Metrics.postEvent({
|
|
4270
|
+
event: eventType.REMOTE_SDP_RECEIVED,
|
|
4271
|
+
meetingId: this.id,
|
|
4272
|
+
data: {
|
|
4273
|
+
canProceed: false,
|
|
4274
|
+
errors: [Metrics.generateErrorPayload(2001, true, MetricsError.name.MEDIA_ENGINE, undefined)]
|
|
4275
|
+
}
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4278
|
+
else if (error instanceof MC.Errors.SdpError) { // this covers also the case of MC.Errors.IceGatheringError which extends MC.Errors.SdpError
|
|
4279
|
+
sendBehavioralMetric(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, error, this.id);
|
|
4280
|
+
|
|
4281
|
+
Metrics.postEvent({
|
|
4282
|
+
event: eventType.LOCAL_SDP_GENERATED,
|
|
4283
|
+
meetingId: this.id,
|
|
4284
|
+
data: {
|
|
4285
|
+
canProceed: false,
|
|
4286
|
+
errors: [
|
|
4287
|
+
Metrics.generateErrorPayload(2001, true, MetricsError.name.MEDIA_ENGINE, undefined)]
|
|
4288
|
+
}
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
};
|
|
4292
|
+
|
|
4293
|
+
setupMediaConnectionListeners = () => {
|
|
4294
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.ROAP_STARTED, () => {
|
|
4295
|
+
this.isRoapInProgress = true;
|
|
4296
|
+
});
|
|
4297
|
+
|
|
4298
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.ROAP_DONE, () => {
|
|
4299
|
+
this.mediaNegotiatedEvent();
|
|
4300
|
+
this.isRoapInProgress = false;
|
|
4301
|
+
this.processNextQueuedMediaUpdate();
|
|
4302
|
+
});
|
|
4303
|
+
|
|
4304
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.ROAP_FAILURE, this.handleRoapFailure);
|
|
4305
|
+
|
|
4306
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.ROAP_MESSAGE_TO_SEND, (event) => {
|
|
4307
|
+
const LOG_HEADER = 'Meeting:index#setupMediaConnectionListeners.ROAP_MESSAGE_TO_SEND -->';
|
|
4308
|
+
|
|
4309
|
+
switch (event.roapMessage.messageType) {
|
|
4310
|
+
case 'OK':
|
|
4311
|
+
Metrics.postEvent({
|
|
4312
|
+
event: eventType.REMOTE_SDP_RECEIVED,
|
|
4313
|
+
meetingId: this.id,
|
|
4314
|
+
});
|
|
4315
|
+
|
|
4316
|
+
logRequest(this.roap.sendRoapOK({
|
|
4317
|
+
seq: event.roapMessage.seq,
|
|
4318
|
+
mediaId: this.mediaId,
|
|
4319
|
+
correlationId: this.correlationId
|
|
4320
|
+
}), {
|
|
4321
|
+
header: `${LOG_HEADER} Send Roap OK`,
|
|
4322
|
+
success: `${LOG_HEADER} Successfully send roap OK`,
|
|
4323
|
+
failure: `${LOG_HEADER} Error joining the call on send roap OK, `
|
|
4324
|
+
});
|
|
4325
|
+
break;
|
|
4326
|
+
|
|
4327
|
+
case 'OFFER':
|
|
4328
|
+
Metrics.postEvent({
|
|
4329
|
+
event: eventType.LOCAL_SDP_GENERATED,
|
|
4330
|
+
meetingId: this.id,
|
|
4331
|
+
});
|
|
4332
|
+
|
|
4333
|
+
logRequest(this.roap
|
|
4334
|
+
.sendRoapMediaRequest({
|
|
4335
|
+
sdp: event.roapMessage.sdp,
|
|
4336
|
+
seq: event.roapMessage.seq,
|
|
4337
|
+
tieBreaker: event.roapMessage.tieBreaker,
|
|
4338
|
+
meeting: this, // or can pass meeting ID
|
|
4339
|
+
reconnect: this.reconnectionManager.isReconnectInProgress()
|
|
4340
|
+
}), {
|
|
4341
|
+
header: `${LOG_HEADER} Send Roap Offer`,
|
|
4342
|
+
success: `${LOG_HEADER} Successfully send roap offer`,
|
|
4343
|
+
failure: `${LOG_HEADER} Error joining the call on send roap offer, `
|
|
4344
|
+
});
|
|
4345
|
+
break;
|
|
4346
|
+
|
|
4347
|
+
case 'ANSWER':
|
|
4348
|
+
Metrics.postEvent({
|
|
4349
|
+
event: eventType.REMOTE_SDP_RECEIVED,
|
|
4350
|
+
meetingId: this.id,
|
|
4351
|
+
});
|
|
4352
|
+
|
|
4353
|
+
logRequest(this.roap.sendRoapAnswer({
|
|
4354
|
+
sdp: event.roapMessage.sdp,
|
|
4355
|
+
seq: event.roapMessage.seq,
|
|
4356
|
+
mediaId: this.mediaId,
|
|
4357
|
+
correlationId: this.correlationId
|
|
4358
|
+
}), {
|
|
4359
|
+
header: `${LOG_HEADER} Send Roap Answer.`,
|
|
4360
|
+
success: `${LOG_HEADER} Successfully send roap answer`,
|
|
4361
|
+
failure: `${LOG_HEADER} Error joining the call on send roap answer, `
|
|
4362
|
+
})
|
|
4363
|
+
.catch((error) => {
|
|
4364
|
+
const metricName = BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE;
|
|
4365
|
+
const data = {
|
|
4366
|
+
correlation_id: this.correlationId,
|
|
4367
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
4368
|
+
reason: error.message,
|
|
4369
|
+
stack: error.stack
|
|
4370
|
+
};
|
|
4371
|
+
const metadata = {
|
|
4372
|
+
type: error.name
|
|
4373
|
+
};
|
|
4374
|
+
|
|
4375
|
+
Metrics.sendBehavioralMetric(metricName, data, metadata);
|
|
4376
|
+
});
|
|
4377
|
+
break;
|
|
4378
|
+
|
|
4379
|
+
case 'ERROR':
|
|
4380
|
+
if (event.roapMessage.errorType === MC.ErrorType.CONFLICT || event.roapMessage.errorType === MC.ErrorType.DOUBLECONFLICT) {
|
|
4381
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
|
|
4382
|
+
correlation_id: this.correlationId,
|
|
4383
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
4384
|
+
sequence: event.roapMessage.seq
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
logRequest(this.roap.sendRoapError({
|
|
4388
|
+
seq: event.roapMessage.seq,
|
|
4389
|
+
errorType: event.roapMessage.errorType,
|
|
4390
|
+
mediaId: this.mediaId,
|
|
4391
|
+
correlationId: this.correlationId
|
|
4392
|
+
}), {
|
|
4393
|
+
header: `${LOG_HEADER} Send Roap Error.`,
|
|
4394
|
+
success: `${LOG_HEADER} Successfully send roap error`,
|
|
4395
|
+
failure: `${LOG_HEADER} Failed to send roap error, `
|
|
4396
|
+
});
|
|
4397
|
+
break;
|
|
4398
|
+
|
|
4399
|
+
default:
|
|
4400
|
+
LoggerProxy.logger.error(`${LOG_HEADER} Unsupported message type: ${event.roapMessage.messageType}`);
|
|
4401
|
+
break;
|
|
4402
|
+
}
|
|
4403
|
+
});
|
|
4404
|
+
|
|
4405
|
+
// eslint-disable-next-line no-param-reassign
|
|
4406
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.REMOTE_TRACK_ADDED, (event) => {
|
|
4407
|
+
LoggerProxy.logger.log(`Meeting:index#setupMediaConnectionListeners --> REMOTE_TRACK_ADDED event received for webrtcMediaConnection: ${JSON.stringify(event)}`);
|
|
4408
|
+
|
|
4409
|
+
const mediaTrack = event.track;
|
|
4410
|
+
|
|
4411
|
+
let eventType;
|
|
4412
|
+
|
|
4413
|
+
switch (event.type) {
|
|
4414
|
+
case MC.RemoteTrackType.AUDIO:
|
|
4415
|
+
eventType = EVENT_TYPES.REMOTE_AUDIO;
|
|
4416
|
+
this.mediaProperties.setRemoteAudioTrack(event.track);
|
|
4417
|
+
break;
|
|
4418
|
+
case MC.RemoteTrackType.VIDEO:
|
|
4419
|
+
eventType = EVENT_TYPES.REMOTE_VIDEO;
|
|
4420
|
+
this.mediaProperties.setRemoteVideoTrack(event.track);
|
|
4421
|
+
break;
|
|
4422
|
+
case MC.RemoteTrackType.SCREENSHARE_VIDEO:
|
|
4423
|
+
if (event.track) {
|
|
4424
|
+
eventType = EVENT_TYPES.REMOTE_SHARE;
|
|
4425
|
+
this.mediaProperties.setRemoteShare(event.track);
|
|
4426
|
+
}
|
|
4427
|
+
break;
|
|
4428
|
+
default: {
|
|
4429
|
+
LoggerProxy.logger.log('Meeting:index#setupMediaConnectionListeners --> unexpected track');
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
// start stats here the stats are coming null if you dont receive streams
|
|
4434
|
+
|
|
4435
|
+
this.statsAnalyzer.startAnalyzer(this.mediaProperties.webrtcMediaConnection);
|
|
4436
|
+
|
|
4437
|
+
if (eventType && mediaTrack) {
|
|
4438
|
+
Trigger.trigger(
|
|
4439
|
+
this,
|
|
4440
|
+
{
|
|
4441
|
+
file: 'meeting/index',
|
|
4442
|
+
function: 'setupRemoteTrackListener:Event.REMOTE_TRACK_ADDED'
|
|
4443
|
+
},
|
|
4444
|
+
EVENT_TRIGGERS.MEDIA_READY,
|
|
4445
|
+
{
|
|
4446
|
+
type: eventType,
|
|
4447
|
+
stream: MediaUtil.createMediaStream([mediaTrack])
|
|
4448
|
+
}
|
|
4449
|
+
);
|
|
4450
|
+
}
|
|
4451
|
+
});
|
|
4452
|
+
|
|
4453
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.CONNECTION_STATE_CHANGED, (event) => {
|
|
4454
|
+
const connectionFailed = () => {
|
|
4455
|
+
// we know the media connection failed and browser will not attempt to recover it any more
|
|
4456
|
+
// so reset the timer as it's not needed anymore, we want to reconnect immediately
|
|
4457
|
+
this.reconnectionManager.resetReconnectionTimer();
|
|
4458
|
+
|
|
4459
|
+
this.reconnect({networkDisconnect: true});
|
|
4460
|
+
Metrics.postEvent({
|
|
4461
|
+
event: eventType.ICE_END,
|
|
4462
|
+
meeting: this,
|
|
4463
|
+
data: {
|
|
4464
|
+
canProceed: false,
|
|
4465
|
+
errors: [
|
|
4466
|
+
Metrics.generateErrorPayload(
|
|
4467
|
+
2004, false, MetricsError.name.MEDIA_ENGINE, undefined
|
|
4468
|
+
)]
|
|
4469
|
+
}
|
|
4470
|
+
});
|
|
4471
|
+
|
|
4472
|
+
this.uploadLogs({
|
|
4473
|
+
file: 'peer-connection-manager/index',
|
|
4474
|
+
function: 'connectionFailed'
|
|
4475
|
+
});
|
|
4476
|
+
|
|
4477
|
+
Metrics.sendBehavioralMetric(
|
|
4478
|
+
BEHAVIORAL_METRICS.CONNECTION_FAILURE,
|
|
4479
|
+
{
|
|
4480
|
+
correlation_id: this.correlationId,
|
|
4481
|
+
locus_id: this.locusId
|
|
4482
|
+
}
|
|
4483
|
+
);
|
|
4484
|
+
};
|
|
4485
|
+
|
|
4486
|
+
LoggerProxy.logger.info(`Meeting:index#setupMediaConnectionListeners --> connection state changed to ${event.state}`);
|
|
4487
|
+
switch (event.state) {
|
|
4488
|
+
case MC.ConnectionState.Connecting:
|
|
4489
|
+
Metrics.postEvent({event: eventType.ICE_START, meeting: this});
|
|
4490
|
+
break;
|
|
4491
|
+
case MC.ConnectionState.Connected:
|
|
4492
|
+
Metrics.postEvent({event: eventType.ICE_END, meeting: this});
|
|
4493
|
+
Metrics.sendBehavioralMetric(
|
|
4494
|
+
BEHAVIORAL_METRICS.CONNECTION_SUCCESS,
|
|
4495
|
+
{
|
|
4496
|
+
correlation_id: this.correlationId,
|
|
4497
|
+
locus_id: this.locusId
|
|
4498
|
+
}
|
|
4499
|
+
);
|
|
4500
|
+
this.setNetworkStatus(NETWORK_STATUS.CONNECTED);
|
|
4501
|
+
this.reconnectionManager.iceReconnected();
|
|
4502
|
+
break;
|
|
4503
|
+
case MC.ConnectionState.Disconnected:
|
|
4504
|
+
this.setNetworkStatus(NETWORK_STATUS.DISCONNECTED);
|
|
4505
|
+
this.reconnectionManager.waitForIceReconnect()
|
|
4506
|
+
.catch(() => {
|
|
4507
|
+
LoggerProxy.logger.info('Meeting:index#setupMediaConnectionListeners --> state DISCONNECTED, automatic reconnection timed out.');
|
|
4508
|
+
|
|
4509
|
+
connectionFailed();
|
|
4510
|
+
});
|
|
4511
|
+
break;
|
|
4512
|
+
case MC.ConnectionState.Failed:
|
|
4513
|
+
connectionFailed();
|
|
4514
|
+
break;
|
|
4515
|
+
default:
|
|
4516
|
+
break;
|
|
4517
|
+
}
|
|
4518
|
+
});
|
|
4519
|
+
|
|
4520
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.ACTIVE_SPEAKERS_CHANGED,
|
|
4521
|
+
(msg) => {
|
|
4522
|
+
Trigger.trigger(
|
|
4523
|
+
this,
|
|
4524
|
+
{
|
|
4525
|
+
file: 'meeting/index',
|
|
4526
|
+
function: 'setupMediaConnectionListeners'
|
|
4527
|
+
},
|
|
4528
|
+
EVENT_TRIGGERS.ACTIVE_SPEAKER_CHANGED,
|
|
4529
|
+
{
|
|
4530
|
+
seqNum: msg.seqNum,
|
|
4531
|
+
memberIds: msg.csis.map((csi) => this.members.findMemberByCsi(csi)?.id).filter((item) => (item !== undefined))
|
|
4532
|
+
}
|
|
4533
|
+
);
|
|
4534
|
+
});
|
|
4535
|
+
|
|
4536
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.VIDEO_SOURCES_COUNT_CHANGED,
|
|
4537
|
+
(numTotalSources, numLiveSources) => {
|
|
4538
|
+
Trigger.trigger(
|
|
4539
|
+
this,
|
|
4540
|
+
{
|
|
4541
|
+
file: 'meeting/index',
|
|
4542
|
+
function: 'setupMediaConnectionListeners'
|
|
4543
|
+
},
|
|
4544
|
+
EVENT_TRIGGERS.REMOTE_VIDEO_SOURCE_COUNT_CHANGED,
|
|
4545
|
+
{
|
|
4546
|
+
numTotalSources,
|
|
4547
|
+
numLiveSources
|
|
4548
|
+
}
|
|
4549
|
+
);
|
|
4550
|
+
});
|
|
4551
|
+
|
|
4552
|
+
this.mediaProperties.webrtcMediaConnection.on(MC.Event.AUDIO_SOURCES_COUNT_CHANGED,
|
|
4553
|
+
(numTotalSources, numLiveSources) => {
|
|
4554
|
+
Trigger.trigger(
|
|
4555
|
+
this,
|
|
4556
|
+
{
|
|
4557
|
+
file: 'meeting/index',
|
|
4558
|
+
function: 'setupMediaConnectionListeners'
|
|
4559
|
+
},
|
|
4560
|
+
EVENT_TRIGGERS.REMOTE_AUDIO_SOURCE_COUNT_CHANGED,
|
|
4561
|
+
{
|
|
4562
|
+
numTotalSources,
|
|
4563
|
+
numLiveSources
|
|
4564
|
+
}
|
|
4565
|
+
);
|
|
4566
|
+
});
|
|
4567
|
+
};
|
|
4568
|
+
|
|
4112
4569
|
/**
|
|
4113
4570
|
* Registers for all required StatsAnalyzer events
|
|
4114
4571
|
* @private
|
|
@@ -4119,6 +4576,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4119
4576
|
this.statsAnalyzer.on(StatsAnalyzerEvents.MEDIA_QUALITY, (options) => {
|
|
4120
4577
|
// TODO: might have to send the same event to the developer
|
|
4121
4578
|
// Add ip address info if geo hint is present
|
|
4579
|
+
// @ts-ignore fix type
|
|
4122
4580
|
options.data.intervalMetadata.peerReflexiveIP = this.webex.meetings.geoHintInfo?.clientAddress || options.data.intervalMetadata.peerReflexiveIP || MQA_STATS.DEFAULT_IP;
|
|
4123
4581
|
Metrics.postEvent({event: eventType.MEDIA_QUALITY, meeting: this, data: {intervalData: options.data, networkType: options.networkType}});
|
|
4124
4582
|
});
|
|
@@ -4178,6 +4636,52 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4178
4636
|
});
|
|
4179
4637
|
};
|
|
4180
4638
|
|
|
4639
|
+
getMediaConnectionDebugId() {
|
|
4640
|
+
return `MC-${this.id.substring(0, 4)}`;
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
createMediaConnection(turnServerInfo) {
|
|
4644
|
+
const mc = Media.createMediaConnection(
|
|
4645
|
+
this.isMultistream,
|
|
4646
|
+
this.getMediaConnectionDebugId(),
|
|
4647
|
+
{
|
|
4648
|
+
mediaProperties: this.mediaProperties,
|
|
4649
|
+
remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
|
|
4650
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4651
|
+
enableRtx: this.config.enableRtx,
|
|
4652
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4653
|
+
enableExtmap: this.config.enableExtmap,
|
|
4654
|
+
turnServerInfo
|
|
4655
|
+
}
|
|
4656
|
+
);
|
|
4657
|
+
|
|
4658
|
+
this.mediaProperties.setMediaPeerConnection(mc);
|
|
4659
|
+
this.setupMediaConnectionListeners();
|
|
4660
|
+
|
|
4661
|
+
return mc;
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
/**
|
|
4665
|
+
* Listens for an event emitted by eventEmitter and emits it from the meeting object
|
|
4666
|
+
*
|
|
4667
|
+
* @private
|
|
4668
|
+
* @param {*} eventEmitter object from which to forward the event
|
|
4669
|
+
* @param {*} eventTypeToForward which event type to listen on and to forward
|
|
4670
|
+
* @param {string} meetingEventType event type to be used in the event emitted from the meeting object
|
|
4671
|
+
* @returns {void}
|
|
4672
|
+
*/
|
|
4673
|
+
forwardEvent(eventEmitter, eventTypeToForward, meetingEventType) {
|
|
4674
|
+
eventEmitter.on(eventTypeToForward, (data) => Trigger.trigger(
|
|
4675
|
+
this,
|
|
4676
|
+
{
|
|
4677
|
+
file: 'meetings',
|
|
4678
|
+
function: 'addMedia'
|
|
4679
|
+
},
|
|
4680
|
+
meetingEventType,
|
|
4681
|
+
data
|
|
4682
|
+
));
|
|
4683
|
+
}
|
|
4684
|
+
|
|
4181
4685
|
/**
|
|
4182
4686
|
* Specify joining via audio (option: pstn), video, screenshare
|
|
4183
4687
|
* @param {Object} options A configurable options object for joining a meeting
|
|
@@ -4185,11 +4689,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4185
4689
|
* @param {MediaDirection} options.mediaSettings pass media options
|
|
4186
4690
|
* @param {MediaStream} options.localStream
|
|
4187
4691
|
* @param {MediaStream} options.localShare
|
|
4692
|
+
* @param {RemoteMediaManagerConfig} options.remoteMediaManagerConfig only applies if multistream is enabled
|
|
4188
4693
|
* @returns {Promise}
|
|
4189
4694
|
* @public
|
|
4190
4695
|
* @memberof Meeting
|
|
4191
4696
|
*/
|
|
4192
|
-
addMedia(options = {}) {
|
|
4697
|
+
addMedia(options: any = {}) {
|
|
4193
4698
|
const LOG_HEADER = 'Meeting:index#addMedia -->';
|
|
4194
4699
|
|
|
4195
4700
|
let turnDiscoverySkippedReason;
|
|
@@ -4203,11 +4708,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4203
4708
|
return Promise.reject(new UserNotJoinedError());
|
|
4204
4709
|
}
|
|
4205
4710
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
|
4711
|
+
// @ts-ignore - isUserUnadmitted coming from SelfUtil
|
|
4206
4712
|
if (this.isUserUnadmitted && !this.wirelessShare) {
|
|
4207
4713
|
return Promise.reject(new UserInLobbyError());
|
|
4208
4714
|
}
|
|
4209
4715
|
|
|
4210
|
-
const {
|
|
4716
|
+
const {
|
|
4717
|
+
localStream, localShare, mediaSettings, remoteMediaManagerConfig
|
|
4718
|
+
} = options;
|
|
4211
4719
|
|
|
4212
4720
|
LoggerProxy.logger.info(`${LOG_HEADER} Adding Media.`);
|
|
4213
4721
|
|
|
@@ -4242,32 +4750,42 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4242
4750
|
|
|
4243
4751
|
const {turnServerInfo} = turnDiscoveryObject;
|
|
4244
4752
|
|
|
4245
|
-
this.
|
|
4246
|
-
|
|
4247
|
-
|
|
4753
|
+
this.preMedia(localStream, localShare, mediaSettings);
|
|
4754
|
+
|
|
4755
|
+
const mc = this.createMediaConnection(turnServerInfo);
|
|
4248
4756
|
|
|
4249
|
-
|
|
4757
|
+
if (this.isMultistream) {
|
|
4758
|
+
this.remoteMediaManager = new RemoteMediaManager(
|
|
4759
|
+
this.receiveSlotManager,
|
|
4760
|
+
this.mediaRequestManagers,
|
|
4761
|
+
remoteMediaManagerConfig
|
|
4762
|
+
);
|
|
4763
|
+
|
|
4764
|
+
this.forwardEvent(this.remoteMediaManager, RemoteMediaManagerEvent.AudioCreated, EVENT_TRIGGERS.REMOTE_MEDIA_AUDIO_CREATED);
|
|
4765
|
+
this.forwardEvent(this.remoteMediaManager, RemoteMediaManagerEvent.ScreenShareAudioCreated, EVENT_TRIGGERS.REMOTE_MEDIA_SCREEN_SHARE_AUDIO_CREATED);
|
|
4766
|
+
this.forwardEvent(this.remoteMediaManager, RemoteMediaManagerEvent.VideoLayoutChanged, EVENT_TRIGGERS.REMOTE_MEDIA_VIDEO_LAYOUT_CHANGED);
|
|
4767
|
+
|
|
4768
|
+
return this.remoteMediaManager.start()
|
|
4769
|
+
.then(() => mc.initiateOffer());
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
return mc.initiateOffer();
|
|
4250
4773
|
})
|
|
4251
|
-
.then(() =>
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
enableExtmap: this.config.enableExtmap,
|
|
4256
|
-
setStartLocalSDPGenRemoteSDPRecvDelay: this.setStartLocalSDPGenRemoteSDPRecvDelay.bind(this)
|
|
4257
|
-
}))
|
|
4258
|
-
.then((peerConnection) => this.getDevices().then((devices) => {
|
|
4774
|
+
.then(() => {
|
|
4775
|
+
this.setMercuryListener();
|
|
4776
|
+
})
|
|
4777
|
+
.then(() => this.getDevices().then((devices) => {
|
|
4259
4778
|
MeetingUtil.handleDeviceLogging(devices);
|
|
4260
|
-
|
|
4261
|
-
return peerConnection;
|
|
4262
4779
|
}))
|
|
4263
|
-
.then((
|
|
4780
|
+
.then(() => {
|
|
4264
4781
|
this.handleMediaLogging(this.mediaProperties);
|
|
4265
|
-
LoggerProxy.logger.info(`${LOG_HEADER}
|
|
4782
|
+
LoggerProxy.logger.info(`${LOG_HEADER} media connection created`);
|
|
4266
4783
|
|
|
4267
|
-
|
|
4784
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4268
4785
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
4269
|
-
//
|
|
4786
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4270
4787
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
4788
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4271
4789
|
this.statsAnalyzer = new StatsAnalyzer(this.config.stats, this.networkQualityMonitor);
|
|
4272
4790
|
this.setupStatsAnalyzerEventHandlers();
|
|
4273
4791
|
this.networkQualityMonitor.on(EVENT_TRIGGERS.NETWORK_QUALITY, this.sendNetworkQualityEvent.bind(this));
|
|
@@ -4276,21 +4794,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4276
4794
|
.catch((error) => {
|
|
4277
4795
|
LoggerProxy.logger.error(`${LOG_HEADER} Error adding media , setting up peerconnection, `, error);
|
|
4278
4796
|
|
|
4279
|
-
Metrics.sendBehavioralMetric(
|
|
4280
|
-
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
|
4281
|
-
{
|
|
4282
|
-
correlation_id: this.correlationId,
|
|
4283
|
-
locus_id: this.locusUrl.split('/').pop(),
|
|
4284
|
-
reason: error.message,
|
|
4285
|
-
stack: error.stack,
|
|
4286
|
-
turnDiscoverySkippedReason,
|
|
4287
|
-
turnServerUsed
|
|
4288
|
-
}
|
|
4289
|
-
);
|
|
4290
|
-
|
|
4291
4797
|
throw error;
|
|
4292
4798
|
})
|
|
4293
|
-
.then(() => new Promise((resolve, reject) => {
|
|
4799
|
+
.then(() => new Promise<void>((resolve, reject) => {
|
|
4294
4800
|
let timerCount = 0;
|
|
4295
4801
|
|
|
4296
4802
|
// eslint-disable-next-line func-names
|
|
@@ -4311,29 +4817,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4311
4817
|
}
|
|
4312
4818
|
}, 1000);
|
|
4313
4819
|
}))
|
|
4314
|
-
.then(() =>
|
|
4315
|
-
logRequest(this.roap
|
|
4316
|
-
.sendRoapMediaRequest({
|
|
4317
|
-
sdp: this.mediaProperties.peerConnection.sdp,
|
|
4318
|
-
roapSeq: this.roapSeq,
|
|
4319
|
-
meeting: this // or can pass meeting ID
|
|
4320
|
-
}), {
|
|
4321
|
-
header: `${LOG_HEADER} Send Roap Media Request.`,
|
|
4322
|
-
success: `${LOG_HEADER} Successfully send roap media request`,
|
|
4323
|
-
failure: `${LOG_HEADER} Error joining the call on send roap media request, `
|
|
4324
|
-
}))
|
|
4325
4820
|
.then(
|
|
4326
|
-
() => this.mediaProperties.
|
|
4821
|
+
() => this.mediaProperties.waitForMediaConnectionConnected()
|
|
4327
4822
|
.catch(() => {
|
|
4328
4823
|
throw createMeetingsError(30202, 'Meeting connection failed');
|
|
4329
4824
|
})
|
|
4330
4825
|
)
|
|
4331
4826
|
.then(() => {
|
|
4332
4827
|
LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection CONNECTED`);
|
|
4333
|
-
|
|
4334
4828
|
if (mediaSettings && mediaSettings.sendShare && localShare) {
|
|
4335
4829
|
if (this.state === MEETING_STATE.STATES.JOINED) {
|
|
4336
|
-
return this.
|
|
4830
|
+
return this.requestScreenShareFloor();
|
|
4337
4831
|
}
|
|
4338
4832
|
|
|
4339
4833
|
// When the self state changes to JOINED then request the floor
|
|
@@ -4361,7 +4855,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4361
4855
|
.then(() => {
|
|
4362
4856
|
this.statsAnalyzer = null;
|
|
4363
4857
|
|
|
4364
|
-
if (this.mediaProperties.
|
|
4858
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4365
4859
|
this.closePeerConnections();
|
|
4366
4860
|
this.unsetPeerConnections();
|
|
4367
4861
|
}
|
|
@@ -4392,10 +4886,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4392
4886
|
this
|
|
4393
4887
|
);
|
|
4394
4888
|
|
|
4395
|
-
|
|
4396
|
-
// leave the meeting with reson connection failed as meeting anyways will end
|
|
4397
|
-
// and cannot be connected unless network condition is checked for firewall
|
|
4398
|
-
if (error.code === InvalidSdpError.CODE) {
|
|
4889
|
+
if (error instanceof MC.Errors.SdpError) {
|
|
4399
4890
|
this.leave({reason: MEETING_REMOVED_REASON.MEETING_CONNECTION_FAILED});
|
|
4400
4891
|
}
|
|
4401
4892
|
|
|
@@ -4409,7 +4900,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4409
4900
|
* @returns {Boolean}
|
|
4410
4901
|
*/
|
|
4411
4902
|
canUpdateMedia() {
|
|
4412
|
-
|
|
4903
|
+
// in theory we shouldn't need this as RoapMediaConnection handles multiple updates, glare, etc,
|
|
4904
|
+
// but there are some server issues, like https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-248394
|
|
4905
|
+
// so for now it's better to keep queuing any media updates at SDK meeting level
|
|
4906
|
+
return !this.isRoapInProgress;
|
|
4413
4907
|
}
|
|
4414
4908
|
|
|
4415
4909
|
/**
|
|
@@ -4420,7 +4914,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4420
4914
|
* @private
|
|
4421
4915
|
* @memberof Meeting
|
|
4422
4916
|
*/
|
|
4423
|
-
enqueueMediaUpdate(mediaUpdateType, options) {
|
|
4917
|
+
private enqueueMediaUpdate(mediaUpdateType: string, options: object) {
|
|
4424
4918
|
return new Promise((resolve, reject) => {
|
|
4425
4919
|
const queueItem = {
|
|
4426
4920
|
pendingPromiseResolve: resolve, pendingPromiseReject: reject, mediaUpdateType, options
|
|
@@ -4438,6 +4932,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4438
4932
|
* @memberof Meeting
|
|
4439
4933
|
*/
|
|
4440
4934
|
mediaNegotiatedEvent = () => {
|
|
4935
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4441
4936
|
if (this.config.experimental.enableMediaNegotiatedEvent) {
|
|
4442
4937
|
LoggerProxy.logger.info('Meeting:mediaNegotiatedEvent --> Media server negotiated');
|
|
4443
4938
|
Trigger.trigger(
|
|
@@ -4493,11 +4988,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4493
4988
|
* @param {MediaStream} options.localShare
|
|
4494
4989
|
* @param {MediaDirection} options.mediaSettings
|
|
4495
4990
|
* @returns {Promise}
|
|
4496
|
-
* @todo fix setRemoteStream for updateMedia
|
|
4497
4991
|
* @public
|
|
4498
4992
|
* @memberof Meeting
|
|
4499
4993
|
*/
|
|
4500
|
-
updateMedia(
|
|
4994
|
+
public updateMedia(
|
|
4995
|
+
options: {
|
|
4996
|
+
localStream?: MediaStream;
|
|
4997
|
+
localShare?: MediaStream;
|
|
4998
|
+
mediaSettings?: any;
|
|
4999
|
+
} = {} as any
|
|
5000
|
+
) {
|
|
4501
5001
|
const LOG_HEADER = 'Meeting:index#updateMedia -->';
|
|
4502
5002
|
|
|
4503
5003
|
if (!this.canUpdateMedia()) {
|
|
@@ -4509,20 +5009,27 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4509
5009
|
|
|
4510
5010
|
const previousSendShareStatus = this.mediaProperties.mediaDirection.sendShare;
|
|
4511
5011
|
|
|
5012
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
5013
|
+
return Promise.reject(new Error('media connection not established, call addMedia() first'));
|
|
5014
|
+
}
|
|
5015
|
+
|
|
4512
5016
|
return MeetingUtil.validateOptions(options)
|
|
4513
5017
|
.then(() => this.preMedia(localStream, localShare, mediaSettings))
|
|
4514
|
-
.then(() =>
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
5018
|
+
.then(() => this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions({
|
|
5019
|
+
send: {
|
|
5020
|
+
audio: this.mediaProperties.mediaDirection.sendAudio ? this.mediaProperties.audioTrack : null,
|
|
5021
|
+
video: this.mediaProperties.mediaDirection.sendVideo ? this.mediaProperties.videoTrack : null,
|
|
5022
|
+
screenShareVideo: this.mediaProperties.mediaDirection.sendShare ? this.mediaProperties.shareTrack : null
|
|
5023
|
+
},
|
|
5024
|
+
receive: {
|
|
5025
|
+
audio: this.mediaProperties.mediaDirection.receiveAudio,
|
|
5026
|
+
video: this.mediaProperties.mediaDirection.receiveVideo,
|
|
5027
|
+
screenShareVideo: this.mediaProperties.mediaDirection.receiveShare,
|
|
5028
|
+
remoteQualityLevel: this.mediaProperties.remoteQualityLevel
|
|
5029
|
+
}
|
|
4519
5030
|
})
|
|
4520
|
-
.then((
|
|
4521
|
-
LoggerProxy.logger.info(`${LOG_HEADER}
|
|
4522
|
-
this.setRemoteStream(peerConnection);
|
|
4523
|
-
if (mediaSettings.receiveShare || localShare) {
|
|
4524
|
-
PeerConnectionManager.setContentSlides(peerConnection);
|
|
4525
|
-
}
|
|
5031
|
+
.then(() => {
|
|
5032
|
+
LoggerProxy.logger.info(`${LOG_HEADER} webrtcMediaConnection.updateSendReceiveOptions done`);
|
|
4526
5033
|
})
|
|
4527
5034
|
.catch((error) => {
|
|
4528
5035
|
LoggerProxy.logger.error(`${LOG_HEADER} Error updatedMedia, `, error);
|
|
@@ -4539,25 +5046,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4539
5046
|
|
|
4540
5047
|
throw error;
|
|
4541
5048
|
})
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
roapSeq: this.roapSeq,
|
|
4547
|
-
meeting: this, // or can pass meeting ID
|
|
4548
|
-
}),
|
|
4549
|
-
{
|
|
4550
|
-
header: `${LOG_HEADER} sendRoapMediaRequest being sent`,
|
|
4551
|
-
success: `${LOG_HEADER} sendRoadMediaRequest successful`,
|
|
4552
|
-
failure: `${LOG_HEADER} Error updateMedia on send roap media request, `
|
|
4553
|
-
}))
|
|
5049
|
+
// todo: the following code used to be called always after sending the roap message with the new SDP
|
|
5050
|
+
// now it's called independently from the roap message (so might be before it), check if that's OK
|
|
5051
|
+
// if not, ensure it's called after (now it's called after roap message is sent out, but we're not
|
|
5052
|
+
// waiting for sendRoapMediaRequest() to be resolved)
|
|
4554
5053
|
.then(() => this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus))
|
|
4555
5054
|
.then((startShare) => {
|
|
4556
5055
|
// This is a special case if we do an /floor grant followed by /media
|
|
4557
5056
|
// we actually get a OFFER from the server and a GLAR condition happens
|
|
4558
5057
|
if (startShare) {
|
|
4559
5058
|
// We are assuming that the clients are connected when doing an update
|
|
4560
|
-
return this.
|
|
5059
|
+
return this.requestScreenShareFloor();
|
|
4561
5060
|
}
|
|
4562
5061
|
|
|
4563
5062
|
return Promise.resolve();
|
|
@@ -4566,6 +5065,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4566
5065
|
|
|
4567
5066
|
/**
|
|
4568
5067
|
* Update the main audio track with new parameters
|
|
5068
|
+
*
|
|
5069
|
+
* NOTE: this method can only be used with transcoded meetings, for multistream meetings use publishTrack()
|
|
5070
|
+
*
|
|
4569
5071
|
* @param {Object} options
|
|
4570
5072
|
* @param {boolean} options.sendAudio
|
|
4571
5073
|
* @param {boolean} options.receiveAudio
|
|
@@ -4574,21 +5076,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4574
5076
|
* @public
|
|
4575
5077
|
* @memberof Meeting
|
|
4576
5078
|
*/
|
|
4577
|
-
async updateAudio(options) {
|
|
5079
|
+
public async updateAudio(options: { sendAudio: boolean; receiveAudio: boolean; stream: MediaStream }) {
|
|
4578
5080
|
if (!this.canUpdateMedia()) {
|
|
4579
5081
|
return this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.AUDIO, options);
|
|
4580
5082
|
}
|
|
4581
|
-
const {
|
|
4582
|
-
sendAudio, receiveAudio, stream
|
|
4583
|
-
} = options;
|
|
4584
|
-
|
|
4585
|
-
const {audioTransceiver} = this.mediaProperties.peerConnection;
|
|
5083
|
+
const {sendAudio, receiveAudio, stream} = options;
|
|
4586
5084
|
let track = MeetingUtil.getTrack(stream).audioTrack;
|
|
4587
5085
|
|
|
4588
5086
|
if (typeof sendAudio !== 'boolean' || typeof receiveAudio !== 'boolean') {
|
|
4589
5087
|
return Promise.reject(new ParameterError('Pass sendAudio and receiveAudio parameter'));
|
|
4590
5088
|
}
|
|
4591
5089
|
|
|
5090
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
5091
|
+
return Promise.reject(new Error('media connection not established, call addMedia() first'));
|
|
5092
|
+
}
|
|
5093
|
+
|
|
4592
5094
|
if (this.effects && this.effects.state) {
|
|
4593
5095
|
const bnrEnabled = this.effects.state.bnr.enabled;
|
|
4594
5096
|
|
|
@@ -4600,38 +5102,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4600
5102
|
}
|
|
4601
5103
|
|
|
4602
5104
|
return MeetingUtil.validateOptions({sendAudio, localStream: stream})
|
|
4603
|
-
.then(() => {
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
};
|
|
4611
|
-
}
|
|
4612
|
-
else {
|
|
4613
|
-
this.mediaProperties.mediaDirection = {};
|
|
5105
|
+
.then(() => this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions({
|
|
5106
|
+
send: {audio: track},
|
|
5107
|
+
receive: {
|
|
5108
|
+
audio: options.receiveAudio,
|
|
5109
|
+
video: this.mediaProperties.mediaDirection.receiveVideo,
|
|
5110
|
+
screenShareVideo: this.mediaProperties.mediaDirection.receiveShare,
|
|
5111
|
+
remoteQualityLevel: this.mediaProperties.remoteQualityLevel
|
|
4614
5112
|
}
|
|
4615
|
-
|
|
4616
|
-
return MeetingUtil.updateTransceiver(
|
|
4617
|
-
{
|
|
4618
|
-
type: 'audio',
|
|
4619
|
-
sendTrack: options.sendAudio,
|
|
4620
|
-
receiveTrack: options.receiveAudio,
|
|
4621
|
-
track,
|
|
4622
|
-
transceiver: audioTransceiver,
|
|
4623
|
-
peerConnection: this.mediaProperties.peerConnection,
|
|
4624
|
-
previousMediaDirection
|
|
4625
|
-
},
|
|
4626
|
-
{
|
|
4627
|
-
mediaProperties: this.mediaProperties,
|
|
4628
|
-
meeting: this,
|
|
4629
|
-
id: this.id
|
|
4630
|
-
}
|
|
4631
|
-
);
|
|
4632
|
-
})
|
|
5113
|
+
}))
|
|
4633
5114
|
.then(() => {
|
|
4634
5115
|
this.setLocalAudioTrack(track);
|
|
5116
|
+
// todo: maybe this.mediaProperties.mediaDirection could be removed? it's duplicating stuff from webrtcMediaConnection
|
|
4635
5117
|
this.mediaProperties.mediaDirection.sendAudio = sendAudio;
|
|
4636
5118
|
this.mediaProperties.mediaDirection.receiveAudio = receiveAudio;
|
|
4637
5119
|
|
|
@@ -4642,6 +5124,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4642
5124
|
|
|
4643
5125
|
/**
|
|
4644
5126
|
* Update the main video track with new parameters
|
|
5127
|
+
*
|
|
5128
|
+
* NOTE: this method can only be used with transcoded meetings, for multistream meetings use publishTrack()
|
|
5129
|
+
*
|
|
4645
5130
|
* @param {Object} options
|
|
4646
5131
|
* @param {boolean} options.sendVideo
|
|
4647
5132
|
* @param {boolean} options.receiveVideo
|
|
@@ -4650,35 +5135,30 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4650
5135
|
* @public
|
|
4651
5136
|
* @memberof Meeting
|
|
4652
5137
|
*/
|
|
4653
|
-
updateVideo(options) {
|
|
5138
|
+
public updateVideo(options: { sendVideo: boolean; receiveVideo: boolean; stream: MediaStream }) {
|
|
4654
5139
|
if (!this.canUpdateMedia()) {
|
|
4655
5140
|
return this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.VIDEO, options);
|
|
4656
5141
|
}
|
|
4657
5142
|
const {sendVideo, receiveVideo, stream} = options;
|
|
4658
|
-
const {videoTransceiver} = this.mediaProperties.peerConnection;
|
|
4659
5143
|
const track = MeetingUtil.getTrack(stream).videoTrack;
|
|
4660
5144
|
|
|
4661
5145
|
if (typeof sendVideo !== 'boolean' || typeof receiveVideo !== 'boolean') {
|
|
4662
5146
|
return Promise.reject(new ParameterError('Pass sendVideo and receiveVideo parameter'));
|
|
4663
5147
|
}
|
|
4664
5148
|
|
|
5149
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
5150
|
+
return Promise.reject(new Error('media connection not established, call addMedia() first'));
|
|
5151
|
+
}
|
|
5152
|
+
|
|
4665
5153
|
return MeetingUtil.validateOptions({sendVideo, localStream: stream})
|
|
4666
|
-
.then(() =>
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
previousMediaDirection: {
|
|
4674
|
-
sendTrack: this.mediaProperties.mediaDirection.sendVideo,
|
|
4675
|
-
receiveTrack: this.mediaProperties.mediaDirection.receiveVideo
|
|
5154
|
+
.then(() => this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions({
|
|
5155
|
+
send: {video: track},
|
|
5156
|
+
receive: {
|
|
5157
|
+
audio: this.mediaProperties.mediaDirection.receiveAudio,
|
|
5158
|
+
video: options.receiveVideo,
|
|
5159
|
+
screenShareVideo: this.mediaProperties.mediaDirection.receiveShare,
|
|
5160
|
+
remoteQualityLevel: this.mediaProperties.remoteQualityLevel
|
|
4676
5161
|
}
|
|
4677
|
-
},
|
|
4678
|
-
{
|
|
4679
|
-
mediaProperties: this.mediaProperties,
|
|
4680
|
-
meeting: this,
|
|
4681
|
-
id: this.id
|
|
4682
5162
|
}))
|
|
4683
5163
|
.then(() => {
|
|
4684
5164
|
this.setLocalVideoTrack(track);
|
|
@@ -4698,7 +5178,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4698
5178
|
* @private
|
|
4699
5179
|
* @memberof Meeting
|
|
4700
5180
|
*/
|
|
4701
|
-
checkForStopShare(sendShare, previousShareStatus) {
|
|
5181
|
+
private checkForStopShare(sendShare: boolean, previousShareStatus: boolean) {
|
|
4702
5182
|
if (sendShare && !previousShareStatus) {
|
|
4703
5183
|
// When user starts sharing
|
|
4704
5184
|
return Promise.resolve(true);
|
|
@@ -4706,7 +5186,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4706
5186
|
|
|
4707
5187
|
if (!sendShare && previousShareStatus) {
|
|
4708
5188
|
// When user stops sharing
|
|
4709
|
-
return this.
|
|
5189
|
+
return this.releaseScreenShareFloor()
|
|
4710
5190
|
.then(() => Promise.resolve(false));
|
|
4711
5191
|
}
|
|
4712
5192
|
|
|
@@ -4715,6 +5195,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4715
5195
|
|
|
4716
5196
|
/**
|
|
4717
5197
|
* Update the share streams, can be used to start sharing
|
|
5198
|
+
*
|
|
5199
|
+
* NOTE: this method can only be used with transcoded meetings, for multistream meetings use publishTrack()
|
|
5200
|
+
*
|
|
4718
5201
|
* @param {Object} options
|
|
4719
5202
|
* @param {boolean} options.sendShare
|
|
4720
5203
|
* @param {boolean} options.receiveShare
|
|
@@ -4722,43 +5205,39 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4722
5205
|
* @public
|
|
4723
5206
|
* @memberof Meeting
|
|
4724
5207
|
*/
|
|
4725
|
-
updateShare(options) {
|
|
5208
|
+
public updateShare(options: { sendShare?: boolean; receiveShare?: boolean, stream?: any, skipSignalingCheck?: boolean }) {
|
|
4726
5209
|
if (!options.skipSignalingCheck && !this.canUpdateMedia()) {
|
|
4727
5210
|
return this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.SHARE, options);
|
|
4728
5211
|
}
|
|
4729
5212
|
const {sendShare, receiveShare, stream} = options;
|
|
4730
|
-
const {shareTransceiver} = this.mediaProperties.peerConnection;
|
|
4731
5213
|
const track = MeetingUtil.getTrack(stream).videoTrack;
|
|
4732
5214
|
|
|
4733
5215
|
if (typeof sendShare !== 'boolean' || typeof receiveShare !== 'boolean') {
|
|
4734
5216
|
return Promise.reject(new ParameterError('Pass sendShare and receiveShare parameter'));
|
|
4735
5217
|
}
|
|
5218
|
+
|
|
5219
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
5220
|
+
return Promise.reject(new Error('media connection not established, call addMedia() first'));
|
|
5221
|
+
}
|
|
5222
|
+
|
|
4736
5223
|
const previousSendShareStatus = this.mediaProperties.mediaDirection.sendShare;
|
|
4737
5224
|
|
|
4738
5225
|
this.setLocalShareTrack(stream);
|
|
4739
5226
|
|
|
4740
5227
|
return MeetingUtil.validateOptions({sendShare, localShare: stream})
|
|
4741
5228
|
.then(() => this.checkForStopShare(sendShare, previousSendShareStatus))
|
|
4742
|
-
.then((startShare) =>
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
previousMediaDirection: {
|
|
4750
|
-
sendTrack: this.mediaProperties.mediaDirection.sendShare,
|
|
4751
|
-
receiveTrack: this.mediaProperties.mediaDirection.receiveShare
|
|
5229
|
+
.then((startShare) => this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions({
|
|
5230
|
+
send: {screenShareVideo: track},
|
|
5231
|
+
receive: {
|
|
5232
|
+
audio: this.mediaProperties.mediaDirection.receiveAudio,
|
|
5233
|
+
video: this.mediaProperties.mediaDirection.receiveVideo,
|
|
5234
|
+
screenShareVideo: options.receiveShare,
|
|
5235
|
+
remoteQualityLevel: this.mediaProperties.remoteQualityLevel
|
|
4752
5236
|
}
|
|
4753
|
-
},
|
|
4754
|
-
{
|
|
4755
|
-
mediaProperties: this.mediaProperties,
|
|
4756
|
-
meeting: this,
|
|
4757
|
-
id: this.id
|
|
4758
5237
|
})
|
|
4759
5238
|
.then(() => {
|
|
4760
5239
|
if (startShare) {
|
|
4761
|
-
return this.
|
|
5240
|
+
return this.requestScreenShareFloor();
|
|
4762
5241
|
}
|
|
4763
5242
|
|
|
4764
5243
|
return Promise.resolve();
|
|
@@ -4768,24 +5247,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4768
5247
|
this.mediaProperties.mediaDirection.receiveShare = receiveShare;
|
|
4769
5248
|
})
|
|
4770
5249
|
.catch((error) => {
|
|
4771
|
-
this.unsetLocalShareTrack(
|
|
5250
|
+
this.unsetLocalShareTrack();
|
|
4772
5251
|
throw error;
|
|
4773
|
-
})
|
|
4774
|
-
.finally(() => {
|
|
4775
|
-
const delay = 1e3;
|
|
4776
|
-
// Check to see if share was stopped natively before onended was assigned.
|
|
4777
|
-
const sharingModeIsActive = this.mediaProperties.peerConnection.shareTransceiver.direction === SENDRECV;
|
|
4778
|
-
const isSharingOutOfSync = sharingModeIsActive && !this.isLocalShareLive;
|
|
4779
|
-
|
|
4780
|
-
if (isSharingOutOfSync) {
|
|
4781
|
-
// Adding a delay to avoid a 409 from server
|
|
4782
|
-
// which results in user still appearing as if sharing.
|
|
4783
|
-
// Also delay give time for changes to peerConnection.
|
|
4784
|
-
setTimeout(
|
|
4785
|
-
() => this.handleShareTrackEnded(stream),
|
|
4786
|
-
delay
|
|
4787
|
-
);
|
|
4788
|
-
}
|
|
4789
5252
|
});
|
|
4790
5253
|
}
|
|
4791
5254
|
|
|
@@ -4798,9 +5261,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4798
5261
|
* @private
|
|
4799
5262
|
* @memberof Meeting
|
|
4800
5263
|
*/
|
|
4801
|
-
preMedia(localStream, localShare, mediaSettings) {
|
|
5264
|
+
private preMedia(localStream: MediaStream, localShare: MediaStream, mediaSettings: any) {
|
|
4802
5265
|
// eslint-disable-next-line no-warning-comments
|
|
4803
5266
|
// TODO wire into default config. There's currently an issue with the stateless plugin or how we register
|
|
5267
|
+
// @ts-ignore - config coming from registerPlugin
|
|
4804
5268
|
this.mediaProperties.setMediaDirection(Object.assign(this.config.mediaSettings, mediaSettings));
|
|
4805
5269
|
// add a setup a function move the create and setup media in future
|
|
4806
5270
|
// TODO: delete old audio and video if stale
|
|
@@ -4818,7 +5282,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4818
5282
|
* @public
|
|
4819
5283
|
* @memberof Meeting
|
|
4820
5284
|
*/
|
|
4821
|
-
acknowledge(type) {
|
|
5285
|
+
public acknowledge(type: string) {
|
|
4822
5286
|
if (!type) {
|
|
4823
5287
|
return Promise.reject(new ParameterError('Type must be set to acknowledge the meeting.'));
|
|
4824
5288
|
}
|
|
@@ -4853,7 +5317,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4853
5317
|
* @public
|
|
4854
5318
|
* @memberof Meeting
|
|
4855
5319
|
*/
|
|
4856
|
-
decline(reason) {
|
|
5320
|
+
public decline(reason: string) {
|
|
4857
5321
|
return MeetingUtil.declineMeeting(this, reason).then((decline) => {
|
|
4858
5322
|
this.meetingFiniteStateMachine.decline();
|
|
4859
5323
|
|
|
@@ -4873,7 +5337,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4873
5337
|
* @public
|
|
4874
5338
|
* @memberof Meeting
|
|
4875
5339
|
*/
|
|
4876
|
-
leave(options = {}) {
|
|
5340
|
+
public leave(options: { resourceId?: string, reason?: any } = {} as any) {
|
|
4877
5341
|
Metrics.postEvent({event: eventType.LEAVE, meeting: this, data: {trigger: trigger.USER_INTERACTION, canProceed: false}});
|
|
4878
5342
|
const leaveReason = options.reason || MEETING_REMOVED_REASON.CLIENT_LEAVE_REQUEST;
|
|
4879
5343
|
|
|
@@ -4950,7 +5414,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4950
5414
|
* @public
|
|
4951
5415
|
* @memberof Meeting
|
|
4952
5416
|
*/
|
|
4953
|
-
startWhiteboardShare(channelUrl, resourceToken) {
|
|
5417
|
+
public startWhiteboardShare(channelUrl: string, resourceToken: string) {
|
|
4954
5418
|
const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
|
|
4955
5419
|
|
|
4956
5420
|
if (!channelUrl) {
|
|
@@ -4960,7 +5424,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4960
5424
|
if (whiteboard) {
|
|
4961
5425
|
Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_INITIATED, meeting: this});
|
|
4962
5426
|
|
|
4963
|
-
const body = {
|
|
5427
|
+
const body: any = {
|
|
4964
5428
|
disposition: FLOOR_ACTION.GRANTED,
|
|
4965
5429
|
personUrl: this.locusInfo.self.url,
|
|
4966
5430
|
deviceUrl: this.deviceUrl,
|
|
@@ -5006,7 +5470,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5006
5470
|
* @public
|
|
5007
5471
|
* @memberof Meeting
|
|
5008
5472
|
*/
|
|
5009
|
-
stopWhiteboardShare(channelUrl) {
|
|
5473
|
+
public stopWhiteboardShare(channelUrl: string) {
|
|
5010
5474
|
const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
|
|
5011
5475
|
|
|
5012
5476
|
if (whiteboard) {
|
|
@@ -5022,6 +5486,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5022
5486
|
LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
|
|
5023
5487
|
|
|
5024
5488
|
Metrics.sendBehavioralMetric(
|
|
5489
|
+
// @ts-ignore - check if STOP_WHITEBOARD_SHARE_FAILURE exists
|
|
5025
5490
|
BEHAVIORAL_METRICS.STOP_WHITEBOARD_SHARE_FAILURE,
|
|
5026
5491
|
{
|
|
5027
5492
|
correlation_id: this.correlationId,
|
|
@@ -5042,12 +5507,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5042
5507
|
}
|
|
5043
5508
|
|
|
5044
5509
|
/**
|
|
5045
|
-
*
|
|
5510
|
+
* Sends a request to Locus to obtain the screen share floor
|
|
5046
5511
|
* @returns {Promise} see #meetingRequest.changeMeetingFloor
|
|
5047
5512
|
* @private
|
|
5048
5513
|
* @memberof Meeting
|
|
5049
5514
|
*/
|
|
5050
|
-
|
|
5515
|
+
private requestScreenShareFloor() {
|
|
5051
5516
|
const content = this.locusInfo.mediaShares.find((element) => element.name === CONTENT);
|
|
5052
5517
|
|
|
5053
5518
|
if (content && (this.shareStatus !== SHARE_STATUS.LOCAL_SHARE_ACTIVE)) {
|
|
@@ -5093,7 +5558,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5093
5558
|
*/
|
|
5094
5559
|
// Internal only, temporarily allows optional params
|
|
5095
5560
|
// eslint-disable-next-line valid-jsdoc
|
|
5096
|
-
stopShare(options = {}) {
|
|
5561
|
+
public stopShare(options = {}) {
|
|
5097
5562
|
return this.updateShare({
|
|
5098
5563
|
sendShare: false,
|
|
5099
5564
|
receiveShare: this.mediaProperties.mediaDirection.receiveShare,
|
|
@@ -5102,12 +5567,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5102
5567
|
}
|
|
5103
5568
|
|
|
5104
5569
|
/**
|
|
5105
|
-
*
|
|
5570
|
+
* Sends a request to Locus to release the screen share floor.
|
|
5106
5571
|
* @returns {Promise} see #meetingRequest.changeMeetingFloor
|
|
5107
5572
|
* @private
|
|
5108
5573
|
* @memberof Meeting
|
|
5109
5574
|
*/
|
|
5110
|
-
|
|
5575
|
+
private releaseScreenShareFloor() {
|
|
5111
5576
|
const content = this.locusInfo.mediaShares.find((element) => element.name === CONTENT);
|
|
5112
5577
|
|
|
5113
5578
|
if (content && (this.mediaProperties.mediaDirection.sendShare)) {
|
|
@@ -5129,7 +5594,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5129
5594
|
resourceUrl: this.resourceUrl
|
|
5130
5595
|
})
|
|
5131
5596
|
.catch((error) => {
|
|
5132
|
-
LoggerProxy.logger.error('Meeting:index#
|
|
5597
|
+
LoggerProxy.logger.error('Meeting:index#releaseScreenShareFloor --> Error ', error);
|
|
5133
5598
|
|
|
5134
5599
|
Metrics.sendBehavioralMetric(
|
|
5135
5600
|
BEHAVIORAL_METRICS.STOP_FLOOR_REQUEST_FAILURE,
|
|
@@ -5157,7 +5622,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5157
5622
|
* @public
|
|
5158
5623
|
* @memberof Meeting
|
|
5159
5624
|
*/
|
|
5160
|
-
startRecording() {
|
|
5625
|
+
public startRecording() {
|
|
5161
5626
|
return MeetingUtil.startRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
|
|
5162
5627
|
}
|
|
5163
5628
|
|
|
@@ -5167,7 +5632,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5167
5632
|
* @public
|
|
5168
5633
|
* @memberof Meeting
|
|
5169
5634
|
*/
|
|
5170
|
-
stopRecording() {
|
|
5635
|
+
public stopRecording() {
|
|
5171
5636
|
return MeetingUtil.stopRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
|
|
5172
5637
|
}
|
|
5173
5638
|
|
|
@@ -5177,7 +5642,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5177
5642
|
* @public
|
|
5178
5643
|
* @memberof Meeting
|
|
5179
5644
|
*/
|
|
5180
|
-
pauseRecording() {
|
|
5645
|
+
public pauseRecording() {
|
|
5181
5646
|
return MeetingUtil.pauseRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
|
|
5182
5647
|
}
|
|
5183
5648
|
|
|
@@ -5187,7 +5652,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5187
5652
|
* @public
|
|
5188
5653
|
* @memberof Meeting
|
|
5189
5654
|
*/
|
|
5190
|
-
resumeRecording() {
|
|
5655
|
+
public resumeRecording() {
|
|
5191
5656
|
return MeetingUtil.resumeRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
|
|
5192
5657
|
}
|
|
5193
5658
|
|
|
@@ -5197,7 +5662,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5197
5662
|
* @public
|
|
5198
5663
|
* @memberof Meeting
|
|
5199
5664
|
*/
|
|
5200
|
-
lockMeeting() {
|
|
5665
|
+
public lockMeeting() {
|
|
5201
5666
|
return MeetingUtil.lockMeeting(this.inMeetingActions, this.meetingRequest, this.locusUrl);
|
|
5202
5667
|
}
|
|
5203
5668
|
|
|
@@ -5207,7 +5672,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5207
5672
|
* @public
|
|
5208
5673
|
* @memberof Meeting
|
|
5209
5674
|
*/
|
|
5210
|
-
unlockMeeting() {
|
|
5675
|
+
public unlockMeeting() {
|
|
5211
5676
|
return MeetingUtil.unlockMeeting(this.inMeetingActions, this.meetingRequest, this.locusUrl);
|
|
5212
5677
|
}
|
|
5213
5678
|
|
|
@@ -5218,7 +5683,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5218
5683
|
* @private
|
|
5219
5684
|
* @memberof Meeting
|
|
5220
5685
|
*/
|
|
5221
|
-
rejectWithErrorLog(message) {
|
|
5686
|
+
private rejectWithErrorLog(message: string) {
|
|
5222
5687
|
LoggerProxy.logger.error(message);
|
|
5223
5688
|
|
|
5224
5689
|
return Promise.reject(new Error(message));
|
|
@@ -5231,7 +5696,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5231
5696
|
* @public
|
|
5232
5697
|
* @memberof Meeting
|
|
5233
5698
|
*/
|
|
5234
|
-
sendDTMF(tones) {
|
|
5699
|
+
public sendDTMF(tones: string) {
|
|
5235
5700
|
if (this.locusInfo && this.locusInfo.self) {
|
|
5236
5701
|
if (this.locusInfo.self.enableDTMF) {
|
|
5237
5702
|
return this.meetingRequest
|
|
@@ -5262,7 +5727,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5262
5727
|
* @public
|
|
5263
5728
|
* @memberof Meeting
|
|
5264
5729
|
*/
|
|
5265
|
-
changeVideoLayout(
|
|
5730
|
+
public changeVideoLayout(
|
|
5731
|
+
layoutType?: string,
|
|
5732
|
+
renderInfo: {
|
|
5733
|
+
main: {
|
|
5734
|
+
width: number;
|
|
5735
|
+
height: number;
|
|
5736
|
+
};
|
|
5737
|
+
content: {
|
|
5738
|
+
width: number;
|
|
5739
|
+
height: number;
|
|
5740
|
+
};
|
|
5741
|
+
} = {} as any
|
|
5742
|
+
) {
|
|
5266
5743
|
const {main, content} = renderInfo;
|
|
5267
5744
|
const {mediaDirection, remoteShare, remoteVideoTrack} = this.mediaProperties;
|
|
5268
5745
|
|
|
@@ -5358,7 +5835,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5358
5835
|
* @param {String} level {LOW|MEDIUM|HIGH}
|
|
5359
5836
|
* @returns {Promise<MediaStream>} localStream
|
|
5360
5837
|
*/
|
|
5361
|
-
setLocalVideoQuality(level) {
|
|
5838
|
+
setLocalVideoQuality(level: string) {
|
|
5362
5839
|
LoggerProxy.logger.log(`Meeting:index#setLocalVideoQuality --> Setting quality to ${level}`);
|
|
5363
5840
|
|
|
5364
5841
|
if (!VIDEO_RESOLUTIONS[level]) {
|
|
@@ -5408,7 +5885,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5408
5885
|
* @param {String} level {LOW|MEDIUM|HIGH}
|
|
5409
5886
|
* @returns {Promise}
|
|
5410
5887
|
*/
|
|
5411
|
-
setRemoteQualityLevel(level) {
|
|
5888
|
+
setRemoteQualityLevel(level: string) {
|
|
5412
5889
|
LoggerProxy.logger.log(`Meeting:index#setRemoteQualityLevel --> Setting quality to ${level}`);
|
|
5413
5890
|
|
|
5414
5891
|
if (!QUALITY_LEVELS[level]) {
|
|
@@ -5438,7 +5915,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5438
5915
|
* @returns {Promise}
|
|
5439
5916
|
* @deprecated After FHD support
|
|
5440
5917
|
*/
|
|
5441
|
-
setMeetingQuality(level) {
|
|
5918
|
+
setMeetingQuality(level: string) {
|
|
5442
5919
|
LoggerProxy.logger.log(`Meeting:index#setMeetingQuality --> Setting quality to ${level}`);
|
|
5443
5920
|
|
|
5444
5921
|
if (!QUALITY_LEVELS[level]) {
|
|
@@ -5493,6 +5970,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5493
5970
|
}
|
|
5494
5971
|
|
|
5495
5972
|
/**
|
|
5973
|
+
*
|
|
5974
|
+
* NOTE: this method can only be used with transcoded meetings, for multistream use publishTrack()
|
|
5975
|
+
*
|
|
5496
5976
|
* @param {Object} options parameter
|
|
5497
5977
|
* @param {Boolean} options.sendAudio send audio from the display share
|
|
5498
5978
|
* @param {Boolean} options.sendShare send video from the display share
|
|
@@ -5502,7 +5982,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5502
5982
|
* @param {Boolean} options.sharePreferences.highFrameRate if shareConstraints isn't provided, set default values based off of this boolean
|
|
5503
5983
|
* @returns {Promise}
|
|
5504
5984
|
*/
|
|
5505
|
-
shareScreen(
|
|
5985
|
+
shareScreen(
|
|
5986
|
+
options: {
|
|
5987
|
+
sendAudio: boolean;
|
|
5988
|
+
sendShare: boolean;
|
|
5989
|
+
sharePreferences: { shareConstraints: MediaTrackConstraints };
|
|
5990
|
+
} = {} as any
|
|
5991
|
+
) {
|
|
5506
5992
|
LoggerProxy.logger.log('Meeting:index#shareScreen --> Getting local share');
|
|
5507
5993
|
|
|
5508
5994
|
const shareConstraints = {
|
|
@@ -5511,6 +5997,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5511
5997
|
...options
|
|
5512
5998
|
};
|
|
5513
5999
|
|
|
6000
|
+
// @ts-ignore - config coming from registerPlugin
|
|
5514
6001
|
return Media.getDisplayMedia(shareConstraints, this.config)
|
|
5515
6002
|
.then((shareStream) => this.updateShare({
|
|
5516
6003
|
sendShare: true,
|
|
@@ -5548,7 +6035,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5548
6035
|
* @param {MediaStream} localShare
|
|
5549
6036
|
* @returns {undefined}
|
|
5550
6037
|
*/
|
|
5551
|
-
handleShareTrackEnded(localShare) {
|
|
6038
|
+
private handleShareTrackEnded(localShare: MediaStream) {
|
|
5552
6039
|
if (this.wirelessShare) {
|
|
5553
6040
|
this.leave({reason: MEETING_REMOVED_REASON.USER_ENDED_SHARE_STREAMS});
|
|
5554
6041
|
}
|
|
@@ -5588,7 +6075,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5588
6075
|
* @private
|
|
5589
6076
|
* @memberof Meeting
|
|
5590
6077
|
*/
|
|
5591
|
-
sendNetworkQualityEvent(res) {
|
|
6078
|
+
private sendNetworkQualityEvent(res: any) {
|
|
5592
6079
|
Trigger.trigger(
|
|
5593
6080
|
this,
|
|
5594
6081
|
{
|
|
@@ -5610,7 +6097,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5610
6097
|
* @private
|
|
5611
6098
|
* @returns {undefined}
|
|
5612
6099
|
*/
|
|
5613
|
-
handleMediaLogging({audioTrack, videoTrack}) {
|
|
6100
|
+
private handleMediaLogging({ audioTrack, videoTrack }: any) {
|
|
5614
6101
|
MeetingUtil.handleVideoLogging(videoTrack);
|
|
5615
6102
|
MeetingUtil.handleAudioLogging(audioTrack);
|
|
5616
6103
|
}
|
|
@@ -5619,7 +6106,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5619
6106
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5620
6107
|
* @returns {undefined}
|
|
5621
6108
|
*/
|
|
5622
|
-
setStartSetupDelay(typeMedia) {
|
|
6109
|
+
setStartSetupDelay(typeMedia: string) {
|
|
5623
6110
|
this[`startSetupDelay${typeMedia}`] = performance.now();
|
|
5624
6111
|
this[`endSetupDelay${typeMedia}`] = undefined;
|
|
5625
6112
|
}
|
|
@@ -5628,7 +6115,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5628
6115
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5629
6116
|
* @returns {undefined}
|
|
5630
6117
|
*/
|
|
5631
|
-
setEndSetupDelay(typeMedia) {
|
|
6118
|
+
setEndSetupDelay(typeMedia: string) {
|
|
5632
6119
|
this[`endSetupDelay${typeMedia}`] = performance.now();
|
|
5633
6120
|
}
|
|
5634
6121
|
|
|
@@ -5636,7 +6123,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5636
6123
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5637
6124
|
* @returns {string} duration between start and end of setup
|
|
5638
6125
|
*/
|
|
5639
|
-
getSetupDelayDuration(typeMedia) {
|
|
6126
|
+
getSetupDelayDuration(typeMedia: string) {
|
|
5640
6127
|
const start = this[`startSetupDelay${typeMedia}`];
|
|
5641
6128
|
const end = this[`endSetupDelay${typeMedia}`];
|
|
5642
6129
|
|
|
@@ -5647,7 +6134,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5647
6134
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5648
6135
|
* @returns {undefined}
|
|
5649
6136
|
*/
|
|
5650
|
-
setStartSendingMediaDelay(typeMedia) {
|
|
6137
|
+
setStartSendingMediaDelay(typeMedia: string) {
|
|
5651
6138
|
this[`startSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5652
6139
|
this[`endSendingMediaDelay${typeMedia}`] = undefined;
|
|
5653
6140
|
}
|
|
@@ -5656,7 +6143,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5656
6143
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5657
6144
|
* @returns {undefined}
|
|
5658
6145
|
*/
|
|
5659
|
-
setEndSendingMediaDelay(typeMedia) {
|
|
6146
|
+
setEndSendingMediaDelay(typeMedia: string) {
|
|
5660
6147
|
this[`endSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5661
6148
|
}
|
|
5662
6149
|
|
|
@@ -5664,7 +6151,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5664
6151
|
* @param {string} typeMedia 'audio' or 'video'
|
|
5665
6152
|
* @returns {string} duration between join response and first media tx
|
|
5666
6153
|
*/
|
|
5667
|
-
getSendingMediaDelayDuration(typeMedia) {
|
|
6154
|
+
getSendingMediaDelayDuration(typeMedia: string) {
|
|
5668
6155
|
const start = this[`startSendingMediaDelay${typeMedia}`];
|
|
5669
6156
|
const end = this[`endSendingMediaDelay${typeMedia}`];
|
|
5670
6157
|
|
|
@@ -5675,7 +6162,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5675
6162
|
*
|
|
5676
6163
|
* @returns {undefined}
|
|
5677
6164
|
*/
|
|
5678
|
-
|
|
6165
|
+
setStartLocalSDPGenRemoteSDPRecvDelay() {
|
|
5679
6166
|
if (!this.startLocalSDPGenRemoteSDPRecvDelay) {
|
|
5680
6167
|
this.startLocalSDPGenRemoteSDPRecvDelay = performance.now();
|
|
5681
6168
|
this.endLocalSDPGenRemoteSDPRecvDelay = undefined;
|
|
@@ -5801,7 +6288,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5801
6288
|
* @public
|
|
5802
6289
|
* @memberof Meeting
|
|
5803
6290
|
*/
|
|
5804
|
-
endMeetingForAll() {
|
|
6291
|
+
public endMeetingForAll() {
|
|
5805
6292
|
Metrics.postEvent({event: eventType.LEAVE, meeting: this, data: {trigger: trigger.USER_INTERACTION, canProceed: false}});
|
|
5806
6293
|
|
|
5807
6294
|
LoggerProxy.logger.log('Meeting:index#endMeetingForAll --> End meeting for All');
|
|
@@ -5886,7 +6373,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5886
6373
|
* @public
|
|
5887
6374
|
* @memberof Meeting
|
|
5888
6375
|
*/
|
|
5889
|
-
isBnrEnabled() {
|
|
6376
|
+
public isBnrEnabled() {
|
|
5890
6377
|
return this.effects && this.effects.isBnrEnabled();
|
|
5891
6378
|
}
|
|
5892
6379
|
|
|
@@ -5897,7 +6384,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5897
6384
|
* @param {MedaiStreamTrack} audioTrack from updateAudio
|
|
5898
6385
|
* @memberof Meeting
|
|
5899
6386
|
*/
|
|
5900
|
-
async internal_enableBNR(audioTrack) {
|
|
6387
|
+
private async internal_enableBNR(audioTrack: any) {
|
|
5901
6388
|
try {
|
|
5902
6389
|
LoggerProxy.logger.info('Meeting:index#internal_enableBNR. Internal enable BNR called');
|
|
5903
6390
|
const bnrAudioTrack = await WebRTCMedia.Effects.BNR.enableBNR(audioTrack);
|
|
@@ -5918,7 +6405,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5918
6405
|
* @public
|
|
5919
6406
|
* @memberof Meeting
|
|
5920
6407
|
*/
|
|
5921
|
-
enableBNR() {
|
|
6408
|
+
public enableBNR() {
|
|
5922
6409
|
if (typeof this.mediaProperties === 'undefined' || typeof this.mediaProperties.audioTrack === 'undefined') {
|
|
5923
6410
|
return Promise.reject(new Error("Meeting doesn't have an audioTrack attached"));
|
|
5924
6411
|
}
|
|
@@ -5953,7 +6440,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5953
6440
|
* @public
|
|
5954
6441
|
* @memberof Meeting
|
|
5955
6442
|
*/
|
|
5956
|
-
disableBNR() {
|
|
6443
|
+
public disableBNR() {
|
|
5957
6444
|
if (typeof this.mediaProperties === 'undefined' || typeof this.mediaProperties.audioTrack === 'undefined') {
|
|
5958
6445
|
return Promise.reject(new Error("Meeting doesn't have an audioTrack attached"));
|
|
5959
6446
|
}
|