@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.
Files changed (274) hide show
  1. package/dist/common/browser-detection.js.map +1 -1
  2. package/dist/common/collection.js.map +1 -1
  3. package/dist/common/config.js.map +1 -1
  4. package/dist/common/errors/captcha-error.js +7 -0
  5. package/dist/common/errors/captcha-error.js.map +1 -1
  6. package/dist/common/errors/intent-to-join.js +8 -0
  7. package/dist/common/errors/intent-to-join.js.map +1 -1
  8. package/dist/common/errors/join-meeting.js +8 -0
  9. package/dist/common/errors/join-meeting.js.map +1 -1
  10. package/dist/common/errors/media.js +7 -0
  11. package/dist/common/errors/media.js.map +1 -1
  12. package/dist/common/errors/parameter.js.map +1 -1
  13. package/dist/common/errors/password-error.js +7 -0
  14. package/dist/common/errors/password-error.js.map +1 -1
  15. package/dist/common/errors/permission.js +7 -0
  16. package/dist/common/errors/permission.js.map +1 -1
  17. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  18. package/dist/common/errors/reconnection.js +7 -0
  19. package/dist/common/errors/reconnection.js.map +1 -1
  20. package/dist/common/errors/stats.js +7 -0
  21. package/dist/common/errors/stats.js.map +1 -1
  22. package/dist/common/errors/webex-errors.js +5 -29
  23. package/dist/common/errors/webex-errors.js.map +1 -1
  24. package/dist/common/errors/webex-meetings-error.js +5 -2
  25. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  26. package/dist/common/events/events-scope.js.map +1 -1
  27. package/dist/common/events/events.js.map +1 -1
  28. package/dist/common/events/trigger-proxy.js.map +1 -1
  29. package/dist/common/events/util.js.map +1 -1
  30. package/dist/common/logs/logger-config.js.map +1 -1
  31. package/dist/common/logs/logger-proxy.js.map +1 -1
  32. package/dist/common/logs/request.js +3 -0
  33. package/dist/common/logs/request.js.map +1 -1
  34. package/dist/common/queue.js.map +1 -1
  35. package/dist/config.js +1 -0
  36. package/dist/config.js.map +1 -1
  37. package/dist/constants.js +15 -74
  38. package/dist/constants.js.map +1 -1
  39. package/dist/locus-info/controlsUtils.js.map +1 -1
  40. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  41. package/dist/locus-info/fullState.js.map +1 -1
  42. package/dist/locus-info/hostUtils.js.map +1 -1
  43. package/dist/locus-info/index.js +43 -5
  44. package/dist/locus-info/index.js.map +1 -1
  45. package/dist/locus-info/infoUtils.js +4 -0
  46. package/dist/locus-info/infoUtils.js.map +1 -1
  47. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  48. package/dist/locus-info/parser.js +12 -3
  49. package/dist/locus-info/parser.js.map +1 -1
  50. package/dist/locus-info/selfUtils.js.map +1 -1
  51. package/dist/media/index.js +71 -210
  52. package/dist/media/index.js.map +1 -1
  53. package/dist/media/internal-media-core-wrapper.js +22 -0
  54. package/dist/media/internal-media-core-wrapper.js.map +1 -0
  55. package/dist/media/properties.js +32 -25
  56. package/dist/media/properties.js.map +1 -1
  57. package/dist/media/util.js +0 -27
  58. package/dist/media/util.js.map +1 -1
  59. package/dist/mediaQualityMetrics/config.js.map +1 -1
  60. package/dist/meeting/effectsState.js +8 -1
  61. package/dist/meeting/effectsState.js.map +1 -1
  62. package/dist/meeting/index.js +1116 -613
  63. package/dist/meeting/index.js.map +1 -1
  64. package/dist/meeting/muteState.js +6 -0
  65. package/dist/meeting/muteState.js.map +1 -1
  66. package/dist/meeting/request.js +55 -24
  67. package/dist/meeting/request.js.map +1 -1
  68. package/dist/meeting/state.js.map +1 -1
  69. package/dist/meeting/util.js +5 -44
  70. package/dist/meeting/util.js.map +1 -1
  71. package/dist/meeting-info/collection.js +4 -1
  72. package/dist/meeting-info/collection.js.map +1 -1
  73. package/dist/meeting-info/index.js +5 -0
  74. package/dist/meeting-info/index.js.map +1 -1
  75. package/dist/meeting-info/meeting-info-v2.js +14 -2
  76. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  77. package/dist/meeting-info/request.js +3 -0
  78. package/dist/meeting-info/request.js.map +1 -1
  79. package/dist/meeting-info/util.js.map +1 -1
  80. package/dist/meeting-info/utilv2.js.map +1 -1
  81. package/dist/meetings/collection.js +4 -1
  82. package/dist/meetings/collection.js.map +1 -1
  83. package/dist/meetings/index.js +136 -25
  84. package/dist/meetings/index.js.map +1 -1
  85. package/dist/meetings/request.js +4 -0
  86. package/dist/meetings/request.js.map +1 -1
  87. package/dist/meetings/util.js +24 -1
  88. package/dist/meetings/util.js.map +1 -1
  89. package/dist/member/index.js +30 -7
  90. package/dist/member/index.js.map +1 -1
  91. package/dist/member/util.js +2 -1
  92. package/dist/member/util.js.map +1 -1
  93. package/dist/members/collection.js +1 -0
  94. package/dist/members/collection.js.map +1 -1
  95. package/dist/members/index.js +82 -1
  96. package/dist/members/index.js.map +1 -1
  97. package/dist/members/request.js +19 -9
  98. package/dist/members/request.js.map +1 -1
  99. package/dist/members/util.js.map +1 -1
  100. package/dist/metrics/config.js.map +1 -1
  101. package/dist/metrics/constants.js.map +1 -1
  102. package/dist/metrics/index.js +8 -0
  103. package/dist/metrics/index.js.map +1 -1
  104. package/dist/multistream/mediaRequestManager.js +133 -0
  105. package/dist/multistream/mediaRequestManager.js.map +1 -0
  106. package/dist/multistream/multistreamMedia.js +116 -0
  107. package/dist/multistream/multistreamMedia.js.map +1 -0
  108. package/dist/multistream/receiveSlot.js +209 -0
  109. package/dist/multistream/receiveSlot.js.map +1 -0
  110. package/dist/multistream/receiveSlotManager.js +195 -0
  111. package/dist/multistream/receiveSlotManager.js.map +1 -0
  112. package/dist/multistream/remoteMedia.js +289 -0
  113. package/dist/multistream/remoteMedia.js.map +1 -0
  114. package/dist/multistream/remoteMediaGroup.js +243 -0
  115. package/dist/multistream/remoteMediaGroup.js.map +1 -0
  116. package/dist/multistream/remoteMediaManager.js +1113 -0
  117. package/dist/multistream/remoteMediaManager.js.map +1 -0
  118. package/dist/networkQualityMonitor/index.js +10 -2
  119. package/dist/networkQualityMonitor/index.js.map +1 -1
  120. package/dist/personal-meeting-room/index.js +11 -0
  121. package/dist/personal-meeting-room/index.js.map +1 -1
  122. package/dist/personal-meeting-room/request.js +2 -1
  123. package/dist/personal-meeting-room/request.js.map +1 -1
  124. package/dist/personal-meeting-room/util.js.map +1 -1
  125. package/dist/reachability/index.js +17 -7
  126. package/dist/reachability/index.js.map +1 -1
  127. package/dist/reachability/request.js +1 -0
  128. package/dist/reachability/request.js.map +1 -1
  129. package/dist/reconnection-manager/index.js +130 -132
  130. package/dist/reconnection-manager/index.js.map +1 -1
  131. package/dist/roap/index.js +58 -231
  132. package/dist/roap/index.js.map +1 -1
  133. package/dist/roap/request.js +7 -116
  134. package/dist/roap/request.js.map +1 -1
  135. package/dist/roap/turnDiscovery.js +20 -6
  136. package/dist/roap/turnDiscovery.js.map +1 -1
  137. package/dist/statsAnalyzer/global.js +2 -0
  138. package/dist/statsAnalyzer/global.js.map +1 -1
  139. package/dist/statsAnalyzer/index.js +58 -37
  140. package/dist/statsAnalyzer/index.js.map +1 -1
  141. package/dist/statsAnalyzer/mqaUtil.js +9 -3
  142. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  143. package/dist/transcription/index.js +10 -3
  144. package/dist/transcription/index.js.map +1 -1
  145. package/package.json +21 -20
  146. package/src/common/{browser-detection.js → browser-detection.ts} +1 -1
  147. package/src/common/collection.ts +6 -6
  148. package/src/common/{config.js → config.ts} +1 -1
  149. package/src/common/errors/{captcha-error.js → captcha-error.ts} +5 -1
  150. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +6 -1
  151. package/src/common/errors/{join-meeting.js → join-meeting.ts} +6 -1
  152. package/src/common/errors/{media.js → media.ts} +5 -1
  153. package/src/common/errors/parameter.ts +3 -2
  154. package/src/common/errors/{password-error.js → password-error.ts} +5 -1
  155. package/src/common/errors/{permission.js → permission.ts} +5 -1
  156. package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
  157. package/src/common/errors/{reconnection.js → reconnection.ts} +5 -1
  158. package/src/common/errors/{stats.js → stats.ts} +5 -1
  159. package/src/common/errors/{webex-errors.js → webex-errors.ts} +1 -20
  160. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +3 -1
  161. package/src/common/events/{events-scope.js → events-scope.ts} +1 -1
  162. package/src/common/events/{events.js → events.ts} +0 -0
  163. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +1 -2
  164. package/src/common/events/{util.js → util.ts} +1 -1
  165. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  166. package/src/common/logs/{logger-proxy.js → logger-proxy.ts} +1 -1
  167. package/src/common/logs/{request.js → request.ts} +12 -2
  168. package/src/common/queue.ts +1 -2
  169. package/src/{config.js → config.ts} +2 -0
  170. package/src/constants.ts +139 -179
  171. package/src/locus-info/{controlsUtils.js → controlsUtils.ts} +4 -4
  172. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  173. package/src/locus-info/{fullState.js → fullState.ts} +1 -1
  174. package/src/locus-info/{hostUtils.js → hostUtils.ts} +5 -5
  175. package/src/locus-info/{index.js → index.ts} +67 -32
  176. package/src/locus-info/{infoUtils.js → infoUtils.ts} +7 -4
  177. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +13 -13
  178. package/src/locus-info/{parser.js → parser.ts} +22 -12
  179. package/src/locus-info/{selfUtils.js → selfUtils.ts} +17 -19
  180. package/src/media/{index.js → index.ts} +130 -205
  181. package/src/media/internal-media-core-wrapper.ts +9 -0
  182. package/src/media/{properties.js → properties.ts} +35 -29
  183. package/src/media/util.ts +16 -0
  184. package/src/mediaQualityMetrics/{config.js → config.ts} +1 -1
  185. package/src/meeting/{effectsState.js → effectsState.ts} +12 -6
  186. package/src/meeting/{index.js → index.ts} +961 -474
  187. package/src/meeting/{muteState.js → muteState.ts} +16 -11
  188. package/src/meeting/{request.js → request.ts} +125 -36
  189. package/src/meeting/{state.js → state.ts} +6 -6
  190. package/src/meeting/{util.js → util.ts} +9 -51
  191. package/src/meeting-info/{collection.js → collection.ts} +4 -1
  192. package/src/meeting-info/{index.js → index.ts} +10 -6
  193. package/src/meeting-info/{meeting-info-v2.js → meeting-info-v2.ts} +28 -10
  194. package/src/meeting-info/{request.js → request.ts} +6 -2
  195. package/src/meeting-info/{util.js → util.ts} +6 -5
  196. package/src/meeting-info/{utilv2.js → utilv2.ts} +8 -7
  197. package/src/meetings/{collection.js → collection.ts} +5 -2
  198. package/src/meetings/{index.js → index.ts} +118 -22
  199. package/src/meetings/{request.js → request.ts} +6 -1
  200. package/src/meetings/{util.js → util.ts} +28 -5
  201. package/src/member/{index.js → index.ts} +46 -15
  202. package/src/member/{util.js → util.ts} +17 -16
  203. package/src/members/{collection.js → collection.ts} +2 -1
  204. package/src/members/{index.js → index.ts} +94 -26
  205. package/src/members/{request.js → request.ts} +16 -5
  206. package/src/members/{util.js → util.ts} +7 -7
  207. package/src/metrics/{config.js → config.ts} +0 -2
  208. package/src/metrics/{constants.js → constants.ts} +0 -0
  209. package/src/metrics/{index.js → index.ts} +27 -8
  210. package/src/multistream/mediaRequestManager.ts +166 -0
  211. package/src/multistream/multistreamMedia.ts +92 -0
  212. package/src/multistream/receiveSlot.ts +141 -0
  213. package/src/multistream/receiveSlotManager.ts +142 -0
  214. package/src/multistream/remoteMedia.ts +228 -0
  215. package/src/multistream/remoteMediaGroup.ts +224 -0
  216. package/src/multistream/remoteMediaManager.ts +911 -0
  217. package/src/networkQualityMonitor/{index.js → index.ts} +18 -3
  218. package/src/personal-meeting-room/{index.js → index.ts} +17 -4
  219. package/src/personal-meeting-room/{request.js → request.ts} +3 -1
  220. package/src/personal-meeting-room/{util.js → util.ts} +1 -1
  221. package/src/reachability/{index.js → index.ts} +28 -17
  222. package/src/reachability/request.ts +4 -2
  223. package/src/reconnection-manager/{index.js → index.ts} +81 -65
  224. package/src/roap/index.ts +229 -0
  225. package/src/roap/{request.js → request.ts} +15 -74
  226. package/src/roap/turnDiscovery.ts +26 -11
  227. package/src/statsAnalyzer/{global.js → global.ts} +2 -0
  228. package/src/statsAnalyzer/{index.js → index.ts} +66 -61
  229. package/src/statsAnalyzer/{mqaUtil.js → mqaUtil.ts} +6 -1
  230. package/src/transcription/{index.js → index.ts} +16 -11
  231. package/test/integration/spec/journey.js +1 -1
  232. package/test/integration/spec/space-meeting.js +1 -2
  233. package/test/unit/spec/locus-info/infoUtils.js +17 -1
  234. package/test/unit/spec/media/index.ts +207 -0
  235. package/test/unit/spec/media/properties.ts +73 -82
  236. package/test/unit/spec/meeting/effectsState.js +1 -3
  237. package/test/unit/spec/meeting/index.js +585 -245
  238. package/test/unit/spec/meeting/muteState.js +7 -0
  239. package/test/unit/spec/meeting/utils.js +63 -2
  240. package/test/unit/spec/meetings/index.js +0 -4
  241. package/test/unit/spec/members/index.js +164 -2
  242. package/test/unit/spec/multistream/mediaRequestManager.ts +515 -0
  243. package/test/unit/spec/multistream/receiveSlot.ts +104 -0
  244. package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
  245. package/test/unit/spec/multistream/remoteMedia.ts +225 -0
  246. package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
  247. package/test/unit/spec/multistream/remoteMediaManager.ts +1309 -0
  248. package/test/unit/spec/reconnection-manager/index.js +68 -2
  249. package/test/unit/spec/roap/index.ts +63 -35
  250. package/test/unit/spec/stats-analyzer/index.js +19 -22
  251. package/dist/peer-connection-manager/index.js +0 -794
  252. package/dist/peer-connection-manager/index.js.map +0 -1
  253. package/dist/peer-connection-manager/util.js +0 -124
  254. package/dist/peer-connection-manager/util.js.map +0 -1
  255. package/dist/roap/collection.js +0 -73
  256. package/dist/roap/collection.js.map +0 -1
  257. package/dist/roap/handler.js +0 -337
  258. package/dist/roap/handler.js.map +0 -1
  259. package/dist/roap/state.js +0 -164
  260. package/dist/roap/state.js.map +0 -1
  261. package/dist/roap/util.js +0 -102
  262. package/dist/roap/util.js.map +0 -1
  263. package/src/media/util.js +0 -38
  264. package/src/peer-connection-manager/index.js +0 -723
  265. package/src/peer-connection-manager/util.ts +0 -117
  266. package/src/roap/collection.js +0 -63
  267. package/src/roap/handler.js +0 -252
  268. package/src/roap/index.js +0 -380
  269. package/src/roap/state.js +0 -149
  270. package/src/roap/util.js +0 -93
  271. package/test/unit/spec/peerconnection-manager/index.js +0 -188
  272. package/test/unit/spec/peerconnection-manager/utils.js +0 -48
  273. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
  274. 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, InvalidSdpError
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 RoapCollection from '../roap/collection';
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({locusUrl: (attrs.locus && attrs.locus.url)}, {parent: this.webex});
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 {Boolean}
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 {floorGrantPending}
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.share()
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
- setupLocusControlsListener() {
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
- setUpLocusMediaSharesListener() {
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: 'stopFloorRequest'
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(invitee, alertIfActive = true) {
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(meetingInfo, destination = null) {
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
- const mtgLocus = locus.locus || locus;
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 {Stream} localShare the local media stream
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. Triggers an event
2963
- * when each is closed.
2964
- * @returns {Promise} returns a resolved promise with an array of closed peer connections
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
- return PeerConnectionManager.close(this.mediaProperties.peerConnection);
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(options = {}) {
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
- }).then(async (join) => {
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.stopFloorRequest();
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 {localStream, localShare, mediaSettings} = options;
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.mediaProperties.setMediaPeerConnection(MediaUtil.createPeerConnection(turnServerInfo));
4246
- this.setMercuryListener();
4247
- PeerConnectionManager.setPeerConnectionEvents(this);
4753
+ this.preMedia(localStream, localShare, mediaSettings);
4754
+
4755
+ const mc = this.createMediaConnection(turnServerInfo);
4248
4756
 
4249
- return this.preMedia(localStream, localShare, mediaSettings);
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(() => Media.attachMedia(this.mediaProperties, {
4252
- meetingId: this.id,
4253
- remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
4254
- enableRtx: this.config.enableRtx,
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((peerConnection) => {
4780
+ .then(() => {
4264
4781
  this.handleMediaLogging(this.mediaProperties);
4265
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection Received from attachMedia `);
4782
+ LoggerProxy.logger.info(`${LOG_HEADER} media connection created`);
4266
4783
 
4267
- this.setRemoteStream(peerConnection);
4784
+ // @ts-ignore - config coming from registerPlugin
4268
4785
  if (this.config.stats.enableStatsAnalyzer) {
4269
- // TODO: ** Dont re create StatsAnalyzer on reconnect or rejoin
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.waitForIceConnectedState()
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.share();
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.peerConnection) {
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
- // If addMedia failes for not establishing connection then
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
- return this.mediaProperties.peerConnection.signalingState === SDP.STABLE && !RoapCollection.isBusy(this.correlationId);
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(options = {}) {
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(() => Media.updateMedia(this.mediaProperties, {
4515
- meetingId: this.id,
4516
- remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
4517
- enableRtx: this.config.enableRtx,
4518
- enableExtmap: this.config.enableExtmap,
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((peerConnection) => {
4521
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection received from updateMedia, ${peerConnection}`);
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
- .then(() =>
4543
- logRequest(this.roap
4544
- .sendRoapMediaRequest({
4545
- sdp: this.mediaProperties.peerConnection.sdp,
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.share();
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
- let previousMediaDirection = {};
4605
-
4606
- if (this.mediaProperties.mediaDirection) {
4607
- previousMediaDirection = {
4608
- sendTrack: this.mediaProperties.mediaDirection.sendAudio,
4609
- receiveTrack: this.mediaProperties.mediaDirection.receiveAudio
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(() => MeetingUtil.updateTransceiver({
4667
- type: 'video',
4668
- sendTrack: options.sendVideo,
4669
- receiveTrack: options.receiveVideo,
4670
- track,
4671
- transceiver: videoTransceiver,
4672
- peerConnection: this.mediaProperties.peerConnection,
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.stopFloorRequest()
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) => MeetingUtil.updateTransceiver({
4743
- type: 'video',
4744
- sendTrack: sendShare,
4745
- receiveTrack: receiveShare,
4746
- track,
4747
- transceiver: shareTransceiver,
4748
- peerConnection: this.mediaProperties.peerConnection,
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.share();
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(stream);
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
- * Start sharing content with server
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
- share() {
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
- * sends stops floor request
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
- stopFloorRequest() {
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#stopFloorRequest --> Error ', error);
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(layoutType, renderInfo = {}) {
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(options = {}) {
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
- setStartLocalSDPGenRemoteSDPRecvDelay() {
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
  }