@observertc/observer-js 0.42.9 → 1.0.1-57a1811.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 +0 -104
- package/lib/ObservedCall.d.ts +38 -41
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +97 -109
- 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 +49 -127
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +201 -750
- 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 +23 -69
- package/lib/ObservedDataChannel.d.ts.map +1 -1
- package/lib/ObservedDataChannel.js +67 -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 +44 -0
- package/lib/ObservedIceCandidatePair.d.ts.map +1 -0
- package/lib/ObservedIceCandidatePair.js +111 -0
- package/lib/ObservedIceTransport.d.ts +32 -0
- package/lib/ObservedIceTransport.d.ts.map +1 -0
- package/lib/ObservedIceTransport.js +65 -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 +29 -0
- package/lib/ObservedInboundTrack.d.ts.map +1 -0
- package/lib/ObservedInboundTrack.js +57 -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 +29 -0
- package/lib/ObservedOutboundTrack.d.ts.map +1 -0
- package/lib/ObservedOutboundTrack.js +57 -0
- package/lib/ObservedPeerConnection.d.ts +128 -78
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +624 -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 +31 -51
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +75 -229
- 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 +1 -0
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +13 -1
- 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 +24 -25
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +41 -41
- 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 +33 -0
- package/lib/schema/ClientEventTypes.d.ts.map +1 -0
- package/lib/schema/ClientEventTypes.js +38 -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/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/utils/CallUpdater.d.ts +5 -0
- package/lib/utils/CallUpdater.d.ts.map +1 -0
- package/lib/utils/CallUpdater.js +2 -0
- package/lib/utils/OnAllClientCallUpdater.d.ts +16 -0
- package/lib/utils/OnAllClientCallUpdater.d.ts.map +1 -0
- package/lib/utils/OnAllClientCallUpdater.js +58 -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/lib/utils/onAnyClientCallUpdater.d.ts +14 -0
- package/lib/utils/onAnyClientCallUpdater.d.ts.map +1 -0
- package/lib/utils/onAnyClientCallUpdater.js +51 -0
- package/package.json +4 -4
- package/src/ObservedCall.ts +116 -166
- package/src/ObservedCertificate.ts +43 -0
- package/src/ObservedClient.ts +213 -1060
- package/src/ObservedCodec.ts +49 -0
- package/src/ObservedDataChannel.ts +68 -181
- package/src/ObservedIceCandidate.ts +64 -0
- package/src/ObservedIceCandidatePair.ts +118 -0
- package/src/ObservedIceTransport.ts +70 -0
- package/src/ObservedInboundRtp.ts +205 -0
- package/src/ObservedInboundTrack.ts +70 -0
- package/src/ObservedMediaPlayout.ts +49 -0
- package/src/ObservedMediaSource.ts +60 -0
- package/src/ObservedOutboundRtp.ts +156 -0
- package/src/ObservedOutboundTrack.ts +69 -0
- package/src/ObservedPeerConnection.ts +827 -405
- 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 +91 -302
- 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 +12 -0
- package/src/detectors/Detector.ts +6 -0
- package/src/detectors/Detectors.ts +38 -0
- package/src/index.ts +24 -90
- package/src/monitors/TurnUsageMonitor.ts +164 -164
- package/src/schema/ClientEventTypes.ts +36 -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/utils/CallUpdater.ts +5 -0
- package/src/utils/OnAllClientCallUpdater.ts +68 -0
- package/src/utils/RemoteTrackResolver.ts +12 -0
- package/src/utils/onAnyClientCallUpdater.ts +59 -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 → scores/CalculatedScore.js} +0 -0
package/src/ObservedClient.ts
CHANGED
|
@@ -1,109 +1,76 @@
|
|
|
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';
|
|
14
13
|
|
|
15
14
|
const logger = createLogger('ObservedClient');
|
|
16
15
|
|
|
17
|
-
export type
|
|
16
|
+
export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
18
17
|
clientId: string;
|
|
19
|
-
|
|
20
|
-
userId?: string,
|
|
21
|
-
marker?: string,
|
|
22
|
-
operationSystem?: OperationSystem,
|
|
23
|
-
engine?: Engine,
|
|
24
|
-
platform?: Platform,
|
|
25
|
-
browser?: Browser,
|
|
26
|
-
coordinates?: {
|
|
27
|
-
latitude: number;
|
|
28
|
-
longitude: number;
|
|
29
|
-
},
|
|
18
|
+
appData?: AppData;
|
|
30
19
|
};
|
|
31
20
|
|
|
32
21
|
export type ObservedClientEvents = {
|
|
33
|
-
update: [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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],
|
|
22
|
+
update: [
|
|
23
|
+
{
|
|
24
|
+
sample: ClientSample;
|
|
25
|
+
elapsedTimeInMs: number;
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
close: [];
|
|
29
|
+
joined: [];
|
|
30
|
+
issue: [ClientIssue];
|
|
31
|
+
metaData: [ClientMetaData];
|
|
32
|
+
rejoined: [timestamp: number];
|
|
33
|
+
left: [];
|
|
34
|
+
usingturn: [boolean];
|
|
35
|
+
usermediaerror: [string];
|
|
36
|
+
|
|
37
|
+
// do we need this?
|
|
38
|
+
newpeerconnection: [ObservedPeerConnection];
|
|
65
39
|
};
|
|
66
40
|
|
|
67
|
-
export declare interface ObservedClient
|
|
41
|
+
export declare interface ObservedClient {
|
|
68
42
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
69
43
|
off<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
70
44
|
once<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
71
45
|
emit<U extends keyof ObservedClientEvents>(event: U, ...args: ObservedClientEvents[U]): boolean;
|
|
72
|
-
readonly appData: AppData;
|
|
73
46
|
}
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
peerConnectionId: string;
|
|
78
|
-
timestamp: number;
|
|
79
|
-
}
|
|
48
|
+
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
49
|
+
public readonly detectors: Detectors;
|
|
80
50
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
51
|
+
public readonly clientId: string;
|
|
52
|
+
public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
|
|
53
|
+
public readonly calculatedScore: CalculatedScore = {
|
|
54
|
+
weight: 1,
|
|
55
|
+
value: undefined,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
public appData?: AppData;
|
|
59
|
+
public attachments?: Record<string, unknown>;
|
|
87
60
|
|
|
88
|
-
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
89
|
-
|
|
90
|
-
public readonly created = Date.now();
|
|
91
61
|
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;
|
|
62
|
+
public acceptedSamples = 0;
|
|
63
|
+
public closed = false;
|
|
100
64
|
|
|
65
|
+
public joinedAt?: number;
|
|
66
|
+
public leftAt?: number;
|
|
101
67
|
// the timestamp of the CLIENT_JOINED event
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
public
|
|
68
|
+
public operationSystem?: MetaData.OperationSystem;
|
|
69
|
+
public engine?: MetaData.Engine;
|
|
70
|
+
public platform?: MetaData.Platform;
|
|
71
|
+
public browser?: MetaData.Browser;
|
|
72
|
+
public mediaConstraints: string[] = [];
|
|
105
73
|
|
|
106
|
-
public score?: CalculatedScore;
|
|
107
74
|
public usingTURN = false;
|
|
108
75
|
public availableOutgoingBitrate = 0;
|
|
109
76
|
public availableIncomingBitrate = 0;
|
|
@@ -112,6 +79,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
112
79
|
public totalOutboundPacketsSent = 0;
|
|
113
80
|
public totalDataChannelBytesSent = 0;
|
|
114
81
|
public totalDataChannelBytesReceived = 0;
|
|
82
|
+
public totalDataChannelMessagesSent = 0;
|
|
83
|
+
public totalDataChannelMessagesReceived = 0;
|
|
115
84
|
public totalSentBytes = 0;
|
|
116
85
|
public totalReceivedBytes = 0;
|
|
117
86
|
public totalReceivedAudioBytes = 0;
|
|
@@ -125,1047 +94,231 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
125
94
|
public deltaSentVideoBytes = 0;
|
|
126
95
|
public deltaDataChannelBytesSent = 0;
|
|
127
96
|
public deltaDataChannelBytesReceived = 0;
|
|
97
|
+
public deltaDataChannelMessagesSent = 0;
|
|
98
|
+
public deltaDataChannelMessagesReceived = 0;
|
|
128
99
|
public deltaInboundPacketsLost = 0;
|
|
129
100
|
public deltaInboundPacketsReceived = 0;
|
|
130
101
|
public deltaOutboundPacketsSent = 0;
|
|
131
102
|
|
|
132
103
|
public avgRttInMs?: number;
|
|
133
|
-
public
|
|
134
|
-
public
|
|
135
|
-
public
|
|
136
|
-
public
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
public
|
|
140
|
-
public
|
|
141
|
-
public
|
|
104
|
+
public sendingAudioBitrate = 0;
|
|
105
|
+
public sendingVideoBitrate = 0;
|
|
106
|
+
public receivingAudioBitrate = 0;
|
|
107
|
+
public receivingVideoBitrate = 0;
|
|
108
|
+
|
|
109
|
+
public numberOfInboundRtpStreams = 0;
|
|
110
|
+
public numberOfInbundTracks = 0;
|
|
111
|
+
public numberOfOutboundRtpStreams = 0;
|
|
112
|
+
public numberOfOutboundTracks = 0;
|
|
113
|
+
public numberOfDataChannels = 0;
|
|
114
|
+
|
|
115
|
+
public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
|
|
142
116
|
public issues: ClientIssue[] = [];
|
|
143
117
|
|
|
144
|
-
public
|
|
145
|
-
public ωpendingMediaTrackTimestamps: PendingMediaTrackTimestamp[] = [];
|
|
146
|
-
|
|
147
|
-
public constructor(
|
|
148
|
-
private readonly _model: ObservedClientModel,
|
|
149
|
-
public readonly call: ObservedCall,
|
|
150
|
-
public readonly appData: AppData
|
|
151
|
-
) {
|
|
118
|
+
public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
|
|
152
119
|
super();
|
|
153
120
|
this.setMaxListeners(Infinity);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
public getSfu<T extends Record<string, unknown> = Record<string, unknown>>(): ObservedSfu<T> | undefined {
|
|
157
|
-
const sfu = this.call.observer.observedSfus.get(this.sfuId ?? '');
|
|
158
|
-
|
|
159
|
-
if (!sfu) return;
|
|
160
|
-
|
|
161
|
-
return sfu as ObservedSfu<T>;
|
|
162
|
-
}
|
|
163
121
|
|
|
164
|
-
|
|
165
|
-
|
|
122
|
+
this.clientId = settings.clientId;
|
|
123
|
+
this.appData = settings.appData;
|
|
124
|
+
this.detectors = new Detectors();
|
|
166
125
|
}
|
|
167
126
|
|
|
168
|
-
public
|
|
169
|
-
this.
|
|
170
|
-
|
|
171
|
-
if (this._joined) {
|
|
172
|
-
this.emit('joined');
|
|
173
|
-
}
|
|
127
|
+
public get numberOfPeerConnections() {
|
|
128
|
+
return this.observedPeerConnections.size;
|
|
174
129
|
}
|
|
175
130
|
|
|
176
|
-
public get
|
|
177
|
-
return this.
|
|
131
|
+
public get score() {
|
|
132
|
+
return this.calculatedScore.value;
|
|
178
133
|
}
|
|
179
134
|
|
|
180
|
-
public set left(value: number | undefined) {
|
|
181
|
-
this._left = value;
|
|
182
|
-
|
|
183
|
-
if (this._left) {
|
|
184
|
-
this.emit('left');
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
public get sfu(): ObservedSfu | undefined {
|
|
189
|
-
if (!this.sfuId) return;
|
|
190
|
-
|
|
191
|
-
return this.call.observer.observedSfus.get(this.sfuId);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
public get coordinates() {
|
|
195
|
-
return this._model.coordinates;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
public set coordinates(value: { latitude: number; longitude: number } | undefined) {
|
|
199
|
-
this._model.coordinates = value;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
public get clientId(): string {
|
|
203
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
204
|
-
return this._model.clientId!;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
public get roomId() {
|
|
208
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
209
|
-
return this.call.roomId!;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
public get serviceId() {
|
|
213
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
214
|
-
return this.call.serviceId!;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
public get callId() {
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
219
|
-
return this.call.callId!;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public get mediaUnitId() {
|
|
223
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
224
|
-
return this._model.mediaUnitId!;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
public get reports() {
|
|
228
|
-
return this.call.reports;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
public get userId() {
|
|
232
|
-
return this._model.userId;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
public set userId(userId: string | undefined) {
|
|
236
|
-
this._model.userId = userId;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
public get timeZoneOffsetInHours() {
|
|
240
|
-
return this._timeZoneOffsetInHours;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
public get marker() {
|
|
244
|
-
return this._model.marker;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
public get browser() {
|
|
248
|
-
return this._model.browser;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
public get engine() {
|
|
252
|
-
return this._model.engine;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
public get operationSystem() {
|
|
256
|
-
return this._model.operationSystem;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
public get platform() {
|
|
260
|
-
return this._model.platform;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
public get acceptedSamples() {
|
|
264
|
-
return this._acceptedSamples;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
public get closed() {
|
|
268
|
-
return this._closed;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
public get peerConnections(): ReadonlyMap<string, ObservedPeerConnection> {
|
|
272
|
-
return this._peerConnections;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
135
|
public close() {
|
|
276
|
-
if (this.
|
|
277
|
-
this.
|
|
136
|
+
if (this.closed) return;
|
|
137
|
+
this.closed = true;
|
|
278
138
|
|
|
279
|
-
Array.from(this.
|
|
139
|
+
Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
280
140
|
|
|
281
141
|
this.emit('close');
|
|
282
142
|
}
|
|
283
143
|
|
|
284
|
-
public addEventReport(params: PartialBy<Omit<CallEventReport, 'serviceId' | 'roomId' | 'callId' | 'clientId' | 'userId' | 'marker' | 'attachments'>, 'timestamp'> & { attachments?: Record<string, unknown> }) {
|
|
285
|
-
const {
|
|
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
|
-
|
|
304
|
-
public addExtensionStatsReport(extensionType: string, payload?: Record<string, unknown>) {
|
|
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
|
-
}
|
|
318
|
-
|
|
319
|
-
public addIssue(issue: ClientIssue) {
|
|
320
|
-
try {
|
|
321
|
-
|
|
322
|
-
this.reports.addCallEventReport({
|
|
323
|
-
serviceId: this.serviceId,
|
|
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
|
-
}
|
|
341
|
-
|
|
342
|
-
this._addAndEmitIssue(issue);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
144
|
public accept(sample: ClientSample): void {
|
|
346
|
-
if (this.
|
|
347
|
-
if (sample.clientId && sample.clientId !== 'NULL' && sample.clientId !== this.clientId) {
|
|
348
|
-
throw new Error(`Sample client id (${sample.clientId}) does not match the client id of the observed client (${this.clientId})`);
|
|
349
|
-
}
|
|
350
|
-
const now = Date.now();
|
|
145
|
+
if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
|
|
351
146
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
this.issues = [];
|
|
356
|
-
}
|
|
357
|
-
for (const peerConnection of this._peerConnections.values()) {
|
|
358
|
-
if (peerConnection.closed) continue;
|
|
359
|
-
peerConnection.resetMetrics();
|
|
360
|
-
}
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const elapsedInMs = now - this.updated;
|
|
149
|
+
const elapsedInSeconds = elapsedInMs / 1000;
|
|
361
150
|
|
|
362
|
-
|
|
363
|
-
if (sample.userId && sample.userId !== 'NULL' && sample.userId !== this._model.userId) {
|
|
364
|
-
this._model.userId = sample.userId;
|
|
365
|
-
}
|
|
366
|
-
} else if (sample.userId && sample.userId !== 'NULL') {
|
|
367
|
-
this._model.userId = sample.userId;
|
|
368
|
-
}
|
|
151
|
+
++this.acceptedSamples;
|
|
369
152
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
}
|
|
403
|
-
|
|
404
|
-
if (sample.engine && (
|
|
405
|
-
this._model.engine?.name !== sample.engine.name ||
|
|
406
|
-
this._model.engine?.version !== sample.engine.version
|
|
407
|
-
)) {
|
|
408
|
-
this._model.engine = sample.engine;
|
|
409
|
-
|
|
410
|
-
const callMetaReport = createCallMetaReport(
|
|
411
|
-
this.serviceId,
|
|
412
|
-
this.mediaUnitId,
|
|
413
|
-
this.roomId,
|
|
414
|
-
this.callId,
|
|
415
|
-
this.clientId,
|
|
416
|
-
{
|
|
417
|
-
type: CallMetaType.ENGINE,
|
|
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
|
-
}
|
|
452
|
-
|
|
453
|
-
if (sample.browser && (
|
|
454
|
-
this._model.browser?.name !== sample.browser.name ||
|
|
455
|
-
this._model.browser?.version !== sample.browser.version
|
|
456
|
-
)) {
|
|
457
|
-
this._model.browser = sample.browser;
|
|
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
|
-
}
|
|
476
|
-
|
|
477
|
-
for (const mediaConstraint of sample.mediaConstraints ?? []) {
|
|
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
|
-
}
|
|
496
|
-
|
|
497
|
-
for (const localSDP of sample.localSDPs ?? []) {
|
|
498
|
-
const callMetaReport = createCallMetaReport(
|
|
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
|
-
}
|
|
515
|
-
|
|
516
|
-
for (const extensionStats of sample.extensionStats ?? []) {
|
|
517
|
-
this.reports.addClientExtensionReport({
|
|
518
|
-
serviceId: this.serviceId,
|
|
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
|
-
});
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
|
|
531
|
-
switch (callEvent.name) {
|
|
532
|
-
case CallEventType.CLIENT_JOINED: {
|
|
533
|
-
const lastJoined = this.joined;
|
|
534
|
-
|
|
535
|
-
this.joined = timestamp;
|
|
536
|
-
|
|
537
|
-
// if it is joined before and it is joined again
|
|
538
|
-
if (lastJoined && this.joined && lastJoined !== this.joined) {
|
|
539
|
-
this.emit('rejoined', { lastJoined });
|
|
540
|
-
}
|
|
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
|
-
}
|
|
551
|
-
case CallEventType.PEER_CONNECTION_OPENED: {
|
|
552
|
-
callEvent.peerConnectionId && this.ωpendingPeerConnectionTimestamps.push({
|
|
553
|
-
type: 'opened',
|
|
554
|
-
peerConnectionId: callEvent.peerConnectionId,
|
|
555
|
-
timestamp: timestamp ?? sample.timestamp,
|
|
556
|
-
});
|
|
557
|
-
break;
|
|
558
|
-
}
|
|
559
|
-
case CallEventType.PEER_CONNECTION_CLOSED: {
|
|
560
|
-
callEvent.peerConnectionId && this.ωpendingPeerConnectionTimestamps.push({
|
|
561
|
-
type: 'closed',
|
|
562
|
-
peerConnectionId: callEvent.peerConnectionId,
|
|
563
|
-
timestamp: timestamp ?? sample.timestamp,
|
|
564
|
-
});
|
|
565
|
-
break;
|
|
566
|
-
}
|
|
567
|
-
case CallEventType.MEDIA_TRACK_ADDED: {
|
|
568
|
-
callEvent.peerConnectionId && callEvent.mediaTrackId && this.ωpendingMediaTrackTimestamps.push({
|
|
569
|
-
type: 'added',
|
|
570
|
-
peerConnectionId: callEvent.peerConnectionId,
|
|
571
|
-
mediaTrackId: callEvent.mediaTrackId,
|
|
572
|
-
timestamp: timestamp ?? sample.timestamp,
|
|
573
|
-
});
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
|
-
case CallEventType.MEDIA_TRACK_REMOVED: {
|
|
577
|
-
callEvent.peerConnectionId && callEvent.mediaTrackId && this.ωpendingMediaTrackTimestamps.push({
|
|
578
|
-
type: 'removed',
|
|
579
|
-
peerConnectionId: callEvent.peerConnectionId,
|
|
580
|
-
mediaTrackId: callEvent.mediaTrackId,
|
|
581
|
-
timestamp: timestamp ?? sample.timestamp,
|
|
582
|
-
});
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
case CallEventType.CLIENT_ISSUE: {
|
|
586
|
-
const severity = callEvent.value ? callEvent.value as ClientIssue['severity'] : 'minor';
|
|
587
|
-
|
|
588
|
-
try {
|
|
589
|
-
const issue: ClientIssue = {
|
|
590
|
-
severity,
|
|
591
|
-
timestamp: timestamp ?? Date.now(),
|
|
592
|
-
description: callEvent.message,
|
|
593
|
-
peerConnectionId: callEvent.peerConnectionId,
|
|
594
|
-
trackId: callEvent.mediaTrackId,
|
|
595
|
-
attachments: callEvent.attachments ? JSON.parse(callEvent.attachments) : undefined,
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
this._addAndEmitIssue(issue);
|
|
599
|
-
} catch (err) {
|
|
600
|
-
logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
|
|
601
|
-
}
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
this.reports.addCallEventReport({
|
|
607
|
-
serviceId: this.serviceId,
|
|
608
|
-
mediaUnitId: this.mediaUnitId,
|
|
609
|
-
roomId: this.roomId,
|
|
610
|
-
callId: this.callId,
|
|
611
|
-
clientId: this.clientId,
|
|
612
|
-
userId: this.userId,
|
|
613
|
-
timestamp: timestamp ?? now,
|
|
614
|
-
...callEvent,
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
this.call.emit('callevent', {
|
|
618
|
-
mediaUnitId: this.mediaUnitId,
|
|
619
|
-
clientId: this.clientId,
|
|
620
|
-
userId: this.userId,
|
|
621
|
-
timestamp: timestamp ?? now,
|
|
622
|
-
...callEvent,
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
}
|
|
153
|
+
this.availableIncomingBitrate = 0;
|
|
154
|
+
this.availableOutgoingBitrate = 0;
|
|
155
|
+
this.deltaDataChannelBytesReceived = 0;
|
|
156
|
+
this.deltaDataChannelBytesSent = 0;
|
|
157
|
+
this.deltaDataChannelMessagesReceived = 0;
|
|
158
|
+
this.deltaDataChannelMessagesSent = 0;
|
|
159
|
+
this.deltaInboundPacketsLost = 0;
|
|
160
|
+
this.deltaInboundPacketsReceived = 0;
|
|
161
|
+
this.deltaOutboundPacketsSent = 0;
|
|
162
|
+
this.deltaReceivedAudioBytes = 0;
|
|
163
|
+
this.deltaReceivedVideoBytes = 0;
|
|
164
|
+
this.deltaSentAudioBytes = 0;
|
|
165
|
+
this.deltaSentVideoBytes = 0;
|
|
626
166
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
this.callId,
|
|
633
|
-
this.clientId, {
|
|
634
|
-
type: CallMetaType.USER_MEDIA_ERROR,
|
|
635
|
-
payload: userMediaError,
|
|
636
|
-
},
|
|
637
|
-
this.userId,
|
|
638
|
-
);
|
|
167
|
+
this.numberOfDataChannels = 0;
|
|
168
|
+
this.numberOfInboundRtpStreams = 0;
|
|
169
|
+
this.numberOfInbundTracks = 0;
|
|
170
|
+
this.numberOfOutboundRtpStreams = 0;
|
|
171
|
+
this.numberOfOutboundTracks = 0;
|
|
639
172
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
173
|
+
sample.clientEvents?.forEach(this._processClientEvent.bind(this));
|
|
174
|
+
sample.clientMetaItems?.forEach(this.processMetadata.bind(this));
|
|
175
|
+
sample.clientIssues?.forEach(this.addIssue.bind(this));
|
|
176
|
+
sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
|
|
177
|
+
sample.extensionStats?.forEach(this.addExtensionStats.bind(this));
|
|
644
178
|
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
);
|
|
179
|
+
// emit new attachments?
|
|
180
|
+
this.attachments = sample.attachments;
|
|
657
181
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
payload: codec,
|
|
670
|
-
}, this.userId);
|
|
182
|
+
this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
|
|
183
|
+
this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
|
|
184
|
+
this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
|
|
185
|
+
this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
|
|
186
|
+
this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
|
|
187
|
+
this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
|
|
188
|
+
this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
|
|
189
|
+
this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
|
|
190
|
+
this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
|
|
191
|
+
this.totalSentAudioBytes += this.deltaSentAudioBytes;
|
|
192
|
+
this.totalSentVideoBytes += this.deltaSentVideoBytes;
|
|
671
193
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
for (const iceServer of sample.iceServers ?? []) {
|
|
679
|
-
const callMetaReport = createCallMetaReport(
|
|
680
|
-
this.serviceId,
|
|
681
|
-
this.mediaUnitId,
|
|
682
|
-
this.roomId,
|
|
683
|
-
this.callId,
|
|
684
|
-
this.clientId, {
|
|
685
|
-
type: CallMetaType.ICE_SERVER,
|
|
686
|
-
payload: iceServer,
|
|
687
|
-
}, this.userId);
|
|
194
|
+
this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
|
|
195
|
+
this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
|
|
196
|
+
this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
|
|
197
|
+
this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
|
|
688
198
|
|
|
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);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
for (const mediaSource of sample.mediaSources ?? []) {
|
|
710
|
-
const callMetaReport = createCallMetaReport(
|
|
711
|
-
this.serviceId,
|
|
712
|
-
this.mediaUnitId,
|
|
713
|
-
this.roomId,
|
|
714
|
-
this.callId,
|
|
715
|
-
this.clientId, {
|
|
716
|
-
type: CallMetaType.MEDIA_SOURCE,
|
|
717
|
-
payload: mediaSource,
|
|
718
|
-
}, this.userId);
|
|
719
|
-
|
|
720
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
721
|
-
}
|
|
199
|
+
this.calculatedScore.value = sample.score;
|
|
200
|
+
this.detectors.update();
|
|
722
201
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
202
|
+
// emit update
|
|
203
|
+
this.emit('update', {
|
|
204
|
+
sample,
|
|
205
|
+
elapsedTimeInMs: now - this.updated,
|
|
206
|
+
});
|
|
207
|
+
this.updated = now;
|
|
208
|
+
}
|
|
726
209
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
210
|
+
private _processClientEvent(event: ClientEvent) {
|
|
211
|
+
switch (event.type) {
|
|
212
|
+
case ClientEventTypes.CALL_STARTED: {
|
|
213
|
+
// update call timestamp if it's the first time
|
|
214
|
+
break;
|
|
731
215
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
for (const track of sample.inboundAudioTracks ?? []) {
|
|
736
|
-
if (!track.peerConnectionId || !track.trackId) {
|
|
737
|
-
logger.warn(`InboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
738
|
-
|
|
739
|
-
continue;
|
|
216
|
+
case ClientEventTypes.CALL_ENDED: {
|
|
217
|
+
// update call timestamp if it's the first time
|
|
218
|
+
break;
|
|
740
219
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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 ?? '');
|
|
763
|
-
});
|
|
764
|
-
remoteOutboundTrack.remoteInboundTracks.set(inboundAudioTrack.trackId ?? '', inboundAudioTrack);
|
|
220
|
+
case ClientEventTypes.CLIENT_JOINED: {
|
|
221
|
+
if (event.timestamp) {
|
|
222
|
+
if (!this.joinedAt) {
|
|
223
|
+
this.joinedAt = event.timestamp;
|
|
224
|
+
this.emit('joined');
|
|
225
|
+
} else if (this.joinedAt < event.timestamp) {
|
|
226
|
+
this.emit('rejoined', event.timestamp);
|
|
227
|
+
} else {
|
|
228
|
+
this.joinedAt = event.timestamp;
|
|
229
|
+
logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
|
|
765
230
|
}
|
|
766
231
|
}
|
|
767
|
-
|
|
768
|
-
logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
for (const track of sample.inboundVideoTracks ?? []) {
|
|
773
|
-
if (!track.peerConnectionId || !track.trackId) {
|
|
774
|
-
logger.warn(`InboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
775
|
-
|
|
776
|
-
continue;
|
|
232
|
+
break;
|
|
777
233
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
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 ?? '');
|
|
806
|
-
});
|
|
807
|
-
remoteOutboundTrack.remoteInboundTracks.set(inboundVideoTrack.trackId ?? '', inboundVideoTrack);
|
|
234
|
+
case ClientEventTypes.CLIENT_LEFT: {
|
|
235
|
+
if (event.timestamp) {
|
|
236
|
+
if (!this.leftAt) {
|
|
237
|
+
this.leftAt = event.timestamp;
|
|
238
|
+
this.emit('left');
|
|
808
239
|
}
|
|
809
240
|
}
|
|
810
|
-
|
|
811
|
-
logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
for (const track of sample.outboundAudioTracks ?? []) {
|
|
816
|
-
if (!track.peerConnectionId || !track.trackId) {
|
|
817
|
-
logger.warn(`OutboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
818
|
-
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
try {
|
|
823
|
-
const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
|
|
824
|
-
|
|
825
|
-
if (!peerConnection) continue;
|
|
826
|
-
|
|
827
|
-
const outboundAudioTrack = peerConnection.outboundAudioTracks.get(track.trackId) ?? peerConnection.createOutboundAudioTrack({
|
|
828
|
-
trackId: track.trackId,
|
|
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);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
} catch (err) {
|
|
839
|
-
logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
for (const track of sample.outboundVideoTracks ?? []) {
|
|
845
|
-
if (!track.peerConnectionId || !track.trackId) {
|
|
846
|
-
logger.warn(`OutboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
|
|
847
|
-
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
try {
|
|
852
|
-
const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
|
|
853
|
-
|
|
854
|
-
if (!peerConnection) continue;
|
|
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);
|
|
862
|
-
|
|
863
|
-
if (outboundVideoTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundVideoTrack.has(outboundVideoTrack.sfuStreamId)) {
|
|
864
|
-
this.call.sfuStreamIdToOutboundVideoTrack.set(outboundVideoTrack.sfuStreamId, outboundVideoTrack);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
} catch (err) {
|
|
868
|
-
logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
for (const iceLocalCandidate of sample.iceLocalCandidates ?? []) {
|
|
873
|
-
if (!iceLocalCandidate.peerConnectionId) {
|
|
874
|
-
logger.warn(`Local ice candidate without peerConnectionId: ${JSON.stringify(iceLocalCandidate)}`);
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
const peerConnection = this._peerConnections.get(iceLocalCandidate.peerConnectionId);
|
|
878
|
-
|
|
879
|
-
if (!peerConnection) {
|
|
880
|
-
logger.debug(`Peer connection ${iceLocalCandidate.peerConnectionId} not found for ice local candidate ${iceLocalCandidate.id}`);
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
peerConnection.ICE.addLocalCandidate(iceLocalCandidate, sample.timestamp);
|
|
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);
|
|
893
|
-
|
|
894
|
-
if (!peerConnection) {
|
|
895
|
-
logger.debug(`Peer connection ${iceRemoteCandidate.peerConnectionId} not found for ice remote candidate ${iceRemoteCandidate.id}`);
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
peerConnection.ICE.addRemoteCandidate(iceRemoteCandidate, sample.timestamp);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
for (const candidatePair of sample.iceCandidatePairs ?? []) {
|
|
903
|
-
const peerConnection = this._peerConnections.get(candidatePair.peerConnectionId);
|
|
904
|
-
|
|
905
|
-
if (!peerConnection) {
|
|
906
|
-
logger.debug(`Peer connection ${candidatePair.peerConnectionId} not found for ice candidate pair ${candidatePair.localCandidateId}, ${candidatePair.remoteCandidateId}`);
|
|
907
|
-
|
|
908
|
-
continue;
|
|
241
|
+
break;
|
|
909
242
|
}
|
|
910
|
-
|
|
911
|
-
peerConnection.ICE.update(candidatePair, sample.timestamp);
|
|
912
243
|
}
|
|
913
244
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
logger.warn(`DataChannel without peerConnectionId or dataChannelIdentifier: ${JSON.stringify(dataChannel)}`);
|
|
245
|
+
this.call.observer.emit('client-event', this, event);
|
|
246
|
+
}
|
|
917
247
|
|
|
918
|
-
|
|
248
|
+
public processMetadata(metadata: ClientMetaData) {
|
|
249
|
+
switch (metadata.type) {
|
|
250
|
+
case ClientMetaTypes.BROWSER: {
|
|
251
|
+
this.browser = parseJsonAs(metadata.payload);
|
|
252
|
+
break;
|
|
919
253
|
}
|
|
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}`);
|
|
254
|
+
case ClientMetaTypes.ENGINE: {
|
|
255
|
+
this.engine = parseJsonAs(metadata.payload);
|
|
256
|
+
break;
|
|
931
257
|
}
|
|
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;
|
|
258
|
+
case ClientMetaTypes.PLATFORM: {
|
|
259
|
+
this.platform = parseJsonAs(metadata.payload);
|
|
260
|
+
break;
|
|
947
261
|
}
|
|
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;
|
|
262
|
+
case ClientMetaTypes.OPERATION_SYSTEM: {
|
|
263
|
+
this.operationSystem = parseJsonAs(metadata.payload);
|
|
264
|
+
break;
|
|
970
265
|
}
|
|
971
|
-
|
|
972
|
-
this.ωpendingMediaTrackTimestamps = newPendingTimestamps;
|
|
973
266
|
}
|
|
974
267
|
|
|
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();
|
|
986
|
-
|
|
987
|
-
continue;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
track.visited = false;
|
|
991
|
-
}
|
|
992
|
-
for (const track of peerConnection.inboundVideoTracks.values()) {
|
|
993
|
-
if (!track.visited) {
|
|
994
|
-
track.close();
|
|
995
|
-
|
|
996
|
-
continue;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
track.visited = false;
|
|
1000
|
-
}
|
|
1001
|
-
for (const track of peerConnection.outboundAudioTracks.values()) {
|
|
1002
|
-
if (!track.visited) {
|
|
1003
|
-
track.close();
|
|
268
|
+
this.call.observer.emit('client-metadata', this, metadata);
|
|
269
|
+
}
|
|
1004
270
|
|
|
1005
|
-
|
|
1006
|
-
|
|
271
|
+
public addIssue(issue: ClientIssue) {
|
|
272
|
+
this.emit('issue', issue);
|
|
273
|
+
this.call.observer.emit('client-issue', this, issue);
|
|
274
|
+
}
|
|
1007
275
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
if (!dataChannel.visited) {
|
|
1012
|
-
dataChannel.close();
|
|
276
|
+
public addExtensionStats(stats: ExtensionStat) {
|
|
277
|
+
this.call.observer.emit('client-extension-stats', this, stats);
|
|
278
|
+
}
|
|
1013
279
|
|
|
1014
|
-
|
|
1015
|
-
|
|
280
|
+
private _updatePeerConnection(sample: PeerConnectionSample) {
|
|
281
|
+
let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
|
|
1016
282
|
|
|
1017
|
-
|
|
283
|
+
if (!observedPeerConnection) {
|
|
284
|
+
if (!sample.peerConnectionId) {
|
|
285
|
+
return logger.warn(
|
|
286
|
+
`ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
|
|
287
|
+
sample
|
|
288
|
+
);
|
|
1018
289
|
}
|
|
1019
290
|
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// update metrics
|
|
1024
|
-
const wasUsingTURN = this.usingTURN;
|
|
1025
|
-
const elapsedTimeInMs = now - this.updated;
|
|
291
|
+
observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
|
|
1026
292
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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;
|
|
1046
|
-
|
|
1047
|
-
let minPcScore: number | undefined;
|
|
1048
|
-
let maxPcScore: number | undefined;
|
|
1049
|
-
let numberOfScoredPeerConnections = 0;
|
|
1050
|
-
|
|
1051
|
-
for (const peerConnection of this._peerConnections.values()) {
|
|
1052
|
-
if (peerConnection.closed) continue;
|
|
1053
|
-
|
|
1054
|
-
peerConnection.updateMetrics();
|
|
293
|
+
observedPeerConnection.once('close', () => {
|
|
294
|
+
this.observedPeerConnections.delete(sample.peerConnectionId);
|
|
295
|
+
});
|
|
296
|
+
this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
|
|
1055
297
|
|
|
1056
|
-
this.
|
|
1057
|
-
this.deltaInboundPacketsReceived += peerConnection.deltaInboundPacketsReceived;
|
|
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;
|
|
1072
|
-
|
|
1073
|
-
if (peerConnection.score) {
|
|
1074
|
-
if (minPcScore === undefined || peerConnection.score.score < minPcScore) minPcScore = peerConnection.score.score;
|
|
1075
|
-
if (maxPcScore === undefined || peerConnection.score.score > maxPcScore) maxPcScore = peerConnection.score.score;
|
|
1076
|
-
|
|
1077
|
-
++numberOfScoredPeerConnections;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
this.score = {
|
|
1082
|
-
score: minPcScore ?? -1,
|
|
1083
|
-
remarks: [ {
|
|
1084
|
-
severity: 'none',
|
|
1085
|
-
text: `Min and max score of all peer connections: ${minPcScore}, ${maxPcScore}, number of PeerConnections with scores: ${numberOfScoredPeerConnections}`,
|
|
1086
|
-
} ],
|
|
1087
|
-
timestamp: sample.timestamp,
|
|
1088
|
-
};
|
|
1089
|
-
this.emit('score', this.score);
|
|
1090
|
-
|
|
1091
|
-
this.usingTURN = anyPeerConnectionUsingTurn === true;
|
|
1092
|
-
|
|
1093
|
-
if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
|
|
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;
|
|
1106
|
-
|
|
1107
|
-
this.outboundAudioBitrate = (this.deltaSentAudioBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
1108
|
-
this.outboundVideoBitrate = (this.deltaSentVideoBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
1109
|
-
this.inboundAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
1110
|
-
this.inboundVideoBitrate = (this.deltaReceivedVideoBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
1111
|
-
|
|
1112
|
-
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
1113
|
-
|
|
1114
|
-
// to make sure when sample is emitted it can be associated to this client
|
|
1115
|
-
sample.clientId = this.clientId;
|
|
1116
|
-
|
|
1117
|
-
this.updated = now;
|
|
1118
|
-
this.lastStatsTimestamp = sample.timestamp;
|
|
1119
|
-
this.emit('update', {
|
|
1120
|
-
sample,
|
|
1121
|
-
elapsedTimeInMs,
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
private _addAndEmitIssue(issue: ClientIssue) {
|
|
1126
|
-
this.issues.push(issue);
|
|
1127
|
-
|
|
1128
|
-
if (issue.peerConnectionId) {
|
|
1129
|
-
const peerConnection = this._peerConnections.get(issue.peerConnectionId);
|
|
1130
|
-
|
|
1131
|
-
if (peerConnection) {
|
|
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
|
-
}
|
|
298
|
+
this.emit('newpeerconnection', observedPeerConnection);
|
|
1141
299
|
}
|
|
1142
300
|
|
|
1143
|
-
|
|
1144
|
-
}
|
|
301
|
+
observedPeerConnection.accept(sample);
|
|
1145
302
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
303
|
+
this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
|
|
304
|
+
this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
|
|
305
|
+
this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
|
|
306
|
+
this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
|
|
307
|
+
this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
|
|
308
|
+
this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
|
|
309
|
+
this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
|
|
310
|
+
this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
|
|
311
|
+
this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
|
|
312
|
+
this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
|
|
313
|
+
this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
|
|
1151
314
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
peerConnection: result,
|
|
1155
|
-
localCandidate,
|
|
1156
|
-
remoteCandidate,
|
|
1157
|
-
});
|
|
1158
|
-
};
|
|
315
|
+
this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
|
|
316
|
+
this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
|
|
1159
317
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
this._peerConnections.set(peerConnectionId, result);
|
|
1166
|
-
|
|
1167
|
-
this.emit('newpeerconnection', result);
|
|
1168
|
-
|
|
1169
|
-
return result;
|
|
318
|
+
this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
|
|
319
|
+
this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
|
|
320
|
+
this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
|
|
321
|
+
this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
|
|
322
|
+
this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
|
|
1170
323
|
}
|
|
1171
|
-
}
|
|
324
|
+
}
|