@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.2
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/common/errors/webex-errors.js +5 -29
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/constants.js +15 -74
- package/dist/constants.js.map +1 -1
- package/dist/media/index.js +68 -213
- package/dist/media/index.js.map +1 -1
- package/dist/media/internal-media-core-wrapper.js +22 -0
- package/dist/media/internal-media-core-wrapper.js.map +1 -0
- package/dist/media/properties.js +20 -25
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js +0 -27
- package/dist/media/util.js.map +1 -1
- package/dist/meeting/index.js +694 -432
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +1 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +3 -44
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +64 -5
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +24 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/members/index.js +68 -0
- package/dist/members/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +132 -0
- package/dist/multistream/mediaRequestManager.js.map +1 -0
- package/dist/multistream/multistreamMedia.js +116 -0
- package/dist/multistream/multistreamMedia.js.map +1 -0
- package/dist/multistream/receiveSlot.js +209 -0
- package/dist/multistream/receiveSlot.js.map +1 -0
- package/dist/multistream/receiveSlotManager.js +195 -0
- package/dist/multistream/receiveSlotManager.js.map +1 -0
- package/dist/multistream/remoteMedia.js +284 -0
- package/dist/multistream/remoteMedia.js.map +1 -0
- package/dist/multistream/remoteMediaGroup.js +243 -0
- package/dist/multistream/remoteMediaGroup.js.map +1 -0
- package/dist/multistream/remoteMediaManager.js +1113 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -0
- package/dist/reconnection-manager/index.js +109 -130
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +57 -240
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +2 -114
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +11 -5
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/global.js +2 -0
- package/dist/statsAnalyzer/global.js.map +1 -1
- package/dist/statsAnalyzer/index.js +39 -36
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/package.json +20 -19
- package/src/common/errors/webex-errors.js +0 -18
- package/src/constants.ts +139 -180
- package/src/media/index.js +60 -194
- package/src/media/internal-media-core-wrapper.ts +9 -0
- package/src/media/properties.js +19 -25
- package/src/media/util.js +0 -22
- package/src/meeting/index.js +565 -320
- package/src/meeting/request.js +1 -0
- package/src/meeting/util.js +3 -46
- package/src/meetings/index.js +30 -1
- package/src/meetings/util.js +23 -2
- package/src/members/index.js +48 -0
- package/src/multistream/mediaRequestManager.ts +164 -0
- package/src/multistream/multistreamMedia.ts +92 -0
- package/src/multistream/receiveSlot.ts +141 -0
- package/src/multistream/receiveSlotManager.ts +142 -0
- package/src/multistream/remoteMedia.ts +219 -0
- package/src/multistream/remoteMediaGroup.ts +224 -0
- package/src/multistream/remoteMediaManager.ts +911 -0
- package/src/reconnection-manager/index.js +40 -53
- package/src/roap/index.js +47 -207
- package/src/roap/request.js +1 -72
- package/src/roap/turnDiscovery.ts +12 -6
- package/src/statsAnalyzer/global.js +2 -0
- package/src/statsAnalyzer/index.js +32 -46
- package/test/integration/spec/journey.js +1 -1
- package/test/unit/spec/media/index.ts +223 -0
- package/test/unit/spec/media/properties.ts +73 -82
- package/test/unit/spec/meeting/effectsState.js +1 -3
- package/test/unit/spec/meeting/index.js +420 -228
- package/test/unit/spec/meeting/muteState.js +7 -0
- package/test/unit/spec/meeting/utils.js +61 -2
- package/test/unit/spec/meetings/index.js +0 -4
- package/test/unit/spec/members/index.js +164 -2
- package/test/unit/spec/multistream/mediaRequestManager.ts +511 -0
- package/test/unit/spec/multistream/receiveSlot.ts +104 -0
- package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
- package/test/unit/spec/multistream/remoteMedia.ts +217 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +1251 -0
- package/test/unit/spec/roap/index.ts +63 -35
- package/test/unit/spec/stats-analyzer/index.js +19 -22
- package/dist/peer-connection-manager/index.js +0 -794
- package/dist/peer-connection-manager/index.js.map +0 -1
- package/dist/roap/collection.js +0 -73
- package/dist/roap/collection.js.map +0 -1
- package/dist/roap/handler.js +0 -337
- package/dist/roap/handler.js.map +0 -1
- package/dist/roap/state.js +0 -164
- package/dist/roap/state.js.map +0 -1
- package/dist/roap/util.js +0 -102
- package/dist/roap/util.js.map +0 -1
- package/src/peer-connection-manager/index.js +0 -723
- package/src/roap/collection.js +0 -63
- package/src/roap/handler.js +0 -252
- package/src/roap/state.js +0 -149
- package/src/roap/util.js +0 -93
- package/test/unit/spec/peerconnection-manager/index.js +0 -188
- package/test/unit/spec/peerconnection-manager/utils.js +0 -48
- package/test/unit/spec/roap/util.js +0 -30
|
@@ -18,11 +18,9 @@ import {
|
|
|
18
18
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
19
19
|
import ReconnectionError from '../common/errors/reconnection';
|
|
20
20
|
import ReconnectInProgress from '../common/errors/reconnection-in-progress';
|
|
21
|
-
import PeerConnectionManager from '../peer-connection-manager';
|
|
22
21
|
import {eventType, reconnection, errorObjects} from '../metrics/config';
|
|
23
22
|
import Media from '../media';
|
|
24
23
|
import Metrics from '../metrics';
|
|
25
|
-
import RoapCollection from '../roap/collection';
|
|
26
24
|
|
|
27
25
|
/**
|
|
28
26
|
* Used to indicate that the reconnect logic needs to be retried.
|
|
@@ -119,6 +117,21 @@ export default class ReconnectionManager {
|
|
|
119
117
|
this.reset();
|
|
120
118
|
}
|
|
121
119
|
|
|
120
|
+
/**
|
|
121
|
+
* @public
|
|
122
|
+
* @memberof ReconnectionManager
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
125
|
+
resetReconnectionTimer() {
|
|
126
|
+
this.iceState.resolve();
|
|
127
|
+
this.iceState.resolve = () => {};
|
|
128
|
+
|
|
129
|
+
if (this.iceState.timer) {
|
|
130
|
+
clearTimeout(this.iceState.timer);
|
|
131
|
+
delete this.iceState.timer;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
/**
|
|
123
136
|
* Sets the iceState to connected and clears any disconnect timeouts and
|
|
124
137
|
* related timeout data within the iceState.
|
|
@@ -131,13 +144,7 @@ export default class ReconnectionManager {
|
|
|
131
144
|
if (this.iceState.disconnected) {
|
|
132
145
|
LoggerProxy.logger.log('ReconnectionManager:index#iceReconnected --> ice has reconnected');
|
|
133
146
|
|
|
134
|
-
this.
|
|
135
|
-
this.iceState.resolve = () => {};
|
|
136
|
-
|
|
137
|
-
if (this.iceState.timer) {
|
|
138
|
-
clearTimeout(this.iceState.timer);
|
|
139
|
-
delete this.iceState.timer;
|
|
140
|
-
}
|
|
147
|
+
this.resetReconnectionTimer();
|
|
141
148
|
|
|
142
149
|
this.iceState.disconnected = false;
|
|
143
150
|
}
|
|
@@ -199,6 +206,16 @@ export default class ReconnectionManager {
|
|
|
199
206
|
this.meeting = null;
|
|
200
207
|
}
|
|
201
208
|
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @public
|
|
212
|
+
* @memberof ReconnectionManager
|
|
213
|
+
* @returns {Boolean} true if reconnection operation is in progress
|
|
214
|
+
*/
|
|
215
|
+
isReconnectInProgress() {
|
|
216
|
+
return (this.status === RECONNECTION.STATE.IN_PROGRESS);
|
|
217
|
+
}
|
|
218
|
+
|
|
202
219
|
/**
|
|
203
220
|
* @returns {Boolean}
|
|
204
221
|
* @throws {ReconnectionError}
|
|
@@ -388,13 +405,10 @@ export default class ReconnectionManager {
|
|
|
388
405
|
async rejoinMeeting(wasSharing = false) {
|
|
389
406
|
try {
|
|
390
407
|
LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> attemping meeting rejoin');
|
|
391
|
-
const previousCorrelationId = this.meeting.correlationId;
|
|
392
408
|
|
|
393
409
|
await this.meeting.join({rejoin: true});
|
|
394
410
|
LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> meeting rejoined');
|
|
395
411
|
|
|
396
|
-
RoapCollection.deleteSession(previousCorrelationId);
|
|
397
|
-
|
|
398
412
|
if (wasSharing) {
|
|
399
413
|
// Stop the share streams if user tried to rejoin
|
|
400
414
|
Media.stopTracks(this.meeting.mediaProperties.shareTrack);
|
|
@@ -451,28 +465,22 @@ export default class ReconnectionManager {
|
|
|
451
465
|
* @private
|
|
452
466
|
* @memberof ReconnectionManager
|
|
453
467
|
*/
|
|
454
|
-
reconnectMedia() {
|
|
468
|
+
async reconnectMedia() {
|
|
455
469
|
LoggerProxy.logger.log('ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media');
|
|
456
470
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
sdp: this.meeting.mediaProperties.peerConnection.sdp,
|
|
471
|
-
roapSeq: this.meeting.roapSeq,
|
|
472
|
-
meeting: this.meeting,
|
|
473
|
-
reconnect: true
|
|
474
|
-
});
|
|
475
|
-
});
|
|
471
|
+
// we are not simply calling this.meeting.mediaProperties.webrtcMediaConnection.reconnect(),
|
|
472
|
+
// but instead manually closing and creating new media connection, because we need to do the TURN discovery again
|
|
473
|
+
|
|
474
|
+
await this.meeting.closePeerConnections();
|
|
475
|
+
this.meeting.mediaProperties.unsetPeerConnection();
|
|
476
|
+
|
|
477
|
+
const turnServerInfo = await this.meeting.roap.doTurnDiscovery(this.meeting, true);
|
|
478
|
+
|
|
479
|
+
const mc = this.meeting.createMediaConnection(turnServerInfo);
|
|
480
|
+
|
|
481
|
+
this.meeting.statsAnalyzer.updateMediaConnection(mc);
|
|
482
|
+
|
|
483
|
+
return mc.initiateOffer();
|
|
476
484
|
}
|
|
477
485
|
|
|
478
486
|
/**
|
|
@@ -508,25 +516,4 @@ export default class ReconnectionManager {
|
|
|
508
516
|
throw (connectError);
|
|
509
517
|
}
|
|
510
518
|
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* @param {Meeting} meeting
|
|
514
|
-
* @returns {undefined}
|
|
515
|
-
* @private
|
|
516
|
-
* @memberof ReconnectionManager
|
|
517
|
-
*/
|
|
518
|
-
static async setupPeerConnection(meeting) {
|
|
519
|
-
LoggerProxy.logger.log('ReconnectionManager:index#setupPeerConnection --> Begin resetting peer connection');
|
|
520
|
-
// close pcs, unset to null and create a new one with out closing any streams
|
|
521
|
-
PeerConnectionManager.close(meeting.mediaProperties.peerConnection);
|
|
522
|
-
meeting.mediaProperties.unsetPeerConnection();
|
|
523
|
-
|
|
524
|
-
const turnInfo = await meeting.roap.doTurnDiscovery(meeting, true);
|
|
525
|
-
|
|
526
|
-
meeting.mediaProperties.reInitiatePeerconnection(turnInfo);
|
|
527
|
-
PeerConnectionManager.setPeerConnectionEvents(meeting);
|
|
528
|
-
|
|
529
|
-
// update the peerconnection in the stats manager when ever we reconnect
|
|
530
|
-
meeting.statsAnalyzer.updatePeerconnection(meeting.mediaProperties.peerConnection);
|
|
531
|
-
}
|
|
532
519
|
}
|
package/src/roap/index.js
CHANGED
|
@@ -2,11 +2,8 @@ import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
|
2
2
|
|
|
3
3
|
import {ROAP} from '../constants';
|
|
4
4
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
|
-
import MeetingUtil from '../meeting/util';
|
|
6
5
|
|
|
7
|
-
import RoapHandler from './handler';
|
|
8
6
|
import RoapRequest from './request';
|
|
9
|
-
import RoapCollection from './collection';
|
|
10
7
|
import TurnDiscovery from './turnDiscovery';
|
|
11
8
|
|
|
12
9
|
/**
|
|
@@ -14,8 +11,9 @@ import TurnDiscovery from './turnDiscovery';
|
|
|
14
11
|
* @typedef {Object} RoapOptions
|
|
15
12
|
* @property {String} sdp
|
|
16
13
|
* @property {Meeting} meeting
|
|
17
|
-
* @property {Number}
|
|
18
|
-
* @property {
|
|
14
|
+
* @property {Number} seq
|
|
15
|
+
* @property {Number} tieBreaker
|
|
16
|
+
* @property {Boolean} reconnect
|
|
19
17
|
*/
|
|
20
18
|
|
|
21
19
|
/**
|
|
@@ -52,14 +50,6 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
52
50
|
* @memberof Roap
|
|
53
51
|
*/
|
|
54
52
|
this.options = options;
|
|
55
|
-
/**
|
|
56
|
-
* The Roap Process State Handler
|
|
57
|
-
* @instance
|
|
58
|
-
* @type {RoapHandler}
|
|
59
|
-
* @private
|
|
60
|
-
* @memberof Roap
|
|
61
|
-
*/
|
|
62
|
-
this.roapHandler = new RoapHandler(this.attrs, this.options, this.sendRoapOK.bind(this), this.sendRoapAnswer.bind(this), this.roapFinished.bind(this));
|
|
63
53
|
/**
|
|
64
54
|
* The Roap Request Server Proxy Object
|
|
65
55
|
* @instance
|
|
@@ -68,63 +58,10 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
68
58
|
* @memberof Roap
|
|
69
59
|
*/
|
|
70
60
|
this.roapRequest = new RoapRequest({}, options);
|
|
71
|
-
/**
|
|
72
|
-
* The last roap offer sent to server and acked
|
|
73
|
-
* @instance
|
|
74
|
-
* @type {Object}
|
|
75
|
-
* @private
|
|
76
|
-
* @memberof Roap
|
|
77
|
-
*/
|
|
78
|
-
this.lastRoapOffer = {};
|
|
79
61
|
|
|
80
62
|
this.turnDiscovery = new TurnDiscovery(this.roapRequest);
|
|
81
63
|
}
|
|
82
64
|
|
|
83
|
-
/**
|
|
84
|
-
* Starts listening to mercury events for Roap messages
|
|
85
|
-
* @param {object} data event object
|
|
86
|
-
* @returns {Promise}
|
|
87
|
-
* @private
|
|
88
|
-
* @memberof Roap
|
|
89
|
-
*/
|
|
90
|
-
roapEvent(data) {
|
|
91
|
-
const msg = data.message;
|
|
92
|
-
const {correlationId} = data;
|
|
93
|
-
|
|
94
|
-
LoggerProxy.logger.log(`Roap:index#roapEvent --> Received Roap Message [${JSON.stringify(msg, null, 2)}]`);
|
|
95
|
-
|
|
96
|
-
if (msg.messageType === ROAP.ROAP_TYPES.TURN_DISCOVERY_RESPONSE) {
|
|
97
|
-
// turn discovery is not part of normal roap protocol and so we are not handling it
|
|
98
|
-
// through the usual roap state machine
|
|
99
|
-
this.turnDiscovery.handleTurnDiscoveryResponse(msg);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
this.roapHandler.submit({
|
|
103
|
-
type: ROAP.RECEIVE_ROAP_MSG,
|
|
104
|
-
msg,
|
|
105
|
-
correlationId
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
112
|
-
* @param {String} correlationId correlation id of a meeting
|
|
113
|
-
* @param {Number} seq ROAP sequence number
|
|
114
|
-
* @returns {Promise}
|
|
115
|
-
* @private
|
|
116
|
-
* @memberof Roap
|
|
117
|
-
*/
|
|
118
|
-
stop(correlationId, seq) {
|
|
119
|
-
this.roapHandler.submit({
|
|
120
|
-
type: ROAP.RECEIVE_CALL_LEAVE,
|
|
121
|
-
seq,
|
|
122
|
-
correlationId
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return Promise.resolve();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
65
|
/**
|
|
129
66
|
*
|
|
130
67
|
* @param {SeqOptions} options
|
|
@@ -151,22 +88,15 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
151
88
|
correlationId: options.correlationId,
|
|
152
89
|
audioMuted: meeting.isAudioMuted(),
|
|
153
90
|
videoMuted: meeting.isVideoMuted(),
|
|
154
|
-
meetingId: meeting.id
|
|
91
|
+
meetingId: meeting.id,
|
|
92
|
+
preferTranscoding: !meeting.isMultistream,
|
|
155
93
|
})
|
|
156
94
|
.then(() => {
|
|
157
|
-
this.roapHandler.submit({
|
|
158
|
-
type: ROAP.SEND_ROAP_MSG,
|
|
159
|
-
msg: roapMessage,
|
|
160
|
-
correlationId: options.correlationId
|
|
161
|
-
});
|
|
162
95
|
LoggerProxy.logger.log(`Roap:index#sendRoapOK --> ROAP OK sent with seq ${options.seq}`);
|
|
163
|
-
meeting.setRoapSeq(options.seq);
|
|
164
96
|
});
|
|
165
97
|
});
|
|
166
98
|
}
|
|
167
99
|
|
|
168
|
-
// eslint-disable-next-line no-warning-comments
|
|
169
|
-
// TODO: try to merge sendRoapOk and roapAnswer
|
|
170
100
|
/**
|
|
171
101
|
* Sends a ROAP answer...
|
|
172
102
|
* @param {SeqOptions} options
|
|
@@ -180,56 +110,54 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
180
110
|
const meeting = this.webex.meetings.meetingCollection.getByKey('correlationId', options.correlationId);
|
|
181
111
|
const roapMessage = {
|
|
182
112
|
messageType: ROAP.ROAP_TYPES.ANSWER,
|
|
183
|
-
sdps: options.
|
|
113
|
+
sdps: [options.sdp],
|
|
184
114
|
version: ROAP.ROAP_VERSION,
|
|
185
115
|
seq: options.seq
|
|
186
116
|
};
|
|
187
117
|
|
|
188
|
-
this.roapHandler.submit({
|
|
189
|
-
type: ROAP.SEND_ROAP_MSG,
|
|
190
|
-
msg: roapMessage,
|
|
191
|
-
correlationId: options.correlationId
|
|
192
|
-
});
|
|
193
|
-
|
|
194
118
|
return this.roapRequest
|
|
195
119
|
.sendRoap({
|
|
196
120
|
roapMessage,
|
|
197
121
|
locusSelfUrl: meeting.selfUrl,
|
|
198
122
|
mediaId: options.mediaId,
|
|
199
123
|
correlationId: options.correlationId,
|
|
200
|
-
audioMuted:
|
|
201
|
-
videoMuted:
|
|
202
|
-
meetingId: meeting.id
|
|
203
|
-
|
|
204
|
-
.then(() => {
|
|
205
|
-
meeting.setRoapSeq(options.seq);
|
|
206
|
-
|
|
207
|
-
this.roapHandler.submit({
|
|
208
|
-
type: ROAP.SEND_ROAP_MSG_SUCCESS,
|
|
209
|
-
seq: roapMessage.seq,
|
|
210
|
-
correlationId: meeting.correlationId
|
|
211
|
-
});
|
|
124
|
+
audioMuted: meeting.isAudioMuted(),
|
|
125
|
+
videoMuted: meeting.isVideoMuted(),
|
|
126
|
+
meetingId: meeting.id,
|
|
127
|
+
preferTranscoding: !meeting.isMultistream,
|
|
212
128
|
});
|
|
213
129
|
}
|
|
214
130
|
|
|
215
131
|
/**
|
|
216
132
|
* Sends a ROAP error...
|
|
217
|
-
* @param {Object}
|
|
218
|
-
* @param {Object} locus
|
|
219
|
-
* @param {String} errorType
|
|
133
|
+
* @param {Object} options
|
|
220
134
|
* @returns {Promise}
|
|
221
135
|
* @private
|
|
222
136
|
* @memberof Roap
|
|
223
137
|
*/
|
|
224
|
-
sendRoapError(
|
|
225
|
-
const
|
|
138
|
+
sendRoapError(options) {
|
|
139
|
+
const meeting = this.webex.meetings.meetingCollection.getByKey('correlationId', options.correlationId);
|
|
140
|
+
const roapMessage = {
|
|
226
141
|
messageType: ROAP.ROAP_TYPES.ERROR,
|
|
227
142
|
version: ROAP.ROAP_VERSION,
|
|
228
|
-
errorType,
|
|
229
|
-
seq:
|
|
143
|
+
errorType: options.errorType,
|
|
144
|
+
seq: options.seq
|
|
145
|
+
|
|
230
146
|
};
|
|
231
147
|
|
|
232
|
-
return this.roapRequest.sendRoap(
|
|
148
|
+
return this.roapRequest.sendRoap({
|
|
149
|
+
roapMessage,
|
|
150
|
+
locusSelfUrl: meeting.selfUrl,
|
|
151
|
+
mediaId: options.mediaId,
|
|
152
|
+
correlationId: options.correlationId,
|
|
153
|
+
audioMuted: meeting.isAudioMuted(),
|
|
154
|
+
videoMuted: meeting.isVideoMuted(),
|
|
155
|
+
meetingId: meeting.id,
|
|
156
|
+
preferTranscoding: !meeting.isMultistream,
|
|
157
|
+
})
|
|
158
|
+
.then(() => {
|
|
159
|
+
LoggerProxy.logger.log(`Roap:index#sendRoapError --> ROAP ERROR sent with seq ${options.seq}`);
|
|
160
|
+
});
|
|
233
161
|
}
|
|
234
162
|
|
|
235
163
|
/**
|
|
@@ -240,22 +168,17 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
240
168
|
* @memberof Roap
|
|
241
169
|
*/
|
|
242
170
|
sendRoapMediaRequest(options) {
|
|
243
|
-
const {
|
|
171
|
+
const {
|
|
172
|
+
meeting, seq, sdp, reconnect, tieBreaker
|
|
173
|
+
} = options;
|
|
244
174
|
const roapMessage = {
|
|
245
175
|
messageType: ROAP.ROAP_TYPES.OFFER,
|
|
246
|
-
sdps: [
|
|
247
|
-
// sdps: [options.sdp],
|
|
176
|
+
sdps: [sdp],
|
|
248
177
|
version: ROAP.ROAP_VERSION,
|
|
249
|
-
seq
|
|
250
|
-
tieBreaker
|
|
178
|
+
seq,
|
|
179
|
+
tieBreaker
|
|
251
180
|
};
|
|
252
181
|
|
|
253
|
-
this.roapHandler.submit({
|
|
254
|
-
type: ROAP.SEND_ROAP_MSG,
|
|
255
|
-
msg: roapMessage,
|
|
256
|
-
correlationId: meeting.correlationId
|
|
257
|
-
});
|
|
258
|
-
|
|
259
182
|
// When reconnecting, it's important that the first roap message being sent out has empty media id.
|
|
260
183
|
// Normally this is the roap offer, but when TURN discovery is enabled,
|
|
261
184
|
// then this is the TURN discovery request message
|
|
@@ -269,111 +192,28 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
269
192
|
mediaId: sendEmptyMediaId ? '' : meeting.mediaId,
|
|
270
193
|
audioMuted: meeting.isAudioMuted(),
|
|
271
194
|
videoMuted: meeting.isVideoMuted(),
|
|
272
|
-
meetingId: meeting.id
|
|
195
|
+
meetingId: meeting.id,
|
|
196
|
+
preferTranscoding: !meeting.isMultistream,
|
|
273
197
|
})
|
|
274
198
|
.then(({locus, mediaConnections}) => {
|
|
275
|
-
this.roapHandler.submit({
|
|
276
|
-
type: ROAP.SEND_ROAP_MSG_SUCCESS,
|
|
277
|
-
seq: roapMessage.seq,
|
|
278
|
-
correlationId: meeting.correlationId
|
|
279
|
-
});
|
|
280
|
-
meeting.setRoapSeq(roapMessage.seq);
|
|
281
|
-
|
|
282
199
|
if (mediaConnections) {
|
|
283
200
|
meeting.updateMediaConnections(mediaConnections);
|
|
284
201
|
}
|
|
285
202
|
|
|
286
|
-
// eslint-disable-next-line no-warning-comments
|
|
287
|
-
// TODO: we need to attach peerconenction to locus not sure if we need to pass everything here
|
|
288
203
|
return locus;
|
|
289
|
-
// eslint-disable-next-line no-warning-comments
|
|
290
|
-
// TODO: check where to update the sequence number
|
|
291
204
|
});
|
|
292
205
|
}
|
|
293
206
|
|
|
294
207
|
/**
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
messageType: ROAP.ROAP_TYPES.OFFER,
|
|
305
|
-
sdps: [options.sdp],
|
|
306
|
-
version: ROAP.ROAP_VERSION,
|
|
307
|
-
seq: typeof options.roapSeq !== 'number' && Number.isNaN(parseFloat(options.roapSeq)) ? 0 : options.roapSeq + 1,
|
|
308
|
-
tieBreaker: 4294967294 // Math.floor(Math.random() * (2 ** 32) - 1) // TODO: Handle the roap conflict scenario
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
this.roapHandler.submit({
|
|
312
|
-
type: ROAP.SEND_ROAP_MSG,
|
|
313
|
-
msg: roapMessage,
|
|
314
|
-
correlationId: meeting.correlationId
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
const roapBody = {
|
|
318
|
-
localMedias: [
|
|
319
|
-
{
|
|
320
|
-
localSdp: JSON.stringify(this.roapRequest.attachRechabilityData({
|
|
321
|
-
roapMessage,
|
|
322
|
-
// eslint-disable-next-line no-warning-comments
|
|
323
|
-
// TODO: check whats the need for video and audiomute
|
|
324
|
-
audioMuted: !!meeting.isAudioMuted(),
|
|
325
|
-
videoMuted: !!meeting.isVideoMuted()
|
|
326
|
-
}))
|
|
327
|
-
// mediaId: meeting.mediaId
|
|
328
|
-
}
|
|
329
|
-
]
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
return MeetingUtil.joinMeetingOptions(meeting, {roapMessage: roapBody})
|
|
333
|
-
.then((locus) => {
|
|
334
|
-
this.roapHandler.submit({
|
|
335
|
-
type: ROAP.SEND_ROAP_MSG_SUCCESS,
|
|
336
|
-
seq: roapMessage.seq,
|
|
337
|
-
correlationId: meeting.correlationId
|
|
338
|
-
});
|
|
339
|
-
meeting.setRoapSeq(roapMessage.seq);
|
|
340
|
-
|
|
341
|
-
// eslint-disable-next-line no-warning-comments
|
|
342
|
-
// TODO: we need to attach peerconenction to locus not sure if we need to pass everything here
|
|
343
|
-
return locus;
|
|
344
|
-
// eslint-disable-next-line no-warning-comments
|
|
345
|
-
// TODO: check where to update the sequence number
|
|
346
|
-
});
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Called when the roap sequence is finished (completed successfully or failed)
|
|
351
|
-
* @param {String} correlationId id of the meeting affected
|
|
352
|
-
* @param {String} sequenceId the id of the finished sequence
|
|
353
|
-
* @returns {undefined}
|
|
354
|
-
* @private
|
|
355
|
-
* @memberof Roap
|
|
356
|
-
*/
|
|
357
|
-
roapFinished(correlationId, sequenceId) {
|
|
358
|
-
RoapCollection.onSessionSequenceFinish(correlationId, sequenceId);
|
|
359
|
-
const meeting = this.webex.meetings.meetingCollection.getByKey('correlationId', correlationId);
|
|
360
|
-
|
|
361
|
-
meeting.mediaNegotiatedEvent();
|
|
362
|
-
if (!RoapCollection.isBusy(correlationId)) {
|
|
363
|
-
meeting.processNextQueuedMediaUpdate();
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Performs a TURN server discovery procedure, which involves exchanging
|
|
369
|
-
* some roap messages with the server. This exchange has to be done before
|
|
370
|
-
* any other roap messages are sent
|
|
371
|
-
*
|
|
372
|
-
* @param {Meeting} meeting
|
|
373
|
-
* @param {Boolean} isReconnecting should be set to true if this is a new
|
|
374
|
-
* media connection just after a reconnection
|
|
375
|
-
* @returns {Promise}
|
|
376
|
-
*/
|
|
208
|
+
* Performs a TURN server discovery procedure, which involves exchanging
|
|
209
|
+
* some roap messages with the server. This exchange has to be done before
|
|
210
|
+
* any other roap messages are sent
|
|
211
|
+
*
|
|
212
|
+
* @param {Meeting} meeting
|
|
213
|
+
* @param {Boolean} isReconnecting should be set to true if this is a new
|
|
214
|
+
* media connection just after a reconnection
|
|
215
|
+
* @returns {Promise}
|
|
216
|
+
*/
|
|
377
217
|
doTurnDiscovery(meeting, isReconnecting) {
|
|
378
218
|
return this.turnDiscovery.doTurnDiscovery(meeting, isReconnecting);
|
|
379
219
|
}
|
package/src/roap/request.js
CHANGED
|
@@ -4,16 +4,13 @@ import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
|
4
4
|
|
|
5
5
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
6
6
|
import {
|
|
7
|
-
PARTICIPANT,
|
|
8
|
-
LOCI,
|
|
9
|
-
CALL,
|
|
10
7
|
MEDIA,
|
|
11
8
|
HTTP_VERBS,
|
|
12
9
|
REACHABILITY
|
|
13
10
|
} from '../constants';
|
|
14
11
|
import Metrics from '../metrics';
|
|
15
12
|
import {eventType} from '../metrics/config';
|
|
16
|
-
|
|
13
|
+
|
|
17
14
|
/**
|
|
18
15
|
* @class RoapRequest
|
|
19
16
|
*/
|
|
@@ -45,74 +42,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
45
42
|
return localSdp;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
joinMeetingWithRoap(options) {
|
|
49
|
-
LoggerProxy.logger.info('Roap:request#joinMeetingWithRoap --> Join locus with roap');
|
|
50
|
-
LoggerProxy.logger.info(`Roap:request#joinMeetingWithRoap --> Local SDP: ${options.roapMessage}`);
|
|
51
|
-
|
|
52
|
-
return Promise.resolve().then(async () => {
|
|
53
|
-
const deviceUrl = this.webex.internal.device.url;
|
|
54
|
-
let url = '';
|
|
55
|
-
|
|
56
|
-
const body = {
|
|
57
|
-
deviceUrl,
|
|
58
|
-
usingResource: options.resourceId || null,
|
|
59
|
-
correlationId: options.correlationId,
|
|
60
|
-
localMedias: [
|
|
61
|
-
{
|
|
62
|
-
localSdp: JSON.stringify(this.attachRechabilityData({
|
|
63
|
-
roapMessage: options.roapMessage,
|
|
64
|
-
audioMuted: false,
|
|
65
|
-
videoMuted: false
|
|
66
|
-
}))
|
|
67
|
-
}
|
|
68
|
-
],
|
|
69
|
-
clientMediaPreferences: {
|
|
70
|
-
preferTranscoding: options.preferTranscoding ?? true
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
if (options.locusUrl) {
|
|
75
|
-
url = `${options.locusUrl}/${PARTICIPANT}`;
|
|
76
|
-
}
|
|
77
|
-
else if (options.sipUrl) {
|
|
78
|
-
try {
|
|
79
|
-
await this.webex.internal.services.waitForCatalog('postauth');
|
|
80
|
-
url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
|
|
81
|
-
body.invitee = {
|
|
82
|
-
address: options.sipTarget
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
LoggerProxy.logger.error(`Roap:request#joinMeetingWithRoap --> ${e}`);
|
|
87
|
-
throw (e);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
throw new ParameterError('Must provide a locusUrl or sipTarget');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return this.webex
|
|
95
|
-
.request({
|
|
96
|
-
method: HTTP_VERBS.POST,
|
|
97
|
-
uri: url,
|
|
98
|
-
body
|
|
99
|
-
})
|
|
100
|
-
.then((res) => {
|
|
101
|
-
const {locus} = res.body;
|
|
102
|
-
|
|
103
|
-
locus.roapSeq = options.roapMessage.seq;
|
|
104
|
-
locus.id = locus.url.split('/').pop();
|
|
105
|
-
LoggerProxy.logger.info(`Roap:request#joinMeetingWithRoap --> Joined locus [${locus.id}][${locus.fullState.lastActive}]`);
|
|
106
|
-
|
|
107
|
-
return locus;
|
|
108
|
-
})
|
|
109
|
-
.catch((err) => {
|
|
110
|
-
LoggerProxy.logger.error(`Roap:request#joinMeetingWithRoap --> failed with error: ${err}`);
|
|
111
|
-
throw err;
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
45
|
/**
|
|
117
46
|
* Sends a ROAP message
|
|
118
47
|
* @param {Object} options
|
|
@@ -9,6 +9,12 @@ import RoapRequest from './request';
|
|
|
9
9
|
|
|
10
10
|
const TURN_DISCOVERY_TIMEOUT = 10; // in seconds
|
|
11
11
|
|
|
12
|
+
// Roap spec says that seq should start from 1, but TURN discovery works fine with seq=0
|
|
13
|
+
// and this is handy for us, because TURN discovery is always done before the first SDP exchange,
|
|
14
|
+
// so we can do it with seq=0 or not do it at all and then we create the RoapMediaConnection
|
|
15
|
+
// and do the SDP offer with seq=1
|
|
16
|
+
const TURN_DISCOVERY_SEQ = 0;
|
|
17
|
+
|
|
12
18
|
/**
|
|
13
19
|
* Handles the process of finding out TURN server information from Linus.
|
|
14
20
|
* This is achieved by sending a TURN_DISCOVERY_REQUEST.
|
|
@@ -126,8 +132,6 @@ export default class TurnDiscovery {
|
|
|
126
132
|
* @memberof Roap
|
|
127
133
|
*/
|
|
128
134
|
sendRoapTurnDiscoveryRequest(meeting, isReconnecting) {
|
|
129
|
-
const seq = meeting.roapSeq + 1;
|
|
130
|
-
|
|
131
135
|
if (this.defer) {
|
|
132
136
|
LoggerProxy.logger.warn('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> already in progress');
|
|
133
137
|
|
|
@@ -139,7 +143,7 @@ export default class TurnDiscovery {
|
|
|
139
143
|
const roapMessage = {
|
|
140
144
|
messageType: ROAP.ROAP_TYPES.TURN_DISCOVERY_REQUEST,
|
|
141
145
|
version: ROAP.ROAP_VERSION,
|
|
142
|
-
seq,
|
|
146
|
+
seq: TURN_DISCOVERY_SEQ,
|
|
143
147
|
};
|
|
144
148
|
|
|
145
149
|
LoggerProxy.logger.info('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> sending TURN_DISCOVERY_REQUEST');
|
|
@@ -155,8 +159,6 @@ export default class TurnDiscovery {
|
|
|
155
159
|
meetingId: meeting.id
|
|
156
160
|
})
|
|
157
161
|
.then(({mediaConnections}) => {
|
|
158
|
-
meeting.setRoapSeq(seq);
|
|
159
|
-
|
|
160
162
|
if (mediaConnections) {
|
|
161
163
|
meeting.updateMediaConnections(mediaConnections);
|
|
162
164
|
}
|
|
@@ -177,7 +179,7 @@ export default class TurnDiscovery {
|
|
|
177
179
|
roapMessage: {
|
|
178
180
|
messageType: ROAP.ROAP_TYPES.OK,
|
|
179
181
|
version: ROAP.ROAP_VERSION,
|
|
180
|
-
seq:
|
|
182
|
+
seq: TURN_DISCOVERY_SEQ,
|
|
181
183
|
},
|
|
182
184
|
locusSelfUrl: meeting.selfUrl,
|
|
183
185
|
mediaId: meeting.mediaId,
|
|
@@ -196,6 +198,10 @@ export default class TurnDiscovery {
|
|
|
196
198
|
* | <----TURN_DISCOVERY_RESPONSE----- |
|
|
197
199
|
* | --------------OK----------------> |
|
|
198
200
|
*
|
|
201
|
+
* This TURN discovery roap exchange is always done with seq=0.
|
|
202
|
+
* The RoapMediaConnection SDP exchange always starts with seq=1,
|
|
203
|
+
* so it works fine no matter if TURN discovery is done or not.
|
|
204
|
+
*
|
|
199
205
|
* @param {Meeting} meeting
|
|
200
206
|
* @param {Boolean} isReconnecting should be set to true if this is a new
|
|
201
207
|
* media connection just after a reconnection
|
|
@@ -2,6 +2,7 @@ const STATS_DEFAULT = {
|
|
|
2
2
|
encryption: 'sha-256',
|
|
3
3
|
audio: {
|
|
4
4
|
send: {
|
|
5
|
+
trackLabel: '',
|
|
5
6
|
maxPacketLossRatio: 0,
|
|
6
7
|
availableBandwidth: 0,
|
|
7
8
|
bytesSent: 0,
|
|
@@ -17,6 +18,7 @@ const STATS_DEFAULT = {
|
|
|
17
18
|
},
|
|
18
19
|
video: {
|
|
19
20
|
send: {
|
|
21
|
+
trackLabel: '',
|
|
20
22
|
maxPacketLossRatio: 0,
|
|
21
23
|
availableBandwidth: 0,
|
|
22
24
|
meanRemoteJitter: [],
|