@webex/plugin-meetings 3.0.0-beta.26 → 3.0.0-beta.261

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 (361) 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 +196 -28
  29. package/dist/constants.js.map +1 -1
  30. package/dist/controls-options-manager/enums.js +14 -2
  31. package/dist/controls-options-manager/enums.js.map +1 -1
  32. package/dist/controls-options-manager/index.js +109 -15
  33. package/dist/controls-options-manager/index.js.map +1 -1
  34. package/dist/controls-options-manager/types.js +7 -0
  35. package/dist/controls-options-manager/types.js.map +1 -0
  36. package/dist/controls-options-manager/util.js +309 -18
  37. package/dist/controls-options-manager/util.js.map +1 -1
  38. package/dist/index.js +112 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/interpretation/collection.js +23 -0
  41. package/dist/interpretation/collection.js.map +1 -0
  42. package/dist/interpretation/index.js +366 -0
  43. package/dist/interpretation/index.js.map +1 -0
  44. package/dist/interpretation/siLanguage.js +25 -0
  45. package/dist/interpretation/siLanguage.js.map +1 -0
  46. package/dist/locus-info/controlsUtils.js +91 -2
  47. package/dist/locus-info/controlsUtils.js.map +1 -1
  48. package/dist/locus-info/index.js +381 -62
  49. package/dist/locus-info/index.js.map +1 -1
  50. package/dist/locus-info/infoUtils.js +7 -1
  51. package/dist/locus-info/infoUtils.js.map +1 -1
  52. package/dist/locus-info/mediaSharesUtils.js +43 -1
  53. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  54. package/dist/locus-info/parser.js +224 -63
  55. package/dist/locus-info/parser.js.map +1 -1
  56. package/dist/locus-info/selfUtils.js +89 -14
  57. package/dist/locus-info/selfUtils.js.map +1 -1
  58. package/dist/media/index.js +58 -116
  59. package/dist/media/index.js.map +1 -1
  60. package/dist/media/properties.js +60 -121
  61. package/dist/media/properties.js.map +1 -1
  62. package/dist/meeting/in-meeting-actions.js +82 -2
  63. package/dist/meeting/in-meeting-actions.js.map +1 -1
  64. package/dist/meeting/index.js +3022 -2795
  65. package/dist/meeting/index.js.map +1 -1
  66. package/dist/meeting/locusMediaRequest.js +292 -0
  67. package/dist/meeting/locusMediaRequest.js.map +1 -0
  68. package/dist/meeting/muteState.js +230 -124
  69. package/dist/meeting/muteState.js.map +1 -1
  70. package/dist/meeting/request.js +256 -196
  71. package/dist/meeting/request.js.map +1 -1
  72. package/dist/meeting/util.js +601 -417
  73. package/dist/meeting/util.js.map +1 -1
  74. package/dist/meeting-info/index.js +70 -7
  75. package/dist/meeting-info/index.js.map +1 -1
  76. package/dist/meeting-info/meeting-info-v2.js +189 -51
  77. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  78. package/dist/meeting-info/util.js +1 -1
  79. package/dist/meeting-info/util.js.map +1 -1
  80. package/dist/meeting-info/utilv2.js +36 -36
  81. package/dist/meeting-info/utilv2.js.map +1 -1
  82. package/dist/meetings/collection.js +22 -0
  83. package/dist/meetings/collection.js.map +1 -1
  84. package/dist/meetings/index.js +372 -90
  85. package/dist/meetings/index.js.map +1 -1
  86. package/dist/meetings/meetings.types.js +7 -0
  87. package/dist/meetings/meetings.types.js.map +1 -0
  88. package/dist/meetings/request.js +2 -0
  89. package/dist/meetings/request.js.map +1 -1
  90. package/dist/meetings/util.js +88 -1
  91. package/dist/meetings/util.js.map +1 -1
  92. package/dist/member/index.js +49 -0
  93. package/dist/member/index.js.map +1 -1
  94. package/dist/member/types.js +25 -0
  95. package/dist/member/types.js.map +1 -0
  96. package/dist/member/util.js +121 -25
  97. package/dist/member/util.js.map +1 -1
  98. package/dist/members/collection.js +10 -0
  99. package/dist/members/collection.js.map +1 -1
  100. package/dist/members/index.js +86 -5
  101. package/dist/members/index.js.map +1 -1
  102. package/dist/members/request.js +106 -38
  103. package/dist/members/request.js.map +1 -1
  104. package/dist/members/types.js +15 -0
  105. package/dist/members/types.js.map +1 -0
  106. package/dist/members/util.js +316 -233
  107. package/dist/members/util.js.map +1 -1
  108. package/dist/metrics/constants.js +10 -5
  109. package/dist/metrics/constants.js.map +1 -1
  110. package/dist/metrics/index.js +1 -468
  111. package/dist/metrics/index.js.map +1 -1
  112. package/dist/multistream/mediaRequestManager.js +238 -49
  113. package/dist/multistream/mediaRequestManager.js.map +1 -1
  114. package/dist/multistream/receiveSlot.js +49 -16
  115. package/dist/multistream/receiveSlot.js.map +1 -1
  116. package/dist/multistream/receiveSlotManager.js +52 -34
  117. package/dist/multistream/receiveSlotManager.js.map +1 -1
  118. package/dist/multistream/remoteMedia.js +44 -18
  119. package/dist/multistream/remoteMedia.js.map +1 -1
  120. package/dist/multistream/remoteMediaGroup.js +60 -3
  121. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  122. package/dist/multistream/remoteMediaManager.js +209 -59
  123. package/dist/multistream/remoteMediaManager.js.map +1 -1
  124. package/dist/multistream/sendSlotManager.js +233 -0
  125. package/dist/multistream/sendSlotManager.js.map +1 -0
  126. package/dist/reachability/index.js +161 -57
  127. package/dist/reachability/index.js.map +1 -1
  128. package/dist/reachability/request.js +17 -8
  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 -97
  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/enums.d.ts +11 -1
  162. package/dist/types/controls-options-manager/index.d.ts +17 -1
  163. package/dist/types/controls-options-manager/types.d.ts +43 -0
  164. package/dist/types/controls-options-manager/util.d.ts +1 -7
  165. package/dist/types/index.d.ts +6 -4
  166. package/dist/types/interpretation/collection.d.ts +5 -0
  167. package/dist/types/interpretation/index.d.ts +5 -0
  168. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  169. package/dist/types/locus-info/index.d.ts +57 -4
  170. package/dist/types/locus-info/parser.d.ts +65 -6
  171. package/dist/types/media/index.d.ts +2 -0
  172. package/dist/types/media/properties.d.ts +34 -48
  173. package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
  174. package/dist/types/meeting/index.d.ts +344 -506
  175. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  176. package/dist/types/meeting/muteState.d.ts +99 -23
  177. package/dist/types/meeting/request.d.ts +72 -43
  178. package/dist/types/meeting/util.d.ts +101 -1
  179. package/dist/types/meeting-info/index.d.ts +13 -1
  180. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  181. package/dist/types/meetings/collection.d.ts +8 -0
  182. package/dist/types/meetings/index.d.ts +86 -12
  183. package/dist/types/meetings/meetings.types.d.ts +4 -0
  184. package/dist/types/member/index.d.ts +13 -0
  185. package/dist/types/member/types.d.ts +32 -0
  186. package/dist/types/members/collection.d.ts +5 -0
  187. package/dist/types/members/index.d.ts +35 -2
  188. package/dist/types/members/request.d.ts +73 -9
  189. package/dist/types/members/types.d.ts +24 -0
  190. package/dist/types/members/util.d.ts +209 -1
  191. package/dist/types/metrics/constants.d.ts +9 -4
  192. package/dist/types/metrics/index.d.ts +4 -119
  193. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  194. package/dist/types/multistream/receiveSlot.d.ts +16 -12
  195. package/dist/types/multistream/receiveSlotManager.d.ts +19 -4
  196. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  197. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  198. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  199. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  200. package/dist/types/reachability/index.d.ts +44 -7
  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 +178 -18
  226. package/src/controls-options-manager/enums.ts +12 -0
  227. package/src/controls-options-manager/index.ts +116 -21
  228. package/src/controls-options-manager/types.ts +59 -0
  229. package/src/controls-options-manager/util.ts +294 -14
  230. package/src/index.ts +40 -0
  231. package/src/interpretation/README.md +60 -0
  232. package/src/interpretation/collection.ts +19 -0
  233. package/src/interpretation/index.ts +332 -0
  234. package/src/interpretation/siLanguage.ts +18 -0
  235. package/src/locus-info/controlsUtils.ts +108 -0
  236. package/src/locus-info/index.ts +412 -59
  237. package/src/locus-info/infoUtils.ts +10 -2
  238. package/src/locus-info/mediaSharesUtils.ts +48 -0
  239. package/src/locus-info/parser.ts +231 -39
  240. package/src/locus-info/selfUtils.ts +81 -5
  241. package/src/media/index.ts +100 -122
  242. package/src/media/properties.ts +70 -108
  243. package/src/meeting/in-meeting-actions.ts +163 -3
  244. package/src/meeting/index.ts +2471 -2306
  245. package/src/meeting/locusMediaRequest.ts +313 -0
  246. package/src/meeting/muteState.ts +229 -131
  247. package/src/meeting/request.ts +172 -121
  248. package/src/meeting/util.ts +588 -394
  249. package/src/meeting-info/index.ts +79 -8
  250. package/src/meeting-info/meeting-info-v2.ts +168 -14
  251. package/src/meeting-info/util.ts +1 -1
  252. package/src/meeting-info/utilv2.ts +23 -23
  253. package/src/meetings/collection.ts +20 -0
  254. package/src/meetings/index.ts +414 -108
  255. package/src/meetings/meetings.types.ts +12 -0
  256. package/src/meetings/request.ts +2 -0
  257. package/src/meetings/util.ts +103 -4
  258. package/src/member/index.ts +49 -0
  259. package/src/member/types.ts +38 -0
  260. package/src/member/util.ts +127 -25
  261. package/src/members/collection.ts +8 -0
  262. package/src/members/index.ts +107 -6
  263. package/src/members/request.ts +97 -17
  264. package/src/members/types.ts +28 -0
  265. package/src/members/util.ts +319 -240
  266. package/src/metrics/constants.ts +9 -4
  267. package/src/metrics/index.ts +1 -490
  268. package/src/multistream/mediaRequestManager.ts +289 -79
  269. package/src/multistream/receiveSlot.ts +55 -18
  270. package/src/multistream/receiveSlotManager.ts +46 -24
  271. package/src/multistream/remoteMedia.ts +27 -2
  272. package/src/multistream/remoteMediaGroup.ts +59 -0
  273. package/src/multistream/remoteMediaManager.ts +148 -30
  274. package/src/multistream/sendSlotManager.ts +170 -0
  275. package/src/reachability/index.ts +165 -37
  276. package/src/reachability/request.ts +17 -8
  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 +100 -104
  282. package/src/roap/turnDiscovery.ts +51 -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 +320 -261
  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 +163 -0
  299. package/test/unit/spec/controls-options-manager/util.js +576 -60
  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 +104 -37
  312. package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
  313. package/test/unit/spec/meeting/index.js +4095 -1913
  314. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  315. package/test/unit/spec/meeting/muteState.js +408 -208
  316. package/test/unit/spec/meeting/request.js +440 -45
  317. package/test/unit/spec/meeting/utils.js +679 -64
  318. package/test/unit/spec/meeting-info/index.js +293 -0
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +517 -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 +941 -151
  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/multistream/sendSlotManager.ts +242 -0
  337. package/test/unit/spec/reachability/index.ts +343 -9
  338. package/test/unit/spec/reachability/request.js +68 -0
  339. package/test/unit/spec/reconnection-manager/index.js +84 -9
  340. package/test/unit/spec/recording-controller/index.js +294 -218
  341. package/test/unit/spec/recording-controller/util.js +223 -96
  342. package/test/unit/spec/roap/index.ts +31 -51
  343. package/test/unit/spec/roap/request.ts +203 -85
  344. package/test/unit/spec/roap/turnDiscovery.ts +48 -13
  345. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  346. package/test/unit/spec/stats-analyzer/index.js +29 -2
  347. package/test/utils/constants.js +9 -0
  348. package/test/utils/integrationTestUtils.js +46 -0
  349. package/test/utils/testUtils.js +0 -45
  350. package/test/utils/webex-config.js +4 -0
  351. package/test/utils/webex-test-users.js +6 -3
  352. package/dist/meeting/effectsState.js +0 -262
  353. package/dist/meeting/effectsState.js.map +0 -1
  354. package/dist/metrics/config.js +0 -299
  355. package/dist/metrics/config.js.map +0 -1
  356. package/dist/types/meeting/effectsState.d.ts +0 -42
  357. package/dist/types/metrics/config.d.ts +0 -178
  358. package/src/index.js +0 -16
  359. package/src/meeting/effectsState.ts +0 -211
  360. package/src/metrics/config.ts +0 -495
  361. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,17 +1,19 @@
