@webex/plugin-meetings 3.1.0-next.9 → 3.2.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.
- package/dist/annotation/annotation.types.d.ts +42 -0
- package/dist/annotation/constants.d.ts +31 -0
- package/dist/annotation/index.d.ts +117 -0
- package/dist/breakouts/breakout.d.ts +8 -0
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/collection.d.ts +5 -0
- package/dist/breakouts/edit-lock-error.d.ts +15 -0
- package/dist/breakouts/events.d.ts +8 -0
- package/dist/breakouts/index.d.ts +5 -0
- package/dist/breakouts/index.js +1 -1
- package/dist/breakouts/request.d.ts +22 -0
- package/dist/breakouts/utils.d.ts +15 -0
- package/dist/common/browser-detection.d.ts +9 -0
- package/dist/common/collection.d.ts +48 -0
- package/dist/common/config.d.ts +2 -0
- package/dist/common/errors/captcha-error.d.ts +15 -0
- package/dist/common/errors/intent-to-join.d.ts +16 -0
- package/dist/common/errors/join-meeting.d.ts +17 -0
- package/dist/common/errors/media.d.ts +15 -0
- package/dist/common/errors/no-meeting-info.d.ts +14 -0
- package/dist/common/errors/parameter.d.ts +15 -0
- package/dist/common/errors/password-error.d.ts +15 -0
- package/dist/common/errors/permission.d.ts +14 -0
- package/dist/common/errors/reclaim-host-role-errors.d.ts +60 -0
- package/dist/common/errors/reconnection-in-progress.d.ts +9 -0
- package/dist/common/errors/reconnection-in-progress.js +34 -0
- package/dist/common/errors/reconnection-in-progress.js.map +1 -0
- package/dist/common/errors/reconnection.d.ts +15 -0
- package/dist/common/errors/stats.d.ts +15 -0
- package/dist/common/errors/webex-errors.d.ts +93 -0
- package/dist/common/errors/webex-meetings-error.d.ts +20 -0
- package/dist/common/events/events-scope.d.ts +17 -0
- package/dist/common/events/events.d.ts +12 -0
- package/dist/common/events/trigger-proxy.d.ts +2 -0
- package/dist/common/events/util.d.ts +2 -0
- package/dist/common/logs/logger-config.d.ts +2 -0
- package/dist/common/logs/logger-proxy.d.ts +2 -0
- package/dist/common/logs/request.d.ts +36 -0
- package/dist/common/queue.d.ts +34 -0
- package/dist/config.d.ts +73 -0
- package/dist/constants.d.ts +1088 -0
- package/dist/constants.js +6 -3
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.d.ts +4 -0
- package/dist/controls-options-manager/enums.d.ts +15 -0
- package/dist/controls-options-manager/index.d.ts +136 -0
- package/dist/controls-options-manager/types.d.ts +43 -0
- package/dist/controls-options-manager/util.d.ts +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/interceptors/index.d.ts +2 -0
- package/dist/interceptors/locusRetry.d.ts +27 -0
- package/dist/interpretation/collection.d.ts +5 -0
- package/dist/interpretation/index.d.ts +5 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.d.ts +5 -0
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.d.ts +2 -0
- package/dist/locus-info/embeddedAppsUtils.d.ts +2 -0
- package/dist/locus-info/fullState.d.ts +2 -0
- package/dist/locus-info/hostUtils.d.ts +2 -0
- package/dist/locus-info/index.d.ts +322 -0
- package/dist/locus-info/infoUtils.d.ts +2 -0
- package/dist/locus-info/mediaSharesUtils.d.ts +2 -0
- package/dist/locus-info/parser.d.ts +272 -0
- package/dist/locus-info/selfUtils.d.ts +2 -0
- package/dist/media/MediaConnectionAwaiter.d.ts +61 -0
- package/dist/media/index.d.ts +34 -0
- package/dist/media/properties.d.ts +93 -0
- package/dist/media/util.d.ts +2 -0
- package/dist/mediaQualityMetrics/config.d.ts +241 -0
- package/dist/mediaQualityMetrics/config.js +10 -10
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/in-meeting-actions.d.ts +167 -0
- package/dist/meeting/index.d.ts +1825 -0
- package/dist/meeting/index.js +112 -64
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.d.ts +74 -0
- package/dist/meeting/locusMediaRequest.js +27 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.d.ts +178 -0
- package/dist/meeting/request.d.ts +295 -0
- package/dist/meeting/request.type.d.ts +11 -0
- package/dist/meeting/state.d.ts +9 -0
- package/dist/meeting/util.d.ts +119 -0
- package/dist/meeting/util.js +0 -16
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting/voicea-meeting.d.ts +16 -0
- package/dist/meeting/voicea-meeting.js +37 -49
- package/dist/meeting/voicea-meeting.js.map +1 -1
- package/dist/meeting-info/collection.d.ts +20 -0
- package/dist/meeting-info/index.d.ts +69 -0
- package/dist/meeting-info/meeting-info-v2.d.ts +123 -0
- package/dist/meeting-info/request.d.ts +22 -0
- package/dist/meeting-info/util.d.ts +2 -0
- package/dist/meeting-info/utilv2.d.ts +2 -0
- package/dist/meetings/collection.d.ts +40 -0
- package/dist/meetings/index.d.ts +398 -0
- package/dist/meetings/index.js +12 -28
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.d.ts +4 -0
- package/dist/meetings/request.d.ts +27 -0
- package/dist/meetings/util.d.ts +18 -0
- package/dist/member/index.d.ts +160 -0
- package/dist/member/types.d.ts +32 -0
- package/dist/member/util.d.ts +2 -0
- package/dist/members/collection.d.ts +29 -0
- package/dist/members/index.d.ts +353 -0
- package/dist/members/request.d.ts +114 -0
- package/dist/members/types.d.ts +25 -0
- package/dist/members/util.d.ts +215 -0
- package/dist/metrics/constants.d.ts +70 -0
- package/dist/metrics/index.d.ts +45 -0
- package/dist/multistream/mediaRequestManager.d.ts +119 -0
- package/dist/multistream/receiveSlot.d.ts +68 -0
- package/dist/multistream/receiveSlotManager.d.ts +56 -0
- package/dist/multistream/remoteMedia.d.ts +72 -0
- package/dist/multistream/remoteMediaGroup.d.ts +49 -0
- package/dist/multistream/remoteMediaManager.d.ts +300 -0
- package/dist/multistream/sendSlotManager.d.ts +69 -0
- package/dist/networkQualityMonitor/index.d.ts +70 -0
- package/dist/personal-meeting-room/index.d.ts +47 -0
- package/dist/personal-meeting-room/request.d.ts +14 -0
- package/dist/personal-meeting-room/util.d.ts +2 -0
- package/dist/reachability/clusterReachability.d.ts +110 -0
- package/dist/reachability/index.d.ts +109 -0
- package/dist/reachability/index.js +88 -9
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.d.ts +39 -0
- package/dist/reachability/util.d.ts +15 -0
- package/dist/reactions/constants.d.ts +3 -0
- package/dist/reactions/reactions.d.ts +4 -0
- package/dist/reactions/reactions.type.d.ts +52 -0
- package/dist/reconnection-manager/index.d.ts +136 -0
- package/dist/recording-controller/enums.d.ts +7 -0
- package/dist/recording-controller/index.d.ts +207 -0
- package/dist/recording-controller/util.d.ts +14 -0
- package/dist/roap/index.d.ts +86 -0
- package/dist/roap/request.d.ts +39 -0
- package/dist/roap/request.js +3 -27
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.d.ts +155 -0
- package/dist/rtcMetrics/constants.d.ts +4 -0
- package/dist/rtcMetrics/index.d.ts +61 -0
- package/dist/statsAnalyzer/global.d.ts +36 -0
- package/dist/statsAnalyzer/index.d.ts +217 -0
- package/dist/statsAnalyzer/index.js +4 -2
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.d.ts +48 -0
- package/dist/statsAnalyzer/mqaUtil.js +14 -0
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/transcription/index.d.ts +64 -0
- package/dist/types/constants.d.ts +3 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +8 -2
- package/dist/types/meeting/index.d.ts +10 -1
- package/dist/types/meeting/locusMediaRequest.d.ts +1 -0
- package/dist/types/meeting/voicea-meeting.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +1 -16
- package/dist/types/reachability/index.d.ts +11 -0
- package/dist/webinar/collection.d.ts +16 -0
- package/dist/webinar/index.d.ts +5 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -21
- package/src/constants.ts +3 -2
- package/src/mediaQualityMetrics/config.ts +13 -7
- package/src/meeting/index.ts +73 -30
- package/src/meeting/locusMediaRequest.ts +31 -0
- package/src/meeting/util.ts +1 -16
- package/src/meeting/voicea-meeting.ts +44 -46
- package/src/meetings/index.ts +15 -27
- package/src/reachability/index.ts +60 -0
- package/src/roap/request.ts +1 -24
- package/src/statsAnalyzer/index.ts +6 -3
- package/src/statsAnalyzer/mqaUtil.ts +18 -0
- package/test/unit/spec/meeting/index.js +70 -33
- package/test/unit/spec/meeting/locusMediaRequest.ts +49 -0
- package/test/unit/spec/meeting/utils.js +0 -10
- package/test/unit/spec/meeting/voicea-meeting.ts +5 -14
- package/test/unit/spec/meetings/index.js +59 -17
- package/test/unit/spec/reachability/index.ts +266 -0
- package/test/unit/spec/roap/request.ts +0 -37
- package/test/unit/spec/stats-analyzer/index.js +89 -8
|
@@ -296,6 +296,63 @@ export default class Reachability {
|
|
|
296
296
|
return reachable;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Returns true only if ALL protocols (UDP, TCP and TLS) have been tested and none
|
|
301
|
+
* of the media clusters where reachable with any of the protocols. This is done
|
|
302
|
+
* irrespective of the config, so for example:
|
|
303
|
+
* if config.meetings.experimental.enableTlsReachability === false,
|
|
304
|
+
* it will return false, because TLS reachability won't be tested,
|
|
305
|
+
* so we can't say for sure that media backend is unreachable over TLS.
|
|
306
|
+
*
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
*/
|
|
309
|
+
async isWebexMediaBackendUnreachable() {
|
|
310
|
+
let unreachable = false;
|
|
311
|
+
|
|
312
|
+
// @ts-ignore
|
|
313
|
+
const reachabilityData = await this.webex.boundedStorage
|
|
314
|
+
.get(this.namespace, REACHABILITY.localStorageResult)
|
|
315
|
+
.catch(() => {});
|
|
316
|
+
|
|
317
|
+
if (reachabilityData) {
|
|
318
|
+
try {
|
|
319
|
+
const reachabilityResults: ReachabilityResults = JSON.parse(reachabilityData);
|
|
320
|
+
|
|
321
|
+
const protocols = {
|
|
322
|
+
udp: {tested: false, reachable: undefined},
|
|
323
|
+
tcp: {tested: false, reachable: undefined},
|
|
324
|
+
xtls: {tested: false, reachable: undefined},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
Object.values(reachabilityResults).forEach((result) => {
|
|
328
|
+
Object.keys(protocols).forEach((protocol) => {
|
|
329
|
+
if (
|
|
330
|
+
result[protocol]?.result === 'reachable' ||
|
|
331
|
+
result[protocol]?.result === 'unreachable'
|
|
332
|
+
) {
|
|
333
|
+
protocols[protocol].tested = true;
|
|
334
|
+
|
|
335
|
+
// we need at least 1 'reachable' result to mark the whole protocol as reachable
|
|
336
|
+
if (result[protocol].result === 'reachable') {
|
|
337
|
+
protocols[protocol].reachable = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
unreachable = Object.values(protocols).every(
|
|
344
|
+
(protocol) => protocol.tested && !protocol.reachable
|
|
345
|
+
);
|
|
346
|
+
} catch (e) {
|
|
347
|
+
LoggerProxy.logger.error(
|
|
348
|
+
`Roap:request#attachReachabilityData --> Error in parsing reachability data: ${e}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return unreachable;
|
|
354
|
+
}
|
|
355
|
+
|
|
299
356
|
/**
|
|
300
357
|
* Get list of all unreachable clusters
|
|
301
358
|
* @returns {array} Unreachable clusters
|
|
@@ -314,6 +371,9 @@ export default class Reachability {
|
|
|
314
371
|
if (result.tcp.result === 'unreachable') {
|
|
315
372
|
unreachableList.push({name: key, protocol: 'tcp'});
|
|
316
373
|
}
|
|
374
|
+
if (result.xtls.result === 'unreachable') {
|
|
375
|
+
unreachableList.push({name: key, protocol: 'xtls'});
|
|
376
|
+
}
|
|
317
377
|
});
|
|
318
378
|
|
|
319
379
|
return unreachableList;
|
package/src/roap/request.ts
CHANGED
|
@@ -61,7 +61,7 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
61
61
|
ipVersion?: IP_VERSION;
|
|
62
62
|
locusMediaRequest?: LocusMediaRequest;
|
|
63
63
|
}) {
|
|
64
|
-
const {roapMessage, locusSelfUrl, mediaId,
|
|
64
|
+
const {roapMessage, locusSelfUrl, mediaId, locusMediaRequest, ipVersion} = options;
|
|
65
65
|
|
|
66
66
|
if (!mediaId) {
|
|
67
67
|
LoggerProxy.logger.info('Roap:request#sendRoap --> sending empty mediaID');
|
|
@@ -82,14 +82,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
82
82
|
`Roap:request#sendRoap --> ${locusSelfUrl} \n ${roapMessage.messageType} \n seq:${roapMessage.seq}`
|
|
83
83
|
);
|
|
84
84
|
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
87
|
-
name: 'client.locus.media.request',
|
|
88
|
-
options: {
|
|
89
|
-
meetingId,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
85
|
return locusMediaRequest
|
|
94
86
|
.send({
|
|
95
87
|
type: 'RoapMessage',
|
|
@@ -101,13 +93,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
101
93
|
ipVersion,
|
|
102
94
|
})
|
|
103
95
|
.then((res) => {
|
|
104
|
-
// @ts-ignore
|
|
105
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
106
|
-
name: 'client.locus.media.response',
|
|
107
|
-
options: {
|
|
108
|
-
meetingId,
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
96
|
// always it will be the first mediaConnection Object
|
|
112
97
|
const mediaConnections =
|
|
113
98
|
res.body.mediaConnections &&
|
|
@@ -131,14 +116,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
131
116
|
};
|
|
132
117
|
})
|
|
133
118
|
.catch((err) => {
|
|
134
|
-
// @ts-ignore
|
|
135
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
136
|
-
name: 'client.locus.media.response',
|
|
137
|
-
options: {
|
|
138
|
-
meetingId,
|
|
139
|
-
rawError: err,
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
119
|
LoggerProxy.logger.error(`Roap:request#sendRoap --> Error:`, err);
|
|
143
120
|
LoggerProxy.logger.error(
|
|
144
121
|
`Roap:request#sendRoapRequest --> roapMessage that caused error:${JSON.stringify(
|
|
@@ -998,12 +998,11 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
998
998
|
result.qualityLimitationReason;
|
|
999
999
|
this.statsResults[mediaType][sendrecvType].qualityLimitationResolutionChanges =
|
|
1000
1000
|
result.qualityLimitationResolutionChanges;
|
|
1001
|
-
this.statsResults[mediaType][sendrecvType].
|
|
1001
|
+
this.statsResults[mediaType][sendrecvType].totalRtxPacketsSent =
|
|
1002
1002
|
result.retransmittedPacketsSent;
|
|
1003
|
+
this.statsResults[mediaType][sendrecvType].totalRtxBytesSent = result.retransmittedBytesSent;
|
|
1003
1004
|
this.statsResults[mediaType][sendrecvType].totalBytesSent = result.bytesSent;
|
|
1004
1005
|
this.statsResults[mediaType][sendrecvType].headerBytesSent = result.headerBytesSent;
|
|
1005
|
-
this.statsResults[mediaType][sendrecvType].retransmittedBytesSent =
|
|
1006
|
-
result.retransmittedBytesSent;
|
|
1007
1006
|
this.statsResults[mediaType][sendrecvType].requestedBitrate = result.requestedBitrate;
|
|
1008
1007
|
this.statsResults[mediaType][sendrecvType].requestedFrameSize = result.requestedFrameSize;
|
|
1009
1008
|
}
|
|
@@ -1136,6 +1135,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1136
1135
|
this.statsResults[mediaType][sendrecvType].fecPacketsReceived = result.fecPacketsReceived;
|
|
1137
1136
|
this.statsResults[mediaType][sendrecvType].totalBytesReceived = result.bytesReceived;
|
|
1138
1137
|
this.statsResults[mediaType][sendrecvType].headerBytesReceived = result.headerBytesReceived;
|
|
1138
|
+
this.statsResults[mediaType][sendrecvType].totalRtxPacketsReceived =
|
|
1139
|
+
result.retransmittedPacketsReceived;
|
|
1140
|
+
this.statsResults[mediaType][sendrecvType].totalRtxBytesReceived =
|
|
1141
|
+
result.retransmittedBytesReceived;
|
|
1139
1142
|
|
|
1140
1143
|
this.statsResults[mediaType][sendrecvType].meanRtpJitter.push(result.jitter);
|
|
1141
1144
|
|
|
@@ -237,10 +237,16 @@ export const getVideoReceiverMqa = ({
|
|
|
237
237
|
const lastPacketsLost = getLastTotalValue('totalPacketsLost');
|
|
238
238
|
const lastBytesReceived = getLastTotalValue('totalBytesReceived');
|
|
239
239
|
|
|
240
|
+
const lastRtxPacketsReceived = getLastTotalValue('totalRtxPacketsReceived');
|
|
241
|
+
const lastRtxBytesReceived = getLastTotalValue('totalRtxBytesReceived');
|
|
242
|
+
|
|
240
243
|
const packetsLost = getTotalValue('totalPacketsLost');
|
|
241
244
|
const totalPacketsReceived = getTotalValue('totalPacketsReceived');
|
|
242
245
|
const totalBytesReceived = getTotalValue('totalBytesReceived');
|
|
243
246
|
|
|
247
|
+
const totalRtxPacketsReceived = getTotalValue('totalRtxPacketsReceived');
|
|
248
|
+
const totalRtxBytesReceived = getTotalValue('totalRtxBytesReceived');
|
|
249
|
+
|
|
244
250
|
const meanRemoteJitter = Object.keys(statsResults)
|
|
245
251
|
.filter((mt) => mt.includes(baseMediaType))
|
|
246
252
|
.reduce((acc, mt) => acc.concat(statsResults[mt][sendrecvType].meanRemoteJitter), []);
|
|
@@ -266,10 +272,15 @@ export const getVideoReceiverMqa = ({
|
|
|
266
272
|
|
|
267
273
|
// Calculate the outgoing bitrate
|
|
268
274
|
const totalBytesReceivedInaMin = totalBytesReceived - lastBytesReceived;
|
|
275
|
+
const totalRtxBytesReceivedInaMin = totalRtxBytesReceived - lastRtxBytesReceived;
|
|
269
276
|
|
|
270
277
|
videoReceiver.common.rtpBitrate = totalBytesReceivedInaMin
|
|
271
278
|
? (totalBytesReceivedInaMin * 8) / 60
|
|
272
279
|
: 0;
|
|
280
|
+
videoReceiver.common.rtxPackets = totalRtxPacketsReceived - lastRtxPacketsReceived;
|
|
281
|
+
videoReceiver.common.rtxBitrate = totalRtxBytesReceivedInaMin
|
|
282
|
+
? (totalRtxBytesReceivedInaMin * 8) / 60
|
|
283
|
+
: 0;
|
|
273
284
|
};
|
|
274
285
|
|
|
275
286
|
export const getVideoReceiverStreamMqa = ({
|
|
@@ -349,11 +360,15 @@ export const getVideoSenderMqa = ({videoSender, statsResults, lastMqaDataSent, b
|
|
|
349
360
|
const lastPacketsSent = getLastTotalValue('totalPacketsSent');
|
|
350
361
|
const lastBytesSent = getLastTotalValue('totalBytesSent');
|
|
351
362
|
const lastPacketsLostTotal = getLastTotalValue('totalPacketsLostOnReceiver');
|
|
363
|
+
const lastRtxPacketsSent = getLastTotalValue('totalRtxPacketsSent');
|
|
364
|
+
const lastRtxBytesSent = getLastTotalValue('totalRtxBytesSent');
|
|
352
365
|
|
|
353
366
|
const totalPacketsLostOnReceiver = getTotalValue('totalPacketsLostOnReceiver');
|
|
354
367
|
const totalPacketsSent = getTotalValue('totalPacketsSent');
|
|
355
368
|
const totalBytesSent = getTotalValue('totalBytesSent');
|
|
356
369
|
const availableOutgoingBitrate = getTotalValue('availableOutgoingBitrate');
|
|
370
|
+
const totalRtxPacketsSent = getTotalValue('totalRtxPacketsSent');
|
|
371
|
+
const totalRtxBytesSent = getTotalValue('totalRtxBytesSent');
|
|
357
372
|
|
|
358
373
|
videoSender.common.common.direction =
|
|
359
374
|
statsResults[Object.keys(statsResults).find((mediaType) => mediaType.includes(baseMediaType))]
|
|
@@ -389,8 +404,11 @@ export const getVideoSenderMqa = ({videoSender, statsResults, lastMqaDataSent, b
|
|
|
389
404
|
|
|
390
405
|
// Calculate the outgoing bitrate
|
|
391
406
|
const totalBytesSentInaMin = totalBytesSent - lastBytesSent;
|
|
407
|
+
const totalRtxBytesSentInaMin = totalRtxBytesSent - lastRtxBytesSent;
|
|
392
408
|
|
|
393
409
|
videoSender.common.rtpBitrate = totalBytesSentInaMin ? (totalBytesSentInaMin * 8) / 60 : 0;
|
|
410
|
+
videoSender.common.rtxPackets = totalRtxPacketsSent - lastRtxPacketsSent;
|
|
411
|
+
videoSender.common.rtxBitrate = totalRtxBytesSentInaMin ? (totalRtxBytesSentInaMin * 8) / 60 : 0;
|
|
394
412
|
};
|
|
395
413
|
|
|
396
414
|
export const getVideoSenderStreamMqa = ({
|
|
@@ -906,12 +906,12 @@ describe('plugin-meetings', () => {
|
|
|
906
906
|
|
|
907
907
|
describe('#isTranscriptionSupported', () => {
|
|
908
908
|
it('should return false if the feature is not supported for the meeting', () => {
|
|
909
|
-
meeting.locusInfo.controls = {transcribe: {
|
|
909
|
+
meeting.locusInfo.controls = {transcribe: {caption: false}};
|
|
910
910
|
|
|
911
911
|
assert.equal(meeting.isTranscriptionSupported(), false);
|
|
912
912
|
});
|
|
913
913
|
it('should return true if webex assitant is enabled', () => {
|
|
914
|
-
meeting.locusInfo.controls = {transcribe: {
|
|
914
|
+
meeting.locusInfo.controls = {transcribe: {caption: true}};
|
|
915
915
|
|
|
916
916
|
assert.equal(meeting.isTranscriptionSupported(), true);
|
|
917
917
|
});
|
|
@@ -1302,6 +1302,31 @@ describe('plugin-meetings', () => {
|
|
|
1302
1302
|
);
|
|
1303
1303
|
});
|
|
1304
1304
|
});
|
|
1305
|
+
|
|
1306
|
+
describe('#handleLLMOnline', () => {
|
|
1307
|
+
beforeEach(() => {
|
|
1308
|
+
webex.internal.llm.off = sinon.stub();
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
it('turns off llm online, emits transcription connected events', () => {
|
|
1312
|
+
meeting.handleLLMOnline();
|
|
1313
|
+
assert.calledOnceWithExactly(
|
|
1314
|
+
webex.internal.llm.off,
|
|
1315
|
+
'online',
|
|
1316
|
+
meeting.handleLLMOnline
|
|
1317
|
+
);
|
|
1318
|
+
assert.calledWith(
|
|
1319
|
+
TriggerProxy.trigger,
|
|
1320
|
+
sinon.match.instanceOf(Meeting),
|
|
1321
|
+
{
|
|
1322
|
+
file: 'meeting/index',
|
|
1323
|
+
function: 'handleLLMOnline',
|
|
1324
|
+
},
|
|
1325
|
+
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1326
|
+
);
|
|
1327
|
+
});
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1305
1330
|
describe('#join', () => {
|
|
1306
1331
|
let sandbox = null;
|
|
1307
1332
|
let setCorrelationIdSpy;
|
|
@@ -1351,15 +1376,10 @@ describe('plugin-meetings', () => {
|
|
|
1351
1376
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
1352
1377
|
assert.calledOnce(meeting.setLocus);
|
|
1353
1378
|
assert.equal(result, joinMeetingResult);
|
|
1354
|
-
|
|
1355
1379
|
assert.calledWith(
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
file: 'meeting/index',
|
|
1360
|
-
function: 'join',
|
|
1361
|
-
},
|
|
1362
|
-
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1380
|
+
webex.internal.llm.on,
|
|
1381
|
+
'online',
|
|
1382
|
+
meeting.handleLLMOnline
|
|
1363
1383
|
);
|
|
1364
1384
|
});
|
|
1365
1385
|
|
|
@@ -6166,11 +6186,22 @@ describe('plugin-meetings', () => {
|
|
|
6166
6186
|
|
|
6167
6187
|
beforeEach(() => {
|
|
6168
6188
|
sandbox = sinon.createSandbox();
|
|
6169
|
-
|
|
6189
|
+
meeting.statsAnalyzer = {
|
|
6190
|
+
stopAnalyzer: sinon.stub().returns(Promise.resolve())
|
|
6191
|
+
};
|
|
6192
|
+
|
|
6193
|
+
meeting.reconnectionManager = {
|
|
6194
|
+
cleanUp: sinon.stub()
|
|
6195
|
+
};
|
|
6170
6196
|
|
|
6171
|
-
|
|
6197
|
+
meeting.cleanupLocalStreams=sinon.stub();
|
|
6198
|
+
meeting.closeRemoteStreams = sinon.stub().returns(Promise.resolve());
|
|
6199
|
+
meeting.closePeerConnections = sinon.stub().returns(Promise.resolve());
|
|
6200
|
+
meeting.unsetRemoteStreams = sinon.stub();
|
|
6201
|
+
meeting.unsetPeerConnections = sinon.stub();
|
|
6202
|
+
meeting.addMedia = sinon.stub().returns(Promise.resolve());
|
|
6203
|
+
meeting.mediaProperties.setMediaDirection = sinon.stub();
|
|
6172
6204
|
|
|
6173
|
-
sandbox.stub(meeting.reconnectionManager, 'reconnectMedia').returns(Promise.resolve());
|
|
6174
6205
|
sandbox
|
|
6175
6206
|
.stub(MeetingUtil, 'joinMeeting')
|
|
6176
6207
|
.returns(
|
|
@@ -6232,9 +6263,11 @@ describe('plugin-meetings', () => {
|
|
|
6232
6263
|
});
|
|
6233
6264
|
});
|
|
6234
6265
|
|
|
6235
|
-
it('should
|
|
6266
|
+
it('should cleanup on moveTo & addMedia after', async () => {
|
|
6236
6267
|
await meeting.moveTo('resourceId');
|
|
6237
6268
|
|
|
6269
|
+
assert.equal(meeting.isMoveToInProgress, true);
|
|
6270
|
+
|
|
6238
6271
|
await meeting.locusInfo.emitScoped(
|
|
6239
6272
|
{
|
|
6240
6273
|
file: 'locus-info',
|
|
@@ -6242,26 +6275,26 @@ describe('plugin-meetings', () => {
|
|
|
6242
6275
|
},
|
|
6243
6276
|
'SELF_OBSERVING'
|
|
6244
6277
|
);
|
|
6278
|
+
|
|
6245
6279
|
|
|
6246
|
-
//
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
await
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
});
|
|
6280
|
+
// Verify that the event handler behaves as expected
|
|
6281
|
+
expect(meeting.statsAnalyzer.stopAnalyzer.calledOnce).to.be.true;
|
|
6282
|
+
expect(meeting.closeRemoteStreams.calledOnce).to.be.true;
|
|
6283
|
+
await testUtils.flushPromises();
|
|
6284
|
+
expect(meeting.closePeerConnections.calledOnce).to.be.true;
|
|
6285
|
+
await testUtils.flushPromises();
|
|
6286
|
+
expect(meeting.cleanupLocalStreams.calledOnce).to.be.true;
|
|
6287
|
+
expect(meeting.unsetRemoteStreams.calledOnce).to.be.true;
|
|
6288
|
+
expect(meeting.unsetPeerConnections.calledOnce).to.be.true;
|
|
6289
|
+
expect(meeting.reconnectionManager.cleanUp.calledOnce).to.be.true;
|
|
6290
|
+
expect(meeting.mediaProperties.setMediaDirection.calledOnce).to.be.true;
|
|
6291
|
+
expect(meeting.addMedia.calledOnceWithExactly({
|
|
6292
|
+
audioEnabled: false,
|
|
6293
|
+
videoEnabled: false,
|
|
6294
|
+
shareVideoEnabled: true
|
|
6295
|
+
})).to.be.true;
|
|
6296
|
+
await testUtils.flushPromises();
|
|
6297
|
+
assert.equal(meeting.isMoveToInProgress, false);
|
|
6265
6298
|
});
|
|
6266
6299
|
|
|
6267
6300
|
it('should throw an error if moveTo call fails', async () => {
|
|
@@ -6269,6 +6302,7 @@ describe('plugin-meetings', () => {
|
|
|
6269
6302
|
try {
|
|
6270
6303
|
await meeting.moveTo('resourceId');
|
|
6271
6304
|
} catch {
|
|
6305
|
+
assert.equal(meeting.isMoveToInProgress, false);
|
|
6272
6306
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
6273
6307
|
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_TO_FAILURE, {
|
|
6274
6308
|
correlation_id: meeting.correlationId,
|
|
@@ -6290,6 +6324,7 @@ describe('plugin-meetings', () => {
|
|
|
6290
6324
|
'SELF_OBSERVING'
|
|
6291
6325
|
);
|
|
6292
6326
|
} catch {
|
|
6327
|
+
assert.equal(meeting.isMoveToInProgress, false);
|
|
6293
6328
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
6294
6329
|
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_TO_FAILURE, {
|
|
6295
6330
|
correlation_id: meeting.correlationId,
|
|
@@ -7852,6 +7887,7 @@ describe('plugin-meetings', () => {
|
|
|
7852
7887
|
describe('#setUpLocusInfoSelfListener', () => {
|
|
7853
7888
|
it('listens to the self unadmitted guest event', (done) => {
|
|
7854
7889
|
meeting.startKeepAlive = sinon.stub();
|
|
7890
|
+
meeting.updateLLMConnection = sinon.stub();
|
|
7855
7891
|
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_UNADMITTED_GUEST', test1);
|
|
7856
7892
|
assert.calledOnceWithExactly(meeting.startKeepAlive);
|
|
7857
7893
|
assert.calledThrice(TriggerProxy.trigger);
|
|
@@ -7862,6 +7898,7 @@ describe('plugin-meetings', () => {
|
|
|
7862
7898
|
'meeting:self:lobbyWaiting',
|
|
7863
7899
|
{payload: test1}
|
|
7864
7900
|
);
|
|
7901
|
+
assert.calledOnce(meeting.updateLLMConnection);
|
|
7865
7902
|
done();
|
|
7866
7903
|
});
|
|
7867
7904
|
it('listens to the self admitted guest event', (done) => {
|
|
@@ -106,6 +106,12 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
106
106
|
},
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
mockWebex.internal = {
|
|
110
|
+
newMetrics: {
|
|
111
|
+
submitClientEvent: sinon.stub()
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
109
115
|
locusMediaRequest = new LocusMediaRequest({
|
|
110
116
|
device: {
|
|
111
117
|
url: 'deviceUrl',
|
|
@@ -113,6 +119,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
113
119
|
regionCode: 'regionCode',
|
|
114
120
|
},
|
|
115
121
|
correlationId: 'correlationId',
|
|
122
|
+
meetingId: 'meetingId',
|
|
116
123
|
preferTranscoding: true,
|
|
117
124
|
}, {
|
|
118
125
|
parent: mockWebex,
|
|
@@ -134,6 +141,27 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
134
141
|
await sendRoapMessage('OFFER');
|
|
135
142
|
|
|
136
143
|
webexRequestStub.resetHistory();
|
|
144
|
+
mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const checkMetrics = (expectedMetrics: boolean = true) => {
|
|
148
|
+
if (expectedMetrics) {
|
|
149
|
+
assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
|
|
150
|
+
name: 'client.locus.media.request',
|
|
151
|
+
options: {
|
|
152
|
+
meetingId: 'meetingId',
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
|
|
157
|
+
name: 'client.locus.media.response',
|
|
158
|
+
options: {
|
|
159
|
+
meetingId: 'meetingId',
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
|
|
164
|
+
}
|
|
137
165
|
}
|
|
138
166
|
|
|
139
167
|
it('sends a roap message', async () => {
|
|
@@ -146,6 +174,21 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
146
174
|
uri: 'fakeMeetingSelfUrl/media',
|
|
147
175
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
|
148
176
|
});
|
|
177
|
+
|
|
178
|
+
checkMetrics();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('sends correct metric event when roap message fails', async () => {
|
|
182
|
+
webexRequestStub.rejects({code: 300, message: 'fake error'});
|
|
183
|
+
await assert.isRejected(sendRoapMessage('OFFER'));
|
|
184
|
+
|
|
185
|
+
assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
|
|
186
|
+
name: 'client.locus.media.response',
|
|
187
|
+
options: {
|
|
188
|
+
meetingId: 'meetingId',
|
|
189
|
+
rawError: {code: 300, message: 'fake error'},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
149
192
|
});
|
|
150
193
|
|
|
151
194
|
it('sends a local mute request', async () => {
|
|
@@ -160,6 +203,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
160
203
|
uri: 'fakeMeetingSelfUrl/media',
|
|
161
204
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
|
|
162
205
|
});
|
|
206
|
+
|
|
207
|
+
checkMetrics(false);
|
|
163
208
|
});
|
|
164
209
|
|
|
165
210
|
it('sends a local mute request with sequence', async () => {
|
|
@@ -207,6 +252,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
207
252
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
|
208
253
|
});
|
|
209
254
|
|
|
255
|
+
checkMetrics(false);
|
|
210
256
|
});
|
|
211
257
|
|
|
212
258
|
it('sends a local mute request with the last audio/video mute values', async () => {
|
|
@@ -225,6 +271,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
225
271
|
body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
|
|
226
272
|
});
|
|
227
273
|
|
|
274
|
+
checkMetrics(false);
|
|
228
275
|
});
|
|
229
276
|
|
|
230
277
|
it('sends only roap when roap and local mute are requested', async () => {
|
|
@@ -242,6 +289,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
242
289
|
uri: 'fakeMeetingSelfUrl/media',
|
|
243
290
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
|
|
244
291
|
});
|
|
292
|
+
|
|
293
|
+
checkMetrics();
|
|
245
294
|
});
|
|
246
295
|
|
|
247
296
|
describe('queueing', () => {
|
|
@@ -370,16 +370,6 @@ describe('plugin-meetings', () => {
|
|
|
370
370
|
sequence: {},
|
|
371
371
|
type: 'LocalMute',
|
|
372
372
|
});
|
|
373
|
-
|
|
374
|
-
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
375
|
-
name: 'client.locus.media.request',
|
|
376
|
-
options: {meetingId: meeting.id},
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
380
|
-
name: 'client.locus.media.response',
|
|
381
|
-
options: {meetingId: meeting.id},
|
|
382
|
-
});
|
|
383
373
|
});
|
|
384
374
|
});
|
|
385
375
|
|
|
@@ -101,14 +101,7 @@ describe('plugin-meetings', () => {
|
|
|
101
101
|
],
|
|
102
102
|
transcript_language_code: "en"
|
|
103
103
|
}
|
|
104
|
-
]
|
|
105
|
-
transcript: {
|
|
106
|
-
text: "Don't bother me talking I'm just going to get the transcript data that is interim and I needed if I keep talking, I get the interim data",
|
|
107
|
-
csis: [
|
|
108
|
-
1234867712
|
|
109
|
-
],
|
|
110
|
-
transcript_language_code: "en"
|
|
111
|
-
}
|
|
104
|
+
]
|
|
112
105
|
};
|
|
113
106
|
});
|
|
114
107
|
|
|
@@ -160,7 +153,6 @@ describe('plugin-meetings', () => {
|
|
|
160
153
|
it('should process new final captions correctly', () => {
|
|
161
154
|
let transcriptData = fakeMeeting.transcription;
|
|
162
155
|
let transcriptId = fakeVoiceaPayload.transcriptId;
|
|
163
|
-
delete fakeVoiceaPayload.transcripts;
|
|
164
156
|
|
|
165
157
|
// Assuming that processNewCaptions is a pure function that doesn't mutate the input but returns a new state
|
|
166
158
|
processNewCaptions({
|
|
@@ -169,7 +161,7 @@ describe('plugin-meetings', () => {
|
|
|
169
161
|
});
|
|
170
162
|
|
|
171
163
|
// Check if speaker details are cached if needed
|
|
172
|
-
const csisKey = fakeVoiceaPayload.
|
|
164
|
+
const csisKey = fakeVoiceaPayload.transcripts[0].csis[0];
|
|
173
165
|
const speaker = transcriptData.speakerProxy[csisKey];
|
|
174
166
|
expect(speaker).to.exist;
|
|
175
167
|
|
|
@@ -178,6 +170,7 @@ describe('plugin-meetings', () => {
|
|
|
178
170
|
|
|
179
171
|
//check if the interim caption is removed
|
|
180
172
|
const oldInterimCaption = transcriptData.captions.find(caption => caption.id === `${transcriptId}_${speaker.speakerId}`);
|
|
173
|
+
console.log(oldInterimCaption);
|
|
181
174
|
expect(oldInterimCaption).to.not.exist;
|
|
182
175
|
|
|
183
176
|
// Check the final caption data
|
|
@@ -186,8 +179,8 @@ describe('plugin-meetings', () => {
|
|
|
186
179
|
expect(newCaption).to.include({
|
|
187
180
|
id: transcriptId,
|
|
188
181
|
isFinal: fakeVoiceaPayload.isFinal,
|
|
189
|
-
text: fakeVoiceaPayload.
|
|
190
|
-
currentSpokenLanguage: fakeVoiceaPayload.
|
|
182
|
+
text: fakeVoiceaPayload.transcripts[0].text,
|
|
183
|
+
currentSpokenLanguage: fakeVoiceaPayload.transcripts[0].transcript_language_code,
|
|
191
184
|
});
|
|
192
185
|
|
|
193
186
|
// Check the speaker data in the new caption
|
|
@@ -197,7 +190,6 @@ describe('plugin-meetings', () => {
|
|
|
197
190
|
it('should process new interim captions correctly', () => {
|
|
198
191
|
let transcriptData = fakeMeeting.transcription;
|
|
199
192
|
let transcriptId = fakeVoiceaPayload.transcriptId;
|
|
200
|
-
delete fakeVoiceaPayload.transcript;
|
|
201
193
|
|
|
202
194
|
transcriptData.captions.splice(transcriptData.length - 1, 1);
|
|
203
195
|
fakeVoiceaPayload.isFinal = false;
|
|
@@ -232,7 +224,6 @@ describe('plugin-meetings', () => {
|
|
|
232
224
|
it('should process interim captions with an existing one correctly', () => {
|
|
233
225
|
let transcriptData = fakeMeeting.transcription;
|
|
234
226
|
let transcriptId = fakeVoiceaPayload.transcriptId;
|
|
235
|
-
delete fakeVoiceaPayload.transcript;
|
|
236
227
|
fakeVoiceaPayload.isFinal = false;
|
|
237
228
|
|
|
238
229
|
processNewCaptions({
|