@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.
- package/dist/breakouts/breakout.js +116 -0
- package/dist/breakouts/breakout.js.map +1 -0
- package/dist/breakouts/collection.js +23 -0
- package/dist/breakouts/collection.js.map +1 -0
- package/dist/breakouts/index.js +226 -0
- package/dist/breakouts/index.js.map +1 -0
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +43 -6
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +2 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +48 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +1 -0
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js +19 -11
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +3 -3
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -4
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +5 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +652 -459
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +25 -44
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +22 -57
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -0
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +28 -18
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +14 -12
- package/dist/meetings/request.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +14 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +8 -6
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -1
- package/dist/members/request.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +46 -6
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/multistreamMedia.js +4 -0
- package/dist/multistream/multistreamMedia.js.map +1 -1
- package/dist/multistream/receiveSlot.js +3 -3
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +8 -6
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +168 -63
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +63 -51
- package/dist/reachability/index.js.map +1 -1
- package/dist/reactions/constants.js +13 -0
- package/dist/reactions/constants.js.map +1 -0
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +25 -12
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/enums.js +17 -0
- package/dist/recording-controller/enums.js.map +1 -0
- package/dist/recording-controller/index.js +343 -0
- package/dist/recording-controller/index.js.map +1 -0
- package/dist/recording-controller/util.js +63 -0
- package/dist/recording-controller/util.js.map +1 -0
- package/dist/roap/request.js +88 -68
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +72 -47
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +3 -3
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +18 -6
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/package.json +24 -19
- package/src/breakouts/README.md +190 -0
- package/src/breakouts/breakout.ts +110 -0
- package/src/breakouts/collection.ts +19 -0
- package/src/breakouts/index.ts +225 -0
- package/src/config.ts +4 -1
- package/src/constants.ts +39 -1
- package/src/locus-info/controlsUtils.ts +2 -0
- package/src/locus-info/index.ts +59 -1
- package/src/locus-info/parser.ts +1 -0
- package/src/locus-info/selfUtils.ts +8 -0
- package/src/media/index.ts +1 -2
- package/src/media/properties.ts +6 -9
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +360 -111
- package/src/meeting/request.ts +9 -31
- package/src/meeting/request.type.ts +2 -0
- package/src/meeting/util.ts +25 -60
- package/src/meeting-info/meeting-info-v2.ts +2 -0
- package/src/meetings/index.ts +10 -5
- package/src/meetings/request.ts +1 -1
- package/src/member/index.ts +9 -0
- package/src/member/util.ts +14 -1
- package/src/members/index.ts +1 -0
- package/src/members/request.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +79 -15
- package/src/multistream/multistreamMedia.ts +4 -0
- package/src/multistream/receiveSlot.ts +17 -12
- package/src/multistream/receiveSlotManager.ts +22 -21
- package/src/multistream/remoteMedia.ts +1 -1
- package/src/multistream/remoteMediaGroup.ts +2 -2
- package/src/multistream/remoteMediaManager.ts +150 -37
- package/src/reachability/index.ts +16 -13
- package/src/reactions/constants.ts +4 -0
- package/src/reactions/reactions.type.ts +25 -0
- package/src/reconnection-manager/index.ts +18 -9
- package/src/recording-controller/enums.ts +8 -0
- package/src/recording-controller/index.ts +315 -0
- package/src/recording-controller/util.ts +58 -0
- package/src/roap/request.ts +78 -73
- package/src/roap/turnDiscovery.ts +8 -6
- package/src/statsAnalyzer/index.ts +4 -4
- package/src/statsAnalyzer/mqaUtil.ts +6 -0
- package/test/unit/spec/breakouts/breakout.ts +119 -0
- package/test/unit/spec/breakouts/collection.ts +15 -0
- package/test/unit/spec/breakouts/index.ts +293 -0
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +103 -0
- package/test/unit/spec/locus-info/selfConstant.js +25 -0
- package/test/unit/spec/locus-info/selfUtils.js +84 -0
- package/test/unit/spec/media/index.ts +1 -1
- package/test/unit/spec/media/properties.ts +9 -9
- package/test/unit/spec/meeting/effectsState.js +5 -1
- package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
- package/test/unit/spec/meeting/index.js +241 -50
- package/test/unit/spec/meeting/request.js +17 -0
- package/test/unit/spec/meeting/utils.js +28 -122
- package/test/unit/spec/meetings/index.js +1 -0
- package/test/unit/spec/member/util.js +26 -1
- package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
- package/test/unit/spec/multistream/receiveSlot.ts +6 -6
- package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
- package/test/unit/spec/multistream/remoteMedia.ts +2 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
- package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
- package/test/unit/spec/reachability/index.ts +58 -24
- package/test/unit/spec/reconnection-manager/index.js +42 -13
- package/test/unit/spec/recording-controller/index.js +231 -0
- package/test/unit/spec/recording-controller/util.js +102 -0
- package/test/unit/spec/roap/index.ts +2 -1
- package/test/unit/spec/roap/request.ts +114 -0
- package/test/unit/spec/roap/turnDiscovery.ts +45 -29
- package/test/unit/spec/stats-analyzer/index.js +2 -2
- package/test/utils/webex-test-users.js +1 -0
- package/tsconfig.json +6 -0
- package/dist/media/internal-media-core-wrapper.js +0 -18
- package/dist/media/internal-media-core-wrapper.js.map +0 -1
- 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 {
|
|
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
|
|
15
|
+
private allocatedSlots: {[key in MediaType]: ReceiveSlot[]};
|
|
16
16
|
|
|
17
|
-
private freeSlots: {[key in
|
|
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
|
-
[
|
|
28
|
-
[
|
|
29
|
-
[
|
|
30
|
-
[
|
|
27
|
+
[MediaType.AudioMain]: [],
|
|
28
|
+
[MediaType.VideoMain]: [],
|
|
29
|
+
[MediaType.AudioSlides]: [],
|
|
30
|
+
[MediaType.VideoSlides]: [],
|
|
31
31
|
};
|
|
32
32
|
this.freeSlots = {
|
|
33
|
-
[
|
|
34
|
-
[
|
|
35
|
-
[
|
|
36
|
-
[
|
|
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 {
|
|
44
|
+
* @param {MediaType} mediaType
|
|
45
45
|
* @returns {Promise<ReceiveSlot>}
|
|
46
46
|
*/
|
|
47
|
-
async allocateSlot(mediaType:
|
|
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
|
-
[
|
|
105
|
-
[
|
|
106
|
-
[
|
|
107
|
-
[
|
|
105
|
+
[MediaType.AudioMain]: [],
|
|
106
|
+
[MediaType.VideoMain]: [],
|
|
107
|
+
[MediaType.AudioSlides]: [],
|
|
108
|
+
[MediaType.VideoSlides]: [],
|
|
108
109
|
};
|
|
109
110
|
this.freeSlots = {
|
|
110
|
-
[
|
|
111
|
-
[
|
|
112
|
-
[
|
|
113
|
-
[
|
|
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
|
|
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
|
|
12
|
-
preferLiveVideo?: boolean; // applies only to groups of type
|
|
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 {
|
|
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
|
|
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 = '
|
|
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:
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
|
|
292
|
-
|
|
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
|
-
|
|
364
|
-
|
|
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({
|
|
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
|
-
//
|
|
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(
|
|
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(
|
|
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(
|
|
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({
|
|
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: {
|
|
701
|
-
|
|
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
|
|
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:
|
|
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(
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
);
|
|
@@ -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
|
-
//
|
|
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
|
|
544
|
+
const iceServers = [];
|
|
550
545
|
|
|
551
|
-
|
|
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
|
-
|
|
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
|
/**
|