@webex/plugin-meetings 3.0.0-beta.16 → 3.0.0-beta.18

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 (156) hide show
  1. package/dist/breakouts/breakout.js +116 -0
  2. package/dist/breakouts/breakout.js.map +1 -0
  3. package/dist/breakouts/collection.js +23 -0
  4. package/dist/breakouts/collection.js.map +1 -0
  5. package/dist/breakouts/index.js +226 -0
  6. package/dist/breakouts/index.js.map +1 -0
  7. package/dist/config.js +4 -1
  8. package/dist/config.js.map +1 -1
  9. package/dist/constants.js +43 -6
  10. package/dist/constants.js.map +1 -1
  11. package/dist/locus-info/controlsUtils.js +2 -1
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +48 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/locus-info/parser.js +1 -0
  16. package/dist/locus-info/parser.js.map +1 -1
  17. package/dist/locus-info/selfUtils.js +19 -11
  18. package/dist/locus-info/selfUtils.js.map +1 -1
  19. package/dist/media/index.js +3 -3
  20. package/dist/media/index.js.map +1 -1
  21. package/dist/media/properties.js +4 -4
  22. package/dist/media/properties.js.map +1 -1
  23. package/dist/meeting/in-meeting-actions.js +5 -1
  24. package/dist/meeting/in-meeting-actions.js.map +1 -1
  25. package/dist/meeting/index.js +652 -459
  26. package/dist/meeting/index.js.map +1 -1
  27. package/dist/meeting/request.js +25 -44
  28. package/dist/meeting/request.js.map +1 -1
  29. package/dist/meeting/request.type.js.map +1 -1
  30. package/dist/meeting/util.js +22 -57
  31. package/dist/meeting/util.js.map +1 -1
  32. package/dist/meeting-info/meeting-info-v2.js +2 -0
  33. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  34. package/dist/meetings/index.js +28 -18
  35. package/dist/meetings/index.js.map +1 -1
  36. package/dist/meetings/request.js +14 -12
  37. package/dist/meetings/request.js.map +1 -1
  38. package/dist/member/index.js +9 -0
  39. package/dist/member/index.js.map +1 -1
  40. package/dist/member/util.js +14 -1
  41. package/dist/member/util.js.map +1 -1
  42. package/dist/members/index.js +8 -6
  43. package/dist/members/index.js.map +1 -1
  44. package/dist/members/request.js +3 -1
  45. package/dist/members/request.js.map +1 -1
  46. package/dist/multistream/mediaRequestManager.js +46 -6
  47. package/dist/multistream/mediaRequestManager.js.map +1 -1
  48. package/dist/multistream/multistreamMedia.js +4 -0
  49. package/dist/multistream/multistreamMedia.js.map +1 -1
  50. package/dist/multistream/receiveSlot.js +3 -3
  51. package/dist/multistream/receiveSlot.js.map +1 -1
  52. package/dist/multistream/receiveSlotManager.js +8 -6
  53. package/dist/multistream/receiveSlotManager.js.map +1 -1
  54. package/dist/multistream/remoteMedia.js.map +1 -1
  55. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  56. package/dist/multistream/remoteMediaManager.js +168 -63
  57. package/dist/multistream/remoteMediaManager.js.map +1 -1
  58. package/dist/reachability/index.js +63 -51
  59. package/dist/reachability/index.js.map +1 -1
  60. package/dist/reactions/constants.js +13 -0
  61. package/dist/reactions/constants.js.map +1 -0
  62. package/dist/reactions/reactions.type.js.map +1 -1
  63. package/dist/reconnection-manager/index.js +25 -12
  64. package/dist/reconnection-manager/index.js.map +1 -1
  65. package/dist/recording-controller/enums.js +17 -0
  66. package/dist/recording-controller/enums.js.map +1 -0
  67. package/dist/recording-controller/index.js +343 -0
  68. package/dist/recording-controller/index.js.map +1 -0
  69. package/dist/recording-controller/util.js +63 -0
  70. package/dist/recording-controller/util.js.map +1 -0
  71. package/dist/roap/request.js +88 -68
  72. package/dist/roap/request.js.map +1 -1
  73. package/dist/roap/turnDiscovery.js +72 -47
  74. package/dist/roap/turnDiscovery.js.map +1 -1
  75. package/dist/statsAnalyzer/index.js +3 -3
  76. package/dist/statsAnalyzer/index.js.map +1 -1
  77. package/dist/statsAnalyzer/mqaUtil.js +18 -6
  78. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  79. package/package.json +24 -19
  80. package/src/breakouts/README.md +190 -0
  81. package/src/breakouts/breakout.ts +110 -0
  82. package/src/breakouts/collection.ts +19 -0
  83. package/src/breakouts/index.ts +225 -0
  84. package/src/config.ts +4 -1
  85. package/src/constants.ts +39 -1
  86. package/src/locus-info/controlsUtils.ts +2 -0
  87. package/src/locus-info/index.ts +59 -1
  88. package/src/locus-info/parser.ts +1 -0
  89. package/src/locus-info/selfUtils.ts +8 -0
  90. package/src/media/index.ts +1 -2
  91. package/src/media/properties.ts +6 -9
  92. package/src/meeting/in-meeting-actions.ts +8 -0
  93. package/src/meeting/index.ts +360 -111
  94. package/src/meeting/request.ts +9 -31
  95. package/src/meeting/request.type.ts +2 -0
  96. package/src/meeting/util.ts +25 -60
  97. package/src/meeting-info/meeting-info-v2.ts +2 -0
  98. package/src/meetings/index.ts +10 -5
  99. package/src/meetings/request.ts +1 -1
  100. package/src/member/index.ts +9 -0
  101. package/src/member/util.ts +14 -1
  102. package/src/members/index.ts +1 -0
  103. package/src/members/request.ts +1 -0
  104. package/src/multistream/mediaRequestManager.ts +79 -15
  105. package/src/multistream/multistreamMedia.ts +4 -0
  106. package/src/multistream/receiveSlot.ts +17 -12
  107. package/src/multistream/receiveSlotManager.ts +22 -21
  108. package/src/multistream/remoteMedia.ts +1 -1
  109. package/src/multistream/remoteMediaGroup.ts +2 -2
  110. package/src/multistream/remoteMediaManager.ts +150 -37
  111. package/src/reachability/index.ts +16 -13
  112. package/src/reactions/constants.ts +4 -0
  113. package/src/reactions/reactions.type.ts +25 -0
  114. package/src/reconnection-manager/index.ts +18 -9
  115. package/src/recording-controller/enums.ts +8 -0
  116. package/src/recording-controller/index.ts +315 -0
  117. package/src/recording-controller/util.ts +58 -0
  118. package/src/roap/request.ts +78 -73
  119. package/src/roap/turnDiscovery.ts +8 -6
  120. package/src/statsAnalyzer/index.ts +4 -4
  121. package/src/statsAnalyzer/mqaUtil.ts +6 -0
  122. package/test/unit/spec/breakouts/breakout.ts +119 -0
  123. package/test/unit/spec/breakouts/collection.ts +15 -0
  124. package/test/unit/spec/breakouts/index.ts +293 -0
  125. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  126. package/test/unit/spec/locus-info/index.js +103 -0
  127. package/test/unit/spec/locus-info/selfConstant.js +25 -0
  128. package/test/unit/spec/locus-info/selfUtils.js +84 -0
  129. package/test/unit/spec/media/index.ts +1 -1
  130. package/test/unit/spec/media/properties.ts +9 -9
  131. package/test/unit/spec/meeting/effectsState.js +5 -1
  132. package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
  133. package/test/unit/spec/meeting/index.js +241 -50
  134. package/test/unit/spec/meeting/request.js +17 -0
  135. package/test/unit/spec/meeting/utils.js +28 -122
  136. package/test/unit/spec/meetings/index.js +1 -0
  137. package/test/unit/spec/member/util.js +26 -1
  138. package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
  139. package/test/unit/spec/multistream/receiveSlot.ts +6 -6
  140. package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
  141. package/test/unit/spec/multistream/remoteMedia.ts +2 -2
  142. package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
  143. package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
  144. package/test/unit/spec/reachability/index.ts +58 -24
  145. package/test/unit/spec/reconnection-manager/index.js +42 -13
  146. package/test/unit/spec/recording-controller/index.js +231 -0
  147. package/test/unit/spec/recording-controller/util.js +102 -0
  148. package/test/unit/spec/roap/index.ts +2 -1
  149. package/test/unit/spec/roap/request.ts +114 -0
  150. package/test/unit/spec/roap/turnDiscovery.ts +45 -29
  151. package/test/unit/spec/stats-analyzer/index.js +2 -2
  152. package/test/utils/webex-test-users.js +1 -0
  153. package/tsconfig.json +6 -0
  154. package/dist/media/internal-media-core-wrapper.js +0 -18
  155. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  156. package/src/media/internal-media-core-wrapper.ts +0 -9
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  /* eslint-disable import/prefer-default-export */
3
- import {MediaConnection as MC} from '@webex/internal-media-core';
3
+ import {MediaType} from '@webex/internal-media-core';
4
4
 
