@webex/plugin-meetings 3.0.0-beta.41 → 3.0.0-beta.410
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/README.md +58 -8
- package/dist/annotation/annotation.types.js +7 -0
- package/dist/annotation/annotation.types.js.map +1 -0
- package/dist/annotation/constants.js +49 -0
- package/dist/annotation/constants.js.map +1 -0
- package/dist/annotation/index.js +342 -0
- package/dist/annotation/index.js.map +1 -0
- package/dist/breakouts/breakout.js +94 -15
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/events.js +45 -0
- package/dist/breakouts/events.js.map +1 -0
- package/dist/breakouts/index.js +625 -123
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/utils.js +27 -8
- package/dist/breakouts/utils.js.map +1 -1
- package/dist/common/errors/no-meeting-info.js +51 -0
- package/dist/common/errors/no-meeting-info.js.map +1 -0
- package/dist/common/errors/reclaim-host-role-errors.js +158 -0
- package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
- package/dist/common/errors/webex-errors.js +48 -7
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/logs/logger-proxy.js +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +5 -1
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js +24 -9
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +6 -10
- package/dist/config.js.map +1 -1
- package/dist/constants.js +247 -34
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +14 -2
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/index.js +109 -15
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/types.js +7 -0
- package/dist/controls-options-manager/types.js.map +1 -0
- package/dist/controls-options-manager/util.js +309 -18
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/index.js +116 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +15 -0
- package/dist/interceptors/index.js.map +1 -0
- package/dist/interceptors/locusRetry.js +93 -0
- package/dist/interceptors/locusRetry.js.map +1 -0
- package/dist/interpretation/collection.js +23 -0
- package/dist/interpretation/collection.js.map +1 -0
- package/dist/interpretation/index.js +380 -0
- package/dist/interpretation/index.js.map +1 -0
- package/dist/interpretation/siLanguage.js +25 -0
- package/dist/interpretation/siLanguage.js.map +1 -0
- package/dist/locus-info/controlsUtils.js +91 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +386 -62
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js +7 -1
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js +71 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +249 -72
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js +89 -14
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +65 -102
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +73 -124
- package/dist/media/properties.js.map +1 -1
- package/dist/mediaQualityMetrics/config.js +135 -330
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +86 -2
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +4531 -2994
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +292 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -0
- package/dist/meeting/muteState.js +236 -136
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +189 -155
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +676 -417
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting/voicea-meeting.js +172 -0
- package/dist/meeting/voicea-meeting.js.map +1 -0
- package/dist/meeting-info/index.js +73 -7
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +201 -57
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +8 -7
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +44 -40
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +39 -0
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +484 -119
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +7 -0
- package/dist/meetings/meetings.types.js.map +1 -0
- package/dist/meetings/request.js +2 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +73 -7
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +57 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +25 -0
- package/dist/member/types.js.map +1 -0
- package/dist/member/util.js +132 -25
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +10 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +100 -5
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +106 -38
- package/dist/members/request.js.map +1 -1
- package/dist/members/types.js +15 -0
- package/dist/members/types.js.map +1 -0
- package/dist/members/util.js +326 -232
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +18 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +1 -446
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +223 -32
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +10 -0
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +39 -36
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +3 -1
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +76 -5
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +366 -104
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +255 -0
- package/dist/multistream/sendSlotManager.js.map +1 -0
- package/dist/reachability/clusterReachability.js +356 -0
- package/dist/reachability/clusterReachability.js.map +1 -0
- package/dist/reachability/index.js +263 -390
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +6 -4
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.js +29 -0
- package/dist/reachability/util.js.map +1 -0
- package/dist/reconnection-manager/index.js +266 -202
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/index.js +21 -2
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +9 -8
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +66 -28
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +48 -64
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +407 -79
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/rtcMetrics/constants.js +12 -0
- package/dist/rtcMetrics/constants.js.map +1 -0
- package/dist/rtcMetrics/index.js +179 -0
- package/dist/rtcMetrics/index.js.map +1 -0
- package/dist/statsAnalyzer/index.js +389 -304
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +296 -156
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/annotation/annotation.types.d.ts +42 -0
- package/dist/types/annotation/constants.d.ts +31 -0
- package/dist/types/annotation/index.d.ts +117 -0
- package/dist/types/breakouts/events.d.ts +8 -0
- package/dist/types/breakouts/utils.d.ts +9 -2
- package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
- package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
- package/dist/types/common/errors/webex-errors.d.ts +25 -1
- package/dist/types/common/logs/request.d.ts +2 -0
- package/dist/types/common/queue.d.ts +9 -7
- package/dist/types/config.d.ts +2 -7
- package/dist/types/constants.d.ts +204 -32
- package/dist/types/controls-options-manager/enums.d.ts +11 -1
- package/dist/types/controls-options-manager/index.d.ts +17 -1
- package/dist/types/controls-options-manager/types.d.ts +43 -0
- package/dist/types/controls-options-manager/util.d.ts +1 -7
- package/dist/types/index.d.ts +6 -5
- package/dist/types/interceptors/index.d.ts +2 -0
- package/dist/types/interceptors/locusRetry.d.ts +27 -0
- package/dist/types/interpretation/collection.d.ts +5 -0
- package/dist/types/interpretation/index.d.ts +5 -0
- package/dist/types/interpretation/siLanguage.d.ts +5 -0
- package/dist/types/locus-info/index.d.ts +57 -4
- package/dist/types/locus-info/parser.d.ts +66 -6
- package/dist/types/media/index.d.ts +2 -0
- package/dist/types/media/properties.d.ts +34 -49
- package/dist/types/mediaQualityMetrics/config.d.ts +99 -223
- package/dist/types/meeting/in-meeting-actions.d.ts +86 -2
- package/dist/types/meeting/index.d.ts +630 -505
- package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
- package/dist/types/meeting/muteState.d.ts +88 -26
- package/dist/types/meeting/request.d.ts +67 -43
- package/dist/types/meeting/util.d.ts +117 -1
- package/dist/types/meeting/voicea-meeting.d.ts +16 -0
- package/dist/types/meeting-info/index.d.ts +13 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
- package/dist/types/meetings/collection.d.ts +17 -0
- package/dist/types/meetings/index.d.ts +113 -21
- package/dist/types/meetings/meetings.types.d.ts +4 -0
- package/dist/types/member/index.d.ts +14 -0
- package/dist/types/member/types.d.ts +32 -0
- package/dist/types/members/collection.d.ts +5 -0
- package/dist/types/members/index.d.ts +35 -2
- package/dist/types/members/request.d.ts +73 -9
- package/dist/types/members/types.d.ts +25 -0
- package/dist/types/members/util.d.ts +214 -1
- package/dist/types/metrics/constants.d.ts +17 -0
- package/dist/types/metrics/index.d.ts +4 -111
- package/dist/types/multistream/mediaRequestManager.d.ts +71 -3
- package/dist/types/multistream/receiveSlot.d.ts +7 -3
- package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
- package/dist/types/multistream/remoteMedia.d.ts +3 -31
- package/dist/types/multistream/remoteMediaGroup.d.ts +2 -9
- package/dist/types/multistream/remoteMediaManager.d.ts +61 -2
- package/dist/types/multistream/sendSlotManager.d.ts +69 -0
- package/dist/types/reachability/clusterReachability.d.ts +109 -0
- package/dist/types/reachability/index.d.ts +60 -95
- package/dist/types/reachability/request.d.ts +3 -1
- package/dist/types/reachability/util.d.ts +8 -0
- package/dist/types/reconnection-manager/index.d.ts +19 -0
- package/dist/types/recording-controller/index.d.ts +15 -1
- package/dist/types/recording-controller/util.d.ts +5 -4
- package/dist/types/roap/index.d.ts +11 -2
- package/dist/types/roap/request.d.ts +9 -8
- package/dist/types/roap/turnDiscovery.d.ts +90 -9
- package/dist/types/rtcMetrics/constants.d.ts +4 -0
- package/dist/types/rtcMetrics/index.d.ts +61 -0
- package/dist/types/statsAnalyzer/index.d.ts +34 -12
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +28 -4
- package/dist/types/webinar/collection.d.ts +16 -0
- package/dist/types/webinar/index.d.ts +5 -0
- package/dist/webinar/collection.js +44 -0
- package/dist/webinar/collection.js.map +1 -0
- package/dist/webinar/index.js +69 -0
- package/dist/webinar/index.js.map +1 -0
- package/package.json +22 -19
- package/src/annotation/annotation.types.ts +50 -0
- package/src/annotation/constants.ts +36 -0
- package/src/annotation/index.ts +328 -0
- package/src/breakouts/README.md +27 -6
- package/src/breakouts/breakout.ts +67 -9
- package/src/breakouts/events.ts +56 -0
- package/src/breakouts/index.ts +494 -73
- package/src/breakouts/utils.ts +26 -8
- package/src/common/errors/no-meeting-info.ts +24 -0
- package/src/common/errors/reclaim-host-role-errors.ts +134 -0
- package/src/common/errors/webex-errors.ts +44 -2
- package/src/common/logs/logger-proxy.ts +1 -1
- package/src/common/logs/request.ts +5 -1
- package/src/common/queue.ts +22 -8
- package/src/config.ts +6 -13
- package/src/constants.ts +234 -22
- package/src/controls-options-manager/enums.ts +12 -0
- package/src/controls-options-manager/index.ts +116 -21
- package/src/controls-options-manager/types.ts +59 -0
- package/src/controls-options-manager/util.ts +294 -14
- package/src/index.ts +45 -0
- package/src/interceptors/index.ts +3 -0
- package/src/interceptors/locusRetry.ts +67 -0
- package/src/interpretation/README.md +60 -0
- package/src/interpretation/collection.ts +19 -0
- package/src/interpretation/index.ts +349 -0
- package/src/interpretation/siLanguage.ts +18 -0
- package/src/locus-info/controlsUtils.ts +108 -0
- package/src/locus-info/index.ts +417 -59
- package/src/locus-info/infoUtils.ts +10 -2
- package/src/locus-info/mediaSharesUtils.ts +80 -0
- package/src/locus-info/parser.ts +258 -47
- package/src/locus-info/selfUtils.ts +81 -5
- package/src/media/index.ts +100 -108
- package/src/media/properties.ts +88 -117
- package/src/mediaQualityMetrics/config.ts +103 -238
- package/src/meeting/in-meeting-actions.ts +171 -3
- package/src/meeting/index.ts +3929 -2622
- package/src/meeting/locusMediaRequest.ts +313 -0
- package/src/meeting/muteState.ts +237 -136
- package/src/meeting/request.ts +173 -122
- package/src/meeting/util.ts +690 -395
- package/src/meeting/voicea-meeting.ts +122 -0
- package/src/meeting-info/index.ts +81 -8
- package/src/meeting-info/meeting-info-v2.ts +166 -16
- package/src/meeting-info/util.ts +13 -10
- package/src/meeting-info/utilv2.ts +47 -37
- package/src/meetings/collection.ts +33 -0
- package/src/meetings/index.ts +507 -127
- package/src/meetings/meetings.types.ts +12 -0
- package/src/meetings/request.ts +2 -0
- package/src/meetings/util.ts +81 -12
- package/src/member/index.ts +57 -0
- package/src/member/types.ts +38 -0
- package/src/member/util.ts +141 -25
- package/src/members/collection.ts +8 -0
- package/src/members/index.ts +133 -7
- package/src/members/request.ts +97 -17
- package/src/members/types.ts +29 -0
- package/src/members/util.ts +333 -240
- package/src/metrics/constants.ts +17 -0
- package/src/metrics/index.ts +1 -469
- package/src/multistream/mediaRequestManager.ts +271 -56
- package/src/multistream/receiveSlot.ts +11 -4
- package/src/multistream/receiveSlotManager.ts +34 -24
- package/src/multistream/remoteMedia.ts +5 -3
- package/src/multistream/remoteMediaGroup.ts +78 -0
- package/src/multistream/remoteMediaManager.ts +248 -45
- package/src/multistream/sendSlotManager.ts +198 -0
- package/src/reachability/clusterReachability.ts +320 -0
- package/src/reachability/index.ts +229 -346
- package/src/reachability/request.ts +8 -4
- package/src/reachability/util.ts +24 -0
- package/src/reconnection-manager/index.ts +128 -97
- package/src/recording-controller/index.ts +20 -3
- package/src/recording-controller/util.ts +26 -9
- package/src/roap/index.ts +76 -25
- package/src/roap/request.ts +48 -67
- package/src/roap/turnDiscovery.ts +331 -67
- package/src/rtcMetrics/constants.ts +3 -0
- package/src/rtcMetrics/index.ts +166 -0
- package/src/statsAnalyzer/index.ts +496 -419
- package/src/statsAnalyzer/mqaUtil.ts +317 -170
- package/src/webinar/collection.ts +31 -0
- package/src/webinar/index.ts +62 -0
- package/test/integration/spec/converged-space-meetings.js +60 -3
- package/test/integration/spec/journey.js +321 -262
- package/test/integration/spec/space-meeting.js +76 -3
- package/test/unit/spec/annotation/index.ts +418 -0
- package/test/unit/spec/breakouts/breakout.ts +119 -28
- package/test/unit/spec/breakouts/events.ts +89 -0
- package/test/unit/spec/breakouts/index.ts +1204 -118
- package/test/unit/spec/breakouts/utils.js +27 -2
- package/test/unit/spec/common/queue.js +31 -2
- package/test/unit/spec/controls-options-manager/index.js +163 -0
- package/test/unit/spec/controls-options-manager/util.js +576 -60
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interceptors/locusRetry.ts +131 -0
- package/test/unit/spec/interpretation/collection.ts +15 -0
- package/test/unit/spec/interpretation/index.ts +625 -0
- package/test/unit/spec/interpretation/siLanguage.ts +28 -0
- package/test/unit/spec/locus-info/controlsUtils.js +316 -43
- package/test/unit/spec/locus-info/index.js +1372 -37
- package/test/unit/spec/locus-info/infoUtils.js +37 -15
- package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +41 -0
- package/test/unit/spec/locus-info/parser.js +116 -35
- package/test/unit/spec/locus-info/selfConstant.js +27 -4
- package/test/unit/spec/locus-info/selfUtils.js +203 -17
- package/test/unit/spec/media/index.ts +178 -81
- package/test/unit/spec/media/properties.ts +2 -2
- package/test/unit/spec/meeting/in-meeting-actions.ts +85 -3
- package/test/unit/spec/meeting/index.js +7835 -2501
- package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
- package/test/unit/spec/meeting/muteState.js +549 -207
- package/test/unit/spec/meeting/request.js +494 -54
- package/test/unit/spec/meeting/utils.js +827 -74
- package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
- package/test/unit/spec/meeting-info/index.js +300 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +535 -9
- package/test/unit/spec/meeting-info/utilv2.js +21 -0
- package/test/unit/spec/meetings/collection.js +26 -0
- package/test/unit/spec/meetings/index.js +1489 -219
- package/test/unit/spec/meetings/utils.js +229 -2
- package/test/unit/spec/member/index.js +61 -6
- package/test/unit/spec/member/util.js +510 -34
- package/test/unit/spec/members/index.js +432 -1
- package/test/unit/spec/members/request.js +206 -27
- package/test/unit/spec/members/utils.js +210 -0
- package/test/unit/spec/metrics/index.js +2 -52
- package/test/unit/spec/multistream/mediaRequestManager.ts +782 -114
- package/test/unit/spec/multistream/receiveSlot.ts +9 -1
- package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
- package/test/unit/spec/multistream/remoteMedia.ts +2 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +344 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +524 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +274 -0
- package/test/unit/spec/reachability/clusterReachability.ts +279 -0
- package/test/unit/spec/reachability/index.ts +551 -14
- package/test/unit/spec/reachability/request.js +3 -1
- package/test/unit/spec/reachability/util.ts +40 -0
- package/test/unit/spec/reconnection-manager/index.js +171 -11
- package/test/unit/spec/recording-controller/index.js +293 -218
- package/test/unit/spec/recording-controller/util.js +223 -96
- package/test/unit/spec/roap/index.ts +233 -81
- package/test/unit/spec/roap/request.ts +100 -62
- package/test/unit/spec/roap/turnDiscovery.ts +682 -108
- package/test/unit/spec/rtcMetrics/index.ts +122 -0
- package/test/unit/spec/stats-analyzer/index.js +1431 -12
- package/test/unit/spec/webinar/collection.ts +13 -0
- package/test/unit/spec/webinar/index.ts +60 -0
- package/test/utils/integrationTestUtils.js +46 -0
- package/test/utils/testUtils.js +0 -57
- package/test/utils/webex-test-users.js +12 -4
- package/dist/metrics/config.js +0 -289
- package/dist/metrics/config.js.map +0 -1
- package/dist/types/metrics/config.d.ts +0 -169
- package/src/index.js +0 -18
- package/src/metrics/config.ts +0 -485
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable prefer-destructuring */
|
|
2
2
|
|
|
3
|
-
import {cloneDeep} from 'lodash';
|
|
3
|
+
import {cloneDeep, isEmpty} from 'lodash';
|
|
4
4
|
import {ConnectionState} from '@webex/internal-media-core';
|
|
5
5
|
|
|
6
6
|
import EventsScope from '../common/events/events-scope';
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_GET_STATS_FILTER,
|
|
9
9
|
STATS,
|
|
10
|
-
|
|
10
|
+
MQA_INTERVAL,
|
|
11
11
|
NETWORK_TYPE,
|
|
12
12
|
MEDIA_DEVICES,
|
|
13
13
|
_UNKNOWN_,
|
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
emptyMqaInterval,
|
|
19
19
|
emptyVideoReceive,
|
|
20
20
|
emptyVideoTransmit,
|
|
21
|
+
emptyAudioReceiveStream,
|
|
22
|
+
emptyAudioTransmitStream,
|
|
23
|
+
emptyVideoReceiveStream,
|
|
24
|
+
emptyVideoTransmitStream,
|
|
21
25
|
} from '../mediaQualityMetrics/config';
|
|
22
26
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
23
27
|
|
|
@@ -27,7 +31,12 @@ import {
|
|
|
27
31
|
getAudioReceiverMqa,
|
|
28
32
|
getVideoSenderMqa,
|
|
29
33
|
getVideoReceiverMqa,
|
|
34
|
+
getAudioSenderStreamMqa,
|
|
35
|
+
getAudioReceiverStreamMqa,
|
|
36
|
+
getVideoSenderStreamMqa,
|
|
37
|
+
getVideoReceiverStreamMqa,
|
|
30
38
|
} from './mqaUtil';
|
|
39
|
+
import {ReceiveSlot} from '../multistream/receiveSlot';
|
|
31
40
|
|
|
32
41
|
export const EVENTS = {
|
|
33
42
|
MEDIA_QUALITY: 'MEDIA_QUALITY',
|
|
@@ -53,6 +62,11 @@ const emptyReceiver = {
|
|
|
53
62
|
meanRoundTripTime: [],
|
|
54
63
|
};
|
|
55
64
|
|
|
65
|
+
type ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;
|
|
66
|
+
type MediaStatus = {
|
|
67
|
+
actual?: any;
|
|
68
|
+
expected?: any;
|
|
69
|
+
};
|
|
56
70
|
/**
|
|
57
71
|
* Stats Analyzer class that will emit events based on detected quality
|
|
58
72
|
*
|
|
@@ -74,17 +88,22 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
74
88
|
statsInterval: NodeJS.Timeout;
|
|
75
89
|
statsResults: any;
|
|
76
90
|
statsStarted: any;
|
|
91
|
+
successfulCandidatePair: any;
|
|
92
|
+
localIpAddress: string; // Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses
|
|
93
|
+
receiveSlotCallback: ReceiveSlotCallback;
|
|
77
94
|
|
|
78
95
|
/**
|
|
79
96
|
* Creates a new instance of StatsAnalyzer
|
|
80
97
|
* @constructor
|
|
81
98
|
* @public
|
|
82
99
|
* @param {Object} config SDK Configuration Object
|
|
100
|
+
* @param {Function} receiveSlotCallback Callback used to access receive slots.
|
|
83
101
|
* @param {Object} networkQualityMonitor class for assessing network characteristics (jitter, packetLoss, latency)
|
|
84
102
|
* @param {Object} statsResults Default properties for stats
|
|
85
103
|
*/
|
|
86
104
|
constructor(
|
|
87
105
|
config: any,
|
|
106
|
+
receiveSlotCallback: ReceiveSlotCallback = () => undefined,
|
|
88
107
|
networkQualityMonitor: object = {},
|
|
89
108
|
statsResults: object = defaultStats
|
|
90
109
|
) {
|
|
@@ -98,6 +117,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
98
117
|
this.mqaSentCount = -1;
|
|
99
118
|
this.lastMqaDataSent = {};
|
|
100
119
|
this.lastEmittedStartStopEvent = {};
|
|
120
|
+
this.receiveSlotCallback = receiveSlotCallback;
|
|
121
|
+
this.successfulCandidatePair = {};
|
|
122
|
+
this.localIpAddress = '';
|
|
101
123
|
}
|
|
102
124
|
|
|
103
125
|
/**
|
|
@@ -128,8 +150,17 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
128
150
|
* @memberof StatsAnalyzer
|
|
129
151
|
* @returns {void}
|
|
130
152
|
*/
|
|
131
|
-
public updateMediaStatus(status:
|
|
132
|
-
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
|
+
};
|
|
133
164
|
}
|
|
134
165
|
|
|
135
166
|
/**
|
|
@@ -142,65 +173,215 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
142
173
|
sendMqaData() {
|
|
143
174
|
const newMqa = cloneDeep(emptyMqaInterval);
|
|
144
175
|
|
|
176
|
+
// Fill in empty stats items for lastMqaDataSent
|
|
177
|
+
Object.keys(this.statsResults).forEach((mediaType) => {
|
|
178
|
+
if (!this.lastMqaDataSent[mediaType]) {
|
|
179
|
+
this.lastMqaDataSent[mediaType] = {};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!this.lastMqaDataSent[mediaType].send && mediaType.includes('-send')) {
|
|
183
|
+
this.lastMqaDataSent[mediaType].send = {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!this.lastMqaDataSent[mediaType].recv && mediaType.includes('-recv')) {
|
|
187
|
+
this.lastMqaDataSent[mediaType].recv = {};
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Create stats the first level, totals for senders and receivers
|
|
192
|
+
const audioSender = cloneDeep(emptyAudioTransmit);
|
|
193
|
+
const audioShareSender = cloneDeep(emptyAudioTransmit);
|
|
194
|
+
const audioReceiver = cloneDeep(emptyAudioReceive);
|
|
195
|
+
const audioShareReceiver = cloneDeep(emptyAudioReceive);
|
|
196
|
+
const videoSender = cloneDeep(emptyVideoTransmit);
|
|
197
|
+
const videoShareSender = cloneDeep(emptyVideoTransmit);
|
|
198
|
+
const videoReceiver = cloneDeep(emptyVideoReceive);
|
|
199
|
+
const videoShareReceiver = cloneDeep(emptyVideoReceive);
|
|
200
|
+
|
|
201
|
+
getAudioSenderMqa({
|
|
202
|
+
audioSender,
|
|
203
|
+
statsResults: this.statsResults,
|
|
204
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
205
|
+
baseMediaType: 'audio-send',
|
|
206
|
+
});
|
|
207
|
+
newMqa.audioTransmit.push(audioSender);
|
|
208
|
+
|
|
209
|
+
getAudioSenderMqa({
|
|
210
|
+
audioSender: audioShareSender,
|
|
211
|
+
statsResults: this.statsResults,
|
|
212
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
213
|
+
baseMediaType: 'audio-share-send',
|
|
214
|
+
});
|
|
215
|
+
newMqa.audioTransmit.push(audioShareSender);
|
|
216
|
+
|
|
217
|
+
getAudioReceiverMqa({
|
|
218
|
+
audioReceiver,
|
|
219
|
+
statsResults: this.statsResults,
|
|
220
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
221
|
+
baseMediaType: 'audio-recv',
|
|
222
|
+
});
|
|
223
|
+
newMqa.audioReceive.push(audioReceiver);
|
|
224
|
+
|
|
225
|
+
getAudioReceiverMqa({
|
|
226
|
+
audioReceiver: audioShareReceiver,
|
|
227
|
+
statsResults: this.statsResults,
|
|
228
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
229
|
+
baseMediaType: 'audio-share-recv',
|
|
230
|
+
});
|
|
231
|
+
newMqa.audioReceive.push(audioShareReceiver);
|
|
232
|
+
|
|
233
|
+
getVideoSenderMqa({
|
|
234
|
+
videoSender,
|
|
235
|
+
statsResults: this.statsResults,
|
|
236
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
237
|
+
baseMediaType: 'video-send',
|
|
238
|
+
});
|
|
239
|
+
newMqa.videoTransmit.push(videoSender);
|
|
240
|
+
|
|
241
|
+
getVideoSenderMqa({
|
|
242
|
+
videoSender: videoShareSender,
|
|
243
|
+
statsResults: this.statsResults,
|
|
244
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
245
|
+
baseMediaType: 'video-share-send',
|
|
246
|
+
});
|
|
247
|
+
newMqa.videoTransmit.push(videoShareSender);
|
|
248
|
+
|
|
249
|
+
getVideoReceiverMqa({
|
|
250
|
+
videoReceiver,
|
|
251
|
+
statsResults: this.statsResults,
|
|
252
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
253
|
+
baseMediaType: 'video-recv',
|
|
254
|
+
});
|
|
255
|
+
newMqa.videoReceive.push(videoReceiver);
|
|
256
|
+
|
|
257
|
+
getVideoReceiverMqa({
|
|
258
|
+
videoReceiver: videoShareReceiver,
|
|
259
|
+
statsResults: this.statsResults,
|
|
260
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
261
|
+
baseMediaType: 'video-share-recv',
|
|
262
|
+
});
|
|
263
|
+
newMqa.videoReceive.push(videoShareReceiver);
|
|
264
|
+
|
|
265
|
+
// Add stats for individual streams
|
|
145
266
|
Object.keys(this.statsResults).forEach((mediaType) => {
|
|
146
|
-
if (mediaType.
|
|
147
|
-
const
|
|
267
|
+
if (mediaType.startsWith('audio-send')) {
|
|
268
|
+
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
269
|
+
|
|
270
|
+
getAudioSenderStreamMqa({
|
|
271
|
+
audioSenderStream,
|
|
272
|
+
statsResults: this.statsResults,
|
|
273
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
274
|
+
mediaType,
|
|
275
|
+
});
|
|
276
|
+
newMqa.audioTransmit[0].streams.push(audioSenderStream);
|
|
277
|
+
|
|
278
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
279
|
+
} else if (mediaType.startsWith('audio-share-send')) {
|
|
280
|
+
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
281
|
+
|
|
282
|
+
getAudioSenderStreamMqa({
|
|
283
|
+
audioSenderStream,
|
|
284
|
+
statsResults: this.statsResults,
|
|
285
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
286
|
+
mediaType,
|
|
287
|
+
});
|
|
288
|
+
newMqa.audioTransmit[1].streams.push(audioSenderStream);
|
|
289
|
+
|
|
290
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
291
|
+
} else if (mediaType.startsWith('audio-recv')) {
|
|
292
|
+
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
293
|
+
|
|
294
|
+
getAudioReceiverStreamMqa({
|
|
295
|
+
audioReceiverStream,
|
|
296
|
+
statsResults: this.statsResults,
|
|
297
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
298
|
+
mediaType,
|
|
299
|
+
});
|
|
300
|
+
newMqa.audioReceive[0].streams.push(audioReceiverStream);
|
|
301
|
+
|
|
302
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
303
|
+
} else if (mediaType.startsWith('audio-share-recv')) {
|
|
304
|
+
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
305
|
+
|
|
306
|
+
getAudioReceiverStreamMqa({
|
|
307
|
+
audioReceiverStream,
|
|
308
|
+
statsResults: this.statsResults,
|
|
309
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
310
|
+
mediaType,
|
|
311
|
+
});
|
|
312
|
+
newMqa.audioReceive[1].streams.push(audioReceiverStream);
|
|
313
|
+
|
|
314
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
315
|
+
} else if (mediaType.startsWith('video-send-layer')) {
|
|
316
|
+
// We only want the stream-specific stats we get with video-send-layer-0, video-send-layer-1, etc.
|
|
317
|
+
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
148
318
|
|
|
149
|
-
|
|
150
|
-
|
|
319
|
+
getVideoSenderStreamMqa({
|
|
320
|
+
videoSenderStream,
|
|
151
321
|
statsResults: this.statsResults,
|
|
152
322
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
153
323
|
mediaType,
|
|
154
324
|
});
|
|
155
|
-
newMqa.
|
|
156
|
-
} else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {
|
|
157
|
-
const audioReceiver = cloneDeep(emptyAudioReceive);
|
|
325
|
+
newMqa.videoTransmit[0].streams.push(videoSenderStream);
|
|
158
326
|
|
|
159
|
-
|
|
160
|
-
|
|
327
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
328
|
+
} else if (mediaType.startsWith('video-share-send')) {
|
|
329
|
+
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
330
|
+
|
|
331
|
+
getVideoSenderStreamMqa({
|
|
332
|
+
videoSenderStream,
|
|
161
333
|
statsResults: this.statsResults,
|
|
162
334
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
163
335
|
mediaType,
|
|
164
336
|
});
|
|
165
|
-
newMqa.
|
|
166
|
-
|
|
167
|
-
|
|
337
|
+
newMqa.videoTransmit[1].streams.push(videoSenderStream);
|
|
338
|
+
|
|
339
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
340
|
+
} else if (mediaType.startsWith('video-recv')) {
|
|
341
|
+
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
168
342
|
|
|
169
|
-
|
|
170
|
-
|
|
343
|
+
getVideoReceiverStreamMqa({
|
|
344
|
+
videoReceiverStream,
|
|
171
345
|
statsResults: this.statsResults,
|
|
172
346
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
173
347
|
mediaType,
|
|
174
348
|
});
|
|
175
|
-
newMqa.
|
|
176
|
-
} else if (mediaType.includes('video-recv') || mediaType.includes('video-share-recv')) {
|
|
177
|
-
const videoReceiver = cloneDeep(emptyVideoReceive);
|
|
349
|
+
newMqa.videoReceive[0].streams.push(videoReceiverStream);
|
|
178
350
|
|
|
179
|
-
|
|
180
|
-
|
|
351
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
352
|
+
} else if (mediaType.startsWith('video-share-recv')) {
|
|
353
|
+
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
354
|
+
|
|
355
|
+
getVideoReceiverStreamMqa({
|
|
356
|
+
videoReceiverStream,
|
|
181
357
|
statsResults: this.statsResults,
|
|
182
358
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
183
359
|
mediaType,
|
|
184
360
|
});
|
|
185
|
-
newMqa.videoReceive.push(
|
|
361
|
+
newMqa.videoReceive[1].streams.push(videoReceiverStream);
|
|
362
|
+
|
|
363
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
186
364
|
}
|
|
187
365
|
});
|
|
188
366
|
|
|
189
|
-
newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress
|
|
367
|
+
newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress;
|
|
190
368
|
|
|
191
369
|
// Adding peripheral information
|
|
192
|
-
newMqa.intervalMetadata.peripherals = [];
|
|
193
|
-
|
|
194
370
|
newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});
|
|
195
371
|
if (this.statsResults['audio-send']) {
|
|
196
372
|
newMqa.intervalMetadata.peripherals.push({
|
|
197
|
-
information: this.statsResults['audio-send']
|
|
373
|
+
information: this.statsResults['audio-send'].trackLabel || _UNKNOWN_,
|
|
198
374
|
name: MEDIA_DEVICES.MICROPHONE,
|
|
199
375
|
});
|
|
200
376
|
}
|
|
201
|
-
|
|
377
|
+
|
|
378
|
+
const existingVideoSender = Object.keys(this.statsResults).find((item) =>
|
|
379
|
+
item.includes('video-send')
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
if (existingVideoSender) {
|
|
202
383
|
newMqa.intervalMetadata.peripherals.push({
|
|
203
|
-
information: this.statsResults[
|
|
384
|
+
information: this.statsResults[existingVideoSender].trackLabel || _UNKNOWN_,
|
|
204
385
|
name: MEDIA_DEVICES.CAMERA,
|
|
205
386
|
});
|
|
206
387
|
}
|
|
@@ -239,6 +420,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
239
420
|
this.mediaConnection = mediaConnection;
|
|
240
421
|
}
|
|
241
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Returns the local IP address for diagnostics.
|
|
425
|
+
* this is the local IP of the interface used for the current media connection
|
|
426
|
+
* a host can have many local Ip Addresses
|
|
427
|
+
* @returns {string | undefined} The local IP address.
|
|
428
|
+
*/
|
|
429
|
+
getLocalIpAddress(): string {
|
|
430
|
+
return this.localIpAddress;
|
|
431
|
+
}
|
|
432
|
+
|
|
242
433
|
/**
|
|
243
434
|
* Starts the stats analyzer on interval
|
|
244
435
|
*
|
|
@@ -260,7 +451,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
260
451
|
this.sendMqaData();
|
|
261
452
|
this.mqaInterval = setInterval(() => {
|
|
262
453
|
this.sendMqaData();
|
|
263
|
-
},
|
|
454
|
+
}, MQA_INTERVAL);
|
|
264
455
|
});
|
|
265
456
|
}
|
|
266
457
|
|
|
@@ -293,7 +484,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
293
484
|
this.mediaConnection = null;
|
|
294
485
|
});
|
|
295
486
|
}
|
|
296
|
-
this.mediaConnection = null;
|
|
297
487
|
|
|
298
488
|
return Promise.resolve();
|
|
299
489
|
}
|
|
@@ -324,26 +514,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
324
514
|
this.statsResults[type].recv = cloneDeep(emptyReceiver);
|
|
325
515
|
}
|
|
326
516
|
|
|
327
|
-
if (!this.statsResults.resolutions[type]) {
|
|
328
|
-
this.statsResults.resolutions[type] = {};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (isSender && !this.statsResults.resolutions[type].send) {
|
|
332
|
-
this.statsResults.resolutions[type].send = cloneDeep(emptySender);
|
|
333
|
-
} else if (!isSender && !this.statsResults.resolutions[type].recv) {
|
|
334
|
-
this.statsResults.resolutions[type].recv = cloneDeep(emptyReceiver);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (!this.statsResults.internal[type]) {
|
|
338
|
-
this.statsResults.internal[type] = {};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (isSender && !this.statsResults.internal[type].send) {
|
|
342
|
-
this.statsResults.internal[type].send = cloneDeep(emptySender);
|
|
343
|
-
} else if (!isSender && !this.statsResults.internal[type].recv) {
|
|
344
|
-
this.statsResults.internal[type].recv = cloneDeep(emptyReceiver);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
517
|
switch (getStatsResult.type) {
|
|
348
518
|
case 'outbound-rtp':
|
|
349
519
|
this.processOutboundRTPResult(getStatsResult, type);
|
|
@@ -351,13 +521,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
351
521
|
case 'inbound-rtp':
|
|
352
522
|
this.processInboundRTPResult(getStatsResult, type);
|
|
353
523
|
break;
|
|
354
|
-
case 'track':
|
|
355
|
-
this.processTrackResult(getStatsResult, type);
|
|
356
|
-
break;
|
|
357
524
|
case 'remote-inbound-rtp':
|
|
358
525
|
case 'remote-outbound-rtp':
|
|
359
|
-
|
|
360
|
-
this.compareSentAndReceived(getStatsResult, type, isSender);
|
|
526
|
+
this.compareSentAndReceived(getStatsResult, type);
|
|
361
527
|
break;
|
|
362
528
|
case 'remotecandidate':
|
|
363
529
|
case 'remote-candidate':
|
|
@@ -367,7 +533,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
367
533
|
this.parseCandidate(getStatsResult, type, isSender, false);
|
|
368
534
|
break;
|
|
369
535
|
case 'media-source':
|
|
370
|
-
// @ts-ignore
|
|
371
536
|
this.parseAudioSource(getStatsResult, type);
|
|
372
537
|
break;
|
|
373
538
|
default:
|
|
@@ -386,9 +551,28 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
386
551
|
filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {
|
|
387
552
|
const {types} = DEFAULT_GET_STATS_FILTER;
|
|
388
553
|
|
|
554
|
+
// get the successful candidate pair before parsing stats.
|
|
555
|
+
statsItem.report.forEach((report) => {
|
|
556
|
+
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
|
|
557
|
+
this.successfulCandidatePair = report;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
let videoSenderIndex = 0;
|
|
389
562
|
statsItem.report.forEach((result) => {
|
|
390
563
|
if (types.includes(result.type)) {
|
|
391
|
-
|
|
564
|
+
// if the video sender has multiple streams in the report, it is a new stream object.
|
|
565
|
+
if (type === 'video-send' && result.type === 'outbound-rtp') {
|
|
566
|
+
const newType = `video-send-layer-${videoSenderIndex}`;
|
|
567
|
+
this.parseGetStatsResult(result, newType, isSender);
|
|
568
|
+
videoSenderIndex += 1;
|
|
569
|
+
|
|
570
|
+
this.statsResults[newType].direction = statsItem.currentDirection;
|
|
571
|
+
this.statsResults[newType].trackLabel = statsItem.localTrackLabel;
|
|
572
|
+
this.statsResults[newType].csi = statsItem.csi;
|
|
573
|
+
} else {
|
|
574
|
+
this.parseGetStatsResult(result, type, isSender);
|
|
575
|
+
}
|
|
392
576
|
}
|
|
393
577
|
});
|
|
394
578
|
|
|
@@ -396,6 +580,12 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
396
580
|
this.statsResults[type].direction = statsItem.currentDirection;
|
|
397
581
|
this.statsResults[type].trackLabel = statsItem.localTrackLabel;
|
|
398
582
|
this.statsResults[type].csi = statsItem.csi;
|
|
583
|
+
this.extractAndSetLocalIpAddressInfoForDiagnostics(
|
|
584
|
+
this.successfulCandidatePair?.localCandidateId,
|
|
585
|
+
this.statsResults?.candidates
|
|
586
|
+
);
|
|
587
|
+
// reset the successful candidate pair.
|
|
588
|
+
this.successfulCandidatePair = {};
|
|
399
589
|
}
|
|
400
590
|
}
|
|
401
591
|
|
|
@@ -492,50 +682,53 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
492
682
|
const getCurrentStatsTotals = (keyPrefix: string, value: string): number =>
|
|
493
683
|
Object.keys(this.statsResults)
|
|
494
684
|
.filter((key) => key.startsWith(keyPrefix))
|
|
495
|
-
.reduce(
|
|
685
|
+
.reduce(
|
|
686
|
+
(prev, cur) =>
|
|
687
|
+
prev +
|
|
688
|
+
(this.statsResults[cur]?.[keyPrefix.includes('send') ? 'send' : 'recv'][value] || 0),
|
|
689
|
+
0
|
|
690
|
+
);
|
|
496
691
|
|
|
497
692
|
const getPreviousStatsTotals = (keyPrefix: string, value: string): number =>
|
|
498
|
-
Object.keys(this.statsResults)
|
|
499
|
-
.filter((key) => key.startsWith(keyPrefix))
|
|
500
|
-
.reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);
|
|
501
|
-
|
|
502
|
-
const getCurrentResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
|
|
503
|
-
Object.keys(this.statsResults)
|
|
504
|
-
.filter((key) => key.startsWith(keyPrefix))
|
|
505
|
-
.reduce((prev, cur) => prev + (this.statsResults.resolutions[cur]?.recv[value] || 0), 0);
|
|
506
|
-
|
|
507
|
-
const getPreviousResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
|
|
508
693
|
Object.keys(this.statsResults)
|
|
509
694
|
.filter((key) => key.startsWith(keyPrefix))
|
|
510
695
|
.reduce(
|
|
511
|
-
(prev, cur) =>
|
|
696
|
+
(prev, cur) =>
|
|
697
|
+
prev +
|
|
698
|
+
(this.lastStatsResults[cur]?.[keyPrefix.includes('send') ? 'send' : 'recv'][value] ||
|
|
699
|
+
0),
|
|
512
700
|
0
|
|
513
701
|
);
|
|
514
702
|
|
|
515
|
-
|
|
703
|
+
// Audio Transmit
|
|
704
|
+
if (this.lastStatsResults['audio-send']) {
|
|
516
705
|
// compare audio stats sent
|
|
517
706
|
// NOTE: relies on there being only one sender.
|
|
518
707
|
const currentStats = this.statsResults['audio-send'].send;
|
|
519
708
|
const previousStats = this.lastStatsResults['audio-send'].send;
|
|
520
709
|
|
|
521
710
|
if (
|
|
522
|
-
|
|
711
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
712
|
+
currentStats.totalPacketsSent === previousStats.totalPacketsSent) ||
|
|
523
713
|
currentStats.totalPacketsSent === 0
|
|
524
714
|
) {
|
|
525
715
|
LoggerProxy.logger.info(
|
|
526
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent
|
|
716
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,
|
|
717
|
+
currentStats.totalPacketsSent
|
|
527
718
|
);
|
|
528
719
|
} else {
|
|
529
720
|
if (
|
|
530
|
-
|
|
721
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
722
|
+
currentStats.totalAudioEnergy === previousStats.totalAudioEnergy) ||
|
|
531
723
|
currentStats.totalAudioEnergy === 0
|
|
532
724
|
) {
|
|
533
725
|
LoggerProxy.logger.info(
|
|
534
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present
|
|
726
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,
|
|
727
|
+
currentStats.totalAudioEnergy
|
|
535
728
|
);
|
|
536
729
|
}
|
|
537
730
|
|
|
538
|
-
if (currentStats.audioLevel === 0) {
|
|
731
|
+
if (this.meetingMediaStatus.expected.sendAudio && currentStats.audioLevel === 0) {
|
|
539
732
|
LoggerProxy.logger.info(
|
|
540
733
|
`StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`
|
|
541
734
|
);
|
|
@@ -550,217 +743,141 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
550
743
|
);
|
|
551
744
|
}
|
|
552
745
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
'audio-recv',
|
|
563
|
-
'totalSamplesReceived'
|
|
564
|
-
);
|
|
746
|
+
// Audio Receive
|
|
747
|
+
const currentAudioPacketsReceived = getCurrentStatsTotals(
|
|
748
|
+
'audio-recv',
|
|
749
|
+
'totalPacketsReceived'
|
|
750
|
+
);
|
|
751
|
+
const previousAudioPacketsReceived = getPreviousStatsTotals(
|
|
752
|
+
'audio-recv',
|
|
753
|
+
'totalPacketsReceived'
|
|
754
|
+
);
|
|
565
755
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
currentSamplesReceived === 0
|
|
573
|
-
) {
|
|
574
|
-
LoggerProxy.logger.info(
|
|
575
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`
|
|
576
|
-
);
|
|
577
|
-
}
|
|
756
|
+
this.emitStartStopEvents(
|
|
757
|
+
'audio',
|
|
758
|
+
previousAudioPacketsReceived,
|
|
759
|
+
currentAudioPacketsReceived,
|
|
760
|
+
false
|
|
761
|
+
);
|
|
578
762
|
|
|
579
|
-
|
|
580
|
-
|
|
763
|
+
const currentTotalPacketsSent = getCurrentStatsTotals('video-send', 'totalPacketsSent');
|
|
764
|
+
const previousTotalPacketsSent = getPreviousStatsTotals('video-send', 'totalPacketsSent');
|
|
765
|
+
|
|
766
|
+
const currentFramesEncoded = getCurrentStatsTotals('video-send', 'framesEncoded');
|
|
767
|
+
const previousFramesEncoded = getPreviousStatsTotals('video-send', 'framesEncoded');
|
|
768
|
+
|
|
769
|
+
const currentFramesSent = getCurrentStatsTotals('video-send', 'framesSent');
|
|
770
|
+
const previousFramesSent = getPreviousStatsTotals('video-send', 'framesSent');
|
|
581
771
|
|
|
582
|
-
|
|
772
|
+
const doesVideoSendExist = Object.keys(this.lastStatsResults).some((item) =>
|
|
773
|
+
item.includes('video-send')
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// Video Transmit
|
|
777
|
+
if (doesVideoSendExist) {
|
|
583
778
|
// compare video stats sent
|
|
584
|
-
const currentStats = this.statsResults['video-send'].send;
|
|
585
|
-
const previousStats = this.lastStatsResults['video-send'].send;
|
|
586
779
|
|
|
587
780
|
if (
|
|
588
|
-
|
|
589
|
-
|
|
781
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
782
|
+
(currentTotalPacketsSent === previousTotalPacketsSent || currentTotalPacketsSent === 0)
|
|
590
783
|
) {
|
|
591
784
|
LoggerProxy.logger.info(
|
|
592
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent
|
|
785
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
|
|
786
|
+
currentTotalPacketsSent
|
|
593
787
|
);
|
|
594
788
|
} else {
|
|
595
789
|
if (
|
|
596
|
-
|
|
597
|
-
|
|
790
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
791
|
+
(currentFramesEncoded === previousFramesEncoded || currentFramesEncoded === 0)
|
|
598
792
|
) {
|
|
599
793
|
LoggerProxy.logger.info(
|
|
600
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded
|
|
794
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
|
|
795
|
+
currentFramesEncoded
|
|
601
796
|
);
|
|
602
797
|
}
|
|
603
798
|
|
|
604
799
|
if (
|
|
605
|
-
this.
|
|
606
|
-
|
|
607
|
-
this.statsResults.resolutions['video-send'].send.framesSent === 0
|
|
800
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
801
|
+
(currentFramesSent === previousFramesSent || currentFramesSent === 0)
|
|
608
802
|
) {
|
|
609
803
|
LoggerProxy.logger.info(
|
|
610
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent
|
|
804
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
|
|
805
|
+
currentFramesSent
|
|
611
806
|
);
|
|
612
807
|
}
|
|
613
808
|
}
|
|
614
809
|
|
|
615
|
-
this.emitStartStopEvents('video',
|
|
810
|
+
this.emitStartStopEvents('video', previousFramesSent, currentFramesSent, true);
|
|
616
811
|
}
|
|
617
812
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const previousPacketsReceived = getPreviousStatsTotals(
|
|
622
|
-
'video-recv',
|
|
623
|
-
'totalPacketsReceived'
|
|
624
|
-
);
|
|
625
|
-
const currentFramesReceived = getCurrentResolutionsStatsTotals(
|
|
626
|
-
'video-recv',
|
|
627
|
-
'framesReceived'
|
|
628
|
-
);
|
|
629
|
-
const previousFramesReceived = getPreviousResolutionsStatsTotals(
|
|
630
|
-
'video-recv',
|
|
631
|
-
'framesReceived'
|
|
632
|
-
);
|
|
633
|
-
const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
|
|
634
|
-
const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
|
|
635
|
-
const currentFramesDropped = getCurrentResolutionsStatsTotals(
|
|
636
|
-
'video-recv',
|
|
637
|
-
'framesDropped'
|
|
638
|
-
);
|
|
639
|
-
const previousFramesDropped = getPreviousResolutionsStatsTotals(
|
|
640
|
-
'video-recv',
|
|
641
|
-
'framesDropped'
|
|
642
|
-
);
|
|
813
|
+
// Video Receive
|
|
814
|
+
const currentVideoFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
|
|
815
|
+
const previousVideoFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
|
|
643
816
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
LoggerProxy.logger.info(
|
|
651
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames received`
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
656
|
-
LoggerProxy.logger.info(
|
|
657
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`
|
|
658
|
-
);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
if (currentFramesDropped - previousFramesDropped > 10) {
|
|
662
|
-
LoggerProxy.logger.info(
|
|
663
|
-
`StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);
|
|
669
|
-
}
|
|
817
|
+
this.emitStartStopEvents(
|
|
818
|
+
'video',
|
|
819
|
+
previousVideoFramesDecoded,
|
|
820
|
+
currentVideoFramesDecoded,
|
|
821
|
+
false
|
|
822
|
+
);
|
|
670
823
|
|
|
671
|
-
|
|
824
|
+
// Share Transmit
|
|
825
|
+
if (this.lastStatsResults['video-share-send']) {
|
|
672
826
|
// compare share stats sent
|
|
673
827
|
|
|
674
828
|
const currentStats = this.statsResults['video-share-send'].send;
|
|
675
829
|
const previousStats = this.lastStatsResults['video-share-send'].send;
|
|
676
830
|
|
|
677
831
|
if (
|
|
678
|
-
|
|
679
|
-
currentStats.totalPacketsSent ===
|
|
832
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
833
|
+
(currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
|
|
834
|
+
currentStats.totalPacketsSent === 0)
|
|
680
835
|
) {
|
|
681
836
|
LoggerProxy.logger.info(
|
|
682
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent
|
|
837
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,
|
|
838
|
+
currentStats.totalPacketsSent
|
|
683
839
|
);
|
|
684
840
|
} else {
|
|
685
841
|
if (
|
|
686
|
-
|
|
687
|
-
currentStats.framesEncoded ===
|
|
842
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
843
|
+
(currentStats.framesEncoded === previousStats.framesEncoded ||
|
|
844
|
+
currentStats.framesEncoded === 0)
|
|
688
845
|
) {
|
|
689
846
|
LoggerProxy.logger.info(
|
|
690
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded
|
|
847
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,
|
|
848
|
+
currentStats.framesEncoded
|
|
691
849
|
);
|
|
692
850
|
}
|
|
693
851
|
|
|
694
852
|
if (
|
|
695
|
-
this.
|
|
696
|
-
|
|
697
|
-
|
|
853
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
854
|
+
(this.statsResults['video-share-send'].send.framesSent ===
|
|
855
|
+
this.lastStatsResults['video-share-send'].send.framesSent ||
|
|
856
|
+
this.statsResults['video-share-send'].send.framesSent === 0)
|
|
698
857
|
) {
|
|
699
858
|
LoggerProxy.logger.info(
|
|
700
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent
|
|
859
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,
|
|
860
|
+
this.statsResults['video-share-send'].send.framesSent
|
|
701
861
|
);
|
|
702
862
|
}
|
|
703
863
|
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
if (this.meetingMediaStatus.expected.sendShare) {
|
|
707
|
-
// TODO:need to check receive share value
|
|
708
|
-
// compare share stats received
|
|
709
|
-
const currentPacketsReceived = getCurrentStatsTotals(
|
|
710
|
-
'video-share-recv',
|
|
711
|
-
'totalPacketsReceived'
|
|
712
|
-
);
|
|
713
|
-
const previousPacketsReceived = getPreviousStatsTotals(
|
|
714
|
-
'video-share-recv',
|
|
715
|
-
'totalPacketsReceived'
|
|
716
|
-
);
|
|
717
|
-
const currentFramesReceived = getCurrentResolutionsStatsTotals(
|
|
718
|
-
'video-share-recv',
|
|
719
|
-
'framesReceived'
|
|
720
|
-
);
|
|
721
|
-
const previousFramesReceived = getPreviousResolutionsStatsTotals(
|
|
722
|
-
'video-share-recv',
|
|
723
|
-
'framesReceived'
|
|
724
|
-
);
|
|
725
|
-
const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
|
|
726
|
-
const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');
|
|
727
|
-
const currentFramesDropped = getCurrentResolutionsStatsTotals(
|
|
728
|
-
'video-share-recv',
|
|
729
|
-
'framesDropped'
|
|
730
|
-
);
|
|
731
|
-
const previousFramesDropped = getPreviousResolutionsStatsTotals(
|
|
732
|
-
'video-share-recv',
|
|
733
|
-
'framesDropped'
|
|
734
|
-
);
|
|
735
|
-
|
|
736
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
737
|
-
LoggerProxy.logger.info(
|
|
738
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`
|
|
739
|
-
);
|
|
740
|
-
} else {
|
|
741
|
-
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
742
|
-
LoggerProxy.logger.info(
|
|
743
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames received`
|
|
744
|
-
);
|
|
745
|
-
}
|
|
746
864
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
865
|
+
this.emitStartStopEvents('share', previousStats.framesSent, currentStats.framesSent, true);
|
|
866
|
+
}
|
|
752
867
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
868
|
+
// Share receive
|
|
869
|
+
const currentShareFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
|
|
870
|
+
const previousShareFramesDecoded = getPreviousStatsTotals(
|
|
871
|
+
'video-share-recv',
|
|
872
|
+
'framesDecoded'
|
|
873
|
+
);
|
|
759
874
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
875
|
+
this.emitStartStopEvents(
|
|
876
|
+
'share',
|
|
877
|
+
previousShareFramesDecoded,
|
|
878
|
+
currentShareFramesDecoded,
|
|
879
|
+
false
|
|
880
|
+
);
|
|
764
881
|
}
|
|
765
882
|
}
|
|
766
883
|
|
|
@@ -851,43 +968,20 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
851
968
|
const sendrecvType = STATS.SEND_DIRECTION;
|
|
852
969
|
|
|
853
970
|
if (result.bytesSent) {
|
|
854
|
-
|
|
971
|
+
const kilobytes = 0;
|
|
855
972
|
|
|
856
|
-
if (
|
|
857
|
-
this.statsResults
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
this.statsResults
|
|
973
|
+
if (result.frameWidth && result.frameHeight) {
|
|
974
|
+
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
975
|
+
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
976
|
+
this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;
|
|
977
|
+
this.statsResults[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;
|
|
861
978
|
}
|
|
862
|
-
if (!this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded) {
|
|
863
|
-
this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded =
|
|
864
|
-
result.keyFramesEncoded;
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
const bytes =
|
|
868
|
-
result.bytesSent - this.statsResults.internal[mediaType][sendrecvType].prevBytesSent;
|
|
869
|
-
|
|
870
|
-
this.statsResults.internal[mediaType][sendrecvType].prevBytesSent = result.bytesSent;
|
|
871
|
-
|
|
872
|
-
kilobytes = bytes / 1024;
|
|
873
979
|
|
|
874
980
|
this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
|
875
|
-
this.statsResults[mediaType].bytesSent = kilobytes;
|
|
876
|
-
|
|
877
|
-
this.statsResults[mediaType][sendrecvType].framesEncoded =
|
|
878
|
-
result.framesEncoded - this.statsResults.internal[mediaType][sendrecvType].framesEncoded;
|
|
879
|
-
this.statsResults[mediaType][sendrecvType].keyFramesEncoded =
|
|
880
|
-
result.keyFramesEncoded -
|
|
881
|
-
this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded;
|
|
882
|
-
this.statsResults.internal[mediaType].outboundRtpId = result.id;
|
|
883
|
-
|
|
884
|
-
if (!this.statsResults.internal[mediaType][sendrecvType].packetsSent) {
|
|
885
|
-
this.statsResults.internal[mediaType][sendrecvType].packetsSent = result.packetsSent;
|
|
886
|
-
}
|
|
887
981
|
|
|
888
|
-
this.statsResults[mediaType][sendrecvType].
|
|
889
|
-
|
|
890
|
-
this.statsResults
|
|
982
|
+
this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;
|
|
983
|
+
this.statsResults[mediaType][sendrecvType].keyFramesEncoded = result.keyFramesEncoded;
|
|
984
|
+
this.statsResults[mediaType][sendrecvType].packetsSent = result.packetsSent;
|
|
891
985
|
|
|
892
986
|
// Data saved to send MQA metrics
|
|
893
987
|
|
|
@@ -925,75 +1019,92 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
925
1019
|
|
|
926
1020
|
if (result.bytesReceived) {
|
|
927
1021
|
let kilobytes = 0;
|
|
1022
|
+
const receiveSlot = this.receiveSlotCallback(result.ssrc);
|
|
1023
|
+
const sourceState = receiveSlot?.sourceState;
|
|
1024
|
+
const idAndCsi = receiveSlot
|
|
1025
|
+
? `id: "${receiveSlot.id || ''}"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`
|
|
1026
|
+
: '';
|
|
928
1027
|
|
|
929
|
-
|
|
930
|
-
this.statsResults
|
|
931
|
-
result.bytesReceived;
|
|
932
|
-
}
|
|
1028
|
+
const bytes =
|
|
1029
|
+
result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;
|
|
933
1030
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
1031
|
+
kilobytes = bytes / 1024;
|
|
1032
|
+
this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
|
937
1033
|
|
|
938
|
-
|
|
939
|
-
this.statsResults
|
|
1034
|
+
let currentPacketsLost =
|
|
1035
|
+
result.packetsLost - this.statsResults[mediaType][sendrecvType].totalPacketsLost;
|
|
1036
|
+
if (currentPacketsLost < 0) {
|
|
1037
|
+
currentPacketsLost = 0;
|
|
940
1038
|
}
|
|
1039
|
+
const packetsReceivedDiff =
|
|
1040
|
+
result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;
|
|
1041
|
+
this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;
|
|
941
1042
|
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
1043
|
+
if (packetsReceivedDiff === 0) {
|
|
1044
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1045
|
+
LoggerProxy.logger.info(
|
|
1046
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total packets received on slot: `,
|
|
1047
|
+
result.packetsReceived
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
945
1050
|
}
|
|
946
1051
|
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
result.
|
|
950
|
-
}
|
|
1052
|
+
if (mediaType.startsWith('video') || mediaType.startsWith('share')) {
|
|
1053
|
+
const videoFramesReceivedDiff =
|
|
1054
|
+
result.framesReceived - this.statsResults[mediaType][sendrecvType].framesReceived;
|
|
951
1055
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1056
|
+
if (videoFramesReceivedDiff === 0) {
|
|
1057
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1058
|
+
LoggerProxy.logger.info(
|
|
1059
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames received on slot: `,
|
|
1060
|
+
result.framesReceived
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
955
1064
|
|
|
956
|
-
|
|
1065
|
+
const videoFramesDecodedDiff =
|
|
1066
|
+
result.framesDecoded - this.statsResults[mediaType][sendrecvType].framesDecoded;
|
|
957
1067
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (this.statsResults[mediaType][sendrecvType].currentPacketsLost < 0) {
|
|
967
|
-
this.statsResults[mediaType][sendrecvType].currentPacketsLost = 0;
|
|
968
|
-
}
|
|
1068
|
+
if (videoFramesDecodedDiff === 0) {
|
|
1069
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1070
|
+
LoggerProxy.logger.info(
|
|
1071
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames decoded on slot: `,
|
|
1072
|
+
result.framesDecoded
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
969
1076
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived;
|
|
973
|
-
this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived =
|
|
974
|
-
result.packetsReceived;
|
|
1077
|
+
const videoFramesDroppedDiff =
|
|
1078
|
+
result.framesDropped - this.statsResults[mediaType][sendrecvType].framesDropped;
|
|
975
1079
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1080
|
+
if (videoFramesDroppedDiff > 10) {
|
|
1081
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1082
|
+
LoggerProxy.logger.info(
|
|
1083
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Frames dropped for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames dropped on slot: `,
|
|
1084
|
+
result.framesDropped
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
981
1088
|
}
|
|
982
1089
|
|
|
983
1090
|
// Check the over all packet Lost ratio
|
|
984
1091
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =
|
|
985
|
-
|
|
986
|
-
?
|
|
987
|
-
(this.statsResults[mediaType][sendrecvType].packetsReceived +
|
|
988
|
-
this.statsResults[mediaType][sendrecvType].currentPacketsLost)
|
|
1092
|
+
currentPacketsLost > 0
|
|
1093
|
+
? currentPacketsLost / (packetsReceivedDiff + currentPacketsLost)
|
|
989
1094
|
: 0;
|
|
990
1095
|
if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {
|
|
991
1096
|
LoggerProxy.logger.info(
|
|
992
|
-
|
|
1097
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,
|
|
993
1098
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio
|
|
994
1099
|
);
|
|
995
1100
|
}
|
|
996
1101
|
|
|
1102
|
+
if (result.frameWidth && result.frameHeight) {
|
|
1103
|
+
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
1104
|
+
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
1105
|
+
this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
997
1108
|
// TODO: check the packet loss value is negative values here
|
|
998
1109
|
|
|
999
1110
|
if (result.packetsLost) {
|
|
@@ -1011,6 +1122,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1011
1122
|
this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;
|
|
1012
1123
|
this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
|
|
1013
1124
|
this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;
|
|
1125
|
+
this.statsResults[mediaType][sendrecvType].framesDropped = result.framesDropped;
|
|
1014
1126
|
|
|
1015
1127
|
this.statsResults[mediaType][sendrecvType].decoderImplementation =
|
|
1016
1128
|
result.decoderImplementation;
|
|
@@ -1035,6 +1147,48 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1035
1147
|
}
|
|
1036
1148
|
}
|
|
1037
1149
|
|
|
1150
|
+
/**
|
|
1151
|
+
* extracts the local Ip address from the statsResult object by looking at stats results candidates
|
|
1152
|
+
* and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
|
|
1153
|
+
* and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
|
|
1154
|
+
* note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address
|
|
1155
|
+
* for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
|
|
1156
|
+
* @private
|
|
1157
|
+
* @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
|
|
1158
|
+
* @param {Object} candidates - the stats result candidates
|
|
1159
|
+
* @returns {void}
|
|
1160
|
+
*/
|
|
1161
|
+
extractAndSetLocalIpAddressInfoForDiagnostics = (
|
|
1162
|
+
successfulCandidatePairId: string,
|
|
1163
|
+
candidates: {[key: string]: Record<string, unknown>}
|
|
1164
|
+
) => {
|
|
1165
|
+
let newIpAddress = '';
|
|
1166
|
+
if (successfulCandidatePairId && !isEmpty(candidates)) {
|
|
1167
|
+
const localCandidate = candidates[successfulCandidatePairId];
|
|
1168
|
+
if (localCandidate) {
|
|
1169
|
+
if (localCandidate.candidateType === 'host') {
|
|
1170
|
+
// if it's a host candidate, use the address property - it will be the local IP
|
|
1171
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1172
|
+
} else if (localCandidate.candidateType === 'prflx') {
|
|
1173
|
+
// if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)
|
|
1174
|
+
// then look at the relatedAddress - it will be the local
|
|
1175
|
+
//
|
|
1176
|
+
// Firefox doesn't populate the relayProtocol property
|
|
1177
|
+
if (!localCandidate.relayProtocol) {
|
|
1178
|
+
newIpAddress = `${localCandidate.relatedAddress}`;
|
|
1179
|
+
} else {
|
|
1180
|
+
// if it's a peer reflexive candidate and we are using a relay -
|
|
1181
|
+
// in that case the relatedAddress will be the IP of the TURN server (Linus),
|
|
1182
|
+
// so we can only look at the address, but it might be local IP or public IP,
|
|
1183
|
+
// depending on if the user is behind a NAT or not
|
|
1184
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
this.localIpAddress = newIpAddress;
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1038
1192
|
/**
|
|
1039
1193
|
* Processes remote and local candidate result and stores
|
|
1040
1194
|
* @private
|
|
@@ -1049,125 +1203,53 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1049
1203
|
if (!result || !result.id) {
|
|
1050
1204
|
return;
|
|
1051
1205
|
}
|
|
1052
|
-
const RemoteCandidateType = {};
|
|
1053
|
-
const RemoteTransport = {};
|
|
1054
|
-
const RemoteIpAddress = {};
|
|
1055
|
-
const RemoteNetworkType = {};
|
|
1056
|
-
|
|
1057
|
-
if (!result.id) return;
|
|
1058
|
-
|
|
1059
|
-
const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;
|
|
1060
|
-
const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;
|
|
1061
|
-
|
|
1062
|
-
if (!RemoteCandidateType[result.id]) {
|
|
1063
|
-
RemoteCandidateType[result.id] = [];
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
if (!RemoteTransport[result.id]) {
|
|
1067
|
-
RemoteTransport[result.id] = [];
|
|
1068
|
-
}
|
|
1069
1206
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if (!RemoteNetworkType[result.id]) {
|
|
1074
|
-
RemoteNetworkType[result.id] = [];
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
if (
|
|
1078
|
-
result.candidateType &&
|
|
1079
|
-
RemoteCandidateType[result.id].indexOf(result.candidateType) === -1
|
|
1080
|
-
) {
|
|
1081
|
-
RemoteCandidateType[result.id].push(result.candidateType);
|
|
1207
|
+
// We only care about the successful local candidate
|
|
1208
|
+
if (this.successfulCandidatePair?.localCandidateId !== result.id) {
|
|
1209
|
+
return;
|
|
1082
1210
|
}
|
|
1083
1211
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1212
|
+
let transport;
|
|
1213
|
+
if (result.relayProtocol) {
|
|
1214
|
+
transport = result.relayProtocol.toUpperCase();
|
|
1215
|
+
} else if (result.protocol) {
|
|
1216
|
+
transport = result.protocol.toUpperCase();
|
|
1086
1217
|
}
|
|
1087
1218
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
RemoteIpAddress[result.id].indexOf(`${result.ip}:${result.portNumber}`) === -1
|
|
1091
|
-
) {
|
|
1092
|
-
RemoteIpAddress[result.id].push(`${result.ip}`); // TODO: Add ports
|
|
1093
|
-
}
|
|
1219
|
+
const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;
|
|
1220
|
+
const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;
|
|
1094
1221
|
|
|
1095
|
-
if (
|
|
1096
|
-
|
|
1222
|
+
if (!this.statsResults.candidates) {
|
|
1223
|
+
this.statsResults.candidates = {};
|
|
1097
1224
|
}
|
|
1098
1225
|
|
|
1099
|
-
this.statsResults.
|
|
1100
|
-
candidateType:
|
|
1101
|
-
ipAddress:
|
|
1226
|
+
this.statsResults.candidates[result.id] = {
|
|
1227
|
+
candidateType: result.candidateType,
|
|
1228
|
+
ipAddress: result.ip, // TODO: add ports
|
|
1229
|
+
relatedAddress: result.relatedAddress,
|
|
1230
|
+
relatedPort: result.relatedPort,
|
|
1231
|
+
relayProtocol: result.relayProtocol,
|
|
1232
|
+
protocol: result.protocol,
|
|
1233
|
+
address: result.address,
|
|
1102
1234
|
portNumber: result.port,
|
|
1103
|
-
networkType:
|
|
1235
|
+
networkType: result.networkType,
|
|
1104
1236
|
priority: result.priority,
|
|
1105
|
-
transport
|
|
1237
|
+
transport,
|
|
1106
1238
|
timestamp: result.time,
|
|
1107
1239
|
id: result.id,
|
|
1108
1240
|
type: result.type,
|
|
1109
1241
|
};
|
|
1110
1242
|
|
|
1111
|
-
this.statsResults.connectionType[ipType].candidateType =
|
|
1112
|
-
this.statsResults.connectionType[ipType].ipAddress =
|
|
1243
|
+
this.statsResults.connectionType[ipType].candidateType = result.candidateType;
|
|
1244
|
+
this.statsResults.connectionType[ipType].ipAddress = result.ipAddress;
|
|
1113
1245
|
|
|
1114
1246
|
this.statsResults.connectionType[ipType].networkType =
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
: RemoteNetworkType[result.id][0];
|
|
1118
|
-
this.statsResults.connectionType[ipType].transport = RemoteTransport[result.id];
|
|
1247
|
+
result.networkType === NETWORK_TYPE.VPN ? NETWORK_TYPE.UNKNOWN : result.networkType;
|
|
1248
|
+
this.statsResults.connectionType[ipType].transport = transport;
|
|
1119
1249
|
|
|
1120
1250
|
this.statsResults[type][sendRecvType].totalRoundTripTime = result.totalRoundTripTime;
|
|
1121
1251
|
};
|
|
1122
1252
|
|
|
1123
|
-
/**
|
|
1124
|
-
* Process Track results
|
|
1125
|
-
*
|
|
1126
|
-
* @private
|
|
1127
|
-
* @param {*} result
|
|
1128
|
-
* @param {*} mediaType
|
|
1129
|
-
* @returns {void}
|
|
1130
|
-
* @memberof StatsAnalyzer
|
|
1131
|
-
*/
|
|
1132
|
-
private processTrackResult(result: any, mediaType: any) {
|
|
1133
|
-
if (!result || result.type !== 'track') {
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
const sendrecvType =
|
|
1138
|
-
result.remoteSource === true ? STATS.RECEIVE_DIRECTION : STATS.SEND_DIRECTION;
|
|
1139
|
-
|
|
1140
|
-
if (result.frameWidth && result.frameHeight) {
|
|
1141
|
-
this.statsResults.resolutions[mediaType][sendrecvType].width = result.frameWidth;
|
|
1142
|
-
this.statsResults.resolutions[mediaType][sendrecvType].height = result.frameHeight;
|
|
1143
|
-
this.statsResults.resolutions[mediaType][sendrecvType].framesSent = result.framesSent;
|
|
1144
|
-
this.statsResults.resolutions[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
if (sendrecvType === STATS.RECEIVE_DIRECTION) {
|
|
1148
|
-
this.statsResults.resolutions[mediaType][sendrecvType].framesReceived = result.framesReceived;
|
|
1149
|
-
this.statsResults.resolutions[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
|
|
1150
|
-
this.statsResults.resolutions[mediaType][sendrecvType].framesDropped = result.framesDropped;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
if (result.trackIdentifier && !mediaType.includes('audio')) {
|
|
1154
|
-
this.statsResults.resolutions[mediaType][sendrecvType].trackIdentifier =
|
|
1155
|
-
result.trackIdentifier;
|
|
1156
|
-
|
|
1157
|
-
const jitterBufferDelay = result && result.jitterBufferDelay;
|
|
1158
|
-
const jitterBufferEmittedCount = result && result.jitterBufferEmittedCount;
|
|
1159
|
-
|
|
1160
|
-
this.statsResults.resolutions[mediaType][sendrecvType].avgJitterDelay =
|
|
1161
|
-
jitterBufferEmittedCount && +jitterBufferDelay / +jitterBufferEmittedCount;
|
|
1162
|
-
|
|
1163
|
-
// Used to calculate the jitter
|
|
1164
|
-
this.statsResults.resolutions[mediaType][sendrecvType].jitterBufferDelay =
|
|
1165
|
-
result.jitterBufferDelay;
|
|
1166
|
-
this.statsResults.resolutions[mediaType][sendrecvType].jitterBufferEmittedCount =
|
|
1167
|
-
result.jitterBufferEmittedCount;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
1253
|
/**
|
|
1172
1254
|
*
|
|
1173
1255
|
* @private
|
|
@@ -1178,20 +1260,15 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1178
1260
|
*/
|
|
1179
1261
|
compareSentAndReceived(result, type) {
|
|
1180
1262
|
// Don't compare on transceivers without a sender.
|
|
1181
|
-
if (!type || !this.statsResults
|
|
1263
|
+
if (!type || !this.statsResults[type].send) {
|
|
1182
1264
|
return;
|
|
1183
1265
|
}
|
|
1184
1266
|
|
|
1185
1267
|
const mediaType = type;
|
|
1186
1268
|
|
|
1187
|
-
if (!this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver) {
|
|
1188
|
-
this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
1269
|
const currentPacketLoss =
|
|
1192
|
-
result.packetsLost - this.statsResults
|
|
1270
|
+
result.packetsLost - this.statsResults[mediaType].send.totalPacketsLostOnReceiver;
|
|
1193
1271
|
|
|
1194
|
-
this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1195
1272
|
this.statsResults[mediaType].send.packetsLostOnReceiver = currentPacketLoss;
|
|
1196
1273
|
this.statsResults[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1197
1274
|
|