@webex/plugin-meetings 3.0.0-beta.39 → 3.0.0-beta.391
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 +671 -81
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/utils.js +45 -1
- 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 +5 -10
- package/dist/config.js.map +1 -1
- package/dist/constants.js +242 -33
- 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 +110 -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 +4075 -2827
- 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 +224 -136
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +177 -152
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +672 -417
- package/dist/meeting/util.js.map +1 -1
- 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 +192 -51
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +1 -1
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +36 -36
- 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 +58 -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 +102 -6
- 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 +51 -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 +220 -70
- 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 +357 -295
- 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 +14 -0
- 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 +203 -31
- 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 +567 -496
- package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
- package/dist/types/meeting/muteState.d.ts +93 -25
- package/dist/types/meeting/request.d.ts +64 -43
- package/dist/types/meeting/util.d.ts +117 -1
- 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 +72 -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 +62 -2
- package/dist/types/multistream/sendSlotManager.d.ts +70 -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 +2 -1
- package/dist/types/roap/request.d.ts +9 -8
- package/dist/types/roap/turnDiscovery.d.ts +39 -5
- 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 +35 -11
- package/src/breakouts/breakout.ts +67 -9
- package/src/breakouts/events.ts +56 -0
- package/src/breakouts/index.ts +558 -59
- package/src/breakouts/utils.ts +42 -0
- 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 +4 -9
- package/src/constants.ts +229 -21
- 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 +44 -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 +3411 -2435
- package/src/meeting/locusMediaRequest.ts +313 -0
- package/src/meeting/muteState.ts +223 -136
- package/src/meeting/request.ts +155 -120
- package/src/meeting/util.ts +685 -395
- package/src/meeting-info/index.ts +81 -8
- package/src/meeting-info/meeting-info-v2.ts +170 -14
- package/src/meeting-info/util.ts +1 -1
- package/src/meeting-info/utilv2.ts +23 -23
- 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 +58 -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 +134 -8
- 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 -44
- package/src/multistream/sendSlotManager.ts +199 -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 +52 -23
- package/src/roap/request.ts +48 -67
- package/src/roap/turnDiscovery.ts +147 -49
- package/src/rtcMetrics/constants.ts +3 -0
- package/src/rtcMetrics/index.ts +166 -0
- package/src/statsAnalyzer/index.ts +457 -416
- 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 +320 -261
- 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 +118 -28
- package/test/unit/spec/breakouts/events.ts +89 -0
- package/test/unit/spec/breakouts/index.ts +1349 -114
- package/test/unit/spec/breakouts/utils.js +52 -1
- 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 +1363 -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 +208 -17
- package/test/unit/spec/media/index.ts +173 -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 +6821 -2172
- package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
- package/test/unit/spec/meeting/muteState.js +402 -212
- package/test/unit/spec/meeting/request.js +473 -54
- package/test/unit/spec/meeting/utils.js +773 -67
- package/test/unit/spec/meeting-info/index.js +300 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
- 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 +1415 -213
- 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 +1 -50
- package/test/unit/spec/multistream/mediaRequestManager.ts +781 -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 +345 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +525 -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 +294 -218
- package/test/unit/spec/recording-controller/util.js +223 -96
- package/test/unit/spec/roap/index.ts +180 -83
- package/test/unit/spec/roap/request.ts +100 -62
- package/test/unit/spec/roap/turnDiscovery.ts +388 -96
- package/test/unit/spec/rtcMetrics/index.ts +122 -0
- package/test/unit/spec/stats-analyzer/index.js +1252 -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,209 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
142
173
|
sendMqaData() {
|
|
143
174
|
const newMqa = cloneDeep(emptyMqaInterval);
|
|
144
175
|
|
|
176
|
+
// Fill in empty stats items for lastMqaDataSent
|
|
145
177
|
Object.keys(this.statsResults).forEach((mediaType) => {
|
|
146
|
-
if (
|
|
147
|
-
|
|
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);
|
|
148
224
|
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
266
|
+
Object.keys(this.statsResults).forEach((mediaType) => {
|
|
267
|
+
if (mediaType.includes('audio-send')) {
|
|
268
|
+
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
269
|
+
|
|
270
|
+
getAudioSenderStreamMqa({
|
|
271
|
+
audioSenderStream,
|
|
151
272
|
statsResults: this.statsResults,
|
|
152
273
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
153
274
|
mediaType,
|
|
154
275
|
});
|
|
155
|
-
newMqa.audioTransmit.push(
|
|
156
|
-
} else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {
|
|
157
|
-
const audioReceiver = cloneDeep(emptyAudioReceive);
|
|
276
|
+
newMqa.audioTransmit[0].streams.push(audioSenderStream);
|
|
158
277
|
|
|
159
|
-
|
|
160
|
-
|
|
278
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
279
|
+
} else if (mediaType.includes('audio-share-send')) {
|
|
280
|
+
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
281
|
+
|
|
282
|
+
getAudioSenderStreamMqa({
|
|
283
|
+
audioSenderStream,
|
|
161
284
|
statsResults: this.statsResults,
|
|
162
285
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
163
286
|
mediaType,
|
|
164
287
|
});
|
|
165
|
-
newMqa.
|
|
166
|
-
|
|
167
|
-
|
|
288
|
+
newMqa.audioTransmit[1].streams.push(audioSenderStream);
|
|
289
|
+
|
|
290
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
291
|
+
} else if (mediaType.includes('audio-recv')) {
|
|
292
|
+
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
168
293
|
|
|
169
|
-
|
|
170
|
-
|
|
294
|
+
getAudioReceiverStreamMqa({
|
|
295
|
+
audioReceiverStream,
|
|
171
296
|
statsResults: this.statsResults,
|
|
172
297
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
173
298
|
mediaType,
|
|
174
299
|
});
|
|
175
|
-
newMqa.
|
|
176
|
-
|
|
177
|
-
|
|
300
|
+
newMqa.audioReceive[0].streams.push(audioReceiverStream);
|
|
301
|
+
|
|
302
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
303
|
+
} else if (mediaType.includes('audio-share-recv')) {
|
|
304
|
+
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
178
305
|
|
|
179
|
-
|
|
180
|
-
|
|
306
|
+
getAudioReceiverStreamMqa({
|
|
307
|
+
audioReceiverStream,
|
|
181
308
|
statsResults: this.statsResults,
|
|
182
309
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
183
310
|
mediaType,
|
|
184
311
|
});
|
|
185
|
-
newMqa.
|
|
312
|
+
newMqa.audioReceive[1].streams.push(audioReceiverStream);
|
|
313
|
+
|
|
314
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
315
|
+
} else if (mediaType.includes('video-send')) {
|
|
316
|
+
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
317
|
+
|
|
318
|
+
getVideoSenderStreamMqa({
|
|
319
|
+
videoSenderStream,
|
|
320
|
+
statsResults: this.statsResults,
|
|
321
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
322
|
+
mediaType,
|
|
323
|
+
});
|
|
324
|
+
newMqa.videoTransmit[0].streams.push(videoSenderStream);
|
|
325
|
+
|
|
326
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
327
|
+
} else if (mediaType.includes('video-share-send')) {
|
|
328
|
+
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
329
|
+
|
|
330
|
+
getVideoSenderStreamMqa({
|
|
331
|
+
videoSenderStream,
|
|
332
|
+
statsResults: this.statsResults,
|
|
333
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
334
|
+
mediaType,
|
|
335
|
+
});
|
|
336
|
+
newMqa.videoTransmit[1].streams.push(videoSenderStream);
|
|
337
|
+
|
|
338
|
+
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
339
|
+
} else if (mediaType.includes('video-recv')) {
|
|
340
|
+
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
341
|
+
|
|
342
|
+
getVideoReceiverStreamMqa({
|
|
343
|
+
videoReceiverStream,
|
|
344
|
+
statsResults: this.statsResults,
|
|
345
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
346
|
+
mediaType,
|
|
347
|
+
});
|
|
348
|
+
newMqa.videoReceive[0].streams.push(videoReceiverStream);
|
|
349
|
+
|
|
350
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
351
|
+
} else if (mediaType.includes('video-share-recv')) {
|
|
352
|
+
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
353
|
+
|
|
354
|
+
getVideoReceiverStreamMqa({
|
|
355
|
+
videoReceiverStream,
|
|
356
|
+
statsResults: this.statsResults,
|
|
357
|
+
lastMqaDataSent: this.lastMqaDataSent,
|
|
358
|
+
mediaType,
|
|
359
|
+
});
|
|
360
|
+
newMqa.videoReceive[1].streams.push(videoReceiverStream);
|
|
361
|
+
|
|
362
|
+
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
186
363
|
}
|
|
187
364
|
});
|
|
188
365
|
|
|
189
|
-
newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress
|
|
366
|
+
newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress;
|
|
190
367
|
|
|
191
368
|
// Adding peripheral information
|
|
192
|
-
newMqa.intervalMetadata.peripherals = [];
|
|
193
|
-
|
|
194
369
|
newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});
|
|
195
370
|
if (this.statsResults['audio-send']) {
|
|
196
371
|
newMqa.intervalMetadata.peripherals.push({
|
|
197
|
-
information: this.statsResults['audio-send']
|
|
372
|
+
information: this.statsResults['audio-send'].trackLabel || _UNKNOWN_,
|
|
198
373
|
name: MEDIA_DEVICES.MICROPHONE,
|
|
199
374
|
});
|
|
200
375
|
}
|
|
201
376
|
if (this.statsResults['video-send']) {
|
|
202
377
|
newMqa.intervalMetadata.peripherals.push({
|
|
203
|
-
information: this.statsResults['video-send']
|
|
378
|
+
information: this.statsResults['video-send'].trackLabel || _UNKNOWN_,
|
|
204
379
|
name: MEDIA_DEVICES.CAMERA,
|
|
205
380
|
});
|
|
206
381
|
}
|
|
@@ -239,6 +414,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
239
414
|
this.mediaConnection = mediaConnection;
|
|
240
415
|
}
|
|
241
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Returns the local IP address for diagnostics.
|
|
419
|
+
* this is the local IP of the interface used for the current media connection
|
|
420
|
+
* a host can have many local Ip Addresses
|
|
421
|
+
* @returns {string | undefined} The local IP address.
|
|
422
|
+
*/
|
|
423
|
+
getLocalIpAddress(): string {
|
|
424
|
+
return this.localIpAddress;
|
|
425
|
+
}
|
|
426
|
+
|
|
242
427
|
/**
|
|
243
428
|
* Starts the stats analyzer on interval
|
|
244
429
|
*
|
|
@@ -260,7 +445,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
260
445
|
this.sendMqaData();
|
|
261
446
|
this.mqaInterval = setInterval(() => {
|
|
262
447
|
this.sendMqaData();
|
|
263
|
-
},
|
|
448
|
+
}, MQA_INTERVAL);
|
|
264
449
|
});
|
|
265
450
|
}
|
|
266
451
|
|
|
@@ -293,7 +478,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
293
478
|
this.mediaConnection = null;
|
|
294
479
|
});
|
|
295
480
|
}
|
|
296
|
-
this.mediaConnection = null;
|
|
297
481
|
|
|
298
482
|
return Promise.resolve();
|
|
299
483
|
}
|
|
@@ -324,26 +508,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
324
508
|
this.statsResults[type].recv = cloneDeep(emptyReceiver);
|
|
325
509
|
}
|
|
326
510
|
|
|
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
511
|
switch (getStatsResult.type) {
|
|
348
512
|
case 'outbound-rtp':
|
|
349
513
|
this.processOutboundRTPResult(getStatsResult, type);
|
|
@@ -351,13 +515,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
351
515
|
case 'inbound-rtp':
|
|
352
516
|
this.processInboundRTPResult(getStatsResult, type);
|
|
353
517
|
break;
|
|
354
|
-
case 'track':
|
|
355
|
-
this.processTrackResult(getStatsResult, type);
|
|
356
|
-
break;
|
|
357
518
|
case 'remote-inbound-rtp':
|
|
358
519
|
case 'remote-outbound-rtp':
|
|
359
|
-
|
|
360
|
-
this.compareSentAndReceived(getStatsResult, type, isSender);
|
|
520
|
+
this.compareSentAndReceived(getStatsResult, type);
|
|
361
521
|
break;
|
|
362
522
|
case 'remotecandidate':
|
|
363
523
|
case 'remote-candidate':
|
|
@@ -367,7 +527,6 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
367
527
|
this.parseCandidate(getStatsResult, type, isSender, false);
|
|
368
528
|
break;
|
|
369
529
|
case 'media-source':
|
|
370
|
-
// @ts-ignore
|
|
371
530
|
this.parseAudioSource(getStatsResult, type);
|
|
372
531
|
break;
|
|
373
532
|
default:
|
|
@@ -386,6 +545,13 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
386
545
|
filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {
|
|
387
546
|
const {types} = DEFAULT_GET_STATS_FILTER;
|
|
388
547
|
|
|
548
|
+
// get the successful candidate pair before parsing stats.
|
|
549
|
+
statsItem.report.forEach((report) => {
|
|
550
|
+
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
|
|
551
|
+
this.successfulCandidatePair = report;
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
389
555
|
statsItem.report.forEach((result) => {
|
|
390
556
|
if (types.includes(result.type)) {
|
|
391
557
|
this.parseGetStatsResult(result, type, isSender);
|
|
@@ -396,6 +562,12 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
396
562
|
this.statsResults[type].direction = statsItem.currentDirection;
|
|
397
563
|
this.statsResults[type].trackLabel = statsItem.localTrackLabel;
|
|
398
564
|
this.statsResults[type].csi = statsItem.csi;
|
|
565
|
+
this.extractAndSetLocalIpAddressInfoForDiagnostics(
|
|
566
|
+
this.successfulCandidatePair?.localCandidateId,
|
|
567
|
+
this.statsResults?.candidates
|
|
568
|
+
);
|
|
569
|
+
// reset the successful candidate pair.
|
|
570
|
+
this.successfulCandidatePair = {};
|
|
399
571
|
}
|
|
400
572
|
}
|
|
401
573
|
|
|
@@ -499,43 +671,35 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
499
671
|
.filter((key) => key.startsWith(keyPrefix))
|
|
500
672
|
.reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);
|
|
501
673
|
|
|
502
|
-
|
|
503
|
-
|
|
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
|
-
Object.keys(this.statsResults)
|
|
509
|
-
.filter((key) => key.startsWith(keyPrefix))
|
|
510
|
-
.reduce(
|
|
511
|
-
(prev, cur) => prev + (this.lastStatsResults.resolutions[cur]?.recv[value] || 0),
|
|
512
|
-
0
|
|
513
|
-
);
|
|
514
|
-
|
|
515
|
-
if (this.meetingMediaStatus.expected.sendAudio && this.lastStatsResults['audio-send']) {
|
|
674
|
+
// Audio Transmit
|
|
675
|
+
if (this.lastStatsResults['audio-send']) {
|
|
516
676
|
// compare audio stats sent
|
|
517
677
|
// NOTE: relies on there being only one sender.
|
|
518
678
|
const currentStats = this.statsResults['audio-send'].send;
|
|
519
679
|
const previousStats = this.lastStatsResults['audio-send'].send;
|
|
520
680
|
|
|
521
681
|
if (
|
|
522
|
-
|
|
682
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
683
|
+
currentStats.totalPacketsSent === previousStats.totalPacketsSent) ||
|
|
523
684
|
currentStats.totalPacketsSent === 0
|
|
524
685
|
) {
|
|
525
686
|
LoggerProxy.logger.info(
|
|
526
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent
|
|
687
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,
|
|
688
|
+
currentStats.totalPacketsSent
|
|
527
689
|
);
|
|
528
690
|
} else {
|
|
529
691
|
if (
|
|
530
|
-
|
|
692
|
+
(this.meetingMediaStatus.expected.sendAudio &&
|
|
693
|
+
currentStats.totalAudioEnergy === previousStats.totalAudioEnergy) ||
|
|
531
694
|
currentStats.totalAudioEnergy === 0
|
|
532
695
|
) {
|
|
533
696
|
LoggerProxy.logger.info(
|
|
534
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present
|
|
697
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,
|
|
698
|
+
currentStats.totalAudioEnergy
|
|
535
699
|
);
|
|
536
700
|
}
|
|
537
701
|
|
|
538
|
-
if (currentStats.audioLevel === 0) {
|
|
702
|
+
if (this.meetingMediaStatus.expected.sendAudio && currentStats.audioLevel === 0) {
|
|
539
703
|
LoggerProxy.logger.info(
|
|
540
704
|
`StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`
|
|
541
705
|
);
|
|
@@ -550,64 +714,59 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
550
714
|
);
|
|
551
715
|
}
|
|
552
716
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
'audio-recv',
|
|
563
|
-
'totalSamplesReceived'
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
567
|
-
LoggerProxy.logger.info(
|
|
568
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`
|
|
569
|
-
);
|
|
570
|
-
} else if (
|
|
571
|
-
currentSamplesReceived === previousSamplesReceived ||
|
|
572
|
-
currentSamplesReceived === 0
|
|
573
|
-
) {
|
|
574
|
-
LoggerProxy.logger.info(
|
|
575
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`
|
|
576
|
-
);
|
|
577
|
-
}
|
|
717
|
+
// Audio Receive
|
|
718
|
+
const currentAudioPacketsReceived = getCurrentStatsTotals(
|
|
719
|
+
'audio-recv',
|
|
720
|
+
'totalPacketsReceived'
|
|
721
|
+
);
|
|
722
|
+
const previousAudioPacketsReceived = getPreviousStatsTotals(
|
|
723
|
+
'audio-recv',
|
|
724
|
+
'totalPacketsReceived'
|
|
725
|
+
);
|
|
578
726
|
|
|
579
|
-
|
|
580
|
-
|
|
727
|
+
this.emitStartStopEvents(
|
|
728
|
+
'audio',
|
|
729
|
+
previousAudioPacketsReceived,
|
|
730
|
+
currentAudioPacketsReceived,
|
|
731
|
+
false
|
|
732
|
+
);
|
|
581
733
|
|
|
582
|
-
|
|
734
|
+
// Video Transmit
|
|
735
|
+
if (this.lastStatsResults['video-send']) {
|
|
583
736
|
// compare video stats sent
|
|
584
737
|
const currentStats = this.statsResults['video-send'].send;
|
|
585
738
|
const previousStats = this.lastStatsResults['video-send'].send;
|
|
586
739
|
|
|
587
740
|
if (
|
|
588
|
-
|
|
589
|
-
currentStats.totalPacketsSent ===
|
|
741
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
742
|
+
(currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
|
|
743
|
+
currentStats.totalPacketsSent === 0)
|
|
590
744
|
) {
|
|
591
745
|
LoggerProxy.logger.info(
|
|
592
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent
|
|
746
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
|
|
747
|
+
currentStats.totalPacketsSent
|
|
593
748
|
);
|
|
594
749
|
} else {
|
|
595
750
|
if (
|
|
596
|
-
|
|
597
|
-
currentStats.framesEncoded ===
|
|
751
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
752
|
+
(currentStats.framesEncoded === previousStats.framesEncoded ||
|
|
753
|
+
currentStats.framesEncoded === 0)
|
|
598
754
|
) {
|
|
599
755
|
LoggerProxy.logger.info(
|
|
600
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded
|
|
756
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
|
|
757
|
+
currentStats.framesEncoded
|
|
601
758
|
);
|
|
602
759
|
}
|
|
603
760
|
|
|
604
761
|
if (
|
|
605
|
-
this.
|
|
606
|
-
|
|
607
|
-
|
|
762
|
+
this.meetingMediaStatus.expected.sendVideo &&
|
|
763
|
+
(this.statsResults['video-send'].send.framesSent ===
|
|
764
|
+
this.lastStatsResults['video-send'].send.framesSent ||
|
|
765
|
+
this.statsResults['video-send'].send.framesSent === 0)
|
|
608
766
|
) {
|
|
609
767
|
LoggerProxy.logger.info(
|
|
610
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent
|
|
768
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
|
|
769
|
+
this.statsResults['video-send'].send.framesSent
|
|
611
770
|
);
|
|
612
771
|
}
|
|
613
772
|
}
|
|
@@ -615,152 +774,74 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
615
774
|
this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);
|
|
616
775
|
}
|
|
617
776
|
|
|
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
|
-
);
|
|
643
|
-
|
|
644
|
-
if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
|
|
645
|
-
LoggerProxy.logger.info(
|
|
646
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`
|
|
647
|
-
);
|
|
648
|
-
} else {
|
|
649
|
-
if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
|
|
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
|
-
}
|
|
777
|
+
// Video Receive
|
|
778
|
+
const currentVideoFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
|
|
779
|
+
const previousVideoFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
|
|
660
780
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);
|
|
669
|
-
}
|
|
781
|
+
this.emitStartStopEvents(
|
|
782
|
+
'video',
|
|
783
|
+
previousVideoFramesDecoded,
|
|
784
|
+
currentVideoFramesDecoded,
|
|
785
|
+
false
|
|
786
|
+
);
|
|
670
787
|
|
|
671
|
-
|
|
788
|
+
// Share Transmit
|
|
789
|
+
if (this.lastStatsResults['video-share-send']) {
|
|
672
790
|
// compare share stats sent
|
|
673
791
|
|
|
674
792
|
const currentStats = this.statsResults['video-share-send'].send;
|
|
675
793
|
const previousStats = this.lastStatsResults['video-share-send'].send;
|
|
676
794
|
|
|
677
795
|
if (
|
|
678
|
-
|
|
679
|
-
currentStats.totalPacketsSent ===
|
|
796
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
797
|
+
(currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
|
|
798
|
+
currentStats.totalPacketsSent === 0)
|
|
680
799
|
) {
|
|
681
800
|
LoggerProxy.logger.info(
|
|
682
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent
|
|
801
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,
|
|
802
|
+
currentStats.totalPacketsSent
|
|
683
803
|
);
|
|
684
804
|
} else {
|
|
685
805
|
if (
|
|
686
|
-
|
|
687
|
-
currentStats.framesEncoded ===
|
|
806
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
807
|
+
(currentStats.framesEncoded === previousStats.framesEncoded ||
|
|
808
|
+
currentStats.framesEncoded === 0)
|
|
688
809
|
) {
|
|
689
810
|
LoggerProxy.logger.info(
|
|
690
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded
|
|
811
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,
|
|
812
|
+
currentStats.framesEncoded
|
|
691
813
|
);
|
|
692
814
|
}
|
|
693
815
|
|
|
694
816
|
if (
|
|
695
|
-
this.
|
|
696
|
-
|
|
697
|
-
|
|
817
|
+
this.meetingMediaStatus.expected.sendShare &&
|
|
818
|
+
(this.statsResults['video-share-send'].send.framesSent ===
|
|
819
|
+
this.lastStatsResults['video-share-send'].send.framesSent ||
|
|
820
|
+
this.statsResults['video-share-send'].send.framesSent === 0)
|
|
698
821
|
) {
|
|
699
822
|
LoggerProxy.logger.info(
|
|
700
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent
|
|
823
|
+
`StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,
|
|
824
|
+
this.statsResults['video-share-send'].send.framesSent
|
|
701
825
|
);
|
|
702
826
|
}
|
|
703
827
|
}
|
|
704
|
-
}
|
|
705
828
|
|
|
706
|
-
|
|
707
|
-
|
|
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
|
-
|
|
747
|
-
if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
|
|
748
|
-
LoggerProxy.logger.info(
|
|
749
|
-
`StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
829
|
+
this.emitStartStopEvents('share', previousStats.framesSent, currentStats.framesSent, true);
|
|
830
|
+
}
|
|
752
831
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
832
|
+
// Share receive
|
|
833
|
+
const currentShareFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
|
|
834
|
+
const previousShareFramesDecoded = getPreviousStatsTotals(
|
|
835
|
+
'video-share-recv',
|
|
836
|
+
'framesDecoded'
|
|
837
|
+
);
|
|
759
838
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
839
|
+
this.emitStartStopEvents(
|
|
840
|
+
'share',
|
|
841
|
+
previousShareFramesDecoded,
|
|
842
|
+
currentShareFramesDecoded,
|
|
843
|
+
false
|
|
844
|
+
);
|
|
764
845
|
}
|
|
765
846
|
}
|
|
766
847
|
|
|
@@ -851,43 +932,20 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
851
932
|
const sendrecvType = STATS.SEND_DIRECTION;
|
|
852
933
|
|
|
853
934
|
if (result.bytesSent) {
|
|
854
|
-
|
|
935
|
+
const kilobytes = 0;
|
|
855
936
|
|
|
856
|
-
if (
|
|
857
|
-
this.statsResults
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
this.statsResults
|
|
861
|
-
}
|
|
862
|
-
if (!this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded) {
|
|
863
|
-
this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded =
|
|
864
|
-
result.keyFramesEncoded;
|
|
937
|
+
if (result.frameWidth && result.frameHeight) {
|
|
938
|
+
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
939
|
+
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
940
|
+
this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;
|
|
941
|
+
this.statsResults[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;
|
|
865
942
|
}
|
|
866
943
|
|
|
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
|
-
|
|
874
944
|
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
945
|
|
|
888
|
-
this.statsResults[mediaType][sendrecvType].
|
|
889
|
-
|
|
890
|
-
this.statsResults
|
|
946
|
+
this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;
|
|
947
|
+
this.statsResults[mediaType][sendrecvType].keyFramesEncoded = result.keyFramesEncoded;
|
|
948
|
+
this.statsResults[mediaType][sendrecvType].packetsSent = result.packetsSent;
|
|
891
949
|
|
|
892
950
|
// Data saved to send MQA metrics
|
|
893
951
|
|
|
@@ -925,75 +983,92 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
925
983
|
|
|
926
984
|
if (result.bytesReceived) {
|
|
927
985
|
let kilobytes = 0;
|
|
986
|
+
const receiveSlot = this.receiveSlotCallback(result.ssrc);
|
|
987
|
+
const sourceState = receiveSlot?.sourceState;
|
|
988
|
+
const idAndCsi = receiveSlot
|
|
989
|
+
? `id: "${receiveSlot.id || ''}"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`
|
|
990
|
+
: '';
|
|
928
991
|
|
|
929
|
-
|
|
930
|
-
this.statsResults
|
|
931
|
-
result.bytesReceived;
|
|
932
|
-
}
|
|
992
|
+
const bytes =
|
|
993
|
+
result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;
|
|
933
994
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
995
|
+
kilobytes = bytes / 1024;
|
|
996
|
+
this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
|
937
997
|
|
|
938
|
-
|
|
939
|
-
this.statsResults
|
|
998
|
+
let currentPacketsLost =
|
|
999
|
+
result.packetsLost - this.statsResults[mediaType][sendrecvType].totalPacketsLost;
|
|
1000
|
+
if (currentPacketsLost < 0) {
|
|
1001
|
+
currentPacketsLost = 0;
|
|
940
1002
|
}
|
|
1003
|
+
const packetsReceivedDiff =
|
|
1004
|
+
result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;
|
|
1005
|
+
this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;
|
|
941
1006
|
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
1007
|
+
if (packetsReceivedDiff === 0) {
|
|
1008
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1009
|
+
LoggerProxy.logger.info(
|
|
1010
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total packets received on slot: `,
|
|
1011
|
+
result.packetsReceived
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
945
1014
|
}
|
|
946
1015
|
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
result.
|
|
950
|
-
}
|
|
1016
|
+
if (mediaType.startsWith('video') || mediaType.startsWith('share')) {
|
|
1017
|
+
const videoFramesReceivedDiff =
|
|
1018
|
+
result.framesReceived - this.statsResults[mediaType][sendrecvType].framesReceived;
|
|
951
1019
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1020
|
+
if (videoFramesReceivedDiff === 0) {
|
|
1021
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1022
|
+
LoggerProxy.logger.info(
|
|
1023
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames received on slot: `,
|
|
1024
|
+
result.framesReceived
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
955
1028
|
|
|
956
|
-
|
|
1029
|
+
const videoFramesDecodedDiff =
|
|
1030
|
+
result.framesDecoded - this.statsResults[mediaType][sendrecvType].framesDecoded;
|
|
957
1031
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (this.statsResults[mediaType][sendrecvType].currentPacketsLost < 0) {
|
|
967
|
-
this.statsResults[mediaType][sendrecvType].currentPacketsLost = 0;
|
|
968
|
-
}
|
|
1032
|
+
if (videoFramesDecodedDiff === 0) {
|
|
1033
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1034
|
+
LoggerProxy.logger.info(
|
|
1035
|
+
`StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames decoded on slot: `,
|
|
1036
|
+
result.framesDecoded
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
969
1040
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived;
|
|
973
|
-
this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived =
|
|
974
|
-
result.packetsReceived;
|
|
1041
|
+
const videoFramesDroppedDiff =
|
|
1042
|
+
result.framesDropped - this.statsResults[mediaType][sendrecvType].framesDropped;
|
|
975
1043
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1044
|
+
if (videoFramesDroppedDiff > 10) {
|
|
1045
|
+
if (receiveSlot && sourceState === 'live') {
|
|
1046
|
+
LoggerProxy.logger.info(
|
|
1047
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Frames dropped for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames dropped on slot: `,
|
|
1048
|
+
result.framesDropped
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
981
1052
|
}
|
|
982
1053
|
|
|
983
1054
|
// Check the over all packet Lost ratio
|
|
984
1055
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =
|
|
985
|
-
|
|
986
|
-
?
|
|
987
|
-
(this.statsResults[mediaType][sendrecvType].packetsReceived +
|
|
988
|
-
this.statsResults[mediaType][sendrecvType].currentPacketsLost)
|
|
1056
|
+
currentPacketsLost > 0
|
|
1057
|
+
? currentPacketsLost / (packetsReceivedDiff + currentPacketsLost)
|
|
989
1058
|
: 0;
|
|
990
1059
|
if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {
|
|
991
1060
|
LoggerProxy.logger.info(
|
|
992
|
-
|
|
1061
|
+
`StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,
|
|
993
1062
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio
|
|
994
1063
|
);
|
|
995
1064
|
}
|
|
996
1065
|
|
|
1066
|
+
if (result.frameWidth && result.frameHeight) {
|
|
1067
|
+
this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
|
|
1068
|
+
this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
|
|
1069
|
+
this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
997
1072
|
// TODO: check the packet loss value is negative values here
|
|
998
1073
|
|
|
999
1074
|
if (result.packetsLost) {
|
|
@@ -1011,6 +1086,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1011
1086
|
this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;
|
|
1012
1087
|
this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
|
|
1013
1088
|
this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;
|
|
1089
|
+
this.statsResults[mediaType][sendrecvType].framesDropped = result.framesDropped;
|
|
1014
1090
|
|
|
1015
1091
|
this.statsResults[mediaType][sendrecvType].decoderImplementation =
|
|
1016
1092
|
result.decoderImplementation;
|
|
@@ -1035,6 +1111,48 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1035
1111
|
}
|
|
1036
1112
|
}
|
|
1037
1113
|
|
|
1114
|
+
/**
|
|
1115
|
+
* extracts the local Ip address from the statsResult object by looking at stats results candidates
|
|
1116
|
+
* and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
|
|
1117
|
+
* and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
|
|
1118
|
+
* 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
|
|
1119
|
+
* for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
|
|
1120
|
+
* @private
|
|
1121
|
+
* @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
|
|
1122
|
+
* @param {Object} candidates - the stats result candidates
|
|
1123
|
+
* @returns {void}
|
|
1124
|
+
*/
|
|
1125
|
+
extractAndSetLocalIpAddressInfoForDiagnostics = (
|
|
1126
|
+
successfulCandidatePairId: string,
|
|
1127
|
+
candidates: {[key: string]: Record<string, unknown>}
|
|
1128
|
+
) => {
|
|
1129
|
+
let newIpAddress = '';
|
|
1130
|
+
if (successfulCandidatePairId && !isEmpty(candidates)) {
|
|
1131
|
+
const localCandidate = candidates[successfulCandidatePairId];
|
|
1132
|
+
if (localCandidate) {
|
|
1133
|
+
if (localCandidate.candidateType === 'host') {
|
|
1134
|
+
// if it's a host candidate, use the address property - it will be the local IP
|
|
1135
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1136
|
+
} else if (localCandidate.candidateType === 'prflx') {
|
|
1137
|
+
// if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)
|
|
1138
|
+
// then look at the relatedAddress - it will be the local
|
|
1139
|
+
//
|
|
1140
|
+
// Firefox doesn't populate the relayProtocol property
|
|
1141
|
+
if (!localCandidate.relayProtocol) {
|
|
1142
|
+
newIpAddress = `${localCandidate.relatedAddress}`;
|
|
1143
|
+
} else {
|
|
1144
|
+
// if it's a peer reflexive candidate and we are using a relay -
|
|
1145
|
+
// in that case the relatedAddress will be the IP of the TURN server (Linus),
|
|
1146
|
+
// so we can only look at the address, but it might be local IP or public IP,
|
|
1147
|
+
// depending on if the user is behind a NAT or not
|
|
1148
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
this.localIpAddress = newIpAddress;
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1038
1156
|
/**
|
|
1039
1157
|
* Processes remote and local candidate result and stores
|
|
1040
1158
|
* @private
|
|
@@ -1049,125 +1167,53 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1049
1167
|
if (!result || !result.id) {
|
|
1050
1168
|
return;
|
|
1051
1169
|
}
|
|
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
1170
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
if (!RemoteIpAddress[result.id]) {
|
|
1071
|
-
RemoteIpAddress[result.id] = [];
|
|
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);
|
|
1171
|
+
// We only care about the successful local candidate
|
|
1172
|
+
if (this.successfulCandidatePair?.localCandidateId !== result.id) {
|
|
1173
|
+
return;
|
|
1082
1174
|
}
|
|
1083
1175
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1176
|
+
let transport;
|
|
1177
|
+
if (result.relayProtocol) {
|
|
1178
|
+
transport = result.relayProtocol.toUpperCase();
|
|
1179
|
+
} else if (result.protocol) {
|
|
1180
|
+
transport = result.protocol.toUpperCase();
|
|
1086
1181
|
}
|
|
1087
1182
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
RemoteIpAddress[result.id].indexOf(`${result.ip}:${result.portNumber}`) === -1
|
|
1091
|
-
) {
|
|
1092
|
-
RemoteIpAddress[result.id].push(`${result.ip}`); // TODO: Add ports
|
|
1093
|
-
}
|
|
1183
|
+
const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;
|
|
1184
|
+
const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;
|
|
1094
1185
|
|
|
1095
|
-
if (
|
|
1096
|
-
|
|
1186
|
+
if (!this.statsResults.candidates) {
|
|
1187
|
+
this.statsResults.candidates = {};
|
|
1097
1188
|
}
|
|
1098
1189
|
|
|
1099
|
-
this.statsResults.
|
|
1100
|
-
candidateType:
|
|
1101
|
-
ipAddress:
|
|
1190
|
+
this.statsResults.candidates[result.id] = {
|
|
1191
|
+
candidateType: result.candidateType,
|
|
1192
|
+
ipAddress: result.ip, // TODO: add ports
|
|
1193
|
+
relatedAddress: result.relatedAddress,
|
|
1194
|
+
relatedPort: result.relatedPort,
|
|
1195
|
+
relayProtocol: result.relayProtocol,
|
|
1196
|
+
protocol: result.protocol,
|
|
1197
|
+
address: result.address,
|
|
1102
1198
|
portNumber: result.port,
|
|
1103
|
-
networkType:
|
|
1199
|
+
networkType: result.networkType,
|
|
1104
1200
|
priority: result.priority,
|
|
1105
|
-
transport
|
|
1201
|
+
transport,
|
|
1106
1202
|
timestamp: result.time,
|
|
1107
1203
|
id: result.id,
|
|
1108
1204
|
type: result.type,
|
|
1109
1205
|
};
|
|
1110
1206
|
|
|
1111
|
-
this.statsResults.connectionType[ipType].candidateType =
|
|
1112
|
-
this.statsResults.connectionType[ipType].ipAddress =
|
|
1207
|
+
this.statsResults.connectionType[ipType].candidateType = result.candidateType;
|
|
1208
|
+
this.statsResults.connectionType[ipType].ipAddress = result.ipAddress;
|
|
1113
1209
|
|
|
1114
1210
|
this.statsResults.connectionType[ipType].networkType =
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
: RemoteNetworkType[result.id][0];
|
|
1118
|
-
this.statsResults.connectionType[ipType].transport = RemoteTransport[result.id];
|
|
1211
|
+
result.networkType === NETWORK_TYPE.VPN ? NETWORK_TYPE.UNKNOWN : result.networkType;
|
|
1212
|
+
this.statsResults.connectionType[ipType].transport = transport;
|
|
1119
1213
|
|
|
1120
1214
|
this.statsResults[type][sendRecvType].totalRoundTripTime = result.totalRoundTripTime;
|
|
1121
1215
|
};
|
|
1122
1216
|
|
|
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
1217
|
/**
|
|
1172
1218
|
*
|
|
1173
1219
|
* @private
|
|
@@ -1178,20 +1224,15 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1178
1224
|
*/
|
|
1179
1225
|
compareSentAndReceived(result, type) {
|
|
1180
1226
|
// Don't compare on transceivers without a sender.
|
|
1181
|
-
if (!type || !this.statsResults
|
|
1227
|
+
if (!type || !this.statsResults[type].send) {
|
|
1182
1228
|
return;
|
|
1183
1229
|
}
|
|
1184
1230
|
|
|
1185
1231
|
const mediaType = type;
|
|
1186
1232
|
|
|
1187
|
-
if (!this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver) {
|
|
1188
|
-
this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
1233
|
const currentPacketLoss =
|
|
1192
|
-
result.packetsLost - this.statsResults
|
|
1234
|
+
result.packetsLost - this.statsResults[mediaType].send.totalPacketsLostOnReceiver;
|
|
1193
1235
|
|
|
1194
|
-
this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1195
1236
|
this.statsResults[mediaType].send.packetsLostOnReceiver = currentPacketLoss;
|
|
1196
1237
|
this.statsResults[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
|
|
1197
1238
|
|