@webex/plugin-meetings 2.60.1-next.16 → 2.60.1-next.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +4 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +27 -8
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/index.d.ts +1 -1
- package/dist/meetings/index.js +6 -20
- package/dist/meetings/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +22 -26
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/statsAnalyzer/index.d.ts +5 -1
- package/dist/statsAnalyzer/index.js +75 -88
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -23
- package/src/constants.ts +1 -0
- package/src/locus-info/index.ts +4 -1
- package/src/meeting/index.ts +25 -1
- package/src/meetings/index.ts +10 -23
- package/src/reconnection-manager/index.ts +11 -13
- package/src/statsAnalyzer/index.ts +131 -153
- package/test/integration/spec/journey.js +2 -2
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/locus-info/index.js +13 -6
- package/test/unit/spec/meeting/index.js +49 -1
- package/test/unit/spec/meetings/index.js +21 -0
- package/test/unit/spec/metrics/index.js +1 -2
- package/test/unit/spec/reconnection-manager/index.js +1 -11
- package/test/unit/spec/stats-analyzer/index.js +686 -493
package/src/meetings/index.ts
CHANGED
|
@@ -726,20 +726,12 @@ export default class Meetings extends WebexPlugin {
|
|
|
726
726
|
* @memberof Meetings
|
|
727
727
|
*/
|
|
728
728
|
public register() {
|
|
729
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
730
|
-
step: '[sdk] begin registration',
|
|
731
|
-
});
|
|
732
|
-
|
|
733
729
|
// @ts-ignore
|
|
734
730
|
if (!this.webex.canAuthorize) {
|
|
735
731
|
LoggerProxy.logger.error(
|
|
736
732
|
'Meetings:index#register --> ERROR, Unable to register, SDK cannot authorize'
|
|
737
733
|
);
|
|
738
734
|
|
|
739
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
740
|
-
step: '[sdk] cannot authorize',
|
|
741
|
-
});
|
|
742
|
-
|
|
743
735
|
return Promise.reject(new Error('SDK cannot authorize'));
|
|
744
736
|
}
|
|
745
737
|
|
|
@@ -748,17 +740,9 @@ export default class Meetings extends WebexPlugin {
|
|
|
748
740
|
'Meetings:index#register --> INFO, Meetings plugin already registered'
|
|
749
741
|
);
|
|
750
742
|
|
|
751
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
752
|
-
step: '[sdk] already registered',
|
|
753
|
-
});
|
|
754
|
-
|
|
755
743
|
return Promise.resolve();
|
|
756
744
|
}
|
|
757
745
|
|
|
758
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
759
|
-
step: '[sdk] begin Promise.all()',
|
|
760
|
-
});
|
|
761
|
-
|
|
762
746
|
return Promise.all([
|
|
763
747
|
this.fetchUserPreferredWebexSite(),
|
|
764
748
|
this.getGeoHint(),
|
|
@@ -780,9 +764,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
780
764
|
MeetingsUtil.checkH264Support.call(this),
|
|
781
765
|
])
|
|
782
766
|
.then(() => {
|
|
783
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
784
|
-
step: '[sdk] end Promise.all()',
|
|
785
|
-
});
|
|
786
767
|
this.listenForEvents();
|
|
787
768
|
Trigger.trigger(
|
|
788
769
|
this,
|
|
@@ -792,9 +773,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
792
773
|
},
|
|
793
774
|
EVENT_TRIGGERS.MEETINGS_REGISTERED
|
|
794
775
|
);
|
|
795
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_STEP, {
|
|
796
|
-
step: '[sdk] registration complete, triggered MEETINGS_REGISTERED event',
|
|
797
|
-
});
|
|
798
776
|
this.registered = true;
|
|
799
777
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_SUCCESS);
|
|
800
778
|
})
|
|
@@ -1406,13 +1384,22 @@ export default class Meetings extends WebexPlugin {
|
|
|
1406
1384
|
}
|
|
1407
1385
|
|
|
1408
1386
|
/**
|
|
1409
|
-
*
|
|
1387
|
+
* Syncs all the meetings from server. Does nothing and returns immediately if unverified guest.
|
|
1410
1388
|
* @param {boolean} keepOnlyLocusMeetings - whether the sync should keep only locus meetings or any other meeting in meetingCollection
|
|
1411
1389
|
* @returns {Promise<void>}
|
|
1412
1390
|
* @public
|
|
1413
1391
|
* @memberof Meetings
|
|
1414
1392
|
*/
|
|
1415
1393
|
public syncMeetings({keepOnlyLocusMeetings = true} = {}): Promise<void> {
|
|
1394
|
+
// @ts-ignore
|
|
1395
|
+
if (this.webex.credentials.isUnverifiedGuest) {
|
|
1396
|
+
LoggerProxy.logger.info(
|
|
1397
|
+
'Meetings:index#syncMeetings --> skipping meeting sync as unverified guest'
|
|
1398
|
+
);
|
|
1399
|
+
|
|
1400
|
+
return Promise.resolve();
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1416
1403
|
return this.request
|
|
1417
1404
|
.getActiveMeetings()
|
|
1418
1405
|
.then((locusArray) => {
|
|
@@ -448,19 +448,17 @@ export default class ReconnectionManager {
|
|
|
448
448
|
}
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
throw new NeedsRetryError(syncError);
|
|
463
|
-
}
|
|
451
|
+
try {
|
|
452
|
+
LoggerProxy.logger.info(
|
|
453
|
+
'ReconnectionManager:index#executeReconnection --> Updating meeting data from server.'
|
|
454
|
+
);
|
|
455
|
+
await this.webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
|
|
456
|
+
} catch (syncError) {
|
|
457
|
+
LoggerProxy.logger.info(
|
|
458
|
+
'ReconnectionManager:index#executeReconnection --> Unable to sync meetings, reconnecting.',
|
|
459
|
+
syncError
|
|
460
|
+
);
|
|
461
|
+
throw new NeedsRetryError(syncError);
|
|
464
462
|
}
|
|
465
463
|
|
|
466
464
|
// TODO: try to improve this logic as the reconnection manager saves the instance of deleted meeting object
|
|
@@ -63,7 +63,10 @@ const emptyReceiver = {
|
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
type ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;
|
|
66
|
-
|
|
66
|
+
type MediaStatus = {
|
|
67
|
+
actual?: any;
|
|
68
|
+
expected?: any;
|
|
69
|
+
};
|
|
67
70
|
/**
|
|
68
71
|
* Stats Analyzer class that will emit events based on detected quality
|
|
69
72
|
*
|
|
@@ -147,8 +150,17 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
147
150
|
* @memberof StatsAnalyzer
|
|
148
151
|
* @returns {void}
|
|
149
152
|
*/
|
|
150
|
-
public updateMediaStatus(status:
|
|
151
|
-
this.meetingMediaStatus =
|
|
153
|
+
public updateMediaStatus(status: MediaStatus) {
|
|
154
|
+
this.meetingMediaStatus = {
|
|
155
|
+
actual: {
|
|
156
|
+
...this.meetingMediaStatus?.actual,
|
|
157
|
+
...status?.actual,
|
|
158
|
+
},
|
|
159
|
+
expected: {
|
|
160
|
+
...this.meetingMediaStatus?.expected,
|
|
161
|
+
...status?.expected,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
/**
|
|
@@ -659,14 +671,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
659
671
|
.filter((key) => key.startsWith(keyPrefix))
|
|
660
672
|
.reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);
|
|
661
673
|
|
|
662
|
-
|
|
674
|
+
// Audio Transmit
|
|
675
|
+
if (this.lastStatsResults['audio-send']) {
|
|
663
676
|
// compare audio stats sent
|
|
664
677
|
// NOTE: relies on there being only one sender.
|
|
665
678
|
const currentStats = this.statsResults['audio-send'].send;
|
|
666
679
|
const previousStats = this.lastStatsResults['audio-send'].send;
|
|
667
680
|
|
|
668
681
|
if (
|
|
669
|
-
|
|
682
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
683
|
+
currentStats.totalPacketsSent === previousStats.totalPacketsSent) ||
|
|
670
684
|
currentStats.totalPacketsSent === 0
|
|
671
685
|
) {
|
|
672
686
|
LoggerProxy.logger.info(
|
|
@@ -675,7 +689,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
675
689
|
);
|
|
676
690
|
} else {
|
|
677
691
|
if (
|
|
678
|
-
|
|
692
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
693
|
+
currentStats.totalAudioEnergy === previousStats.totalAudioEnergy) ||
|
|
679
694
|
currentStats.totalAudioEnergy === 0
|
|
680
695
|
) {
|
|
681
696
|
LoggerProxy.logger.info(
|
|
@@ -684,7 +699,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
684
699
|
);
|
|
685
700
|
}
|
|
686
701
|
|
|
687
|
-
if (currentStats.audioLevel === 0) {
|
|
702
|
+
if (this.meetingMediaStatus.expected.sendAudio && currentStats.audioLevel === 0) {
|
|
688
703
|
LoggerProxy.logger.info(
|
|
689
704
|
`StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`
|
|
690
705
|
);
|
|
@@ -699,45 +714,33 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
699
714
|
);
|
|
700
715
|
}
|
|
701
716
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
'audio-recv',
|
|
712
|
-
'totalSamplesReceived'
|
|
713
|
-
);
|
|
714
|
-
|
|
715
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
716
|
-
LoggerProxy.logger.info(
|
|
717
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`,
|
|
718
|
-
currentPacketsReceived
|
|
719
|
-
);
|
|
720
|
-
} else if (
|
|
721
|
-
currentSamplesReceived === previousSamplesReceived ||
|
|
722
|
-
currentSamplesReceived === 0
|
|
723
|
-
) {
|
|
724
|
-
LoggerProxy.logger.info(
|
|
725
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`,
|
|
726
|
-
currentSamplesReceived
|
|
727
|
-
);
|
|
728
|
-
}
|
|
717
|
+
// Audio Receive
|
|
718
|
+
const currentAudioPacketsReceived = getCurrentStatsTotals(
|
|
719
|
+
'audio-recv',
|
|
720
|
+
'totalPacketsReceived'
|
|
721
|
+
);
|
|
722
|
+
const previousAudioPacketsReceived = getPreviousStatsTotals(
|
|
723
|
+
'audio-recv',
|
|
724
|
+
'totalPacketsReceived'
|
|
725
|
+
);
|
|
729
726
|
|
|
730
|
-
|
|
731
|
-
|
|
727
|
+
this.emitStartStopEvents(
|
|
728
|
+
'audio',
|
|
729
|
+
previousAudioPacketsReceived,
|
|
730
|
+
currentAudioPacketsReceived,
|
|
731
|
+
false
|
|
732
|
+
);
|
|
732
733
|
|
|
733
|
-
|
|
734
|
+
// Video Transmit
|
|
735
|
+
if (this.lastStatsResults['video-send']) {
|
|
734
736
|
// compare video stats sent
|
|
735
737
|
const currentStats = this.statsResults['video-send'].send;
|
|
736
738
|
const previousStats = this.lastStatsResults['video-send'].send;
|
|
737
739
|
|
|
738
740
|
if (
|
|
739
|
-
|
|
740
|
-
currentStats.totalPacketsSent ===
|
|
741
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
742
|
+
(currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
|
|
743
|
+
currentStats.totalPacketsSent === 0)
|
|
741
744
|
) {
|
|
742
745
|
LoggerProxy.logger.info(
|
|
743
746
|
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
|
|
@@ -745,8 +748,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
745
748
|
);
|
|
746
749
|
} else {
|
|
747
750
|
if (
|
|
748
|
-
|
|
749
|
-
currentStats.framesEncoded ===
|
|
751
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
752
|
+
(currentStats.framesEncoded === previousStats.framesEncoded ||
|
|
753
|
+
currentStats.framesEncoded === 0)
|
|
750
754
|
) {
|
|
751
755
|
LoggerProxy.logger.info(
|
|
752
756
|
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
|
|
@@ -755,9 +759,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
755
759
|
}
|
|
756
760
|
|
|
757
761
|
if (
|
|
758
|
-
this.
|
|
762
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
763
|
+
(this.statsResults['video-send'].send.framesSent ===
|
|
759
764
|
this.lastStatsResults['video-send'].send.framesSent ||
|
|
760
|
-
|
|
765
|
+
this.statsResults['video-send'].send.framesSent === 0)
|
|
761
766
|
) {
|
|
762
767
|
LoggerProxy.logger.info(
|
|
763
768
|
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
|
|
@@ -769,60 +774,28 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
769
774
|
this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);
|
|
770
775
|
}
|
|
771
776
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
const previousPacketsReceived = getPreviousStatsTotals(
|
|
776
|
-
'video-recv',
|
|
777
|
-
'totalPacketsReceived'
|
|
778
|
-
);
|
|
779
|
-
const currentFramesReceived = getCurrentStatsTotals('video-recv', 'framesReceived');
|
|
780
|
-
const previousFramesReceived = getPreviousStatsTotals('video-recv', 'framesReceived');
|
|
781
|
-
const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
|
|
782
|
-
const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
|
|
783
|
-
const currentFramesDropped = getCurrentStatsTotals('video-recv', 'framesDropped');
|
|
784
|
-
const previousFramesDropped = getPreviousStatsTotals('video-recv', 'framesDropped');
|
|
785
|
-
|
|
786
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
787
|
-
LoggerProxy.logger.info(
|
|
788
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`,
|
|
789
|
-
currentPacketsReceived
|
|
790
|
-
);
|
|
791
|
-
} else {
|
|
792
|
-
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
793
|
-
LoggerProxy.logger.info(
|
|
794
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames received`,
|
|
795
|
-
currentFramesReceived
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
800
|
-
LoggerProxy.logger.info(
|
|
801
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`,
|
|
802
|
-
currentFramesDecoded
|
|
803
|
-
);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (currentFramesDropped - previousFramesDropped > 10) {
|
|
807
|
-
LoggerProxy.logger.info(
|
|
808
|
-
`StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`,
|
|
809
|
-
currentFramesDropped - previousFramesDropped
|
|
810
|
-
);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
777
|
+
// Video Receive
|
|
778
|
+
const currentVideoFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
|
|
779
|
+
const previousVideoFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
|
|
813
780
|
|
|
814
|
-
|
|
815
|
-
|
|
781
|
+
this.emitStartStopEvents(
|
|
782
|
+
'video',
|
|
783
|
+
previousVideoFramesDecoded,
|
|
784
|
+
currentVideoFramesDecoded,
|
|
785
|
+
false
|
|
786
|
+
);
|
|
816
787
|
|
|
817
|
-
|
|
788
|
+
// Share Transmit
|
|
789
|
+
if (this.lastStatsResults['video-share-send']) {
|
|
818
790
|
// compare share stats sent
|
|
819
791
|
|
|
820
792
|
const currentStats = this.statsResults['video-share-send'].send;
|
|
821
793
|
const previousStats = this.lastStatsResults['video-share-send'].send;
|
|
822
794
|
|
|
823
795
|
if (
|
|
824
|
-
|
|
825
|
-
currentStats.totalPacketsSent ===
|
|
796
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
797
|
+
(currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
|
|
798
|
+
currentStats.totalPacketsSent === 0)
|
|
826
799
|
) {
|
|
827
800
|
LoggerProxy.logger.info(
|
|
828
801
|
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,
|
|
@@ -830,8 +803,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
830
803
|
);
|
|
831
804
|
} else {
|
|
832
805
|
if (
|
|
833
|
-
|
|
834
|
-
currentStats.framesEncoded ===
|
|
806
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
807
|
+
(currentStats.framesEncoded === previousStats.framesEncoded ||
|
|
808
|
+
currentStats.framesEncoded === 0)
|
|
835
809
|
) {
|
|
836
810
|
LoggerProxy.logger.info(
|
|
837
811
|
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,
|
|
@@ -840,9 +814,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
840
814
|
}
|
|
841
815
|
|
|
842
816
|
if (
|
|
843
|
-
this.
|
|
817
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
818
|
+
(this.statsResults['video-share-send'].send.framesSent ===
|
|
844
819
|
this.lastStatsResults['video-share-send'].send.framesSent ||
|
|
845
|
-
|
|
820
|
+
this.statsResults['video-share-send'].send.framesSent === 0)
|
|
846
821
|
) {
|
|
847
822
|
LoggerProxy.logger.info(
|
|
848
823
|
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,
|
|
@@ -850,58 +825,23 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
850
825
|
);
|
|
851
826
|
}
|
|
852
827
|
}
|
|
853
|
-
}
|
|
854
828
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
// compare share stats received
|
|
858
|
-
const currentPacketsReceived = getCurrentStatsTotals(
|
|
859
|
-
'video-share-recv',
|
|
860
|
-
'totalPacketsReceived'
|
|
861
|
-
);
|
|
862
|
-
const previousPacketsReceived = getPreviousStatsTotals(
|
|
863
|
-
'video-share-recv',
|
|
864
|
-
'totalPacketsReceived'
|
|
865
|
-
);
|
|
866
|
-
const currentFramesReceived = getCurrentStatsTotals('video-share-recv', 'framesReceived');
|
|
867
|
-
const previousFramesReceived = getPreviousStatsTotals('video-share-recv', 'framesReceived');
|
|
868
|
-
const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
|
|
869
|
-
const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');
|
|
870
|
-
const currentFramesDropped = getCurrentStatsTotals('video-share-recv', 'framesDropped');
|
|
871
|
-
const previousFramesDropped = getPreviousStatsTotals('video-share-recv', 'framesDropped');
|
|
872
|
-
|
|
873
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
874
|
-
LoggerProxy.logger.info(
|
|
875
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`,
|
|
876
|
-
currentPacketsReceived
|
|
877
|
-
);
|
|
878
|
-
} else {
|
|
879
|
-
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
880
|
-
LoggerProxy.logger.info(
|
|
881
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames received`,
|
|
882
|
-
currentFramesReceived
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
887
|
-
LoggerProxy.logger.info(
|
|
888
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`,
|
|
889
|
-
currentFramesDecoded
|
|
890
|
-
);
|
|
891
|
-
}
|
|
829
|
+
this.emitStartStopEvents('share', previousStats.framesSent, currentStats.framesSent, true);
|
|
830
|
+
}
|
|
892
831
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}
|
|
832
|
+
// Share receive
|
|
833
|
+
const currentShareFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
|
|
834
|
+
const previousShareFramesDecoded = getPreviousStatsTotals(
|
|
835
|
+
'video-share-recv',
|
|
836
|
+
'framesDecoded'
|
|
837
|
+
);
|
|
900
838
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
839
|
+
this.emitStartStopEvents(
|
|
840
|
+
'share',
|
|
841
|
+
previousShareFramesDecoded,
|
|
842
|
+
currentShareFramesDecoded,
|
|
843
|
+
false
|
|
844
|
+
);
|
|
905
845
|
}
|
|
906
846
|
}
|
|
907
847
|
|
|
@@ -1049,12 +989,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1049
989
|
? `id: "${receiveSlot.id || ''}"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`
|
|
1050
990
|
: '';
|
|
1051
991
|
|
|
1052
|
-
if (result.frameWidth && result.frameHeight) {
|
|
1053
|
-
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
1054
|
-
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
1055
|
-
this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
992
|
const bytes =
|
|
1059
993
|
result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;
|
|
1060
994
|
|
|
@@ -1066,24 +1000,61 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1066
1000
|
if (currentPacketsLost < 0) {
|
|
1067
1001
|
currentPacketsLost = 0;
|
|
1068
1002
|
}
|
|
1069
|
-
|
|
1070
|
-
const currentPacketsReceived =
|
|
1003
|
+
const packetsReceivedDiff =
|
|
1071
1004
|
result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;
|
|
1072
1005
|
this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;
|
|
1073
1006
|
|
|
1074
|
-
if (
|
|
1007
|
+
if (packetsReceivedDiff === 0) {
|
|
1075
1008
|
if (receiveSlot && sourceState === 'live') {
|
|
1076
1009
|
LoggerProxy.logger.info(
|
|
1077
|
-
`StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot ${idAndCsi}. Total packets received on slot: `,
|
|
1010
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total packets received on slot: `,
|
|
1078
1011
|
result.packetsReceived
|
|
1079
1012
|
);
|
|
1080
1013
|
}
|
|
1081
1014
|
}
|
|
1082
1015
|
|
|
1016
|
+
if (mediaType.startsWith('video') || mediaType.startsWith('share')) {
|
|
1017
|
+
const videoFramesReceivedDiff =
|
|
1018
|
+
result.framesReceived - this.statsResults[mediaType][sendrecvType].framesReceived;
|
|
1019
|
+
|
|
1020
|
+
if (videoFramesReceivedDiff === 0) {
|
|
1021
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1022
|
+
LoggerProxy.logger.info(
|
|
1023
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames received on slot: `,
|
|
1024
|
+
result.framesReceived
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const videoFramesDecodedDiff =
|
|
1030
|
+
result.framesDecoded - this.statsResults[mediaType][sendrecvType].framesDecoded;
|
|
1031
|
+
|
|
1032
|
+
if (videoFramesDecodedDiff === 0) {
|
|
1033
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1034
|
+
LoggerProxy.logger.info(
|
|
1035
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames decoded on slot: `,
|
|
1036
|
+
result.framesDecoded
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const videoFramesDroppedDiff =
|
|
1042
|
+
result.framesDropped - this.statsResults[mediaType][sendrecvType].framesDropped;
|
|
1043
|
+
|
|
1044
|
+
if (videoFramesDroppedDiff > 10) {
|
|
1045
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1046
|
+
LoggerProxy.logger.info(
|
|
1047
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Frames dropped for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames dropped on slot: `,
|
|
1048
|
+
result.framesDropped
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1083
1054
|
// Check the over all packet Lost ratio
|
|
1084
1055
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =
|
|
1085
1056
|
currentPacketsLost > 0
|
|
1086
|
-
? currentPacketsLost / (
|
|
1057
|
+
? currentPacketsLost / (packetsReceivedDiff + currentPacketsLost)
|
|
1087
1058
|
: 0;
|
|
1088
1059
|
if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {
|
|
1089
1060
|
LoggerProxy.logger.info(
|
|
@@ -1092,6 +1063,12 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1092
1063
|
);
|
|
1093
1064
|
}
|
|
1094
1065
|
|
|
1066
|
+
if (result.frameWidth && result.frameHeight) {
|
|
1067
|
+
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
1068
|
+
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
1069
|
+
this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1095
1072
|
// TODO: check the packet loss value is negative values here
|
|
1096
1073
|
|
|
1097
1074
|
if (result.packetsLost) {
|
|
@@ -1109,6 +1086,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1109
1086
|
this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;
|
|
1110
1087
|
this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
|
|
1111
1088
|
this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;
|
|
1089
|
+
this.statsResults[mediaType][sendrecvType].framesDropped = result.framesDropped;
|
|
1112
1090
|
|
|
1113
1091
|
this.statsResults[mediaType][sendrecvType].decoderImplementation =
|
|
1114
1092
|
result.decoderImplementation;
|
|
@@ -239,7 +239,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
239
239
|
|
|
240
240
|
// Enabled when config.enableUnifiedMeetings = true
|
|
241
241
|
xdescribe('Conversation URL', () => {
|
|
242
|
-
describe('Successful 1:1 meeting',
|
|
242
|
+
describe('Successful 1:1 meeting', () => {
|
|
243
243
|
it('Fetch meeting information with a conversation URL for a 1:1 space', async () => {
|
|
244
244
|
assert.equal(Object.keys(bob.webex.meetings.getAllMeetings()), 0);
|
|
245
245
|
assert.equal(Object.keys(chris.webex.meetings.getAllMeetings()), 0);
|
|
@@ -871,7 +871,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
871
871
|
assert.equal(bob.meeting.shareStatus, 'whiteboard_share_active');
|
|
872
872
|
}));
|
|
873
873
|
|
|
874
|
-
it('alice adds chris as guest to 1:1 meeting', () =>
|
|
874
|
+
it('alice adds chris as guest to 1:1 meeting', async() =>
|
|
875
875
|
Promise.all([
|
|
876
876
|
testUtils.delayedPromise(alice.meeting.invite({emailAddress: chris.emailAddress})),
|
|
877
877
|
testUtils.waitForEvents([
|
|
@@ -1377,7 +1377,7 @@ describe('plugin-meetings', () => {
|
|
|
1377
1377
|
);
|
|
1378
1378
|
});
|
|
1379
1379
|
|
|
1380
|
-
const checkMeetingInfoUpdatedCalled = (expected) => {
|
|
1380
|
+
const checkMeetingInfoUpdatedCalled = (expected, payload) => {
|
|
1381
1381
|
const expectedArgs = [
|
|
1382
1382
|
locusInfo.emitScoped,
|
|
1383
1383
|
{
|
|
@@ -1385,6 +1385,7 @@ describe('plugin-meetings', () => {
|
|
|
1385
1385
|
function: 'updateMeetingInfo',
|
|
1386
1386
|
},
|
|
1387
1387
|
LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
|
|
1388
|
+
payload
|
|
1388
1389
|
];
|
|
1389
1390
|
|
|
1390
1391
|
if (expected) {
|
|
@@ -1395,7 +1396,7 @@ describe('plugin-meetings', () => {
|
|
|
1395
1396
|
locusInfo.emitScoped.resetHistory();
|
|
1396
1397
|
};
|
|
1397
1398
|
|
|
1398
|
-
const checkMeetingInfoUpdatedCalledForRoles = (expected) => {
|
|
1399
|
+
const checkMeetingInfoUpdatedCalledForRoles = (expected, payload) => {
|
|
1399
1400
|
const expectedArgs = [
|
|
1400
1401
|
locusInfo.emitScoped,
|
|
1401
1402
|
{
|
|
@@ -1403,6 +1404,7 @@ describe('plugin-meetings', () => {
|
|
|
1403
1404
|
function: 'updateMeetingInfo',
|
|
1404
1405
|
},
|
|
1405
1406
|
LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
|
|
1407
|
+
payload
|
|
1406
1408
|
];
|
|
1407
1409
|
|
|
1408
1410
|
if (expected) {
|
|
@@ -1447,7 +1449,7 @@ describe('plugin-meetings', () => {
|
|
|
1447
1449
|
|
|
1448
1450
|
// since it was initially undefined, this should trigger the event
|
|
1449
1451
|
|
|
1450
|
-
checkMeetingInfoUpdatedCalled(true);
|
|
1452
|
+
checkMeetingInfoUpdatedCalled(true, {isInitializing: false});
|
|
1451
1453
|
|
|
1452
1454
|
const newInfo = cloneDeep(meetingInfo);
|
|
1453
1455
|
|
|
@@ -1472,7 +1474,7 @@ describe('plugin-meetings', () => {
|
|
|
1472
1474
|
};
|
|
1473
1475
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
1474
1476
|
|
|
1475
|
-
checkMeetingInfoUpdatedCalled(true);
|
|
1477
|
+
checkMeetingInfoUpdatedCalled(true, {isInitializing: false});
|
|
1476
1478
|
|
|
1477
1479
|
// update it with the same info
|
|
1478
1480
|
expectedMeeting = {
|
|
@@ -1494,7 +1496,7 @@ describe('plugin-meetings', () => {
|
|
|
1494
1496
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
1495
1497
|
|
|
1496
1498
|
// since the info is the same it should not call trigger the event
|
|
1497
|
-
checkMeetingInfoUpdatedCalled(false);
|
|
1499
|
+
checkMeetingInfoUpdatedCalled(false, {isInitializing: false});
|
|
1498
1500
|
|
|
1499
1501
|
// update it with the same info, but roles changed
|
|
1500
1502
|
const updateSelf = cloneDeep(self);
|
|
@@ -1525,7 +1527,7 @@ describe('plugin-meetings', () => {
|
|
|
1525
1527
|
};
|
|
1526
1528
|
locusInfo.updateMeetingInfo(newInfo, updateSelf);
|
|
1527
1529
|
// since the info is the same but roles changed, it should call trigger the event
|
|
1528
|
-
checkMeetingInfoUpdatedCalledForRoles(true);
|
|
1530
|
+
checkMeetingInfoUpdatedCalledForRoles(true, {isInitializing: false});
|
|
1529
1531
|
});
|
|
1530
1532
|
|
|
1531
1533
|
it('gets roles from self if available', () => {
|
|
@@ -1546,12 +1548,17 @@ describe('plugin-meetings', () => {
|
|
|
1546
1548
|
roles: ['MODERATOR', 'COHOST'],
|
|
1547
1549
|
};
|
|
1548
1550
|
|
|
1551
|
+
sinon.stub(locusInfo, 'emitScoped');
|
|
1552
|
+
|
|
1549
1553
|
const parsedLocusInfo = cloneDeep(locusInfo.parsedLocus.info);
|
|
1550
1554
|
|
|
1551
1555
|
locusInfo.updateMeetingInfo(initialInfo);
|
|
1552
1556
|
assert.calledWith(isJoinedSpy, locusInfo.parsedLocus.self);
|
|
1553
1557
|
assert.neverCalledWith(getRolesSpy, self);
|
|
1554
1558
|
assert.calledWith(getInfosSpy, parsedLocusInfo, initialInfo, ['MODERATOR', 'COHOST']);
|
|
1559
|
+
|
|
1560
|
+
// since self is not passed to updateMeetingInfo, MEETING_INFO_UPDATED should be triggered with isIntializing: true
|
|
1561
|
+
checkMeetingInfoUpdatedCalledForRoles(true, {isInitializing: true});
|
|
1555
1562
|
});
|
|
1556
1563
|
});
|
|
1557
1564
|
|