@webex/plugin-meetings 3.0.0-beta.25 → 3.0.0-beta.250

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 (360) hide show
  1. package/README.md +46 -8
  2. package/dist/annotation/annotation.types.js +7 -0
  3. package/dist/annotation/annotation.types.js.map +1 -0
  4. package/dist/annotation/constants.js +49 -0
  5. package/dist/annotation/constants.js.map +1 -0
  6. package/dist/annotation/index.js +342 -0
  7. package/dist/annotation/index.js.map +1 -0
  8. package/dist/breakouts/breakout.js +114 -14
  9. package/dist/breakouts/breakout.js.map +1 -1
  10. package/dist/breakouts/edit-lock-error.js +52 -0
  11. package/dist/breakouts/edit-lock-error.js.map +1 -0
  12. package/dist/breakouts/events.js +45 -0
  13. package/dist/breakouts/events.js.map +1 -0
  14. package/dist/breakouts/index.js +841 -19
  15. package/dist/breakouts/index.js.map +1 -1
  16. package/dist/breakouts/request.js +78 -0
  17. package/dist/breakouts/request.js.map +1 -0
  18. package/dist/breakouts/utils.js +67 -0
  19. package/dist/breakouts/utils.js.map +1 -0
  20. package/dist/common/errors/webex-errors.js +28 -7
  21. package/dist/common/errors/webex-errors.js.map +1 -1
  22. package/dist/common/logs/logger-proxy.js +1 -1
  23. package/dist/common/logs/logger-proxy.js.map +1 -1
  24. package/dist/common/queue.js +24 -9
  25. package/dist/common/queue.js.map +1 -1
  26. package/dist/config.js +5 -10
  27. package/dist/config.js.map +1 -1
  28. package/dist/constants.js +193 -28
  29. package/dist/constants.js.map +1 -1
  30. package/dist/controls-options-manager/constants.js +14 -0
  31. package/dist/controls-options-manager/constants.js.map +1 -0
  32. package/dist/controls-options-manager/enums.js +27 -0
  33. package/dist/controls-options-manager/enums.js.map +1 -0
  34. package/dist/controls-options-manager/index.js +297 -0
  35. package/dist/controls-options-manager/index.js.map +1 -0
  36. package/dist/controls-options-manager/types.js +7 -0
  37. package/dist/controls-options-manager/types.js.map +1 -0
  38. package/dist/controls-options-manager/util.js +319 -0
  39. package/dist/controls-options-manager/util.js.map +1 -0
  40. package/dist/index.js +106 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interpretation/collection.js +23 -0
  43. package/dist/interpretation/collection.js.map +1 -0
  44. package/dist/interpretation/index.js +366 -0
  45. package/dist/interpretation/index.js.map +1 -0
  46. package/dist/interpretation/siLanguage.js +25 -0
  47. package/dist/interpretation/siLanguage.js.map +1 -0
  48. package/dist/locus-info/controlsUtils.js +91 -2
  49. package/dist/locus-info/controlsUtils.js.map +1 -1
  50. package/dist/locus-info/index.js +381 -62
  51. package/dist/locus-info/index.js.map +1 -1
  52. package/dist/locus-info/infoUtils.js +7 -1
  53. package/dist/locus-info/infoUtils.js.map +1 -1
  54. package/dist/locus-info/mediaSharesUtils.js +43 -1
  55. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  56. package/dist/locus-info/parser.js +224 -63
  57. package/dist/locus-info/parser.js.map +1 -1
  58. package/dist/locus-info/selfUtils.js +89 -14
  59. package/dist/locus-info/selfUtils.js.map +1 -1
  60. package/dist/media/index.js +49 -106
  61. package/dist/media/index.js.map +1 -1
  62. package/dist/media/properties.js +29 -90
  63. package/dist/media/properties.js.map +1 -1
  64. package/dist/meeting/in-meeting-actions.js +90 -2
  65. package/dist/meeting/in-meeting-actions.js.map +1 -1
  66. package/dist/meeting/index.js +2627 -2557
  67. package/dist/meeting/index.js.map +1 -1
  68. package/dist/meeting/locusMediaRequest.js +292 -0
  69. package/dist/meeting/locusMediaRequest.js.map +1 -0
  70. package/dist/meeting/muteState.js +228 -123
  71. package/dist/meeting/muteState.js.map +1 -1
  72. package/dist/meeting/request.js +256 -196
  73. package/dist/meeting/request.js.map +1 -1
  74. package/dist/meeting/util.js +601 -417
  75. package/dist/meeting/util.js.map +1 -1
  76. package/dist/meeting-info/index.js +48 -7
  77. package/dist/meeting-info/index.js.map +1 -1
  78. package/dist/meeting-info/meeting-info-v2.js +171 -51
  79. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  80. package/dist/meeting-info/util.js +1 -1
  81. package/dist/meeting-info/util.js.map +1 -1
  82. package/dist/meeting-info/utilv2.js +36 -36
  83. package/dist/meeting-info/utilv2.js.map +1 -1
  84. package/dist/meetings/collection.js +22 -0
  85. package/dist/meetings/collection.js.map +1 -1
  86. package/dist/meetings/index.js +370 -90
  87. package/dist/meetings/index.js.map +1 -1
  88. package/dist/meetings/meetings.types.js +7 -0
  89. package/dist/meetings/meetings.types.js.map +1 -0
  90. package/dist/meetings/request.js +2 -0
  91. package/dist/meetings/request.js.map +1 -1
  92. package/dist/meetings/util.js +88 -1
  93. package/dist/meetings/util.js.map +1 -1
  94. package/dist/member/index.js +49 -0
  95. package/dist/member/index.js.map +1 -1
  96. package/dist/member/types.js +25 -0
  97. package/dist/member/types.js.map +1 -0
  98. package/dist/member/util.js +121 -25
  99. package/dist/member/util.js.map +1 -1
  100. package/dist/members/collection.js +10 -0
  101. package/dist/members/collection.js.map +1 -1
  102. package/dist/members/index.js +86 -5
  103. package/dist/members/index.js.map +1 -1
  104. package/dist/members/request.js +106 -38
  105. package/dist/members/request.js.map +1 -1
  106. package/dist/members/types.js +15 -0
  107. package/dist/members/types.js.map +1 -0
  108. package/dist/members/util.js +316 -233
  109. package/dist/members/util.js.map +1 -1
  110. package/dist/metrics/constants.js +6 -5
  111. package/dist/metrics/constants.js.map +1 -1
  112. package/dist/metrics/index.js +1 -468
  113. package/dist/metrics/index.js.map +1 -1
  114. package/dist/multistream/mediaRequestManager.js +238 -49
  115. package/dist/multistream/mediaRequestManager.js.map +1 -1
  116. package/dist/multistream/receiveSlot.js +49 -16
  117. package/dist/multistream/receiveSlot.js.map +1 -1
  118. package/dist/multistream/receiveSlotManager.js +52 -34
  119. package/dist/multistream/receiveSlotManager.js.map +1 -1
  120. package/dist/multistream/remoteMedia.js +44 -18
  121. package/dist/multistream/remoteMedia.js.map +1 -1
  122. package/dist/multistream/remoteMediaGroup.js +60 -3
  123. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  124. package/dist/multistream/remoteMediaManager.js +209 -59
  125. package/dist/multistream/remoteMediaManager.js.map +1 -1
  126. package/dist/reachability/index.js +81 -30
  127. package/dist/reachability/index.js.map +1 -1
  128. package/dist/reachability/request.js +16 -7
  129. package/dist/reachability/request.js.map +1 -1
  130. package/dist/reconnection-manager/index.js +199 -154
  131. package/dist/reconnection-manager/index.js.map +1 -1
  132. package/dist/recording-controller/index.js +21 -2
  133. package/dist/recording-controller/index.js.map +1 -1
  134. package/dist/recording-controller/util.js +9 -8
  135. package/dist/recording-controller/util.js.map +1 -1
  136. package/dist/roap/index.js +23 -29
  137. package/dist/roap/index.js.map +1 -1
  138. package/dist/roap/request.js +112 -89
  139. package/dist/roap/request.js.map +1 -1
  140. package/dist/roap/turnDiscovery.js +96 -36
  141. package/dist/roap/turnDiscovery.js.map +1 -1
  142. package/dist/rtcMetrics/constants.js +12 -0
  143. package/dist/rtcMetrics/constants.js.map +1 -0
  144. package/dist/rtcMetrics/index.js +117 -0
  145. package/dist/rtcMetrics/index.js.map +1 -0
  146. package/dist/statsAnalyzer/index.js +51 -34
  147. package/dist/statsAnalyzer/index.js.map +1 -1
  148. package/dist/statsAnalyzer/mqaUtil.js +6 -6
  149. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  150. package/dist/types/annotation/annotation.types.d.ts +42 -0
  151. package/dist/types/annotation/constants.d.ts +31 -0
  152. package/dist/types/annotation/index.d.ts +117 -0
  153. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  154. package/dist/types/breakouts/events.d.ts +8 -0
  155. package/dist/types/breakouts/request.d.ts +22 -0
  156. package/dist/types/breakouts/utils.d.ts +15 -0
  157. package/dist/types/common/errors/webex-errors.d.ts +13 -1
  158. package/dist/types/common/queue.d.ts +9 -7
  159. package/dist/types/config.d.ts +1 -6
  160. package/dist/types/constants.d.ts +155 -21
  161. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  162. package/dist/types/controls-options-manager/enums.d.ts +15 -0
  163. package/dist/types/controls-options-manager/index.d.ts +136 -0
  164. package/dist/types/controls-options-manager/types.d.ts +43 -0
  165. package/dist/types/controls-options-manager/util.d.ts +1 -0
  166. package/dist/types/index.d.ts +6 -4
  167. package/dist/types/interpretation/collection.d.ts +5 -0
  168. package/dist/types/interpretation/index.d.ts +5 -0
  169. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  170. package/dist/types/locus-info/index.d.ts +57 -4
  171. package/dist/types/locus-info/parser.d.ts +65 -6
  172. package/dist/types/media/index.d.ts +2 -0
  173. package/dist/types/media/properties.d.ts +22 -36
  174. package/dist/types/meeting/in-meeting-actions.d.ts +90 -2
  175. package/dist/types/meeting/index.d.ts +312 -492
  176. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  177. package/dist/types/meeting/muteState.d.ts +98 -22
  178. package/dist/types/meeting/request.d.ts +72 -43
  179. package/dist/types/meeting/util.d.ts +101 -1
  180. package/dist/types/meeting-info/index.d.ts +6 -1
  181. package/dist/types/meeting-info/meeting-info-v2.d.ts +30 -1
  182. package/dist/types/meetings/collection.d.ts +8 -0
  183. package/dist/types/meetings/index.d.ts +76 -12
  184. package/dist/types/meetings/meetings.types.d.ts +4 -0
  185. package/dist/types/member/index.d.ts +13 -0
  186. package/dist/types/member/types.d.ts +32 -0
  187. package/dist/types/members/collection.d.ts +5 -0
  188. package/dist/types/members/index.d.ts +35 -2
  189. package/dist/types/members/request.d.ts +73 -9
  190. package/dist/types/members/types.d.ts +24 -0
  191. package/dist/types/members/util.d.ts +209 -1
  192. package/dist/types/metrics/constants.d.ts +5 -4
  193. package/dist/types/metrics/index.d.ts +4 -119
  194. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  195. package/dist/types/multistream/receiveSlot.d.ts +16 -12
  196. package/dist/types/multistream/receiveSlotManager.d.ts +19 -4
  197. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  198. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  199. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  200. package/dist/types/reachability/index.d.ts +15 -3
  201. package/dist/types/reachability/request.d.ts +7 -3
  202. package/dist/types/reconnection-manager/index.d.ts +9 -0
  203. package/dist/types/recording-controller/index.d.ts +15 -1
  204. package/dist/types/recording-controller/util.d.ts +5 -4
  205. package/dist/types/roap/request.d.ts +15 -11
  206. package/dist/types/roap/turnDiscovery.d.ts +18 -1
  207. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  208. package/dist/types/rtcMetrics/index.d.ts +47 -0
  209. package/dist/types/statsAnalyzer/index.d.ts +6 -1
  210. package/package.json +23 -20
  211. package/src/annotation/annotation.types.ts +50 -0
  212. package/src/annotation/constants.ts +36 -0
  213. package/src/annotation/index.ts +328 -0
  214. package/src/breakouts/README.md +44 -14
  215. package/src/breakouts/breakout.ts +87 -9
  216. package/src/breakouts/edit-lock-error.ts +25 -0
  217. package/src/breakouts/events.ts +56 -0
  218. package/src/breakouts/index.ts +710 -10
  219. package/src/breakouts/request.ts +55 -0
  220. package/src/breakouts/utils.ts +57 -0
  221. package/src/common/errors/webex-errors.ts +27 -2
  222. package/src/common/logs/logger-proxy.ts +1 -1
  223. package/src/common/queue.ts +22 -8
  224. package/src/config.ts +4 -9
  225. package/src/constants.ts +176 -18
  226. package/src/controls-options-manager/constants.ts +5 -0
  227. package/src/controls-options-manager/enums.ts +18 -0
  228. package/src/controls-options-manager/index.ts +278 -0
  229. package/src/controls-options-manager/types.ts +59 -0
  230. package/src/controls-options-manager/util.ts +300 -0
  231. package/src/index.ts +39 -0
  232. package/src/interpretation/README.md +60 -0
  233. package/src/interpretation/collection.ts +19 -0
  234. package/src/interpretation/index.ts +332 -0
  235. package/src/interpretation/siLanguage.ts +18 -0
  236. package/src/locus-info/controlsUtils.ts +108 -0
  237. package/src/locus-info/index.ts +412 -59
  238. package/src/locus-info/infoUtils.ts +10 -2
  239. package/src/locus-info/mediaSharesUtils.ts +48 -0
  240. package/src/locus-info/parser.ts +231 -39
  241. package/src/locus-info/selfUtils.ts +81 -5
  242. package/src/media/index.ts +89 -109
  243. package/src/media/properties.ts +48 -87
  244. package/src/meeting/in-meeting-actions.ts +179 -3
  245. package/src/meeting/index.ts +2111 -2143
  246. package/src/meeting/locusMediaRequest.ts +313 -0
  247. package/src/meeting/muteState.ts +227 -130
  248. package/src/meeting/request.ts +172 -121
  249. package/src/meeting/util.ts +588 -395
  250. package/src/meeting-info/index.ts +54 -8
  251. package/src/meeting-info/meeting-info-v2.ts +148 -14
  252. package/src/meeting-info/util.ts +1 -1
  253. package/src/meeting-info/utilv2.ts +23 -23
  254. package/src/meetings/collection.ts +20 -0
  255. package/src/meetings/index.ts +407 -108
  256. package/src/meetings/meetings.types.ts +12 -0
  257. package/src/meetings/request.ts +2 -0
  258. package/src/meetings/util.ts +103 -4
  259. package/src/member/index.ts +49 -0
  260. package/src/member/types.ts +38 -0
  261. package/src/member/util.ts +127 -25
  262. package/src/members/collection.ts +8 -0
  263. package/src/members/index.ts +107 -6
  264. package/src/members/request.ts +97 -17
  265. package/src/members/types.ts +28 -0
  266. package/src/members/util.ts +319 -240
  267. package/src/metrics/constants.ts +5 -4
  268. package/src/metrics/index.ts +1 -490
  269. package/src/multistream/mediaRequestManager.ts +289 -79
  270. package/src/multistream/receiveSlot.ts +55 -18
  271. package/src/multistream/receiveSlotManager.ts +46 -24
  272. package/src/multistream/remoteMedia.ts +27 -2
  273. package/src/multistream/remoteMediaGroup.ts +59 -0
  274. package/src/multistream/remoteMediaManager.ts +148 -30
  275. package/src/reachability/index.ts +69 -17
  276. package/src/reachability/request.ts +16 -7
  277. package/src/reconnection-manager/index.ts +81 -54
  278. package/src/recording-controller/index.ts +20 -3
  279. package/src/recording-controller/util.ts +26 -9
  280. package/src/roap/index.ts +23 -30
  281. package/src/roap/request.ts +104 -95
  282. package/src/roap/turnDiscovery.ts +50 -25
  283. package/src/rtcMetrics/constants.ts +3 -0
  284. package/src/rtcMetrics/index.ts +100 -0
  285. package/src/statsAnalyzer/index.ts +73 -35
  286. package/src/statsAnalyzer/mqaUtil.ts +8 -10
  287. package/test/integration/spec/converged-space-meetings.js +233 -0
  288. package/test/integration/spec/journey.js +336 -259
  289. package/test/integration/spec/space-meeting.js +76 -3
  290. package/test/unit/spec/annotation/index.ts +418 -0
  291. package/test/unit/spec/breakouts/breakout.ts +142 -24
  292. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  293. package/test/unit/spec/breakouts/events.ts +89 -0
  294. package/test/unit/spec/breakouts/index.ts +1545 -48
  295. package/test/unit/spec/breakouts/request.ts +104 -0
  296. package/test/unit/spec/breakouts/utils.js +72 -0
  297. package/test/unit/spec/common/queue.js +31 -2
  298. package/test/unit/spec/controls-options-manager/index.js +287 -0
  299. package/test/unit/spec/controls-options-manager/util.js +582 -0
  300. package/test/unit/spec/fixture/locus.js +1 -0
  301. package/test/unit/spec/interpretation/collection.ts +15 -0
  302. package/test/unit/spec/interpretation/index.ts +589 -0
  303. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  304. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  305. package/test/unit/spec/locus-info/index.js +1283 -33
  306. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  307. package/test/unit/spec/locus-info/mediaSharesUtils.ts +22 -0
  308. package/test/unit/spec/locus-info/parser.js +62 -22
  309. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  310. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  311. package/test/unit/spec/media/index.ts +138 -28
  312. package/test/unit/spec/meeting/in-meeting-actions.ts +89 -3
  313. package/test/unit/spec/meeting/index.js +3576 -1748
  314. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  315. package/test/unit/spec/meeting/muteState.js +370 -208
  316. package/test/unit/spec/meeting/request.js +440 -45
  317. package/test/unit/spec/meeting/utils.js +671 -54
  318. package/test/unit/spec/meeting-info/index.js +181 -0
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +383 -5
  320. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  321. package/test/unit/spec/meetings/collection.js +14 -0
  322. package/test/unit/spec/meetings/index.js +939 -150
  323. package/test/unit/spec/meetings/utils.js +206 -2
  324. package/test/unit/spec/member/index.js +58 -4
  325. package/test/unit/spec/member/util.js +479 -35
  326. package/test/unit/spec/members/index.js +319 -1
  327. package/test/unit/spec/members/request.js +206 -27
  328. package/test/unit/spec/members/utils.js +184 -0
  329. package/test/unit/spec/metrics/index.js +1 -50
  330. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  331. package/test/unit/spec/multistream/receiveSlot.ts +72 -13
  332. package/test/unit/spec/multistream/receiveSlotManager.ts +58 -28
  333. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  334. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  335. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  336. package/test/unit/spec/reachability/index.ts +185 -7
  337. package/test/unit/spec/reachability/request.js +68 -0
  338. package/test/unit/spec/reconnection-manager/index.js +80 -6
  339. package/test/unit/spec/recording-controller/index.js +294 -218
  340. package/test/unit/spec/recording-controller/util.js +223 -96
  341. package/test/unit/spec/roap/index.ts +31 -51
  342. package/test/unit/spec/roap/request.ts +202 -85
  343. package/test/unit/spec/roap/turnDiscovery.ts +45 -10
  344. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  345. package/test/unit/spec/stats-analyzer/index.js +29 -2
  346. package/test/utils/constants.js +9 -0
  347. package/test/utils/integrationTestUtils.js +46 -0
  348. package/test/utils/testUtils.js +0 -45
  349. package/test/utils/webex-config.js +4 -0
  350. package/test/utils/webex-test-users.js +6 -3
  351. package/dist/meeting/effectsState.js +0 -262
  352. package/dist/meeting/effectsState.js.map +0 -1
  353. package/dist/metrics/config.js +0 -299
  354. package/dist/metrics/config.js.map +0 -1
  355. package/dist/types/meeting/effectsState.d.ts +0 -42
  356. package/dist/types/metrics/config.d.ts +0 -178
  357. package/src/index.js +0 -16
  358. package/src/meeting/effectsState.ts +0 -211
  359. package/src/metrics/config.ts +0 -495
  360. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,11 +1,13 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  /* eslint-disable import/prefer-default-export */
