@webex/plugin-meetings 2.60.1-next.13 → 2.60.1-next.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/interpretation/index.js +1 -1
  4. package/dist/interpretation/siLanguage.js +1 -1
  5. package/dist/mediaQualityMetrics/config.d.ts +103 -99
  6. package/dist/mediaQualityMetrics/config.js +133 -129
  7. package/dist/mediaQualityMetrics/config.js.map +1 -1
  8. package/dist/meeting/index.js +4 -2
  9. package/dist/meeting/index.js.map +1 -1
  10. package/dist/meeting/request.d.ts +2 -0
  11. package/dist/meeting/request.js +4 -0
  12. package/dist/meeting/request.js.map +1 -1
  13. package/dist/meetings/index.js +19 -0
  14. package/dist/meetings/index.js.map +1 -1
  15. package/dist/meetings/util.js +1 -1
  16. package/dist/meetings/util.js.map +1 -1
  17. package/dist/metrics/constants.d.ts +2 -0
  18. package/dist/metrics/constants.js +3 -1
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/reachability/index.js +14 -20
  21. package/dist/reachability/index.js.map +1 -1
  22. package/dist/reconnection-manager/index.js +63 -43
  23. package/dist/reconnection-manager/index.js.map +1 -1
  24. package/dist/roap/turnDiscovery.d.ts +18 -2
  25. package/dist/roap/turnDiscovery.js +163 -69
  26. package/dist/roap/turnDiscovery.js.map +1 -1
  27. package/dist/rtcMetrics/index.d.ts +7 -0
  28. package/dist/rtcMetrics/index.js +38 -1
  29. package/dist/rtcMetrics/index.js.map +1 -1
  30. package/dist/statsAnalyzer/index.js +135 -23
  31. package/dist/statsAnalyzer/index.js.map +1 -1
  32. package/dist/statsAnalyzer/mqaUtil.d.ts +28 -4
  33. package/dist/statsAnalyzer/mqaUtil.js +278 -148
  34. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  35. package/dist/webinar/index.js +1 -1
  36. package/package.json +21 -21
  37. package/src/mediaQualityMetrics/config.ts +107 -107
  38. package/src/meeting/index.ts +2 -0
  39. package/src/meeting/request.ts +6 -0
  40. package/src/meetings/index.ts +22 -0
  41. package/src/meetings/util.ts +1 -1
  42. package/src/metrics/constants.ts +2 -0
  43. package/src/reachability/index.ts +0 -6
  44. package/src/reconnection-manager/index.ts +18 -7
  45. package/src/roap/turnDiscovery.ts +100 -24
  46. package/src/rtcMetrics/index.ts +43 -1
  47. package/src/statsAnalyzer/index.ts +158 -24
  48. package/src/statsAnalyzer/mqaUtil.ts +302 -154
  49. package/test/unit/spec/meeting/index.js +46 -0
  50. package/test/unit/spec/meeting/request.js +2 -0
  51. package/test/unit/spec/meetings/utils.js +35 -8
  52. package/test/unit/spec/reachability/index.ts +74 -0
  53. package/test/unit/spec/reconnection-manager/index.js +36 -1
  54. package/test/unit/spec/roap/turnDiscovery.ts +326 -76
  55. package/test/unit/spec/rtcMetrics/index.ts +32 -3
  56. package/test/unit/spec/stats-analyzer/index.js +439 -1
  57. package/test/utils/webex-test-users.js +12 -4
