@webex/plugin-meetings 3.9.0-webinar5k.1 → 3.9.0

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 (83) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +16 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/interpretation/index.js +1 -1
  6. package/dist/interpretation/siLanguage.js +1 -1
  7. package/dist/locus-info/index.js +40 -328
  8. package/dist/locus-info/index.js.map +1 -1
  9. package/dist/meeting/in-meeting-actions.js +6 -0
  10. package/dist/meeting/in-meeting-actions.js.map +1 -1
  11. package/dist/meeting/index.js +196 -160
  12. package/dist/meeting/index.js.map +1 -1
  13. package/dist/meeting/muteState.js +5 -2
  14. package/dist/meeting/muteState.js.map +1 -1
  15. package/dist/meeting/type.js +7 -0
  16. package/dist/meeting/type.js.map +1 -0
  17. package/dist/meeting/util.js +79 -10
  18. package/dist/meeting/util.js.map +1 -1
  19. package/dist/meetings/index.js +37 -39
  20. package/dist/meetings/index.js.map +1 -1
  21. package/dist/member/types.js.map +1 -1
  22. package/dist/members/collection.js +0 -13
  23. package/dist/members/collection.js.map +1 -1
  24. package/dist/members/index.js +21 -40
  25. package/dist/members/index.js.map +1 -1
  26. package/dist/members/util.js.map +1 -1
  27. package/dist/multistream/mediaRequestManager.js +1 -1
  28. package/dist/multistream/mediaRequestManager.js.map +1 -1
  29. package/dist/multistream/remoteMedia.js +34 -5
  30. package/dist/multistream/remoteMedia.js.map +1 -1
  31. package/dist/multistream/remoteMediaGroup.js +42 -2
  32. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  33. package/dist/types/constants.d.ts +16 -0
  34. package/dist/types/locus-info/index.d.ts +3 -102
  35. package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
  36. package/dist/types/meeting/index.d.ts +23 -28
  37. package/dist/types/meeting/type.d.ts +9 -0
  38. package/dist/types/meeting/util.d.ts +6 -3
  39. package/dist/types/member/types.d.ts +0 -1
  40. package/dist/types/members/collection.d.ts +0 -6
  41. package/dist/types/members/index.d.ts +7 -16
  42. package/dist/types/members/util.d.ts +2 -1
  43. package/dist/types/multistream/remoteMedia.d.ts +20 -1
  44. package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
  45. package/dist/webinar/index.js +1 -1
  46. package/package.json +22 -24
  47. package/src/constants.ts +16 -2
  48. package/src/locus-info/index.ts +39 -409
  49. package/src/meeting/in-meeting-actions.ts +13 -0
  50. package/src/meeting/index.ts +92 -63
  51. package/src/meeting/muteState.ts +6 -2
  52. package/src/meeting/type.ts +9 -0
  53. package/src/meeting/util.ts +93 -19
  54. package/src/meetings/index.ts +6 -19
  55. package/src/member/types.ts +0 -1
  56. package/src/members/collection.ts +0 -11
  57. package/src/members/index.ts +10 -33
  58. package/src/members/util.ts +2 -1
  59. package/src/multistream/mediaRequestManager.ts +7 -7
  60. package/src/multistream/remoteMedia.ts +34 -4
  61. package/src/multistream/remoteMediaGroup.ts +37 -2
  62. package/test/unit/spec/locus-info/index.js +8 -365
  63. package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
  64. package/test/unit/spec/meeting/index.js +254 -38
  65. package/test/unit/spec/meeting/utils.js +122 -1
  66. package/test/unit/spec/meetings/index.js +2 -0
  67. package/test/unit/spec/members/index.js +37 -1
  68. package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
  69. package/test/unit/spec/multistream/remoteMedia.ts +66 -2
  70. package/dist/hashTree/constants.js +0 -23
  71. package/dist/hashTree/constants.js.map +0 -1
  72. package/dist/hashTree/hashTree.js +0 -516
  73. package/dist/hashTree/hashTree.js.map +0 -1
  74. package/dist/hashTree/hashTreeParser.js +0 -521
  75. package/dist/hashTree/hashTreeParser.js.map +0 -1
  76. package/dist/types/hashTree/constants.d.ts +0 -8
  77. package/dist/types/hashTree/hashTree.d.ts +0 -128
  78. package/dist/types/hashTree/hashTreeParser.d.ts +0 -152
  79. package/src/hashTree/constants.ts +0 -12
  80. package/src/hashTree/hashTree.ts +0 -460
  81. package/src/hashTree/hashTreeParser.ts +0 -556
  82. package/test/unit/spec/hashTree/hashTree.ts +0 -394
  83. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -156