3
- import {MediaType} from '@webex/internal-media-core';
4
-
3
+ import {MediaType, ReceiveSlot as WcmeReceiveSlot} from '@webex/internal-media-core';
5
4
  import LoggerProxy from '../common/logs/logger-proxy';
6
- import Meeting from '../meeting';
7
5
 
8
- import {CSI, ReceiveSlot} from './receiveSlot';
6
+ import {FindMemberIdCallback, ReceiveSlot} from './receiveSlot';
7
+
8
+ export type CreateSlotCallback = (mediaType: MediaType) => Promise<WcmeReceiveSlot>;
9
+
10
+ export type {CSI, FindMemberIdCallback} from './receiveSlot';
9
11
 
10
12
  /**
11
13
  * Manages all receive slots used by a meeting. WMCE receive slots cannot be ever deleted,
@@ -16,13 +18,18 @@ export class ReceiveSlotManager {
16
18
 
17
19
  private freeSlots: {[key in MediaType]: ReceiveSlot[]};
18
20
 
19
- private meeting: Meeting;
21
+ private createSlotCallback: CreateSlotCallback;
22
+
23
+ private findMemberIdByCsiCallback: FindMemberIdCallback;
20
24
 
21
25
  /**
22
26
  * Constructor
23
27
  * @param {Meeting} meeting
24
28
  */