@@ -244,6 +244,7 @@ describe('plugin-meetings', () => {
244
244
  let statsAnalyzer;
245
245
  let mqeData;
246
246
  let loggerSpy;
247
+ let receiveSlot;
247
248
 
248
249
  let receivedEventsData = {
249
250
  local: {},
@@ -273,6 +274,7 @@ describe('plugin-meetings', () => {
273
274
 
274
275
  beforeEach(() => {
275
276
  clock = sinon.useFakeTimers();
277
+ receiveSlot = undefined;
276
278
 
277
279
  resetReceivedEvents();
278
280
 
@@ -437,7 +439,7 @@ describe('plugin-meetings', () => {
437
439
 
438
440
  networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
439
441
 
440
- statsAnalyzer = new StatsAnalyzer(initialConfig, () => ({}), networkQualityMonitor);
442
+ statsAnalyzer = new StatsAnalyzer(initialConfig, () => (receiveSlot), networkQualityMonitor);
441
443
 
442
444
  statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
443
445
  receivedEventsData.local.started = data;
@@ -912,6 +914,442 @@ describe('plugin-meetings', () => {
912
914
 
913
915
  assert(loggerSpy.neverCalledWith('StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received'));
914
916
  });
917
+
918
+ it('logs a message when no packets are recieved for a receive slot with sourceState "live"', async () => {
919
+ receiveSlot = {
920
+ sourceState: 'live',
921
+ csi: 2,
922
+ id: "4",
923
+ };
924
+
925
+ await startStatsAnalyzer();
926
+
927
+ // don't increase the packets when time progresses.
928
+ await progressTime();
929
+
930
+ assert.calledWith(loggerSpy, 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ', 0);
931
+ });
932
+
933
+ ["avatar", "invalid", "no source", "bandwidth limited", "policy violation"].forEach((sourceState) => {
934
+ it(`does not log a message when no packets are recieved for a receive slot with sourceState "${sourceState}"`, async () => {
935
+ receiveSlot = {
936
+ sourceState,
937
+ csi: 2,
938
+ id: "4",
939
+ };
940
+
941
+ await startStatsAnalyzer();
942
+
943
+ // don't increase the packets when time progresses.
944
+ await progressTime();
945
+
946
+ assert.neverCalledWith(loggerSpy, 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ', 0);
947
+ });
948
+ });
949
+
950
+ it(`does not log a message if receiveSlot is undefined`, async () => {
951
+ await startStatsAnalyzer();
952
+
953
+ // don't increase the packets when time progresses.
954
+ await progressTime();
955
+
956
+ assert.neverCalledWith(loggerSpy, 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot "". Total packets received on slot: ', 0);
957
+ });
958
+
959
+ it('has the correct number of senders and receivers (2)', async () => {
960
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
961
+
962
+ await progressTime();
963
+
964
+ assert.lengthOf(mqeData.audioTransmit, 2);
965
+ assert.lengthOf(mqeData.audioReceive, 2);
966
+ assert.lengthOf(mqeData.videoTransmit, 2);
967
+ assert.lengthOf(mqeData.videoReceive, 2);
968
+ });
969
+
970
+ it('has one stream per sender/reciever', async () => {
971
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
972
+
973
+ await progressTime();
974
+
975
+ assert.deepEqual(
976
+ mqeData.audioTransmit[0].streams,
977
+ [
978
+ {
979
+ common: {
980
+ codec: 'opus',
981
+ csi: [],
982
+ requestedBitrate: 0,
983
+ requestedFrames: 0,
984
+ rtpPackets: 0,
985
+ ssci: 0,
986
+ transmittedBitrate: 0.13333333333333333,
987
+ transmittedFrameRate: 0
988
+ },
989
+ transmittedKeyFrames: 0,
990
+ requestedKeyFrames: 0
991
+ }
992
+ ]
993
+ );
994
+ assert.deepEqual(
995
+ mqeData.audioTransmit[1].streams,
996
+ [
997
+ {
998
+ common: {
999
+ codec: 'opus',
1000
+ csi: [],
1001
+ requestedBitrate: 0,
1002
+ requestedFrames: 0,
1003
+ rtpPackets: 0,
1004
+ ssci: 0,
1005
+ transmittedBitrate: 0.13333333333333333,
1006
+ transmittedFrameRate: 0
1007
+ },
1008
+ transmittedKeyFrames: 0,
1009
+ requestedKeyFrames: 0
1010
+ }
1011
+ ]
1012
+ );
1013
+ assert.deepEqual(
1014
+ mqeData.audioReceive[0].streams,
1015
+ [
1016
+ {
1017
+ common: {
1018
+ codec: 'opus',
1019
+ concealedFrames: 0,
1020
+ csi: [],
1021
+ maxConcealRunLength: 0,
1022
+ optimalBitrate: 0,
1023
+ optimalFrameRate: 0,
1024
+ receivedBitrate: 0.13333333333333333,
1025
+ receivedFrameRate: 0,
1026
+ renderedFrameRate: 0,
1027
+ requestedBitrate: 0,
1028
+ requestedFrameRate: 0,
1029
+ rtpEndToEndLost: 0,
1030
+ maxRtpJitter: 0,
1031
+ meanRtpJitter: 0,
1032
+ rtpPackets: 0,
1033
+ ssci: 0,
1034
+ rtpJitter: 0,
1035
+ framesDropped: 0,
1036
+ framesReceived: 0
1037
+ }
1038
+ }
1039
+ ]
1040
+ );
1041
+ assert.deepEqual(
1042
+ mqeData.audioReceive[1].streams,
1043
+ [
1044
+ {
1045
+ common: {
1046
+ codec: 'opus',
1047
+ concealedFrames: 0,
1048
+ csi: [],
1049
+ maxConcealRunLength: 0,
1050
+ optimalBitrate: 0,
1051
+ optimalFrameRate: 0,
1052
+ receivedBitrate: 0.13333333333333333,
1053
+ receivedFrameRate: 0,
1054
+ renderedFrameRate: 0,
1055
+ requestedBitrate: 0,
1056
+ requestedFrameRate: 0,
1057
+ rtpEndToEndLost: 0,
1058
+ maxRtpJitter: 0,
1059
+ meanRtpJitter: 0,
1060
+ rtpPackets: 0,
1061
+ ssci: 0,
1062
+ rtpJitter: 0,
1063
+ framesDropped: 0,
1064
+ framesReceived: 0
1065
+ }
1066
+ }
1067
+ ]
1068
+ );
1069
+ assert.deepEqual(
1070
+ mqeData.videoTransmit[0].streams,
1071
+ [
1072
+ {
1073
+ common: {
1074
+ codec: 'H264',
1075
+ csi: [],
1076
+ duplicateSsci: 0,
1077
+ requestedBitrate: 0,
1078
+ requestedFrames: 0,
1079
+ rtpPackets: 0,
1080
+ ssci: 0,
1081
+ transmittedBitrate: 0.13333333333333333,
1082
+ transmittedFrameRate: 0
1083
+ },
1084
+ h264CodecProfile: 'BP',
1085
+ isAvatar: false,
1086
+ isHardwareEncoded: false,
1087
+ localConfigurationChanges: 2,
1088
+ maxFrameQp: 0,
1089
+ maxNoiseLevel: 0,
1090
+ minRegionQp: 0,
1091
+ remoteConfigurationChanges: 0,
1092
+ requestedFrameSize: 0,
1093
+ requestedKeyFrames: 0,
1094
+ transmittedFrameSize: 0,
1095
+ transmittedHeight: 0,
1096
+ transmittedKeyFrames: 0,
1097
+ transmittedKeyFramesClient: 0,
1098
+ transmittedKeyFramesConfigurationChange: 0,
1099
+ transmittedKeyFramesFeedback: 0,
1100
+ transmittedKeyFramesLocalDrop: 0,
1101
+ transmittedKeyFramesOtherLayer: 0,
1102
+ transmittedKeyFramesPeriodic: 0,
1103
+ transmittedKeyFramesSceneChange: 0,
1104
+ transmittedKeyFramesStartup: 0,
1105
+ transmittedKeyFramesUnknown: 0,
1106
+ transmittedWidth: 0
1107
+ }
1108
+ ]
1109
+ );
1110
+ assert.deepEqual(
1111
+ mqeData.videoTransmit[1].streams,
1112
+ [
1113
+ {
1114
+ common: {
1115
+ codec: 'H264',
1116
+ csi: [],
1117
+ duplicateSsci: 0,
1118
+ requestedBitrate: 0,
1119
+ requestedFrames: 0,
1120
+ rtpPackets: 0,
1121
+ ssci: 0,
1122
+ transmittedBitrate: 0.13333333333333333,
1123
+ transmittedFrameRate: 0
1124
+ },
1125
+ h264CodecProfile: 'BP',
1126
+ isAvatar: false,
1127
+ isHardwareEncoded: false,
1128
+ localConfigurationChanges: 2,
1129
+ maxFrameQp: 0,
1130
+ maxNoiseLevel: 0,
1131
+ minRegionQp: 0,
1132
+ remoteConfigurationChanges: 0,
1133
+ requestedFrameSize: 0,
1134
+ requestedKeyFrames: 0,
1135
+ transmittedFrameSize: 0,
1136
+ transmittedHeight: 0,
1137
+ transmittedKeyFrames: 0,
1138
+ transmittedKeyFramesClient: 0,
1139
+ transmittedKeyFramesConfigurationChange: 0,
1140
+ transmittedKeyFramesFeedback: 0,
1141
+ transmittedKeyFramesLocalDrop: 0,
1142
+ transmittedKeyFramesOtherLayer: 0,
1143
+ transmittedKeyFramesPeriodic: 0,
1144
+ transmittedKeyFramesSceneChange: 0,
1145
+ transmittedKeyFramesStartup: 0,
1146
+ transmittedKeyFramesUnknown: 0,
1147
+ transmittedWidth: 0
1148
+ }
1149
+ ]
1150
+ );
1151
+ assert.deepEqual(
1152
+ mqeData.videoReceive[0].streams,
1153
+ [
1154
+ {
1155
+ common: {
1156
+ codec: 'H264',
1157
+ concealedFrames: 0,
1158
+ csi: [],
1159
+ maxConcealRunLength: 0,
1160
+ optimalBitrate: 0,
1161
+ optimalFrameRate: 0,
1162
+ receivedBitrate: 0.13333333333333333,
1163
+ receivedFrameRate: 0,
1164
+ renderedFrameRate: 0,
1165
+ requestedBitrate: 0,
1166
+ requestedFrameRate: 0,
1167
+ rtpEndToEndLost: 0,
1168
+ rtpJitter: 0,
1169
+ rtpPackets: 0,
1170
+ ssci: 0,
1171
+ framesDropped: 0
1172
+ },
1173
+ h264CodecProfile: 'BP',
1174
+ isActiveSpeaker: true,
1175
+ optimalFrameSize: 0,
1176
+ receivedFrameSize: 3600,
1177
+ receivedHeight: 720,
1178
+ receivedKeyFrames: 0,
1179
+ receivedKeyFramesForRequest: 0,
1180
+ receivedKeyFramesSourceChange: 0,
1181
+ receivedKeyFramesUnknown: 0,
1182
+ receivedWidth: 1280,
1183
+ requestedFrameSize: 0,
1184
+ requestedKeyFrames: 0
1185
+ }
1186
+ ]
1187
+ );
1188
+ assert.deepEqual(
1189
+ mqeData.videoReceive[1].streams,
1190
+ [
1191
+ {
1192
+ common: {
1193
+ codec: 'H264',
1194
+ concealedFrames: 0,
1195
+ csi: [],
1196
+ maxConcealRunLength: 0,
1197
+ optimalBitrate: 0,
1198
+ optimalFrameRate: 0,
1199
+ receivedBitrate: 0.13333333333333333,
1200
+ receivedFrameRate: 0,
1201
+ renderedFrameRate: 0,
1202
+ requestedBitrate: 0,
1203
+ requestedFrameRate: 0,
1204
+ rtpEndToEndLost: 0,
1205
+ rtpJitter: 0,
1206
+ rtpPackets: 0,
1207
+ ssci: 0,
1208
+ framesDropped: 0
1209
+ },
1210
+ h264CodecProfile: 'BP',
1211
+ isActiveSpeaker: true,
1212
+ optimalFrameSize: 0,
1213
+ receivedFrameSize: 3600,
1214
+ receivedHeight: 720,
1215
+ receivedKeyFrames: 0,
1216
+ receivedKeyFramesForRequest: 0,
1217
+ receivedKeyFramesSourceChange: 0,
1218
+ receivedKeyFramesUnknown: 0,
1219
+ receivedWidth: 1280,
1220
+ requestedFrameSize: 0,
1221
+ requestedKeyFrames: 0
1222
+ }
1223
+ ]
1224
+ );
1225
+ });
1226
+
1227
+ it('has three streams for video receivers when three exist', async () => {
1228
+ pc.getTransceiverStats = sinon.stub().resolves({
1229
+ audio: {
1230
+ senders: [fakeStats.audio.senders[0]],
1231
+ receivers: [fakeStats.audio.receivers[0]],
1232
+ },
1233
+ video: {
1234
+ senders: [fakeStats.video.senders[0]],
1235
+ receivers: [fakeStats.video.receivers[0], fakeStats.video.receivers[0], fakeStats.video.receivers[0]],
1236
+ },
1237
+ screenShareAudio: {
1238
+ senders: [fakeStats.audio.senders[0]],
1239
+ receivers: [fakeStats.audio.receivers[0]],
1240
+ },
1241
+ screenShareVideo: {
1242
+ senders: [fakeStats.video.senders[0]],
1243
+ receivers: [fakeStats.video.receivers[0]],
1244
+ },
1245
+ });
1246
+
1247
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
1248
+
1249
+ await progressTime();
1250
+
1251
+ assert.deepEqual(
1252
+ mqeData.videoReceive[0].streams,
1253
+ [
1254
+ {
1255
+ common: {
1256
+ codec: 'H264',
1257
+ concealedFrames: 0,
1258
+ csi: [],
1259
+ maxConcealRunLength: 0,
1260
+ optimalBitrate: 0,
1261
+ optimalFrameRate: 0,
1262
+ receivedBitrate: 0.13333333333333333,
1263
+ receivedFrameRate: 0,
1264
+ renderedFrameRate: 0,
1265
+ requestedBitrate: 0,
1266
+ requestedFrameRate: 0,
1267
+ rtpEndToEndLost: 0,
1268
+ rtpJitter: 0,
1269
+ rtpPackets: 0,
1270
+ ssci: 0,
1271
+ framesDropped: 0
1272
+ },
1273
+ h264CodecProfile: 'BP',
1274
+ isActiveSpeaker: true,
1275
+ optimalFrameSize: 0,
1276
+ receivedFrameSize: 3600,
1277
+ receivedHeight: 720,
1278
+ receivedKeyFrames: 0,
1279
+ receivedKeyFramesForRequest: 0,
1280
+ receivedKeyFramesSourceChange: 0,
1281
+ receivedKeyFramesUnknown: 0,
1282
+ receivedWidth: 1280,
1283
+ requestedFrameSize: 0,
1284
+ requestedKeyFrames: 0
1285
+ },
1286
+ {
1287
+ common: {
1288
+ codec: 'H264',
1289
+ concealedFrames: 0,
1290
+ csi: [],
1291
+ maxConcealRunLength: 0,
1292
+ optimalBitrate: 0,
1293
+ optimalFrameRate: 0,
1294
+ receivedBitrate: 0.13333333333333333,
1295
+ receivedFrameRate: 0,
1296
+ renderedFrameRate: 0,
1297
+ requestedBitrate: 0,
1298
+ requestedFrameRate: 0,
1299
+ rtpEndToEndLost: 0,
1300
+ rtpJitter: 0,
1301
+ rtpPackets: 0,
1302
+ ssci: 0,
1303
+ framesDropped: 0
1304
+ },
1305
+ h264CodecProfile: 'BP',
1306
+ isActiveSpeaker: true,
1307
+ optimalFrameSize: 0,
1308
+ receivedFrameSize: 3600,
1309
+ receivedHeight: 720,
1310
+ receivedKeyFrames: 0,
1311
+ receivedKeyFramesForRequest: 0,
1312
+ receivedKeyFramesSourceChange: 0,
1313
+ receivedKeyFramesUnknown: 0,
1314
+ receivedWidth: 1280,
1315
+ requestedFrameSize: 0,
1316
+ requestedKeyFrames: 0
1317
+ },
1318
+ {
1319
+ common: {
1320
+ codec: 'H264',
1321
+ concealedFrames: 0,
1322
+ csi: [],
1323
+ maxConcealRunLength: 0,
1324
+ optimalBitrate: 0,
1325
+ optimalFrameRate: 0,
1326
+ receivedBitrate: 0.13333333333333333,
1327
+ receivedFrameRate: 0,
1328
+ renderedFrameRate: 0,
1329
+ requestedBitrate: 0,
1330
+ requestedFrameRate: 0,
1331
+ rtpEndToEndLost: 0,
1332
+ rtpJitter: 0,
1333
+ rtpPackets: 0,
1334
+ ssci: 0,
1335
+ framesDropped: 0
1336
+ },
1337
+ h264CodecProfile: 'BP',
1338
+ isActiveSpeaker: true,
1339
+ optimalFrameSize: 0,
1340
+ receivedFrameSize: 3600,
1341
+ receivedHeight: 720,
1342
+ receivedKeyFrames: 0,
1343
+ receivedKeyFramesForRequest: 0,
1344
+ receivedKeyFramesSourceChange: 0,
1345
+ receivedKeyFramesUnknown: 0,
1346
+ receivedWidth: 1280,
1347
+ requestedFrameSize: 0,
1348
+ requestedKeyFrames: 0
1349
+ }
1350
+ ]
1351
+ );
1352
+ });
915
1353
  });
916
1354
  });
917
1355
  });
@@ -17,7 +17,7 @@ require('@webex/plugin-people');
17
17
  require('@webex/plugin-rooms');
18
18
  require('@webex/plugin-meetings');
19
19
 
20
- const generateTestUsers = (options = {}) => {
20
+ const generateTestUsers = async (options = {}) => {
21
21
  options.config = options.config || {};
22
22
  options.config.orgId = options.config.orgId || process.env.WEBEX_CONVERGED_ORG_ID;
23
23
 
@@ -30,7 +30,8 @@ const generateTestUsers = (options = {}) => {
30
30
  // Pause for 5 seconds for CI
31
31
  await new Promise((done) => setTimeout(done, 5000));
32
32
 
33
- const userRegisterPromises = [];
33
+ const webexRegisterPromises = [];
34
+ const userWebexMeetings = [];
34
35
 
35
36
  userSet.forEach((user) => {
36
37
  // eslint-disable-next-line no-param-reassign
@@ -44,12 +45,19 @@ const generateTestUsers = (options = {}) => {
44
45
  },
45
46
  });
46
47
 
48
+ webexRegisterPromises.push(new Promise((resolve) => {
49
+ user.webex.on('ready', resolve);
50
+ }));
51
+
47
52
  user.webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
48
53
 
49
- userRegisterPromises.push(user.webex.meetings.register());
54
+ userWebexMeetings.push(user.webex.meetings);
50
55
  });
51
56
 
52
- return Promise.all(userRegisterPromises).then(() => userSet);
57
+ // wait for webex to be ready before registering meetings
58
+ return Promise.all(webexRegisterPromises).then(() => {
59
+ return Promise.all(userWebexMeetings.map(m => m.register())).then(() => userSet);
60
+ })
53
61
  })
54
62
  .catch((error) => {
55
63
  console.error('#generateTestUsers=>ERROR', error);