1
1
  /* eslint-disable require-jsdoc */
2
2
  import {
3
- MediaRequest as WcmeMediaRequest,
3
+ StreamRequest,
4
4
  Policy,
5
5
  ActiveSpeakerInfo,
6
6
  ReceiverSelectedInfo,
7
7
  CodecInfo as WcmeCodecInfo,
8
8
  H264Codec,
9
+ getRecommendedMaxBitrateForFrameSize,
10
+ RecommendedOpusBitrates,
9
11
  } from '@webex/internal-media-core';
10
- import {cloneDeep} from 'lodash';
12
+ import {cloneDeepWith, debounce, isEmpty} from 'lodash';
11
13
 
12
14
  import LoggerProxy from '../common/logs/logger-proxy';
13
15
 
14
- import {ReceiveSlot, ReceiveSlotId} from './receiveSlot';
16
+ import {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';
15
17
  import {getMaxFs} from './remoteMedia';
16
18
 
17
19
  export interface ActiveSpeakerPolicyInfo {
@@ -44,6 +46,8 @@ export interface MediaRequest {
44
46
  policyInfo: PolicyInfo;
45
47
  receiveSlots: Array<ReceiveSlot>;
46
48
  codecInfo?: CodecInfo;
49
+ preferredMaxFs?: number;
50
+ handleMaxFs?: ({maxFs}: {maxFs: number}) => void;
47
51
  }
48
52
 
49
53
  export type MediaRequestId = string;
@@ -56,57 +60,58 @@ const CODEC_DEFAULTS = {
56
60
  },
57
61
  };
58
62
 
63
+ const DEBOUNCED_SOURCE_UPDATE_TIME = 1000;
64
+
59
65
  type DegradationPreferences = {
60
66
  maxMacroblocksLimit: number;
61
67
  };
62
68
 
63
- type SendMediaRequestsCallback = (mediaRequests: WcmeMediaRequest[]) => void;
69
+ type SendMediaRequestsCallback = (streamRequests: StreamRequest[]) => void;
70
+ type Kind = 'audio' | 'video';
71
+
72
+ type Options = {
73
+ degradationPreferences: DegradationPreferences;
74
+ kind: Kind;
75
+ trimRequestsToNumOfSources: boolean; // if enabled, AS speaker requests will be trimmed based on the calls to setNumCurrentSources()
76
+ };
77
+
78
+ type ClientRequestsMap = {[key: MediaRequestId]: MediaRequest};
64
79
 
65
80
  export class MediaRequestManager {
66
81
  private sendMediaRequestsCallback: SendMediaRequestsCallback;
67
82
 
68
- private counter: number;
83
+ private kind: Kind;
69
84
 
70
- private clientRequests: {[key: MediaRequestId]: MediaRequest};
85
+ private counter: number;
71
86
 
72
- private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};
87
+ private clientRequests: ClientRequestsMap;
73
88
 
74
89
  private degradationPreferences: DegradationPreferences;
75
90
 
76
- constructor(
77
- degradationPreferences: DegradationPreferences,
78
- sendMediaRequestsCallback: SendMediaRequestsCallback
79
- ) {
80
- this.sendMediaRequestsCallback = sendMediaRequestsCallback;
81
- this.counter = 0;
82
- this.clientRequests = {};
83
- this.slotsActiveInLastMediaRequest = {};
84
- this.degradationPreferences = degradationPreferences;
85
- }
91
+ private sourceUpdateListener: () => void;
86
92
 
87
- private resetInactiveReceiveSlots() {
88
- const activeSlots: {[key: ReceiveSlotId]: ReceiveSlot} = {};
93
+ private debouncedSourceUpdateListener: () => void;
89
94
 
90
- // create a map of all currently used slot ids
91
- Object.values(this.clientRequests).forEach((request) =>
92
- request.receiveSlots.forEach((slot) => {
93
- activeSlots[slot.id] = slot;
94
- })
95
- );
95
+ private previousStreamRequests: Array<StreamRequest> = [];
96
96
 
97
- // when we stop using some receive slots and they are not included in the new media request,
98
- // we will never get a 'no source' notification for them, so we reset their state,
99
- // so that the client doesn't try to display their video anymore
100
- for (const [slotId, slot] of Object.entries(this.slotsActiveInLastMediaRequest)) {
101
- if (!(slotId in activeSlots)) {
102
- LoggerProxy.logger.info(
103
- `multistream:mediaRequestManager --> resetting sourceState to "no source" for slot ${slot.id}`
104
- );
105
- slot.resetSourceState();
106
- }
107
- }
97
+ private trimRequestsToNumOfSources: boolean;
98
+ private numTotalSources: number;
99
+ private numLiveSources: number;
108
100
 
109
- this.slotsActiveInLastMediaRequest = activeSlots;
101
+ constructor(sendMediaRequestsCallback: SendMediaRequestsCallback, options: Options) {
102
+ this.sendMediaRequestsCallback = sendMediaRequestsCallback;
103
+ this.counter = 0;
104
+ this.numLiveSources = 0;
105
+ this.numTotalSources = 0;
106
+ this.clientRequests = {};
107
+ this.degradationPreferences = options.degradationPreferences;
108
+ this.kind = options.kind;
109
+ this.trimRequestsToNumOfSources = options.trimRequestsToNumOfSources;
110
+ this.sourceUpdateListener = this.commit.bind(this);
111
+ this.debouncedSourceUpdateListener = debounce(
112
+ this.sourceUpdateListener,
113
+ DEBOUNCED_SOURCE_UPDATE_TIME
114
+ );
110
115
  }
111
116
 
112
117
  public setDegradationPreferences(degradationPreferences: DegradationPreferences) {
@@ -114,8 +119,7 @@ export class MediaRequestManager {
114
119
  this.sendRequests(); // re-send requests after preferences are set
115
120
  }
116
121
 
117
- private getDegradedClientRequests() {
118
- const clientRequests = cloneDeep(this.clientRequests);
122
+ private getDegradedClientRequests(clientRequests: ClientRequestsMap) {
119
123
  const maxFsLimits = [
120
124
  getMaxFs('best'),
121
125
  getMaxFs('large'),
@@ -131,16 +135,19 @@ export class MediaRequestManager {
131
135
  Object.values(clientRequests).forEach((mr) => {
132
136
  if (mr.codecInfo) {
133
137
  mr.codecInfo.maxFs = Math.min(
138
+ mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs,
134
139
  mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
135
140
  maxFsLimits[i]
136
141
  );
137
- totalMacroblocksRequested += mr.codecInfo.maxFs * mr.receiveSlots.length;
142
+ // we only consider sources with "live" state
143
+ const slotsWithLiveSource = mr.receiveSlots.filter((rs) => rs.sourceState === 'live');
144
+ totalMacroblocksRequested += mr.codecInfo.maxFs * slotsWithLiveSource.length;
138
145
  }
139
146
  });
140
147
  if (totalMacroblocksRequested <= this.degradationPreferences.maxMacroblocksLimit) {
141
148
  if (i !== 0) {
142
149
  LoggerProxy.logger.warn(
143
- `multistream:mediaRequestManager --> too many requests with high max-fs, frame size will be limited to ${maxFsLimits[i]}`
150
+ `multistream:mediaRequestManager --> too many streams with high max-fs, frame size will be limited to ${maxFsLimits[i]}`
144
151
  );
145
152
  }
146
153
  break;
@@ -150,52 +157,229 @@ export class MediaRequestManager {
150
157
  );
151
158
  }
152
159
  }
160
+ }
161
+
162
+ /**
163
+ * Returns true if two stream requests are the same, false otherwise.
164
+ *
165
+ * @param {StreamRequest} streamRequestA - Stream request A for comparison.
166
+ * @param {StreamRequest} streamRequestB - Stream request B for comparison.
167
+ * @returns {boolean} - Whether they are equal.
168
+ */
169
+ // eslint-disable-next-line class-methods-use-this
170
+ public isEqual(streamRequestA: StreamRequest, streamRequestB: StreamRequest) {
171
+ return (
172
+ JSON.stringify(streamRequestA._toJmpStreamRequest()) ===
173
+ JSON.stringify(streamRequestB._toJmpStreamRequest())
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Compares new stream requests to previous ones and determines
179
+ * if they are the same.
180
+ *
181
+ * @param {StreamRequest[]} newRequests - Array with new requests.
182
+ * @returns {boolean} - True if they are equal, false otherwise.
183
+ */
184
+ private checkIsNewRequestsEqualToPrev(newRequests: StreamRequest[]) {
185
+ return (
186
+ !isEmpty(this.previousStreamRequests) &&
187
+ this.previousStreamRequests.length === newRequests.length &&
188
+ this.previousStreamRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Returns the maxPayloadBitsPerSecond per Stream
194
+ *
195
+ * If MediaRequestManager kind is "audio", a constant bitrate will be returned.
196
+ * If MediaRequestManager kind is "video", the bitrate will be calculated based
197
+ * on maxFs (default h264 maxFs as fallback if maxFs is not defined)
198
+ *
199
+ * @param {MediaRequest} mediaRequest - mediaRequest to take data from
200
+ * @returns {number} maxPayloadBitsPerSecond
201
+ */
202
+ private getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number {
203
+ if (this.kind === 'audio') {
204
+ // return mono_music bitrate default if the kind of mediarequest manager is audio:
205
+ return RecommendedOpusBitrates.FB_MONO_MUSIC;
206
+ }
207
+
208
+ return getRecommendedMaxBitrateForFrameSize(
209
+ mediaRequest.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs
210
+ );
211
+ }
153
212
 
154
- return clientRequests;
213
+ /**
214
+ * Returns the max Macro Blocks per second (maxMbps) per H264 Stream
215
+ *
216
+ * The maxMbps will be calculated based on maxFs and maxFps
217
+ * (default h264 maxFps as fallback if maxFps is not defined)
218
+ *
219
+ * @param {MediaRequest} mediaRequest - mediaRequest to take data from
220
+ * @returns {number} maxMbps
221
+ */
222
+ // eslint-disable-next-line class-methods-use-this
223
+ private getH264MaxMbps(mediaRequest: MediaRequest): number {
224
+ // fallback for maxFps (not needed for maxFs, since there is a fallback already in getDegradedClientRequests)
225
+ const maxFps = mediaRequest.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps;
226
+
227
+ // divided by 100 since maxFps is 3000 (for 30 frames per seconds)
228
+ return (mediaRequest.codecInfo.maxFs * maxFps) / 100;
229
+ }
230
+
231
+ /**
232
+ * Clears the previous stream requests.
233
+ *
234
+ * @returns {void}
235
+ */
236
+ public clearPreviousRequests(): void {
237
+ this.previousStreamRequests = [];
238
+ }
239
+
240
+ /** Modifies the passed in clientRequests and makes sure that in total they don't ask
241
+ * for more streams than there are available.
242
+ *
243
+ * @param {Object} clientRequests
244
+ * @returns {void}
245
+ */
246
+ private trimRequests(clientRequests: ClientRequestsMap) {
247
+ const preferLiveVideo = this.getPreferLiveVideo();
248
+
249
+ if (!this.trimRequestsToNumOfSources) {
250
+ return;
251
+ }
252
+
253
+ // preferLiveVideo being undefined means that there are no active-speaker requests so we don't need to do any trimming
254
+ if (preferLiveVideo === undefined) {
255
+ return;
256
+ }
257
+
258
+ let numStreamsAvailable = preferLiveVideo ? this.numLiveSources : this.numTotalSources;
259
+
260
+ Object.values(clientRequests)
261
+ .sort((a, b) => {
262
+ // we have to count how many streams we're asking for
263
+ // and should not ask for more than numStreamsAvailable in total,
264
+ // so we might need to trim active-speaker requests and first ones to trim should be
265
+ // the ones with lowest priority
266
+
267
+ // receiver-selected requests have priority over active-speakers
268
+ if (a.policyInfo.policy === 'receiver-selected') {
269
+ return -1;
270
+ }
271
+ if (b.policyInfo.policy === 'receiver-selected') {
272
+ return 1;
273
+ }
274
+
275
+ // and active-speakers are sorted by descending priority
276
+ return b.policyInfo.priority - a.policyInfo.priority;
277
+ })
278
+ .forEach((request) => {
279
+ // we only trim active-speaker requests
280
+ if (request.policyInfo.policy === 'active-speaker') {
281
+ const trimmedCount = Math.min(numStreamsAvailable, request.receiveSlots.length);
282
+
283
+ request.receiveSlots.length = trimmedCount;
284
+
285
+ numStreamsAvailable -= trimmedCount;
286
+ } else {
287
+ numStreamsAvailable -= request.receiveSlots.length;
288
+ }
289
+
290
+ if (numStreamsAvailable < 0) {
291
+ numStreamsAvailable = 0;
292
+ }
293
+ });
294
+ }
295
+
296
+ private getPreferLiveVideo(): boolean | undefined {
297
+ let preferLiveVideo;
298
+
299
+ Object.values(this.clientRequests).forEach((mr) => {
300
+ if (mr.policyInfo.policy === 'active-speaker') {
301
+ // take the value from first encountered active speaker request
302
+ if (preferLiveVideo === undefined) {
303
+ preferLiveVideo = mr.policyInfo.preferLiveVideo;
304
+ }
305
+
306
+ if (mr.policyInfo.preferLiveVideo !== preferLiveVideo) {
307
+ throw new Error(
308
+ 'a mix of active-speaker groups with different values for preferLiveVideo is not supported'
309
+ );
310
+ }
311
+ }
312
+ });
313
+
314
+ return preferLiveVideo;
315
+ }
316
+
317
+ private cloneClientRequests(): ClientRequestsMap {
318
+ // we clone the client requests but without cloning the ReceiveSlots that they reference
319
+ return cloneDeepWith(this.clientRequests, (value, key) => {
320
+ if (key === 'receiveSlots') {
321
+ return [...value];
322
+ }
323
+
324
+ return undefined;
325
+ });
155
326
  }
156
327
 
157
328
  private sendRequests() {
158
- const wcmeMediaRequests: WcmeMediaRequest[] = [];
329
+ const streamRequests: StreamRequest[] = [];
330
+
331
+ // clone the requests so that any modifications we do to them don't affect the original ones
332
+ const clientRequests = this.cloneClientRequests();
159
333
 
160
- const clientRequests = this.getDegradedClientRequests();
161
- const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
334
+ this.trimRequests(clientRequests);
335
+ this.getDegradedClientRequests(clientRequests);
162
336
 
163
- // map all the client media requests to wcme media requests
337
+ // map all the client media requests to wcme stream requests
164
338
  Object.values(clientRequests).forEach((mr) => {
165
- wcmeMediaRequests.push(
166
- new WcmeMediaRequest(
167
- mr.policyInfo.policy === 'active-speaker'
168
- ? Policy.ActiveSpeaker
169
- : Policy.ReceiverSelected,
170
- mr.policyInfo.policy === 'active-speaker'
171
- ? new ActiveSpeakerInfo(
172
- mr.policyInfo.priority,
173
- mr.policyInfo.crossPriorityDuplication,
174
- mr.policyInfo.crossPolicyDuplication,
175
- mr.policyInfo.preferLiveVideo
176
- )
177
- : new ReceiverSelectedInfo(mr.policyInfo.csi),
178
- mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
179
- maxPayloadBitsPerSecond,
180
- mr.codecInfo && [
181
- new WcmeCodecInfo(
182
- 0x80,
183
- new H264Codec(
184
- mr.codecInfo.maxFs,
185
- mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
186
- mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,
187
- mr.codecInfo.maxWidth,
188
- mr.codecInfo.maxHeight
189
- )
190
- ),
191
- ]
192
- )
193
- );
339
+ if (mr.receiveSlots.length > 0) {
340
+ streamRequests.push(
341
+ new StreamRequest(
342
+ mr.policyInfo.policy === 'active-speaker'
343
+ ? Policy.ActiveSpeaker
344
+ : Policy.ReceiverSelected,
345
+ mr.policyInfo.policy === 'active-speaker'
346
+ ? new ActiveSpeakerInfo(
347
+ mr.policyInfo.priority,
348
+ mr.policyInfo.crossPriorityDuplication,
349
+ mr.policyInfo.crossPolicyDuplication,
350
+ mr.policyInfo.preferLiveVideo
351
+ )
352
+ : new ReceiverSelectedInfo(mr.policyInfo.csi),
353
+ mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
354
+ this.getMaxPayloadBitsPerSecond(mr),
355
+ mr.codecInfo && [
356
+ new WcmeCodecInfo(
357
+ 0x80,
358
+ new H264Codec(
359
+ mr.codecInfo.maxFs,
360
+ mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
361
+ this.getH264MaxMbps(mr),
362
+ mr.codecInfo.maxWidth,
363
+ mr.codecInfo.maxHeight
364
+ )
365
+ ),
366
+ ]
367
+ )
368
+ );
369
+ }
194
370
  });
195
371
 
196
- this.sendMediaRequestsCallback(wcmeMediaRequests);
197
-
198
- this.resetInactiveReceiveSlots();
372
+ //! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/json-multistream)
373
+ // https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713
374
+ if (!this.checkIsNewRequestsEqualToPrev(streamRequests)) {
375
+ this.sendMediaRequestsCallback(streamRequests);
376
+ this.previousStreamRequests = streamRequests;
377
+ LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);
378
+ } else {
379
+ LoggerProxy.logger.info(
380
+ `multistream:sendRequests --> detected duplicate WCME requests, skipping them... `
381
+ );
382
+ }
199
383
  }
200
384
 
201
385
  public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
@@ -204,6 +388,17 @@ export class MediaRequestManager {
204
388
 
205
389
  this.clientRequests[newId] = mediaRequest;
206
390
 
391
+ const eventHandler = ({maxFs}) => {
392
+ mediaRequest.preferredMaxFs = maxFs;
393
+ this.debouncedSourceUpdateListener();
394
+ };
395
+ mediaRequest.handleMaxFs = eventHandler;
396
+
397
+ mediaRequest.receiveSlots.forEach((rs) => {
398
+ rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);
399
+ rs.on(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);
400
+ });
401
+
207
402
  if (commit) {
208
403
  this.commit();
209
404
  }
@@ -212,6 +407,13 @@ export class MediaRequestManager {
212
407
  }
213
408
 
214
409
  public cancelRequest(requestId: MediaRequestId, commit = true) {
410
+ const mediaRequest = this.clientRequests[requestId];
411
+
412
+ mediaRequest?.receiveSlots.forEach((rs) => {
413
+ rs.off(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);
414
+ rs.off(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);
415
+ });
416
+
215
417
  delete this.clientRequests[requestId];
216
418
 
217
419
  if (commit) {
@@ -225,6 +427,14 @@ export class MediaRequestManager {
225
427
 
226
428
  public reset() {
227
429
  this.clientRequests = {};
228
- this.slotsActiveInLastMediaRequest = {};
430
+ this.numTotalSources = 0;
431
+ this.numLiveSources = 0;
432
+ }
433
+
434
+ public setNumCurrentSources(numTotalSources: number, numLiveSources: number) {
435
+ this.numTotalSources = numTotalSources;
436
+ this.numLiveSources = numLiveSources;
437
+
438
+ this.sendRequests();
229
439
  }
230
440
  }
@@ -3,7 +3,7 @@ import {
3
3
  MediaType,
4
4
  ReceiveSlot as WcmeReceiveSlot,
5
5
  ReceiveSlotEvents as WcmeReceiveSlotEvents,
6
- SourceState,
6
+ StreamState,
7
7
  } from '@webex/internal-media-core';
8
8
 
9
9
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -11,9 +11,10 @@ import EventsScope from '../common/events/events-scope';
11
11
 
12
12
  export const ReceiveSlotEvents = {
13
13
  SourceUpdate: 'sourceUpdate',
14
+ MaxFsUpdate: 'maxFsUpdate',
14
15
  };
15
16
 
16
- export type {SourceState} from '@webex/internal-media-core';
17
+ export type {StreamState} from '@webex/internal-media-core';
17
18
  export type CSI = number;
18
19
  export type MemberId = string;
19
20
  export type ReceiveSlotId = string;
@@ -39,7 +40,7 @@ export class ReceiveSlot extends EventsScope {
39
40
 
40
41
  #csi?: CSI;
41
42
 
42
- #sourceState: SourceState;
43
+ #sourceState: StreamState;
43
44
 
44
45
  /**
45
46
  * constructor - don't use it directly, you should always use meeting.receiveSlotManager.allocateSlot()
@@ -81,6 +82,25 @@ export class ReceiveSlot extends EventsScope {
81
82
  return this.#csi;
82
83
  }
83
84
 
85
+ /**
86
+ * Set the max frame size for this slot
87
+ * @param newFs frame size
88
+ */
89
+ public setMaxFs(newFs) {
90
+ // emit event for media request manager to listen to
91
+
92
+ this.emit(
93
+ {
94
+ file: 'meeting/receiveSlot',
95
+ function: 'findMemberId',
96
+ },
97
+ ReceiveSlotEvents.MaxFsUpdate,
98
+ {
99
+ maxFs: newFs,
100
+ }
101
+ );
102
+ }
103
+
84
104
  /**
85
105
  * Getter for sourceState
86
106
  */
@@ -91,7 +111,7 @@ export class ReceiveSlot extends EventsScope {
91
111
  /**
92
112
  * registers event handlers with the underlying ReceiveSlot
93
113
  */
94
- setupEventListeners() {
114
+ private setupEventListeners() {
95
115
  const scope = {
96
116
  file: 'meeting/receiveSlot',
97
117
  function: 'setupEventListeners',
@@ -99,7 +119,7 @@ export class ReceiveSlot extends EventsScope {
99
119
 
100
120
  this.mcReceiveSlot.on(
101
121
  WcmeReceiveSlotEvents.SourceUpdate,
102
- (state: SourceState, csi?: number) => {
122
+ (state: StreamState, csi?: number) => {
103
123
  LoggerProxy.logger.log(
104
124
  `ReceiveSlot#setupEventListeners --> got source update on receive slot ${this.id}, mediaType=${this.mediaType}, csi=${csi}, state=${state}`
105
125
  );
@@ -116,6 +136,36 @@ export class ReceiveSlot extends EventsScope {
116
136
  );
117
137
  }
118
138
 
139
+ /** Tries to find the member id for this receive slot if it hasn't got one */
140
+ public findMemberId() {
141
+ if (this.#memberId === undefined && this.#csi) {
142
+ this.#memberId = this.findMemberIdCallback(this.#csi);
143
+
144
+ if (this.#memberId) {
145
+ // if we found the memberId, simulate source update so that the client app knows that something's changed
146
+ this.emit(
147
+ {
148
+ file: 'meeting/receiveSlot',
149
+ function: 'findMemberId',
150
+ },
151
+ ReceiveSlotEvents.SourceUpdate,
152
+ {
153
+ state: this.#sourceState,
154
+ csi: this.#csi,
155
+ memberId: this.#memberId,
156
+ }
157
+ );
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @returns {string} a log message used to identify the receive slot
164
+ */
165
+ public get logString() {
166
+ return `ReceiveSlot - ${this.id}: ${JSON.stringify(this.mcReceiveSlot.id)}`;
167
+ }
168
+
119
169
  /**
120
170
  * The MediaStream object associated with this slot.
121
171
  *
@@ -131,17 +181,4 @@ export class ReceiveSlot extends EventsScope {
131
181
  get wcmeReceiveSlot(): WcmeReceiveSlot {
132
182
  return this.mcReceiveSlot;
133
183
  }
134
-
135
- /**
136
- * Resets the source state to the default 'no source' value.
137
- * This function should be called on receive slots that are
138
- * no longer part of a media request. It's needed because WCME
139
- * does not send any more events on such slots, so the sourceState
140
- * value would not represent the truth anymore.
141
- */
142
- public resetSourceState() {
143
- this.#sourceState = 'no source';
144
- this.#csi = undefined;
145
- this.#memberId = undefined;
146
- }
147
184
  }