5
5
  import LoggerProxy from '../common/logs/logger-proxy';
6
6
  import Meeting from '../meeting';
@@ -12,9 +12,9 @@ import {CSI, ReceiveSlot} from './receiveSlot';
12
12
  * so this manager has a pool in order to re-use the slots that were released earlier.
13
13
  */
14
14
  export class ReceiveSlotManager {
15
- private allocatedSlots: {[key in MC.MediaType]: ReceiveSlot[]};
15
+ private allocatedSlots: {[key in MediaType]: ReceiveSlot[]};
16
16
 
17
- private freeSlots: {[key in MC.MediaType]: ReceiveSlot[]};
17
+ private freeSlots: {[key in MediaType]: ReceiveSlot[]};
18
18
 
19
19
  private meeting: Meeting;
20
20
 
@@ -24,16 +24,16 @@ export class ReceiveSlotManager {
24
24
  */
25
25
  constructor(meeting) {
26
26
  this.allocatedSlots = {
27
- [MC.MediaType.AudioMain]: [],
28
- [MC.MediaType.VideoMain]: [],
29
- [MC.MediaType.AudioSlides]: [],
30
- [MC.MediaType.VideoSlides]: [],
27
+ [MediaType.AudioMain]: [],
28
+ [MediaType.VideoMain]: [],
29
+ [MediaType.AudioSlides]: [],
30
+ [MediaType.VideoSlides]: [],
31
31
  };
32
32
  this.freeSlots = {
33
- [MC.MediaType.AudioMain]: [],
34
- [MC.MediaType.VideoMain]: [],
35
- [MC.MediaType.AudioSlides]: [],
36
- [MC.MediaType.VideoSlides]: [],
33
+ [MediaType.AudioMain]: [],
34
+ [MediaType.VideoMain]: [],
35
+ [MediaType.AudioSlides]: [],
36
+ [MediaType.VideoSlides]: [],
37
37
  };
38
38
  this.meeting = meeting;
39
39
  }
@@ -41,10 +41,10 @@ export class ReceiveSlotManager {
41
41
  /**
42
42
  * Creates a new receive slot or returns one from the existing pool of free slots
43
43
  *
44
- * @param {MC.MediaType} mediaType
44
+ * @param {MediaType} mediaType
45
45
  * @returns {Promise<ReceiveSlot>}
46
46
  */
47
- async allocateSlot(mediaType: MC.MediaType): Promise<ReceiveSlot> {
47
+ async allocateSlot(mediaType: MediaType): Promise<ReceiveSlot> {
48
48
  if (!this.meeting?.mediaProperties?.webrtcMediaConnection) {
49
49
  return Promise.reject(new Error('Webrtc media connection is missing'));
50
50
  }
@@ -67,6 +67,7 @@ export class ReceiveSlotManager {
67
67
  const receiveSlot = new ReceiveSlot(
68
68
  mediaType,
69
69
  wcmeReceiveSlot,
70
+ // @ts-ignore
70
71
  (csi: CSI) => this.meeting.members.findMemberByCsi(csi)?.id
71
72
  );
72
73
 
@@ -101,16 +102,16 @@ export class ReceiveSlotManager {
101
102
  */
102
103
  reset() {
103
104
  this.allocatedSlots = {
104
- [MC.MediaType.AudioMain]: [],
105
- [MC.MediaType.VideoMain]: [],
106
- [MC.MediaType.AudioSlides]: [],
107
- [MC.MediaType.VideoSlides]: [],
105
+ [MediaType.AudioMain]: [],
106
+ [MediaType.VideoMain]: [],
107
+ [MediaType.AudioSlides]: [],
108
+ [MediaType.VideoSlides]: [],
108
109
  };
109
110
  this.freeSlots = {
110
- [MC.MediaType.AudioMain]: [],
111
- [MC.MediaType.VideoMain]: [],
112
- [MC.MediaType.AudioSlides]: [],
113
- [MC.MediaType.VideoSlides]: [],
111
+ [MediaType.AudioMain]: [],
112
+ [MediaType.VideoMain]: [],
113
+ [MediaType.AudioSlides]: [],
114
+ [MediaType.VideoSlides]: [],
114
115
  };
115
116
  }
116
117
 
@@ -56,7 +56,7 @@ export function getMaxFs(paneSize: RemoteVideoResolution): number {
56
56
  }
57
57
 
58
58
  type Options = {
59
- resolution?: RemoteVideoResolution; // applies only to groups of type MC.MediaType.VideoMain and MC.MediaType.VideoSlides
59
+ resolution?: RemoteVideoResolution; // applies only to groups of type MediaType.VideoMain and MediaType.VideoSlides
60
60
  };
61
61
 
62
62
  export type RemoteMediaId = string;
@@ -8,8 +8,8 @@ import {MediaRequestId, MediaRequestManager} from './mediaRequestManager';
8
8
  import {CSI, ReceiveSlot} from './receiveSlot';
9
9
 
10
10
  type Options = {
11
- resolution?: RemoteVideoResolution; // applies only to groups of type MC.MediaType.VideoMain and MC.MediaType.VideoSlides
12
- preferLiveVideo?: boolean; // applies only to groups of type MC.MediaType.VideoMain and MC.MediaType.VideoSlides
11
+ resolution?: RemoteVideoResolution; // applies only to groups of type MediaType.VideoMain and MediaType.VideoSlides
12
+ preferLiveVideo?: boolean; // applies only to groups of type MediaType.VideoMain and MediaType.VideoSlides
13
13
  };
14
14
 
15
15
  export class RemoteMediaGroup {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  import {cloneDeep, remove} from 'lodash';
3
3
  import {EventMap} from 'typed-emitter';
4
- import {MediaConnection as MC} from '@webex/internal-media-core';
4
+ import {MediaType} from '@webex/internal-media-core';
5
5
 
6
6
  import LoggerProxy from '../common/logs/logger-proxy';
7
7
  import EventsScope from '../common/events/events-scope';
@@ -31,8 +31,8 @@ export interface MemberVideoPane {
31
31
  }
32
32
 
33
33
  export interface VideoLayout {
34
- screenShareVideo: {
35
- size: PaneSize | null; // null if you don't want to receive any screen share video
34
+ screenShareVideo?: {
35
+ size: PaneSize;
36
36
  };
37
37
  activeSpeakerVideoPaneGroups?: ActiveSpeakerVideoPaneGroup[]; // list of active speaker video pane groups
38
38
  memberVideoPanes?: MemberVideoPane[]; // list of video panes for specific members, CSI values can be changed later via setVideoPaneCsi()
@@ -41,6 +41,7 @@ export interface VideoLayout {
41
41
  export interface Configuration {
42
42
  audio: {
43
43
  numOfActiveSpeakerStreams: number; // number of audio streams we want to receive
44
+ numOfScreenShareStreams: number; // 1 should be enough, because in webex only 1 person at a time can be presenting screen share
44
45
  };
45
46
  video: {
46
47
  preferLiveVideo: boolean; // applies to all pane groups with active speaker policy
@@ -48,17 +49,12 @@ export interface Configuration {
48
49
 
49
50
  layouts: {[key: LayoutId]: VideoLayout}; // a map of all available layouts, a layout can be set via setLayout() method
50
51
  };
51
- screenShare: {
52
- audio: boolean; // whether we ever want to receive screen share audio at all
53
- video: boolean; // whether we ever want to receive screen share video at all
54
- };
55
52
  }
56
53
 
57
54
  /* Predefined layouts: */
58
55
 
59
56
  // An "all equal" grid, with size up to 3 x 3 = 9:
60
57
  const AllEqualLayout: VideoLayout = {
61
- screenShareVideo: {size: null},
62
58
  activeSpeakerVideoPaneGroups: [
63
59
  {
64
60
  id: 'main',
@@ -71,7 +67,6 @@ const AllEqualLayout: VideoLayout = {
71
67
 
72
68
  // A layout with just a single remote active speaker video pane:
73
69
  const SingleLayout: VideoLayout = {
74
- screenShareVideo: {size: null},
75
70
  activeSpeakerVideoPaneGroups: [
76
71
  {
77
72
  id: 'main',
@@ -84,7 +79,6 @@ const SingleLayout: VideoLayout = {
84
79
 
85
80
  // A layout with 1 big pane for the highest priority active speaker and 5 small panes for other active speakers:
86
81
  const OnePlusFiveLayout: VideoLayout = {
87
- screenShareVideo: {size: null},
88
82
  activeSpeakerVideoPaneGroups: [
89
83
  {
90
84
  id: 'mainBigOne',
@@ -103,7 +97,6 @@ const OnePlusFiveLayout: VideoLayout = {
103
97
 
104
98
  // A layout with 2 big panes for 2 main active speakers and a strip of 6 small panes for other active speakers:
105
99
  const TwoMainPlusSixSmallLayout: VideoLayout = {
106
- screenShareVideo: {size: null},
107
100
  activeSpeakerVideoPaneGroups: [
108
101
  {
109
102
  id: 'mainGroupWith2BigPanes',
@@ -122,7 +115,7 @@ const TwoMainPlusSixSmallLayout: VideoLayout = {
122
115
 
123
116
  // A strip of 8 small video panes (thumbnails) displayed at the top of a remote screenshare:
124
117
  const RemoteScreenShareWithSmallThumbnailsLayout: VideoLayout = {
125
- screenShareVideo: {size: 'best'},
118
+ // screenShareVideo: {size: 'best'}, // todo: SPARK-393485: uncomment this once backend supports screen sharing
126
119
  activeSpeakerVideoPaneGroups: [
127
120
  {
128
121
  id: 'thumbnails',
@@ -135,7 +128,6 @@ const RemoteScreenShareWithSmallThumbnailsLayout: VideoLayout = {
135
128
 
136
129
  // A staged layout with 4 pre-selected meeting participants in the main 2x2 grid and 6 small panes for other active speakers at the top:
137
130
  const Stage2x2With6ThumbnailsLayout: VideoLayout = {
138
- screenShareVideo: {size: null},
139
131
  activeSpeakerVideoPaneGroups: [
140
132
  {
141
133
  id: 'thumbnails',
@@ -161,6 +153,7 @@ const Stage2x2With6ThumbnailsLayout: VideoLayout = {
161
153
  export const DefaultConfiguration: Configuration = {
162
154
  audio: {
163
155
  numOfActiveSpeakerStreams: 3,
156
+ numOfScreenShareStreams: 0, // todo: SPARK-393485: change to 1 once backend supports screen sharing
164
157
  },
165
158
  video: {
166
159
  preferLiveVideo: true,
@@ -174,16 +167,12 @@ export const DefaultConfiguration: Configuration = {
174
167
  ScreenShareView: RemoteScreenShareWithSmallThumbnailsLayout,
175
168
  },
176
169
  },
177
- screenShare: {
178
- audio: true,
179
- video: true,
180
- },
181
170
  };
182
171
 
183
172
  export enum Event {
184
173
  // events for audio streams
185
174
  AudioCreated = 'AudioCreated',
186
- ScreenShareAudioCreated = 'ScreenShareCreated',
175
+ ScreenShareAudioCreated = 'ScreenShareAudioCreated',
187
176
 
188
177
  // events for video streams
189
178
  VideoLayoutChanged = 'VideoLayoutChanged',
@@ -200,7 +189,7 @@ export interface VideoLayoutChangedEventData {
200
189
  export interface Events extends EventMap {
201
190
  // audio
202
191
  [Event.AudioCreated]: (audio: RemoteMediaGroup) => void;
203
- [Event.ScreenShareAudioCreated]: (screenShareAudio: RemoteMedia) => void;
192
+ [Event.ScreenShareAudioCreated]: (screenShareAudio: RemoteMediaGroup) => void;
204
193
 
205
194
  // video
206
195
  [Event.VideoLayoutChanged]: (data: VideoLayoutChangedEventData) => void;
@@ -224,14 +213,18 @@ export class RemoteMediaManager extends EventsScope {
224
213
  private mediaRequestManagers: {
225
214
  audio: MediaRequestManager;
226
215
  video: MediaRequestManager;
216
+ screenShareAudio: MediaRequestManager;
217
+ screenShareVideo: MediaRequestManager;
227
218
  };
228
219
 
229
220
  private currentLayout?: VideoLayout;
230
221
 
231
222
  private slots: {
232
223
  audio: ReceiveSlot[];
233
- screenShareAudio?: ReceiveSlot;
234
- screenShareVideo?: ReceiveSlot;
224
+ screenShare: {
225
+ audio: ReceiveSlot[];
226
+ video?: ReceiveSlot;
227
+ };
235
228
  video: {
236
229
  unused: ReceiveSlot[];
237
230
  activeSpeaker: ReceiveSlot[];
@@ -247,6 +240,10 @@ export class RemoteMediaManager extends EventsScope {
247
240
  };
248
241
  memberPanes: {[key: PaneId]: RemoteMedia};
249
242
  };
243
+ screenShare: {
244
+ audio?: RemoteMediaGroup;
245
+ video?: RemoteMediaGroup;
246
+ };
250
247
  };
251
248
 
252
249
  private receiveSlotAllocations: {
@@ -268,6 +265,8 @@ export class RemoteMediaManager extends EventsScope {
268
265
  mediaRequestManagers: {
269
266
  audio: MediaRequestManager;
270
267
  video: MediaRequestManager;
268
+ screenShareAudio: MediaRequestManager;
269
+ screenShareVideo: MediaRequestManager;
271
270
  },
272
271
  config: Configuration = DefaultConfiguration
273
272
  ) {
@@ -282,14 +281,20 @@ export class RemoteMediaManager extends EventsScope {
282
281
  activeSpeakerGroups: {},
283
282
  memberPanes: {},
284
283
  },
284
+ screenShare: {
285
+ audio: undefined,
286
+ video: undefined,
287
+ },
285
288
  };
286
289
 
287
290
  this.checkConfigValidity();
288
291
 
289
292
  this.slots = {
290
293
  audio: [],
291
- screenShareAudio: undefined,
292
- screenShareVideo: undefined,
294
+ screenShare: {
295
+ audio: [],
296
+ video: undefined,
297
+ },
293
298
  video: {
294
299
  unused: [],
295
300
  activeSpeaker: [],
@@ -360,8 +365,8 @@ export class RemoteMediaManager extends EventsScope {
360
365
 
361
366
  await this.createAudioMedia();
362
367
 
363
- // todo: create screen share audio remote media (SPARK-377812)
364
- // todo: create screen share video receive slot (SPARK-377812)
368
+ await this.createScreenShareReceiveSlots();
369
+ this.createScreenShareAudioMedia();
365
370
 
366
371
  await this.preallocateVideoReceiveSlots();
367
372
 
@@ -374,14 +379,27 @@ export class RemoteMediaManager extends EventsScope {
374
379
  */
375
380
  public stop() {
376
381
  // invalidate all remoteMedia objects
377
- this.invalidateCurrentRemoteMedia({audio: true, video: true, commit: true});
382
+ this.invalidateCurrentRemoteMedia({
383
+ audio: true,
384
+ video: true,
385
+ screenShareAudio: true,
386
+ screenShareVideo: true,
387
+ commit: true,
388
+ });
378
389
 
379
390
  // release all audio receive slots
380
391
  this.slots.audio.forEach((slot) => this.receiveSlotManager.releaseSlot(slot));
381
392
  this.slots.audio.length = 0;
382
393
 
383
- // todo: screenshare slots... (SPARK-377812)
394
+ // release screen share slots
395
+ this.slots.screenShare.audio.forEach((slot) => this.receiveSlotManager.releaseSlot(slot));
396
+ this.slots.screenShare.audio.length = 0;
397
+ if (this.slots.screenShare.video) {
398
+ this.receiveSlotManager.releaseSlot(this.slots.screenShare.video);
399
+ this.slots.screenShare.video = undefined;
400
+ }
384
401
 
402
+ // release video slots
385
403
  this.receiveSlotAllocations = {activeSpeaker: {}, receiverSelected: {}};
386
404
 
387
405
  this.slots.video.unused.push(...this.slots.video.activeSpeaker);
@@ -434,7 +452,7 @@ export class RemoteMediaManager extends EventsScope {
434
452
  // eslint-disable-next-line no-await-in-loop
435
453
  this.slots.video.unused.push(
436
454
  // eslint-disable-next-line no-await-in-loop
437
- await this.receiveSlotManager.allocateSlot(MC.MediaType.VideoMain)
455
+ await this.receiveSlotManager.allocateSlot(MediaType.VideoMain)
438
456
  );
439
457
  }
440
458
  }
@@ -459,6 +477,7 @@ export class RemoteMediaManager extends EventsScope {
459
477
 
460
478
  await this.updateVideoReceiveSlots();
461
479
  this.updateVideoRemoteMediaObjects();
480
+ this.updateScreenShareVideoRemoteMediaObject();
462
481
  this.emitVideoLayoutChangedEvent();
463
482
  }
464
483
 
@@ -478,7 +497,7 @@ export class RemoteMediaManager extends EventsScope {
478
497
  // create the audio receive slots
479
498
  for (let i = 0; i < this.config.audio.numOfActiveSpeakerStreams; i += 1) {
480
499
  // eslint-disable-next-line no-await-in-loop
481
- const slot = await this.receiveSlotManager.allocateSlot(MC.MediaType.AudioMain);
500
+ const slot = await this.receiveSlotManager.allocateSlot(MediaType.AudioMain);
482
501
 
483
502
  this.slots.audio.push(slot);
484
503
  }
@@ -498,6 +517,50 @@ export class RemoteMediaManager extends EventsScope {
498
517
  );
499
518
  }
500
519
 
520
+ /**
521
+ * Creates receive slots required for receiving screen share audio and video
522
+ */
523
+ private async createScreenShareReceiveSlots() {
524
+ // audio
525
+ for (let i = 0; i < this.config.audio.numOfScreenShareStreams; i += 1) {
526
+ // eslint-disable-next-line no-await-in-loop
527
+ const slot = await this.receiveSlotManager.allocateSlot(MediaType.AudioSlides);
528
+
529
+ this.slots.screenShare.audio.push(slot);
530
+ }
531
+
532
+ // video
533
+ const isAnyLayoutContainingScreenShareVideo = Object.values(this.config.video.layouts).some(
534
+ (layout) => !!layout.screenShareVideo
535
+ );
536
+
537
+ if (isAnyLayoutContainingScreenShareVideo) {
538
+ this.slots.screenShare.video = await this.receiveSlotManager.allocateSlot(
539
+ MediaType.VideoSlides
540
+ );
541
+ }
542
+ }
543
+
544
+ /**
545
+ * Creates RemoteMedia objects for screen share
546
+ */
547
+ private createScreenShareAudioMedia() {
548
+ if (this.slots.screenShare.audio.length > 0) {
549
+ this.media.screenShare.audio = new RemoteMediaGroup(
550
+ this.mediaRequestManagers.screenShareAudio,
551
+ this.slots.screenShare.audio,
552
+ 255,
553
+ true
554
+ );
555
+
556
+ this.emit(
557
+ {file: 'multistream/remoteMediaManager', function: 'createScreenShareAudioMedia'},
558
+ Event.ScreenShareAudioCreated,
559
+ this.media.screenShare.audio
560
+ );
561
+ }
562
+ }
563
+
501
564
  /**
502
565
  * Goes over all receiver-selected slots and keeps only the ones that are required by a given layout,
503
566
  * the rest are all moved to the "unused" list
@@ -603,7 +666,7 @@ export class RemoteMediaManager extends EventsScope {
603
666
  // eslint-disable-next-line no-await-in-loop
604
667
  this.slots.video.unused.push(
605
668
  // eslint-disable-next-line no-await-in-loop
606
- await this.receiveSlotManager.allocateSlot(MC.MediaType.VideoMain)
669
+ await this.receiveSlotManager.allocateSlot(MediaType.VideoMain)
607
670
  );
608
671
  numSlotsToCreate -= 1;
609
672
  }
@@ -634,7 +697,13 @@ export class RemoteMediaManager extends EventsScope {
634
697
  */
635
698
  private updateVideoRemoteMediaObjects() {
636
699
  // invalidate all the previous remote media objects and cancel their media requests
637
- this.invalidateCurrentRemoteMedia({audio: false, video: true, commit: false});
700
+ this.invalidateCurrentRemoteMedia({
701
+ audio: false,
702
+ video: true,
703
+ screenShareAudio: false,
704
+ screenShareVideo: false,
705
+ commit: false,
706
+ });
638
707
 
639
708
  // create new remoteMediaGroup objects
640
709
  this.media.video.activeSpeakerGroups = {};
@@ -689,16 +758,53 @@ export class RemoteMediaManager extends EventsScope {
689
758
  );
690
759
  }
691
760
  }
692
- // todo: screenshare (SPARK-377812)
693
761
 
694
762
  this.mediaRequestManagers.video.commit();
695
763
  }
696
764
 
765
+ /**
766
+ * Checks if current layout requires a screen share.
767
+ * If it does, it creates new RemoteMediaGroup object for screen share
768
+ * and sends the media requests for it.
769
+ * If it doesn't, it makes sure we clean up any RemoteMediaGroup objects
770
+ * created earlier for screen share (for previous layout).
771
+ */
772
+ private updateScreenShareVideoRemoteMediaObject() {
773
+ this.invalidateCurrentRemoteMedia({
774
+ audio: false,
775
+ video: false,
776
+ screenShareAudio: false,
777
+ screenShareVideo: true,
778
+ commit: false,
779
+ });
780
+
781
+ this.media.screenShare.video = undefined;
782
+
783
+ if (this.currentLayout?.screenShareVideo) {
784
+ // we create a group of 1, because for screen share we need to use the "active speaker" policy
785
+ this.media.screenShare.video = new RemoteMediaGroup(
786
+ this.mediaRequestManagers.screenShareVideo,
787
+ [this.slots.screenShare.video],
788
+ 255,
789
+ false,
790
+ {resolution: this.currentLayout.screenShareVideo.size}
791
+ );
792
+ }
793
+
794
+ this.mediaRequestManagers.screenShareVideo.commit();
795
+ }
796
+
697
797
  /**
698
798
  * Invalidates all remote media objects belonging to currently selected layout
699
799
  */
700
- private invalidateCurrentRemoteMedia(options: {audio: boolean; video: boolean; commit: boolean}) {
701
- const {audio, video, commit} = options;
800
+ private invalidateCurrentRemoteMedia(options: {
801
+ audio: boolean;
802
+ video: boolean;
803
+ screenShareAudio: boolean;
804
+ screenShareVideo: boolean;
805
+ commit: boolean;
806
+ }) {
807
+ const {audio, video, screenShareAudio, screenShareVideo, commit} = options;
702
808
 
703
809
  if (audio && this.media.audio) {
704
810
  this.media.audio.stop(commit);
@@ -714,12 +820,19 @@ export class RemoteMediaManager extends EventsScope {
714
820
  this.mediaRequestManagers.video.commit();
715
821
  }
716
822
  }
823
+
824
+ if (screenShareAudio && this.media.screenShare.audio) {
825
+ this.media.screenShare.audio.stop(commit);
826
+ }
827
+ if (screenShareVideo && this.media.screenShare.video) {
828
+ this.media.screenShare.video.stop(commit);
829
+ }
717
830
  }
718
831
 
719
832
  /** emits Event.VideoLayoutChanged */
720
833
  private emitVideoLayoutChangedEvent() {
721
834
  // todo: at this point the receive slots might still be showing a participant from previous layout, we should
722
- // wait for our media requests to be fullfilled, but there is no API for that right now (we could wait for source updates
835
+ // wait for our media requests to be fulfilled, but there is no API for that right now (we could wait for source updates
723
836
  // but in some cases they might never come, or would need to always make sure to use a new set of receiver slots)
724
837
  // for now it's fine to have it like this, we will re-evaluate if it needs improving after more testing
725
838
 
@@ -733,7 +846,7 @@ export class RemoteMediaManager extends EventsScope {
733
846
  layoutId: this.currentLayoutId,
734
847
  activeSpeakerVideoPanes: this.media.video.activeSpeakerGroups,
735
848
  memberVideoPanes: this.media.video.memberPanes,
736
- screenShareVideo: undefined, // todo: screen share (SPARK-377812)
849
+ screenShareVideo: this.media.screenShare.video?.getRemoteMedia()[0],
737
850
  }
738
851
  );
739
852
  }
@@ -781,7 +894,7 @@ export class RemoteMediaManager extends EventsScope {
781
894
 
782
895
  this.currentLayout.memberVideoPanes.push(newPane);
783
896
 
784
- const receiveSlot = await this.receiveSlotManager.allocateSlot(MC.MediaType.VideoMain);
897
+ const receiveSlot = await this.receiveSlotManager.allocateSlot(MediaType.VideoMain);
785
898
 
786
899
  this.slots.video.receiverSelected.push(receiveSlot);
787
900
 
@@ -19,6 +19,7 @@ const VIDEO_MESH_TIMEOUT = 1000;
19
19
  * @export
20
20
  */
21
21
  export default class Reachability {
22
+ namespace = REACHABILITY.namespace;
22
23
  webex: object;
23
24
  reachabilityRequest: any;
24
25
  clusterLatencyResults: any;
@@ -61,15 +62,8 @@ export default class Reachability {
61
62
  this.setup();
62
63
 
63
64
  // Remove stored reachability results to ensure no stale data
64
- if (window?.localStorage?.removeItem) {
65
- window.localStorage.removeItem(REACHABILITY.localStorage);
66
- } else {
67
- LoggerProxy.logger.error(
68
- 'Reachability:index#gatherReachability --> Error in accessing LocalStorage.'
69
- );
70
-
71
- return {};
72
- }
65
+ // @ts-ignore
66
+ await this.webex.boundedStorage.del(this.namespace, REACHABILITY.localStorage);
73
67
 
74
68
  // Fetch clusters and measure latency
75
69
  try {
@@ -78,7 +72,12 @@ export default class Reachability {
78
72
  // Perform Reachability Check
79
73
  const results = await this.performReachabilityCheck(clusters);
80
74
 
81
- window.localStorage.setItem(REACHABILITY.localStorage, JSON.stringify(results));
75
+ // @ts-ignore
76
+ await this.webex.boundedStorage.put(
77
+ this.namespace,
78
+ REACHABILITY.localStorage,
79
+ JSON.stringify(results)
80
+ );
82
81
 
83
82
  LoggerProxy.logger.log(
84
83
  'Reachability:index#gatherReachability --> Reachability checks completed'
@@ -100,9 +99,12 @@ export default class Reachability {
100
99
  * @public
101
100
  * @memberof Reachability
102
101
  */
103
- isAnyClusterReachable() {
102
+ async isAnyClusterReachable() {
104
103
  let reachable = false;
105
- const reachabilityData = window.localStorage.getItem(REACHABILITY.localStorage);
104
+ // @ts-ignore
105
+ const reachabilityData = await this.webex.boundedStorage
106
+ .get(this.namespace, REACHABILITY.localStorage)
107
+ .catch(() => {});
106
108
 
107
109
  if (reachabilityData) {
108
110
  try {
@@ -261,6 +263,7 @@ export default class Reachability {
261
263
 
262
264
  // @ts-ignore
263
265
  LoggerProxy.logger.log(
266
+ // @ts-ignore
264
267
  `Reachability:index#onIceGatheringStateChange --> Successfully pinged ${peerConnection.key}:`,
265
268
  elapsed
266
269
  );
@@ -284,8 +287,8 @@ export default class Reachability {
284
287
  if (e.candidate && String(e.candidate.type).toLowerCase() === SERVER_REFLEXIVE) {
285
288
  const elapsed = this.getElapsedTime(peerConnection);
286
289
 
287
- // @ts-ignore
288
290
  LoggerProxy.logger.log(
291
+ // @ts-ignore
289
292
  `Reachability:index#onIceCandidate --> Successfully pinged ${peerConnection.key}:`,
290
293
  elapsed
291
294
  );
@@ -0,0 +1,4 @@
1
+ // eslint-disable-next-line import/prefer-default-export
2
+ export const REACTION_RELAY_TYPES = {
3
+ REACTION: 'react',
4
+ } as const;
@@ -1,3 +1,5 @@
1
+ import {REACTION_RELAY_TYPES} from './constants';
2
+
1
3
  export type EmoticonData = {
2
4
  type: string;
3
5
  codepoints?: string;
@@ -5,6 +7,7 @@ export type EmoticonData = {
5
7
  };
6
8
 
7
9
  export type SkinTone = EmoticonData;
10
+
8
11
  export type Reaction = EmoticonData & {
9
12
  tone?: SkinTone;
10
13
  };
@@ -35,3 +38,25 @@ export enum SkinToneType {
35
38
  medium_dark = 'medium_dark',
36
39
  dark = 'dark',
37
40
  }
41
+
42
+ export type Sender = {
43
+ participantId: string;
44
+ };
45
+
46
+ export type ProcessedReaction = {
47
+ reaction: Reaction;
48
+ sender: {
49
+ id: Sender['participantId'];
50
+ name: string;
51
+ };
52
+ };
53
+
54
+ type RelayEventData = {
55
+ relayType: (typeof REACTION_RELAY_TYPES)['REACTION'];
56
+ reaction: Reaction;
57
+ sender: Sender;
58
+ };
59
+
60
+ export type RelayEvent = {
61
+ data: RelayEventData;
62
+ };
@@ -538,19 +538,28 @@ export default class ReconnectionManager {
538
538
  'ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media'
539
539
  );
540
540
 
541
- // we are not simply calling this.meeting.mediaProperties.webrtcMediaConnection.reconnect(),
542
- // but instead manually closing and creating new media connection, because we need to do the TURN discovery again
543
-
544
- await this.meeting.closePeerConnections();
545
- this.meeting.mediaProperties.unsetPeerConnection();
546
-
541
+ // do the TURN server discovery again since the TURN server might change
547
542
  const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true);
548
543
 
549
- const mc = this.meeting.createMediaConnection(turnServerResult.turnServerInfo);
544
+ const iceServers = [];
550
545
 
551
- this.meeting.statsAnalyzer.updateMediaConnection(mc);
546
+ if (turnServerResult.turnServerInfo) {
547
+ iceServers.push({
548
+ urls: turnServerResult.turnServerInfo.url,
549
+ username: turnServerResult.turnServerInfo.username || '',
550
+ credential: turnServerResult.turnServerInfo.password || '',
551
+ });
552
+ }
552
553
 
553
- return mc.initiateOffer();
554
+ await this.meeting.mediaProperties.webrtcMediaConnection.reconnect(iceServers);
555
+
556
+ // resend media requests
557
+ if (this.meeting.isMultistream) {
558
+ Object.values(this.meeting.mediaRequestManagers).forEach((mediaRequestManager) =>
559
+ // @ts-ignore - Fix type
560
+ mediaRequestManager.commit()
561
+ );
562
+ }
554
563
  }
555
564
 
556
565
  /**
@@ -0,0 +1,8 @@
1
+ enum RecordingAction {
2
+ Start = 'Start',
3
+ Stop = 'Stop',
4
+ Pause = 'Pause',
5
+ Resume = 'Resume',
6
+ }
7
+
8
+ export default RecordingAction;