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