@webex/plugin-meetings 3.7.0-next.33 → 3.7.0-next.34
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 +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/multistream-not-supported-error.js +53 -0
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +242 -164
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +9 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meetings/util.js +1 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/roap/index.js +10 -8
- package/dist/roap/index.js.map +1 -1
- package/dist/types/common/errors/multistream-not-supported-error.d.ts +17 -0
- package/dist/types/constants.d.ts +5 -0
- package/dist/types/meeting/index.d.ts +11 -2
- package/dist/types/meeting/locusMediaRequest.d.ts +4 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -21
- package/src/common/errors/multistream-not-supported-error.ts +30 -0
- package/src/constants.ts +5 -0
- package/src/meeting/index.ts +92 -26
- package/src/meeting/locusMediaRequest.ts +7 -0
- package/src/meetings/util.ts +2 -1
- package/src/roap/index.ts +10 -8
- package/test/unit/spec/meeting/index.js +303 -21
- package/test/unit/spec/meetings/utils.js +10 -0
- package/test/unit/spec/roap/index.ts +47 -0
package/src/meeting/index.ts
CHANGED
|
@@ -161,6 +161,7 @@ import {LocusMediaRequest} from './locusMediaRequest';
|
|
|
161
161
|
import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
|
|
162
162
|
import JoinWebinarError from '../common/errors/join-webinar-error';
|
|
163
163
|
import Member from '../member';
|
|
164
|
+
import MultistreamNotSupportedError from '../common/errors/multistream-not-supported-error';
|
|
164
165
|
|
|
165
166
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
|
166
167
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
|
@@ -4627,11 +4628,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4627
4628
|
* Close the peer connections and remove them from the class.
|
|
4628
4629
|
* Cleanup any media connection related things.
|
|
4629
4630
|
*
|
|
4631
|
+
* @param {boolean} resetMuteStates whether to also reset the audio/video mute state information
|
|
4630
4632
|
* @returns {Promise}
|
|
4631
4633
|
* @public
|
|
4632
4634
|
* @memberof Meeting
|
|
4633
4635
|
*/
|
|
4634
|
-
public closePeerConnections() {
|
|
4636
|
+
public closePeerConnections(resetMuteStates = true) {
|
|
4635
4637
|
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4636
4638
|
if (this.remoteMediaManager) {
|
|
4637
4639
|
this.remoteMediaManager.stop();
|
|
@@ -4648,8 +4650,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4648
4650
|
this.setNetworkStatus(undefined);
|
|
4649
4651
|
}
|
|
4650
4652
|
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
+
if (resetMuteStates) {
|
|
4654
|
+
this.audio = null;
|
|
4655
|
+
this.video = null;
|
|
4656
|
+
}
|
|
4653
4657
|
|
|
4654
4658
|
return Promise.resolve();
|
|
4655
4659
|
}
|
|
@@ -4909,7 +4913,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4909
4913
|
* @param {Object} options - options to join with media
|
|
4910
4914
|
* @param {JoinOptions} [options.joinOptions] - see #join()
|
|
4911
4915
|
* @param {AddMediaOptions} [options.mediaOptions] - see #addMedia()
|
|
4912
|
-
* @returns {Promise} -- {join: see join(), media: see addMedia()}
|
|
4916
|
+
* @returns {Promise} -- {join: see join(), media: see addMedia(), multistreamEnabled: flag to indicate if we managed to join in multistream mode}
|
|
4913
4917
|
* @public
|
|
4914
4918
|
* @memberof Meeting
|
|
4915
4919
|
* @example
|
|
@@ -4999,6 +5003,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4999
5003
|
return {
|
|
5000
5004
|
join: joinResponse,
|
|
5001
5005
|
media: mediaResponse,
|
|
5006
|
+
multistreamEnabled: this.isMultistream,
|
|
5002
5007
|
};
|
|
5003
5008
|
} catch (error) {
|
|
5004
5009
|
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
@@ -5007,7 +5012,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5007
5012
|
|
|
5008
5013
|
this.roap.abortTurnDiscovery();
|
|
5009
5014
|
|
|
5010
|
-
if
|
|
5015
|
+
// if this was the first attempt, let's do a retry
|
|
5016
|
+
let shouldRetry = !isRetry;
|
|
5017
|
+
|
|
5018
|
+
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
5019
|
+
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
5020
|
+
// so there is no point doing a retry
|
|
5021
|
+
shouldRetry = false;
|
|
5022
|
+
}
|
|
5023
|
+
|
|
5024
|
+
// we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
|
|
5025
|
+
if (joined && (isRetry || !shouldRetry)) {
|
|
5011
5026
|
try {
|
|
5012
5027
|
await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
|
|
5013
5028
|
} catch (e) {
|
|
@@ -5031,15 +5046,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5031
5046
|
}
|
|
5032
5047
|
);
|
|
5033
5048
|
|
|
5034
|
-
// if this was the first attempt, let's do a retry
|
|
5035
|
-
let shouldRetry = !isRetry;
|
|
5036
|
-
|
|
5037
|
-
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
5038
|
-
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
5039
|
-
// so there is no point doing a retry
|
|
5040
|
-
shouldRetry = false;
|
|
5041
|
-
}
|
|
5042
|
-
|
|
5043
5049
|
if (shouldRetry) {
|
|
5044
5050
|
LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
|
|
5045
5051
|
this.joinWithMediaRetryInfo.isRetry = true;
|
|
@@ -6076,6 +6082,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6076
6082
|
public roapMessageReceived = (roapMessage: RoapMessage) => {
|
|
6077
6083
|
const mediaServer = MeetingsUtil.getMediaServer(roapMessage.sdp);
|
|
6078
6084
|
|
|
6085
|
+
if (this.isMultistream && mediaServer !== 'homer') {
|
|
6086
|
+
throw new MultistreamNotSupportedError(
|
|
6087
|
+
`Client asked for multistream backend (Homer), but got ${mediaServer} instead`
|
|
6088
|
+
);
|
|
6089
|
+
}
|
|
6079
6090
|
this.mediaProperties.webrtcMediaConnection.roapMessageReceived(roapMessage);
|
|
6080
6091
|
|
|
6081
6092
|
if (mediaServer) {
|
|
@@ -6198,16 +6209,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6198
6209
|
logText: `${LOG_HEADER} Roap Offer`,
|
|
6199
6210
|
}
|
|
6200
6211
|
).catch((error) => {
|
|
6212
|
+
const multistreamNotSupported = error instanceof MultistreamNotSupportedError;
|
|
6213
|
+
|
|
6201
6214
|
// @ts-ignore
|
|
6202
6215
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6203
6216
|
name: 'client.media-engine.remote-sdp-received',
|
|
6204
6217
|
payload: {
|
|
6205
|
-
canProceed:
|
|
6218
|
+
canProceed: multistreamNotSupported,
|
|
6206
6219
|
errors: [
|
|
6207
6220
|
// @ts-ignore
|
|
6208
6221
|
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
6209
6222
|
{
|
|
6210
|
-
clientErrorCode:
|
|
6223
|
+
clientErrorCode: multistreamNotSupported
|
|
6224
|
+
? CALL_DIAGNOSTIC_CONFIG.MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE
|
|
6225
|
+
: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
6211
6226
|
}
|
|
6212
6227
|
),
|
|
6213
6228
|
],
|
|
@@ -6215,7 +6230,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6215
6230
|
options: {meetingId: this.id, rawError: error},
|
|
6216
6231
|
});
|
|
6217
6232
|
|
|
6218
|
-
this.deferSDPAnswer.reject(
|
|
6233
|
+
this.deferSDPAnswer.reject(error);
|
|
6219
6234
|
clearTimeout(this.sdpResponseTimer);
|
|
6220
6235
|
this.sdpResponseTimer = undefined;
|
|
6221
6236
|
});
|
|
@@ -7105,7 +7120,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7105
7120
|
|
|
7106
7121
|
const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
|
|
7107
7122
|
|
|
7108
|
-
LoggerProxy.logger.info(
|
|
7123
|
+
LoggerProxy.logger.info(
|
|
7124
|
+
`${LOG_HEADER} media connection created this.isMultistream=${this.isMultistream}`
|
|
7125
|
+
);
|
|
7109
7126
|
|
|
7110
7127
|
if (this.isMultistream) {
|
|
7111
7128
|
this.remoteMediaManager = new RemoteMediaManager(
|
|
@@ -7183,6 +7200,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7183
7200
|
}
|
|
7184
7201
|
}
|
|
7185
7202
|
|
|
7203
|
+
/**
|
|
7204
|
+
* Cleans up stats analyzer, peer connection and other things before
|
|
7205
|
+
* we can create a new transcoded media connection
|
|
7206
|
+
*
|
|
7207
|
+
* @private
|
|
7208
|
+
* @returns {Promise<void>}
|
|
7209
|
+
*/
|
|
7210
|
+
private async downgradeFromMultistreamToTranscoded(): Promise<void> {
|
|
7211
|
+
if (this.statsAnalyzer) {
|
|
7212
|
+
await this.statsAnalyzer.stopAnalyzer();
|
|
7213
|
+
}
|
|
7214
|
+
this.statsAnalyzer = null;
|
|
7215
|
+
|
|
7216
|
+
this.isMultistream = false;
|
|
7217
|
+
|
|
7218
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
7219
|
+
// close peer connection, but don't reset mute state information, because we will want to use it on the retry
|
|
7220
|
+
this.closePeerConnections(false);
|
|
7221
|
+
|
|
7222
|
+
this.mediaProperties.unsetPeerConnection();
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
this.locusMediaRequest?.downgradeFromMultistreamToTranscoded();
|
|
7226
|
+
|
|
7227
|
+
this.createStatsAnalyzer();
|
|
7228
|
+
}
|
|
7229
|
+
|
|
7186
7230
|
/**
|
|
7187
7231
|
* Sends stats report, closes peer connection and cleans up any media connection
|
|
7188
7232
|
* related things before trying to establish media connection again with turn server
|
|
@@ -7377,13 +7421,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7377
7421
|
|
|
7378
7422
|
this.createStatsAnalyzer();
|
|
7379
7423
|
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7424
|
+
try {
|
|
7425
|
+
await this.establishMediaConnection(
|
|
7426
|
+
remoteMediaManagerConfig,
|
|
7427
|
+
bundlePolicy,
|
|
7428
|
+
forceTurnDiscovery,
|
|
7429
|
+
turnServerInfo
|
|
7430
|
+
);
|
|
7431
|
+
} catch (error) {
|
|
7432
|
+
if (error instanceof MultistreamNotSupportedError) {
|
|
7433
|
+
LoggerProxy.logger.warn(
|
|
7434
|
+
`${LOG_HEADER} we asked for multistream backend (Homer), but got transcoded backend, recreating media connection...`
|
|
7435
|
+
);
|
|
7386
7436
|
|
|
7437
|
+
await this.downgradeFromMultistreamToTranscoded();
|
|
7438
|
+
|
|
7439
|
+
// Establish new media connection with forced TURN discovery
|
|
7440
|
+
// We need to do TURN discovery again, because backend will be creating a new confluence, so it might land on a different node or cluster
|
|
7441
|
+
await this.establishMediaConnection(
|
|
7442
|
+
remoteMediaManagerConfig,
|
|
7443
|
+
bundlePolicy,
|
|
7444
|
+
true,
|
|
7445
|
+
undefined
|
|
7446
|
+
);
|
|
7447
|
+
} else {
|
|
7448
|
+
throw error;
|
|
7449
|
+
}
|
|
7450
|
+
}
|
|
7387
7451
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
7388
7452
|
await this.enqueueScreenShareFloorRequest();
|
|
7389
7453
|
}
|
|
@@ -8353,7 +8417,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8353
8417
|
if (layoutType) {
|
|
8354
8418
|
if (!LAYOUT_TYPES.includes(layoutType)) {
|
|
8355
8419
|
return this.rejectWithErrorLog(
|
|
8356
|
-
|
|
8420
|
+
`Meeting:index#changeVideoLayout --> cannot change video layout, invalid layoutType "${layoutType}" received.`
|
|
8357
8421
|
);
|
|
8358
8422
|
}
|
|
8359
8423
|
|
|
@@ -8753,10 +8817,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8753
8817
|
|
|
8754
8818
|
return;
|
|
8755
8819
|
}
|
|
8756
|
-
|
|
8820
|
+
|
|
8757
8821
|
const keepAliveInterval = (this.joinedWith.keepAliveSecs - 1) * 750; // taken from UCF
|
|
8758
8822
|
|
|
8759
8823
|
this.keepAliveTimerId = setInterval(() => {
|
|
8824
|
+
const {keepAliveUrl} = this.joinedWith;
|
|
8825
|
+
|
|
8760
8826
|
this.meetingRequest.keepAlive({keepAliveUrl}).catch((error) => {
|
|
8761
8827
|
LoggerProxy.logger.warn(
|
|
8762
8828
|
`Meeting:index#startKeepAlive --> Stopping sending keepAlives to ${keepAliveUrl} after error ${error}`
|
|
@@ -342,4 +342,11 @@ export class LocusMediaRequest extends WebexPlugin {
|
|
|
342
342
|
public isConfluenceCreated() {
|
|
343
343
|
return this.confluenceState === 'created';
|
|
344
344
|
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* This method needs to be called when we downgrade from multistream to transcoded connection.
|
|
348
|
+
*/
|
|
349
|
+
public downgradeFromMultistreamToTranscoded() {
|
|
350
|
+
this.config.preferTranscoding = true;
|
|
351
|
+
}
|
|
345
352
|
}
|
package/src/meetings/util.ts
CHANGED
package/src/roap/index.ts
CHANGED
|
@@ -231,14 +231,16 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
231
231
|
headers,
|
|
232
232
|
} = remoteSdp.roapMessage;
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
234
|
+
if (messageType === ROAP.ROAP_TYPES.ANSWER) {
|
|
235
|
+
roapAnswer = {
|
|
236
|
+
seq: answerSeq,
|
|
237
|
+
messageType,
|
|
238
|
+
sdp: sdps[0],
|
|
239
|
+
errorType,
|
|
240
|
+
errorCause,
|
|
241
|
+
headers,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
242
244
|
}
|
|
243
245
|
}
|
|
244
246
|
|