25
- constructor(meeting) {
29
+ constructor(
30
+ createSlotCallback: CreateSlotCallback,
31
+ findMemberIdByCsiCallback: FindMemberIdCallback
32
+ ) {
26
33
  this.allocatedSlots = {
27
34
  [MediaType.AudioMain]: [],
28
35
  [MediaType.VideoMain]: [],
@@ -35,7 +42,8 @@ export class ReceiveSlotManager {
35
42
  [MediaType.AudioSlides]: [],
36
43
  [MediaType.VideoSlides]: [],
37
44
  };
38
- this.meeting = meeting;
45
+ this.createSlotCallback = createSlotCallback;
46
+ this.findMemberIdByCsiCallback = findMemberIdByCsiCallback;
39
47
  }
40
48
 
41
49
  /**
@@ -45,34 +53,24 @@ export class ReceiveSlotManager {
45
53
  * @returns {Promise<ReceiveSlot>}
46
54
  */
47
55
  async allocateSlot(mediaType: MediaType): Promise<ReceiveSlot> {
48
- if (!this.meeting?.mediaProperties?.webrtcMediaConnection) {
49
- return Promise.reject(new Error('Webrtc media connection is missing'));
50
- }
51
-
52
56
  // try to use one of the free ones
53
57
  const availableSlot = this.freeSlots[mediaType].pop();
54
58
 
55
59
  if (availableSlot) {
56
60
  this.allocatedSlots[mediaType].push(availableSlot);
57
61
 
58
- LoggerProxy.logger.log(`receive slot re-used: ${availableSlot.id}`);
62
+ LoggerProxy.logger.log(`${mediaType}: receive slot re-used: ${availableSlot.id}`);
59
63
 
60
64
  return availableSlot;
61
65
  }
62
66
 
63
67
  // we have to create a new one
64
- const wcmeReceiveSlot =
65
- await this.meeting.mediaProperties.webrtcMediaConnection.createReceiveSlot(mediaType);
66
-
67
- const receiveSlot = new ReceiveSlot(
68
- mediaType,
69
- wcmeReceiveSlot,
70
- // @ts-ignore
71
- (csi: CSI) => this.meeting.members.findMemberByCsi(csi)?.id
72
- );
68
+ const wcmeReceiveSlot = await this.createSlotCallback(mediaType);
69
+
70
+ const receiveSlot = new ReceiveSlot(mediaType, wcmeReceiveSlot, this.findMemberIdByCsiCallback);
73
71
 
74
72
  this.allocatedSlots[mediaType].push(receiveSlot);
75
- LoggerProxy.logger.log(`new receive slot allocated: ${receiveSlot.id}`);
73
+ LoggerProxy.logger.log(`${mediaType}: new receive slot allocated: ${receiveSlot.id}`);
76
74
 
77
75
  return receiveSlot;
78
76
  }
@@ -89,10 +87,10 @@ export class ReceiveSlotManager {
89
87
  if (idx >= 0) {
90
88
  this.allocatedSlots[slot.mediaType].splice(idx, 1);
91
89
  this.freeSlots[slot.mediaType].push(slot);
92
- LoggerProxy.logger.log(`receive slot released: ${slot.id}`);
90
+ LoggerProxy.logger.log(`${slot.mediaType}: receive slot released: ${slot.id}`);
93
91
  } else {
94
92
  LoggerProxy.logger.warn(
95
- 'ReceiveSlotManager#releaseSlot --> trying to release a slot that is not managed by this ReceiveSlotManager'
93
+ `ReceiveSlotManager#releaseSlot --> trying to release a ${slot.mediaType}} slot that is not managed by this ReceiveSlotManager`
96
94
  );
97
95
  }
98
96
  }
@@ -141,4 +139,28 @@ export class ReceiveSlotManager {
141
139
  numFreeSlots,
142
140
  };
143
141
  }
142
+
143
+ /**
144
+ * Tries to find the member id on all allocated receive slots
145
+ * This function should be called when new members are added to the meeting.
146
+ */
147
+ updateMemberIds() {
148
+ Object.keys(this.allocatedSlots).forEach((key) => {
149
+ this.allocatedSlots[key].forEach((slot: ReceiveSlot) => {
150
+ slot.findMemberId();
151
+ });
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Find a receive slot by a ssrc.
157
+ *
158
+ * @param ssrc - The ssrc of the receive slot to find.
159
+ * @returns - The receive slot with this ssrc, undefined if not found.
160
+ */
161
+ findReceiveSlotBySsrc(ssrc: number): ReceiveSlot | undefined {
162
+ return Object.values(this.allocatedSlots)
163
+ .flat()
164
+ .find((r) => ssrc && r.wcmeReceiveSlot?.id?.ssrc === ssrc);
165
+ }
144
166
  }
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable valid-jsdoc */
2
- import {MediaType, SourceState} from '@webex/internal-media-core';
2
+ import {MediaType, StreamState} from '@webex/internal-media-core';
3
3
  import LoggerProxy from '../common/logs/logger-proxy';
4
4
  import EventsScope from '../common/events/events-scope';
5
5
 
@@ -102,6 +102,31 @@ export class RemoteMedia extends EventsScope {
102
102
  this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;
103
103
  }
104
104
 
105
+ /**
106
+ * Supply the width and height of the video element
107
+ * to restrict the requested resolution to this size
108
+ * @param width width of the video element
109
+ * @param height height of the video element
110
+ */
111
+ public setSizeHint(width, height) {
112
+ // only base on height for now
113
+ let fs: number;
114
+
115
+ if (height < 135) {
116
+ fs = 60;
117
+ } else if (height < 270) {
118
+ fs = 240;
119
+ } else if (height < 540) {
120
+ fs = 920;
121
+ } else if (height <= 720) {
122
+ fs = 3600;
123
+ } else {
124
+ fs = 8192;
125
+ }
126
+
127
+ this.receiveSlot?.setMaxFs(fs);
128
+ }
129
+
105
130
  /**
106
131
  * Invalidates the remote media by clearing the reference to a receive slot and
107
132
  * cancelling the media request.
@@ -208,7 +233,7 @@ export class RemoteMedia extends EventsScope {
208
233
  /**
209
234
  * Getter for source state
210
235
  */
211
- public get sourceState(): SourceState {
236
+ public get sourceState(): StreamState {
212
237
  return this.receiveSlot?.sourceState;
213
238
  }
214
239
 
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  /* eslint-disable require-jsdoc */
3
3
  /* eslint-disable import/prefer-default-export */
4
+ import {forEach} from 'lodash';
4
5
  import LoggerProxy from '../common/logs/logger-proxy';
5
6
 
6
7
  import {getMaxFs, RemoteMedia, RemoteVideoResolution} from './remoteMedia';
@@ -66,6 +67,53 @@ export class RemoteMediaGroup {
66
67
  return [...this.unpinnedRemoteMedia, ...this.pinnedRemoteMedia];
67
68
  }
68
69
 
70
+ /**
71
+ * Sets CSIs for multiple RemoteMedia instances belonging to this RemoteMediaGroup.
72
+ * For each entry in the remoteMediaCsis array:
73
+ * - if csi is specified, the RemoteMedia instance is pinned to that CSI
74
+ * - if csi is undefined, the RemoteMedia instance is unpinned
75
+ * @internal
76
+ */
77
+ public setActiveSpeakerCsis(
78
+ remoteMediaCsis: {remoteMedia: RemoteMedia; csi?: number}[],
79
+ commit = true
80
+ ): void {
81
+ forEach(remoteMediaCsis, ({csi, remoteMedia}) => {
82
+ if (csi) {
83
+ if (!(this.pinnedRemoteMedia.indexOf(remoteMedia) >= 0)) {
84
+ const unpinId = this.unpinnedRemoteMedia.indexOf(remoteMedia);
85
+ if (unpinId >= 0) {
86
+ this.unpinnedRemoteMedia.splice(unpinId, 1);
87
+ this.pinnedRemoteMedia.push(remoteMedia);
88
+ } else {
89
+ throw new Error(
90
+ `failed to pin a remote media object ${remoteMedia.id}, because it is not found in this remote media group`
91
+ );
92
+ }
93
+ }
94
+ remoteMedia.sendMediaRequest(csi, false);
95
+ } else {
96
+ if (!(this.unpinnedRemoteMedia.indexOf(remoteMedia) >= 0)) {
97
+ const pinId = this.pinnedRemoteMedia.indexOf(remoteMedia);
98
+ if (pinId >= 0) {
99
+ this.pinnedRemoteMedia.splice(pinId, 1);
100
+ this.unpinnedRemoteMedia.push(remoteMedia);
101
+ } else {
102
+ throw new Error(
103
+ `failed to unpin a remote media object ${remoteMedia.id}, because it is not found in this remote media group`
104
+ );
105
+ }
106
+ }
107
+ remoteMedia.cancelMediaRequest(false);
108
+ }
109
+ });
110
+ this.cancelActiveSpeakerMediaRequest(false);
111
+ this.sendActiveSpeakerMediaRequest(false);
112
+ if (commit) {
113
+ this.mediaRequestManager.commit();
114
+ }
115
+ }
116
+
69
117
  /**
70
118
  * Pins a specific remote media instance to a specfic CSI, so the media will
71
119
  * no longer come from active speaker, but from that CSI.
@@ -151,6 +199,17 @@ export class RemoteMediaGroup {
151
199
  throw new Error(`remote media object ${remoteMedia.id} not found in the group`);
152
200
  }
153
201
 
202
+ /**
203
+ * setPreferLiveVideo - sets preferLiveVideo to true/false
204
+ * @internal
205
+ */
206
+ public setPreferLiveVideo(preferLiveVideo: boolean, commit: boolean) {
207
+ if (this.options.preferLiveVideo !== preferLiveVideo) {
208
+ this.options.preferLiveVideo = preferLiveVideo;
209
+ this.sendActiveSpeakerMediaRequest(commit);
210
+ }
211
+ }
212
+
154
213
  private sendActiveSpeakerMediaRequest(commit: boolean) {
155
214
  this.cancelActiveSpeakerMediaRequest(false);
156
215
 
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable valid-jsdoc */
2
- import {cloneDeep, remove} from 'lodash';
2
+ import {cloneDeep, forEach, remove} from 'lodash';
3
3
  import {EventMap} from 'typed-emitter';
4
4
  import {MediaType} from '@webex/internal-media-core';
5
5
 
@@ -96,6 +96,7 @@ const OnePlusFiveLayout: VideoLayout = {
96
96
  };
97
97
 
98
98
  // A layout with 2 big panes for 2 main active speakers and a strip of 6 small panes for other active speakers:
99
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
100
  const TwoMainPlusSixSmallLayout: VideoLayout = {
100
101
  activeSpeakerVideoPaneGroups: [
101
102
  {
@@ -472,6 +473,8 @@ export class RemoteMediaManager extends EventsScope {
472
473
  if (!this.started) {
473
474
  throw new Error('setLayout() called before start()');
474
475
  }
476
+ LoggerProxy.logger.log(`RemoteMediaManager#setLayout --> new layout selected: ${layoutId}`);
477
+
475
478
  this.currentLayoutId = layoutId;
476
479
  this.currentLayout = cloneDeep(this.config.video.layouts[this.currentLayoutId]);
477
480
 
@@ -490,6 +493,38 @@ export class RemoteMediaManager extends EventsScope {
490
493
  return this.currentLayoutId;
491
494
  }
492
495
 
496
+ /**
497
+ * sets the preferLiveVideo
498
+ */
499
+ public setPreferLiveVideo(preferLiveVideo: boolean) {
500
+ LoggerProxy.logger.log(
501
+ `RemoteMediaManager#setPreferLiveVideo --> setPreferLiveVideo is called to set preferLiveVideo to ${preferLiveVideo}`
502
+ );
503
+ this.config.video.preferLiveVideo = preferLiveVideo;
504
+ Object.values(this.media.video.activeSpeakerGroups).forEach((activeSpeakerGroup) => {
505
+ activeSpeakerGroup.setPreferLiveVideo(preferLiveVideo, false);
506
+ });
507
+ this.mediaRequestManagers.video.commit();
508
+ }
509
+
510
+ /**
511
+ * Sets CSIs for multiple RemoteMedia instances belonging to RemoteMediaGroup.
512
+ * For each entry in the remoteMediaCsis array:
513
+ * - if csi is specified, the RemoteMedia instance is pinned to that CSI
514
+ * - if csi is undefined, the RemoteMedia instance is unpinned
515
+ */
516
+ public setActiveSpeakerCsis(remoteMediaCsis: {remoteMedia: RemoteMedia; csi?: number}[]) {
517
+ Object.values(this.media.video.activeSpeakerGroups).forEach((remoteMediaGroup) => {
518
+ const groupRemoteMediaCsis = remoteMediaCsis.filter(({remoteMedia}) =>
519
+ remoteMediaGroup.includes(remoteMedia)
520
+ );
521
+ if (groupRemoteMediaCsis.length > 0) {
522
+ remoteMediaGroup.setActiveSpeakerCsis(groupRemoteMediaCsis, false);
523
+ }
524
+ });
525
+ this.mediaRequestManagers.video.commit();
526
+ }
527
+
493
528
  /**
494
529
  * Creates the audio slots
495
530
  */
@@ -601,25 +636,37 @@ export class RemoteMediaManager extends EventsScope {
601
636
  }
602
637
 
603
638
  /**
604
- * Allocates receive slots to all video panes in the current selected layout
639
+ * Allocates receive slots to all active speaker video panes
640
+ * in the current selected layout.
641
+ *
642
+ * Allocation tries to keep the same order of the slots between the previous
643
+ * layout and the new one. Sorting helps making sure that highest priority slots
644
+ * go in the same order in the new layout.
605
645
  */
606
- private allocateSlotsToVideoPaneGroups() {
607
- this.receiveSlotAllocations = {activeSpeaker: {}, receiverSelected: {}};
608
-
609
- this.currentLayout?.activeSpeakerVideoPaneGroups?.forEach((group) => {
610
- this.receiveSlotAllocations.activeSpeaker[group.id] = {slots: []};
611
-
612
- for (let paneIndex = 0; paneIndex < group.numPanes; paneIndex += 1) {
613
- // allocate a slot from the "unused" list
614
- const freeSlot = this.slots.video.unused.pop();
615
-
616
- if (freeSlot) {
617
- this.slots.video.activeSpeaker.push(freeSlot);
618
- this.receiveSlotAllocations.activeSpeaker[group.id].slots.push(freeSlot);
646
+ private allocateSlotsToActiveSpeakerPaneGroups() {
647
+ this.currentLayout?.activeSpeakerVideoPaneGroups
648
+ // sorting in descending order based on group priority
649
+ ?.sort((a, b) => (a.priority < b.priority ? 1 : -1))
650
+ ?.forEach((group) => {
651
+ this.receiveSlotAllocations.activeSpeaker[group.id] = {slots: []};
652
+
653
+ for (let paneIndex = 0; paneIndex < group.numPanes; paneIndex += 1) {
654
+ // allocate a slot from the "unused" list, by grabbing in same order (shift) as previous layout
655
+ const freeSlot = this.slots.video.unused.shift();
656
+
657
+ if (freeSlot) {
658
+ this.slots.video.activeSpeaker.push(freeSlot);
659
+ this.receiveSlotAllocations.activeSpeaker[group.id].slots.push(freeSlot);
660
+ }
619
661
  }
620
- }
621
- });
662
+ });
663
+ }
622
664
 
665
+ /**
666
+ * Allocates receive slots to all receiver selected video panes
667
+ * in the current selected layout
668
+ */
669
+ private allocateSlotsToReceiverSelectedVideoPaneGroups() {
623
670
  this.currentLayout?.memberVideoPanes?.forEach((memberPane) => {
624
671
  // check if there is existing slot for this csi
625
672
  const existingSlot = this.slots.video.receiverSelected.find(
@@ -646,19 +693,15 @@ export class RemoteMediaManager extends EventsScope {
646
693
  }
647
694
 
648
695
  /**
649
- * Makes sure we have the right number of receive slots created for the current layout
650
- * and allocates them to the right video panes / pane groups
651
- *
652
- * @returns {Promise}
696
+ * Ensures that we have enough slots for the current layout.
653
697
  */
654
- private async updateVideoReceiveSlots() {
698
+ private async refillRequiredSlotsIfNeeded() {
655
699
  const requiredNumSlots = this.getRequiredNumVideoSlotsForLayout(this.currentLayout);
656
700
  const totalNumSlots =
657
701
  this.slots.video.unused.length +
658
702
  this.slots.video.activeSpeaker.length +
659
703
  this.slots.video.receiverSelected.length;
660
704
 
661
- // ensure we have enough total slots for current layout
662
705
  if (totalNumSlots < requiredNumSlots) {
663
706
  let numSlotsToCreate = requiredNumSlots - totalNumSlots;
664
707
 
@@ -671,20 +714,95 @@ export class RemoteMediaManager extends EventsScope {
671
714
  numSlotsToCreate -= 1;
672
715
  }
673
716
  }
717
+ }
674
718
 
675
- // move all no longer needed receiver-selected slots to "unused"
676
- this.trimReceiverSelectedSlots();
677
-
678
- // move all active speaker slots to "unused"
719
+ /**
720
+ * Move all active speaker slots to "unused"
721
+ */
722
+ private trimActiveSpeakerSlots() {
679
723
  this.slots.video.unused.push(...this.slots.video.activeSpeaker);
680
724
  this.slots.video.activeSpeaker.length = 0;
725
+ }
681
726
 
682
- // allocate the slots to the right panes / pane groups
683
- this.allocateSlotsToVideoPaneGroups();
727
+ /**
728
+ * Logs the state of the receive slots
729
+ */
730
+ private logMainVideoReceiveSlots() {
731
+ let logMessage = '';
732
+ forEach(this.receiveSlotAllocations.activeSpeaker, (group, groupName) => {
733
+ logMessage += `\ngroup: ${groupName}\n${group.slots
734
+ .map((slot) => slot.logString)
735
+ .join(', ')}`;
736
+ });
737
+
738
+ logMessage += '\nreceiverSelected:\n';
739
+ forEach(this.receiveSlotAllocations.receiverSelected, (slot, key) => {
740
+ logMessage += ` ${key}: ${slot.logString}\n`;
741
+ });
742
+
743
+ LoggerProxy.logger.log(
744
+ `RemoteMediaManager#logMainVideoReceiveSlots --> MAIN VIDEO receive slots: unused=${this.slots.video.unused.length}, activeSpeaker=${this.slots.video.activeSpeaker.length}, receiverSelected=${this.slots.video.receiverSelected.length}${logMessage}`
745
+ );
746
+ }
747
+
748
+ /** logs main audio slots */
749
+ private logMainAudioReceiveSlots() {
750
+ LoggerProxy.logger.log(
751
+ `RemoteMediaManager#logMainAudioReceiveSlots --> MAIN AUDIO receive slots: ${this.slots.audio
752
+ .map((slot) => slot.logString)
753
+ .join(', ')}`
754
+ );
755
+ }
756
+
757
+ /** logs slides video slots */
758
+ private logSlidesVideoReceiveSlots() {
759
+ LoggerProxy.logger.log(
760
+ `RemoteMediaManager#logSlidesVideoReceiveSlots --> SLIDES VIDEO receive slot: ${this.slots.screenShare.video?.logString}`
761
+ );
762
+ }
684
763
 
764
+ /** logs slides audio slots */
765
+ private logSlidesAudioReceiveSlots() {
685
766
  LoggerProxy.logger.log(
686
- `RemoteMediaManager#updateVideoReceiveSlots --> receive slots updated: unused=${this.slots.video.unused.length}, activeSpeaker=${this.slots.video.activeSpeaker.length}, receiverSelected=${this.slots.video.receiverSelected.length}`
767
+ `RemoteMediaManager#logSlidesAudioReceiveSlots --> SLIDES AUDIO receive slots: ${this.slots.screenShare.audio
768
+ .map((slot) => slot.logString)
769
+ .join(', ')}`
687
770
  );
771
+ }
772
+
773
+ /** Logs all current receive slots */
774
+ public logAllReceiveSlots() {
775
+ this.logMainVideoReceiveSlots();
776
+ this.logMainAudioReceiveSlots();
777
+ this.logSlidesVideoReceiveSlots();
778
+ this.logSlidesAudioReceiveSlots();
779
+ }
780
+
781
+ /**
782
+ * Makes sure we have the right number of receive slots created for the current layout
783
+ * and allocates them to the right video panes / pane groups
784
+ *
785
+ * @returns {Promise}
786
+ */
787
+ private async updateVideoReceiveSlots() {
788
+ // move all active speaker slots to "unused"
789
+ this.trimActiveSpeakerSlots();
790
+
791
+ // move all no longer needed receiver-selected slots to "unused"
792
+ this.trimReceiverSelectedSlots();
793
+
794
+ // ensure we have enough total slots for current layout
795
+ await this.refillRequiredSlotsIfNeeded();
796
+
797
+ // allocate the slots to the right panes / pane groups
798
+ // reset allocations
799
+ this.receiveSlotAllocations = {activeSpeaker: {}, receiverSelected: {}};
800
+ // allocate active speaker
801
+ this.allocateSlotsToActiveSpeakerPaneGroups();
802
+ // allocate receiver selected
803
+ this.allocateSlotsToReceiverSelectedVideoPaneGroups();
804
+
805
+ this.logMainVideoReceiveSlots();
688
806
 
689
807
  // If this is the initial layout, there may be some "unused" slots left because of the preallocation
690
808
  // done in this.preallocateVideoReceiveSlots(), so release them now
@@ -7,6 +7,8 @@
7
7
  import _ from 'lodash';
8
8
 
9
9
  import LoggerProxy from '../common/logs/logger-proxy';
10
+ import MeetingUtil from '../meeting/util';
11
+
10
12
  import {ICE_GATHERING_STATE, CONNECTION_STATE, REACHABILITY} from '../constants';
11
13
 
12
14
  import ReachabilityRequest from './request';
@@ -14,6 +16,7 @@ import ReachabilityRequest from './request';
14
16
  const DEFAULT_TIMEOUT = 3000;
15
17
  const VIDEO_MESH_TIMEOUT = 1000;
16
18
 
19
+ export type ICECandidateResult = {clusterId: string; elapsed?: string | null; publicIPs?: string[]};
17
20
  /**
18
21
  * @class Reachability
19
22
  * @export
@@ -63,11 +66,15 @@ export default class Reachability {
63
66
 
64
67
  // Remove stored reachability results to ensure no stale data
65
68
  // @ts-ignore
66
- await this.webex.boundedStorage.del(this.namespace, REACHABILITY.localStorage);
69
+ await this.webex.boundedStorage.del(this.namespace, REACHABILITY.localStorageResult);
70
+ // @ts-ignore
71
+ await this.webex.boundedStorage.del(this.namespace, REACHABILITY.localStorageJoinCookie);
67
72
 
68
73
  // Fetch clusters and measure latency
69
74
  try {
70
- const clusters = await this.reachabilityRequest.getClusters();
75
+ const {clusters, joinCookie} = await this.reachabilityRequest.getClusters(
76
+ MeetingUtil.getIpVersion(this.webex)
77
+ );
71
78
 
72
79
  // Perform Reachability Check
73
80
  const results = await this.performReachabilityCheck(clusters);
@@ -75,9 +82,15 @@ export default class Reachability {
75
82
  // @ts-ignore
76
83
  await this.webex.boundedStorage.put(
77
84
  this.namespace,
78
- REACHABILITY.localStorage,
85
+ REACHABILITY.localStorageResult,
79
86
  JSON.stringify(results)
80
87
  );
88
+ // @ts-ignore
89
+ await this.webex.boundedStorage.put(
90
+ this.namespace,
91
+ REACHABILITY.localStorageJoinCookie,
92
+ JSON.stringify(joinCookie)
93
+ );
81
94
 
82
95
  LoggerProxy.logger.log(
83
96
  'Reachability:index#gatherReachability --> Reachability checks completed'
@@ -103,7 +116,7 @@ export default class Reachability {
103
116
  let reachable = false;
104
117
  // @ts-ignore
105
118
  const reachabilityData = await this.webex.boundedStorage
106
- .get(this.namespace, REACHABILITY.localStorage)
119
+ .get(this.namespace, REACHABILITY.localStorageResult)
107
120
  .catch(() => {});
108
121
 
109
122
  if (reachabilityData) {
@@ -131,7 +144,7 @@ export default class Reachability {
131
144
  * @memberof Reachability
132
145
  */
133
146
  private buildPeerConnectionConfig(cluster: any) {
134
- const iceServers = _.uniq([...cluster.udp, ...cluster.tcp]).map((url) => ({
147
+ const iceServers = _.uniq(cluster.udp).map((url) => ({
135
148
  username: '',
136
149
  credential: '',
137
150
  urls: [url],
@@ -292,6 +305,8 @@ export default class Reachability {
292
305
  `Reachability:index#onIceCandidate --> Successfully pinged ${peerConnection.key}:`,
293
306
  elapsed
294
307
  );
308
+ // order is important
309
+ this.addPublicIP(peerConnection, e.candidate.address);
295
310
  this.setLatencyAndClose(peerConnection, elapsed);
296
311
  }
297
312
  };
@@ -309,8 +324,9 @@ export default class Reachability {
309
324
  private iceGatheringState(peerConnection: RTCPeerConnection, timeout: number) {
310
325
  const ELAPSED = 'elapsed';
311
326
 
312
- return new Promise((resolve) => {
327
+ return new Promise<ICECandidateResult>((resolve) => {
313
328
  const peerConnectionProxy = new window.Proxy(peerConnection, {
329
+ // eslint-disable-next-line require-jsdoc
314
330
  get(target, property) {
315
331
  const targetMember = target[property];
316
332
 
@@ -324,7 +340,7 @@ export default class Reachability {
324
340
  // only intercept elapsed property
325
341
  if (property === ELAPSED) {
326
342
  // @ts-ignore
327
- resolve({clusterId: peerConnection.key, elapsed: value});
343
+ resolve({clusterId: peerConnection.key, publicIPs: target.publicIPs, elapsed: value});
328
344
 
329
345
  return true;
330
346
  }
@@ -345,6 +361,8 @@ export default class Reachability {
345
361
 
346
362
  // Close any open peerConnections
347
363
  if (peerConnectionProxy.connectionState !== CLOSED) {
364
+ // order is important
365
+ this.addPublicIP(peerConnectionProxy, null);
348
366
  this.setLatencyAndClose(peerConnectionProxy, null);
349
367
  }
350
368
  }, timeout);
@@ -369,29 +387,36 @@ export default class Reachability {
369
387
 
370
388
  /**
371
389
  * Calculates time to establish connection
372
- * @param {array} iceResults iceResults
390
+ * @param {Array<ICECandidateResult>} iceResults iceResults
373
391
  * @returns {object} reachabilityMap
374
- * @private
392
+ * @protected
375
393
  * @memberof Reachability
376
394
  */
377
- private parseIceResultsToReachabilityResults(iceResults: Array<any>) {
395
+ protected parseIceResultsToReachabilityResults(iceResults: Array<ICECandidateResult>) {
378
396
  const reachabilityMap = {};
379
397
 
380
- iceResults.forEach(({clusterId, elapsed}) => {
381
- let latencyResult;
398
+ iceResults.forEach(({clusterId, elapsed, publicIPs}) => {
399
+ const latencyResult = {};
382
400
 
383
- if (elapsed === null) {
384
- latencyResult = {reachable: 'false'};
401
+ if (!elapsed) {
402
+ Object.assign(latencyResult, {reachable: 'false'});
385
403
  } else {
386
- latencyResult = {
404
+ Object.assign(latencyResult, {
387
405
  reachable: 'true',
388
406
  latencyInMilliseconds: elapsed.toString(),
389
- };
407
+ });
408
+ }
409
+
410
+ if (publicIPs) {
411
+ Object.assign(latencyResult, {
412
+ clientMediaIPs: publicIPs,
413
+ });
390
414
  }
391
415
 
392
416
  reachabilityMap[clusterId] = {
393
417
  udp: latencyResult,
394
- tcp: latencyResult,
418
+ tcp: {untested: 'true'},
419
+ xtls: {untested: 'true'},
395
420
  };
396
421
  });
397
422
 
@@ -432,6 +457,33 @@ export default class Reachability {
432
457
  });
433
458
  }
434
459
 
460
+ /**
461
+ * Adds public IP (client media IPs)
462
+ * @param {RTCPeerConnection} peerConnection
463
+ * @param {string} publicIP
464
+ * @returns {void}
465
+ */
466
+ protected addPublicIP(peerConnection: RTCPeerConnection, publicIP?: string | null) {
467
+ const modifiedPeerConnection: RTCPeerConnection & {publicIPs?: string[]} = peerConnection;
468
+ const {CLOSED} = CONNECTION_STATE;
469
+
470
+ if (modifiedPeerConnection.connectionState === CLOSED) {
471
+ LoggerProxy.logger.log(
472
+ `Reachability:index#addPublicIP --> Attempting to set publicIP of ${publicIP} on closed peerConnection.`
473
+ );
474
+ }
475
+
476
+ if (publicIP) {
477
+ if (modifiedPeerConnection.publicIPs) {
478
+ modifiedPeerConnection.publicIPs.push(publicIP);
479
+ } else {
480
+ modifiedPeerConnection.publicIPs = [publicIP];
481
+ }
482
+ } else {
483
+ modifiedPeerConnection.publicIPs = null;
484
+ }
485
+ }
486
+
435
487
  /**
436
488
  * Records latency and closes the peerConnection
437
489
  * @param {RTCPeerConnection} peerConnection