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

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 (252) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.js +2 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/constants.js +8 -4
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.js +86 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/interpretation/index.js +16 -2
  10. package/dist/interpretation/index.js.map +1 -1
  11. package/dist/interpretation/siLanguage.js +1 -1
  12. package/dist/locus-info/mediaSharesUtils.js +15 -1
  13. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  14. package/dist/locus-info/selfUtils.js +5 -0
  15. package/dist/locus-info/selfUtils.js.map +1 -1
  16. package/dist/media/MediaConnectionAwaiter.js +163 -0
  17. package/dist/media/MediaConnectionAwaiter.js.map +1 -0
  18. package/dist/media/index.js +4 -1
  19. package/dist/media/index.js.map +1 -1
  20. package/dist/media/properties.js +4 -24
  21. package/dist/media/properties.js.map +1 -1
  22. package/dist/meeting/index.js +893 -677
  23. package/dist/meeting/index.js.map +1 -1
  24. package/dist/meeting/muteState.js +37 -25
  25. package/dist/meeting/muteState.js.map +1 -1
  26. package/dist/meeting/request.js +32 -23
  27. package/dist/meeting/request.js.map +1 -1
  28. package/dist/meeting/util.js +1 -0
  29. package/dist/meeting/util.js.map +1 -1
  30. package/dist/meeting-info/util.js +304 -267
  31. package/dist/meeting-info/util.js.map +1 -1
  32. package/dist/meeting-info/utilv2.js +334 -295
  33. package/dist/meeting-info/utilv2.js.map +1 -1
  34. package/dist/meetings/index.js +20 -0
  35. package/dist/meetings/index.js.map +1 -1
  36. package/dist/multistream/mediaRequestManager.js +1 -1
  37. package/dist/multistream/mediaRequestManager.js.map +1 -1
  38. package/dist/multistream/remoteMediaGroup.js +16 -2
  39. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  40. package/dist/multistream/remoteMediaManager.js +179 -65
  41. package/dist/multistream/remoteMediaManager.js.map +1 -1
  42. package/dist/multistream/sendSlotManager.js +22 -0
  43. package/dist/multistream/sendSlotManager.js.map +1 -1
  44. package/dist/reachability/clusterReachability.js +29 -15
  45. package/dist/reachability/clusterReachability.js.map +1 -1
  46. package/dist/reachability/index.js +18 -2
  47. package/dist/reachability/index.js.map +1 -1
  48. package/dist/reachability/request.js +12 -10
  49. package/dist/reachability/request.js.map +1 -1
  50. package/dist/reachability/util.js +19 -0
  51. package/dist/reachability/util.js.map +1 -1
  52. package/dist/reconnection-manager/index.js +2 -1
  53. package/dist/reconnection-manager/index.js.map +1 -1
  54. package/dist/roap/index.js +15 -0
  55. package/dist/roap/index.js.map +1 -1
  56. package/dist/roap/request.js +3 -3
  57. package/dist/roap/request.js.map +1 -1
  58. package/dist/roap/turnDiscovery.js +307 -126
  59. package/dist/roap/turnDiscovery.js.map +1 -1
  60. package/dist/statsAnalyzer/index.js +53 -30
  61. package/dist/statsAnalyzer/index.js.map +1 -1
  62. package/dist/{config.d.ts → types/config.d.ts} +1 -0
  63. package/dist/{constants.d.ts → types/constants.d.ts} +5 -4
  64. package/dist/types/index.d.ts +19 -0
  65. package/dist/types/media/MediaConnectionAwaiter.d.ts +61 -0
  66. package/dist/{meeting → types/meeting}/index.d.ts +26 -7
  67. package/dist/{meeting → types/meeting}/muteState.d.ts +2 -8
  68. package/dist/{meeting → types/meeting}/request.d.ts +3 -0
  69. package/dist/{meeting-info → types/meeting-info}/index.d.ts +1 -1
  70. package/dist/{meeting-info → types/meeting-info}/meeting-info-v2.d.ts +1 -1
  71. package/dist/types/meeting-info/util.d.ts +49 -0
  72. package/dist/types/meeting-info/utilv2.d.ts +65 -0
  73. package/dist/{meetings → types/meetings}/index.d.ts +8 -0
  74. package/dist/{multistream → types/multistream}/mediaRequestManager.d.ts +2 -1
  75. package/dist/{multistream → types/multistream}/remoteMediaGroup.d.ts +2 -0
  76. package/dist/{multistream → types/multistream}/remoteMediaManager.d.ts +15 -0
  77. package/dist/{multistream → types/multistream}/sendSlotManager.d.ts +9 -1
  78. package/dist/{reachability → types/reachability}/clusterReachability.d.ts +1 -0
  79. package/dist/{reachability → types/reachability}/index.d.ts +4 -0
  80. package/dist/{reachability → types/reachability}/util.d.ts +7 -0
  81. package/dist/{roap → types/roap}/index.d.ts +10 -2
  82. package/dist/{roap → types/roap}/turnDiscovery.d.ts +64 -17
  83. package/dist/webinar/index.js +1 -1
  84. package/package.json +23 -23
  85. package/src/config.ts +1 -0
  86. package/src/constants.ts +7 -3
  87. package/src/index.ts +31 -0
  88. package/src/interpretation/index.ts +18 -1
  89. package/src/locus-info/mediaSharesUtils.ts +16 -0
  90. package/src/locus-info/selfUtils.ts +5 -0
  91. package/src/media/MediaConnectionAwaiter.ts +174 -0
  92. package/src/media/index.ts +3 -1
  93. package/src/media/properties.ts +6 -31
  94. package/src/meeting/index.ts +321 -106
  95. package/src/meeting/muteState.ts +34 -20
  96. package/src/meeting/request.ts +18 -2
  97. package/src/meeting/util.ts +1 -0
  98. package/src/meeting-info/util.ts +241 -233
  99. package/src/meeting-info/utilv2.ts +250 -243
  100. package/src/meetings/index.ts +18 -0
  101. package/src/multistream/mediaRequestManager.ts +4 -1
  102. package/src/multistream/remoteMediaGroup.ts +19 -0
  103. package/src/multistream/remoteMediaManager.ts +101 -16
  104. package/src/multistream/sendSlotManager.ts +28 -0
  105. package/src/reachability/clusterReachability.ts +20 -5
  106. package/src/reachability/index.ts +24 -1
  107. package/src/reachability/request.ts +15 -11
  108. package/src/reachability/util.ts +21 -0
  109. package/src/reconnection-manager/index.ts +1 -1
  110. package/src/roap/index.ts +25 -3
  111. package/src/roap/request.ts +3 -3
  112. package/src/roap/turnDiscovery.ts +244 -78
  113. package/src/statsAnalyzer/index.ts +63 -27
  114. package/test/integration/spec/journey.js +14 -14
  115. package/test/integration/spec/space-meeting.js +1 -1
  116. package/test/unit/spec/interpretation/index.ts +39 -3
  117. package/test/unit/spec/locus-info/index.js +28 -19
  118. package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
  119. package/test/unit/spec/locus-info/selfUtils.js +42 -12
  120. package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
  121. package/test/unit/spec/media/index.ts +89 -78
  122. package/test/unit/spec/media/properties.ts +16 -70
  123. package/test/unit/spec/meeting/index.js +638 -139
  124. package/test/unit/spec/meeting/muteState.js +219 -67
  125. package/test/unit/spec/meeting/request.js +21 -0
  126. package/test/unit/spec/meeting/utils.js +6 -1
  127. package/test/unit/spec/meeting-info/utilv2.js +6 -0
  128. package/test/unit/spec/meetings/index.js +40 -20
  129. package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
  130. package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
  131. package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
  132. package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
  133. package/test/unit/spec/reachability/clusterReachability.ts +86 -22
  134. package/test/unit/spec/reachability/index.ts +197 -60
  135. package/test/unit/spec/reachability/request.js +15 -7
  136. package/test/unit/spec/reachability/util.ts +32 -2
  137. package/test/unit/spec/reconnection-manager/index.js +28 -0
  138. package/test/unit/spec/roap/index.ts +61 -6
  139. package/test/unit/spec/roap/turnDiscovery.ts +298 -16
  140. package/test/unit/spec/stats-analyzer/index.js +179 -0
  141. package/dist/index.d.ts +0 -7
  142. package/dist/meeting-info/util.d.ts +0 -2
  143. package/dist/meeting-info/utilv2.d.ts +0 -2
  144. package/dist/member/member.types.d.ts +0 -11
  145. package/dist/member/member.types.js +0 -17
  146. package/dist/member/member.types.js.map +0 -1
  147. package/src/member/member.types.ts +0 -13
  148. /package/dist/{annotation → types/annotation}/annotation.types.d.ts +0 -0
  149. /package/dist/{annotation → types/annotation}/constants.d.ts +0 -0
  150. /package/dist/{annotation → types/annotation}/index.d.ts +0 -0
  151. /package/dist/{breakouts → types/breakouts}/breakout.d.ts +0 -0
  152. /package/dist/{breakouts → types/breakouts}/collection.d.ts +0 -0
  153. /package/dist/{breakouts → types/breakouts}/edit-lock-error.d.ts +0 -0
  154. /package/dist/{breakouts → types/breakouts}/events.d.ts +0 -0
  155. /package/dist/{breakouts → types/breakouts}/index.d.ts +0 -0
  156. /package/dist/{breakouts → types/breakouts}/request.d.ts +0 -0
  157. /package/dist/{breakouts → types/breakouts}/utils.d.ts +0 -0
  158. /package/dist/{common → types/common}/browser-detection.d.ts +0 -0
  159. /package/dist/{common → types/common}/collection.d.ts +0 -0
  160. /package/dist/{common → types/common}/config.d.ts +0 -0
  161. /package/dist/{common → types/common}/errors/captcha-error.d.ts +0 -0
  162. /package/dist/{common → types/common}/errors/intent-to-join.d.ts +0 -0
  163. /package/dist/{common → types/common}/errors/join-meeting.d.ts +0 -0
  164. /package/dist/{common → types/common}/errors/media.d.ts +0 -0
  165. /package/dist/{common → types/common}/errors/no-meeting-info.d.ts +0 -0
  166. /package/dist/{common → types/common}/errors/parameter.d.ts +0 -0
  167. /package/dist/{common → types/common}/errors/password-error.d.ts +0 -0
  168. /package/dist/{common → types/common}/errors/permission.d.ts +0 -0
  169. /package/dist/{common → types/common}/errors/reclaim-host-role-errors.d.ts +0 -0
  170. /package/dist/{common → types/common}/errors/reconnection-in-progress.d.ts +0 -0
  171. /package/dist/{common → types/common}/errors/reconnection.d.ts +0 -0
  172. /package/dist/{common → types/common}/errors/stats.d.ts +0 -0
  173. /package/dist/{common → types/common}/errors/webex-errors.d.ts +0 -0
  174. /package/dist/{common → types/common}/errors/webex-meetings-error.d.ts +0 -0
  175. /package/dist/{common → types/common}/events/events-scope.d.ts +0 -0
  176. /package/dist/{common → types/common}/events/events.d.ts +0 -0
  177. /package/dist/{common → types/common}/events/trigger-proxy.d.ts +0 -0
  178. /package/dist/{common → types/common}/events/util.d.ts +0 -0
  179. /package/dist/{common → types/common}/logs/logger-config.d.ts +0 -0
  180. /package/dist/{common → types/common}/logs/logger-proxy.d.ts +0 -0
  181. /package/dist/{common → types/common}/logs/request.d.ts +0 -0
  182. /package/dist/{common → types/common}/queue.d.ts +0 -0
  183. /package/dist/{controls-options-manager → types/controls-options-manager}/constants.d.ts +0 -0
  184. /package/dist/{controls-options-manager → types/controls-options-manager}/enums.d.ts +0 -0
  185. /package/dist/{controls-options-manager → types/controls-options-manager}/index.d.ts +0 -0
  186. /package/dist/{controls-options-manager → types/controls-options-manager}/types.d.ts +0 -0
  187. /package/dist/{controls-options-manager → types/controls-options-manager}/util.d.ts +0 -0
  188. /package/dist/{interceptors → types/interceptors}/index.d.ts +0 -0
  189. /package/dist/{interceptors → types/interceptors}/locusRetry.d.ts +0 -0
  190. /package/dist/{interpretation → types/interpretation}/collection.d.ts +0 -0
  191. /package/dist/{interpretation → types/interpretation}/index.d.ts +0 -0
  192. /package/dist/{interpretation → types/interpretation}/siLanguage.d.ts +0 -0
  193. /package/dist/{locus-info → types/locus-info}/controlsUtils.d.ts +0 -0
  194. /package/dist/{locus-info → types/locus-info}/embeddedAppsUtils.d.ts +0 -0
  195. /package/dist/{locus-info → types/locus-info}/fullState.d.ts +0 -0
  196. /package/dist/{locus-info → types/locus-info}/hostUtils.d.ts +0 -0
  197. /package/dist/{locus-info → types/locus-info}/index.d.ts +0 -0
  198. /package/dist/{locus-info → types/locus-info}/infoUtils.d.ts +0 -0
  199. /package/dist/{locus-info → types/locus-info}/mediaSharesUtils.d.ts +0 -0
  200. /package/dist/{locus-info → types/locus-info}/parser.d.ts +0 -0
  201. /package/dist/{locus-info → types/locus-info}/selfUtils.d.ts +0 -0
  202. /package/dist/{media → types/media}/index.d.ts +0 -0
  203. /package/dist/{media → types/media}/properties.d.ts +0 -0
  204. /package/dist/{media → types/media}/util.d.ts +0 -0
  205. /package/dist/{mediaQualityMetrics → types/mediaQualityMetrics}/config.d.ts +0 -0
  206. /package/dist/{meeting → types/meeting}/in-meeting-actions.d.ts +0 -0
  207. /package/dist/{meeting → types/meeting}/locusMediaRequest.d.ts +0 -0
  208. /package/dist/{meeting → types/meeting}/request.type.d.ts +0 -0
  209. /package/dist/{meeting → types/meeting}/state.d.ts +0 -0
  210. /package/dist/{meeting → types/meeting}/util.d.ts +0 -0
  211. /package/dist/{meeting → types/meeting}/voicea-meeting.d.ts +0 -0
  212. /package/dist/{meeting-info → types/meeting-info}/collection.d.ts +0 -0
  213. /package/dist/{meeting-info → types/meeting-info}/request.d.ts +0 -0
  214. /package/dist/{meetings → types/meetings}/collection.d.ts +0 -0
  215. /package/dist/{meetings → types/meetings}/meetings.types.d.ts +0 -0
  216. /package/dist/{meetings → types/meetings}/request.d.ts +0 -0
  217. /package/dist/{meetings → types/meetings}/util.d.ts +0 -0
  218. /package/dist/{member → types/member}/index.d.ts +0 -0
  219. /package/dist/{member → types/member}/types.d.ts +0 -0
  220. /package/dist/{member → types/member}/util.d.ts +0 -0
  221. /package/dist/{members → types/members}/collection.d.ts +0 -0
  222. /package/dist/{members → types/members}/index.d.ts +0 -0
  223. /package/dist/{members → types/members}/request.d.ts +0 -0
  224. /package/dist/{members → types/members}/types.d.ts +0 -0
  225. /package/dist/{members → types/members}/util.d.ts +0 -0
  226. /package/dist/{metrics → types/metrics}/constants.d.ts +0 -0
  227. /package/dist/{metrics → types/metrics}/index.d.ts +0 -0
  228. /package/dist/{multistream → types/multistream}/receiveSlot.d.ts +0 -0
  229. /package/dist/{multistream → types/multistream}/receiveSlotManager.d.ts +0 -0
  230. /package/dist/{multistream → types/multistream}/remoteMedia.d.ts +0 -0
  231. /package/dist/{networkQualityMonitor → types/networkQualityMonitor}/index.d.ts +0 -0
  232. /package/dist/{personal-meeting-room → types/personal-meeting-room}/index.d.ts +0 -0
  233. /package/dist/{personal-meeting-room → types/personal-meeting-room}/request.d.ts +0 -0
  234. /package/dist/{personal-meeting-room → types/personal-meeting-room}/util.d.ts +0 -0
  235. /package/dist/{reachability → types/reachability}/request.d.ts +0 -0
  236. /package/dist/{reactions → types/reactions}/constants.d.ts +0 -0
  237. /package/dist/{reactions → types/reactions}/reactions.d.ts +0 -0
  238. /package/dist/{reactions → types/reactions}/reactions.type.d.ts +0 -0
  239. /package/dist/{reconnection-manager → types/reconnection-manager}/index.d.ts +0 -0
  240. /package/dist/{recording-controller → types/recording-controller}/enums.d.ts +0 -0
  241. /package/dist/{recording-controller → types/recording-controller}/index.d.ts +0 -0
  242. /package/dist/{recording-controller → types/recording-controller}/util.d.ts +0 -0
  243. /package/dist/{roap → types/roap}/request.d.ts +0 -0
  244. /package/dist/{rtcMetrics → types/rtcMetrics}/constants.d.ts +0 -0
  245. /package/dist/{rtcMetrics → types/rtcMetrics}/index.d.ts +0 -0
  246. /package/dist/{statsAnalyzer → types/statsAnalyzer}/global.d.ts +0 -0
  247. /package/dist/{statsAnalyzer → types/statsAnalyzer}/index.d.ts +0 -0
  248. /package/dist/{statsAnalyzer → types/statsAnalyzer}/mqaUtil.d.ts +0 -0
  249. /package/dist/{transcription → types/transcription}/index.d.ts +0 -0
  250. /package/dist/{webinar → types/webinar}/collection.d.ts +0 -0
  251. /package/dist/{webinar → types/webinar}/index.d.ts +0 -0
  252. /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  import {cloneDeep, forEach, remove} from 'lodash';
