@webex/plugin-meetings 3.0.0 → 3.1.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/{reconnection-in-progress.js → reconnection-not-started.js} +27 -15
  4. package/dist/common/errors/reconnection-not-started.js.map +1 -0
  5. package/dist/config.js +2 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +18 -6
  8. package/dist/constants.js.map +1 -1
  9. package/dist/index.js +86 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/interpretation/index.js +16 -2
  12. package/dist/interpretation/index.js.map +1 -1
  13. package/dist/interpretation/siLanguage.js +1 -1
  14. package/dist/locus-info/controlsUtils.js +7 -1
  15. package/dist/locus-info/controlsUtils.js.map +1 -1
  16. package/dist/locus-info/index.js +10 -0
  17. package/dist/locus-info/index.js.map +1 -1
  18. package/dist/locus-info/mediaSharesUtils.js +15 -1
  19. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  20. package/dist/locus-info/selfUtils.js +5 -0
  21. package/dist/locus-info/selfUtils.js.map +1 -1
  22. package/dist/media/MediaConnectionAwaiter.js +163 -0
  23. package/dist/media/MediaConnectionAwaiter.js.map +1 -0
  24. package/dist/media/index.js +4 -1
  25. package/dist/media/index.js.map +1 -1
  26. package/dist/media/properties.js +106 -81
  27. package/dist/media/properties.js.map +1 -1
  28. package/dist/meeting/in-meeting-actions.js +6 -0
  29. package/dist/meeting/in-meeting-actions.js.map +1 -1
  30. package/dist/meeting/index.js +1010 -753
  31. package/dist/meeting/index.js.map +1 -1
  32. package/dist/meeting/muteState.js +37 -25
  33. package/dist/meeting/muteState.js.map +1 -1
  34. package/dist/meeting/request.js +32 -23
  35. package/dist/meeting/request.js.map +1 -1
  36. package/dist/meeting/util.js +10 -0
  37. package/dist/meeting/util.js.map +1 -1
  38. package/dist/meeting-info/util.js +304 -267
  39. package/dist/meeting-info/util.js.map +1 -1
  40. package/dist/meeting-info/utilv2.js +334 -295
  41. package/dist/meeting-info/utilv2.js.map +1 -1
  42. package/dist/meetings/index.js +21 -23
  43. package/dist/meetings/index.js.map +1 -1
  44. package/dist/multistream/mediaRequestManager.js +1 -1
  45. package/dist/multistream/mediaRequestManager.js.map +1 -1
  46. package/dist/multistream/remoteMediaGroup.js +16 -2
  47. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  48. package/dist/multistream/remoteMediaManager.js +179 -65
  49. package/dist/multistream/remoteMediaManager.js.map +1 -1
  50. package/dist/multistream/sendSlotManager.js +22 -0
  51. package/dist/multistream/sendSlotManager.js.map +1 -1
  52. package/dist/reachability/clusterReachability.js +29 -15
  53. package/dist/reachability/clusterReachability.js.map +1 -1
  54. package/dist/reachability/index.js +18 -2
  55. package/dist/reachability/index.js.map +1 -1
  56. package/dist/reachability/request.js +12 -10
  57. package/dist/reachability/request.js.map +1 -1
  58. package/dist/reachability/util.js +19 -0
  59. package/dist/reachability/util.js.map +1 -1
  60. package/dist/reconnection-manager/index.js +140 -110
  61. package/dist/reconnection-manager/index.js.map +1 -1
  62. package/dist/roap/index.js +15 -0
  63. package/dist/roap/index.js.map +1 -1
  64. package/dist/roap/request.js +3 -3
  65. package/dist/roap/request.js.map +1 -1
  66. package/dist/roap/turnDiscovery.js +307 -126
  67. package/dist/roap/turnDiscovery.js.map +1 -1
  68. package/dist/statsAnalyzer/index.js +57 -30
  69. package/dist/statsAnalyzer/index.js.map +1 -1
  70. package/dist/statsAnalyzer/mqaUtil.js +3 -0
  71. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  72. package/dist/types/common/errors/reconnection-not-started.d.ts +13 -0
  73. package/dist/{config.d.ts → types/config.d.ts} +1 -0
  74. package/dist/{constants.d.ts → types/constants.d.ts} +15 -6
  75. package/dist/types/index.d.ts +19 -0
  76. package/dist/types/media/MediaConnectionAwaiter.d.ts +61 -0
  77. package/dist/{media → types/media}/properties.d.ts +26 -2
  78. package/dist/{meeting → types/meeting}/in-meeting-actions.d.ts +6 -0
  79. package/dist/{meeting → types/meeting}/index.d.ts +29 -12
  80. package/dist/{meeting → types/meeting}/muteState.d.ts +2 -8
  81. package/dist/{meeting → types/meeting}/request.d.ts +3 -0
  82. package/dist/{meeting → types/meeting}/util.d.ts +3 -0
  83. package/dist/{meeting-info → types/meeting-info}/index.d.ts +1 -1
  84. package/dist/{meeting-info → types/meeting-info}/meeting-info-v2.d.ts +1 -1
  85. package/dist/types/meeting-info/util.d.ts +49 -0
  86. package/dist/types/meeting-info/utilv2.d.ts +65 -0
  87. package/dist/{meetings → types/meetings}/index.d.ts +9 -16
  88. package/dist/{multistream → types/multistream}/mediaRequestManager.d.ts +2 -1
  89. package/dist/{multistream → types/multistream}/remoteMediaGroup.d.ts +2 -0
  90. package/dist/{multistream → types/multistream}/remoteMediaManager.d.ts +15 -0
  91. package/dist/{multistream → types/multistream}/sendSlotManager.d.ts +9 -1
  92. package/dist/{reachability → types/reachability}/clusterReachability.d.ts +1 -0
  93. package/dist/{reachability → types/reachability}/index.d.ts +4 -0
  94. package/dist/{reachability → types/reachability}/util.d.ts +7 -0
  95. package/dist/{reconnection-manager → types/reconnection-manager}/index.d.ts +4 -14
  96. package/dist/{roap → types/roap}/index.d.ts +10 -2
  97. package/dist/{roap → types/roap}/turnDiscovery.d.ts +64 -17
  98. package/dist/webinar/index.js +1 -1
  99. package/package.json +23 -23
  100. package/src/common/errors/reconnection-not-started.ts +25 -0
  101. package/src/config.ts +1 -0
  102. package/src/constants.ts +18 -6
  103. package/src/index.ts +31 -0
  104. package/src/interpretation/index.ts +18 -1
  105. package/src/locus-info/controlsUtils.ts +11 -0
  106. package/src/locus-info/index.ts +16 -0
  107. package/src/locus-info/mediaSharesUtils.ts +16 -0
  108. package/src/locus-info/selfUtils.ts +5 -0
  109. package/src/media/MediaConnectionAwaiter.ts +174 -0
  110. package/src/media/index.ts +3 -1
  111. package/src/media/properties.ts +73 -46
  112. package/src/meeting/in-meeting-actions.ts +12 -0
  113. package/src/meeting/index.ts +389 -180
  114. package/src/meeting/muteState.ts +34 -20
  115. package/src/meeting/request.ts +18 -2
  116. package/src/meeting/util.ts +9 -0
  117. package/src/meeting-info/util.ts +241 -233
  118. package/src/meeting-info/utilv2.ts +250 -243
  119. package/src/meetings/index.ts +20 -24
  120. package/src/multistream/mediaRequestManager.ts +4 -1
  121. package/src/multistream/remoteMediaGroup.ts +19 -0
  122. package/src/multistream/remoteMediaManager.ts +101 -16
  123. package/src/multistream/sendSlotManager.ts +28 -0
  124. package/src/reachability/clusterReachability.ts +20 -5
  125. package/src/reachability/index.ts +24 -1
  126. package/src/reachability/request.ts +15 -11
  127. package/src/reachability/util.ts +21 -0
  128. package/src/reconnection-manager/index.ts +129 -106
  129. package/src/roap/index.ts +25 -3
  130. package/src/roap/request.ts +3 -3
  131. package/src/roap/turnDiscovery.ts +244 -78
  132. package/src/statsAnalyzer/index.ts +67 -27
  133. package/src/statsAnalyzer/mqaUtil.ts +5 -0
  134. package/test/integration/spec/journey.js +14 -14
  135. package/test/integration/spec/space-meeting.js +1 -1
  136. package/test/unit/spec/interpretation/index.ts +39 -3
  137. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  138. package/test/unit/spec/locus-info/index.js +49 -19
  139. package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
  140. package/test/unit/spec/locus-info/selfUtils.js +42 -12
  141. package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
  142. package/test/unit/spec/media/index.ts +89 -78
  143. package/test/unit/spec/media/properties.ts +160 -209
  144. package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
  145. package/test/unit/spec/meeting/index.js +833 -205
  146. package/test/unit/spec/meeting/muteState.js +219 -67
  147. package/test/unit/spec/meeting/request.js +21 -0
  148. package/test/unit/spec/meeting/utils.js +9 -1
  149. package/test/unit/spec/meeting-info/utilv2.js +6 -0
  150. package/test/unit/spec/meetings/index.js +41 -26
  151. package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
  152. package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
  153. package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
  154. package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
  155. package/test/unit/spec/reachability/clusterReachability.ts +86 -22
  156. package/test/unit/spec/reachability/index.ts +197 -60
  157. package/test/unit/spec/reachability/request.js +15 -7
  158. package/test/unit/spec/reachability/util.ts +32 -2
  159. package/test/unit/spec/reconnection-manager/index.js +155 -39
  160. package/test/unit/spec/roap/index.ts +61 -6
  161. package/test/unit/spec/roap/turnDiscovery.ts +298 -16
  162. package/test/unit/spec/stats-analyzer/index.js +190 -0
  163. package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
  164. package/dist/common/errors/reconnection-in-progress.js.map +0 -1
  165. package/dist/index.d.ts +0 -7
  166. package/dist/meeting-info/util.d.ts +0 -2
  167. package/dist/meeting-info/utilv2.d.ts +0 -2
  168. package/dist/member/member.types.d.ts +0 -11
  169. package/dist/member/member.types.js +0 -17
  170. package/dist/member/member.types.js.map +0 -1
  171. package/src/common/errors/reconnection-in-progress.ts +0 -8
  172. package/src/member/member.types.ts +0 -13
  173. /package/dist/{annotation → types/annotation}/annotation.types.d.ts +0 -0
  174. /package/dist/{annotation → types/annotation}/constants.d.ts +0 -0
  175. /package/dist/{annotation → types/annotation}/index.d.ts +0 -0
  176. /package/dist/{breakouts → types/breakouts}/breakout.d.ts +0 -0
  177. /package/dist/{breakouts → types/breakouts}/collection.d.ts +0 -0
  178. /package/dist/{breakouts → types/breakouts}/edit-lock-error.d.ts +0 -0
  179. /package/dist/{breakouts → types/breakouts}/events.d.ts +0 -0
  180. /package/dist/{breakouts → types/breakouts}/index.d.ts +0 -0
  181. /package/dist/{breakouts → types/breakouts}/request.d.ts +0 -0
  182. /package/dist/{breakouts → types/breakouts}/utils.d.ts +0 -0
  183. /package/dist/{common → types/common}/browser-detection.d.ts +0 -0
  184. /package/dist/{common → types/common}/collection.d.ts +0 -0
  185. /package/dist/{common → types/common}/config.d.ts +0 -0
  186. /package/dist/{common → types/common}/errors/captcha-error.d.ts +0 -0
  187. /package/dist/{common → types/common}/errors/intent-to-join.d.ts +0 -0
  188. /package/dist/{common → types/common}/errors/join-meeting.d.ts +0 -0
  189. /package/dist/{common → types/common}/errors/media.d.ts +0 -0
  190. /package/dist/{common → types/common}/errors/no-meeting-info.d.ts +0 -0
  191. /package/dist/{common → types/common}/errors/parameter.d.ts +0 -0
  192. /package/dist/{common → types/common}/errors/password-error.d.ts +0 -0
  193. /package/dist/{common → types/common}/errors/permission.d.ts +0 -0
  194. /package/dist/{common → types/common}/errors/reclaim-host-role-errors.d.ts +0 -0
  195. /package/dist/{common → types/common}/errors/reconnection.d.ts +0 -0
  196. /package/dist/{common → types/common}/errors/stats.d.ts +0 -0
  197. /package/dist/{common → types/common}/errors/webex-errors.d.ts +0 -0
  198. /package/dist/{common → types/common}/errors/webex-meetings-error.d.ts +0 -0
  199. /package/dist/{common → types/common}/events/events-scope.d.ts +0 -0
  200. /package/dist/{common → types/common}/events/events.d.ts +0 -0
  201. /package/dist/{common → types/common}/events/trigger-proxy.d.ts +0 -0
  202. /package/dist/{common → types/common}/events/util.d.ts +0 -0
  203. /package/dist/{common → types/common}/logs/logger-config.d.ts +0 -0
  204. /package/dist/{common → types/common}/logs/logger-proxy.d.ts +0 -0
  205. /package/dist/{common → types/common}/logs/request.d.ts +0 -0
  206. /package/dist/{common → types/common}/queue.d.ts +0 -0
  207. /package/dist/{controls-options-manager → types/controls-options-manager}/constants.d.ts +0 -0
  208. /package/dist/{controls-options-manager → types/controls-options-manager}/enums.d.ts +0 -0
  209. /package/dist/{controls-options-manager → types/controls-options-manager}/index.d.ts +0 -0
  210. /package/dist/{controls-options-manager → types/controls-options-manager}/types.d.ts +0 -0
  211. /package/dist/{controls-options-manager → types/controls-options-manager}/util.d.ts +0 -0
  212. /package/dist/{interceptors → types/interceptors}/index.d.ts +0 -0
  213. /package/dist/{interceptors → types/interceptors}/locusRetry.d.ts +0 -0
  214. /package/dist/{interpretation → types/interpretation}/collection.d.ts +0 -0
  215. /package/dist/{interpretation → types/interpretation}/index.d.ts +0 -0
  216. /package/dist/{interpretation → types/interpretation}/siLanguage.d.ts +0 -0
  217. /package/dist/{locus-info → types/locus-info}/controlsUtils.d.ts +0 -0
  218. /package/dist/{locus-info → types/locus-info}/embeddedAppsUtils.d.ts +0 -0
  219. /package/dist/{locus-info → types/locus-info}/fullState.d.ts +0 -0
  220. /package/dist/{locus-info → types/locus-info}/hostUtils.d.ts +0 -0
  221. /package/dist/{locus-info → types/locus-info}/index.d.ts +0 -0
  222. /package/dist/{locus-info → types/locus-info}/infoUtils.d.ts +0 -0
  223. /package/dist/{locus-info → types/locus-info}/mediaSharesUtils.d.ts +0 -0
  224. /package/dist/{locus-info → types/locus-info}/parser.d.ts +0 -0
  225. /package/dist/{locus-info → types/locus-info}/selfUtils.d.ts +0 -0
  226. /package/dist/{media → types/media}/index.d.ts +0 -0
  227. /package/dist/{media → types/media}/util.d.ts +0 -0
  228. /package/dist/{mediaQualityMetrics → types/mediaQualityMetrics}/config.d.ts +0 -0
  229. /package/dist/{meeting → types/meeting}/locusMediaRequest.d.ts +0 -0
  230. /package/dist/{meeting → types/meeting}/request.type.d.ts +0 -0
  231. /package/dist/{meeting → types/meeting}/state.d.ts +0 -0
  232. /package/dist/{meeting → types/meeting}/voicea-meeting.d.ts +0 -0
  233. /package/dist/{meeting-info → types/meeting-info}/collection.d.ts +0 -0
  234. /package/dist/{meeting-info → types/meeting-info}/request.d.ts +0 -0
  235. /package/dist/{meetings → types/meetings}/collection.d.ts +0 -0
  236. /package/dist/{meetings → types/meetings}/meetings.types.d.ts +0 -0
  237. /package/dist/{meetings → types/meetings}/request.d.ts +0 -0
  238. /package/dist/{meetings → types/meetings}/util.d.ts +0 -0
  239. /package/dist/{member → types/member}/index.d.ts +0 -0
  240. /package/dist/{member → types/member}/types.d.ts +0 -0
  241. /package/dist/{member → types/member}/util.d.ts +0 -0
  242. /package/dist/{members → types/members}/collection.d.ts +0 -0
  243. /package/dist/{members → types/members}/index.d.ts +0 -0
  244. /package/dist/{members → types/members}/request.d.ts +0 -0
  245. /package/dist/{members → types/members}/types.d.ts +0 -0
  246. /package/dist/{members → types/members}/util.d.ts +0 -0
  247. /package/dist/{metrics → types/metrics}/constants.d.ts +0 -0
  248. /package/dist/{metrics → types/metrics}/index.d.ts +0 -0
  249. /package/dist/{multistream → types/multistream}/receiveSlot.d.ts +0 -0
  250. /package/dist/{multistream → types/multistream}/receiveSlotManager.d.ts +0 -0
  251. /package/dist/{multistream → types/multistream}/remoteMedia.d.ts +0 -0
  252. /package/dist/{networkQualityMonitor → types/networkQualityMonitor}/index.d.ts +0 -0
  253. /package/dist/{personal-meeting-room → types/personal-meeting-room}/index.d.ts +0 -0
  254. /package/dist/{personal-meeting-room → types/personal-meeting-room}/request.d.ts +0 -0
  255. /package/dist/{personal-meeting-room → types/personal-meeting-room}/util.d.ts +0 -0
  256. /package/dist/{reachability → types/reachability}/request.d.ts +0 -0
  257. /package/dist/{reactions → types/reactions}/constants.d.ts +0 -0
  258. /package/dist/{reactions → types/reactions}/reactions.d.ts +0 -0
  259. /package/dist/{reactions → types/reactions}/reactions.type.d.ts +0 -0
  260. /package/dist/{recording-controller → types/recording-controller}/enums.d.ts +0 -0
  261. /package/dist/{recording-controller → types/recording-controller}/index.d.ts +0 -0
  262. /package/dist/{recording-controller → types/recording-controller}/util.d.ts +0 -0
  263. /package/dist/{roap → types/roap}/request.d.ts +0 -0
  264. /package/dist/{rtcMetrics → types/rtcMetrics}/constants.d.ts +0 -0
  265. /package/dist/{rtcMetrics → types/rtcMetrics}/index.d.ts +0 -0
  266. /package/dist/{statsAnalyzer → types/statsAnalyzer}/global.d.ts +0 -0
  267. /package/dist/{statsAnalyzer → types/statsAnalyzer}/index.d.ts +0 -0
  268. /package/dist/{statsAnalyzer → types/statsAnalyzer}/mqaUtil.d.ts +0 -0
  269. /package/dist/{transcription → types/transcription}/index.d.ts +0 -0
  270. /package/dist/{webinar → types/webinar}/collection.d.ts +0 -0
  271. /package/dist/{webinar → types/webinar}/index.d.ts +0 -0
  272. /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
