@observertc/observer-js 0.42.9 → 1.0.0-5f150eb.0
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/.prettierrc +6 -6
- package/README.md +493 -63
- package/lib/ObservedCall.d.ts +48 -43
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +164 -107
- package/lib/ObservedCallEventMonitor.d.ts +107 -0
- package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
- package/lib/ObservedCallEventMonitor.js +321 -0
- package/lib/ObservedCallSummary.d.ts +18 -0
- package/lib/ObservedCallSummary.d.ts.map +1 -0
- package/lib/ObservedCertificate.d.ts +19 -0
- package/lib/ObservedCertificate.d.ts.map +1 -0
- package/lib/ObservedCertificate.js +38 -0
- package/lib/ObservedClient.d.ts +78 -145
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +661 -746
- package/lib/ObservedClientEventMonitor.d.ts +91 -0
- package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
- package/lib/ObservedClientEventMonitor.js +254 -0
- package/lib/ObservedClientSummary.d.ts +23 -0
- package/lib/ObservedClientSummary.d.ts.map +1 -0
- package/lib/ObservedClientSummary.js +2 -0
- package/lib/ObservedCodec.d.ts +22 -0
- package/lib/ObservedCodec.d.ts.map +1 -0
- package/lib/ObservedCodec.js +45 -0
- package/lib/ObservedDataChannel.d.ts +25 -69
- package/lib/ObservedDataChannel.d.ts.map +1 -1
- package/lib/ObservedDataChannel.js +69 -109
- package/lib/ObservedIceCandidate.d.ts +29 -0
- package/lib/ObservedIceCandidate.d.ts.map +1 -0
- package/lib/ObservedIceCandidate.js +59 -0
- package/lib/ObservedIceCandidatePair.d.ts +45 -0
- package/lib/ObservedIceCandidatePair.d.ts.map +1 -0
- package/lib/ObservedIceCandidatePair.js +127 -0
- package/lib/ObservedIceTransport.d.ts +36 -0
- package/lib/ObservedIceTransport.d.ts.map +1 -0
- package/lib/ObservedIceTransport.js +93 -0
- package/lib/ObservedInboundRtp.d.ts +92 -0
- package/lib/ObservedInboundRtp.d.ts.map +1 -0
- package/lib/ObservedInboundRtp.js +193 -0
- package/lib/ObservedInboundTrack.d.ts +34 -0
- package/lib/ObservedInboundTrack.d.ts.map +1 -0
- package/lib/ObservedInboundTrack.js +92 -0
- package/lib/ObservedMediaPlayout.d.ts +22 -0
- package/lib/ObservedMediaPlayout.d.ts.map +1 -0
- package/lib/ObservedMediaPlayout.js +42 -0
- package/lib/ObservedMediaSource.d.ts +28 -0
- package/lib/ObservedMediaSource.d.ts.map +1 -0
- package/lib/ObservedMediaSource.js +55 -0
- package/lib/ObservedOutboundRtp.d.ts +62 -0
- package/lib/ObservedOutboundRtp.d.ts.map +1 -0
- package/lib/ObservedOutboundRtp.js +144 -0
- package/lib/ObservedOutboundTrack.d.ts +34 -0
- package/lib/ObservedOutboundTrack.d.ts.map +1 -0
- package/lib/ObservedOutboundTrack.js +65 -0
- package/lib/ObservedPeerConnection.d.ts +154 -80
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +724 -357
- package/lib/ObservedPeerConnectionTransport.d.ts +17 -0
- package/lib/ObservedPeerConnectionTransport.d.ts.map +1 -0
- package/lib/ObservedPeerConnectionTransport.js +34 -0
- package/lib/ObservedRemoteInboundRtp.d.ts +30 -0
- package/lib/ObservedRemoteInboundRtp.d.ts.map +1 -0
- package/lib/ObservedRemoteInboundRtp.js +60 -0
- package/lib/ObservedRemoteOutboundRtp.d.ts +30 -0
- package/lib/ObservedRemoteOutboundRtp.d.ts.map +1 -0
- package/lib/ObservedRemoteOutboundRtp.js +60 -0
- package/lib/ObservedTURN.d.ts +31 -0
- package/lib/ObservedTURN.d.ts.map +1 -0
- package/lib/ObservedTURN.js +58 -0
- package/lib/ObservedTurnServer.d.ts +25 -0
- package/lib/ObservedTurnServer.d.ts.map +1 -0
- package/lib/ObservedTurnServer.js +59 -0
- package/lib/Observer.d.ts +46 -54
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +131 -226
- package/lib/ObserverEventMonitor.d.ts +141 -0
- package/lib/ObserverEventMonitor.d.ts.map +1 -0
- package/lib/ObserverEventMonitor.js +440 -0
- package/lib/ObserverSummary.d.ts +13 -0
- package/lib/ObserverSummary.d.ts.map +1 -0
- package/lib/ObserverSummary.js +2 -0
- package/lib/Reports.d.ts +85 -0
- package/lib/Reports.d.ts.map +1 -0
- package/lib/Reports.js +2 -0
- package/lib/common/Middleware.d.ts +10 -1
- package/lib/common/Middleware.d.ts.map +1 -1
- package/lib/common/Middleware.js +55 -36
- package/lib/common/logger.d.ts +8 -4
- package/lib/common/logger.d.ts.map +1 -1
- package/lib/common/logger.js +44 -68
- package/lib/common/types.d.ts +0 -21
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js +1 -0
- package/lib/common/utils.d.ts +3 -1
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +23 -3
- package/lib/detectors/Detector.d.ts +5 -0
- package/lib/detectors/Detector.d.ts.map +1 -0
- package/lib/detectors/Detector.js +3 -0
- package/lib/detectors/Detectors.d.ts +11 -0
- package/lib/detectors/Detectors.d.ts.map +1 -0
- package/lib/detectors/Detectors.js +34 -0
- package/lib/index.d.ts +27 -25
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +45 -41
- package/lib/mediasoup/ObservedMediaRouter.d.ts +10 -0
- package/lib/mediasoup/ObservedMediaRouter.d.ts.map +1 -0
- package/lib/mediasoup/ObservedMediaRouter.js +6 -0
- package/lib/monitors/TurnUsageMonitor.d.ts +0 -52
- package/lib/monitors/TurnUsageMonitor.d.ts.map +1 -1
- package/lib/monitors/TurnUsageMonitor.js +145 -120
- package/lib/schema/ClientEventTypes.d.ts +228 -0
- package/lib/schema/ClientEventTypes.d.ts.map +1 -0
- package/lib/schema/ClientEventTypes.js +41 -0
- package/lib/schema/ClientMetaTypes.d.ts +34 -0
- package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
- package/lib/schema/ClientMetaTypes.js +16 -0
- package/lib/schema/ClientSample.d.ts +1333 -0
- package/lib/schema/ClientSample.d.ts.map +1 -0
- package/lib/schema/ClientSample.js +4 -0
- package/lib/scores/CalculatedScore.d.ts +6 -0
- package/lib/scores/CalculatedScore.d.ts.map +1 -0
- package/lib/scores/CalculatedScore.js +2 -0
- package/lib/scores/DefaultCallScoreCalculator.d.ts +7 -0
- package/lib/scores/DefaultCallScoreCalculator.d.ts.map +1 -0
- package/lib/scores/DefaultCallScoreCalculator.js +21 -0
- package/lib/scores/ScoreCalculator.d.ts +4 -0
- package/lib/scores/ScoreCalculator.d.ts.map +1 -0
- package/lib/scores/ScoreCalculator.js +2 -0
- package/lib/updaters/CallUpdater.d.ts +5 -0
- package/lib/updaters/CallUpdater.d.ts.map +1 -0
- package/lib/updaters/CallUpdater.js +2 -0
- package/lib/updaters/ObserverUpdater.d.ts +5 -0
- package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
- package/lib/updaters/ObserverUpdater.js +2 -0
- package/lib/updaters/OnAllCallObserverUpdater.d.ts +15 -0
- package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAllCallObserverUpdater.js +50 -0
- package/lib/updaters/OnAllClientCallUpdater.d.ts +15 -0
- package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAllClientCallUpdater.js +53 -0
- package/lib/updaters/OnAnyCallObserverUpdater.d.ts +12 -0
- package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAnyCallObserverUpdater.js +37 -0
- package/lib/updaters/OnAnyClientCallUpdater.d.ts +12 -0
- package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAnyClientCallUpdater.js +36 -0
- package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
- package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
- package/lib/updaters/OnIntervalUpdater.js +17 -0
- package/lib/updaters/Updater.d.ts +6 -0
- package/lib/updaters/Updater.d.ts.map +1 -0
- package/lib/updaters/Updater.js +2 -0
- package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
- package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
- package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
- package/lib/utils/RemoteTrackResolver.d.ts +7 -0
- package/lib/utils/RemoteTrackResolver.d.ts.map +1 -0
- package/lib/utils/RemoteTrackResolver.js +2 -0
- package/package.json +4 -4
- package/src/ObservedCall.ts +243 -161
- package/src/ObservedCallEventMonitor.ts +402 -0
- package/src/ObservedCallSummary.ts +22 -0
- package/src/ObservedCertificate.ts +43 -0
- package/src/ObservedClient.ts +715 -1003
- package/src/ObservedClientEventMonitor.ts +309 -0
- package/src/ObservedClientSummary.ts +24 -0
- package/src/ObservedCodec.ts +49 -0
- package/src/ObservedDataChannel.ts +71 -181
- package/src/ObservedIceCandidate.ts +64 -0
- package/src/ObservedIceCandidatePair.ts +134 -0
- package/src/ObservedIceTransport.ts +99 -0
- package/src/ObservedInboundRtp.ts +205 -0
- package/src/ObservedInboundTrack.ts +105 -0
- package/src/ObservedMediaPlayout.ts +49 -0
- package/src/ObservedMediaSource.ts +60 -0
- package/src/ObservedOutboundRtp.ts +156 -0
- package/src/ObservedOutboundTrack.ts +82 -0
- package/src/ObservedPeerConnection.ts +933 -402
- package/src/ObservedPeerConnectionTransport.ts +38 -0
- package/src/ObservedRemoteInboundRtp.ts +64 -0
- package/src/ObservedRemoteOutboundRtp.ts +65 -0
- package/src/ObservedTURN.ts +86 -0
- package/src/ObservedTurnServer.ts +72 -0
- package/src/Observer.ts +183 -298
- package/src/ObserverEventMonitor.ts +561 -0
- package/src/ObserverSummary.ts +15 -0
- package/src/Reports.ts +87 -0
- package/src/common/Middleware.ts +70 -42
- package/src/common/logger.ts +60 -69
- package/src/common/types.ts +0 -42
- package/src/common/utils.ts +24 -2
- package/src/detectors/Detector.ts +6 -0
- package/src/detectors/Detectors.ts +38 -0
- package/src/index.ts +30 -91
- package/src/mediasoup/ObservedMediaRouter.ts +10 -0
- package/src/monitors/TurnUsageMonitor.ts +164 -164
- package/src/schema/ClientEventTypes.ts +280 -0
- package/src/schema/ClientMetaTypes.ts +41 -0
- package/src/schema/ClientSample.ts +1682 -0
- package/src/scores/CalculatedScore.ts +6 -0
- package/src/scores/DefaultCallScoreCalculator.ts +23 -0
- package/src/scores/ScoreCalculator.ts +3 -0
- package/src/updaters/CallUpdater.ts +5 -0
- package/src/updaters/ObserverUpdater.ts +5 -0
- package/src/updaters/OnAllCallObserverUpdater.ts +59 -0
- package/src/updaters/OnAllClientCallUpdater.ts +61 -0
- package/src/updaters/OnAnyCallObserverUpdater.ts +41 -0
- package/src/updaters/OnAnyClientCallUpdater.ts +39 -0
- package/src/updaters/OnIntervalUpdater.ts +19 -0
- package/src/updaters/Updater.ts +5 -0
- package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
- package/src/utils/RemoteTrackResolver.ts +12 -0
- package/tsconfig.json +3 -3
- package/lib/ObservedICE.d.ts +0 -64
- package/lib/ObservedICE.d.ts.map +0 -1
- package/lib/ObservedICE.js +0 -141
- package/lib/ObservedInboundAudioTrack.d.ts +0 -96
- package/lib/ObservedInboundAudioTrack.d.ts.map +0 -1
- package/lib/ObservedInboundAudioTrack.js +0 -217
- package/lib/ObservedInboundVideoTrack.d.ts +0 -101
- package/lib/ObservedInboundVideoTrack.d.ts.map +0 -1
- package/lib/ObservedInboundVideoTrack.js +0 -232
- package/lib/ObservedOutboundAudioTrack.d.ts +0 -87
- package/lib/ObservedOutboundAudioTrack.d.ts.map +0 -1
- package/lib/ObservedOutboundAudioTrack.js +0 -186
- package/lib/ObservedOutboundVideoTrack.d.ts +0 -91
- package/lib/ObservedOutboundVideoTrack.d.ts.map +0 -1
- package/lib/ObservedOutboundVideoTrack.js +0 -193
- package/lib/ObservedSfu.d.ts +0 -59
- package/lib/ObservedSfu.d.ts.map +0 -1
- package/lib/ObservedSfu.js +0 -157
- package/lib/ObservedSfuInboundRtpPad.d.ts +0 -42
- package/lib/ObservedSfuInboundRtpPad.d.ts.map +0 -1
- package/lib/ObservedSfuInboundRtpPad.js +0 -71
- package/lib/ObservedSfuOutboundRtpPad.d.ts +0 -42
- package/lib/ObservedSfuOutboundRtpPad.d.ts.map +0 -1
- package/lib/ObservedSfuOutboundRtpPad.js +0 -72
- package/lib/ObservedSfuSctpChannel.d.ts +0 -42
- package/lib/ObservedSfuSctpChannel.d.ts.map +0 -1
- package/lib/ObservedSfuSctpChannel.js +0 -57
- package/lib/ObservedSfuTransport.d.ts +0 -58
- package/lib/ObservedSfuTransport.d.ts.map +0 -1
- package/lib/ObservedSfuTransport.js +0 -120
- package/lib/RemoteTrackAssigner.d.ts +0 -14
- package/lib/RemoteTrackAssigner.d.ts.map +0 -1
- package/lib/RemoteTrackAssigner.js +0 -52
- package/lib/ReportsCollector.d.ts +0 -109
- package/lib/ReportsCollector.d.ts.map +0 -1
- package/lib/ReportsCollector.js +0 -207
- package/lib/common/CalculatedScore.d.ts +0 -80
- package/lib/common/CalculatedScore.d.ts.map +0 -1
- package/lib/common/CalculatedScore.js +0 -162
- package/lib/common/CallEventType.d.ts +0 -63
- package/lib/common/CallEventType.d.ts.map +0 -1
- package/lib/common/CallEventType.js +0 -39
- package/lib/common/CallMetaReports.d.ts +0 -42
- package/lib/common/CallMetaReports.d.ts.map +0 -1
- package/lib/common/CallMetaReports.js +0 -36
- package/lib/common/QualityScoreUtils.d.ts +0 -1
- package/lib/common/QualityScoreUtils.d.ts.map +0 -1
- package/lib/common/QualityScoreUtils.js +0 -1
- package/lib/common/TypedEventEmitter.d.ts +0 -22
- package/lib/common/TypedEventEmitter.d.ts.map +0 -1
- package/lib/common/TypedEventEmitter.js +0 -11
- package/lib/common/callEventReports.d.ts +0 -10
- package/lib/common/callEventReports.d.ts.map +0 -1
- package/lib/common/callEventReports.js +0 -112
- package/lib/monitors/CallSummary.d.ts +0 -37
- package/lib/monitors/CallSummary.d.ts.map +0 -1
- package/lib/monitors/CallSummaryMonitor.d.ts +0 -32
- package/lib/monitors/CallSummaryMonitor.d.ts.map +0 -1
- package/lib/monitors/CallSummaryMonitor.js +0 -152
- package/lib/monitors/ClientIssueMonitor.d.ts +0 -32
- package/lib/monitors/ClientIssueMonitor.d.ts.map +0 -1
- package/lib/monitors/ClientIssueMonitor.js +0 -49
- package/lib/monitors/SfuServerMonitor.d.ts +0 -79
- package/lib/monitors/SfuServerMonitor.d.ts.map +0 -1
- package/lib/monitors/SfuServerMonitor.js +0 -198
- package/src/ObservedICE.ts +0 -237
- package/src/ObservedInboundAudioTrack.ts +0 -344
- package/src/ObservedInboundVideoTrack.ts +0 -361
- package/src/ObservedOutboundAudioTrack.ts +0 -307
- package/src/ObservedOutboundVideoTrack.ts +0 -319
- package/src/ObservedSfu.ts +0 -231
- package/src/ObservedSfuInboundRtpPad.ts +0 -106
- package/src/ObservedSfuOutboundRtpPad.ts +0 -107
- package/src/ObservedSfuSctpChannel.ts +0 -103
- package/src/ObservedSfuTransport.ts +0 -185
- package/src/RemoteTrackAssigner.ts +0 -68
- package/src/ReportsCollector.ts +0 -336
- package/src/common/CalculatedScore.ts +0 -188
- package/src/common/CallEventType.ts +0 -69
- package/src/common/CallMetaReports.ts +0 -82
- package/src/common/QualityScoreUtils.ts +0 -0
- package/src/common/TypedEventEmitter.ts +0 -32
- package/src/common/callEventReports.ts +0 -178
- package/src/monitors/CallSummary.ts +0 -38
- package/src/monitors/CallSummaryMonitor.ts +0 -192
- package/src/monitors/ClientIssueMonitor.ts +0 -88
- package/src/monitors/SfuServerMonitor.ts +0 -285
- /package/lib/{monitors/CallSummary.js → ObservedCallSummary.js} +0 -0
package/src/ObservedClient.ts
CHANGED
|
@@ -1,1171 +1,883 @@
|
|
|
1
|
-
import { Browser, ClientSample, Engine, IceLocalCandidate, IceRemoteCandidate, MediaCodecStats, MediaDevice, OperationSystem, Platform } from '@observertc/sample-schemas-js';
|
|
2
|
-
import { ObservedCall } from './ObservedCall';
|
|
3
1
|
import { EventEmitter } from 'events';
|
|
4
2
|
import { ObservedPeerConnection } from './ObservedPeerConnection';
|
|
5
3
|
import { createLogger } from './common/logger';
|
|
6
|
-
import { CallMetaType, createCallMetaReport } from './common/CallMetaReports';
|
|
7
4
|
// eslint-disable-next-line camelcase
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
5
|
+
import { ClientEvent, ClientMetaData, ClientSample, PeerConnectionSample, ClientIssue, ExtensionStat } from './schema/ClientSample';
|
|
6
|
+
import * as MetaData from './schema/ClientMetaTypes';
|
|
7
|
+
import { ClientEventTypes } from './schema/ClientEventTypes';
|
|
8
|
+
import { ObservedCall } from './ObservedCall';
|
|
9
|
+
import { ClientMetaTypes } from './schema/ClientMetaTypes';
|
|
10
|
+
import { parseJsonAs } from './common/utils';
|
|
11
|
+
import { CalculatedScore } from './scores/CalculatedScore';
|
|
12
|
+
import { Detectors } from './detectors/Detectors';
|
|
13
|
+
import { ClientReport, TrackReport } from './Reports';
|
|
14
|
+
import { ObservedInboundTrack } from './ObservedInboundTrack';
|
|
15
|
+
import { ObservedOutboundTrack } from './ObservedOutboundTrack';
|
|
14
16
|
|
|
15
17
|
const logger = createLogger('ObservedClient');
|
|
16
18
|
|
|
17
|
-
export type
|
|
19
|
+
export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
18
20
|
clientId: string;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
marker?: string,
|
|
22
|
-
operationSystem?: OperationSystem,
|
|
23
|
-
engine?: Engine,
|
|
24
|
-
platform?: Platform,
|
|
25
|
-
browser?: Browser,
|
|
26
|
-
coordinates?: {
|
|
27
|
-
latitude: number;
|
|
28
|
-
longitude: number;
|
|
29
|
-
},
|
|
21
|
+
appData?: AppData;
|
|
22
|
+
closeClientIfIdleForMs?: number;
|
|
30
23
|
};
|
|
31
24
|
|
|
32
25
|
export type ObservedClientEvents = {
|
|
33
|
-
update: [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
left: []
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}],
|
|
49
|
-
connectionstatechange: [{
|
|
50
|
-
peerConnection: ObservedPeerConnection,
|
|
51
|
-
state: string,
|
|
52
|
-
}],
|
|
53
|
-
selectedcandidatepair: [{
|
|
54
|
-
peerConnection: ObservedPeerConnection,
|
|
55
|
-
localCandidate: IceLocalCandidate,
|
|
56
|
-
remoteCandidate: IceRemoteCandidate,
|
|
57
|
-
}],
|
|
58
|
-
issue: [ClientIssue],
|
|
59
|
-
usingturn: [boolean],
|
|
60
|
-
usermediaerror: [string],
|
|
61
|
-
rejoined: [{
|
|
62
|
-
lastJoined: number,
|
|
63
|
-
}],
|
|
64
|
-
score: [CalculatedScore],
|
|
26
|
+
update: [sample: ClientSample, elapsedTimeInMs: number];
|
|
27
|
+
close: [];
|
|
28
|
+
joined: [];
|
|
29
|
+
issue: [ClientIssue];
|
|
30
|
+
metaData: [ClientMetaData];
|
|
31
|
+
rejoined: [timestamp: number];
|
|
32
|
+
left: [];
|
|
33
|
+
usingturn: [boolean];
|
|
34
|
+
usermediaerror: [string];
|
|
35
|
+
extensionStats: [ExtensionStat];
|
|
36
|
+
clientEvent: [ClientEvent];
|
|
37
|
+
|
|
38
|
+
newpeerconnection: [ObservedPeerConnection];
|
|
39
|
+
|
|
40
|
+
trackreport: [TrackReport];
|
|
65
41
|
};
|
|
66
42
|
|
|
67
|
-
export declare interface ObservedClient
|
|
43
|
+
export declare interface ObservedClient {
|
|
68
44
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
69
45
|
off<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
70
46
|
once<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
71
47
|
emit<U extends keyof ObservedClientEvents>(event: U, ...args: ObservedClientEvents[U]): boolean;
|
|
72
|
-
readonly appData: AppData;
|
|
73
48
|
}
|
|
74
49
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
peerConnectionId: string;
|
|
78
|
-
timestamp: number;
|
|
79
|
-
}
|
|
50
|
+
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
51
|
+
public readonly detectors: Detectors;
|
|
80
52
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
53
|
+
public readonly clientId: string;
|
|
54
|
+
public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
|
|
55
|
+
public readonly calculatedScore: CalculatedScore = {
|
|
56
|
+
weight: 1,
|
|
57
|
+
value: undefined,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
public readonly settings: Pick<ObservedClientSettings, 'closeClientIfIdleForMs'>;
|
|
61
|
+
|
|
62
|
+
public appData: AppData;
|
|
63
|
+
public attachments?: Record<string, unknown>;
|
|
87
64
|
|
|
88
|
-
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
89
|
-
|
|
90
|
-
public readonly created = Date.now();
|
|
91
65
|
public updated = Date.now();
|
|
92
|
-
public
|
|
93
|
-
|
|
94
|
-
private readonly _peerConnections = new Map<string, ObservedPeerConnection>();
|
|
95
|
-
|
|
96
|
-
private _closed = false;
|
|
97
|
-
|
|
98
|
-
private _acceptedSamples = 0;
|
|
99
|
-
private _timeZoneOffsetInHours?: number;
|
|
66
|
+
public acceptedSamples = 0;
|
|
67
|
+
public closed = false;
|
|
100
68
|
|
|
69
|
+
public joinedAt?: number;
|
|
70
|
+
public leftAt?: number;
|
|
71
|
+
public closedAt?: number;
|
|
72
|
+
public lastSampleTimestamp?: number;
|
|
101
73
|
// the timestamp of the CLIENT_JOINED event
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
public
|
|
74
|
+
public operationSystem?: MetaData.OperationSystem;
|
|
75
|
+
public engine?: MetaData.Engine;
|
|
76
|
+
public platform?: MetaData.Platform;
|
|
77
|
+
public browser?: MetaData.Browser;
|
|
78
|
+
public mediaConstraints: string[] = [];
|
|
105
79
|
|
|
106
|
-
public score?: CalculatedScore;
|
|
107
80
|
public usingTURN = false;
|
|
81
|
+
public usingTCP = false;
|
|
108
82
|
public availableOutgoingBitrate = 0;
|
|
109
83
|
public availableIncomingBitrate = 0;
|
|
110
|
-
public totalInboundPacketsLost = 0;
|
|
111
|
-
public totalInboundPacketsReceived = 0;
|
|
112
|
-
public totalOutboundPacketsSent = 0;
|
|
113
|
-
public totalDataChannelBytesSent = 0;
|
|
114
|
-
public totalDataChannelBytesReceived = 0;
|
|
115
|
-
public totalSentBytes = 0;
|
|
116
|
-
public totalReceivedBytes = 0;
|
|
117
|
-
public totalReceivedAudioBytes = 0;
|
|
118
|
-
public totalReceivedVideoBytes = 0;
|
|
119
|
-
public totalSentAudioBytes = 0;
|
|
120
|
-
public totalSentVideoBytes = 0;
|
|
121
84
|
|
|
85
|
+
// public totalInboundPacketsLost = 0;
|
|
86
|
+
// public totalInboundPacketsReceived = 0;
|
|
87
|
+
// public totalOutboundPacketsSent = 0;
|
|
88
|
+
// public totalDataChannelBytesSent = 0;
|
|
89
|
+
// public totalDataChannelBytesReceived = 0;
|
|
90
|
+
// public totalDataChannelMessagesSent = 0;
|
|
91
|
+
// public totalDataChannelMessagesReceived = 0;
|
|
92
|
+
// public totalReceivedAudioBytes = 0;
|
|
93
|
+
// public totalReceivedVideoBytes = 0;
|
|
94
|
+
// public totalSentAudioBytes = 0;
|
|
95
|
+
// public totalSentVideoBytes = 0;
|
|
96
|
+
// public totalSentBytes = 0;
|
|
97
|
+
// public totalReceivedBytes = 0;
|
|
98
|
+
// public totalRttLt50Measurements = 0;
|
|
99
|
+
// public totalRttLt150Measurements = 0;
|
|
100
|
+
// public totalRttLt300Measurements = 0;
|
|
101
|
+
// public totalRttGtOrEq300Measurements = 0;
|
|
102
|
+
|
|
122
103
|
public deltaReceivedAudioBytes = 0;
|
|
123
104
|
public deltaReceivedVideoBytes = 0;
|
|
124
105
|
public deltaSentAudioBytes = 0;
|
|
125
106
|
public deltaSentVideoBytes = 0;
|
|
126
107
|
public deltaDataChannelBytesSent = 0;
|
|
127
108
|
public deltaDataChannelBytesReceived = 0;
|
|
109
|
+
public deltaDataChannelMessagesSent = 0;
|
|
110
|
+
public deltaDataChannelMessagesReceived = 0;
|
|
128
111
|
public deltaInboundPacketsLost = 0;
|
|
129
112
|
public deltaInboundPacketsReceived = 0;
|
|
130
113
|
public deltaOutboundPacketsSent = 0;
|
|
114
|
+
public deltaTransportSentBytes = 0;
|
|
115
|
+
public deltaTransportReceivedBytes = 0;
|
|
116
|
+
public deltaRttLt50Measurements = 0;
|
|
117
|
+
public deltaRttLt150Measurements = 0;
|
|
118
|
+
public deltaRttLt300Measurements = 0;
|
|
119
|
+
public deltaRttGtOrEq300Measurements = 0;
|
|
131
120
|
|
|
132
|
-
public
|
|
133
|
-
public
|
|
134
|
-
public
|
|
135
|
-
public inboundAudioBitrate?: number;
|
|
136
|
-
public inboundVideoBitrate?: number;
|
|
137
|
-
public mediaConstraints: string[] = [];
|
|
121
|
+
public currentMaxRttInMs?: number;
|
|
122
|
+
public currentMinRttInMs?: number;
|
|
123
|
+
public currentAvgRttInMs?: number;
|
|
138
124
|
|
|
139
|
-
public
|
|
140
|
-
public
|
|
141
|
-
public
|
|
142
|
-
public
|
|
125
|
+
public sendingAudioBitrate = 0;
|
|
126
|
+
public sendingVideoBitrate = 0;
|
|
127
|
+
public receivingAudioBitrate = 0;
|
|
128
|
+
public receivingVideoBitrate = 0;
|
|
143
129
|
|
|
144
|
-
public
|
|
145
|
-
public
|
|
146
|
-
|
|
147
|
-
public
|
|
148
|
-
|
|
149
|
-
public readonly call: ObservedCall,
|
|
150
|
-
public readonly appData: AppData
|
|
151
|
-
) {
|
|
152
|
-
super();
|
|
153
|
-
this.setMaxListeners(Infinity);
|
|
154
|
-
}
|
|
130
|
+
public numberOfInboundRtpStreams = 0;
|
|
131
|
+
public numberOfInbundTracks = 0;
|
|
132
|
+
public numberOfOutboundRtpStreams = 0;
|
|
133
|
+
public numberOfOutboundTracks = 0;
|
|
134
|
+
public numberOfDataChannels = 0;
|
|
155
135
|
|
|
156
|
-
public
|
|
157
|
-
const sfu = this.call.observer.observedSfus.get(this.sfuId ?? '');
|
|
136
|
+
public deltaNumberOfIssues = 0;
|
|
158
137
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
138
|
+
public totalScoreSum = 0;
|
|
139
|
+
public numberOfScoreMeasurements = 0;
|
|
140
|
+
// public totalNumberOfIssues = 0;
|
|
163
141
|
|
|
164
|
-
public
|
|
165
|
-
return this._joined;
|
|
166
|
-
}
|
|
142
|
+
public readonly report: ClientReport;
|
|
167
143
|
|
|
168
|
-
public
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (this._joined) {
|
|
172
|
-
this.emit('joined');
|
|
173
|
-
}
|
|
174
|
-
}
|
|
144
|
+
public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
|
|
145
|
+
public issues: ClientIssue[] = [];
|
|
175
146
|
|
|
176
|
-
|
|
177
|
-
return this._left;
|
|
178
|
-
}
|
|
147
|
+
private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
|
|
179
148
|
|
|
180
|
-
|
|
181
|
-
this._left = value;
|
|
182
|
-
|
|
183
|
-
if (this._left) {
|
|
184
|
-
this.emit('left');
|
|
185
|
-
}
|
|
186
|
-
}
|
|
149
|
+
private closeTimer?: ReturnType<typeof setTimeout>;
|
|
187
150
|
|
|
188
|
-
public
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return this.call.observer.observedSfus.get(this.sfuId);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
public get coordinates() {
|
|
195
|
-
return this._model.coordinates;
|
|
196
|
-
}
|
|
151
|
+
public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
|
|
152
|
+
super();
|
|
153
|
+
this.setMaxListeners(Infinity);
|
|
197
154
|
|
|
198
|
-
|
|
199
|
-
this.
|
|
200
|
-
|
|
155
|
+
this.clientId = settings.clientId;
|
|
156
|
+
this.appData = settings.appData ?? {} as AppData;
|
|
157
|
+
|
|
158
|
+
this.detectors = new Detectors();
|
|
159
|
+
this.settings = {
|
|
160
|
+
closeClientIfIdleForMs: settings.closeClientIfIdleForMs,
|
|
161
|
+
};
|
|
201
162
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
163
|
+
this.report = {
|
|
164
|
+
callId: this.call.callId,
|
|
165
|
+
clientId: this.clientId,
|
|
166
|
+
rttDistribution: {
|
|
167
|
+
gtOrEq300ms: 0,
|
|
168
|
+
lt50ms: 0,
|
|
169
|
+
lt150ms: 0,
|
|
170
|
+
lt300ms: 0,
|
|
171
|
+
count: 0,
|
|
172
|
+
sum: 0,
|
|
173
|
+
},
|
|
174
|
+
scoreDistribution: {
|
|
175
|
+
'0': 0,
|
|
176
|
+
'1': 0,
|
|
177
|
+
'2': 0,
|
|
178
|
+
'3': 0,
|
|
179
|
+
'4': 0,
|
|
180
|
+
'5': 0,
|
|
181
|
+
count: 0,
|
|
182
|
+
sum: 0,
|
|
183
|
+
},
|
|
184
|
+
issues: {},
|
|
185
|
+
totalAudioBytesReceived: 0,
|
|
186
|
+
totalVideoBytesReceived: 0,
|
|
187
|
+
totalAudioBytesSent: 0,
|
|
188
|
+
totalVideoBytesSent: 0,
|
|
189
|
+
totalDataChannelBytesReceived: 0,
|
|
190
|
+
totalDataChannelBytesSent: 0,
|
|
191
|
+
totalDataChannelMessagesReceived: 0,
|
|
192
|
+
totalDataChannelMessagesSent: 0,
|
|
193
|
+
totalInboundRtpBytesReceived: 0,
|
|
194
|
+
totalInboundRtpPacketsLost: 0,
|
|
195
|
+
totalInboundRtpPacketsReceived: 0,
|
|
196
|
+
totalOutboundRtpBytesSent: 0,
|
|
197
|
+
totalOutboundRtpPacketsSent: 0,
|
|
198
|
+
totalNumberOfIssues: 0,
|
|
199
|
+
};
|
|
205
200
|
}
|
|
206
|
-
|
|
207
|
-
public get
|
|
208
|
-
|
|
209
|
-
return this.call.roomId!;
|
|
201
|
+
|
|
202
|
+
public get numberOfPeerConnections() {
|
|
203
|
+
return this.observedPeerConnections.size;
|
|
210
204
|
}
|
|
211
205
|
|
|
212
|
-
public get
|
|
213
|
-
|
|
214
|
-
return this.call.serviceId!;
|
|
206
|
+
public get score() {
|
|
207
|
+
return this.calculatedScore.value;
|
|
215
208
|
}
|
|
216
209
|
|
|
217
|
-
public
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
210
|
+
public close() {
|
|
211
|
+
if (this.closed) return;
|
|
212
|
+
this.closed = true;
|
|
221
213
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
|
|
215
|
+
this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
|
|
216
|
+
this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
|
|
217
|
+
this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
|
|
226
218
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
219
|
+
Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
220
|
+
if (!this.leftAt) {
|
|
221
|
+
this.leftAt = this.lastSampleTimestamp;
|
|
230
222
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
if (this.leftAt) {
|
|
224
|
+
this.emit('left');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
this.closedAt = Date.now();
|
|
234
228
|
|
|
235
|
-
|
|
236
|
-
this._model.userId = userId;
|
|
229
|
+
this.emit('close');
|
|
237
230
|
}
|
|
238
231
|
|
|
239
|
-
public
|
|
240
|
-
|
|
241
|
-
}
|
|
232
|
+
public accept(sample: ClientSample): void {
|
|
233
|
+
if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
|
|
242
234
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
235
|
+
if (this.closeTimer) {
|
|
236
|
+
clearTimeout(this.closeTimer);
|
|
237
|
+
this.closeTimer = undefined;
|
|
238
|
+
}
|
|
246
239
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
const elapsedInMs = now - this.updated;
|
|
242
|
+
const elapsedInSeconds = elapsedInMs / 1000;
|
|
243
|
+
let sumOfRtts = 0;
|
|
244
|
+
let numberOfRttMeasurements = 0;
|
|
245
|
+
|
|
246
|
+
++this.acceptedSamples;
|
|
247
|
+
|
|
248
|
+
this.availableIncomingBitrate = 0;
|
|
249
|
+
this.availableOutgoingBitrate = 0;
|
|
250
|
+
this.deltaDataChannelBytesReceived = 0;
|
|
251
|
+
this.deltaDataChannelBytesSent = 0;
|
|
252
|
+
this.deltaDataChannelMessagesReceived = 0;
|
|
253
|
+
this.deltaDataChannelMessagesSent = 0;
|
|
254
|
+
this.deltaInboundPacketsLost = 0;
|
|
255
|
+
this.deltaInboundPacketsReceived = 0;
|
|
256
|
+
this.deltaOutboundPacketsSent = 0;
|
|
257
|
+
this.deltaReceivedAudioBytes = 0;
|
|
258
|
+
this.deltaReceivedVideoBytes = 0;
|
|
259
|
+
this.deltaSentAudioBytes = 0;
|
|
260
|
+
this.deltaSentVideoBytes = 0;
|
|
261
|
+
this.deltaTransportReceivedBytes = 0;
|
|
262
|
+
this.deltaTransportSentBytes = 0;
|
|
263
|
+
this.deltaRttLt50Measurements = 0;
|
|
264
|
+
this.deltaRttLt150Measurements = 0;
|
|
265
|
+
this.deltaRttLt300Measurements = 0;
|
|
266
|
+
this.deltaRttGtOrEq300Measurements = 0;
|
|
267
|
+
this.deltaNumberOfIssues = 0;
|
|
268
|
+
|
|
269
|
+
this.numberOfDataChannels = 0;
|
|
270
|
+
this.numberOfInboundRtpStreams = 0;
|
|
271
|
+
this.numberOfInbundTracks = 0;
|
|
272
|
+
this.numberOfOutboundRtpStreams = 0;
|
|
273
|
+
this.numberOfOutboundTracks = 0;
|
|
274
|
+
this.usingTURN = false;
|
|
275
|
+
this.usingTCP = false;
|
|
276
|
+
this.currentMinRttInMs = undefined;
|
|
277
|
+
this.currentMaxRttInMs = undefined;
|
|
250
278
|
|
|
251
|
-
|
|
252
|
-
return this._model.engine;
|
|
253
|
-
}
|
|
279
|
+
this._mergeInjections(sample);
|
|
254
280
|
|
|
255
|
-
|
|
256
|
-
return this._model.operationSystem;
|
|
257
|
-
}
|
|
281
|
+
const clientEventsPostBuffer: ClientEvent[] = [];
|
|
258
282
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
283
|
+
for (const clientEvent of sample.clientEvents ?? []) {
|
|
284
|
+
this._processClientEvent(clientEvent, clientEventsPostBuffer);
|
|
262
285
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
286
|
+
this.call.observer.emit('client-event', this, clientEvent);
|
|
287
|
+
}
|
|
266
288
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
289
|
+
for (const metaData of sample.clientMetaItems ?? []) {
|
|
290
|
+
this.addMetadata(metaData);
|
|
291
|
+
}
|
|
270
292
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
public close() {
|
|
276
|
-
if (this._closed) return;
|
|
277
|
-
this._closed = true;
|
|
293
|
+
for (const issue of sample.clientIssues ?? []) {
|
|
294
|
+
this.addIssue(issue);
|
|
278
295
|
|
|
279
|
-
|
|
296
|
+
++this.deltaNumberOfIssues;
|
|
297
|
+
}
|
|
280
298
|
|
|
281
|
-
|
|
282
|
-
|
|
299
|
+
for (const extensionStat of sample.extensionStats ?? []) {
|
|
300
|
+
this.addExtensionStats(extensionStat);
|
|
301
|
+
}
|
|
283
302
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
attachments,
|
|
287
|
-
...fields
|
|
288
|
-
} = params;
|
|
289
|
-
|
|
290
|
-
this.reports.addCallEventReport({
|
|
291
|
-
...fields,
|
|
292
|
-
serviceId: this.serviceId,
|
|
293
|
-
mediaUnitId: this.mediaUnitId,
|
|
294
|
-
roomId: this.roomId,
|
|
295
|
-
callId: this.callId,
|
|
296
|
-
clientId: this.clientId,
|
|
297
|
-
userId: this.userId,
|
|
298
|
-
timestamp: params.timestamp ?? Date.now(),
|
|
299
|
-
marker: this.marker,
|
|
300
|
-
attachments: attachments ? JSON.stringify(attachments) : undefined,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
+
for (const pcSample of sample.peerConnections ?? []) {
|
|
304
|
+
const observedPeerConnection = this._updatePeerConnection(pcSample);
|
|
303
305
|
|
|
304
|
-
|
|
305
|
-
this.reports.addClientExtensionReport({
|
|
306
|
-
serviceId: this.serviceId,
|
|
307
|
-
mediaUnitId: this.mediaUnitId,
|
|
308
|
-
roomId: this.roomId,
|
|
309
|
-
callId: this.callId,
|
|
310
|
-
clientId: this.clientId,
|
|
311
|
-
userId: this.userId,
|
|
312
|
-
timestamp: Date.now(),
|
|
313
|
-
extensionType,
|
|
314
|
-
payload: JSON.stringify(payload),
|
|
315
|
-
marker: this.marker,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
306
|
+
if (!observedPeerConnection) continue;
|
|
318
307
|
|
|
319
|
-
|
|
320
|
-
|
|
308
|
+
this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
|
|
309
|
+
this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
|
|
310
|
+
this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
|
|
311
|
+
this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
|
|
312
|
+
this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
|
|
313
|
+
this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
|
|
314
|
+
this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
|
|
315
|
+
this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
|
|
316
|
+
this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
|
|
317
|
+
this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
|
|
318
|
+
this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
|
|
319
|
+
this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
|
|
320
|
+
this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
|
|
321
321
|
|
|
322
|
-
this.
|
|
323
|
-
|
|
324
|
-
mediaUnitId: this.mediaUnitId,
|
|
325
|
-
roomId: this.roomId,
|
|
326
|
-
callId: this.callId,
|
|
327
|
-
clientId: this.clientId,
|
|
328
|
-
userId: this.userId,
|
|
329
|
-
|
|
330
|
-
name: CallEventType.CLIENT_ISSUE,
|
|
331
|
-
value: issue.severity,
|
|
332
|
-
peerConnectionId: issue.peerConnectionId,
|
|
333
|
-
mediaTrackId: issue.trackId,
|
|
334
|
-
message: issue.description,
|
|
335
|
-
timestamp: issue.timestamp ?? Date.now(),
|
|
336
|
-
attachments: issue.attachments ? JSON.stringify(issue.attachments): undefined,
|
|
337
|
-
});
|
|
338
|
-
} catch (err) {
|
|
339
|
-
logger.warn(`Error adding client issue: ${(err as Error)?.message}`);
|
|
340
|
-
}
|
|
322
|
+
this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
|
|
323
|
+
this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
|
|
341
324
|
|
|
342
|
-
|
|
343
|
-
|
|
325
|
+
this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
|
|
326
|
+
this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
|
|
327
|
+
this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
|
|
328
|
+
this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
|
|
329
|
+
this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
|
|
344
330
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
331
|
+
if (observedPeerConnection.usingTURN) {
|
|
332
|
+
this.usingTURN = true;
|
|
333
|
+
}
|
|
334
|
+
if (observedPeerConnection.usingTCP) {
|
|
335
|
+
this.usingTCP = true;
|
|
336
|
+
}
|
|
351
337
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
338
|
+
if (observedPeerConnection.currentRttInMs) {
|
|
339
|
+
if (this.currentMinRttInMs === undefined || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
|
|
340
|
+
this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
|
|
341
|
+
}
|
|
342
|
+
if (this.currentMaxRttInMs === undefined || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
|
|
343
|
+
this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
|
|
344
|
+
}
|
|
345
|
+
if (observedPeerConnection.currentRttInMs < 50) {
|
|
346
|
+
this.deltaRttLt50Measurements += 1;
|
|
347
|
+
this.report.rttDistribution.lt50ms += 1;
|
|
348
|
+
} else if (observedPeerConnection.currentRttInMs < 150) {
|
|
349
|
+
this.deltaRttLt150Measurements += 1;
|
|
350
|
+
this.report.rttDistribution.lt150ms += 1;
|
|
351
|
+
} else if (observedPeerConnection.currentRttInMs < 300) {
|
|
352
|
+
this.deltaRttLt300Measurements += 1;
|
|
353
|
+
this.report.rttDistribution.lt300ms += 1;
|
|
354
|
+
} else if (300 <= observedPeerConnection.currentRttInMs) {
|
|
355
|
+
this.deltaRttGtOrEq300Measurements += 1;
|
|
356
|
+
this.report.rttDistribution.gtOrEq300ms += 1;
|
|
357
|
+
}
|
|
358
|
+
this.report.rttDistribution.count += 1;
|
|
359
|
+
this.report.rttDistribution.sum += observedPeerConnection.currentRttInMs;
|
|
361
360
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this._model.userId = sample.userId;
|
|
361
|
+
sumOfRtts += observedPeerConnection.currentRttInMs;
|
|
362
|
+
++numberOfRttMeasurements;
|
|
365
363
|
}
|
|
366
|
-
} else if (sample.userId && sample.userId !== 'NULL') {
|
|
367
|
-
this._model.userId = sample.userId;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (this._model.marker !== sample.marker) {
|
|
371
|
-
this._model.marker = sample.marker;
|
|
372
|
-
this._peerConnections.forEach((peerConnection) => (peerConnection.marker = sample.marker));
|
|
373
364
|
}
|
|
374
365
|
|
|
375
|
-
|
|
376
|
-
this.
|
|
366
|
+
for (const clientEvent of clientEventsPostBuffer) {
|
|
367
|
+
this._processClientEvent(clientEvent);
|
|
377
368
|
}
|
|
378
369
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
this._model.operationSystem?.version !== sample.os.version ||
|
|
382
|
-
this._model.operationSystem?.versionName !== sample.os.versionName
|
|
383
|
-
)) {
|
|
384
|
-
this._model.operationSystem = sample.os;
|
|
385
|
-
|
|
386
|
-
const callMetaReport = createCallMetaReport(
|
|
387
|
-
this.serviceId,
|
|
388
|
-
this.mediaUnitId,
|
|
389
|
-
this.roomId,
|
|
390
|
-
this.callId,
|
|
391
|
-
this.clientId,
|
|
392
|
-
{
|
|
393
|
-
type: CallMetaType.OPERATION_SYSTEM,
|
|
394
|
-
payload: sample.os,
|
|
395
|
-
},
|
|
396
|
-
this.userId,
|
|
397
|
-
undefined,
|
|
398
|
-
sample.timestamp
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
402
|
-
}
|
|
370
|
+
// emit new attachments?
|
|
371
|
+
this.attachments = sample.attachments;
|
|
403
372
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
payload: sample.engine,
|
|
419
|
-
},
|
|
420
|
-
this.userId,
|
|
421
|
-
undefined,
|
|
422
|
-
sample.timestamp
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (sample.platform && (
|
|
429
|
-
this._model.platform?.model !== sample.platform.model ||
|
|
430
|
-
this._model.platform?.type !== sample.platform.type ||
|
|
431
|
-
this._model.platform?.vendor !== sample.platform.vendor
|
|
432
|
-
)) {
|
|
433
|
-
this._model.platform = sample.platform;
|
|
434
|
-
|
|
435
|
-
const callMetaReport = createCallMetaReport(
|
|
436
|
-
this.serviceId,
|
|
437
|
-
this.mediaUnitId,
|
|
438
|
-
this.roomId,
|
|
439
|
-
this.callId,
|
|
440
|
-
this.clientId,
|
|
441
|
-
{
|
|
442
|
-
type: CallMetaType.PLATFORM,
|
|
443
|
-
payload: sample.platform,
|
|
444
|
-
},
|
|
445
|
-
this.userId,
|
|
446
|
-
undefined,
|
|
447
|
-
sample.timestamp
|
|
448
|
-
);
|
|
449
|
-
|
|
450
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
451
|
-
}
|
|
373
|
+
this.report.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
|
|
374
|
+
this.report.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
|
|
375
|
+
this.report.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
|
|
376
|
+
this.report.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
|
|
377
|
+
this.report.totalInboundRtpPacketsLost += this.deltaInboundPacketsLost;
|
|
378
|
+
this.report.totalInboundRtpPacketsReceived += this.deltaInboundPacketsReceived;
|
|
379
|
+
this.report.totalOutboundRtpPacketsSent += this.deltaOutboundPacketsSent;
|
|
380
|
+
this.report.totalAudioBytesReceived += this.deltaReceivedAudioBytes;
|
|
381
|
+
this.report.totalVideoBytesReceived += this.deltaReceivedVideoBytes;
|
|
382
|
+
this.report.totalAudioBytesSent += this.deltaSentAudioBytes;
|
|
383
|
+
this.report.totalVideoBytesSent += this.deltaSentVideoBytes;
|
|
384
|
+
this.report.totalInboundRtpBytesReceived += this.deltaTransportReceivedBytes;
|
|
385
|
+
this.report.totalOutboundRtpBytesSent += this.deltaTransportSentBytes;
|
|
386
|
+
this.report.totalNumberOfIssues += this.deltaNumberOfIssues;
|
|
452
387
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
))
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const callMetaReport = createCallMetaReport(
|
|
460
|
-
this.serviceId,
|
|
461
|
-
this.mediaUnitId,
|
|
462
|
-
this.roomId,
|
|
463
|
-
this.callId,
|
|
464
|
-
this.clientId,
|
|
465
|
-
{
|
|
466
|
-
type: CallMetaType.BROWSER,
|
|
467
|
-
payload: sample.browser,
|
|
468
|
-
},
|
|
469
|
-
this.userId,
|
|
470
|
-
undefined,
|
|
471
|
-
sample.timestamp
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
475
|
-
}
|
|
388
|
+
this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
|
|
389
|
+
this.receivingVideoBitrate = (this.deltaReceivedVideoBytes * 8) / (elapsedInSeconds);
|
|
390
|
+
this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
|
|
391
|
+
this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
|
|
392
|
+
this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : undefined;
|
|
476
393
|
|
|
477
|
-
|
|
478
|
-
const callMetaReport = createCallMetaReport(
|
|
479
|
-
this.serviceId,
|
|
480
|
-
this.mediaUnitId,
|
|
481
|
-
this.roomId,
|
|
482
|
-
this.callId,
|
|
483
|
-
this.clientId,
|
|
484
|
-
{
|
|
485
|
-
type: CallMetaType.MEDIA_CONSTRAINT,
|
|
486
|
-
payload: mediaConstraint,
|
|
487
|
-
},
|
|
488
|
-
this.userId,
|
|
489
|
-
undefined,
|
|
490
|
-
sample.timestamp
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
494
|
-
this.mediaConstraints.push(mediaConstraint);
|
|
495
|
-
}
|
|
394
|
+
this.calculatedScore.value = sample.score;
|
|
496
395
|
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
this.serviceId,
|
|
500
|
-
this.mediaUnitId,
|
|
501
|
-
this.roomId,
|
|
502
|
-
this.callId,
|
|
503
|
-
this.clientId,
|
|
504
|
-
{
|
|
505
|
-
type: CallMetaType.LOCAL_SDP,
|
|
506
|
-
payload: localSDP,
|
|
507
|
-
},
|
|
508
|
-
this.userId,
|
|
509
|
-
undefined,
|
|
510
|
-
sample.timestamp
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
514
|
-
}
|
|
396
|
+
if (sample.score !== undefined) {
|
|
397
|
+
const bucket = Math.floor(sample.score).toString() as '0' | '1' | '2' | '3' | '4' | '5';
|
|
515
398
|
|
|
516
|
-
|
|
517
|
-
this.
|
|
518
|
-
|
|
519
|
-
mediaUnitId: this.mediaUnitId,
|
|
520
|
-
roomId: this.roomId,
|
|
521
|
-
callId: this.callId,
|
|
522
|
-
clientId: this.clientId,
|
|
523
|
-
userId: this.userId,
|
|
524
|
-
timestamp: now,
|
|
525
|
-
payload: extensionStats.payload,
|
|
526
|
-
extensionType: extensionStats.type,
|
|
527
|
-
});
|
|
399
|
+
this.report.scoreDistribution[bucket] = (this.report.scoreDistribution[bucket] ?? 0) + 1;
|
|
400
|
+
this.report.scoreDistribution.count += 1;
|
|
401
|
+
this.report.scoreDistribution.sum += sample.score;
|
|
528
402
|
}
|
|
403
|
+
|
|
404
|
+
this.detectors.update();
|
|
529
405
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
406
|
+
this.lastSampleTimestamp = sample.timestamp;
|
|
407
|
+
// emit update
|
|
408
|
+
this.emit('update',
|
|
409
|
+
sample,
|
|
410
|
+
now - this.updated,
|
|
411
|
+
);
|
|
412
|
+
this.updated = now;
|
|
536
413
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
414
|
+
// if result changed after update
|
|
415
|
+
if (this.calculatedScore.value) {
|
|
416
|
+
this.totalScoreSum += this.calculatedScore.value;
|
|
417
|
+
++this.numberOfScoreMeasurements;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (this.settings.closeClientIfIdleForMs !== undefined) {
|
|
421
|
+
this.closeTimer = setTimeout(() => {
|
|
422
|
+
this.close();
|
|
423
|
+
}, this.settings.closeClientIfIdleForMs);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
|
|
428
|
+
// eslint-disable-next-line no-console
|
|
429
|
+
// console.warn('ClientEvent', event);
|
|
430
|
+
switch (event.type) {
|
|
431
|
+
case ClientEventTypes.CLIENT_JOINED: {
|
|
432
|
+
if (event.timestamp) {
|
|
433
|
+
if (!this.joinedAt) {
|
|
434
|
+
this.joinedAt = event.timestamp;
|
|
435
|
+
this.emit('joined');
|
|
436
|
+
} else if (this.joinedAt < event.timestamp) {
|
|
437
|
+
this.emit('rejoined', event.timestamp);
|
|
438
|
+
} else {
|
|
439
|
+
this.joinedAt = event.timestamp;
|
|
440
|
+
logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
|
|
540
441
|
}
|
|
541
|
-
// in case we have a left event before the joined event
|
|
542
|
-
if (this.left && this.joined && this.left < this.joined) {
|
|
543
|
-
this.left = undefined;
|
|
544
|
-
}
|
|
545
|
-
break;
|
|
546
|
-
}
|
|
547
|
-
case CallEventType.CLIENT_LEFT: {
|
|
548
|
-
this.left = timestamp;
|
|
549
|
-
break;
|
|
550
442
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
443
|
+
logger.debug('Client %s joined at %o', this.clientId, event);
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
case ClientEventTypes.CLIENT_LEFT: {
|
|
447
|
+
if (event.timestamp) {
|
|
448
|
+
if (!this.leftAt) {
|
|
449
|
+
this.leftAt = event.timestamp;
|
|
450
|
+
this.emit('left');
|
|
451
|
+
} else {
|
|
452
|
+
logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
|
|
453
|
+
}
|
|
454
|
+
|
|
558
455
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
456
|
+
logger.debug('Client %s left at %o', this.clientId, event);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case ClientEventTypes.PEER_CONNECTION_OPENED: {
|
|
460
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
461
|
+
|
|
462
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
|
|
463
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
464
|
+
|
|
465
|
+
if (observedPeerConnection) {
|
|
466
|
+
observedPeerConnection.openedAt = event.timestamp;
|
|
467
|
+
} else if (postBuffer) {
|
|
468
|
+
postBuffer.push(event);
|
|
469
|
+
} else {
|
|
470
|
+
logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o', event);
|
|
471
|
+
}
|
|
472
|
+
|
|
566
473
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
case ClientEventTypes.PEER_CONNECTION_CLOSED: {
|
|
477
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
478
|
+
|
|
479
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
|
|
480
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
481
|
+
|
|
482
|
+
if (observedPeerConnection) {
|
|
483
|
+
observedPeerConnection.closedAt = event.timestamp;
|
|
484
|
+
} else if (postBuffer) {
|
|
485
|
+
postBuffer.push(event);
|
|
486
|
+
} else {
|
|
487
|
+
logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o', event);
|
|
488
|
+
}
|
|
575
489
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
case ClientEventTypes.MEDIA_TRACK_ADDED: {
|
|
493
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
494
|
+
|
|
495
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
496
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
497
|
+
const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
498
|
+
|
|
499
|
+
if (observedTrack) {
|
|
500
|
+
observedTrack.addedAt = event.timestamp;
|
|
501
|
+
} else if (postBuffer) {
|
|
502
|
+
postBuffer.push(event);
|
|
503
|
+
} else {
|
|
504
|
+
logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
505
|
+
}
|
|
584
506
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
case ClientEventTypes.MEDIA_TRACK_REMOVED: {
|
|
510
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
511
|
+
|
|
512
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
513
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
514
|
+
const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
515
|
+
|
|
516
|
+
if (observedTrack) {
|
|
517
|
+
observedTrack.removedAt = event.timestamp;
|
|
518
|
+
} else if (postBuffer) {
|
|
519
|
+
postBuffer.push(event);
|
|
520
|
+
} else {
|
|
521
|
+
logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
601
522
|
}
|
|
602
|
-
break;
|
|
603
523
|
}
|
|
524
|
+
break;
|
|
604
525
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
timestamp: timestamp ?? now,
|
|
622
|
-
...callEvent,
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
for (const userMediaError of sample.userMediaErrors ?? []) {
|
|
628
|
-
const callMetaReport = createCallMetaReport(
|
|
629
|
-
this.serviceId,
|
|
630
|
-
this.mediaUnitId,
|
|
631
|
-
this.roomId,
|
|
632
|
-
this.callId,
|
|
633
|
-
this.clientId, {
|
|
634
|
-
type: CallMetaType.USER_MEDIA_ERROR,
|
|
635
|
-
payload: userMediaError,
|
|
636
|
-
},
|
|
637
|
-
this.userId,
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
641
|
-
this.userMediaErrors.push(userMediaError);
|
|
642
|
-
this.emit('usermediaerror', userMediaError);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
for (const certificate of sample.certificates ?? []) {
|
|
646
|
-
const callMetaReport = createCallMetaReport(
|
|
647
|
-
this.serviceId,
|
|
648
|
-
this.mediaUnitId,
|
|
649
|
-
this.roomId,
|
|
650
|
-
this.callId,
|
|
651
|
-
this.clientId, {
|
|
652
|
-
type: CallMetaType.CERTIFICATE,
|
|
653
|
-
payload: certificate,
|
|
654
|
-
},
|
|
655
|
-
this.userId
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
for (const codec of sample.codecs ?? []) {
|
|
662
|
-
const callMetaReport = createCallMetaReport(
|
|
663
|
-
this.serviceId,
|
|
664
|
-
this.mediaUnitId,
|
|
665
|
-
this.roomId,
|
|
666
|
-
this.callId,
|
|
667
|
-
this.clientId, {
|
|
668
|
-
type: CallMetaType.CODEC,
|
|
669
|
-
payload: codec,
|
|
670
|
-
}, this.userId);
|
|
671
|
-
|
|
672
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
673
|
-
if (codec.mimeType && !this.mediaCodecs.find((c) => c.mimeType === codec.mimeType)) {
|
|
674
|
-
this.mediaCodecs.push(codec);
|
|
526
|
+
case ClientEventTypes.DATA_CHANNEL_OPEN: {
|
|
527
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
528
|
+
|
|
529
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
|
|
530
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
531
|
+
const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
|
|
532
|
+
|
|
533
|
+
if (observedDataChannel) {
|
|
534
|
+
observedDataChannel.addedAt = event.timestamp;
|
|
535
|
+
} else if (postBuffer) {
|
|
536
|
+
postBuffer.push(event);
|
|
537
|
+
} else {
|
|
538
|
+
logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
675
542
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
for (const mediaDevice of sample.mediaDevices ?? []) {
|
|
693
|
-
const callMetaReport = createCallMetaReport(
|
|
694
|
-
this.serviceId,
|
|
695
|
-
this.mediaUnitId,
|
|
696
|
-
this.roomId,
|
|
697
|
-
this.callId,
|
|
698
|
-
this.clientId, {
|
|
699
|
-
type: CallMetaType.MEDIA_DEVICE,
|
|
700
|
-
payload: mediaDevice,
|
|
701
|
-
}, this.userId);
|
|
702
|
-
|
|
703
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
704
|
-
if (mediaDevice.id && !this.mediaDevices.find((d) => d.id === mediaDevice.id)) {
|
|
705
|
-
this.mediaDevices.push(mediaDevice);
|
|
543
|
+
case ClientEventTypes.DATA_CHANNEL_CLOSED: {
|
|
544
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
545
|
+
|
|
546
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
|
|
547
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
548
|
+
const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
|
|
549
|
+
|
|
550
|
+
if (observedDataChannel) {
|
|
551
|
+
observedDataChannel.removedAt = event.timestamp;
|
|
552
|
+
} else if (postBuffer) {
|
|
553
|
+
postBuffer.push(event);
|
|
554
|
+
} else {
|
|
555
|
+
logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
break;
|
|
706
559
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
560
|
+
case ClientEventTypes.MEDIA_TRACK_MUTED: {
|
|
561
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
562
|
+
|
|
563
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
564
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
565
|
+
const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
|
|
566
|
+
const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
567
|
+
|
|
568
|
+
if (observedPeerConnection) {
|
|
569
|
+
if (observedInboundTrack) {
|
|
570
|
+
observedInboundTrack.muted = true;
|
|
571
|
+
observedPeerConnection?.emit('muted-inbound-track', observedInboundTrack);
|
|
572
|
+
} else if (observedOutboundTrack) {
|
|
573
|
+
observedOutboundTrack.muted = true;
|
|
574
|
+
observedPeerConnection?.emit('muted-outbound-track', observedOutboundTrack);
|
|
575
|
+
}
|
|
576
|
+
} else if (postBuffer) {
|
|
577
|
+
postBuffer.push(event);
|
|
578
|
+
} else {
|
|
579
|
+
logger.warn('Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
726
582
|
|
|
727
|
-
|
|
728
|
-
peerConnection.update(transport, sample.timestamp);
|
|
729
|
-
} catch (err) {
|
|
730
|
-
logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
|
|
583
|
+
break;
|
|
731
584
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
585
|
+
case ClientEventTypes.MEDIA_TRACK_UNMUTED: {
|
|
586
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
587
|
+
|
|
588
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
589
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
590
|
+
const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
|
|
591
|
+
const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
592
|
+
|
|
593
|
+
if (observedPeerConnection) {
|
|
594
|
+
if (observedInboundTrack) {
|
|
595
|
+
observedInboundTrack.muted = false;
|
|
596
|
+
observedPeerConnection?.emit('unmuted-inbound-track', observedInboundTrack);
|
|
597
|
+
} else if (observedOutboundTrack) {
|
|
598
|
+
observedOutboundTrack.muted = false;
|
|
599
|
+
observedPeerConnection?.emit('unmuted-outbound-track', observedOutboundTrack);
|
|
600
|
+
}
|
|
601
|
+
} else if (postBuffer) {
|
|
602
|
+
postBuffer.push(event);
|
|
603
|
+
} else {
|
|
604
|
+
logger.warn('Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
break;
|
|
740
608
|
}
|
|
609
|
+
case ClientEventTypes.ICE_CONNECTION_STATE_CHANGED: {
|
|
610
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
741
611
|
|
|
742
|
-
|
|
743
|
-
|
|
612
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceConnectionState && typeof payload.iceConnectionState === 'string') {
|
|
613
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
744
614
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
sfuStreamId: track.sfuStreamId,
|
|
750
|
-
sfuSinkId: track.sfuSinkId,
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
inboundAudioTrack.update(track, sample.timestamp);
|
|
754
|
-
|
|
755
|
-
if (!inboundAudioTrack.remoteOutboundTrack) {
|
|
756
|
-
const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(inboundAudioTrack.sfuStreamId ?? '');
|
|
757
|
-
|
|
758
|
-
if (remoteOutboundTrack) {
|
|
759
|
-
inboundAudioTrack.remoteOutboundTrack = remoteOutboundTrack;
|
|
760
|
-
|
|
761
|
-
inboundAudioTrack.once('close', () => {
|
|
762
|
-
remoteOutboundTrack?.remoteInboundTracks.delete(inboundAudioTrack.trackId ?? '');
|
|
615
|
+
if (observedPeerConnection) {
|
|
616
|
+
observedPeerConnection.iceConnectionState = payload.iceConnectionState as 'new' | 'checking' | 'connected' | 'completed' | 'failed' | 'disconnected' | 'closed';
|
|
617
|
+
observedPeerConnection.emit('iceconnectionstatechange', {
|
|
618
|
+
state: payload.iceConnectionState,
|
|
763
619
|
});
|
|
764
|
-
|
|
620
|
+
} else if (postBuffer) {
|
|
621
|
+
postBuffer.push(event);
|
|
622
|
+
} else {
|
|
623
|
+
logger.warn('Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
765
624
|
}
|
|
766
625
|
}
|
|
767
|
-
|
|
768
|
-
logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
|
|
626
|
+
break;
|
|
769
627
|
}
|
|
770
|
-
|
|
628
|
+
case ClientEventTypes.ICE_GATHERING_STATE_CHANGED: {
|
|
629
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
771
630
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
logger.warn(`InboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
631
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceGatheringState && typeof payload.iceGatheringState === 'string') {
|
|
632
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
775
633
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (track.trackId === 'probator') continue;
|
|
781
|
-
logger.warn(`InboundVideoTrack with invalid trackId: ${track.trackId}`);
|
|
782
|
-
continue;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
try {
|
|
786
|
-
const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
|
|
787
|
-
|
|
788
|
-
if (!peerConnection) continue;
|
|
789
|
-
|
|
790
|
-
const inboundVideoTrack = peerConnection.inboundVideoTracks.get(track.trackId) ?? peerConnection.createInboundVideoTrack({
|
|
791
|
-
trackId: track.trackId,
|
|
792
|
-
sfuStreamId: track.sfuStreamId,
|
|
793
|
-
sfuSinkId: track.sfuSinkId,
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
inboundVideoTrack.update(track, sample.timestamp);
|
|
797
|
-
|
|
798
|
-
if (!inboundVideoTrack.remoteOutboundTrack) {
|
|
799
|
-
const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
|
|
800
|
-
|
|
801
|
-
if (remoteOutboundTrack) {
|
|
802
|
-
inboundVideoTrack.remoteOutboundTrack = remoteOutboundTrack;
|
|
803
|
-
|
|
804
|
-
inboundVideoTrack.once('close', () => {
|
|
805
|
-
remoteOutboundTrack?.remoteInboundTracks.delete(inboundVideoTrack.trackId ?? '');
|
|
634
|
+
if (observedPeerConnection) {
|
|
635
|
+
observedPeerConnection.iceGatheringState = payload.iceGatheringState as 'new' | 'gathering' | 'complete';
|
|
636
|
+
observedPeerConnection.emit('icegatheringstatechange', {
|
|
637
|
+
state: payload.iceGatheringState,
|
|
806
638
|
});
|
|
807
|
-
|
|
639
|
+
} else if (postBuffer) {
|
|
640
|
+
postBuffer.push(event);
|
|
641
|
+
} else {
|
|
642
|
+
logger.warn('Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
808
643
|
}
|
|
809
644
|
}
|
|
810
|
-
|
|
811
|
-
logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
|
|
645
|
+
break;
|
|
812
646
|
}
|
|
813
|
-
|
|
647
|
+
case ClientEventTypes.PEER_CONNECTION_STATE_CHANGED: {
|
|
648
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
814
649
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
logger.warn(`OutboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
650
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.peerConnectionState && typeof payload.peerConnectionState === 'string') {
|
|
651
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
818
652
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
sfuStreamId: track.sfuStreamId,
|
|
830
|
-
});
|
|
831
|
-
|
|
832
|
-
outboundAudioTrack.update(track, sample.timestamp);
|
|
833
|
-
|
|
834
|
-
if (outboundAudioTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundAudioTrack.has(outboundAudioTrack.sfuStreamId)) {
|
|
835
|
-
this.call.sfuStreamIdToOutboundAudioTrack.set(outboundAudioTrack.sfuStreamId, outboundAudioTrack);
|
|
653
|
+
if (observedPeerConnection) {
|
|
654
|
+
observedPeerConnection.connectionState = payload.peerConnectionState as 'new' | 'connecting' | 'connected' | 'disconnected' | 'failed' | 'closed';
|
|
655
|
+
observedPeerConnection.emit('connectionstatechange', {
|
|
656
|
+
state: payload.peerConnectionState,
|
|
657
|
+
});
|
|
658
|
+
} else if (postBuffer) {
|
|
659
|
+
postBuffer.push(event);
|
|
660
|
+
} else {
|
|
661
|
+
logger.warn('Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
662
|
+
}
|
|
836
663
|
}
|
|
837
|
-
|
|
838
|
-
} catch (err) {
|
|
839
|
-
logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
|
|
664
|
+
break;
|
|
840
665
|
}
|
|
841
|
-
|
|
842
666
|
}
|
|
667
|
+
|
|
668
|
+
this.emit('clientEvent', event);
|
|
669
|
+
}
|
|
843
670
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
try {
|
|
852
|
-
const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
|
|
671
|
+
public injectMetaData(metaData: ClientMetaData) {
|
|
672
|
+
if (this.closed) return;
|
|
673
|
+
|
|
674
|
+
if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
|
|
853
675
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const outboundVideoTrack = peerConnection.outboundVideoTracks.get(track.trackId) ?? peerConnection.createOutboundVideoTrack({
|
|
857
|
-
trackId: track.trackId,
|
|
858
|
-
sfuStreamId: track.sfuStreamId,
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
outboundVideoTrack.update(track, sample.timestamp);
|
|
676
|
+
this._injections.clientMetaItems.push(metaData);
|
|
677
|
+
}
|
|
862
678
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
}
|
|
679
|
+
public injectEvent(event: ClientEvent) {
|
|
680
|
+
if (this.closed) return;
|
|
866
681
|
|
|
867
|
-
|
|
868
|
-
logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
682
|
+
if (!this._injections.clientEvents) this._injections.clientEvents = [];
|
|
871
683
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
logger.warn(`Local ice candidate without peerConnectionId: ${JSON.stringify(iceLocalCandidate)}`);
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
const peerConnection = this._peerConnections.get(iceLocalCandidate.peerConnectionId);
|
|
684
|
+
this._injections.clientEvents.push(event);
|
|
685
|
+
}
|
|
878
686
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
687
|
+
public injectIssue(issue: ClientIssue) {
|
|
688
|
+
if (this.closed) return;
|
|
883
689
|
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
for (const iceRemoteCandidate of sample.iceRemoteCandidates ?? []) {
|
|
888
|
-
if (!iceRemoteCandidate.peerConnectionId) {
|
|
889
|
-
logger.warn(`Remote ice candidate without peerConnectionId: ${JSON.stringify(iceRemoteCandidate)}`);
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
const peerConnection = this._peerConnections.get(iceRemoteCandidate.peerConnectionId);
|
|
690
|
+
if (!this._injections.clientIssues) this._injections.clientIssues = [];
|
|
893
691
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
692
|
+
this._injections.clientIssues.push(issue);
|
|
693
|
+
}
|
|
898
694
|
|
|
899
|
-
|
|
900
|
-
|
|
695
|
+
public injectExtensionStat(stat: ExtensionStat) {
|
|
696
|
+
if (this.closed) return;
|
|
901
697
|
|
|
902
|
-
|
|
903
|
-
const peerConnection = this._peerConnections.get(candidatePair.peerConnectionId);
|
|
698
|
+
if (!this._injections.extensionStats) this._injections.extensionStats = [];
|
|
904
699
|
|
|
905
|
-
|
|
906
|
-
|
|
700
|
+
this._injections.extensionStats.push(stat);
|
|
701
|
+
}
|
|
907
702
|
|
|
908
|
-
|
|
909
|
-
|
|
703
|
+
public injectAttachment(key: string, value: unknown) {
|
|
704
|
+
if (this.closed) return;
|
|
910
705
|
|
|
911
|
-
|
|
912
|
-
}
|
|
706
|
+
if (!this._injections.attachments) this._injections.attachments = {};
|
|
913
707
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
708
|
+
this._injections.attachments[key] = value;
|
|
709
|
+
|
|
710
|
+
}
|
|
917
711
|
|
|
918
|
-
|
|
712
|
+
public addMetadata(metadata: ClientMetaData) {
|
|
713
|
+
if (this.closed) return;
|
|
714
|
+
|
|
715
|
+
switch (metadata.type) {
|
|
716
|
+
case ClientMetaTypes.BROWSER: {
|
|
717
|
+
this.browser = parseJsonAs(metadata.payload);
|
|
718
|
+
break;
|
|
919
719
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
if (!peerConnection) continue;
|
|
925
|
-
|
|
926
|
-
const observedDataChannel = peerConnection.dataChannels.get(dataChannel.dataChannelIdentifier) ?? peerConnection.createDataChannel(dataChannel.dataChannelIdentifier);
|
|
927
|
-
|
|
928
|
-
observedDataChannel.update(dataChannel, sample.timestamp);
|
|
929
|
-
} catch (err) {
|
|
930
|
-
logger.error(`Error creating data channel: ${(err as Error)?.message}`);
|
|
720
|
+
case ClientMetaTypes.ENGINE: {
|
|
721
|
+
this.engine = parseJsonAs(metadata.payload);
|
|
722
|
+
break;
|
|
931
723
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
if (0 < this.ωpendingPeerConnectionTimestamps.length) {
|
|
936
|
-
const newPendingTimestamps: PendingPeerConnectionTimestamp[] = [];
|
|
937
|
-
|
|
938
|
-
for (const pendingTimestamp of this.ωpendingPeerConnectionTimestamps) {
|
|
939
|
-
const peerConnection = this._peerConnections.get(pendingTimestamp.peerConnectionId);
|
|
940
|
-
|
|
941
|
-
if (!peerConnection) {
|
|
942
|
-
newPendingTimestamps.push(pendingTimestamp);
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
if (pendingTimestamp.type === 'opened') peerConnection.opened = pendingTimestamp.timestamp;
|
|
946
|
-
else if (pendingTimestamp.type === 'closed') peerConnection.closedTimestamp = pendingTimestamp.timestamp;
|
|
724
|
+
case ClientMetaTypes.PLATFORM: {
|
|
725
|
+
this.platform = parseJsonAs(metadata.payload);
|
|
726
|
+
break;
|
|
947
727
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
// try to set the timestamps of the media tracks
|
|
953
|
-
if (0 < this.ωpendingMediaTrackTimestamps.length) {
|
|
954
|
-
const newPendingTimestamps: PendingMediaTrackTimestamp[] = [];
|
|
955
|
-
|
|
956
|
-
for (const pendingTimestamp of this.ωpendingMediaTrackTimestamps) {
|
|
957
|
-
const peerConnection = this._peerConnections.get(pendingTimestamp.peerConnectionId);
|
|
958
|
-
const mediaTrack = peerConnection?.inboundAudioTracks.get(pendingTimestamp.mediaTrackId) ??
|
|
959
|
-
peerConnection?.inboundVideoTracks.get(pendingTimestamp.mediaTrackId) ??
|
|
960
|
-
peerConnection?.outboundAudioTracks.get(pendingTimestamp.mediaTrackId) ??
|
|
961
|
-
peerConnection?.outboundVideoTracks.get(pendingTimestamp.mediaTrackId);
|
|
962
|
-
|
|
963
|
-
if (!mediaTrack) {
|
|
964
|
-
newPendingTimestamps.push(pendingTimestamp);
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
if (pendingTimestamp.type === 'added') mediaTrack.added = pendingTimestamp.timestamp;
|
|
969
|
-
else if (pendingTimestamp.type === 'removed') mediaTrack.removed = pendingTimestamp.timestamp;
|
|
728
|
+
case ClientMetaTypes.OPERATION_SYSTEM: {
|
|
729
|
+
this.operationSystem = parseJsonAs(metadata.payload);
|
|
730
|
+
break;
|
|
970
731
|
}
|
|
971
|
-
|
|
972
|
-
this.ωpendingMediaTrackTimestamps = newPendingTimestamps;
|
|
973
732
|
}
|
|
974
733
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
if (!peerConnection.visited) {
|
|
978
|
-
peerConnection.close();
|
|
979
|
-
|
|
980
|
-
continue;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
for (const track of peerConnection.inboundAudioTracks.values()) {
|
|
984
|
-
if (!track.visited) {
|
|
985
|
-
track.close();
|
|
734
|
+
this.call.observer.emit('client-metadata', this, metadata);
|
|
735
|
+
}
|
|
986
736
|
|
|
987
|
-
|
|
988
|
-
|
|
737
|
+
public addIssue(issue: ClientIssue) {
|
|
738
|
+
if (this.closed) return;
|
|
989
739
|
|
|
990
|
-
|
|
991
|
-
}
|
|
992
|
-
for (const track of peerConnection.inboundVideoTracks.values()) {
|
|
993
|
-
if (!track.visited) {
|
|
994
|
-
track.close();
|
|
740
|
+
this.report.issues[issue.type] = (this.report.issues[issue.type] ?? 0) + 1;
|
|
995
741
|
|
|
996
|
-
|
|
997
|
-
|
|
742
|
+
this.emit('issue', issue);
|
|
743
|
+
this.call.observer.emit('client-issue', this, issue);
|
|
744
|
+
}
|
|
998
745
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
track.close();
|
|
746
|
+
public addExtensionStats(stats: ExtensionStat) {
|
|
747
|
+
this.call.observer.emit('client-extension-stats', this, stats);
|
|
748
|
+
this.emit('extensionStats', stats);
|
|
749
|
+
}
|
|
1004
750
|
|
|
1005
|
-
|
|
1006
|
-
|
|
751
|
+
private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
|
|
752
|
+
let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
|
|
1007
753
|
|
|
1008
|
-
|
|
754
|
+
if (!observedPeerConnection) {
|
|
755
|
+
if (!sample.peerConnectionId) {
|
|
756
|
+
return (logger.warn(
|
|
757
|
+
`ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
|
|
758
|
+
sample
|
|
759
|
+
), void 0);
|
|
1009
760
|
}
|
|
1010
|
-
for (const dataChannel of peerConnection.dataChannels.values()) {
|
|
1011
|
-
if (!dataChannel.visited) {
|
|
1012
|
-
dataChannel.close();
|
|
1013
761
|
|
|
1014
|
-
|
|
1015
|
-
|
|
762
|
+
const newObservedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
|
|
763
|
+
const onInboundTrackRemoved = (track: ObservedInboundTrack) => {
|
|
764
|
+
this.emit('trackreport', {
|
|
765
|
+
direction: 'inbound',
|
|
766
|
+
...track.report,
|
|
767
|
+
});
|
|
768
|
+
};
|
|
769
|
+
const onOutboundTrackRemoved = (track: ObservedOutboundTrack) => {
|
|
770
|
+
this.emit('trackreport', {
|
|
771
|
+
direction: 'outbound',
|
|
772
|
+
...track.report,
|
|
773
|
+
});
|
|
774
|
+
};
|
|
1016
775
|
|
|
1017
|
-
|
|
1018
|
-
|
|
776
|
+
newObservedPeerConnection.once('close', () => {
|
|
777
|
+
newObservedPeerConnection?.off('removed-inbound-track', onInboundTrackRemoved);
|
|
778
|
+
newObservedPeerConnection?.off('removed-outbound-track', onOutboundTrackRemoved);
|
|
779
|
+
this.observedPeerConnections.delete(sample.peerConnectionId);
|
|
780
|
+
});
|
|
781
|
+
newObservedPeerConnection.on('removed-inbound-track', onInboundTrackRemoved);
|
|
782
|
+
newObservedPeerConnection.on('removed-outbound-track', onOutboundTrackRemoved);
|
|
783
|
+
this.observedPeerConnections.set(sample.peerConnectionId, newObservedPeerConnection);
|
|
784
|
+
|
|
785
|
+
this.emit('newpeerconnection', newObservedPeerConnection);
|
|
1019
786
|
|
|
1020
|
-
|
|
787
|
+
observedPeerConnection = newObservedPeerConnection;
|
|
1021
788
|
}
|
|
1022
789
|
|
|
1023
|
-
|
|
1024
|
-
const wasUsingTURN = this.usingTURN;
|
|
1025
|
-
const elapsedTimeInMs = now - this.updated;
|
|
1026
|
-
|
|
1027
|
-
this.usingTURN = false;
|
|
1028
|
-
this.availableIncomingBitrate = 0;
|
|
1029
|
-
this.availableOutgoingBitrate = 0;
|
|
1030
|
-
this.deltaInboundPacketsLost = 0;
|
|
1031
|
-
this.deltaInboundPacketsReceived = 0;
|
|
1032
|
-
this.deltaOutboundPacketsSent = 0;
|
|
1033
|
-
this.deltaReceivedAudioBytes = 0;
|
|
1034
|
-
this.deltaReceivedVideoBytes = 0;
|
|
1035
|
-
this.deltaSentAudioBytes = 0;
|
|
1036
|
-
this.deltaSentVideoBytes = 0;
|
|
1037
|
-
this.deltaDataChannelBytesReceived = 0;
|
|
1038
|
-
this.deltaDataChannelBytesSent = 0;
|
|
1039
|
-
|
|
1040
|
-
this.outboundAudioBitrate = 0;
|
|
1041
|
-
this.outboundVideoBitrate = 0;
|
|
1042
|
-
this.inboundAudioBitrate = 0;
|
|
1043
|
-
this.inboundVideoBitrate = 0;
|
|
1044
|
-
let sumRttInMs = 0;
|
|
1045
|
-
let anyPeerConnectionUsingTurn = false;
|
|
790
|
+
observedPeerConnection.accept(sample);
|
|
1046
791
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
let numberOfScoredPeerConnections = 0;
|
|
792
|
+
return observedPeerConnection;
|
|
793
|
+
}
|
|
1050
794
|
|
|
1051
|
-
|
|
1052
|
-
|
|
795
|
+
private _mergeInjections(sample: ClientSample): ClientSample {
|
|
796
|
+
if (this.closed) return sample;
|
|
1053
797
|
|
|
1054
|
-
|
|
798
|
+
if (this._injections.clientEvents) {
|
|
799
|
+
if (!sample.clientEvents) sample.clientEvents = [];
|
|
800
|
+
sample.clientEvents.push(...this._injections.clientEvents);
|
|
1055
801
|
|
|
1056
|
-
this.
|
|
1057
|
-
|
|
1058
|
-
this.deltaOutboundPacketsSent += peerConnection.deltaOutboundPacketsSent;
|
|
1059
|
-
this.deltaReceivedAudioBytes += peerConnection.deltaReceivedAudioBytes;
|
|
1060
|
-
this.deltaReceivedVideoBytes += peerConnection.deltaReceivedVideoBytes;
|
|
1061
|
-
this.deltaSentAudioBytes += peerConnection.deltaSentAudioBytes;
|
|
1062
|
-
this.deltaSentVideoBytes += peerConnection.deltaSentVideoBytes;
|
|
1063
|
-
this.deltaDataChannelBytesReceived += peerConnection.deltaDataChannelBytesReceived;
|
|
1064
|
-
this.deltaDataChannelBytesSent += peerConnection.deltaDataChannelBytesSent;
|
|
1065
|
-
|
|
1066
|
-
this.availableIncomingBitrate += peerConnection.availableIncomingBitrate ?? 0;
|
|
1067
|
-
this.availableOutgoingBitrate += peerConnection.availableOutgoingBitrate ?? 0;
|
|
1068
|
-
|
|
1069
|
-
sumRttInMs += peerConnection.avgRttInMs ?? 0;
|
|
1070
|
-
|
|
1071
|
-
anyPeerConnectionUsingTurn ||= peerConnection.usingTURN;
|
|
802
|
+
this._injections.clientEvents = undefined;
|
|
803
|
+
}
|
|
1072
804
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
805
|
+
if (this._injections.clientIssues) {
|
|
806
|
+
if (!sample.clientIssues) sample.clientIssues = [];
|
|
807
|
+
sample.clientIssues.push(...this._injections.clientIssues);
|
|
808
|
+
|
|
809
|
+
this._injections.clientIssues = undefined;
|
|
810
|
+
}
|
|
1076
811
|
|
|
1077
|
-
|
|
1078
|
-
|
|
812
|
+
if (this._injections.extensionStats) {
|
|
813
|
+
if (!sample.extensionStats) sample.extensionStats = [];
|
|
814
|
+
sample.extensionStats.push(...this._injections.extensionStats);
|
|
815
|
+
|
|
816
|
+
this._injections.extensionStats = undefined;
|
|
1079
817
|
}
|
|
1080
818
|
|
|
1081
|
-
this.
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
timestamp: sample.timestamp,
|
|
1088
|
-
};
|
|
1089
|
-
this.emit('score', this.score);
|
|
819
|
+
if (this._injections.attachments) {
|
|
820
|
+
if (!sample.attachments) sample.attachments = {};
|
|
821
|
+
Object.assign(sample.attachments, this._injections.attachments);
|
|
822
|
+
|
|
823
|
+
this._injections.attachments = undefined;
|
|
824
|
+
}
|
|
1090
825
|
|
|
1091
|
-
this.
|
|
826
|
+
if (this._injections.clientMetaItems) {
|
|
827
|
+
if (!sample.clientMetaItems) sample.clientMetaItems = [];
|
|
828
|
+
sample.clientMetaItems.push(...this._injections.clientMetaItems);
|
|
829
|
+
|
|
830
|
+
this._injections.clientMetaItems = undefined;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return sample;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// public resetSummaryMetrics() {
|
|
837
|
+
// this.totalDataChannelBytesReceived = 0;
|
|
838
|
+
// this.totalDataChannelBytesSent = 0;
|
|
839
|
+
// this.totalDataChannelMessagesReceived = 0;
|
|
840
|
+
// this.totalDataChannelMessagesSent = 0;
|
|
841
|
+
// this.totalInboundPacketsLost = 0;
|
|
842
|
+
// this.totalInboundPacketsReceived = 0;
|
|
843
|
+
// this.totalOutboundPacketsSent = 0;
|
|
844
|
+
// this.totalReceivedAudioBytes = 0;
|
|
845
|
+
// this.totalReceivedVideoBytes = 0;
|
|
846
|
+
// this.totalSentAudioBytes = 0;
|
|
847
|
+
// this.totalSentVideoBytes = 0;
|
|
848
|
+
// this.totalSentBytes = 0;
|
|
849
|
+
// this.totalReceivedBytes = 0;
|
|
1092
850
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
this.totalSentBytes += this.deltaSentAudioBytes + this.deltaSentVideoBytes;
|
|
1096
|
-
this.totalReceivedBytes += this.deltaReceivedAudioBytes + this.deltaReceivedVideoBytes;
|
|
1097
|
-
this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
|
|
1098
|
-
this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
|
|
1099
|
-
this.totalSentAudioBytes += this.deltaSentAudioBytes;
|
|
1100
|
-
this.totalSentVideoBytes += this.deltaSentVideoBytes;
|
|
1101
|
-
this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
|
|
1102
|
-
this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
|
|
1103
|
-
this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
|
|
1104
|
-
this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
|
|
1105
|
-
this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
|
|
851
|
+
// this.totalNumberOfIssues = 0;
|
|
1106
852
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
if (issue.trackId) {
|
|
1134
|
-
const track = peerConnection.inboundAudioTracks.get(issue.trackId) ?? peerConnection.inboundVideoTracks.get(issue.trackId);
|
|
1135
|
-
|
|
1136
|
-
if (track) track.ωpendingIssuesForScores.push(issue);
|
|
1137
|
-
} else {
|
|
1138
|
-
peerConnection.ωpendingIssuesForScores.push(issue);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
this.emit('issue', issue);
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
private _createPeerConnection(peerConnectionId: string, label?: string): ObservedPeerConnection {
|
|
1147
|
-
const result = new ObservedPeerConnection({
|
|
1148
|
-
peerConnectionId,
|
|
1149
|
-
label,
|
|
1150
|
-
}, this);
|
|
1151
|
-
|
|
1152
|
-
const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }: { localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate }) => {
|
|
1153
|
-
this.emit('selectedcandidatepair', {
|
|
1154
|
-
peerConnection: result,
|
|
1155
|
-
localCandidate,
|
|
1156
|
-
remoteCandidate,
|
|
1157
|
-
});
|
|
1158
|
-
};
|
|
1159
|
-
|
|
1160
|
-
result.once('close', () => {
|
|
1161
|
-
result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
1162
|
-
this._peerConnections.delete(peerConnectionId);
|
|
1163
|
-
});
|
|
1164
|
-
result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
1165
|
-
this._peerConnections.set(peerConnectionId, result);
|
|
1166
|
-
|
|
1167
|
-
this.emit('newpeerconnection', result);
|
|
853
|
+
// this.totalScoreSum = 0;
|
|
854
|
+
// this.numberOfScoreMeasurements = 0;
|
|
855
|
+
// }
|
|
856
|
+
|
|
857
|
+
// public createSummary(): ObservedClientSummary {
|
|
858
|
+
// return {
|
|
859
|
+
// totalRttLt50Measurements: this.totalRttLt50Measurements,
|
|
860
|
+
// totalRttLt150Measurements: this.totalRttLt150Measurements,
|
|
861
|
+
// totalRttLt300Measurements: this.totalRttLt300Measurements,
|
|
862
|
+
// totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
|
|
863
|
+
// totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
|
|
864
|
+
// totalDataChannelBytesSent: this.totalDataChannelBytesSent,
|
|
865
|
+
// totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
|
|
866
|
+
// totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
|
|
867
|
+
// totalInboundPacketsLost: this.totalInboundPacketsLost,
|
|
868
|
+
// totalInboundPacketsReceived: this.totalInboundPacketsReceived,
|
|
869
|
+
// totalOutboundPacketsSent: this.totalOutboundPacketsSent,
|
|
870
|
+
// totalReceivedAudioBytes: this.totalReceivedAudioBytes,
|
|
871
|
+
// totalReceivedVideoBytes: this.totalReceivedVideoBytes,
|
|
872
|
+
// totalSentAudioBytes: this.totalSentAudioBytes,
|
|
873
|
+
// totalSentVideoBytes: this.totalSentVideoBytes,
|
|
874
|
+
// totalSentBytes: this.totalSentBytes,
|
|
875
|
+
// totalReceivedBytes: this.totalReceivedBytes,
|
|
876
|
+
|
|
877
|
+
// numberOfIssues: this.totalNumberOfIssues,
|
|
1168
878
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
879
|
+
// totalScoreSum: this.totalScoreSum,
|
|
880
|
+
// numberOfScoreMeasurements: this.numberOfScoreMeasurements,
|
|
881
|
+
// };
|
|
882
|
+
// }
|
|
883
|
+
}
|