3
3
  import {EventMap} from 'typed-emitter';
4
- import {MediaType} from '@webex/internal-media-core';
4
+ import {MediaType, NamedMediaGroup} from '@webex/internal-media-core';
5
5
 
6
6
  import LoggerProxy from '../common/logs/logger-proxy';
7
7
  import EventsScope from '../common/events/events-scope';
@@ -11,6 +11,7 @@ import {ReceiveSlot, CSI} from './receiveSlot';
11
11
  import {ReceiveSlotManager} from './receiveSlotManager';
12
12
  import {RemoteMediaGroup} from './remoteMediaGroup';
13
13
  import {MediaRequestManager} from './mediaRequestManager';
14
+ import {NAMED_MEDIA_GROUP_TYPE_AUDIO} from '../constants';
14
15
 
15
16
  export type PaneSize = RemoteVideoResolution;
16
17
  export type LayoutId = string;
@@ -49,6 +50,7 @@ export interface Configuration {
49
50
 
50
51
  layouts: {[key: LayoutId]: VideoLayout}; // a map of all available layouts, a layout can be set via setLayout() method
51
52
  };
53
+ namedMediaGroup?: NamedMediaGroup;
52
54
  }
53
55
 
54
56
  /* Predefined layouts: */
@@ -173,6 +175,7 @@ export const DefaultConfiguration: Configuration = {
173
175
  export enum Event {
174
176
  // events for audio streams
175
177
  AudioCreated = 'AudioCreated',
178
+ InterpretationAudioCreated = 'InterpretationAudioCreated',
176
179
  ScreenShareAudioCreated = 'ScreenShareAudioCreated',
177
180
 
178
181
  // events for video streams
@@ -221,7 +224,10 @@ export class RemoteMediaManager extends EventsScope {
221
224
  private currentLayout?: VideoLayout;
222
225
 
223
226
  private slots: {
224
- audio: ReceiveSlot[];
227
+ audio: {
228
+ main: ReceiveSlot[];
229
+ si: ReceiveSlot;
230
+ };
225
231
  screenShare: {
226
232
  audio: ReceiveSlot[];
227
233
  video?: ReceiveSlot;
@@ -234,7 +240,10 @@ export class RemoteMediaManager extends EventsScope {
234
240
  };
235
241
 
236
242
  private media: {
237
- audio?: RemoteMediaGroup;
243
+ audio: {
244
+ main?: RemoteMediaGroup;
245
+ si?: RemoteMediaGroup;
246
+ };
238
247
  video: {
239
248
  activeSpeakerGroups: {
240
249
  [key: PaneGroupId]: RemoteMediaGroup;
@@ -277,7 +286,10 @@ export class RemoteMediaManager extends EventsScope {
277
286
  this.receiveSlotManager = receiveSlotManager;
278
287
  this.mediaRequestManagers = mediaRequestManagers;
279
288
  this.media = {
280
- audio: undefined,
289
+ audio: {
290
+ main: undefined,
291
+ si: undefined,
292
+ },
281
293
  video: {
282
294
  activeSpeakerGroups: {},
283
295
  memberPanes: {},
@@ -291,7 +303,10 @@ export class RemoteMediaManager extends EventsScope {
291
303
  this.checkConfigValidity();
292
304
 
293
305
  this.slots = {
294
- audio: [],
306
+ audio: {
307
+ main: [],
308
+ si: undefined,
309
+ },
295
310
  screenShare: {
296
311
  audio: [],
297
312
  video: undefined,
@@ -389,8 +404,11 @@ export class RemoteMediaManager extends EventsScope {
389
404
  });
390
405
 
391
406
  // release all audio receive slots
392
- this.slots.audio.forEach((slot) => this.receiveSlotManager.releaseSlot(slot));
393
- this.slots.audio.length = 0;
407
+ this.slots.audio.main.forEach((slot) => this.receiveSlotManager.releaseSlot(slot));
408
+ this.slots.audio.main.length = 0;
409
+ if (this.slots.audio.si) {
410
+ this.receiveSlotManager.releaseSlot(this.slots.audio.si);
411
+ }
394
412
 
395
413
  // release screen share slots
396
414
  this.slots.screenShare.audio.forEach((slot) => this.receiveSlotManager.releaseSlot(slot));
@@ -525,22 +543,54 @@ export class RemoteMediaManager extends EventsScope {
525
543
  this.mediaRequestManagers.video.commit();
526
544
  }
527
545
 
546
+ /**
547
+ * Sets which named media group need receiving
548
+ * @param {MediaType} mediaType of the stream
549
+ * @param {number} languageCode of the stream. If the languageId is 0, the named media group request will be canceled,
550
+ * and only receive the main audio stream.
551
+ * @returns {void}
552
+ */
553
+ public async setReceiveNamedMediaGroup(mediaType: MediaType, languageId: number) {
554
+ if (mediaType !== MediaType.AudioMain) {
555
+ throw new Error(`cannot set receive named media group which media type is ${mediaType}`);
556
+ }
557
+
558
+ const value = languageId;
559
+ if (value === this.config.namedMediaGroup?.value) {
560
+ return;
561
+ }
562
+
563
+ this.config.namedMediaGroup = {
564
+ type: NAMED_MEDIA_GROUP_TYPE_AUDIO,
565
+ value,
566
+ };
567
+
568
+ if (!this.media.audio.si) {
569
+ await this.createInterpretationAudioMedia(true);
570
+ } else {
571
+ this.media.audio.si.setNamedMediaGroup(this.config.namedMediaGroup, true);
572
+ }
573
+ }
574
+
528
575
  /**
529
576
  * Creates the audio slots
530
577
  */
531
578
  private async createAudioMedia() {
532
- // create the audio receive slots
579
+ // create si audio request
580
+ await this.createInterpretationAudioMedia(false);
581
+
582
+ // create main audio receive slots
533
583
  for (let i = 0; i < this.config.audio.numOfActiveSpeakerStreams; i += 1) {
534
584
  // eslint-disable-next-line no-await-in-loop
535
585
  const slot = await this.receiveSlotManager.allocateSlot(MediaType.AudioMain);
536
586
 
537
- this.slots.audio.push(slot);
587
+ this.slots.audio.main.push(slot);
538
588
  }
539
589
 
540
- // create a remote media group
541
- this.media.audio = new RemoteMediaGroup(
590
+ // create a remote media group for main audio
591
+ this.media.audio.main = new RemoteMediaGroup(
542
592
  this.mediaRequestManagers.audio,
543
- this.slots.audio,
593
+ this.slots.audio.main,
544
594
  255,
545
595
  true
546
596
  );
@@ -548,10 +598,40 @@ export class RemoteMediaManager extends EventsScope {
548
598
  this.emit(
549
599
  {file: 'multistream/remoteMediaManager', function: 'createAudioMedia'},
550
600
  Event.AudioCreated,
551
- this.media.audio
601
+ this.media.audio.main
552
602
  );
553
603
  }
554
604
 
605
+ /**
606
+ * Creates the audio slots for named media
607
+ */
608
+ private async createInterpretationAudioMedia(commitRequest: boolean) {
609
+ // create slot for interpretation language audio
610
+ if (
611
+ this.config.namedMediaGroup?.type === NAMED_MEDIA_GROUP_TYPE_AUDIO &&
612
+ this.config.namedMediaGroup?.value
613
+ ) {
614
+ this.slots.audio.si = await this.receiveSlotManager.allocateSlot(MediaType.AudioMain);
615
+
616
+ // create a remote media group for si audio
617
+ this.media.audio.si = new RemoteMediaGroup(
618
+ this.mediaRequestManagers.audio,
619
+ [this.slots.audio.si],
620
+ 255,
621
+ commitRequest,
622
+ {
623
+ namedMediaGroup: this.config.namedMediaGroup,
624
+ }
625
+ );
626
+
627
+ this.emit(
628
+ {file: 'multistream/remoteMediaManager', function: 'createInterpretationAudioMedia'},
629
+ Event.InterpretationAudioCreated,
630
+ this.media.audio.si
631
+ );
632
+ }
633
+ }
634
+
555
635
  /**
556
636
  * Creates receive slots required for receiving screen share audio and video
557
637
  */
@@ -748,7 +828,7 @@ export class RemoteMediaManager extends EventsScope {
748
828
  /** logs main audio slots */
749
829
  private logMainAudioReceiveSlots() {
750
830
  LoggerProxy.logger.log(
751
- `RemoteMediaManager#logMainAudioReceiveSlots --> MAIN AUDIO receive slots: ${this.slots.audio
831
+ `RemoteMediaManager#logMainAudioReceiveSlots --> MAIN AUDIO receive slots: ${this.slots.audio.main
752
832
  .map((slot) => slot.logString)
753
833
  .join(', ')}`
754
834
  );
@@ -924,8 +1004,13 @@ export class RemoteMediaManager extends EventsScope {
924
1004
  }) {
925
1005
  const {audio, video, screenShareAudio, screenShareVideo, commit} = options;
926
1006
 
927
- if (audio && this.media.audio) {
928
- this.media.audio.stop(commit);
1007
+ if (audio) {
1008
+ if (this.media.audio.main) {
1009
+ this.media.audio.main.stop(commit);
1010
+ }
1011
+ if (this.media.audio.si) {
1012
+ this.media.audio.si.stop(commit);
1013
+ }
929
1014
  }
930
1015
  if (video) {
931
1016
  Object.values(this.media.video.activeSpeakerGroups).forEach((remoteMediaGroup) => {
@@ -3,6 +3,7 @@ import {
3
3
  MediaType,
4
4
  LocalStream,
5
5
  MultistreamRoapMediaConnection,
6
+ NamedMediaGroup,
6
7
  } from '@webex/internal-media-core';
7
8
 
8
9
  export default class SendSlotManager {
@@ -55,6 +56,33 @@ export default class SendSlotManager {
55
56
  return slot;
56
57
  }
57
58
 
59
+ /**
60
+ * Allow users to specify 'namedMediaGroups' to indicate which named media group its audio should be sent to.
61
+ * @param {MediaType} mediaType MediaType of the sendSlot to which the audio stream needs to be send to the media group
62
+ * @param {[]}namedMediaGroups - Allow users to specify 'namedMediaGroups'.If the value of 'namedMediaGroups' is zero,
63
+ * named media group will be canceled and the audio stream will be sent to the floor.
64
+ * @returns {void}
65
+ */
66
+ public setNamedMediaGroups(mediaType: MediaType, namedMediaGroups: NamedMediaGroup[]) {
67
+ if (mediaType !== MediaType.AudioMain) {
68
+ throw new Error(
69
+ `sendSlotManager cannot set named media group which media type is ${mediaType}`
70
+ );
71
+ }
72
+
73
+ const slot = this.slots.get(mediaType);
74
+
75
+ if (!slot) {
76
+ throw new Error(`Slot for ${mediaType} does not exist`);
77
+ }
78
+
79
+ slot.setNamedMediaGroups(namedMediaGroups);
80
+
81
+ this.LoggerProxy.logger.info(
82
+ `SendSlotsManager->setNamedMediaGroups#set named media group ${namedMediaGroups}`
83
+ );
84
+ }
85
+
58
86
  /**
59
87
  * This method publishes the given stream to the sendSlot for the given mediaType
60
88
  * @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES)
@@ -2,7 +2,7 @@ import {Defer} from '@webex/common';
2
2
 
3
3
  import LoggerProxy from '../common/logs/logger-proxy';
4
4
  import {ClusterNode} from './request';
5
- import {convertStunUrlToTurn} from './util';
5
+ import {convertStunUrlToTurn, convertStunUrlToTurnTls} from './util';
6
6
 
7
7
  import {ICE_GATHERING_STATE, CONNECTION_STATE} from '../constants';
8
8
 
@@ -29,6 +29,7 @@ export type ClusterReachabilityResult = {
29
29
  export class ClusterReachability {
30
30
  private numUdpUrls: number;
31
31
  private numTcpUrls: number;
32
+ private numXTlsUrls: number;
32
33
  private result: ClusterReachabilityResult;
33
34
  private pc?: RTCPeerConnection;
34
35
  private defer: Defer; // this defer is resolved once reachability checks for this cluster are completed
@@ -46,6 +47,7 @@ export class ClusterReachability {
46
47
  this.isVideoMesh = clusterInfo.isVideoMesh;
47
48
  this.numUdpUrls = clusterInfo.udp.length;
48
49
  this.numTcpUrls = clusterInfo.tcp.length;
50
+ this.numXTlsUrls = clusterInfo.xtls.length;
49
51
 
50
52
  this.pc = this.createPeerConnection(clusterInfo);
51
53
 
@@ -94,8 +96,16 @@ export class ClusterReachability {
94
96
  };
95
97
  });
96
98
 
99
+ const turnTlsIceServers = cluster.xtls.map((urlString: string) => {
100
+ return {
101
+ username: 'webexturnreachuser',
102
+ credential: 'webexturnreachpwd',
103
+ urls: [convertStunUrlToTurnTls(urlString)],
104
+ };
105
+ });
106
+
97
107
  return {
98
- iceServers: [...udpIceServers, ...tcpIceServers],
108
+ iceServers: [...udpIceServers, ...tcpIceServers, ...turnTlsIceServers],
99
109
  iceCandidatePoolSize: 0,
100
110
  iceTransportPolicy: 'all',
101
111
  };
@@ -194,7 +204,7 @@ export class ClusterReachability {
194
204
  * @returns {boolean} true if we have all results, false otherwise
195
205
  */
196
206
  private haveWeGotAllResults(): boolean {
197
- return ['udp', 'tcp'].every(
207
+ return ['udp', 'tcp', 'xtls'].every(
198
208
  (protocol) =>
199
209
  this.result[protocol].result === 'reachable' || this.result[protocol].result === 'untested'
200
210
  );
@@ -207,7 +217,7 @@ export class ClusterReachability {
207
217
  * @param {number} latency
208
218
  * @returns {void}
209
219
  */
210
- private storeLatencyResult(protocol: 'udp' | 'tcp', latency: number) {
220
+ private storeLatencyResult(protocol: 'udp' | 'tcp' | 'xtls', latency: number) {
211
221
  const result = this.result[protocol];
212
222
 
213
223
  if (result.latencyInMilliseconds === undefined) {
@@ -227,6 +237,7 @@ export class ClusterReachability {
227
237
  */
228
238
  private registerIceCandidateListener() {
229
239
  this.pc.onicecandidate = (e) => {
240
+ const TURN_TLS_PORT = 443;
230
241
  const CANDIDATE_TYPES = {
231
242
  SERVER_REFLEXIVE: 'srflx',
232
243
  RELAY: 'relay',
@@ -239,7 +250,8 @@ export class ClusterReachability {
239
250
  }
240
251
 
241
252
  if (e.candidate.type === CANDIDATE_TYPES.RELAY) {
242
- this.storeLatencyResult('tcp', this.getElapsedTime());
253
+ const protocol = e.candidate.port === TURN_TLS_PORT ? 'xtls' : 'tcp';
254
+ this.storeLatencyResult(protocol, this.getElapsedTime());
243
255
  // we don't add public IP for TCP, because in the case of relay candidates
244
256
  // e.candidate.address is the TURN server address, not the client's public IP
245
257
  }
@@ -275,6 +287,9 @@ export class ClusterReachability {
275
287
  this.result.tcp = {
276
288
  result: this.numTcpUrls > 0 ? 'unreachable' : 'untested',
277
289
  };
290
+ this.result.xtls = {
291
+ result: this.numXTlsUrls > 0 ? 'unreachable' : 'untested',
292
+ };
278
293
 
279
294
  try {
280
295
  const offer = await this.pc.createOffer({offerToReceiveAudio: true});
@@ -22,10 +22,14 @@ export type ReachabilityMetrics = {
22
22
  reachability_public_udp_failed: number;
23
23
  reachability_public_tcp_success: number;
24
24
  reachability_public_tcp_failed: number;
25
+ reachability_public_xtls_success: number;
26
+ reachability_public_xtls_failed: number;
25
27
  reachability_vmn_udp_success: number;
26
28
  reachability_vmn_udp_failed: number;
27
29
  reachability_vmn_tcp_success: number;
28
30
  reachability_vmn_tcp_failed: number;
31
+ reachability_vmn_xtls_success: number;
32
+ reachability_vmn_xtls_failed: number;
29
33
  };
30
34
 
31
35
  /**
@@ -141,10 +145,14 @@ export default class Reachability {
141
145
  reachability_public_udp_failed: 0,
142
146
  reachability_public_tcp_success: 0,
143
147
  reachability_public_tcp_failed: 0,
148
+ reachability_public_xtls_success: 0,
149
+ reachability_public_xtls_failed: 0,
144
150
  reachability_vmn_udp_success: 0,
145
151
  reachability_vmn_udp_failed: 0,
146
152
  reachability_vmn_tcp_success: 0,
147
153
  reachability_vmn_tcp_failed: 0,
154
+ reachability_vmn_xtls_success: 0,
155
+ reachability_vmn_xtls_failed: 0,
148
156
  };
149
157
 
150
158
  const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
@@ -156,6 +164,10 @@ export default class Reachability {
156
164
  const outcome = result.tcp.result === 'reachable' ? 'success' : 'failed';
157
165
  stats[`reachability_${clusterType}_tcp_${outcome}`] += 1;
158
166
  }
167
+ if (result.xtls && result.xtls.result !== 'untested') {
168
+ const outcome = result.xtls.result === 'reachable' ? 'success' : 'failed';
169
+ stats[`reachability_${clusterType}_xtls_${outcome}`] += 1;
170
+ }
159
171
  };
160
172
 
161
173
  try {
@@ -338,7 +350,10 @@ export default class Reachability {
338
350
  LoggerProxy.logger.log(
339
351
  `Reachability:index#performReachabilityChecks --> doing UDP${
340
352
  // @ts-ignore
341
- this.webex.config.meetings.experimental.enableTcpReachability ? ' and TCP' : ''
353
+ this.webex.config.meetings.experimental.enableTcpReachability ? ',TCP' : ''
354
+ }${
355
+ // @ts-ignore
356
+ this.webex.config.meetings.experimental.enableTlsReachability ? ',TLS' : ''
342
357
  } reachability checks`
343
358
  );
344
359
 
@@ -354,6 +369,14 @@ export default class Reachability {
354
369
  cluster.tcp = [];
355
370
  }
356
371
 
372
+ const includeTlsReachability =
373
+ // @ts-ignore
374
+ this.webex.config.meetings.experimental.enableTlsReachability && !cluster.isVideoMesh;
375
+
376
+ if (!includeTlsReachability) {
377
+ cluster.xtls = [];
378
+ }
379
+
357
380
  this.clusterReachability[key] = new ClusterReachability(key, cluster);
358
381
 
359
382
  return this.clusterReachability[key].start().then((result) => {
@@ -34,17 +34,21 @@ class ReachabilityRequest {
34
34
  * @returns {Promise}
35
35
  */
36
36
  getClusters = (ipVersion?: IP_VERSION): Promise<{clusters: ClusterList; joinCookie: any}> =>
37
- this.webex
38
- .request({
39
- method: HTTP_VERBS.GET,
40
- shouldRefreshAccessToken: false,
41
- api: API.CALLIOPEDISCOVERY,
42
- resource: RESOURCE.CLUSTERS,
43
- qs: {
44
- JCSupport: 1,
45
- ipver: ipVersion,
46
- },
47
- })
37
+ this.webex.internal.newMetrics.callDiagnosticLatencies
38
+ .measureLatency(
39
+ () =>
40
+ this.webex.request({
41
+ method: HTTP_VERBS.GET,
42
+ shouldRefreshAccessToken: false,
43
+ api: API.CALLIOPEDISCOVERY,
44
+ resource: RESOURCE.CLUSTERS,
45
+ qs: {
46
+ JCSupport: 1,
47
+ ipver: ipVersion,
48
+ },
49
+ }),
50
+ 'internal.get.cluster.time'
51
+ )
48
52
  .then((res) => {
49
53
  const {clusters, joinCookie} = res.body;
50
54
 
@@ -22,3 +22,24 @@ export function convertStunUrlToTurn(stunUrl: string, protocol: 'udp' | 'tcp') {
22
22
 
23
23
  return url.toString();
24
24
  }
25
+
26
+ /**
27
+ * Converts a stun url to a turns url
28
+ *
29
+ * @param {string} stunUrl url of a stun server
30
+ * @returns {string} url of a turns server
31
+ */
32
+ export function convertStunUrlToTurnTls(stunUrl: string) {
33
+ // stunUrl looks like this: "stun:external-media1.public.wjfkm-a-15.prod.infra.webex.com:443"
34
+ // and we need it to be like this: "turns:external-media1.public.wjfkm-a-15.prod.infra.webex.com:443?transport=tcp"
35
+ const url = new URL(stunUrl);
36
+
37
+ if (url.protocol !== 'stun:') {
38
+ throw new Error(`Not a STUN URL: ${stunUrl}`);
39
+ }
40
+
41
+ url.protocol = 'turns:';
42
+ url.searchParams.append('transport', 'tcp');
43
+
44
+ return url.toString();
45
+ }
@@ -568,7 +568,7 @@ export default class ReconnectionManager {
568
568
 
569
569
  const iceServers = [];
570
570
 
571
- if (turnServerResult.turnServerInfo) {
571
+ if (turnServerResult.turnServerInfo?.url) {
572
572
  iceServers.push({
573
573
  urls: turnServerResult.turnServerInfo.url,
574
574
  username: turnServerResult.turnServerInfo.username || '',
package/src/roap/index.ts CHANGED
@@ -5,12 +5,18 @@ import {ROAP} from '../constants';
5
5
  import LoggerProxy from '../common/logs/logger-proxy';
6
6
 
7
7
  import RoapRequest from './request';
8
- import TurnDiscovery from './turnDiscovery';
8
+ import TurnDiscovery, {TurnDiscoveryResult} from './turnDiscovery';
9
9
  import Meeting from '../meeting';
10
10
  import MeetingUtil from '../meeting/util';
11
11
  import Metrics from '../metrics';
12
12
  import BEHAVIORAL_METRICS from '../metrics/constants';
13
13
 
14
+ export {
15
+ type TurnDiscoveryResult,
16
+ type TurnServerInfo,
17
+ type TurnDiscoverySkipReason,
18
+ } from './turnDiscovery';
19
+
14
20
  /**
15
21
  * Roap options
16
22
  * @typedef {Object} RoapOptions
@@ -39,7 +45,7 @@ export default class Roap extends StatelessWebexPlugin {
39
45
  options: any;
40
46
  roapHandler: any;
41
47
  roapRequest: any;
42
- turnDiscovery: any;
48
+ turnDiscovery: TurnDiscovery;
43
49
 
44
50
  /**
45
51
  *
@@ -260,7 +266,23 @@ export default class Roap extends StatelessWebexPlugin {
260
266
  * @param {Boolean} [isForced]
261
267
  * @returns {Promise}
262
268
  */
263
- doTurnDiscovery(meeting: Meeting, isReconnecting: boolean, isForced?: boolean) {
269
+ doTurnDiscovery(
270
+ meeting: Meeting,
271
+ isReconnecting: boolean,
272
+ isForced?: boolean
273
+ ): Promise<TurnDiscoveryResult> {
264
274
  return this.turnDiscovery.doTurnDiscovery(meeting, isReconnecting, isForced);
265
275
  }
276
+
277
+ generateTurnDiscoveryRequestMessage(meeting: Meeting, isForced: boolean) {
278
+ return this.turnDiscovery.generateTurnDiscoveryRequestMessage(meeting, isForced);
279
+ }
280
+
281
+ handleTurnDiscoveryHttpResponse(meeting: Meeting, httpResponse: object) {
282
+ return this.turnDiscovery.handleTurnDiscoveryHttpResponse(meeting, httpResponse);
283
+ }
284
+
285
+ abortTurnDiscovery() {
286
+ return this.turnDiscovery.abort();
287
+ }
266
288
  }
@@ -123,7 +123,7 @@ export default class RoapRequest extends StatelessWebexPlugin {
123
123
  );
124
124
  const {locus} = res.body;
125
125
 
126
- locus.roapSeq = roapMessage.seq;
126
+ locus.roapSeq = options.roapMessage.seq;
127
127
 
128
128
  return {
129
129
  locus,
@@ -139,9 +139,9 @@ export default class RoapRequest extends StatelessWebexPlugin {
139
139
  rawError: err,
140
140
  },
141
141
  });
142
- LoggerProxy.logger.error(`Roap:request#sendRoap --> Error:${JSON.stringify(err, null, 2)}`);
142
+ LoggerProxy.logger.error(`Roap:request#sendRoap --> Error:`, err);
143
143
  LoggerProxy.logger.error(
144
- `Roap:request#sendRoapRequest --> errorBody:${JSON.stringify(
144
+ `Roap:request#sendRoapRequest --> roapMessage that caused error:${JSON.stringify(
145
145
  roapMessage,
146
146
  null,
147
147
  2