@@ -10,6 +10,8 @@ import {
10
10
  ClientEventLeaveReason,
11
11
  CallDiagnosticUtils,
12
12
  } from '@webex/internal-plugin-metrics';
13
+ import {ClientEvent as RawClientEvent} from '@webex/event-dictionary-ts';
14
+
13
15
  import {
14
16
  ConnectionState,
15
17
  Errors,
@@ -50,8 +52,13 @@ import {
50
52
  import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
51
53
  import NetworkQualityMonitor from '../networkQualityMonitor';
52
54
  import LoggerProxy from '../common/logs/logger-proxy';
55
+ import EventsUtil from '../common/events/util';
53
56
  import Trigger from '../common/events/trigger-proxy';
54
- import Roap from '../roap/index';
57
+ import Roap, {
58
+ type TurnDiscoveryResult,
59
+ type TurnServerInfo,
60
+ type TurnDiscoverySkipReason,
61
+ } from '../roap/index';
55
62
  import Media, {type BundlePolicy} from '../media';
56
63
  import MediaProperties from '../media/properties';
57
64
  import MeetingStateMachine from './state';
@@ -59,6 +66,7 @@ import {createMuteState} from './muteState';
59
66
  import LocusInfo from '../locus-info';
60
67
  import Metrics from '../metrics';
61
68
  import ReconnectionManager from '../reconnection-manager';
69
+ import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
62
70
  import MeetingRequest from './request';
63
71
  import Members from '../members/index';
64
72
  import MeetingUtil from './util';
@@ -69,8 +77,6 @@ import MediaUtil from '../media/util';
69
77
  import {Reactions, SkinTones} from '../reactions/reactions';
70
78
  import PasswordError from '../common/errors/password-error';
71
79
  import CaptchaError from '../common/errors/captcha-error';
72
- import ReconnectionError from '../common/errors/reconnection';
73
- import ReconnectInProgress from '../common/errors/reconnection-in-progress';
74
80
  import {
75
81
  _CONVERSATION_URL_,
76
82
  _INCOMING_,
@@ -110,6 +116,7 @@ import {
110
116
  MEETING_PERMISSION_TOKEN_REFRESH_REASON,
111
117
  ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
112
118
  RECONNECTION,
119
+ NAMED_MEDIA_GROUP_TYPE_AUDIO,
113
120
  LANGUAGE_ENGLISH,
114
121
  } from '../constants';
115
122
  import BEHAVIORAL_METRICS from '../metrics/constants';
@@ -119,7 +126,6 @@ import {
119
126
  MeetingInfoV2CaptchaError,
120
127
  MeetingInfoV2PolicyError,
121
128
  } from '../meeting-info/meeting-info-v2';
122
- import BrowserDetection from '../common/browser-detection';
123
129
  import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
124
130
  import SendSlotManager from '../multistream/sendSlotManager';
125
131
  import {MediaRequestManager} from '../multistream/mediaRequestManager';
@@ -147,8 +153,6 @@ import ControlsOptionsManager from '../controls-options-manager';
147
153
  import PermissionError from '../common/errors/permission';
148
154
  import {LocusMediaRequest} from './locusMediaRequest';
149
155
 
150
- const {isBrowser} = BrowserDetection();
151
-
152
156
  const logRequest = (request: any, {logText = ''}) => {
153
157
  LoggerProxy.logger.info(`${logText} - sending request`);
154
158
 
@@ -614,8 +618,8 @@ export default class Meeting extends StatelessWebexPlugin {
614
618
  resourceUrl: string;
615
619
  selfId: string;
616
620
  state: any;
617
- localAudioStreamMuteStateHandler: (muted: boolean) => void;
618
- localVideoStreamMuteStateHandler: (muted: boolean) => void;
621
+ localAudioStreamMuteStateHandler: () => void;
622
+ localVideoStreamMuteStateHandler: () => void;
619
623
  localOutputTrackChangeHandler: () => void;
620
624
  roles: any[];
621
625
  environment: string;
@@ -623,21 +627,23 @@ export default class Meeting extends StatelessWebexPlugin {
623
627
  allowMediaInLobby: boolean;
624
628
  localShareInstanceId: string;
625
629
  remoteShareInstanceId: string;
626
- turnDiscoverySkippedReason: string;
630
+ turnDiscoverySkippedReason: TurnDiscoverySkipReason;
627
631
  turnServerUsed: boolean;
628
632
  areVoiceaEventsSetup = false;
633
+
629
634
  voiceaListenerCallbacks: object = {
630
635
  [VOICEAEVENTS.VOICEA_ANNOUNCEMENT]: (payload: Transcription['languageOptions']) => {
631
636
  this.transcription.languageOptions = payload;
632
- Trigger.trigger(
633
- this,
634
- {
637
+
638
+ LoggerProxy.logger.debug(
639
+ `${EventsUtil.getScopeLog({
635
640
  file: 'meeting/index',
636
641
  function: 'setUpVoiceaListeners',
637
- },
638
- EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION,
639
- payload
642
+ })}event#${EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION}`
640
643
  );
644
+
645
+ // @ts-ignore
646
+ this.trigger(EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION, payload);
641
647
  },
642
648
  [VOICEAEVENTS.CAPTIONS_TURNED_ON]: () => {
643
649
  this.transcription.status = TURN_ON_CAPTION_STATUS.ENABLED;
@@ -650,18 +656,19 @@ export default class Meeting extends StatelessWebexPlugin {
650
656
  },
651
657
  [VOICEAEVENTS.NEW_CAPTION]: (data) => {
652
658
  processNewCaptions({data, meeting: this});
653
- Trigger.trigger(
654
- this,
655
- {
659
+
660
+ LoggerProxy.logger.debug(
661
+ `${EventsUtil.getScopeLog({
656
662
  file: 'meeting/index',
657
663
  function: 'setUpVoiceaListeners',
658
- },
659
- EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED,
660
- {
661
- captions: this.transcription.captions,
662
- interimCaptions: this.transcription.interimCaptions,
663
- }
664
+ })}event#${EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED}`
664
665
  );
666
+
667
+ // @ts-ignore
668
+ this.trigger(EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED, {
669
+ captions: this.transcription.captions,
670
+ interimCaptions: this.transcription.interimCaptions,
671
+ });
665
672
  },
666
673
  };
667
674
 
@@ -670,6 +677,7 @@ export default class Meeting extends StatelessWebexPlugin {
670
677
  private deferSDPAnswer?: Defer; // used for waiting for a response
671
678
  private sdpResponseTimer?: ReturnType<typeof setTimeout>;
672
679
  private hasMediaConnectionConnectedAtLeastOnce: boolean;
680
+ private joinWithMediaRetryInfo?: {isRetry: boolean; prevJoinResponse?: any};
673
681
 
674
682
  /**
675
683
  * @param {Object} attrs
@@ -1380,12 +1388,12 @@ export default class Meeting extends StatelessWebexPlugin {
1380
1388
  */
1381
1389
  this.remoteMediaManager = null;
1382
1390
 
1383
- this.localAudioStreamMuteStateHandler = (muted: boolean) => {
1384
- this.audio.handleLocalStreamMuteStateChange(this, muted);
1391
+ this.localAudioStreamMuteStateHandler = () => {
1392
+ this.audio.handleLocalStreamMuteStateChange(this);
1385
1393
  };
1386
1394
 
1387
- this.localVideoStreamMuteStateHandler = (muted: boolean) => {
1388
- this.video.handleLocalStreamMuteStateChange(this, muted);
1395
+ this.localVideoStreamMuteStateHandler = () => {
1396
+ this.video.handleLocalStreamMuteStateChange(this);
1389
1397
  };
1390
1398
 
1391
1399
  // The handling of output track changes should be done inside
@@ -1451,6 +1459,15 @@ export default class Meeting extends StatelessWebexPlugin {
1451
1459
  * @memberof Meeting
1452
1460
  */
1453
1461
  this.hasMediaConnectionConnectedAtLeastOnce = false;
1462
+
1463
+ /**
1464
+ * Information needed for a retry of a call to joinWithMedia
1465
+ * @instance
1466
+ * @type {{isRetry: boolean; prevJoinResponse?: any}}
1467
+ * @private
1468
+ * @memberof Meeting
1469
+ */
1470
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
1454
1471
  }
1455
1472
 
1456
1473
  /**
@@ -2393,6 +2410,18 @@ export default class Meeting extends StatelessWebexPlugin {
2393
2410
  }
2394
2411
  );
2395
2412
 
2413
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_MANUAL_CAPTION_UPDATED, ({enable}) => {
2414
+ Trigger.trigger(
2415
+ this,
2416
+ {
2417
+ file: 'meeting/index',
2418
+ function: 'setupLocusControlsListener',
2419
+ },
2420
+ EVENT_TRIGGERS.MEETING_MANUAL_CAPTION_UPDATED,
2421
+ {enable}
2422
+ );
2423
+ });
2424
+
2396
2425
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED, ({breakout}) => {
2397
2426
  this.breakouts.updateBreakout(breakout);
2398
2427
  Trigger.trigger(
@@ -2518,6 +2547,7 @@ export default class Meeting extends StatelessWebexPlugin {
2518
2547
  {
2519
2548
  annotationInfo: contentShare?.annotation,
2520
2549
  meetingId: this.id,
2550
+ resourceType: contentShare?.resourceType,
2521
2551
  }
2522
2552
  );
2523
2553
  }
@@ -2546,7 +2576,8 @@ export default class Meeting extends StatelessWebexPlugin {
2546
2576
  contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
2547
2577
  whiteboardShare.beneficiaryId === previousWhiteboardShare?.beneficiaryId &&
2548
2578
  whiteboardShare.disposition === previousWhiteboardShare?.disposition &&
2549
- whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl
2579
+ whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl &&
2580
+ contentShare.resourceType === previousContentShare?.resourceType
2550
2581
  ) {
2551
2582
  // nothing changed, so ignore
2552
2583
  // (this happens when we steal presentation from remote)
@@ -2668,6 +2699,7 @@ export default class Meeting extends StatelessWebexPlugin {
2668
2699
  url: contentShare.url,
2669
2700
  shareInstanceId: this.remoteShareInstanceId,
2670
2701
  annotationInfo: contentShare.annotation,
2702
+ resourceType: contentShare.resourceType,
2671
2703
  }
2672
2704
  );
2673
2705
  };
@@ -2760,6 +2792,7 @@ export default class Meeting extends StatelessWebexPlugin {
2760
2792
  url: contentShare.url,
2761
2793
  shareInstanceId: this.remoteShareInstanceId,
2762
2794
  annotationInfo: contentShare.annotation,
2795
+ resourceType: contentShare.resourceType,
2763
2796
  }
2764
2797
  );
2765
2798
  this.members.locusMediaSharesUpdate(payload);
@@ -3069,6 +3102,7 @@ export default class Meeting extends StatelessWebexPlugin {
3069
3102
  options: {meetingId: this.id},
3070
3103
  });
3071
3104
  }
3105
+ this.updateLLMConnection();
3072
3106
  });
3073
3107
 
3074
3108
  // @ts-ignore - check if MEDIA_INACTIVITY exists
@@ -3127,7 +3161,7 @@ export default class Meeting extends StatelessWebexPlugin {
3127
3161
  });
3128
3162
 
3129
3163
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED, (payload) => {
3130
- this.simultaneousInterpretation.updateSelfInterpretation(payload);
3164
+ const targetChanged = this.simultaneousInterpretation.updateSelfInterpretation(payload);
3131
3165
  Trigger.trigger(
3132
3166
  this,
3133
3167
  {
@@ -3136,6 +3170,9 @@ export default class Meeting extends StatelessWebexPlugin {
3136
3170
  },
3137
3171
  EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
3138
3172
  );
3173
+ if (targetChanged && this.mediaProperties.audioStream) {
3174
+ this.setSendNamedMediaGroup(MediaType.AudioMain);
3175
+ }
3139
3176
  });
3140
3177
 
3141
3178
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
@@ -3450,8 +3487,10 @@ export default class Meeting extends StatelessWebexPlugin {
3450
3487
  this.owner =
3451
3488
  locusMeetingObject?.info.owner || meetingInfo?.owner || meetingInfo?.hostId || this.owner;
3452
3489
  this.permissionToken = meetingInfo?.permissionToken;
3453
- this.setPermissionTokenPayload(meetingInfo?.permissionToken);
3454
- this.setSelfUserPolicies();
3490
+ if (this.permissionToken) {
3491
+ this.setPermissionTokenPayload(meetingInfo?.permissionToken);
3492
+ this.setSelfUserPolicies();
3493
+ }
3455
3494
  // Need to populate environment when sending CA event
3456
3495
  this.environment = locusMeetingObject?.info.channel || meetingInfo?.channel;
3457
3496
  }
@@ -3557,6 +3596,9 @@ export default class Meeting extends StatelessWebexPlugin {
3557
3596
  canStartTranscribing: MeetingUtil.canStartTranscribing(this.userDisplayHints),
3558
3597
  canStopTranscribing: MeetingUtil.canStopTranscribing(this.userDisplayHints),
3559
3598
  isClosedCaptionActive: MeetingUtil.isClosedCaptionActive(this.userDisplayHints),
3599
+ canStartManualCaption: MeetingUtil.canStartManualCaption(this.userDisplayHints),
3600
+ canStopManualCaption: MeetingUtil.canStopManualCaption(this.userDisplayHints),
3601
+ isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
3560
3602
  isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
3561
3603
  isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
3562
3604
  canViewCaptionPanel: MeetingUtil.canViewCaptionPanel(this.userDisplayHints),
@@ -3887,7 +3929,14 @@ export default class Meeting extends StatelessWebexPlugin {
3887
3929
  private async setLocalAudioStream(localStream?: LocalMicrophoneStream) {
3888
3930
  const oldStream = this.mediaProperties.audioStream;
3889
3931
 
3890
- oldStream?.off(StreamEventNames.MuteStateChange, this.localAudioStreamMuteStateHandler);
3932
+ oldStream?.off(
3933
+ LocalStreamEventNames.UserMuteStateChange,
3934
+ this.localAudioStreamMuteStateHandler
3935
+ );
3936
+ oldStream?.off(
3937
+ LocalStreamEventNames.SystemMuteStateChange,
3938
+ this.localAudioStreamMuteStateHandler
3939
+ );
3891
3940
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3892
3941
 
3893
3942
  // we don't update this.mediaProperties.mediaDirection.sendAudio, because we always keep it as true to avoid extra SDP exchanges
@@ -3895,7 +3944,14 @@ export default class Meeting extends StatelessWebexPlugin {
3895
3944
 
3896
3945
  this.audio.handleLocalStreamChange(this);
3897
3946
 
3898
- localStream?.on(StreamEventNames.MuteStateChange, this.localAudioStreamMuteStateHandler);
3947
+ localStream?.on(
3948
+ LocalStreamEventNames.UserMuteStateChange,
3949
+ this.localAudioStreamMuteStateHandler
3950
+ );
3951
+ localStream?.on(
3952
+ LocalStreamEventNames.SystemMuteStateChange,
3953
+ this.localAudioStreamMuteStateHandler
3954
+ );
3899
3955
  localStream?.on(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3900
3956
 
3901
3957
  if (!this.isMultistream || !localStream) {
@@ -3915,7 +3971,14 @@ export default class Meeting extends StatelessWebexPlugin {
3915
3971
  private async setLocalVideoStream(localStream?: LocalCameraStream) {
3916
3972
  const oldStream = this.mediaProperties.videoStream;
3917
3973
 
3918
- oldStream?.off(StreamEventNames.MuteStateChange, this.localVideoStreamMuteStateHandler);
3974
+ oldStream?.off(
3975
+ LocalStreamEventNames.UserMuteStateChange,
3976
+ this.localVideoStreamMuteStateHandler
3977
+ );
3978
+ oldStream?.off(
3979
+ LocalStreamEventNames.SystemMuteStateChange,
3980
+ this.localVideoStreamMuteStateHandler
3981
+ );
3919
3982
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3920
3983
 
3921
3984
  // we don't update this.mediaProperties.mediaDirection.sendVideo, because we always keep it as true to avoid extra SDP exchanges
@@ -3923,7 +3986,14 @@ export default class Meeting extends StatelessWebexPlugin {
3923
3986
 
3924
3987
  this.video.handleLocalStreamChange(this);
3925
3988
 
3926
- localStream?.on(StreamEventNames.MuteStateChange, this.localVideoStreamMuteStateHandler);
3989
+ localStream?.on(
3990
+ LocalStreamEventNames.UserMuteStateChange,
3991
+ this.localVideoStreamMuteStateHandler
3992
+ );
3993
+ localStream?.on(
3994
+ LocalStreamEventNames.SystemMuteStateChange,
3995
+ this.localVideoStreamMuteStateHandler
3996
+ );
3927
3997
  localStream?.on(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3928
3998
 
3929
3999
  if (!this.isMultistream || !localStream) {
@@ -3944,14 +4014,17 @@ export default class Meeting extends StatelessWebexPlugin {
3944
4014
  private async setLocalShareVideoStream(localDisplayStream?: LocalDisplayStream) {
3945
4015
  const oldStream = this.mediaProperties.shareVideoStream;
3946
4016
 
3947
- oldStream?.off(StreamEventNames.MuteStateChange, this.handleShareVideoStreamMuteStateChange);
4017
+ oldStream?.off(
4018
+ LocalStreamEventNames.SystemMuteStateChange,
4019
+ this.handleShareVideoStreamMuteStateChange
4020
+ );
3948
4021
  oldStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3949
4022
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3950
4023
 
3951
4024
  this.mediaProperties.setLocalShareVideoStream(localDisplayStream);
3952
4025
 
3953
4026
  localDisplayStream?.on(
3954
- StreamEventNames.MuteStateChange,
4027
+ LocalStreamEventNames.SystemMuteStateChange,
3955
4028
  this.handleShareVideoStreamMuteStateChange
3956
4029
  );
3957
4030
  localDisplayStream?.on(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
@@ -4037,10 +4110,24 @@ export default class Meeting extends StatelessWebexPlugin {
4037
4110
  public cleanupLocalStreams() {
4038
4111
  const {audioStream, videoStream, shareAudioStream, shareVideoStream} = this.mediaProperties;
4039
4112
 
4040
- audioStream?.off(StreamEventNames.MuteStateChange, this.localAudioStreamMuteStateHandler);
4113
+ audioStream?.off(
4114
+ LocalStreamEventNames.UserMuteStateChange,
4115
+ this.localAudioStreamMuteStateHandler
4116
+ );
4117
+ audioStream?.off(
4118
+ LocalStreamEventNames.SystemMuteStateChange,
4119
+ this.localAudioStreamMuteStateHandler
4120
+ );
4041
4121
  audioStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
4042
4122
 
4043
- videoStream?.off(StreamEventNames.MuteStateChange, this.localVideoStreamMuteStateHandler);
4123
+ videoStream?.off(
4124
+ LocalStreamEventNames.UserMuteStateChange,
4125
+ this.localVideoStreamMuteStateHandler
4126
+ );
4127
+ videoStream?.off(
4128
+ LocalStreamEventNames.SystemMuteStateChange,
4129
+ this.localVideoStreamMuteStateHandler
4130
+ );
4044
4131
  videoStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
4045
4132
 
4046
4133
  shareAudioStream?.off(StreamEventNames.Ended, this.handleShareAudioStreamEnded);
@@ -4048,8 +4135,9 @@ export default class Meeting extends StatelessWebexPlugin {
4048
4135
  LocalStreamEventNames.OutputTrackChange,
4049
4136
  this.localOutputTrackChangeHandler
4050
4137
  );
4138
+
4051
4139
  shareVideoStream?.off(
4052
- StreamEventNames.MuteStateChange,
4140
+ LocalStreamEventNames.SystemMuteStateChange,
4053
4141
  this.handleShareVideoStreamMuteStateChange
4054
4142
  );
4055
4143
  shareVideoStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
@@ -4441,47 +4529,112 @@ export default class Meeting extends StatelessWebexPlugin {
4441
4529
  * }
4442
4530
  * })
4443
4531
  */
4444
- public joinWithMedia(
4532
+ public async joinWithMedia(
4445
4533
  options: {
4446
4534
  joinOptions?: any;
4447
4535
  mediaOptions?: AddMediaOptions;
4448
4536
  } = {}
4449
4537
  ) {
4450
- const {mediaOptions, joinOptions} = options;
4538
+ const {mediaOptions, joinOptions = {}} = options;
4539
+ const {isRetry, prevJoinResponse} = this.joinWithMediaRetryInfo;
4451
4540
 
4452
4541
  if (!mediaOptions?.allowMediaInLobby) {
4453
4542
  return Promise.reject(
4454
4543
  new ParameterError('joinWithMedia() can only be used with allowMediaInLobby set to true')
4455
4544
  );
4456
4545
  }
4546
+ this.allowMediaInLobby = true;
4457
4547
 
4458
4548
  LoggerProxy.logger.info('Meeting:index#joinWithMedia called');
4459
4549
 
4460
- return this.join(joinOptions)
4461
- .then((joinResponse) =>
4462
- this.addMedia(mediaOptions).then((mediaResponse) => ({
4463
- join: joinResponse,
4464
- media: mediaResponse,
4465
- }))
4466
- )
4467
- .catch((error) => {
4468
- LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
4550
+ let joined = false;
4551
+ let joinResponse = prevJoinResponse;
4469
4552
 
4470
- Metrics.sendBehavioralMetric(
4471
- BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
4472
- {
4473
- correlation_id: this.correlationId,
4474
- locus_id: this.locusUrl.split('/').pop(),
4475
- reason: error.message,
4476
- stack: error.stack,
4477
- },
4478
- {
4479
- type: error.name,
4480
- }
4553
+ try {
4554
+ let turnServerInfo;
4555
+ let turnDiscoverySkippedReason;
4556
+
4557
+ // @ts-ignore
4558
+ joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
4559
+ const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(this, true);
4560
+
4561
+ ({turnDiscoverySkippedReason} = turnDiscoveryRequest);
4562
+ joinOptions.roapMessage = turnDiscoveryRequest.roapMessage;
4563
+
4564
+ if (!joinResponse) {
4565
+ LoggerProxy.logger.info(
4566
+ 'Meeting:index#joinWithMedia ---> calling join with joinOptions, ',
4567
+ joinOptions
4481
4568
  );
4482
4569
 
4483
- return Promise.reject(error);
4484
- });
4570
+ joinResponse = await this.join(joinOptions);
4571
+ }
4572
+
4573
+ joined = true;
4574
+
4575
+ if (joinOptions.roapMessage) {
4576
+ ({turnServerInfo, turnDiscoverySkippedReason} =
4577
+ await this.roap.handleTurnDiscoveryHttpResponse(this, joinResponse));
4578
+
4579
+ this.turnDiscoverySkippedReason = turnDiscoverySkippedReason;
4580
+ this.turnServerUsed = !!turnServerInfo;
4581
+
4582
+ if (turnServerInfo === undefined) {
4583
+ this.roap.abortTurnDiscovery();
4584
+ }
4585
+ }
4586
+
4587
+ const mediaResponse = await this.addMedia(mediaOptions, turnServerInfo);
4588
+
4589
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
4590
+
4591
+ return {
4592
+ join: joinResponse,
4593
+ media: mediaResponse,
4594
+ };
4595
+ } catch (error) {
4596
+ LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
4597
+
4598
+ let leaveError;
4599
+
4600
+ this.roap.abortTurnDiscovery();
4601
+
4602
+ if (joined && isRetry) {
4603
+ try {
4604
+ await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
4605
+ } catch (e) {
4606
+ LoggerProxy.logger.error('Meeting:index#joinWithMedia --> leave error', e);
4607
+ leaveError = e;
4608
+ }
4609
+ }
4610
+
4611
+ Metrics.sendBehavioralMetric(
4612
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
4613
+ {
4614
+ correlation_id: this.correlationId,
4615
+ locus_id: this.locusUrl?.split('/').pop(), // if join fails, we may end up with no locusUrl
4616
+ reason: error.message,
4617
+ stack: error.stack,
4618
+ leaveErrorReason: leaveError?.message,
4619
+ isRetry,
4620
+ },
4621
+ {
4622
+ type: error.name,
4623
+ }
4624
+ );
4625
+
4626
+ if (!isRetry) {
4627
+ LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
4628
+ this.joinWithMediaRetryInfo.isRetry = true;
4629
+ this.joinWithMediaRetryInfo.prevJoinResponse = joinResponse;
4630
+
4631
+ return this.joinWithMedia(options);
4632
+ }
4633
+
4634
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
4635
+
4636
+ throw error;
4637
+ }
4485
4638
  }
4486
4639
 
4487
4640
  /**
@@ -4510,90 +4663,28 @@ export default class Meeting extends StatelessWebexPlugin {
4510
4663
  );
4511
4664
  }
4512
4665
 
4513
- try {
4514
- LoggerProxy.logger.info('Meeting:index#reconnect --> Validating reconnect ability.');
4515
- // @ts-ignore
4516
- this.reconnectionManager.validate();
4517
- } catch (error) {
4518
- // Unable to reconnect this call
4519
- if (error instanceof ReconnectInProgress) {
4520
- LoggerProxy.logger.info(
4521
- 'Meeting:index#reconnect --> Unable to reconnect, reconnection in progress.'
4522
- );
4523
- } else {
4524
- LoggerProxy.logger.log('Meeting:index#reconnect --> Unable to reconnect.', error);
4525
- }
4526
-
4527
- return Promise.resolve();
4528
- }
4529
-
4530
- Trigger.trigger(
4531
- this,
4532
- {
4533
- file: 'meeting/index',
4534
- function: 'reconnect',
4535
- },
4536
- EVENT_TRIGGERS.MEETING_RECONNECTION_STARTING
4537
- );
4538
-
4539
4666
  return this.reconnectionManager
4540
- .reconnect(options)
4541
- .then(() => this.waitForRemoteSDPAnswer())
4542
- .then(() => this.waitForMediaConnectionConnected())
4667
+ .reconnect(options, async () => {
4668
+ await this.waitForRemoteSDPAnswer();
4669
+ await this.waitForMediaConnectionConnected();
4670
+ })
4543
4671
  .then(() => {
4544
- Trigger.trigger(
4545
- this,
4546
- {
4547
- file: 'meeting/index',
4548
- function: 'reconnect',
4549
- },
4550
- EVENT_TRIGGERS.MEETING_RECONNECTION_SUCCESS
4551
- );
4552
4672
  LoggerProxy.logger.log('Meeting:index#reconnect --> Meeting reconnect success');
4553
-
4554
- // @ts-ignore
4555
- this.webex.internal.newMetrics.submitClientEvent({
4556
- name: 'client.media.recovered',
4557
- payload: {
4558
- recoveredBy: 'new',
4559
- },
4560
- options: {
4561
- meetingId: this.id,
4562
- },
4563
- });
4564
- this.reconnectionManager.setStatus(RECONNECTION.STATE.COMPLETE);
4565
4673
  })
4566
4674
  .catch((error) => {
4567
- Trigger.trigger(
4568
- this,
4569
- {
4570
- file: 'meeting/index',
4571
- function: 'reconnect',
4572
- },
4573
- EVENT_TRIGGERS.MEETING_RECONNECTION_FAILURE,
4574
- {
4575
- error: new ReconnectionError('Reconnection failure event', error),
4576
- }
4577
- );
4675
+ if (error instanceof ReconnectionNotStartedError) {
4676
+ LoggerProxy.logger.log('Meeting:index#reconnect --> Meeting reconnect not started');
4578
4677
 
4678
+ return Promise.resolve();
4679
+ }
4579
4680
  LoggerProxy.logger.error('Meeting:index#reconnect --> Meeting reconnect failed', error);
4580
4681
 
4581
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_RECONNECT_FAILURE, {
4582
- correlation_id: this.correlationId,
4583
- locus_id: this.locusUrl.split('/').pop(),
4584
- reason: error.message,
4585
- stack: error.stack,
4586
- });
4587
-
4588
4682
  this.uploadLogs({
4589
4683
  file: 'meeting/index',
4590
4684
  function: 'reconnect',
4591
4685
  });
4592
4686
 
4593
- return Promise.reject(new ReconnectionError('Reconnection failure event', error));
4594
- })
4595
- .finally(() => {
4596
- this.reconnectionManager.reset();
4687
+ return Promise.reject(error);
4597
4688
  });
4598
4689
  }
4599
4690
 
@@ -4747,7 +4838,7 @@ export default class Meeting extends StatelessWebexPlugin {
4747
4838
 
4748
4839
  if (this.getCurUserType() === 'host') {
4749
4840
  // @ts-ignore
4750
- await this.webex.internal.voicea.toggleTranscribing(true, options?.spokenLanguage);
4841
+ await this.webex.internal.voicea.turnOnCaptions(options?.spokenLanguage);
4751
4842
  }
4752
4843
  } catch (error) {
4753
4844
  LoggerProxy.logger.error(`Meeting:index#startTranscription --> ${error}`);
@@ -5604,7 +5695,7 @@ export default class Meeting extends StatelessWebexPlugin {
5604
5695
  logText: `${LOG_HEADER} Roap Offer`,
5605
5696
  }
5606
5697
  ).catch(() => {
5607
- this.deferSDPAnswer.reject();
5698
+ this.deferSDPAnswer.reject(new Error('failed to send ROAP SDP offer'));
5608
5699
  clearTimeout(this.sdpResponseTimer);
5609
5700
  this.sdpResponseTimer = undefined;
5610
5701
  });
@@ -5951,6 +6042,20 @@ export default class Meeting extends StatelessWebexPlugin {
5951
6042
  meetingId: this.id,
5952
6043
  },
5953
6044
  });
6045
+
6046
+ if (data.type === 'share') {
6047
+ // @ts-ignore
6048
+ this.webex.internal.newMetrics.submitClientEvent({
6049
+ name: 'client.media.render.start',
6050
+ payload: {
6051
+ mediaType: 'share',
6052
+ shareInstanceId: this.remoteShareInstanceId,
6053
+ },
6054
+ options: {
6055
+ meetingId: this.id,
6056
+ },
6057
+ });
6058
+ }
5954
6059
  });
5955
6060
  this.statsAnalyzer.on(StatsAnalyzerEvents.REMOTE_MEDIA_STOPPED, (data) => {
5956
6061
  // @ts-ignore
@@ -5964,6 +6069,20 @@ export default class Meeting extends StatelessWebexPlugin {
5964
6069
  meetingId: this.id,
5965
6070
  },
5966
6071
  });
6072
+
6073
+ if (data.type === 'share') {
6074
+ // @ts-ignore
6075
+ this.webex.internal.newMetrics.submitClientEvent({
6076
+ name: 'client.media.render.stop',
6077
+ payload: {
6078
+ mediaType: 'share',
6079
+ shareInstanceId: this.remoteShareInstanceId,
6080
+ },
6081
+ options: {
6082
+ meetingId: this.id,
6083
+ },
6084
+ });
6085
+ }
5967
6086
  });
5968
6087
  };
5969
6088
 
@@ -6020,6 +6139,7 @@ export default class Meeting extends StatelessWebexPlugin {
6020
6139
 
6021
6140
  // publish the streams
6022
6141
  if (this.mediaProperties.audioStream) {
6142
+ this.setSendNamedMediaGroup(MediaType.AudioMain);
6023
6143
  await this.publishStream(MediaType.AudioMain, this.mediaProperties.audioStream);
6024
6144
  }
6025
6145
  if (this.mediaProperties.videoStream) {
@@ -6069,16 +6189,22 @@ export default class Meeting extends StatelessWebexPlugin {
6069
6189
  private async setUpLocalStreamReferences(localStreams: LocalStreams) {
6070
6190
  const setUpStreamPromises = [];
6071
6191
 
6072
- if (localStreams?.microphone) {
6192
+ if (localStreams?.microphone && localStreams?.microphone?.readyState !== 'ended') {
6073
6193
  setUpStreamPromises.push(this.setLocalAudioStream(localStreams.microphone));
6074
6194
  }
6075
- if (localStreams?.camera) {
6195
+ if (localStreams?.camera && localStreams?.camera?.readyState !== 'ended') {
6076
6196
  setUpStreamPromises.push(this.setLocalVideoStream(localStreams.camera));
6077
6197
  }
6078
- if (localStreams?.screenShare?.video) {
6198
+ if (
6199
+ localStreams?.screenShare?.video &&
6200
+ localStreams?.screenShare?.video?.readyState !== 'ended'
6201
+ ) {
6079
6202
  setUpStreamPromises.push(this.setLocalShareVideoStream(localStreams.screenShare.video));
6080
6203
  }
6081
- if (localStreams?.screenShare?.audio) {
6204
+ if (
6205
+ localStreams?.screenShare?.audio &&
6206
+ localStreams?.screenShare?.audio?.readyState !== 'ended'
6207
+ ) {
6082
6208
  setUpStreamPromises.push(this.setLocalShareAudioStream(localStreams.screenShare.audio));
6083
6209
  }
6084
6210
 
@@ -6323,6 +6449,44 @@ export default class Meeting extends StatelessWebexPlugin {
6323
6449
  }
6324
6450
  }
6325
6451
 
6452
+ /**
6453
+ * Performs TURN discovery as a separate call to the Locus /media API
6454
+ *
6455
+ * @param {boolean} isRetry
6456
+ * @param {boolean} isForced
6457
+ * @returns {Promise}
6458
+ */
6459
+ private async doTurnDiscovery(isRetry: boolean, isForced: boolean): Promise<TurnDiscoveryResult> {
6460
+ // @ts-ignore
6461
+ const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
6462
+
6463
+ // @ts-ignore
6464
+ this.webex.internal.newMetrics.submitInternalEvent({
6465
+ name: 'internal.client.add-media.turn-discovery.start',
6466
+ });
6467
+
6468
+ const turnDiscoveryResult = await this.roap.doTurnDiscovery(this, isRetry, isForced);
6469
+
6470
+ this.turnDiscoverySkippedReason = turnDiscoveryResult?.turnDiscoverySkippedReason;
6471
+ this.turnServerUsed = !this.turnDiscoverySkippedReason;
6472
+
6473
+ // @ts-ignore
6474
+ this.webex.internal.newMetrics.submitInternalEvent({
6475
+ name: 'internal.client.add-media.turn-discovery.end',
6476
+ });
6477
+
6478
+ if (this.turnServerUsed && turnDiscoveryResult.turnServerInfo) {
6479
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY, {
6480
+ correlation_id: this.correlationId,
6481
+ latency: cdl.getTurnDiscoveryTime(),
6482
+ turnServerUsed: this.turnServerUsed,
6483
+ retriedWithTurnServer: this.retriedWithTurnServer,
6484
+ });
6485
+ }
6486
+
6487
+ return turnDiscoveryResult;
6488
+ }
6489
+
6326
6490
  /**
6327
6491
  * Does TURN discovery, SDP offer/answer exhange, establishes ICE connection and DTLS handshake.
6328
6492
  *
@@ -6330,43 +6494,21 @@ export default class Meeting extends StatelessWebexPlugin {
6330
6494
  * @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
6331
6495
  * @param {BundlePolicy} [bundlePolicy]
6332
6496
  * @param {boolean} [isForced] - let isForced be true to do turn discovery regardless of reachability results
6497
+ * @param {TurnServerInfo} [turnServerInfo]
6333
6498
  * @returns {Promise<void>}
6334
6499
  */
6335
6500
  private async establishMediaConnection(
6336
6501
  remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
6337
6502
  bundlePolicy?: BundlePolicy,
6338
- isForced?: boolean
6503
+ isForced?: boolean,
6504
+ turnServerInfo?: TurnServerInfo
6339
6505
  ): Promise<void> {
6340
6506
  const LOG_HEADER = 'Meeting:index#addMedia():establishMediaConnection -->';
6341
- // @ts-ignore
6342
- const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
6343
6507
  const isRetry = this.retriedWithTurnServer;
6344
6508
 
6345
6509
  try {
6346
- // @ts-ignore
6347
- this.webex.internal.newMetrics.submitInternalEvent({
6348
- name: 'internal.client.add-media.turn-discovery.start',
6349
- });
6350
-
6351
- const turnDiscoveryObject = await this.roap.doTurnDiscovery(this, isRetry, isForced);
6352
-
6353
- this.turnDiscoverySkippedReason = turnDiscoveryObject?.turnDiscoverySkippedReason;
6354
- this.turnServerUsed = !this.turnDiscoverySkippedReason;
6355
-
6356
- // @ts-ignore
6357
- this.webex.internal.newMetrics.submitInternalEvent({
6358
- name: 'internal.client.add-media.turn-discovery.end',
6359
- });
6360
-
6361
- const {turnServerInfo} = turnDiscoveryObject;
6362
-
6363
- if (this.turnServerUsed && turnServerInfo) {
6364
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY, {
6365
- correlation_id: this.correlationId,
6366
- latency: cdl.getTurnDiscoveryTime(),
6367
- turnServerUsed: this.turnServerUsed,
6368
- retriedWithTurnServer: this.retriedWithTurnServer,
6369
- });
6510
+ if (!turnServerInfo) {
6511
+ ({turnServerInfo} = await this.doTurnDiscovery(isRetry, isForced));
6370
6512
  }
6371
6513
 
6372
6514
  const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
@@ -6385,6 +6527,11 @@ export default class Meeting extends StatelessWebexPlugin {
6385
6527
  RemoteMediaManagerEvent.AudioCreated,
6386
6528
  EVENT_TRIGGERS.REMOTE_MEDIA_AUDIO_CREATED
6387
6529
  );
6530
+ this.forwardEvent(
6531
+ this.remoteMediaManager,
6532
+ RemoteMediaManagerEvent.InterpretationAudioCreated,
6533
+ EVENT_TRIGGERS.REMOTE_MEDIA_INTERPRETATION_AUDIO_CREATED
6534
+ );
6388
6535
  this.forwardEvent(
6389
6536
  this.remoteMediaManager,
6390
6537
  RemoteMediaManagerEvent.ScreenShareAudioCreated,
@@ -6478,15 +6625,21 @@ export default class Meeting extends StatelessWebexPlugin {
6478
6625
  * Creates a media connection to the server. Media connection is required for sending or receiving any audio/video.
6479
6626
  *
6480
6627
  * @param {AddMediaOptions} options
6628
+ * @param {TurnServerInfo} turnServerInfo - TURN server information (used only internally by the SDK)
6481
6629
  * @returns {Promise<void>}
6482
6630
  * @public
6483
6631
  * @memberof Meeting
6484
6632
  */
6485
- async addMedia(options: AddMediaOptions = {}): Promise<void> {
6633
+ async addMedia(
6634
+ options: AddMediaOptions = {},
6635
+ turnServerInfo: TurnServerInfo = undefined
6636
+ ): Promise<void> {
6486
6637
  this.retriedWithTurnServer = false;
6487
6638
  this.hasMediaConnectionConnectedAtLeastOnce = false;
6488
6639
  const LOG_HEADER = 'Meeting:index#addMedia -->';
6489
- LoggerProxy.logger.info(`${LOG_HEADER} called with: ${JSON.stringify(options)}`);
6640
+ LoggerProxy.logger.info(
6641
+ `${LOG_HEADER} called with: ${JSON.stringify(options)}, ${JSON.stringify(turnServerInfo)}`
6642
+ );
6490
6643
 
6491
6644
  if (options.allowMediaInLobby !== true && this.meetingState !== FULL_STATE.ACTIVE) {
6492
6645
  throw new MeetingNotActiveError();
@@ -6504,14 +6657,13 @@ export default class Meeting extends StatelessWebexPlugin {
6504
6657
  shareVideoEnabled = true,
6505
6658
  remoteMediaManagerConfig,
6506
6659
  bundlePolicy,
6507
- allowMediaInLobby,
6508
6660
  } = options;
6509
6661
 
6510
6662
  this.allowMediaInLobby = options?.allowMediaInLobby;
6511
6663
 
6512
6664
  // If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
6513
6665
  // @ts-ignore - isUserUnadmitted coming from SelfUtil
6514
- if (this.isUserUnadmitted && !this.wirelessShare && !allowMediaInLobby) {
6666
+ if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
6515
6667
  throw new UserInLobbyError();
6516
6668
  }
6517
6669
 
@@ -6580,15 +6732,25 @@ export default class Meeting extends StatelessWebexPlugin {
6580
6732
 
6581
6733
  this.createStatsAnalyzer();
6582
6734
 
6583
- await this.establishMediaConnection(remoteMediaManagerConfig, bundlePolicy, false);
6735
+ await this.establishMediaConnection(
6736
+ remoteMediaManagerConfig,
6737
+ bundlePolicy,
6738
+ false,
6739
+ turnServerInfo
6740
+ );
6584
6741
 
6585
- await Meeting.handleDeviceLogging();
6742
+ if (audioEnabled || videoEnabled) {
6743
+ await Meeting.handleDeviceLogging();
6744
+ } else {
6745
+ LoggerProxy.logger.info(`${LOG_HEADER} device logging not required`);
6746
+ }
6586
6747
 
6587
6748
  if (this.mediaProperties.hasLocalShareStream()) {
6588
6749
  await this.enqueueScreenShareFloorRequest();
6589
6750
  }
6590
6751
 
6591
- const connectionType = await this.mediaProperties.getCurrentConnectionType();
6752
+ const {connectionType, selectedCandidatePairChanges, numTransports} =
6753
+ await this.mediaProperties.getCurrentConnectionInfo();
6592
6754
  // @ts-ignore
6593
6755
  const reachabilityStats = await this.webex.meetings.reachability.getReachabilityMetrics();
6594
6756
 
@@ -6596,8 +6758,11 @@ export default class Meeting extends StatelessWebexPlugin {
6596
6758
  correlation_id: this.correlationId,
6597
6759
  locus_id: this.locusUrl.split('/').pop(),
6598
6760
  connectionType,
6761
+ selectedCandidatePairChanges,
6762
+ numTransports,
6599
6763
  isMultistream: this.isMultistream,
6600
6764
  retriedWithTurnServer: this.retriedWithTurnServer,
6765
+ isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
6601
6766
  ...reachabilityStats,
6602
6767
  });
6603
6768
  // @ts-ignore
@@ -6619,16 +6784,22 @@ export default class Meeting extends StatelessWebexPlugin {
6619
6784
  // @ts-ignore
6620
6785
  const reachabilityMetrics = await this.webex.meetings.reachability.getReachabilityMetrics();
6621
6786
 
6787
+ const {selectedCandidatePairChanges, numTransports} =
6788
+ await this.mediaProperties.getCurrentConnectionInfo();
6789
+
6622
6790
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
6623
6791
  correlation_id: this.correlationId,
6624
6792
  locus_id: this.locusUrl.split('/').pop(),
6625
6793
  reason: error.message,
6626
6794
  stack: error.stack,
6627
6795
  code: error.code,
6796
+ selectedCandidatePairChanges,
6797
+ numTransports,
6628
6798
  turnDiscoverySkippedReason: this.turnDiscoverySkippedReason,
6629
6799
  turnServerUsed: this.turnServerUsed,
6630
6800
  retriedWithTurnServer: this.retriedWithTurnServer,
6631
6801
  isMultistream: this.isMultistream,
6802
+ isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
6632
6803
  signalingState:
6633
6804
  this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
6634
6805
  ?.signalingState ||
@@ -7774,9 +7945,9 @@ export default class Meeting extends StatelessWebexPlugin {
7774
7945
 
7775
7946
  /**
7776
7947
  *
7777
- * @returns {string} one of 'attendee','host','cohost', returns the user type of the current user
7948
+ * @returns {string} one of 'panelist', 'attendee', 'host', 'cohost', returns the user type of the current user
7778
7949
  */
7779
- getCurUserType() {
7950
+ getCurUserType(): RawClientEvent['userType'] | null {
7780
7951
  const {roles} = this;
7781
7952
  if (roles) {
7782
7953
  if (roles.includes(SELF_ROLES.MODERATOR)) {
@@ -7785,8 +7956,8 @@ export default class Meeting extends StatelessWebexPlugin {
7785
7956
  if (roles.includes(SELF_ROLES.COHOST)) {
7786
7957
  return 'cohost';
7787
7958
  }
7788
- if (roles.includes(SELF_ROLES.PRESENTER)) {
7789
- return 'presenter';
7959
+ if (roles.includes(SELF_ROLES.PANELIST)) {
7960
+ return 'panelist';
7790
7961
  }
7791
7962
  if (roles.includes(SELF_ROLES.ATTENDEE)) {
7792
7963
  return 'attendee';
@@ -8096,6 +8267,33 @@ export default class Meeting extends StatelessWebexPlugin {
8096
8267
  });
8097
8268
  }
8098
8269
 
8270
+ /**
8271
+ * set sending named media group which the audio should send to
8272
+ * @param {MediaType} mediaType of the stream
8273
+ * @param {number} languageCode of the stream
8274
+ * @returns {void}
8275
+ */
8276
+ public setSendNamedMediaGroup(mediaType: MediaType, languageCode = 0): void {
8277
+ if (mediaType !== MediaType.AudioMain) {
8278
+ throw new Error(`cannot set send named media group which media type is ${mediaType}`);
8279
+ }
8280
+
8281
+ const value = languageCode || this.simultaneousInterpretation.getTargetLanguageCode();
8282
+ let groups = [];
8283
+
8284
+ if (value) {
8285
+ groups = [
8286
+ {
8287
+ type: NAMED_MEDIA_GROUP_TYPE_AUDIO,
8288
+ value,
8289
+ },
8290
+ ];
8291
+ }
8292
+ if (this.isMultistream && this.mediaProperties.webrtcMediaConnection) {
8293
+ this.sendSlotManager.setNamedMediaGroups(mediaType, groups);
8294
+ }
8295
+ }
8296
+
8099
8297
  /**
8100
8298
  * Publishes a stream.
8101
8299
  *
@@ -8164,6 +8362,17 @@ export default class Meeting extends StatelessWebexPlugin {
8164
8362
  return;
8165
8363
  }
8166
8364
 
8365
+ if (
8366
+ streams?.microphone?.readyState === 'ended' ||
8367
+ streams?.camera?.readyState === 'ended' ||
8368
+ streams?.screenShare?.audio?.readyState === 'ended' ||
8369
+ streams?.screenShare?.video?.readyState === 'ended'
8370
+ ) {
8371
+ throw new Error(
8372
+ `Attempted to publish stream with ended readyState, correlationId=${this.correlationId}`
8373
+ );
8374
+ }
8375
+
8167
8376
  let floorRequestNeeded = false;
8168
8377
 
8169
8378
  // Screenshare Audio is supported only in multi stream. So we check for screenshare audio presence only if it's a multi stream meeting