@observertc/observer-js 0.42.9 → 1.0.1-28c6425.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 +66 -42
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +232 -108
- 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 +71 -130
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +311 -750
- 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 +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 +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 +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 +132 -80
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +630 -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 +45 -51
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +150 -226
- package/lib/ObserverSummary.d.ts +13 -0
- package/lib/ObserverSummary.d.ts.map +1 -0
- package/lib/ObserverSummary.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 +2 -1
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +17 -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 +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 +100 -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/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 +13 -0
- package/lib/updaters/OnAllCallObserverUpdater_.d.ts.map +1 -0
- package/lib/updaters/OnAllCallObserverUpdater_.js +38 -0
- package/lib/updaters/OnAllClientCallUpdater.d.ts +13 -0
- package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAllClientCallUpdater.js +37 -0
- package/lib/updaters/OnAnyCallObserverUpdater_.d.ts +11 -0
- package/lib/updaters/OnAnyCallObserverUpdater_.d.ts.map +1 -0
- package/lib/updaters/OnAnyCallObserverUpdater_.js +27 -0
- package/lib/updaters/OnAnyClientCallUpdater.d.ts +11 -0
- package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
- package/lib/updaters/OnAnyClientCallUpdater.js +27 -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/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 +291 -164
- package/src/ObservedCallSummary.ts +22 -0
- package/src/ObservedCertificate.ts +43 -0
- package/src/ObservedClient.ts +342 -1064
- package/src/ObservedClientSummary.ts +24 -0
- 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 +99 -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 +839 -408
- 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 +171 -294
- package/src/ObserverSummary.ts +15 -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 +18 -2
- 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 +112 -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 +46 -0
- package/src/updaters/OnAllClientCallUpdater.ts +46 -0
- package/src/updaters/OnAnyCallObserverUpdater_.ts +32 -0
- package/src/updaters/OnAnyClientCallUpdater.ts +32 -0
- package/src/updaters/OnIntervalUpdater.ts +19 -0
- package/src/updaters/Updater.ts +5 -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,109 +1,77 @@
|
|
|
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 { getMedian, parseJsonAs } from './common/utils';
|
|
11
|
+
import { CalculatedScore } from './scores/CalculatedScore';
|
|
12
|
+
import { Detectors } from './detectors/Detectors';
|
|
13
|
+
import { ObservedClientSummary } from './ObservedClientSummary';
|
|
14
14
|
|
|
15
15
|
const logger = createLogger('ObservedClient');
|
|
16
16
|
|
|
17
|
-
export type
|
|
17
|
+
export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
18
18
|
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
|
-
},
|
|
19
|
+
appData?: AppData;
|
|
30
20
|
};
|
|
31
21
|
|
|
32
22
|
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],
|
|
23
|
+
update: [
|
|
24
|
+
{
|
|
25
|
+
sample: ClientSample;
|
|
26
|
+
elapsedTimeInMs: number;
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
close: [];
|
|
30
|
+
joined: [];
|
|
31
|
+
issue: [ClientIssue];
|
|
32
|
+
metaData: [ClientMetaData];
|
|
33
|
+
rejoined: [timestamp: number];
|
|
34
|
+
left: [];
|
|
35
|
+
usingturn: [boolean];
|
|
36
|
+
usermediaerror: [string];
|
|
37
|
+
|
|
38
|
+
// do we need this?
|
|
39
|
+
newpeerconnection: [ObservedPeerConnection];
|
|
65
40
|
};
|
|
66
41
|
|
|
67
|
-
export declare interface ObservedClient
|
|
42
|
+
export declare interface ObservedClient {
|
|
68
43
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
69
44
|
off<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
70
45
|
once<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
71
46
|
emit<U extends keyof ObservedClientEvents>(event: U, ...args: ObservedClientEvents[U]): boolean;
|
|
72
|
-
readonly appData: AppData;
|
|
73
47
|
}
|
|
74
48
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
peerConnectionId: string;
|
|
78
|
-
timestamp: number;
|
|
79
|
-
}
|
|
49
|
+
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
50
|
+
public readonly detectors: Detectors;
|
|
80
51
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
52
|
+
public readonly clientId: string;
|
|
53
|
+
public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
|
|
54
|
+
public readonly calculatedScore: CalculatedScore = {
|
|
55
|
+
weight: 1,
|
|
56
|
+
value: undefined,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
public appData: AppData;
|
|
60
|
+
public attachments?: Record<string, unknown>;
|
|
87
61
|
|
|
88
|
-
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
89
|
-
|
|
90
|
-
public readonly created = Date.now();
|
|
91
62
|
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;
|
|
63
|
+
public acceptedSamples = 0;
|
|
64
|
+
public closed = false;
|
|
100
65
|
|
|
66
|
+
public joinedAt?: number;
|
|
67
|
+
public leftAt?: number;
|
|
101
68
|
// the timestamp of the CLIENT_JOINED event
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
public
|
|
69
|
+
public operationSystem?: MetaData.OperationSystem;
|
|
70
|
+
public engine?: MetaData.Engine;
|
|
71
|
+
public platform?: MetaData.Platform;
|
|
72
|
+
public browser?: MetaData.Browser;
|
|
73
|
+
public mediaConstraints: string[] = [];
|
|
105
74
|
|
|
106
|
-
public score?: CalculatedScore;
|
|
107
75
|
public usingTURN = false;
|
|
108
76
|
public availableOutgoingBitrate = 0;
|
|
109
77
|
public availableIncomingBitrate = 0;
|
|
@@ -112,12 +80,14 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
112
80
|
public totalOutboundPacketsSent = 0;
|
|
113
81
|
public totalDataChannelBytesSent = 0;
|
|
114
82
|
public totalDataChannelBytesReceived = 0;
|
|
115
|
-
public
|
|
116
|
-
public
|
|
83
|
+
public totalDataChannelMessagesSent = 0;
|
|
84
|
+
public totalDataChannelMessagesReceived = 0;
|
|
117
85
|
public totalReceivedAudioBytes = 0;
|
|
118
86
|
public totalReceivedVideoBytes = 0;
|
|
119
87
|
public totalSentAudioBytes = 0;
|
|
120
88
|
public totalSentVideoBytes = 0;
|
|
89
|
+
public totalSentBytes = 0;
|
|
90
|
+
public totalReceivedBytes = 0;
|
|
121
91
|
|
|
122
92
|
public deltaReceivedAudioBytes = 0;
|
|
123
93
|
public deltaReceivedVideoBytes = 0;
|
|
@@ -125,1047 +95,355 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
125
95
|
public deltaSentVideoBytes = 0;
|
|
126
96
|
public deltaDataChannelBytesSent = 0;
|
|
127
97
|
public deltaDataChannelBytesReceived = 0;
|
|
98
|
+
public deltaDataChannelMessagesSent = 0;
|
|
99
|
+
public deltaDataChannelMessagesReceived = 0;
|
|
128
100
|
public deltaInboundPacketsLost = 0;
|
|
129
101
|
public deltaInboundPacketsReceived = 0;
|
|
130
102
|
public deltaOutboundPacketsSent = 0;
|
|
131
|
-
|
|
132
|
-
public
|
|
133
|
-
public
|
|
134
|
-
public
|
|
135
|
-
public
|
|
136
|
-
public
|
|
137
|
-
public
|
|
138
|
-
|
|
139
|
-
public
|
|
140
|
-
public
|
|
141
|
-
public
|
|
103
|
+
public deltaRttInMsMeasurements: number[] = [];
|
|
104
|
+
public deltaTransportSentBytes = 0;
|
|
105
|
+
public deltaTransportReceivedBytes = 0;
|
|
106
|
+
public deltaRttLt50Measurements = 0;
|
|
107
|
+
public deltaRttLt150Measurements = 0;
|
|
108
|
+
public deltaRttLt300Measurements = 0;
|
|
109
|
+
public deltaRttGtOrEq300Measurements = 0;
|
|
110
|
+
|
|
111
|
+
public currentRttInMs?: number;
|
|
112
|
+
public sendingAudioBitrate = 0;
|
|
113
|
+
public sendingVideoBitrate = 0;
|
|
114
|
+
public receivingAudioBitrate = 0;
|
|
115
|
+
public receivingVideoBitrate = 0;
|
|
116
|
+
|
|
117
|
+
public numberOfInboundRtpStreams = 0;
|
|
118
|
+
public numberOfInbundTracks = 0;
|
|
119
|
+
public numberOfOutboundRtpStreams = 0;
|
|
120
|
+
public numberOfOutboundTracks = 0;
|
|
121
|
+
public numberOfDataChannels = 0;
|
|
122
|
+
|
|
123
|
+
public totalRttLt50Measurements = 0;
|
|
124
|
+
public totalRttLt150Measurements = 0;
|
|
125
|
+
public totalRttLt300Measurements = 0;
|
|
126
|
+
public totalRttGtOrEq300Measurements = 0;
|
|
127
|
+
public deltaNumberOfIssues = 0;
|
|
128
|
+
|
|
129
|
+
public totalScoreSum = 0;
|
|
130
|
+
public numberOfScoreMeasurements = 0;
|
|
131
|
+
public numberOfIssues = 0;
|
|
132
|
+
|
|
133
|
+
public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
|
|
142
134
|
public issues: ClientIssue[] = [];
|
|
143
135
|
|
|
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
|
-
) {
|
|
136
|
+
public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
|
|
152
137
|
super();
|
|
153
138
|
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
139
|
|
|
159
|
-
|
|
140
|
+
this.clientId = settings.clientId;
|
|
141
|
+
this.appData = settings.appData ?? {} as AppData;
|
|
160
142
|
|
|
161
|
-
|
|
143
|
+
this.detectors = new Detectors();
|
|
162
144
|
}
|
|
163
|
-
|
|
164
|
-
public get
|
|
165
|
-
return this.
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public set joined(value: number | undefined) {
|
|
169
|
-
this._joined = value;
|
|
170
|
-
|
|
171
|
-
if (this._joined) {
|
|
172
|
-
this.emit('joined');
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
public get left() {
|
|
177
|
-
return this._left;
|
|
178
|
-
}
|
|
179
|
-
|
|
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;
|
|
145
|
+
|
|
146
|
+
public get numberOfPeerConnections() {
|
|
147
|
+
return this.observedPeerConnections.size;
|
|
265
148
|
}
|
|
266
149
|
|
|
267
|
-
public get
|
|
268
|
-
return this.
|
|
150
|
+
public get score() {
|
|
151
|
+
return this.calculatedScore.value;
|
|
269
152
|
}
|
|
270
153
|
|
|
271
|
-
public get peerConnections(): ReadonlyMap<string, ObservedPeerConnection> {
|
|
272
|
-
return this._peerConnections;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
154
|
public close() {
|
|
276
|
-
if (this.
|
|
277
|
-
this.
|
|
155
|
+
if (this.closed) return;
|
|
156
|
+
this.closed = true;
|
|
278
157
|
|
|
279
|
-
Array.from(this.
|
|
158
|
+
Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
280
159
|
|
|
281
160
|
this.emit('close');
|
|
282
161
|
}
|
|
283
162
|
|
|
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
163
|
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();
|
|
164
|
+
if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
|
|
351
165
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
this.issues = [];
|
|
356
|
-
}
|
|
357
|
-
for (const peerConnection of this._peerConnections.values()) {
|
|
358
|
-
if (peerConnection.closed) continue;
|
|
359
|
-
peerConnection.resetMetrics();
|
|
360
|
-
}
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
const elapsedInMs = now - this.updated;
|
|
168
|
+
const elapsedInSeconds = elapsedInMs / 1000;
|
|
361
169
|
|
|
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
|
-
}
|
|
170
|
+
++this.acceptedSamples;
|
|
369
171
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
}
|
|
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);
|
|
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);
|
|
688
|
-
|
|
689
|
-
this.reports.addCallMetaReport(callMetaReport);
|
|
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
|
-
}
|
|
722
|
-
|
|
723
|
-
for (const transport of sample.pcTransports ?? []) {
|
|
724
|
-
try {
|
|
725
|
-
const peerConnection = this._peerConnections.get(transport.transportId) ?? this._createPeerConnection(transport.peerConnectionId);
|
|
726
|
-
|
|
727
|
-
!peerConnection.label && transport.label && (peerConnection.label = transport.label);
|
|
728
|
-
peerConnection.update(transport, sample.timestamp);
|
|
729
|
-
} catch (err) {
|
|
730
|
-
logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
|
|
731
|
-
}
|
|
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)}`);
|
|
172
|
+
this.availableIncomingBitrate = 0;
|
|
173
|
+
this.availableOutgoingBitrate = 0;
|
|
174
|
+
this.deltaDataChannelBytesReceived = 0;
|
|
175
|
+
this.deltaDataChannelBytesSent = 0;
|
|
176
|
+
this.deltaDataChannelMessagesReceived = 0;
|
|
177
|
+
this.deltaDataChannelMessagesSent = 0;
|
|
178
|
+
this.deltaInboundPacketsLost = 0;
|
|
179
|
+
this.deltaInboundPacketsReceived = 0;
|
|
180
|
+
this.deltaOutboundPacketsSent = 0;
|
|
181
|
+
this.deltaReceivedAudioBytes = 0;
|
|
182
|
+
this.deltaReceivedVideoBytes = 0;
|
|
183
|
+
this.deltaSentAudioBytes = 0;
|
|
184
|
+
this.deltaSentVideoBytes = 0;
|
|
185
|
+
this.deltaTransportReceivedBytes = 0;
|
|
186
|
+
this.deltaTransportSentBytes = 0;
|
|
187
|
+
this.deltaRttInMsMeasurements = [];
|
|
188
|
+
this.deltaRttLt50Measurements = 0;
|
|
189
|
+
this.deltaRttLt150Measurements = 0;
|
|
190
|
+
this.deltaRttLt300Measurements = 0;
|
|
191
|
+
this.deltaRttGtOrEq300Measurements = 0;
|
|
192
|
+
this.deltaNumberOfIssues = 0;
|
|
193
|
+
|
|
194
|
+
this.numberOfDataChannels = 0;
|
|
195
|
+
this.numberOfInboundRtpStreams = 0;
|
|
196
|
+
this.numberOfInbundTracks = 0;
|
|
197
|
+
this.numberOfOutboundRtpStreams = 0;
|
|
198
|
+
this.numberOfOutboundTracks = 0;
|
|
199
|
+
|
|
200
|
+
sample.clientEvents?.forEach(this._processClientEvent.bind(this));
|
|
201
|
+
sample.clientMetaItems?.forEach(this.addMetadata.bind(this));
|
|
202
|
+
sample.clientIssues?.forEach(this.addIssue.bind(this));
|
|
203
|
+
sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
|
|
204
|
+
sample.extensionStats?.forEach(this.addExtensionStats.bind(this));
|
|
738
205
|
|
|
739
|
-
|
|
740
|
-
}
|
|
206
|
+
this._updateRttMeasurements();
|
|
741
207
|
|
|
742
|
-
|
|
743
|
-
|
|
208
|
+
// emit new attachments?
|
|
209
|
+
this.attachments = sample.attachments;
|
|
744
210
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
211
|
+
this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
|
|
212
|
+
this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
|
|
213
|
+
this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
|
|
214
|
+
this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
|
|
215
|
+
this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
|
|
216
|
+
this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
|
|
217
|
+
this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
|
|
218
|
+
this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
|
|
219
|
+
this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
|
|
220
|
+
this.totalSentAudioBytes += this.deltaSentAudioBytes;
|
|
221
|
+
this.totalSentVideoBytes += this.deltaSentVideoBytes;
|
|
222
|
+
this.totalReceivedBytes += this.deltaTransportReceivedBytes;
|
|
223
|
+
this.totalSentBytes += this.deltaTransportSentBytes;
|
|
224
|
+
this.totalRttLt50Measurements += this.deltaRttLt50Measurements;
|
|
225
|
+
this.totalRttLt150Measurements += this.deltaRttLt150Measurements;
|
|
226
|
+
this.totalRttLt300Measurements += this.deltaRttLt300Measurements;
|
|
227
|
+
this.totalRttGtOrEq300Measurements += this.deltaRttGtOrEq300Measurements;
|
|
228
|
+
this.numberOfIssues += this.deltaNumberOfIssues;
|
|
229
|
+
|
|
230
|
+
this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
|
|
231
|
+
this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
|
|
232
|
+
this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
|
|
233
|
+
this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
|
|
234
|
+
|
|
235
|
+
this.calculatedScore.value = sample.score;
|
|
236
|
+
this.detectors.update();
|
|
237
|
+
|
|
238
|
+
// emit update
|
|
239
|
+
this.emit('update', {
|
|
240
|
+
sample,
|
|
241
|
+
elapsedTimeInMs: now - this.updated,
|
|
242
|
+
});
|
|
243
|
+
this.updated = now;
|
|
757
244
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
245
|
+
// if result changed after update
|
|
246
|
+
if (this.calculatedScore.value) {
|
|
247
|
+
this.totalScoreSum += this.calculatedScore.value;
|
|
248
|
+
++this.numberOfScoreMeasurements;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private _processClientEvent(event: ClientEvent) {
|
|
253
|
+
// eslint-disable-next-line no-console
|
|
254
|
+
// console.warn('ClientEvent', event);
|
|
255
|
+
switch (event.type) {
|
|
256
|
+
case ClientEventTypes.CLIENT_JOINED: {
|
|
257
|
+
if (event.timestamp) {
|
|
258
|
+
if (!this.joinedAt) {
|
|
259
|
+
this.joinedAt = event.timestamp;
|
|
260
|
+
this.emit('joined');
|
|
261
|
+
} else if (this.joinedAt < event.timestamp) {
|
|
262
|
+
this.emit('rejoined', event.timestamp);
|
|
263
|
+
} else {
|
|
264
|
+
this.joinedAt = event.timestamp;
|
|
265
|
+
logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
|
|
765
266
|
}
|
|
766
267
|
}
|
|
767
|
-
|
|
768
|
-
logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
|
|
268
|
+
break;
|
|
769
269
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
778
|
-
if (isValidUuid(track.trackId) === false) {
|
|
779
|
-
// mediasoup-probator trackId is not a valid UUID, no need to warn about it
|
|
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 ?? '');
|
|
806
|
-
});
|
|
807
|
-
remoteOutboundTrack.remoteInboundTracks.set(inboundVideoTrack.trackId ?? '', inboundVideoTrack);
|
|
270
|
+
case ClientEventTypes.CLIENT_LEFT: {
|
|
271
|
+
if (event.timestamp) {
|
|
272
|
+
if (!this.leftAt) {
|
|
273
|
+
this.leftAt = event.timestamp;
|
|
274
|
+
this.emit('left');
|
|
275
|
+
} else {
|
|
276
|
+
logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
|
|
808
277
|
}
|
|
809
278
|
}
|
|
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}`);
|
|
279
|
+
break;
|
|
840
280
|
}
|
|
841
|
-
|
|
842
281
|
}
|
|
843
282
|
|
|
844
|
-
|
|
845
|
-
|
|
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
|
-
}
|
|
283
|
+
this.call.observer.emit('client-event', this, event);
|
|
284
|
+
}
|
|
871
285
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
286
|
+
public addMetadata(metadata: ClientMetaData) {
|
|
287
|
+
if (this.closed) return;
|
|
288
|
+
|
|
289
|
+
switch (metadata.type) {
|
|
290
|
+
case ClientMetaTypes.BROWSER: {
|
|
291
|
+
this.browser = parseJsonAs(metadata.payload);
|
|
292
|
+
break;
|
|
876
293
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
logger.debug(`Peer connection ${iceLocalCandidate.peerConnectionId} not found for ice local candidate ${iceLocalCandidate.id}`);
|
|
881
|
-
continue;
|
|
294
|
+
case ClientMetaTypes.ENGINE: {
|
|
295
|
+
this.engine = parseJsonAs(metadata.payload);
|
|
296
|
+
break;
|
|
882
297
|
}
|
|
883
|
-
|
|
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;
|
|
298
|
+
case ClientMetaTypes.PLATFORM: {
|
|
299
|
+
this.platform = parseJsonAs(metadata.payload);
|
|
300
|
+
break;
|
|
891
301
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
logger.debug(`Peer connection ${iceRemoteCandidate.peerConnectionId} not found for ice remote candidate ${iceRemoteCandidate.id}`);
|
|
896
|
-
continue;
|
|
302
|
+
case ClientMetaTypes.OPERATION_SYSTEM: {
|
|
303
|
+
this.operationSystem = parseJsonAs(metadata.payload);
|
|
304
|
+
break;
|
|
897
305
|
}
|
|
898
|
-
|
|
899
|
-
peerConnection.ICE.addRemoteCandidate(iceRemoteCandidate, sample.timestamp);
|
|
900
306
|
}
|
|
901
307
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (!peerConnection) {
|
|
906
|
-
logger.debug(`Peer connection ${candidatePair.peerConnectionId} not found for ice candidate pair ${candidatePair.localCandidateId}, ${candidatePair.remoteCandidateId}`);
|
|
907
|
-
|
|
908
|
-
continue;
|
|
909
|
-
}
|
|
308
|
+
this.call.observer.emit('client-metadata', this, metadata);
|
|
309
|
+
}
|
|
910
310
|
|
|
911
|
-
|
|
912
|
-
|
|
311
|
+
public addIssue(issue: ClientIssue) {
|
|
312
|
+
if (this.closed) return;
|
|
913
313
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
314
|
+
++this.deltaNumberOfIssues;
|
|
315
|
+
this.emit('issue', issue);
|
|
316
|
+
this.call.observer.emit('client-issue', this, issue);
|
|
317
|
+
}
|
|
917
318
|
|
|
918
|
-
|
|
919
|
-
|
|
319
|
+
public addExtensionStats(stats: ExtensionStat) {
|
|
320
|
+
this.call.observer.emit('client-extension-stats', this, stats);
|
|
321
|
+
}
|
|
920
322
|
|
|
921
|
-
|
|
922
|
-
|
|
323
|
+
private _updatePeerConnection(sample: PeerConnectionSample) {
|
|
324
|
+
let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
|
|
923
325
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
logger.error(`Error creating data channel: ${(err as Error)?.message}`);
|
|
326
|
+
if (!observedPeerConnection) {
|
|
327
|
+
if (!sample.peerConnectionId) {
|
|
328
|
+
return logger.warn(
|
|
329
|
+
`ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
|
|
330
|
+
sample
|
|
331
|
+
);
|
|
931
332
|
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// try to set the timestamps of the peer connections
|
|
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
333
|
|
|
941
|
-
|
|
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;
|
|
947
|
-
}
|
|
334
|
+
observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
|
|
948
335
|
|
|
949
|
-
|
|
950
|
-
|
|
336
|
+
observedPeerConnection.once('close', () => {
|
|
337
|
+
this.observedPeerConnections.delete(sample.peerConnectionId);
|
|
338
|
+
});
|
|
339
|
+
this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
|
|
340
|
+
|
|
341
|
+
this.emit('newpeerconnection', observedPeerConnection);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
observedPeerConnection.accept(sample);
|
|
345
|
+
|
|
346
|
+
observedPeerConnection.currentRttInMs;
|
|
347
|
+
|
|
348
|
+
this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
|
|
349
|
+
this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
|
|
350
|
+
this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
|
|
351
|
+
this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
|
|
352
|
+
this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
|
|
353
|
+
this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
|
|
354
|
+
this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
|
|
355
|
+
this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
|
|
356
|
+
this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
|
|
357
|
+
this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
|
|
358
|
+
this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
|
|
359
|
+
this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
|
|
360
|
+
this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
|
|
361
|
+
|
|
362
|
+
this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
|
|
363
|
+
this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
|
|
364
|
+
|
|
365
|
+
this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
|
|
366
|
+
this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
|
|
367
|
+
this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
|
|
368
|
+
this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
|
|
369
|
+
this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
|
|
370
|
+
|
|
371
|
+
if (observedPeerConnection.currentRttInMs) {
|
|
372
|
+
this.deltaRttInMsMeasurements.push(observedPeerConnection.currentRttInMs);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
public resetSummaryMetrics() {
|
|
377
|
+
this.totalDataChannelBytesReceived = 0;
|
|
378
|
+
this.totalDataChannelBytesSent = 0;
|
|
379
|
+
this.totalDataChannelMessagesReceived = 0;
|
|
380
|
+
this.totalDataChannelMessagesSent = 0;
|
|
381
|
+
this.totalInboundPacketsLost = 0;
|
|
382
|
+
this.totalInboundPacketsReceived = 0;
|
|
383
|
+
this.totalOutboundPacketsSent = 0;
|
|
384
|
+
this.totalReceivedAudioBytes = 0;
|
|
385
|
+
this.totalReceivedVideoBytes = 0;
|
|
386
|
+
this.totalSentAudioBytes = 0;
|
|
387
|
+
this.totalSentVideoBytes = 0;
|
|
388
|
+
this.totalSentBytes = 0;
|
|
389
|
+
this.totalReceivedBytes = 0;
|
|
951
390
|
|
|
952
|
-
|
|
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;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
this.ωpendingMediaTrackTimestamps = newPendingTimestamps;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// close resources that are not visited
|
|
976
|
-
for (const peerConnection of this._peerConnections.values()) {
|
|
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();
|
|
1004
|
-
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
track.visited = false;
|
|
1009
|
-
}
|
|
1010
|
-
for (const dataChannel of peerConnection.dataChannels.values()) {
|
|
1011
|
-
if (!dataChannel.visited) {
|
|
1012
|
-
dataChannel.close();
|
|
1013
|
-
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
dataChannel.visited = false;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
peerConnection.visited = false;
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// update metrics
|
|
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;
|
|
391
|
+
this.numberOfIssues = 0;
|
|
1039
392
|
|
|
1040
|
-
this.
|
|
1041
|
-
this.
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
393
|
+
this.totalScoreSum = 0;
|
|
394
|
+
this.numberOfScoreMeasurements = 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
public createSummary(): ObservedClientSummary {
|
|
398
|
+
return {
|
|
399
|
+
totalRttLt50Measurements: this.totalRttLt50Measurements,
|
|
400
|
+
totalRttLt150Measurements: this.totalRttLt150Measurements,
|
|
401
|
+
totalRttLt300Measurements: this.totalRttLt300Measurements,
|
|
402
|
+
totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
|
|
403
|
+
totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
|
|
404
|
+
totalDataChannelBytesSent: this.totalDataChannelBytesSent,
|
|
405
|
+
totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
|
|
406
|
+
totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
|
|
407
|
+
totalInboundPacketsLost: this.totalInboundPacketsLost,
|
|
408
|
+
totalInboundPacketsReceived: this.totalInboundPacketsReceived,
|
|
409
|
+
totalOutboundPacketsSent: this.totalOutboundPacketsSent,
|
|
410
|
+
totalReceivedAudioBytes: this.totalReceivedAudioBytes,
|
|
411
|
+
totalReceivedVideoBytes: this.totalReceivedVideoBytes,
|
|
412
|
+
totalSentAudioBytes: this.totalSentAudioBytes,
|
|
413
|
+
totalSentVideoBytes: this.totalSentVideoBytes,
|
|
414
|
+
totalSentBytes: this.totalSentBytes,
|
|
415
|
+
totalReceivedBytes: this.totalReceivedBytes,
|
|
1055
416
|
|
|
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;
|
|
417
|
+
numberOfIssues: this.numberOfIssues,
|
|
1065
418
|
|
|
1066
|
-
this.
|
|
1067
|
-
this.
|
|
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,
|
|
419
|
+
totalScoreSum: this.totalScoreSum,
|
|
420
|
+
numberOfScoreMeasurements: this.numberOfScoreMeasurements,
|
|
1088
421
|
};
|
|
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
422
|
}
|
|
1124
423
|
|
|
1125
|
-
private
|
|
1126
|
-
this.
|
|
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);
|
|
424
|
+
private _updateRttMeasurements() {
|
|
425
|
+
if (this.deltaRttInMsMeasurements.length < 1) {
|
|
426
|
+
this.currentRttInMs = undefined;
|
|
1135
427
|
|
|
1136
|
-
|
|
1137
|
-
} else {
|
|
1138
|
-
peerConnection.ωpendingIssuesForScores.push(issue);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
428
|
+
return;
|
|
1141
429
|
}
|
|
430
|
+
this.currentRttInMs = getMedian(this.deltaRttInMsMeasurements, false);
|
|
1142
431
|
|
|
1143
|
-
this.
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
private _createPeerConnection(peerConnectionId: string, label?: string): ObservedPeerConnection {
|
|
1147
|
-
const result = new ObservedPeerConnection({
|
|
1148
|
-
peerConnectionId,
|
|
1149
|
-
label,
|
|
1150
|
-
}, this);
|
|
432
|
+
if (this.currentRttInMs < 0) {
|
|
433
|
+
logger.warn(`Current Rtt for client ${this.clientId} is smaller than 0`);
|
|
434
|
+
this.currentRttInMs = undefined;
|
|
1151
435
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
this.
|
|
1163
|
-
}
|
|
1164
|
-
result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
1165
|
-
this._peerConnections.set(peerConnectionId, result);
|
|
1166
|
-
|
|
1167
|
-
this.emit('newpeerconnection', result);
|
|
1168
|
-
|
|
1169
|
-
return result;
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (this.currentRttInMs < 50) {
|
|
440
|
+
this.deltaRttLt50Measurements += 1;
|
|
441
|
+
} else if (this.currentRttInMs < 150) {
|
|
442
|
+
this.deltaRttLt150Measurements += 1;
|
|
443
|
+
} else if (this.currentRttInMs < 300) {
|
|
444
|
+
this.deltaRttLt300Measurements += 1;
|
|
445
|
+
} else if (300 <= this.currentRttInMs) {
|
|
446
|
+
this.deltaRttGtOrEq300Measurements += 1;
|
|
447
|
+
}
|
|
1170
448
|
}
|
|
1171
|
-
}
|
|
449
|
+
}
|