@webex/plugin-meetings 3.0.0 → 3.1.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +8 -4
- package/dist/constants.js.map +1 -1
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +16 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/mediaSharesUtils.js +15 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/selfUtils.js +5 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +163 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -0
- package/dist/media/index.js +4 -1
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -24
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +893 -677
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +37 -25
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +32 -23
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/util.js +304 -267
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +334 -295
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +20 -0
- package/dist/meetings/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +16 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +179 -65
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +22 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +29 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +18 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +12 -10
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.js +19 -0
- package/dist/reachability/util.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +15 -0
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +3 -3
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +307 -126
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +53 -30
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/{config.d.ts → types/config.d.ts} +1 -0
- package/dist/{constants.d.ts → types/constants.d.ts} +5 -4
- package/dist/types/index.d.ts +19 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +61 -0
- package/dist/{meeting → types/meeting}/index.d.ts +26 -7
- package/dist/{meeting → types/meeting}/muteState.d.ts +2 -8
- package/dist/{meeting → types/meeting}/request.d.ts +3 -0
- package/dist/{meeting-info → types/meeting-info}/index.d.ts +1 -1
- package/dist/{meeting-info → types/meeting-info}/meeting-info-v2.d.ts +1 -1
- package/dist/types/meeting-info/util.d.ts +49 -0
- package/dist/types/meeting-info/utilv2.d.ts +65 -0
- package/dist/{meetings → types/meetings}/index.d.ts +8 -0
- package/dist/{multistream → types/multistream}/mediaRequestManager.d.ts +2 -1
- package/dist/{multistream → types/multistream}/remoteMediaGroup.d.ts +2 -0
- package/dist/{multistream → types/multistream}/remoteMediaManager.d.ts +15 -0
- package/dist/{multistream → types/multistream}/sendSlotManager.d.ts +9 -1
- package/dist/{reachability → types/reachability}/clusterReachability.d.ts +1 -0
- package/dist/{reachability → types/reachability}/index.d.ts +4 -0
- package/dist/{reachability → types/reachability}/util.d.ts +7 -0
- package/dist/{roap → types/roap}/index.d.ts +10 -2
- package/dist/{roap → types/roap}/turnDiscovery.d.ts +64 -17
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/config.ts +1 -0
- package/src/constants.ts +7 -3
- package/src/index.ts +31 -0
- package/src/interpretation/index.ts +18 -1
- package/src/locus-info/mediaSharesUtils.ts +16 -0
- package/src/locus-info/selfUtils.ts +5 -0
- package/src/media/MediaConnectionAwaiter.ts +174 -0
- package/src/media/index.ts +3 -1
- package/src/media/properties.ts +6 -31
- package/src/meeting/index.ts +321 -106
- package/src/meeting/muteState.ts +34 -20
- package/src/meeting/request.ts +18 -2
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/util.ts +241 -233
- package/src/meeting-info/utilv2.ts +250 -243
- package/src/meetings/index.ts +18 -0
- package/src/multistream/mediaRequestManager.ts +4 -1
- package/src/multistream/remoteMediaGroup.ts +19 -0
- package/src/multistream/remoteMediaManager.ts +101 -16
- package/src/multistream/sendSlotManager.ts +28 -0
- package/src/reachability/clusterReachability.ts +20 -5
- package/src/reachability/index.ts +24 -1
- package/src/reachability/request.ts +15 -11
- package/src/reachability/util.ts +21 -0
- package/src/reconnection-manager/index.ts +1 -1
- package/src/roap/index.ts +25 -3
- package/src/roap/request.ts +3 -3
- package/src/roap/turnDiscovery.ts +244 -78
- package/src/statsAnalyzer/index.ts +63 -27
- package/test/integration/spec/journey.js +14 -14
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/interpretation/index.ts +39 -3
- package/test/unit/spec/locus-info/index.js +28 -19
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
- package/test/unit/spec/locus-info/selfUtils.js +42 -12
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
- package/test/unit/spec/media/index.ts +89 -78
- package/test/unit/spec/media/properties.ts +16 -70
- package/test/unit/spec/meeting/index.js +638 -139
- package/test/unit/spec/meeting/muteState.js +219 -67
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +6 -1
- package/test/unit/spec/meeting-info/utilv2.js +6 -0
- package/test/unit/spec/meetings/index.js +40 -20
- package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
- package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
- package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
- package/test/unit/spec/reachability/clusterReachability.ts +86 -22
- package/test/unit/spec/reachability/index.ts +197 -60
- package/test/unit/spec/reachability/request.js +15 -7
- package/test/unit/spec/reachability/util.ts +32 -2
- package/test/unit/spec/reconnection-manager/index.js +28 -0
- package/test/unit/spec/roap/index.ts +61 -6
- package/test/unit/spec/roap/turnDiscovery.ts +298 -16
- package/test/unit/spec/stats-analyzer/index.js +179 -0
- package/dist/index.d.ts +0 -7
- package/dist/meeting-info/util.d.ts +0 -2
- package/dist/meeting-info/utilv2.d.ts +0 -2
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -17
- package/dist/member/member.types.js.map +0 -1
- package/src/member/member.types.ts +0 -13
- /package/dist/{annotation → types/annotation}/annotation.types.d.ts +0 -0
- /package/dist/{annotation → types/annotation}/constants.d.ts +0 -0
- /package/dist/{annotation → types/annotation}/index.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/breakout.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/collection.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/edit-lock-error.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/events.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/index.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/request.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/utils.d.ts +0 -0
- /package/dist/{common → types/common}/browser-detection.d.ts +0 -0
- /package/dist/{common → types/common}/collection.d.ts +0 -0
- /package/dist/{common → types/common}/config.d.ts +0 -0
- /package/dist/{common → types/common}/errors/captcha-error.d.ts +0 -0
- /package/dist/{common → types/common}/errors/intent-to-join.d.ts +0 -0
- /package/dist/{common → types/common}/errors/join-meeting.d.ts +0 -0
- /package/dist/{common → types/common}/errors/media.d.ts +0 -0
- /package/dist/{common → types/common}/errors/no-meeting-info.d.ts +0 -0
- /package/dist/{common → types/common}/errors/parameter.d.ts +0 -0
- /package/dist/{common → types/common}/errors/password-error.d.ts +0 -0
- /package/dist/{common → types/common}/errors/permission.d.ts +0 -0
- /package/dist/{common → types/common}/errors/reclaim-host-role-errors.d.ts +0 -0
- /package/dist/{common → types/common}/errors/reconnection-in-progress.d.ts +0 -0
- /package/dist/{common → types/common}/errors/reconnection.d.ts +0 -0
- /package/dist/{common → types/common}/errors/stats.d.ts +0 -0
- /package/dist/{common → types/common}/errors/webex-errors.d.ts +0 -0
- /package/dist/{common → types/common}/errors/webex-meetings-error.d.ts +0 -0
- /package/dist/{common → types/common}/events/events-scope.d.ts +0 -0
- /package/dist/{common → types/common}/events/events.d.ts +0 -0
- /package/dist/{common → types/common}/events/trigger-proxy.d.ts +0 -0
- /package/dist/{common → types/common}/events/util.d.ts +0 -0
- /package/dist/{common → types/common}/logs/logger-config.d.ts +0 -0
- /package/dist/{common → types/common}/logs/logger-proxy.d.ts +0 -0
- /package/dist/{common → types/common}/logs/request.d.ts +0 -0
- /package/dist/{common → types/common}/queue.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/constants.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/enums.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/index.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/types.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/util.d.ts +0 -0
- /package/dist/{interceptors → types/interceptors}/index.d.ts +0 -0
- /package/dist/{interceptors → types/interceptors}/locusRetry.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/collection.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/index.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/siLanguage.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/controlsUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/embeddedAppsUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/fullState.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/hostUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/index.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/infoUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/mediaSharesUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/parser.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/selfUtils.d.ts +0 -0
- /package/dist/{media → types/media}/index.d.ts +0 -0
- /package/dist/{media → types/media}/properties.d.ts +0 -0
- /package/dist/{media → types/media}/util.d.ts +0 -0
- /package/dist/{mediaQualityMetrics → types/mediaQualityMetrics}/config.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/in-meeting-actions.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/locusMediaRequest.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/request.type.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/state.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/util.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/voicea-meeting.d.ts +0 -0
- /package/dist/{meeting-info → types/meeting-info}/collection.d.ts +0 -0
- /package/dist/{meeting-info → types/meeting-info}/request.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/collection.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/meetings.types.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/request.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/util.d.ts +0 -0
- /package/dist/{member → types/member}/index.d.ts +0 -0
- /package/dist/{member → types/member}/types.d.ts +0 -0
- /package/dist/{member → types/member}/util.d.ts +0 -0
- /package/dist/{members → types/members}/collection.d.ts +0 -0
- /package/dist/{members → types/members}/index.d.ts +0 -0
- /package/dist/{members → types/members}/request.d.ts +0 -0
- /package/dist/{members → types/members}/types.d.ts +0 -0
- /package/dist/{members → types/members}/util.d.ts +0 -0
- /package/dist/{metrics → types/metrics}/constants.d.ts +0 -0
- /package/dist/{metrics → types/metrics}/index.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/receiveSlot.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/receiveSlotManager.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/remoteMedia.d.ts +0 -0
- /package/dist/{networkQualityMonitor → types/networkQualityMonitor}/index.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/index.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/request.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/util.d.ts +0 -0
- /package/dist/{reachability → types/reachability}/request.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/constants.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/reactions.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/reactions.type.d.ts +0 -0
- /package/dist/{reconnection-manager → types/reconnection-manager}/index.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/enums.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/index.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/util.d.ts +0 -0
- /package/dist/{roap → types/roap}/request.d.ts +0 -0
- /package/dist/{rtcMetrics → types/rtcMetrics}/constants.d.ts +0 -0
- /package/dist/{rtcMetrics → types/rtcMetrics}/index.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/global.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/index.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/mqaUtil.d.ts +0 -0
- /package/dist/{transcription → types/transcription}/index.d.ts +0 -0
- /package/dist/{webinar → types/webinar}/collection.d.ts +0 -0
- /package/dist/{webinar → types/webinar}/index.d.ts +0 -0
- /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
|
@@ -4,7 +4,7 @@ import {Defer} from '@webex/common';
|
|
|
4
4
|
import Metrics from '../metrics';
|
|
5
5
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
6
6
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
7
|
-
import {ROAP} from '../constants';
|
|
7
|
+
import {ROAP, Enum} from '../constants';
|
|
8
8
|
|
|
9
9
|
import RoapRequest from './request';
|
|
10
10
|
import Meeting from '../meeting';
|
|
@@ -18,6 +18,28 @@ const TURN_DISCOVERY_TIMEOUT = 10; // in seconds
|
|
|
18
18
|
// and do the SDP offer with seq=1
|
|
19
19
|
const TURN_DISCOVERY_SEQ = 0;
|
|
20
20
|
|
|
21
|
+
const TurnDiscoverySkipReason = {
|
|
22
|
+
missingHttpResponse: 'missing http response', // when we asked for the TURN discovery response to be in the http response, but it wasn't there
|
|
23
|
+
reachability: 'reachability', // when udp reachability to public clusters is ok, so we don't need TURN (this doens't apply when joinWithMedia() is used)
|
|
24
|
+
alreadyInProgress: 'already in progress', // when we try to start TURN discovery while it's already in progress
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type TurnDiscoverySkipReason =
|
|
28
|
+
| Enum<typeof TurnDiscoverySkipReason> // this is a kind of FYI, because in practice typescript will infer the type of TurnDiscoverySkipReason as a string
|
|
29
|
+
| string // used in case of errors, contains the error message
|
|
30
|
+
| undefined; // used when TURN discovery is not skipped
|
|
31
|
+
|
|
32
|
+
export type TurnServerInfo = {
|
|
33
|
+
url: string;
|
|
34
|
+
username: string;
|
|
35
|
+
password: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type TurnDiscoveryResult = {
|
|
39
|
+
turnServerInfo?: TurnServerInfo;
|
|
40
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
41
|
+
};
|
|
42
|
+
|
|
21
43
|
/**
|
|
22
44
|
* Handles the process of finding out TURN server information from Linus.
|
|
23
45
|
* This is achieved by sending a TURN_DISCOVERY_REQUEST.
|
|
@@ -27,11 +49,7 @@ export default class TurnDiscovery {
|
|
|
27
49
|
|
|
28
50
|
private defer?: Defer; // used for waiting for the response
|
|
29
51
|
|
|
30
|
-
private turnInfo:
|
|
31
|
-
url: string;
|
|
32
|
-
username: string;
|
|
33
|
-
password: string;
|
|
34
|
-
};
|
|
52
|
+
private turnInfo: TurnServerInfo;
|
|
35
53
|
|
|
36
54
|
private responseTimer?: ReturnType<typeof setTimeout>;
|
|
37
55
|
|
|
@@ -85,7 +103,8 @@ export default class TurnDiscovery {
|
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
/**
|
|
88
|
-
*
|
|
106
|
+
* Handles TURN_DISCOVERY_RESPONSE roap message. Use it if the roap message comes over the websocket,
|
|
107
|
+
* otherwise use handleTurnDiscoveryHttpResponse() if it comes in the http response.
|
|
89
108
|
*
|
|
90
109
|
* @param {Object} roapMessage
|
|
91
110
|
* @param {string} from string to indicate how we got the response (used just for logging)
|
|
@@ -158,18 +177,191 @@ export default class TurnDiscovery {
|
|
|
158
177
|
}
|
|
159
178
|
|
|
160
179
|
/**
|
|
161
|
-
*
|
|
180
|
+
* Generates TURN_DISCOVERY_REQUEST roap message. When this method returns a roapMessage, it means that a TURN discovery process has started.
|
|
181
|
+
* It needs be ended by calling handleTurnDiscoveryHttpResponse() once you get a response from the backend. If you don't get any response
|
|
182
|
+
* or want to abort, you need to call abort().
|
|
162
183
|
*
|
|
163
|
-
* @param {
|
|
164
|
-
* @
|
|
184
|
+
* @param {Meeting} meeting
|
|
185
|
+
* @param {boolean} isForced
|
|
186
|
+
* @returns {Object}
|
|
187
|
+
*/
|
|
188
|
+
public async generateTurnDiscoveryRequestMessage(
|
|
189
|
+
meeting: Meeting,
|
|
190
|
+
isForced: boolean
|
|
191
|
+
): Promise<{roapMessage?: object; turnDiscoverySkippedReason: TurnDiscoverySkipReason}> {
|
|
192
|
+
if (this.defer) {
|
|
193
|
+
LoggerProxy.logger.warn(
|
|
194
|
+
'Roap:turnDiscovery#generateTurnDiscoveryRequestMessage --> TURN discovery already in progress'
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
roapMessage: undefined,
|
|
199
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.alreadyInProgress,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
204
|
+
|
|
205
|
+
if (!isForced) {
|
|
206
|
+
turnDiscoverySkippedReason = await this.getSkipReason(meeting);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (turnDiscoverySkippedReason) {
|
|
210
|
+
return {roapMessage: undefined, turnDiscoverySkippedReason};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.defer = new Defer();
|
|
214
|
+
|
|
215
|
+
const roapMessage = {
|
|
216
|
+
messageType: ROAP.ROAP_TYPES.TURN_DISCOVERY_REQUEST,
|
|
217
|
+
version: ROAP.ROAP_VERSION,
|
|
218
|
+
seq: TURN_DISCOVERY_SEQ,
|
|
219
|
+
headers: ['includeAnswerInHttpResponse', 'noOkInTransaction'],
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
LoggerProxy.logger.info(
|
|
223
|
+
'Roap:turnDiscovery#generateTurnDiscoveryRequestMessage --> generated TURN_DISCOVERY_REQUEST message'
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return {roapMessage, turnDiscoverySkippedReason: undefined};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Handles any errors that occur during TURN discovery without re-throwing them.
|
|
231
|
+
*
|
|
232
|
+
* @param {Meeting} meeting
|
|
233
|
+
* @param {Error} error
|
|
234
|
+
* @returns {TurnDiscoveryResult}
|
|
235
|
+
*/
|
|
236
|
+
private handleTurnDiscoveryFailure(meeting: Meeting, error: Error): TurnDiscoveryResult {
|
|
237
|
+
// we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN
|
|
238
|
+
LoggerProxy.logger.info(
|
|
239
|
+
`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${error}`
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE, {
|
|
243
|
+
correlation_id: meeting.correlationId,
|
|
244
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
245
|
+
reason: error.message,
|
|
246
|
+
stack: error.stack,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {turnServerInfo: undefined, turnDiscoverySkippedReason: `failure: ${error.message}`};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handles TURN_DISCOVERY_RESPONSE roap message that came in http response. If the response is not valid,
|
|
254
|
+
* it returns an object with turnServerInfo set to undefined. In that case you need to call abort()
|
|
255
|
+
* to end the TURN discovery process.
|
|
256
|
+
*
|
|
257
|
+
* @param {Meeting} meeting
|
|
258
|
+
* @param {Object|undefined} httpResponse can be undefined to indicate that we didn't get the response
|
|
259
|
+
* @returns {Promise<TurnDiscoveryResult>}
|
|
165
260
|
* @memberof Roap
|
|
166
261
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
262
|
+
public async handleTurnDiscoveryHttpResponse(
|
|
263
|
+
meeting: Meeting,
|
|
264
|
+
httpResponse?: object
|
|
265
|
+
): Promise<TurnDiscoveryResult> {
|
|
266
|
+
if (!this.defer) {
|
|
267
|
+
LoggerProxy.logger.warn(
|
|
268
|
+
'Roap:turnDiscovery#handleTurnDiscoveryHttpResponse --> unexpected http response, TURN discovery is not in progress'
|
|
269
|
+
);
|
|
171
270
|
|
|
172
|
-
|
|
271
|
+
throw new Error(
|
|
272
|
+
'handleTurnDiscoveryHttpResponse() called before generateTurnDiscoveryRequestMessage()'
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (httpResponse === undefined) {
|
|
277
|
+
return {
|
|
278
|
+
turnServerInfo: undefined,
|
|
279
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.missingHttpResponse,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const roapMessage = this.parseHttpTurnDiscoveryResponse(meeting, httpResponse);
|
|
285
|
+
|
|
286
|
+
if (!roapMessage) {
|
|
287
|
+
return {
|
|
288
|
+
turnServerInfo: undefined,
|
|
289
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.missingHttpResponse,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.handleTurnDiscoveryResponse(roapMessage, 'in http response');
|
|
294
|
+
|
|
295
|
+
const {isOkRequired} = await this.defer.promise;
|
|
296
|
+
|
|
297
|
+
if (isOkRequired) {
|
|
298
|
+
await this.sendRoapOK(meeting);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.defer = undefined;
|
|
302
|
+
|
|
303
|
+
LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');
|
|
304
|
+
|
|
305
|
+
return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.abort();
|
|
308
|
+
|
|
309
|
+
return this.handleTurnDiscoveryFailure(meeting, error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Aborts current TURN discovery. This method needs to be called if you called generateTurnDiscoveryRequestMessage(),
|
|
315
|
+
* but then never got any response from the server.
|
|
316
|
+
* @returns {void}
|
|
317
|
+
*/
|
|
318
|
+
public abort() {
|
|
319
|
+
if (this.defer) {
|
|
320
|
+
this.defer.reject(new Error('TURN discovery aborted'));
|
|
321
|
+
this.defer = undefined;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Parses the TURN_DISCOVERY_RESPONSE roap message out of the http response
|
|
327
|
+
* and returns it.
|
|
328
|
+
*
|
|
329
|
+
* @param {Meeting} meeting
|
|
330
|
+
* @param {any} httpResponse
|
|
331
|
+
* @returns {any}
|
|
332
|
+
*/
|
|
333
|
+
private parseHttpTurnDiscoveryResponse(
|
|
334
|
+
meeting: Meeting,
|
|
335
|
+
httpResponse: {mediaConnections?: Array<{remoteSdp?: string}>}
|
|
336
|
+
) {
|
|
337
|
+
let turnDiscoveryResponse;
|
|
338
|
+
|
|
339
|
+
if (httpResponse.mediaConnections?.[0]?.remoteSdp) {
|
|
340
|
+
const remoteSdp = JSON.parse(httpResponse.mediaConnections[0].remoteSdp);
|
|
341
|
+
|
|
342
|
+
if (remoteSdp.roapMessage) {
|
|
343
|
+
// yes, it's misleading that remoteSdp actually contains a TURN discovery response, but that's how the backend works...
|
|
344
|
+
const {seq, messageType, errorType, errorCause, headers} = remoteSdp.roapMessage;
|
|
345
|
+
|
|
346
|
+
turnDiscoveryResponse = {
|
|
347
|
+
seq,
|
|
348
|
+
messageType,
|
|
349
|
+
errorType,
|
|
350
|
+
errorCause,
|
|
351
|
+
headers,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!turnDiscoveryResponse) {
|
|
357
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING, {
|
|
358
|
+
correlationId: meeting.correlationId,
|
|
359
|
+
messageType: 'TURN_DISCOVERY_RESPONSE',
|
|
360
|
+
isMultistream: meeting.isMultistream,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return turnDiscoveryResponse;
|
|
173
365
|
}
|
|
174
366
|
|
|
175
367
|
/**
|
|
@@ -181,13 +373,19 @@ export default class TurnDiscovery {
|
|
|
181
373
|
* @private
|
|
182
374
|
* @memberof Roap
|
|
183
375
|
*/
|
|
184
|
-
sendRoapTurnDiscoveryRequest(
|
|
376
|
+
private sendRoapTurnDiscoveryRequest(
|
|
377
|
+
meeting: Meeting,
|
|
378
|
+
isReconnecting: boolean
|
|
379
|
+
): Promise<TurnDiscoveryResult> {
|
|
185
380
|
if (this.defer) {
|
|
186
381
|
LoggerProxy.logger.warn(
|
|
187
382
|
'Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> already in progress'
|
|
188
383
|
);
|
|
189
384
|
|
|
190
|
-
return Promise.resolve(
|
|
385
|
+
return Promise.resolve({
|
|
386
|
+
turnServerInfo: undefined,
|
|
387
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.alreadyInProgress,
|
|
388
|
+
});
|
|
191
389
|
}
|
|
192
390
|
|
|
193
391
|
this.defer = new Defer();
|
|
@@ -215,41 +413,14 @@ export default class TurnDiscovery {
|
|
|
215
413
|
// @ts-ignore - because of meeting.webex
|
|
216
414
|
ipVersion: MeetingUtil.getIpVersion(meeting.webex),
|
|
217
415
|
})
|
|
218
|
-
.then((response) => {
|
|
416
|
+
.then(async (response) => {
|
|
219
417
|
const {mediaConnections} = response;
|
|
220
418
|
|
|
221
|
-
let turnDiscoveryResponse;
|
|
222
|
-
|
|
223
419
|
if (mediaConnections) {
|
|
224
420
|
meeting.updateMediaConnections(mediaConnections);
|
|
225
|
-
|
|
226
|
-
if (mediaConnections[0]?.remoteSdp) {
|
|
227
|
-
const remoteSdp = JSON.parse(mediaConnections[0].remoteSdp);
|
|
228
|
-
|
|
229
|
-
if (remoteSdp.roapMessage) {
|
|
230
|
-
// yes, it's misleading that remoteSdp actually contains a TURN discovery response, but that's how the backend works...
|
|
231
|
-
const {seq, messageType, errorType, errorCause, headers} = remoteSdp.roapMessage;
|
|
232
|
-
|
|
233
|
-
turnDiscoveryResponse = {
|
|
234
|
-
seq,
|
|
235
|
-
messageType,
|
|
236
|
-
errorType,
|
|
237
|
-
errorCause,
|
|
238
|
-
headers,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
421
|
}
|
|
243
422
|
|
|
244
|
-
|
|
245
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING, {
|
|
246
|
-
correlationId: meeting.correlationId,
|
|
247
|
-
messageType: 'TURN_DISCOVERY_RESPONSE',
|
|
248
|
-
isMultistream: meeting.isMultistream,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return turnDiscoveryResponse;
|
|
423
|
+
return this.handleTurnDiscoveryHttpResponse(meeting, response);
|
|
253
424
|
});
|
|
254
425
|
}
|
|
255
426
|
|
|
@@ -261,7 +432,14 @@ export default class TurnDiscovery {
|
|
|
261
432
|
* @returns {Promise}
|
|
262
433
|
*/
|
|
263
434
|
sendRoapOK(meeting: Meeting) {
|
|
264
|
-
LoggerProxy.logger.info(
|
|
435
|
+
LoggerProxy.logger.info(
|
|
436
|
+
'Roap:turnDiscovery#sendRoapOK --> TURN discovery response requires OK, sending it...'
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_REQUIRES_OK, {
|
|
440
|
+
correlation_id: meeting.correlationId,
|
|
441
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
442
|
+
});
|
|
265
443
|
|
|
266
444
|
return this.roapRequest.sendRoap({
|
|
267
445
|
roapMessage: {
|
|
@@ -284,7 +462,7 @@ export default class TurnDiscovery {
|
|
|
284
462
|
* @param {Meeting} meeting
|
|
285
463
|
* @returns {Promise<string>} Promise with empty string if reachability is not skipped or a reason if it is skipped
|
|
286
464
|
*/
|
|
287
|
-
private async getSkipReason(meeting: Meeting): Promise<
|
|
465
|
+
private async getSkipReason(meeting: Meeting): Promise<TurnDiscoverySkipReason> {
|
|
288
466
|
const isAnyPublicClusterReachable =
|
|
289
467
|
// @ts-ignore - fix type
|
|
290
468
|
await meeting.webex.meetings.reachability.isAnyPublicClusterReachable();
|
|
@@ -294,10 +472,10 @@ export default class TurnDiscovery {
|
|
|
294
472
|
'Roap:turnDiscovery#getSkipReason --> reachability has not failed, skipping TURN discovery'
|
|
295
473
|
);
|
|
296
474
|
|
|
297
|
-
return
|
|
475
|
+
return TurnDiscoverySkipReason.reachability;
|
|
298
476
|
}
|
|
299
477
|
|
|
300
|
-
return
|
|
478
|
+
return undefined;
|
|
301
479
|
}
|
|
302
480
|
|
|
303
481
|
/**
|
|
@@ -330,8 +508,12 @@ export default class TurnDiscovery {
|
|
|
330
508
|
* @param {Boolean} [isForced]
|
|
331
509
|
* @returns {Promise}
|
|
332
510
|
*/
|
|
333
|
-
async doTurnDiscovery(
|
|
334
|
-
|
|
511
|
+
async doTurnDiscovery(
|
|
512
|
+
meeting: Meeting,
|
|
513
|
+
isReconnecting?: boolean,
|
|
514
|
+
isForced?: boolean
|
|
515
|
+
): Promise<TurnDiscoveryResult> {
|
|
516
|
+
let turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
335
517
|
|
|
336
518
|
if (!isForced) {
|
|
337
519
|
turnDiscoverySkippedReason = await this.getSkipReason(meeting);
|
|
@@ -345,24 +527,20 @@ export default class TurnDiscovery {
|
|
|
345
527
|
}
|
|
346
528
|
|
|
347
529
|
try {
|
|
348
|
-
const
|
|
530
|
+
const turnDiscoveryResult = await this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting);
|
|
531
|
+
|
|
532
|
+
if (
|
|
533
|
+
turnDiscoveryResult.turnDiscoverySkippedReason !==
|
|
534
|
+
TurnDiscoverySkipReason.missingHttpResponse
|
|
535
|
+
) {
|
|
536
|
+
return turnDiscoveryResult;
|
|
537
|
+
}
|
|
349
538
|
|
|
350
539
|
// if we haven't got the response over http, we need to wait for it to come over the websocket via Mercury
|
|
351
|
-
const {isOkRequired} =
|
|
352
|
-
? await this.handleTurnDiscoveryResponseInHttpResponse(httpResponse)
|
|
353
|
-
: await this.waitForTurnDiscoveryResponse();
|
|
540
|
+
const {isOkRequired} = await this.waitForTurnDiscoveryResponse();
|
|
354
541
|
|
|
355
542
|
if (isOkRequired) {
|
|
356
543
|
await this.sendRoapOK(meeting);
|
|
357
|
-
|
|
358
|
-
LoggerProxy.logger.info(
|
|
359
|
-
'Roap:turnDiscovery#doTurnDiscovery --> TURN discovery response requires OK'
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_REQUIRES_OK, {
|
|
363
|
-
correlation_id: meeting.correlationId,
|
|
364
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
365
|
-
});
|
|
366
544
|
}
|
|
367
545
|
|
|
368
546
|
this.defer = undefined;
|
|
@@ -371,19 +549,7 @@ export default class TurnDiscovery {
|
|
|
371
549
|
|
|
372
550
|
return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};
|
|
373
551
|
} catch (e) {
|
|
374
|
-
|
|
375
|
-
LoggerProxy.logger.info(
|
|
376
|
-
`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${e}`
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE, {
|
|
380
|
-
correlation_id: meeting.correlationId,
|
|
381
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
382
|
-
reason: e.message,
|
|
383
|
-
stack: e.stack,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
return {turnServerInfo: undefined, turnDiscoverySkippedReason: undefined};
|
|
552
|
+
return this.handleTurnDiscoveryFailure(meeting, e);
|
|
387
553
|
}
|
|
388
554
|
}
|
|
389
555
|
}
|
|
@@ -264,7 +264,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
264
264
|
|
|
265
265
|
// Add stats for individual streams
|
|
266
266
|
Object.keys(this.statsResults).forEach((mediaType) => {
|
|
267
|
-
if (mediaType.
|
|
267
|
+
if (mediaType.startsWith('audio-send')) {
|
|
268
268
|
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
269
269
|
|
|
270
270
|
getAudioSenderStreamMqa({
|
|
@@ -276,7 +276,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
276
276
|
newMqa.audioTransmit[0].streams.push(audioSenderStream);
|
|
277
277
|
|
|
278
278
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
279
|
-
} else if (mediaType.
|
|
279
|
+
} else if (mediaType.startsWith('audio-share-send')) {
|
|
280
280
|
const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
|
|
281
281
|
|
|
282
282
|
getAudioSenderStreamMqa({
|
|
@@ -288,7 +288,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
288
288
|
newMqa.audioTransmit[1].streams.push(audioSenderStream);
|
|
289
289
|
|
|
290
290
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
291
|
-
} else if (mediaType.
|
|
291
|
+
} else if (mediaType.startsWith('audio-recv')) {
|
|
292
292
|
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
293
293
|
|
|
294
294
|
getAudioReceiverStreamMqa({
|
|
@@ -300,7 +300,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
300
300
|
newMqa.audioReceive[0].streams.push(audioReceiverStream);
|
|
301
301
|
|
|
302
302
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
303
|
-
} else if (mediaType.
|
|
303
|
+
} else if (mediaType.startsWith('audio-share-recv')) {
|
|
304
304
|
const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
|
|
305
305
|
|
|
306
306
|
getAudioReceiverStreamMqa({
|
|
@@ -312,7 +312,8 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
312
312
|
newMqa.audioReceive[1].streams.push(audioReceiverStream);
|
|
313
313
|
|
|
314
314
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
315
|
-
} else if (mediaType.
|
|
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.
|
|
316
317
|
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
317
318
|
|
|
318
319
|
getVideoSenderStreamMqa({
|
|
@@ -324,7 +325,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
324
325
|
newMqa.videoTransmit[0].streams.push(videoSenderStream);
|
|
325
326
|
|
|
326
327
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
327
|
-
} else if (mediaType.
|
|
328
|
+
} else if (mediaType.startsWith('video-share-send')) {
|
|
328
329
|
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
329
330
|
|
|
330
331
|
getVideoSenderStreamMqa({
|
|
@@ -336,7 +337,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
336
337
|
newMqa.videoTransmit[1].streams.push(videoSenderStream);
|
|
337
338
|
|
|
338
339
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
339
|
-
} else if (mediaType.
|
|
340
|
+
} else if (mediaType.startsWith('video-recv')) {
|
|
340
341
|
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
341
342
|
|
|
342
343
|
getVideoReceiverStreamMqa({
|
|
@@ -348,7 +349,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
348
349
|
newMqa.videoReceive[0].streams.push(videoReceiverStream);
|
|
349
350
|
|
|
350
351
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
351
|
-
} else if (mediaType.
|
|
352
|
+
} else if (mediaType.startsWith('video-share-recv')) {
|
|
352
353
|
const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
|
|
353
354
|
|
|
354
355
|
getVideoReceiverStreamMqa({
|
|
@@ -373,9 +374,14 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
373
374
|
name: MEDIA_DEVICES.MICROPHONE,
|
|
374
375
|
});
|
|
375
376
|
}
|
|
376
|
-
|
|
377
|
+
|
|
378
|
+
const existingVideoSender = Object.keys(this.statsResults).find((item) =>
|
|
379
|
+
item.includes('video-send')
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
if (existingVideoSender) {
|
|
377
383
|
newMqa.intervalMetadata.peripherals.push({
|
|
378
|
-
information: this.statsResults[
|
|
384
|
+
information: this.statsResults[existingVideoSender].trackLabel || _UNKNOWN_,
|
|
379
385
|
name: MEDIA_DEVICES.CAMERA,
|
|
380
386
|
});
|
|
381
387
|
}
|
|
@@ -552,9 +558,21 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
552
558
|
}
|
|
553
559
|
});
|
|
554
560
|
|
|
561
|
+
let videoSenderIndex = 0;
|
|
555
562
|
statsItem.report.forEach((result) => {
|
|
556
563
|
if (types.includes(result.type)) {
|
|
557
|
-
|
|
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
|
+
}
|
|
558
576
|
}
|
|
559
577
|
});
|
|
560
578
|
|
|
@@ -664,12 +682,23 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
664
682
|
const getCurrentStatsTotals = (keyPrefix: string, value: string): number =>
|
|
665
683
|
Object.keys(this.statsResults)
|
|
666
684
|
.filter((key) => key.startsWith(keyPrefix))
|
|
667
|
-
.reduce(
|
|
685
|
+
.reduce(
|
|
686
|
+
(prev, cur) =>
|
|
687
|
+
prev +
|
|
688
|
+
(this.statsResults[cur]?.[keyPrefix.includes('send') ? 'send' : 'recv'][value] || 0),
|
|
689
|
+
0
|
|
690
|
+
);
|
|
668
691
|
|
|
669
692
|
const getPreviousStatsTotals = (keyPrefix: string, value: string): number =>
|
|
670
693
|
Object.keys(this.statsResults)
|
|
671
694
|
.filter((key) => key.startsWith(keyPrefix))
|
|
672
|
-
.reduce(
|
|
695
|
+
.reduce(
|
|
696
|
+
(prev, cur) =>
|
|
697
|
+
prev +
|
|
698
|
+
(this.lastStatsResults[cur]?.[keyPrefix.includes('send') ? 'send' : 'recv'][value] ||
|
|
699
|
+
0),
|
|
700
|
+
0
|
|
701
|
+
);
|
|
673
702
|
|
|
674
703
|
// Audio Transmit
|
|
675
704
|
if (this.lastStatsResults['audio-send']) {
|
|
@@ -731,47 +760,54 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
731
760
|
false
|
|
732
761
|
);
|
|
733
762
|
|
|
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');
|
|
771
|
+
|
|
772
|
+
const doesVideoSendExist = Object.keys(this.lastStatsResults).some((item) =>
|
|
773
|
+
item.includes('video-send')
|
|
774
|
+
);
|
|
775
|
+
|
|
734
776
|
// Video Transmit
|
|
735
|
-
if (
|
|
777
|
+
if (doesVideoSendExist) {
|
|
736
778
|
// compare video stats sent
|
|
737
|
-
const currentStats = this.statsResults['video-send'].send;
|
|
738
|
-
const previousStats = this.lastStatsResults['video-send'].send;
|
|
739
779
|
|
|
740
780
|
if (
|
|
741
781
|
this.meetingMediaStatus.expected.sendVideo &&
|
|
742
|
-
(
|
|
743
|
-
currentStats.totalPacketsSent === 0)
|
|
782
|
+
(currentTotalPacketsSent === previousTotalPacketsSent || currentTotalPacketsSent === 0)
|
|
744
783
|
) {
|
|
745
784
|
LoggerProxy.logger.info(
|
|
746
785
|
`StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
|
|
747
|
-
|
|
786
|
+
currentTotalPacketsSent
|
|
748
787
|
);
|
|
749
788
|
} else {
|
|
750
789
|
if (
|
|
751
790
|
this.meetingMediaStatus.expected.sendVideo &&
|
|
752
|
-
(
|
|
753
|
-
currentStats.framesEncoded === 0)
|
|
791
|
+
(currentFramesEncoded === previousFramesEncoded || currentFramesEncoded === 0)
|
|
754
792
|
) {
|
|
755
793
|
LoggerProxy.logger.info(
|
|
756
794
|
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
|
|
757
|
-
|
|
795
|
+
currentFramesEncoded
|
|
758
796
|
);
|
|
759
797
|
}
|
|
760
798
|
|
|
761
799
|
if (
|
|
762
800
|
this.meetingMediaStatus.expected.sendVideo &&
|
|
763
|
-
(
|
|
764
|
-
this.lastStatsResults['video-send'].send.framesSent ||
|
|
765
|
-
this.statsResults['video-send'].send.framesSent === 0)
|
|
801
|
+
(currentFramesSent === previousFramesSent || currentFramesSent === 0)
|
|
766
802
|
) {
|
|
767
803
|
LoggerProxy.logger.info(
|
|
768
804
|
`StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
|
|
769
|
-
|
|
805
|
+
currentFramesSent
|
|
770
806
|
);
|
|
771
807
|
}
|
|
772
808
|
}
|
|
773
809
|
|
|
774
|
-
this.emitStartStopEvents('video',
|
|
810
|
+
this.emitStartStopEvents('video', previousFramesSent, currentFramesSent, true);
|
|
775
811
|
}
|
|
776
812
|
|
|
777
813
|
// Video Receive
|