@@ -39,17 +39,6 @@ export default class MembersCollection {
39
39
  return this.members;
40
40
  }
41
41
 
42
- /**
43
- * Removes a member from the collection
44
- * @param {String} id
45
- * @returns {void}
46
- */
47
- remove(id: string) {
48
- if (this.members[id]) {
49
- delete this.members[id];
50
- }
51
- }
52
-
53
42
  /**
54
43
  * @returns {void}
55
44
  * reset members
@@ -30,6 +30,7 @@ import MembersUtil from './util';
30
30
  import {ReceiveSlotManager} from '../multistream/receiveSlotManager';
31
31
  import {MediaRequestManager} from '../multistream/mediaRequestManager';
32
32
  import {ServerRoleShape} from './types';
33
+ import {Invitee} from '../meeting/type';
33
34
 
34
35
  /**
35
36
  * Members Update Event
@@ -73,11 +74,7 @@ import {ServerRoleShape} from './types';
73
74
  * @memberof Members
74
75
  */
75
76
 
76
- type UpdatedMembers = {
77
- added: Array<Member>;
78
- updated: Array<Member>;
79
- removedIds?: Array<string>; // removed member ids
80
- };
77
+ type UpdatedMembers = {added: Array<Member>; updated: Array<Member>};
81
78
  /**
82
79
  * @class Members
83
80
  */
