@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.11

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 (281) hide show
  1. package/dist/common/browser-detection.js.map +1 -1
  2. package/dist/common/collection.js.map +1 -1
  3. package/dist/common/config.js.map +1 -1
  4. package/dist/common/errors/captcha-error.js +7 -0
  5. package/dist/common/errors/captcha-error.js.map +1 -1
  6. package/dist/common/errors/intent-to-join.js +8 -0
  7. package/dist/common/errors/intent-to-join.js.map +1 -1
  8. package/dist/common/errors/join-meeting.js +8 -0
  9. package/dist/common/errors/join-meeting.js.map +1 -1
  10. package/dist/common/errors/media.js +7 -0
  11. package/dist/common/errors/media.js.map +1 -1
  12. package/dist/common/errors/parameter.js.map +1 -1
  13. package/dist/common/errors/password-error.js +7 -0
  14. package/dist/common/errors/password-error.js.map +1 -1
  15. package/dist/common/errors/permission.js +7 -0
  16. package/dist/common/errors/permission.js.map +1 -1
  17. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  18. package/dist/common/errors/reconnection.js +7 -0
  19. package/dist/common/errors/reconnection.js.map +1 -1
  20. package/dist/common/errors/stats.js +7 -0
  21. package/dist/common/errors/stats.js.map +1 -1
  22. package/dist/common/errors/webex-errors.js +5 -29
  23. package/dist/common/errors/webex-errors.js.map +1 -1
  24. package/dist/common/errors/webex-meetings-error.js +5 -2
  25. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  26. package/dist/common/events/events-scope.js.map +1 -1
  27. package/dist/common/events/events.js.map +1 -1
  28. package/dist/common/events/trigger-proxy.js.map +1 -1
  29. package/dist/common/events/util.js.map +1 -1
  30. package/dist/common/logs/logger-config.js.map +1 -1
  31. package/dist/common/logs/logger-proxy.js.map +1 -1
  32. package/dist/common/logs/request.js +3 -0
  33. package/dist/common/logs/request.js.map +1 -1
  34. package/dist/common/queue.js.map +1 -1
  35. package/dist/config.js +1 -0
  36. package/dist/config.js.map +1 -1
  37. package/dist/constants.js +15 -74
  38. package/dist/constants.js.map +1 -1
  39. package/dist/locus-info/controlsUtils.js.map +1 -1
  40. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  41. package/dist/locus-info/fullState.js.map +1 -1
  42. package/dist/locus-info/hostUtils.js.map +1 -1
  43. package/dist/locus-info/index.js +43 -5
  44. package/dist/locus-info/index.js.map +1 -1
  45. package/dist/locus-info/infoUtils.js +4 -0
  46. package/dist/locus-info/infoUtils.js.map +1 -1
  47. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  48. package/dist/locus-info/parser.js +12 -3
  49. package/dist/locus-info/parser.js.map +1 -1
  50. package/dist/locus-info/selfUtils.js.map +1 -1
  51. package/dist/media/index.js +71 -210
  52. package/dist/media/index.js.map +1 -1
  53. package/dist/media/internal-media-core-wrapper.js +22 -0
  54. package/dist/media/internal-media-core-wrapper.js.map +1 -0
  55. package/dist/media/properties.js +32 -25
  56. package/dist/media/properties.js.map +1 -1
  57. package/dist/media/util.js +0 -27
  58. package/dist/media/util.js.map +1 -1
  59. package/dist/mediaQualityMetrics/config.js.map +1 -1
  60. package/dist/meeting/effectsState.js +8 -1
  61. package/dist/meeting/effectsState.js.map +1 -1
  62. package/dist/meeting/index.js +1146 -602
  63. package/dist/meeting/index.js.map +1 -1
  64. package/dist/meeting/muteState.js +6 -0
  65. package/dist/meeting/muteState.js.map +1 -1
  66. package/dist/meeting/request.js +83 -24
  67. package/dist/meeting/request.js.map +1 -1
  68. package/dist/meeting/state.js.map +1 -1
  69. package/dist/meeting/util.js +5 -44
  70. package/dist/meeting/util.js.map +1 -1
  71. package/dist/meeting-info/collection.js +4 -1
  72. package/dist/meeting-info/collection.js.map +1 -1
  73. package/dist/meeting-info/index.js +5 -0
  74. package/dist/meeting-info/index.js.map +1 -1
  75. package/dist/meeting-info/meeting-info-v2.js +14 -2
  76. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  77. package/dist/meeting-info/request.js +3 -0
  78. package/dist/meeting-info/request.js.map +1 -1
  79. package/dist/meeting-info/util.js.map +1 -1
  80. package/dist/meeting-info/utilv2.js.map +1 -1
  81. package/dist/meetings/collection.js +4 -1
  82. package/dist/meetings/collection.js.map +1 -1
  83. package/dist/meetings/index.js +136 -25
  84. package/dist/meetings/index.js.map +1 -1
  85. package/dist/meetings/request.js +4 -0
  86. package/dist/meetings/request.js.map +1 -1
  87. package/dist/meetings/util.js +24 -1
  88. package/dist/meetings/util.js.map +1 -1
  89. package/dist/member/index.js +30 -7
  90. package/dist/member/index.js.map +1 -1
  91. package/dist/member/util.js +2 -1
  92. package/dist/member/util.js.map +1 -1
  93. package/dist/members/collection.js +1 -0
  94. package/dist/members/collection.js.map +1 -1
  95. package/dist/members/index.js +82 -1
  96. package/dist/members/index.js.map +1 -1
  97. package/dist/members/request.js +19 -9
  98. package/dist/members/request.js.map +1 -1
  99. package/dist/members/util.js.map +1 -1
  100. package/dist/metrics/config.js.map +1 -1
  101. package/dist/metrics/constants.js.map +1 -1
  102. package/dist/metrics/index.js +8 -0
  103. package/dist/metrics/index.js.map +1 -1
  104. package/dist/multistream/mediaRequestManager.js +133 -0
  105. package/dist/multistream/mediaRequestManager.js.map +1 -0
  106. package/dist/multistream/multistreamMedia.js +116 -0
  107. package/dist/multistream/multistreamMedia.js.map +1 -0
  108. package/dist/multistream/receiveSlot.js +209 -0
  109. package/dist/multistream/receiveSlot.js.map +1 -0
  110. package/dist/multistream/receiveSlotManager.js +195 -0
  111. package/dist/multistream/receiveSlotManager.js.map +1 -0
  112. package/dist/multistream/remoteMedia.js +289 -0
  113. package/dist/multistream/remoteMedia.js.map +1 -0
  114. package/dist/multistream/remoteMediaGroup.js +243 -0
  115. package/dist/multistream/remoteMediaGroup.js.map +1 -0
  116. package/dist/multistream/remoteMediaManager.js +1113 -0
  117. package/dist/multistream/remoteMediaManager.js.map +1 -0
  118. package/dist/networkQualityMonitor/index.js +10 -2
  119. package/dist/networkQualityMonitor/index.js.map +1 -1
  120. package/dist/personal-meeting-room/index.js +11 -0
  121. package/dist/personal-meeting-room/index.js.map +1 -1
  122. package/dist/personal-meeting-room/request.js +2 -1
  123. package/dist/personal-meeting-room/request.js.map +1 -1
  124. package/dist/personal-meeting-room/util.js.map +1 -1
  125. package/dist/reachability/index.js +17 -7
  126. package/dist/reachability/index.js.map +1 -1
  127. package/dist/reachability/request.js +1 -0
  128. package/dist/reachability/request.js.map +1 -1
  129. package/dist/reactions/reactions.js +111 -0
  130. package/dist/reactions/reactions.js.map +1 -0
  131. package/dist/reactions/reactions.type.js +40 -0
  132. package/dist/reactions/reactions.type.js.map +1 -0
  133. package/dist/reconnection-manager/index.js +130 -132
  134. package/dist/reconnection-manager/index.js.map +1 -1
  135. package/dist/roap/index.js +58 -231
  136. package/dist/roap/index.js.map +1 -1
  137. package/dist/roap/request.js +7 -116
  138. package/dist/roap/request.js.map +1 -1
  139. package/dist/roap/turnDiscovery.js +20 -6
  140. package/dist/roap/turnDiscovery.js.map +1 -1
  141. package/dist/statsAnalyzer/global.js +2 -0
  142. package/dist/statsAnalyzer/global.js.map +1 -1
  143. package/dist/statsAnalyzer/index.js +58 -37
  144. package/dist/statsAnalyzer/index.js.map +1 -1
  145. package/dist/statsAnalyzer/mqaUtil.js +9 -3
  146. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  147. package/dist/transcription/index.js +10 -3
  148. package/dist/transcription/index.js.map +1 -1
  149. package/package.json +21 -20
  150. package/src/common/{browser-detection.js → browser-detection.ts} +1 -1
  151. package/src/common/collection.ts +6 -6
  152. package/src/common/{config.js → config.ts} +1 -1
  153. package/src/common/errors/{captcha-error.js → captcha-error.ts} +5 -1
  154. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +6 -1
  155. package/src/common/errors/{join-meeting.js → join-meeting.ts} +6 -1
  156. package/src/common/errors/{media.js → media.ts} +5 -1
  157. package/src/common/errors/parameter.ts +3 -2
  158. package/src/common/errors/{password-error.js → password-error.ts} +5 -1
  159. package/src/common/errors/{permission.js → permission.ts} +5 -1
  160. package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
  161. package/src/common/errors/{reconnection.js → reconnection.ts} +5 -1
  162. package/src/common/errors/{stats.js → stats.ts} +5 -1
  163. package/src/common/errors/{webex-errors.js → webex-errors.ts} +1 -20
  164. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +3 -1
  165. package/src/common/events/{events-scope.js → events-scope.ts} +1 -1
  166. package/src/common/events/{events.js → events.ts} +0 -0
  167. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +1 -2
  168. package/src/common/events/{util.js → util.ts} +1 -1
  169. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  170. package/src/common/logs/{logger-proxy.js → logger-proxy.ts} +1 -1
  171. package/src/common/logs/{request.js → request.ts} +12 -2
  172. package/src/common/queue.ts +1 -2
  173. package/src/{config.js → config.ts} +2 -0
  174. package/src/constants.ts +139 -179
  175. package/src/locus-info/{controlsUtils.js → controlsUtils.ts} +4 -4
  176. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  177. package/src/locus-info/{fullState.js → fullState.ts} +1 -1
  178. package/src/locus-info/{hostUtils.js → hostUtils.ts} +5 -5
  179. package/src/locus-info/{index.js → index.ts} +67 -32
  180. package/src/locus-info/{infoUtils.js → infoUtils.ts} +7 -4
  181. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +13 -13
  182. package/src/locus-info/{parser.js → parser.ts} +22 -12
  183. package/src/locus-info/{selfUtils.js → selfUtils.ts} +17 -19
  184. package/src/media/{index.js → index.ts} +130 -205
  185. package/src/media/internal-media-core-wrapper.ts +9 -0
  186. package/src/media/{properties.js → properties.ts} +35 -29
  187. package/src/media/util.ts +16 -0
  188. package/src/mediaQualityMetrics/{config.js → config.ts} +1 -1
  189. package/src/meeting/{effectsState.js → effectsState.ts} +12 -6
  190. package/src/meeting/{index.js → index.ts} +993 -474
  191. package/src/meeting/{muteState.js → muteState.ts} +16 -11
  192. package/src/meeting/{request.js → request.ts} +148 -36
  193. package/src/meeting/{state.js → state.ts} +6 -6
  194. package/src/meeting/{util.js → util.ts} +9 -51
  195. package/src/meeting-info/{collection.js → collection.ts} +4 -1
  196. package/src/meeting-info/{index.js → index.ts} +10 -6
  197. package/src/meeting-info/{meeting-info-v2.js → meeting-info-v2.ts} +28 -10
  198. package/src/meeting-info/{request.js → request.ts} +6 -2
  199. package/src/meeting-info/{util.js → util.ts} +6 -5
  200. package/src/meeting-info/{utilv2.js → utilv2.ts} +8 -7
  201. package/src/meetings/{collection.js → collection.ts} +5 -2
  202. package/src/meetings/{index.js → index.ts} +118 -22
  203. package/src/meetings/{request.js → request.ts} +6 -1
  204. package/src/meetings/{util.js → util.ts} +28 -5
  205. package/src/member/{index.js → index.ts} +46 -15
  206. package/src/member/{util.js → util.ts} +17 -16
  207. package/src/members/{collection.js → collection.ts} +2 -1
  208. package/src/members/{index.js → index.ts} +94 -26
  209. package/src/members/{request.js → request.ts} +16 -5
  210. package/src/members/{util.js → util.ts} +7 -7
  211. package/src/metrics/{config.js → config.ts} +0 -2
  212. package/src/metrics/{constants.js → constants.ts} +0 -0
  213. package/src/metrics/{index.js → index.ts} +27 -8
  214. package/src/multistream/mediaRequestManager.ts +166 -0
  215. package/src/multistream/multistreamMedia.ts +92 -0
  216. package/src/multistream/receiveSlot.ts +141 -0
  217. package/src/multistream/receiveSlotManager.ts +142 -0
  218. package/src/multistream/remoteMedia.ts +228 -0
  219. package/src/multistream/remoteMediaGroup.ts +224 -0
  220. package/src/multistream/remoteMediaManager.ts +911 -0
  221. package/src/networkQualityMonitor/{index.js → index.ts} +18 -3
  222. package/src/personal-meeting-room/{index.js → index.ts} +17 -4
  223. package/src/personal-meeting-room/{request.js → request.ts} +3 -1
  224. package/src/personal-meeting-room/{util.js → util.ts} +1 -1
  225. package/src/reachability/{index.js → index.ts} +28 -17
  226. package/src/reachability/request.ts +4 -2
  227. package/src/reactions/reactions.ts +104 -0
  228. package/src/reactions/reactions.type.ts +36 -0
  229. package/src/reconnection-manager/{index.js → index.ts} +81 -65
  230. package/src/roap/index.ts +229 -0
  231. package/src/roap/{request.js → request.ts} +15 -74
  232. package/src/roap/turnDiscovery.ts +26 -11
  233. package/src/statsAnalyzer/{global.js → global.ts} +2 -0
  234. package/src/statsAnalyzer/{index.js → index.ts} +66 -61
  235. package/src/statsAnalyzer/{mqaUtil.js → mqaUtil.ts} +6 -1
  236. package/src/transcription/{index.js → index.ts} +16 -11
  237. package/test/integration/spec/journey.js +1 -1
  238. package/test/integration/spec/space-meeting.js +1 -2
  239. package/test/unit/spec/locus-info/infoUtils.js +17 -1
  240. package/test/unit/spec/media/index.ts +207 -0
  241. package/test/unit/spec/media/properties.ts +73 -82
  242. package/test/unit/spec/meeting/effectsState.js +1 -3
  243. package/test/unit/spec/meeting/index.js +672 -245
  244. package/test/unit/spec/meeting/muteState.js +7 -0
  245. package/test/unit/spec/meeting/request.js +25 -1
  246. package/test/unit/spec/meeting/utils.js +63 -2
  247. package/test/unit/spec/meetings/index.js +0 -4
  248. package/test/unit/spec/members/index.js +164 -2
  249. package/test/unit/spec/multistream/mediaRequestManager.ts +515 -0
  250. package/test/unit/spec/multistream/receiveSlot.ts +104 -0
  251. package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
  252. package/test/unit/spec/multistream/remoteMedia.ts +225 -0
  253. package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
  254. package/test/unit/spec/multistream/remoteMediaManager.ts +1309 -0
  255. package/test/unit/spec/reconnection-manager/index.js +68 -2
  256. package/test/unit/spec/roap/index.ts +63 -35
  257. package/test/unit/spec/stats-analyzer/index.js +19 -22
  258. package/dist/peer-connection-manager/index.js +0 -794
  259. package/dist/peer-connection-manager/index.js.map +0 -1
  260. package/dist/peer-connection-manager/util.js +0 -124
  261. package/dist/peer-connection-manager/util.js.map +0 -1
  262. package/dist/roap/collection.js +0 -73
  263. package/dist/roap/collection.js.map +0 -1
  264. package/dist/roap/handler.js +0 -337
  265. package/dist/roap/handler.js.map +0 -1
  266. package/dist/roap/state.js +0 -164
  267. package/dist/roap/state.js.map +0 -1
  268. package/dist/roap/util.js +0 -102
  269. package/dist/roap/util.js.map +0 -1
  270. package/src/media/util.js +0 -38
  271. package/src/peer-connection-manager/index.js +0 -723
  272. package/src/peer-connection-manager/util.ts +0 -117
  273. package/src/roap/collection.js +0 -63
  274. package/src/roap/handler.js +0 -252
  275. package/src/roap/index.js +0 -380
  276. package/src/roap/state.js +0 -149
  277. package/src/roap/util.js +0 -93
  278. package/test/unit/spec/peerconnection-manager/index.js +0 -188
  279. package/test/unit/spec/peerconnection-manager/utils.js +0 -48
  280. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
  281. package/test/unit/spec/roap/util.js +0 -30
