@webex/plugin-meetings 3.0.0-beta.75 → 3.0.0-beta.77
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/meeting/index.js +24 -11
- package/dist/meeting/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +51 -9
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +16 -0
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/statsAnalyzer/index.js +32 -23
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/types/multistream/mediaRequestManager.d.ts +28 -1
- package/dist/types/multistream/receiveSlotManager.d.ts +7 -0
- package/dist/types/statsAnalyzer/index.d.ts +6 -1
- package/package.json +19 -19
- package/src/meeting/index.ts +52 -24
- package/src/multistream/mediaRequestManager.ts +53 -8
- package/src/multistream/receiveSlotManager.ts +12 -0
- package/src/statsAnalyzer/index.ts +54 -23
- package/test/unit/spec/meeting/utils.js +1 -1
- package/test/unit/spec/multistream/mediaRequestManager.ts +326 -107
- package/test/unit/spec/multistream/receiveSlotManager.ts +11 -3
- package/test/unit/spec/stats-analyzer/index.js +7 -2
package/src/meeting/index.ts
CHANGED
|
@@ -632,31 +632,47 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
632
632
|
* All multistream media requests sent out for this meeting have to go through them.
|
|
633
633
|
*/
|
|
634
634
|
this.mediaRequestManagers = {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
635
|
+
audio: new MediaRequestManager(
|
|
636
|
+
(mediaRequests) => {
|
|
637
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
638
|
+
LoggerProxy.logger.warn(
|
|
639
|
+
'Meeting:index#mediaRequestManager --> trying to send audio media request before media connection was created'
|
|
640
|
+
);
|
|
641
641
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
video: new MediaRequestManager(this.config.degradationPreferences, (mediaRequests) => {
|
|
648
|
-
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
649
|
-
LoggerProxy.logger.warn(
|
|
650
|
-
'Meeting:index#mediaRequestManager --> trying to send video media request before media connection was created'
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
645
|
+
MediaType.AudioMain,
|
|
646
|
+
mediaRequests
|
|
651
647
|
);
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
// @ts-ignore - config coming from registerPlugin
|
|
651
|
+
degradationPreferences: this.config.degradationPreferences,
|
|
652
|
+
kind: 'audio',
|
|
653
|
+
}
|
|
654
|
+
),
|
|
655
|
+
video: new MediaRequestManager(
|
|
656
|
+
(mediaRequests) => {
|
|
657
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
658
|
+
LoggerProxy.logger.warn(
|
|
659
|
+
'Meeting:index#mediaRequestManager --> trying to send video media request before media connection was created'
|
|
660
|
+
);
|
|
652
661
|
|
|
653
|
-
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
665
|
+
MediaType.VideoMain,
|
|
666
|
+
mediaRequests
|
|
667
|
+
);
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
// @ts-ignore - config coming from registerPlugin
|
|
671
|
+
degradationPreferences: this.config.degradationPreferences,
|
|
672
|
+
kind: 'video',
|
|
654
673
|
}
|
|
655
|
-
|
|
656
|
-
}),
|
|
674
|
+
),
|
|
657
675
|
screenShareAudio: new MediaRequestManager(
|
|
658
|
-
// @ts-ignore - config coming from registerPlugin
|
|
659
|
-
this.config.degradationPreferences,
|
|
660
676
|
(mediaRequests) => {
|
|
661
677
|
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
662
678
|
LoggerProxy.logger.warn(
|
|
@@ -669,11 +685,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
669
685
|
MediaType.AudioSlides,
|
|
670
686
|
mediaRequests
|
|
671
687
|
);
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
// @ts-ignore - config coming from registerPlugin
|
|
691
|
+
degradationPreferences: this.config.degradationPreferences,
|
|
692
|
+
kind: 'audio',
|
|
672
693
|
}
|
|
673
694
|
),
|
|
674
695
|
screenShareVideo: new MediaRequestManager(
|
|
675
|
-
// @ts-ignore - config coming from registerPlugin
|
|
676
|
-
this.config.degradationPreferences,
|
|
677
696
|
(mediaRequests) => {
|
|
678
697
|
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
679
698
|
LoggerProxy.logger.warn(
|
|
@@ -686,6 +705,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
686
705
|
MediaType.VideoSlides,
|
|
687
706
|
mediaRequests
|
|
688
707
|
);
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
// @ts-ignore - config coming from registerPlugin
|
|
711
|
+
degradationPreferences: this.config.degradationPreferences,
|
|
712
|
+
kind: 'video',
|
|
689
713
|
}
|
|
690
714
|
),
|
|
691
715
|
};
|
|
@@ -5343,8 +5367,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5343
5367
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
5344
5368
|
// @ts-ignore - config coming from registerPlugin
|
|
5345
5369
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
5346
|
-
|
|
5347
|
-
|
|
5370
|
+
this.statsAnalyzer = new StatsAnalyzer(
|
|
5371
|
+
// @ts-ignore - config coming from registerPlugin
|
|
5372
|
+
this.config.stats,
|
|
5373
|
+
(ssrc: number) => this.receiveSlotManager.findReceiveSlotBySsrc(ssrc),
|
|
5374
|
+
this.networkQualityMonitor
|
|
5375
|
+
);
|
|
5348
5376
|
this.setupStatsAnalyzerEventHandlers();
|
|
5349
5377
|
this.networkQualityMonitor.on(
|
|
5350
5378
|
EVENT_TRIGGERS.NETWORK_QUALITY,
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
ReceiverSelectedInfo,
|
|
7
7
|
CodecInfo as WcmeCodecInfo,
|
|
8
8
|
H264Codec,
|
|
9
|
+
getRecommendedMaxBitrateForFrameSize,
|
|
10
|
+
RecommendedOpusBitrates,
|
|
9
11
|
} from '@webex/internal-media-core';
|
|
10
12
|
import {cloneDeep, debounce, isEmpty} from 'lodash';
|
|
11
13
|
|
|
@@ -64,10 +66,17 @@ type DegradationPreferences = {
|
|
|
64
66
|
};
|
|
65
67
|
|
|
66
68
|
type SendMediaRequestsCallback = (mediaRequests: WcmeMediaRequest[]) => void;
|
|
69
|
+
type Kind = 'audio' | 'video';
|
|
67
70
|
|
|
71
|
+
type Options = {
|
|
72
|
+
degradationPreferences: DegradationPreferences;
|
|
73
|
+
kind: Kind;
|
|
74
|
+
};
|
|
68
75
|
export class MediaRequestManager {
|
|
69
76
|
private sendMediaRequestsCallback: SendMediaRequestsCallback;
|
|
70
77
|
|
|
78
|
+
private kind: Kind;
|
|
79
|
+
|
|
71
80
|
private counter: number;
|
|
72
81
|
|
|
73
82
|
private clientRequests: {[key: MediaRequestId]: MediaRequest};
|
|
@@ -80,14 +89,12 @@ export class MediaRequestManager {
|
|
|
80
89
|
|
|
81
90
|
private previousWCMEMediaRequests: Array<WcmeMediaRequest> = [];
|
|
82
91
|
|
|
83
|
-
constructor(
|
|
84
|
-
degradationPreferences: DegradationPreferences,
|
|
85
|
-
sendMediaRequestsCallback: SendMediaRequestsCallback
|
|
86
|
-
) {
|
|
92
|
+
constructor(sendMediaRequestsCallback: SendMediaRequestsCallback, options: Options) {
|
|
87
93
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
88
94
|
this.counter = 0;
|
|
89
95
|
this.clientRequests = {};
|
|
90
|
-
this.degradationPreferences = degradationPreferences;
|
|
96
|
+
this.degradationPreferences = options.degradationPreferences;
|
|
97
|
+
this.kind = options.kind;
|
|
91
98
|
this.sourceUpdateListener = this.commit.bind(this);
|
|
92
99
|
this.debouncedSourceUpdateListener = debounce(
|
|
93
100
|
this.sourceUpdateListener,
|
|
@@ -175,6 +182,45 @@ export class MediaRequestManager {
|
|
|
175
182
|
);
|
|
176
183
|
}
|
|
177
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Returns the maxPayloadBitsPerSecond per Stream
|
|
187
|
+
*
|
|
188
|
+
* If MediaRequestManager kind is "audio", a constant bitrate will be returned.
|
|
189
|
+
* If MediaRequestManager kind is "video", the bitrate will be calculated based
|
|
190
|
+
* on maxFs (default h264 maxFs as fallback if maxFs is not defined)
|
|
191
|
+
*
|
|
192
|
+
* @param {MediaRequest} mediaRequest - mediaRequest to take data from
|
|
193
|
+
* @returns {number} maxPayloadBitsPerSecond
|
|
194
|
+
*/
|
|
195
|
+
private getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number {
|
|
196
|
+
if (this.kind === 'audio') {
|
|
197
|
+
// return mono_music bitrate default if the kind of mediarequest manager is audio:
|
|
198
|
+
return RecommendedOpusBitrates.FB_MONO_MUSIC;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return getRecommendedMaxBitrateForFrameSize(
|
|
202
|
+
mediaRequest.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Returns the max Macro Blocks per second (maxMbps) per H264 Stream
|
|
208
|
+
*
|
|
209
|
+
* The maxMbps will be calculated based on maxFs and maxFps
|
|
210
|
+
* (default h264 maxFps as fallback if maxFps is not defined)
|
|
211
|
+
*
|
|
212
|
+
* @param {MediaRequest} mediaRequest - mediaRequest to take data from
|
|
213
|
+
* @returns {number} maxMbps
|
|
214
|
+
*/
|
|
215
|
+
// eslint-disable-next-line class-methods-use-this
|
|
216
|
+
private getH264MaxMbps(mediaRequest: MediaRequest): number {
|
|
217
|
+
// fallback for maxFps (not needed for maxFs, since there is a fallback already in getDegradedClientRequests)
|
|
218
|
+
const maxFps = mediaRequest.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps;
|
|
219
|
+
|
|
220
|
+
// divided by 100 since maxFps is 3000 (for 30 frames per seconds)
|
|
221
|
+
return (mediaRequest.codecInfo.maxFs * maxFps) / 100;
|
|
222
|
+
}
|
|
223
|
+
|
|
178
224
|
/**
|
|
179
225
|
* Clears the previous media requests.
|
|
180
226
|
*
|
|
@@ -188,7 +234,6 @@ export class MediaRequestManager {
|
|
|
188
234
|
const wcmeMediaRequests: WcmeMediaRequest[] = [];
|
|
189
235
|
|
|
190
236
|
const clientRequests = this.getDegradedClientRequests();
|
|
191
|
-
const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
|
|
192
237
|
|
|
193
238
|
// map all the client media requests to wcme media requests
|
|
194
239
|
Object.values(clientRequests).forEach((mr) => {
|
|
@@ -206,14 +251,14 @@ export class MediaRequestManager {
|
|
|
206
251
|
)
|
|
207
252
|
: new ReceiverSelectedInfo(mr.policyInfo.csi),
|
|
208
253
|
mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
|
|
209
|
-
|
|
254
|
+
this.getMaxPayloadBitsPerSecond(mr),
|
|
210
255
|
mr.codecInfo && [
|
|
211
256
|
new WcmeCodecInfo(
|
|
212
257
|
0x80,
|
|
213
258
|
new H264Codec(
|
|
214
259
|
mr.codecInfo.maxFs,
|
|
215
260
|
mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
|
|
216
|
-
mr
|
|
261
|
+
this.getH264MaxMbps(mr),
|
|
217
262
|
mr.codecInfo.maxWidth,
|
|
218
263
|
mr.codecInfo.maxHeight
|
|
219
264
|
)
|
|
@@ -151,4 +151,16 @@ export class ReceiveSlotManager {
|
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Find a receive slot by a ssrc.
|
|
157
|
+
*
|
|
158
|
+
* @param ssrc - The ssrc of the receive slot to find.
|
|
159
|
+
* @returns - The receive slot with this ssrc, undefined if not found.
|
|
160
|
+
*/
|
|
161
|
+
findReceiveSlotBySsrc(ssrc: number): ReceiveSlot | undefined {
|
|
162
|
+
return Object.values(this.allocatedSlots)
|
|
163
|
+
.flat()
|
|
164
|
+
.find((r) => ssrc && r.wcmeReceiveSlot?.id?.ssrc === ssrc);
|
|
165
|
+
}
|
|
154
166
|
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
getVideoSenderMqa,
|
|
29
29
|
getVideoReceiverMqa,
|
|
30
30
|
} from './mqaUtil';
|
|
31
|
+
import {ReceiveSlot} from '../multistream/receiveSlot';
|
|
31
32
|
|
|
32
33
|
export const EVENTS = {
|
|
33
34
|
MEDIA_QUALITY: 'MEDIA_QUALITY',
|
|
@@ -53,6 +54,8 @@ const emptyReceiver = {
|
|
|
53
54
|
meanRoundTripTime: [],
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
type ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;
|
|
58
|
+
|
|
56
59
|
/**
|
|
57
60
|
* Stats Analyzer class that will emit events based on detected quality
|
|
58
61
|
*
|
|
@@ -74,17 +77,20 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
74
77
|
statsInterval: NodeJS.Timeout;
|
|
75
78
|
statsResults: any;
|
|
76
79
|
statsStarted: any;
|
|
80
|
+
receiveSlotCallback: ReceiveSlotCallback;
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
83
|
* Creates a new instance of StatsAnalyzer
|
|
80
84
|
* @constructor
|
|
81
85
|
* @public
|
|
82
86
|
* @param {Object} config SDK Configuration Object
|
|
87
|
+
* @param {Function} receiveSlotCallback Callback used to access receive slots.
|
|
83
88
|
* @param {Object} networkQualityMonitor class for assessing network characteristics (jitter, packetLoss, latency)
|
|
84
89
|
* @param {Object} statsResults Default properties for stats
|
|
85
90
|
*/
|
|
86
91
|
constructor(
|
|
87
92
|
config: any,
|
|
93
|
+
receiveSlotCallback: ReceiveSlotCallback = () => undefined,
|
|
88
94
|
networkQualityMonitor: object = {},
|
|
89
95
|
statsResults: object = defaultStats
|
|
90
96
|
) {
|
|
@@ -98,6 +104,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
98
104
|
this.mqaSentCount = -1;
|
|
99
105
|
this.lastMqaDataSent = {};
|
|
100
106
|
this.lastEmittedStartStopEvent = {};
|
|
107
|
+
this.receiveSlotCallback = receiveSlotCallback;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
/**
|
|
@@ -523,7 +530,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
523
530
|
currentStats.totalPacketsSent === 0
|
|
524
531
|
) {
|
|
525
532
|
LoggerProxy.logger.info(
|
|
526
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent
|
|
533
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,
|
|
534
|
+
currentStats.totalPacketsSent
|
|
527
535
|
);
|
|
528
536
|
} else {
|
|
529
537
|
if (
|
|
@@ -531,7 +539,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
531
539
|
currentStats.totalAudioEnergy === 0
|
|
532
540
|
) {
|
|
533
541
|
LoggerProxy.logger.info(
|
|
534
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present
|
|
542
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,
|
|
543
|
+
currentStats.totalAudioEnergy
|
|
535
544
|
);
|
|
536
545
|
}
|
|
537
546
|
|
|
@@ -565,14 +574,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
565
574
|
|
|
566
575
|
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
567
576
|
LoggerProxy.logger.info(
|
|
568
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received
|
|
577
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`,
|
|
578
|
+
currentPacketsReceived
|
|
569
579
|
);
|
|
570
580
|
} else if (
|
|
571
581
|
currentSamplesReceived === previousSamplesReceived ||
|
|
572
582
|
currentSamplesReceived === 0
|
|
573
583
|
) {
|
|
574
584
|
LoggerProxy.logger.info(
|
|
575
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio samples received
|
|
585
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`,
|
|
586
|
+
currentSamplesReceived
|
|
576
587
|
);
|
|
577
588
|
}
|
|
578
589
|
|
|
@@ -589,7 +600,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
589
600
|
currentStats.totalPacketsSent === 0
|
|
590
601
|
) {
|
|
591
602
|
LoggerProxy.logger.info(
|
|
592
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent
|
|
603
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
|
|
604
|
+
currentStats.totalPacketsSent
|
|
593
605
|
);
|
|
594
606
|
} else {
|
|
595
607
|
if (
|
|
@@ -597,7 +609,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
597
609
|
currentStats.framesEncoded === 0
|
|
598
610
|
) {
|
|
599
611
|
LoggerProxy.logger.info(
|
|
600
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded
|
|
612
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
|
|
613
|
+
currentStats.framesEncoded
|
|
601
614
|
);
|
|
602
615
|
}
|
|
603
616
|
|
|
@@ -607,7 +620,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
607
620
|
this.statsResults.resolutions['video-send'].send.framesSent === 0
|
|
608
621
|
) {
|
|
609
622
|
LoggerProxy.logger.info(
|
|
610
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent
|
|
623
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
|
|
624
|
+
this.statsResults.resolutions['video-send'].send.framesSent
|
|
611
625
|
);
|
|
612
626
|
}
|
|
613
627
|
}
|
|
@@ -643,24 +657,28 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
643
657
|
|
|
644
658
|
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
645
659
|
LoggerProxy.logger.info(
|
|
646
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received
|
|
660
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`,
|
|
661
|
+
currentPacketsReceived
|
|
647
662
|
);
|
|
648
663
|
} else {
|
|
649
664
|
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
650
665
|
LoggerProxy.logger.info(
|
|
651
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames received
|
|
666
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video frames received`,
|
|
667
|
+
currentFramesReceived
|
|
652
668
|
);
|
|
653
669
|
}
|
|
654
670
|
|
|
655
671
|
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
656
672
|
LoggerProxy.logger.info(
|
|
657
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded
|
|
673
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`,
|
|
674
|
+
currentFramesDecoded
|
|
658
675
|
);
|
|
659
676
|
}
|
|
660
677
|
|
|
661
678
|
if (currentFramesDropped - previousFramesDropped > 10) {
|
|
662
679
|
LoggerProxy.logger.info(
|
|
663
|
-
`StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped
|
|
680
|
+
`StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`,
|
|
681
|
+
currentFramesDropped - previousFramesDropped
|
|
664
682
|
);
|
|
665
683
|
}
|
|
666
684
|
}
|
|
@@ -679,7 +697,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
679
697
|
currentStats.totalPacketsSent === 0
|
|
680
698
|
) {
|
|
681
699
|
LoggerProxy.logger.info(
|
|
682
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent
|
|
700
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,
|
|
701
|
+
currentStats.totalPacketsSent
|
|
683
702
|
);
|
|
684
703
|
} else {
|
|
685
704
|
if (
|
|
@@ -687,7 +706,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
687
706
|
currentStats.framesEncoded === 0
|
|
688
707
|
) {
|
|
689
708
|
LoggerProxy.logger.info(
|
|
690
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded
|
|
709
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,
|
|
710
|
+
currentStats.framesEncoded
|
|
691
711
|
);
|
|
692
712
|
}
|
|
693
713
|
|
|
@@ -697,7 +717,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
697
717
|
this.statsResults.resolutions['video-share-send'].send.framesSent === 0
|
|
698
718
|
) {
|
|
699
719
|
LoggerProxy.logger.info(
|
|
700
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent
|
|
720
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,
|
|
721
|
+
this.statsResults.resolutions['video-share-send'].send.framesSent
|
|
701
722
|
);
|
|
702
723
|
}
|
|
703
724
|
}
|
|
@@ -735,24 +756,28 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
735
756
|
|
|
736
757
|
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
737
758
|
LoggerProxy.logger.info(
|
|
738
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received
|
|
759
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`,
|
|
760
|
+
currentPacketsReceived
|
|
739
761
|
);
|
|
740
762
|
} else {
|
|
741
763
|
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
742
764
|
LoggerProxy.logger.info(
|
|
743
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames received
|
|
765
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames received`,
|
|
766
|
+
currentFramesReceived
|
|
744
767
|
);
|
|
745
768
|
}
|
|
746
769
|
|
|
747
770
|
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
748
771
|
LoggerProxy.logger.info(
|
|
749
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded
|
|
772
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`,
|
|
773
|
+
currentFramesDecoded
|
|
750
774
|
);
|
|
751
775
|
}
|
|
752
776
|
|
|
753
777
|
if (currentFramesDropped - previousFramesDropped > 10) {
|
|
754
778
|
LoggerProxy.logger.info(
|
|
755
|
-
`StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped
|
|
779
|
+
`StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped`,
|
|
780
|
+
currentFramesDropped - previousFramesDropped
|
|
756
781
|
);
|
|
757
782
|
}
|
|
758
783
|
}
|
|
@@ -933,6 +958,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
933
958
|
|
|
934
959
|
if (result.bytesReceived) {
|
|
935
960
|
let kilobytes = 0;
|
|
961
|
+
const receiveSlot = this.receiveSlotCallback(result.ssrc);
|
|
962
|
+
const idAndCsi = receiveSlot
|
|
963
|
+
? `id: "${receiveSlot.id || ''}"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`
|
|
964
|
+
: '';
|
|
936
965
|
|
|
937
966
|
if (result.frameWidth && result.frameHeight) {
|
|
938
967
|
this.statsResults.resolutions[mediaType][sendrecvType].width = result.frameWidth;
|
|
@@ -989,10 +1018,12 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
989
1018
|
result.packetsReceived;
|
|
990
1019
|
|
|
991
1020
|
if (this.statsResults[mediaType][sendrecvType].packetsReceived === 0) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1021
|
+
if (receiveSlot) {
|
|
1022
|
+
LoggerProxy.logger.info(
|
|
1023
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot ${idAndCsi}`,
|
|
1024
|
+
this.statsResults[mediaType][sendrecvType].packetsReceived
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
996
1027
|
}
|
|
997
1028
|
|
|
998
1029
|
// Check the over all packet Lost ratio
|
|
@@ -1004,7 +1035,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1004
1035
|
: 0;
|
|
1005
1036
|
if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {
|
|
1006
1037
|
LoggerProxy.logger.info(
|
|
1007
|
-
|
|
1038
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,
|
|
1008
1039
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio
|
|
1009
1040
|
);
|
|
1010
1041
|
}
|
|
@@ -4,7 +4,7 @@ import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
|
4
4
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
5
5
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
|
6
6
|
import Metrics from '@webex/plugin-meetings/src/metrics/index';
|
|
7
|
-
import {DISPLAY_HINTS} from '@webex/plugin-meetings/
|
|
7
|
+
import {DISPLAY_HINTS} from '@webex/plugin-meetings/src/constants';
|
|
8
8
|
|
|
9
9
|
describe('plugin-meetings', () => {
|
|
10
10
|
describe('Meeting utils function', () => {
|