@@ -392,11 +389,7 @@ export default class Members extends StatelessWebexPlugin {
392
389
  * @private
393
390
  * @memberof Members
394
391
  */
395
- locusParticipantsUpdate(payload: {
396
- participants: object;
397
- isReplace?: boolean;
398
- removedParticipantIds?: Array<string>;
399
- }) {
392
+ locusParticipantsUpdate(payload: {participants: object; isReplace?: boolean}) {
400
393
  if (payload) {
401
394
  if (payload.isReplace) {
402
395
  this.clearMembers();
@@ -554,22 +547,10 @@ export default class Members extends StatelessWebexPlugin {
554
547
  private handleMembersUpdate(membersUpdate: UpdatedMembers) {
555
548
  this.constructMembers(membersUpdate.updated, true);
556
549
  this.constructMembers(membersUpdate.added, false);
557
- this.removeMembers(membersUpdate.removedIds);
558
550
 
559
551
  return this.membersCollection.getAll();
560
552
  }
561
553
 
562
- /**
563
- * removes members from the collection
564
- * @param {Array<string>} removedMembers removed members ids
565
- * @returns {void}
566
- */
567
- private removeMembers(removedMembers: Array<string>) {
568
- removedMembers.forEach((memberId) => {
569
- this.membersCollection.remove(memberId);
570
- });
571
- }
572
-
573
554
  /**
574
555
  * set members to the member collection from each updated/added lists as passed in
575
556
  * @param {Array} list
@@ -619,10 +600,6 @@ export default class Members extends StatelessWebexPlugin {
619
600
  }
620
601
  const memberUpdate = this.update(payload.participants);
621
602
 
622
- // this code depends on memberIds being the same as participantIds
623
- // if MemberUtil.extractId() ever changes, this will need to be updated
624
- memberUpdate.removedIds = payload.removedParticipantIds || [];
625
-
626
603
  return memberUpdate;
627
604
  }
628
605
 
@@ -824,18 +801,18 @@ export default class Members extends StatelessWebexPlugin {
824
801
 
825
802
  /**
826
803
  * Adds a guest Member to the associated meeting
827
- * @param {String} invitee
804
+ * @param {Invitee} invitee
828
805
  * @param {Boolean} [alertIfActive]
829
806
  * @returns {Promise}
830
807
  * @memberof Members
831
808
  */
832
- addMember(invitee: any, alertIfActive?: boolean) {
809
+ addMember(invitee: Invitee, alertIfActive?: boolean) {
833
810
  if (!this.locusUrl) {
834
811
  return Promise.reject(
835
812
  new ParameterError('The associated locus url for this meeting object must be defined.')
836
813
  );
837
814
  }
838
- if (MembersUtil.isInvalidInvitee(invitee)) {
815
+ if (invitee?.skipEmailValidation !== true && MembersUtil.isInvalidInvitee(invitee)) {
839
816
  return Promise.reject(
840
817
  new ParameterError(
841
818
  'The invitee must be defined with either a valid email, emailAddress or phoneNumber property.'
@@ -849,11 +826,11 @@ export default class Members extends StatelessWebexPlugin {
849
826
 
850
827
  /**
851
828
  * Cancels an outgoing PSTN call to the associated meeting
852
- * @param {String} invitee
829
+ * @param {Invitee} invitee
853
830
  * @returns {Promise}
854
831
  * @memberof Members
855
832
  */
856
- cancelPhoneInvite(invitee: any) {
833
+ cancelPhoneInvite(invitee: Invitee) {
857
834
  if (!this.locusUrl) {
858
835
  return Promise.reject(
859
836
  new ParameterError('The associated locus url for this meeting object must be defined.')
@@ -871,13 +848,13 @@ export default class Members extends StatelessWebexPlugin {
871
848
 
872
849
  /**
873
850
  * Cancels an SIP/phone call to the associated meeting
874
- * @param {Object} invitee
851
+ * @param {Invitee} invitee
875
852
  * @param {String} invitee.memberId - The memberId of the invitee
876
853
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
877
854
  * @returns {Promise}
878
855
  * @memberof Members
879
856
  */
880
- cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
857
+ cancelInviteByMemberId(invitee: Invitee) {
881
858
  if (!this.locusUrl) {
882
859
  return Promise.reject(
883
860
  new ParameterError('The associated locus url for this meeting object must be defined.')
@@ -14,6 +14,7 @@ import {
14
14
  } from '../constants';
15
15
 
16
16
  import {RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape} from './types';
17
+ import {Invitee} from '../meeting/type';
17
18
 
18
19
  const MembersUtil = {
19
20
  /**
@@ -105,7 +106,7 @@ const MembersUtil = {
105
106
  return requestParams;
106
107
  },
107
108
 
108
- isInvalidInvitee: (invitee) => {
109
+ isInvalidInvitee: (invitee: Invitee) => {
109
110
  if (!(invitee && (invitee.email || invitee.emailAddress || invitee.phoneNumber))) {
110
111
  return true;
111
112
  }
@@ -15,7 +15,7 @@ import {cloneDeepWith, debounce, isEmpty} from 'lodash';
15
15
  import LoggerProxy from '../common/logs/logger-proxy';
16
16
 
17
17
  import {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';
18
- import {getMaxFs} from './remoteMedia';
18
+ import {MAX_FS_VALUES} from './remoteMedia';
19
19
 
20
20
  export interface ActiveSpeakerPolicyInfo {
21
21
  policy: 'active-speaker';
@@ -123,12 +123,12 @@ export class MediaRequestManager {
123
123
 
124
124
  private getDegradedClientRequests(clientRequests: ClientRequestsMap) {
125
125
  const maxFsLimits = [
126
- getMaxFs('best'),
127
- getMaxFs('large'),
128
- getMaxFs('medium'),
129
- getMaxFs('small'),
130
- getMaxFs('very small'),
131
- getMaxFs('thumbnail'),
126
+ MAX_FS_VALUES['1080p'],
127
+ MAX_FS_VALUES['720p'],
128
+ MAX_FS_VALUES['540p'],
129
+ MAX_FS_VALUES['360p'],
130
+ MAX_FS_VALUES['180p'],
131
+ MAX_FS_VALUES['90p'],
132
132
  ];
133
133
 
134
134
  // reduce max-fs until total macroblocks is below limit
@@ -19,17 +19,18 @@ export type RemoteVideoResolution =
19
19
  | 'large' // 1080p or less
20
20
  | 'best'; // highest possible resolution
21
21
 
22
- const MAX_FS_VALUES = {
22
+ export const MAX_FS_VALUES = {
23
23
  '90p': 60,
24
24
  '180p': 240,
25
25
  '360p': 920,
26
+ '540p': 2040,
26
27
  '720p': 3600,
27
28
  '1080p': 8192,
28
29
  };
29
30
 
30
31
  /**
31
32
  * Converts pane size into h264 maxFs
32
- * @param {PaneSize} paneSize
33
+ * @param {RemoteVideoResolution} paneSize
33
34
  * @returns {number}
34
35
  */
35
36
  export function getMaxFs(paneSize: RemoteVideoResolution): number {
@@ -89,6 +90,13 @@ export class RemoteMedia extends EventsScope {
89
90
 
90
91
  public readonly id: RemoteMediaId;
91
92
 
93
+ /**
94
+ * The max frame size of the media request, used for logging and media requests.
95
+ * Set by setSizeHint() based on video element dimensions.
96
+ * When > 0, this value takes precedence over options.resolution in sendMediaRequest().
97
+ */
98
+ private maxFrameSize = 0;
99
+
92
100
  /**
93
101
  * Constructs RemoteMedia instance
94
102
  *
@@ -136,15 +144,34 @@ export class RemoteMedia extends EventsScope {
136
144
  fs = MAX_FS_VALUES['180p'];
137
145
  } else if (height < getThresholdHeight(360)) {
138
146
  fs = MAX_FS_VALUES['360p'];
147
+ } else if (height < getThresholdHeight(540)) {
148
+ fs = MAX_FS_VALUES['540p'];
139
149
  } else if (height <= 720) {
140
150
  fs = MAX_FS_VALUES['720p'];
141
151
  } else {
142
152
  fs = MAX_FS_VALUES['1080p'];
143
153
  }
144
154
 
155
+ this.maxFrameSize = fs;
145
156
  this.receiveSlot?.setMaxFs(fs);
146
157
  }
147
158
 
159
+ /**
160
+ * Get the current effective maxFs value that would be used in media requests
161
+ * @returns {number | undefined} The maxFs value, or undefined if no constraints
162
+ */
163
+ public getEffectiveMaxFs(): number | undefined {
164
+ if (this.maxFrameSize > 0) {
165
+ return this.maxFrameSize;
166
+ }
167
+
168
+ if (this.options.resolution) {
169
+ return getMaxFs(this.options.resolution);
170
+ }
171
+
172
+ return undefined;
173
+ }
174
+
148
175
  /**
149
176
  * Invalidates the remote media by clearing the reference to a receive slot and
150
177
  * cancelling the media request.
@@ -185,6 +212,9 @@ export class RemoteMedia extends EventsScope {
185
212
  throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');
186
213
  }
187
214
 
215
+ // Use maxFrameSize from setSizeHint if available, otherwise fallback to options.resolution
216
+ const maxFs = this.getEffectiveMaxFs();
217
+
188
218
  this.mediaRequestId = this.mediaRequestManager.addRequest(
189
219
  {
190
220
  policyInfo: {
@@ -192,9 +222,9 @@ export class RemoteMedia extends EventsScope {
192
222
  csi,
193
223
  },
194
224
  receiveSlots: [this.receiveSlot],
195
- codecInfo: this.options.resolution && {
225
+ codecInfo: maxFs && {
196
226
  codec: 'h264',
197
- maxFs: getMaxFs(this.options.resolution),
227
+ maxFs,
198
228
  },
199
229
  },
200
230
  commit
@@ -215,6 +215,9 @@ export class RemoteMediaGroup {
215
215
  private sendActiveSpeakerMediaRequest(commit: boolean) {
216
216
  this.cancelActiveSpeakerMediaRequest(false);
217
217
 
218
+ // Calculate the effective maxFs based on all unpinned RemoteMedia instances
219
+ const effectiveMaxFs = this.getEffectiveMaxFsForActiveSpeaker();
220
+
218
221
  this.mediaRequestId = this.mediaRequestManager.addRequest(
219
222
  {
220
223
  policyInfo: {
@@ -230,9 +233,9 @@ export class RemoteMediaGroup {
230
233
  receiveSlots: this.unpinnedRemoteMedia.map((remoteMedia) =>
231
234
  remoteMedia.getUnderlyingReceiveSlot()
232
235
  ) as ReceiveSlot[],
233
- codecInfo: this.options.resolution && {
236
+ codecInfo: effectiveMaxFs && {
234
237
  codec: 'h264',
235
- maxFs: getMaxFs(this.options.resolution),
238
+ maxFs: effectiveMaxFs,
236
239
  },
237
240
  },
238
241
  commit
@@ -300,4 +303,36 @@ export class RemoteMediaGroup {
300
303
  this.unpinnedRemoteMedia.includes(remoteMedia) || this.pinnedRemoteMedia.includes(remoteMedia)
301
304
  );
302
305
  }
306
+
307
+ /**
308
+ * Calculate the effective maxFs for the active speaker media request based on unpinned RemoteMedia instances
309
+ * @returns {number | undefined} The calculated maxFs value, or undefined if no constraints
310
+ * @private
311
+ */
312
+ private getEffectiveMaxFsForActiveSpeaker(): number | undefined {
313
+ // Get all effective maxFs values from unpinned RemoteMedia instances
314
+ const maxFsValues = this.unpinnedRemoteMedia
315
+ .map((remoteMedia) => remoteMedia.getEffectiveMaxFs())
316
+ .filter((maxFs) => maxFs !== undefined);
317
+
318
+ // Use the highest maxFs value to ensure we don't under-request resolution for any instance
319
+ if (maxFsValues.length > 0) {
320
+ return Math.max(...maxFsValues);
321
+ }
322
+
323
+ // Fall back to group's resolution option
324
+ if (this.options.resolution) {
325
+ return getMaxFs(this.options.resolution);
326
+ }
327
+
328
+ return undefined;
329
+ }
330
+
331
+ /**
332
+ * Get the current effective maxFs that would be used for the active speaker media request
333
+ * @returns {number | undefined} The effective maxFs value
334
+ */
335
+ public getEffectiveMaxFs(): number | undefined {
336
+ return this.getEffectiveMaxFsForActiveSpeaker();
337
+ }
303
338
  }