@@ -18,6 +18,7 @@ import {
18
18
  } from './config';
19
19
 
20
20
  const OSMap = {
21
+ // @ts-ignore
21
22
  'Chrome OS': OS_NAME.chrome,
22
23
  macOS: OS_NAME.MAC,
23
24
  Windows: OS_NAME.WINDOWS,
@@ -76,6 +77,13 @@ const triggerTimers = ({event, meeting, data}) => {
76
77
  * @class Metrics
77
78
  */
78
79
  class Metrics {
80
+ static instance: Metrics;
81
+
82
+ _events: any;
83
+ keys: any;
84
+ meetingCollection: any;
85
+ webex: any;
86
+
79
87
  /**
80
88
  * Create Metrics Object
81
89
  * @constructor
@@ -125,7 +133,7 @@ class Metrics {
125
133
  *
126
134
  * @returns {void}
127
135
  */
128
- initialSetup(meetingCollection, webex) {
136
+ initialSetup(meetingCollection: object, webex: object) {
129
137
  this.meetingCollection = meetingCollection;
130
138
  this.webex = webex;
131
139
  }
@@ -139,7 +147,7 @@ class Metrics {
139
147
  * @param {object} options.event
140
148
  * @returns {object} null
141
149
  */
142
- postEvent(options) {
150
+ postEvent(options: { meeting?: any; meetingId?: string; data?: object; event?: any } | any) {
143
151
  const {meetingId, data = {}, event} = options;
144
152
  let {meeting} = options;
145
153
 
@@ -181,7 +189,7 @@ class Metrics {
181
189
  */
182
190
 
183
191
  initPayload(eventType, identifiers, options) {
184
- const payload = {
192
+ const payload: any = {
185
193
  eventId: uuid.v4(),
186
194
  version: 1,
187
195
  origin: {
@@ -252,7 +260,7 @@ class Metrics {
252
260
  * @private
253
261
  * @memberof Metrics
254
262
  */
255
- getOsName() {
263
+ private getOsName() {
256
264
  return OSMap[getOSName()] ?? OS_NAME.OTHERS;
257
265
  }
258
266
 
@@ -270,7 +278,18 @@ class Metrics {
270
278
  * @public
271
279
  * @memberof Metrics
272
280
  */
273
- initMediaPayload(eventType, identifiers, options = {}) {
281
+ public initMediaPayload(
282
+ eventType: string,
283
+ identifiers: {
284
+ correlationId: string;
285
+ locusUrl: string;
286
+ locusId: string;
287
+ },
288
+ options: {
289
+ intervalData: object;
290
+ clientType: string;
291
+ } | any = {}
292
+ ) {
274
293
  const {audioSetupDelay, videoSetupDelay, joinTimes} = options;
275
294
 
276
295
  const payload = {
@@ -329,7 +348,7 @@ class Metrics {
329
348
  * @returns {{showToUser: boolean, category: string, errorDescription: string,
330
349
  * errorCode: number, errorData: *, fatal: boolean, name: string}}
331
350
  */
332
- parseLocusError(err, showToUser) {
351
+ parseLocusError(err: any, showToUser: boolean) {
333
352
  let errorCode;
334
353
 
335
354
  if (err && err.statusCode && err.statusCode >= 500) {
@@ -447,7 +466,7 @@ class Metrics {
447
466
 
448
467
  generateErrorPayload(errorCode, shownToUser, name, err) {
449
468
  if (error.errors[errorCode]) {
450
- const errorPayload = {
469
+ const errorPayload: any = {
451
470
  shownToUser: shownToUser || false,
452
471
  category: error.errors[errorCode][2],
453
472
  errorDescription: error.errors[errorCode][0],
@@ -515,7 +534,7 @@ class Metrics {
515
534
  *
516
535
  * @returns {void}
517
536
  */
518
- sendBehavioralMetric(metricName, metricFields = {}, metricTags = {}) {
537
+ sendBehavioralMetric(metricName: string, metricFields: object = {}, metricTags: object = {}) {
519
538
  this.webex.internal.metrics.submitClientMetrics(metricName, {
520
539
  type: this.webex.config.metrics.type,
521
540
  fields: metricFields,
@@ -0,0 +1,166 @@
1
+ /* eslint-disable require-jsdoc */
2
+ import {MediaConnection as MC} from '@webex/internal-media-core';
3
+
4
+ import LoggerProxy from '../common/logs/logger-proxy';
5
+
6
+ import {ReceiveSlot, ReceiveSlotId} from './receiveSlot';
7
+
8
+ export interface ActiveSpeakerPolicyInfo {
9
+ policy: 'active-speaker';
10
+ priority: number;
11
+ crossPriorityDuplication: boolean;
12
+ crossPolicyDuplication: boolean;
13
+ preferLiveVideo: boolean;
14
+ }
15
+
16
+ export interface ReceiverSelectedPolicyInfo {
17
+ policy: 'receiver-selected';
18
+ csi: number;
19
+ }
20
+
21
+ export type PolicyInfo = ActiveSpeakerPolicyInfo | ReceiverSelectedPolicyInfo;
22
+
23
+ export interface H264CodecInfo {
24
+ codec: 'h264';
25
+ maxFs?: number;
26
+ maxFps?: number;
27
+ maxMbps?: number;
28
+ maxWidth?: number;
29
+ maxHeight?: number;
30
+ }
31
+
32
+ export type CodecInfo = H264CodecInfo; // we'll add AV1 here in the future when it's available
33
+
34
+ export interface MediaRequest {
35
+ policyInfo: PolicyInfo;
36
+ receiveSlots: Array<ReceiveSlot>;
37
+ codecInfo?: CodecInfo;
38
+ }
39
+
40
+ export type MediaRequestId = string;
41
+
42
+ const CODEC_DEFAULTS = {
43
+ h264: {
44
+ maxFs: 8192,
45
+ maxFps: 3000,
46
+ maxMbps: 245760,
47
+ },
48
+ };
49
+
50
+ type SendMediaRequestsCallback = (mediaRequests: MC.MediaRequest[]) => void;
51
+
52
+ export class MediaRequestManager {
53
+ private sendMediaRequestsCallback: SendMediaRequestsCallback;
54
+
55
+ private counter;
56
+
57
+ private clientRequests: {[key: MediaRequestId]: MediaRequest};
58
+
59
+ private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};
60
+
61
+ constructor(sendMediaRequestsCallback: SendMediaRequestsCallback) {
62
+ this.sendMediaRequestsCallback = sendMediaRequestsCallback;
63
+ this.counter = 0;
64
+ this.clientRequests = {};
65
+ this.slotsActiveInLastMediaRequest = {};
66
+ }
67
+
68
+ private resetInactiveReceiveSlots() {
69
+ const activeSlots: {[key: ReceiveSlotId]: ReceiveSlot} = {};
70
+
71
+ // create a map of all currently used slot ids
72
+ Object.values(this.clientRequests).forEach((request) =>
73
+ request.receiveSlots.forEach((slot) => {
74
+ activeSlots[slot.id] = slot;
75
+ })
76
+ );
77
+
78
+ // when we stop using some receive slots and they are not included in the new media request,
79
+ // we will never get a 'no source' notification for them, so we reset their state,
80
+ // so that the client doesn't try to display their video anymore
81
+ for (const [slotId, slot] of Object.entries(this.slotsActiveInLastMediaRequest)) {
82
+ if (!(slotId in activeSlots)) {
83
+ LoggerProxy.logger.info(
84
+ `multistream:mediaRequestManager --> resetting sourceState to "no source" for slot ${slot.id}`
85
+ );
86
+ slot.resetSourceState();
87
+ }
88
+ }
89
+
90
+ this.slotsActiveInLastMediaRequest = activeSlots;
91
+ }
92
+
93
+ private sendRequests() {
94
+ const wcmeMediaRequests: MC.MediaRequest[] = [];
95
+
96
+ // todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)
97
+ const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
98
+
99
+ // map all the client media requests to wcme media requests
100
+ Object.values(this.clientRequests).forEach((mr) => {
101
+ wcmeMediaRequests.push(
102
+ new MC.MediaRequest(
103
+ mr.policyInfo.policy === 'active-speaker'
104
+ ? MC.Policy.ActiveSpeaker
105
+ : MC.Policy.ReceiverSelected,
106
+ mr.policyInfo.policy === 'active-speaker'
107
+ ? new MC.ActiveSpeakerInfo(
108
+ mr.policyInfo.priority,
109
+ mr.policyInfo.crossPriorityDuplication,
110
+ mr.policyInfo.crossPolicyDuplication,
111
+ mr.policyInfo.preferLiveVideo
112
+ )
113
+ : new MC.ReceiverSelectedInfo(mr.policyInfo.csi),
114
+ mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
115
+ maxPayloadBitsPerSecond,
116
+ mr.codecInfo && [
117
+ new MC.CodecInfo(
118
+ 0x80,
119
+ new MC.H264Codec(
120
+ mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
121
+ mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
122
+ mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,
123
+ mr.codecInfo.maxWidth,
124
+ mr.codecInfo.maxHeight
125
+ )
126
+ ),
127
+ ]
128
+ )
129
+ );
130
+ });
131
+
132
+ this.sendMediaRequestsCallback(wcmeMediaRequests);
133
+
134
+ this.resetInactiveReceiveSlots();
135
+ }
136
+
137
+ public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
138
+ // eslint-disable-next-line no-plusplus
139
+ const newId = `${this.counter++}`;
140
+
141
+ this.clientRequests[newId] = mediaRequest;
142
+
143
+ if (commit) {
144
+ this.commit();
145
+ }
146
+
147
+ return newId;
148
+ }
149
+
150
+ public cancelRequest(requestId: MediaRequestId, commit = true) {
151
+ delete this.clientRequests[requestId];
152
+
153
+ if (commit) {
154
+ this.commit();
155
+ }
156
+ }
157
+
158
+ public commit() {
159
+ return this.sendRequests();
160
+ }
161
+
162
+ public reset() {
163
+ this.clientRequests = {};
164
+ this.slotsActiveInLastMediaRequest = {};
165
+ }
166
+ }
@@ -0,0 +1,92 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ import {AUDIO, VIDEO} from '../constants';
4
+ import createMuteState from '../meeting/muteState';
5
+ import Meeting from '../meeting';
6
+
7
+ /**
8
+ * Class wrapping all the multistream specific APIs that need to be exposed at meeting level.
9
+ *
10
+ */
11
+ export class MultistreamMedia {
12
+ private meeting: Meeting;
13
+
14
+ /**
15
+ * Constructor
16
+ * @param {Meeting} meeting
17
+ */
18
+ constructor(meeting: Meeting) {
19
+ this.meeting = meeting;
20
+ }
21
+
22
+ /**
23
+ * throws if we don't have a media connection created
24
+ */
25
+ private checkMediaConnection() {
26
+ if (this.meeting?.mediaProperties?.webrtcMediaConnection) {
27
+ return;
28
+ }
29
+ throw new Error('Webrtc media connection is missing');
30
+ }
31
+
32
+ /**
33
+ * Publishes a local track in the meeting
34
+ *
35
+ * @param {MediaStreamTrack} track
36
+ * @returns {Promise}
37
+ */
38
+ publishTrack(track: MediaStreamTrack): Promise<void> {
39
+ this.checkMediaConnection();
40
+
41
+ /* todo: for now we don't support screen share (waiting for server to support bundling, see SPARK-377812)
42
+ for sharing:
43
+ we have to call meeting.stopFloorRequest() before unpublishing a track
44
+ we have to call meeting.share() after publishing a track
45
+ */
46
+
47
+ // todo: depending on how muting is done with Local tracks, this code here might need to change...
48
+
49
+ if (track.kind === 'audio') {
50
+ this.meeting.setLocalAudioTrack(track);
51
+ this.meeting.mediaProperties.mediaDirection.sendAudio = true;
52
+
53
+ // audio state could be undefined if you have not sent audio before
54
+ this.meeting.audio =
55
+ this.meeting.audio ||
56
+ createMuteState(AUDIO, this.meeting, this.meeting.mediaProperties.mediaDirection);
57
+ } else if (track.kind === 'video') {
58
+ this.meeting.setLocalVideoTrack(track);
59
+ this.meeting.mediaProperties.mediaDirection.sendVideo = true;
60
+
61
+ // video state could be undefined if you have not sent video before
62
+ this.meeting.video =
63
+ this.meeting.video ||
64
+ createMuteState(VIDEO, this.meeting, this.meeting.mediaProperties.mediaDirection);
65
+ }
66
+
67
+ return this.meeting.mediaProperties.webrtcMediaConnection.publishTrack(track);
68
+ }
69
+
70
+ /**
71
+ * Unpublishes a local track in the meeting
72
+ *
73
+ * @param {MediaStreamTrack} track
74
+ * @returns {Promise}
75
+ */
76
+ unpublishTrack(track: MediaStreamTrack): Promise<void> {
77
+ // todo: see todos in publishTrack() - they all apply here too:
78
+ // screen sharing - SPARK-377812
79
+ // muting etc
80
+
81
+ if (track.kind === 'audio') {
82
+ this.meeting.setLocalVideoTrack(null);
83
+ this.meeting.mediaProperties.mediaDirection.sendAudio = false;
84
+ } else if (track.kind === 'video') {
85
+ this.meeting.setLocalAudioTrack(null);
86
+ this.meeting.mediaProperties.mediaDirection.sendVideo = false;
87
+ }
88
+ this.checkMediaConnection();
89
+
90
+ return this.meeting.mediaProperties.webrtcMediaConnection.unpublishTrack(track);
91
+ }
92
+ }
@@ -0,0 +1,141 @@
1
+ import {MediaConnection as MC} from '@webex/internal-media-core';
2
+
3
+ import LoggerProxy from '../common/logs/logger-proxy';
4
+ import EventsScope from '../common/events/events-scope';
5
+
6
+ export const ReceiveSlotEvents = {
7
+ SourceUpdate: 'sourceUpdate',
8
+ };
9
+
10
+ export type SourceState = MC.SourceState;
11
+ export type CSI = number;
12
+ export type MemberId = string;
13
+ export type ReceiveSlotId = string;
14
+
15
+ let receiveSlotCounter = 0;
16
+
17
+ export type FindMemberIdCallback = (csi: CSI) => MemberId | undefined;
18
+
19
+ /**
20
+ * Class representing a receive slot. A single receive slot is able to receive a single track
21
+ * for example some participant's main video or audio
22
+ */
23
+ export class ReceiveSlot extends EventsScope {
24
+ private readonly mcReceiveSlot: MC.ReceiveSlot;
25
+
26
+ private readonly findMemberIdCallback: FindMemberIdCallback;
27
+
28
+ public readonly id: ReceiveSlotId;
29
+
30
+ public readonly mediaType: MC.MediaType;
31
+
32
+ #memberId?: MemberId;
33
+
34
+ #csi?: CSI;
35
+
36
+ #sourceState: MC.SourceState;
37
+
38
+ /**
39
+ * constructor - don't use it directly, you should always use meeting.receiveSlotManager.allocateSlot()
40
+ * to create any receive slots
41
+ *
42
+ * @param {MC.MediaType} mediaType
43
+ * @param {MC.ReceiveSlot} mcReceiveSlot
44
+ * @param {FindMemberIdCallback} findMemberIdCallback callback for finding memberId for given CSI
45
+ */
46
+ constructor(
47
+ mediaType: MC.MediaType,
48
+ mcReceiveSlot: MC.ReceiveSlot,
49
+ findMemberIdCallback: FindMemberIdCallback
50
+ ) {
51
+ super();
52
+
53
+ receiveSlotCounter += 1;
54
+
55
+ this.findMemberIdCallback = findMemberIdCallback;
56
+ this.mediaType = mediaType;
57
+ this.mcReceiveSlot = mcReceiveSlot;
58
+ this.#sourceState = 'no source';
59
+ this.id = `r${receiveSlotCounter}`;
60
+
61
+ this.setupEventListeners();
62
+ }
63
+
64
+ /**
65
+ * Getter for memberId
66
+ */
67
+ public get memberId() {
68
+ return this.#memberId;
69
+ }
70
+
71
+ /**
72
+ * Getter for csi
73
+ */
74
+ public get csi() {
75
+ return this.#csi;
76
+ }
77
+
78
+ /**
79
+ * Getter for sourceState
80
+ */
81
+ public get sourceState() {
82
+ return this.#sourceState;
83
+ }
84
+
85
+ /**
86
+ * registers event handlers with the underlying ReceiveSlot
87
+ */
88
+ setupEventListeners() {
89
+ const scope = {
90
+ file: 'meeting/receiveSlot',
91
+ function: 'setupEventListeners',
92
+ };
93
+
94
+ this.mcReceiveSlot.on(
95
+ MC.ReceiveSlotEvents.SourceUpdate,
96
+ (state: MC.SourceState, csi?: number) => {
97
+ LoggerProxy.logger.log(
98
+ `ReceiveSlot#setupEventListeners --> got source update on receive slot ${this.id}, mediaType=${this.mediaType}, csi=${csi}, state=${state}`
99
+ );
100
+ this.#memberId = csi ? this.findMemberIdCallback(csi) : undefined;
101
+ this.#csi = csi;
102
+ this.#sourceState = state;
103
+
104
+ this.emit(scope, ReceiveSlotEvents.SourceUpdate, {
105
+ state: this.#sourceState,
106
+ csi: this.#csi,
107
+ memberId: this.#memberId,
108
+ });
109
+ }
110
+ );
111
+ }
112
+
113
+ /**
114
+ * The MediaStream object associated with this slot.
115
+ *
116
+ * @returns {MediaStream} The MediaStreamTrack.
117
+ */
118
+ get stream(): MediaStream {
119
+ return this.mcReceiveSlot.stream;
120
+ }
121
+
122
+ /**
123
+ * The underlying WCME receive slot
124
+ */
125
+ get wcmeReceiveSlot(): MC.ReceiveSlot {
126
+ return this.mcReceiveSlot;
127
+ }
128
+
129
+ /**
130
+ * Resets the source state to the default 'no source' value.
131
+ * This function should be called on receive slots that are
132
+ * no longer part of a media request. It's needed because WCME
133
+ * does not send any more events on such slots, so the sourceState
134
+ * value would not represent the truth anymore.
135
+ */
136
+ public resetSourceState() {
137
+ this.#sourceState = 'no source';
138
+ this.#csi = undefined;
139
+ this.#memberId = undefined;
140
+ }
141
+ }
@@ -0,0 +1,142 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import {MediaConnection as MC} from '@webex/internal-media-core';
3
+
4
+ import LoggerProxy from '../common/logs/logger-proxy';
5
+ import Meeting from '../meeting';
6
+
7
+ import {CSI, ReceiveSlot} from './receiveSlot';
8
+
9
+ /**
10
+ * Manages all receive slots used by a meeting. WMCE receive slots cannot be ever deleted,
11
+ * so this manager has a pool in order to re-use the slots that were released earlier.
12
+ */
13
+ export class ReceiveSlotManager {
14
+ private allocatedSlots: {[key in MC.MediaType]: ReceiveSlot[]};
15
+
16
+ private freeSlots: {[key in MC.MediaType]: ReceiveSlot[]};
17
+
18
+ private meeting: Meeting;
19
+
20
+ /**
21
+ * Constructor
22
+ * @param {Meeting} meeting
23
+ */
24
+ constructor(meeting) {
25
+ this.allocatedSlots = {
26
+ [MC.MediaType.AudioMain]: [],
27
+ [MC.MediaType.VideoMain]: [],
28
+ [MC.MediaType.AudioSlides]: [],
29
+ [MC.MediaType.VideoSlides]: [],
30
+ };
31
+ this.freeSlots = {
32
+ [MC.MediaType.AudioMain]: [],
33
+ [MC.MediaType.VideoMain]: [],
34
+ [MC.MediaType.AudioSlides]: [],
35
+ [MC.MediaType.VideoSlides]: [],
36
+ };
37
+ this.meeting = meeting;
38
+ }
39
+
40
+ /**
41
+ * Creates a new receive slot or returns one from the existing pool of free slots
42
+ *
43
+ * @param {MC.MediaType} mediaType
44
+ * @returns {Promise<ReceiveSlot>}
45
+ */
46
+ async allocateSlot(mediaType: MC.MediaType): Promise<ReceiveSlot> {
47
+ if (!this.meeting?.mediaProperties?.webrtcMediaConnection) {
48
+ return Promise.reject(new Error('Webrtc media connection is missing'));
49
+ }
50
+
51
+ // try to use one of the free ones
52
+ const availableSlot = this.freeSlots[mediaType].pop();
53
+
54
+ if (availableSlot) {
55
+ this.allocatedSlots[mediaType].push(availableSlot);
56
+
57
+ LoggerProxy.logger.log(`receive slot re-used: ${availableSlot.id}`);
58
+
59
+ return availableSlot;
60
+ }
61
+
62
+ // we have to create a new one
63
+ const wcmeReceiveSlot =
64
+ await this.meeting.mediaProperties.webrtcMediaConnection.createReceiveSlot(mediaType);
65
+
66
+ const receiveSlot = new ReceiveSlot(
67
+ mediaType,
68
+ wcmeReceiveSlot,
69
+ (csi: CSI) => this.meeting.members.findMemberByCsi(csi)?.id
70
+ );
71
+
72
+ this.allocatedSlots[mediaType].push(receiveSlot);
73
+ LoggerProxy.logger.log(`new receive slot allocated: ${receiveSlot.id}`);
74
+
75
+ return receiveSlot;
76
+ }
77
+
78
+ /**
79
+ * Releases the slot back to the pool so it can be re-used by others in the future
80
+ * @param {ReceiveSlot} slot
81
+ */
82
+ releaseSlot(slot: ReceiveSlot) {
83
+ const idx = this.allocatedSlots[slot.mediaType].findIndex(
84
+ (allocatedSlot) => allocatedSlot === slot
85
+ );
86
+
87
+ if (idx >= 0) {
88
+ this.allocatedSlots[slot.mediaType].splice(idx, 1);
89
+ this.freeSlots[slot.mediaType].push(slot);
90
+ LoggerProxy.logger.log(`receive slot released: ${slot.id}`);
91
+ } else {
92
+ LoggerProxy.logger.warn(
93
+ 'ReceiveSlotManager#releaseSlot --> trying to release a slot that is not managed by this ReceiveSlotManager'
94
+ );
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Resets the slot manager - this method should be called when the media connection is torn down
100
+ */
101
+ reset() {
102
+ this.allocatedSlots = {
103
+ [MC.MediaType.AudioMain]: [],
104
+ [MC.MediaType.VideoMain]: [],
105
+ [MC.MediaType.AudioSlides]: [],
106
+ [MC.MediaType.VideoSlides]: [],
107
+ };
108
+ this.freeSlots = {
109
+ [MC.MediaType.AudioMain]: [],
110
+ [MC.MediaType.VideoMain]: [],
111
+ [MC.MediaType.AudioSlides]: [],
112
+ [MC.MediaType.VideoSlides]: [],
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Returns statistics about the managed slots
118
+ *
119
+ * @returns {Object}
120
+ */
121
+ getStats() {
122
+ const numAllocatedSlots = {};
123
+ const numFreeSlots = {};
124
+
125
+ Object.keys(this.allocatedSlots).forEach((key) => {
126
+ if (this.allocatedSlots[key].length > 0) {
127
+ numAllocatedSlots[key] = this.allocatedSlots[key].length;
128
+ }
129
+ });
130
+
131
+ Object.keys(this.freeSlots).forEach((key) => {
132
+ if (this.freeSlots[key].length > 0) {
133
+ numFreeSlots[key] = this.freeSlots[key].length;
134
+ }
135
+ });
136
+
137
+ return {
138
+ numAllocatedSlots,
139
+ numFreeSlots,
140
+ };
141
+ }
142
+ }