@observertc/observer-js 0.42.9 → 1.0.1-23e88d8.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 +25 -41
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +61 -109
- package/lib/ObservedCertificate.d.ts +19 -0
- package/lib/ObservedCertificate.d.ts.map +1 -0
- package/lib/ObservedCertificate.js +38 -0
- package/lib/ObservedClient.d.ts +33 -143
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +156 -757
- 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 +20 -70
- package/lib/ObservedDataChannel.d.ts.map +1 -1
- package/lib/ObservedDataChannel.js +39 -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 +40 -0
- package/lib/ObservedIceCandidatePair.d.ts.map +1 -0
- package/lib/ObservedIceCandidatePair.js +83 -0
- package/lib/ObservedIceTransport.d.ts +32 -0
- package/lib/ObservedIceTransport.d.ts.map +1 -0
- package/lib/ObservedIceTransport.js +65 -0
- package/lib/ObservedInboundRtp.d.ts +92 -0
- package/lib/ObservedInboundRtp.d.ts.map +1 -0
- package/lib/ObservedInboundRtp.js +193 -0
- package/lib/ObservedInboundTrack.d.ts +29 -0
- package/lib/ObservedInboundTrack.d.ts.map +1 -0
- package/lib/ObservedInboundTrack.js +57 -0
- package/lib/ObservedMediaPlayout.d.ts +22 -0
- package/lib/ObservedMediaPlayout.d.ts.map +1 -0
- package/lib/ObservedMediaPlayout.js +42 -0
- package/lib/ObservedMediaSource.d.ts +28 -0
- package/lib/ObservedMediaSource.d.ts.map +1 -0
- package/lib/ObservedMediaSource.js +55 -0
- package/lib/ObservedOutboundRtp.d.ts +62 -0
- package/lib/ObservedOutboundRtp.d.ts.map +1 -0
- package/lib/ObservedOutboundRtp.js +144 -0
- package/lib/ObservedOutboundTrack.d.ts +29 -0
- package/lib/ObservedOutboundTrack.d.ts.map +1 -0
- package/lib/ObservedOutboundTrack.js +57 -0
- package/lib/ObservedPeerConnection.d.ts +118 -78
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +556 -359
- 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/Observer.d.ts +30 -53
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +52 -225
- package/lib/common/Middleware.d.ts +10 -1
- package/lib/common/Middleware.d.ts.map +1 -1
- package/lib/common/Middleware.js +55 -36
- package/lib/common/logger.d.ts +8 -4
- package/lib/common/logger.d.ts.map +1 -1
- package/lib/common/logger.js +44 -68
- package/lib/common/types.d.ts +0 -21
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js +1 -0
- package/lib/common/utils.d.ts +1 -0
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +13 -1
- package/lib/detectors/Detector.d.ts +5 -0
- package/lib/detectors/Detector.d.ts.map +1 -0
- package/lib/detectors/Detector.js +3 -0
- package/lib/detectors/Detectors.d.ts +11 -0
- package/lib/detectors/Detectors.d.ts.map +1 -0
- package/lib/detectors/Detectors.js +34 -0
- package/lib/index.d.ts +23 -25
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +41 -41
- package/lib/monitors/TurnUsageMonitor.d.ts +0 -52
- package/lib/monitors/TurnUsageMonitor.d.ts.map +1 -1
- package/lib/monitors/TurnUsageMonitor.js +145 -120
- package/lib/schema/ClientEventTypes.d.ts +33 -0
- package/lib/schema/ClientEventTypes.d.ts.map +1 -0
- package/lib/schema/ClientEventTypes.js +38 -0
- package/lib/schema/ClientMetaTypes.d.ts +34 -0
- package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
- package/lib/schema/ClientMetaTypes.js +16 -0
- package/lib/schema/ClientSample.d.ts +1333 -0
- package/lib/schema/ClientSample.d.ts.map +1 -0
- package/lib/schema/ClientSample.js +4 -0
- package/lib/scores/CalculatedScore.d.ts +6 -0
- package/lib/scores/CalculatedScore.d.ts.map +1 -0
- package/lib/scores/DefaultCallScoreCalculator.d.ts +7 -0
- package/lib/scores/DefaultCallScoreCalculator.d.ts.map +1 -0
- package/lib/scores/DefaultCallScoreCalculator.js +21 -0
- package/lib/scores/ScoreCalculator.d.ts +4 -0
- package/lib/scores/ScoreCalculator.d.ts.map +1 -0
- package/lib/scores/ScoreCalculator.js +2 -0
- package/lib/utils/CallUpdater.d.ts +5 -0
- package/lib/utils/CallUpdater.d.ts.map +1 -0
- package/lib/utils/CallUpdater.js +2 -0
- package/lib/utils/OnAllClientCallUpdater.d.ts +16 -0
- package/lib/utils/OnAllClientCallUpdater.d.ts.map +1 -0
- package/lib/utils/OnAllClientCallUpdater.js +58 -0
- package/lib/utils/RemoteTrackResolver.d.ts +7 -0
- package/lib/utils/RemoteTrackResolver.d.ts.map +1 -0
- package/lib/utils/RemoteTrackResolver.js +2 -0
- package/lib/utils/onAnyClientCallUpdater.d.ts +14 -0
- package/lib/utils/onAnyClientCallUpdater.d.ts.map +1 -0
- package/lib/utils/onAnyClientCallUpdater.js +51 -0
- package/package.json +4 -4
- package/src/ObservedCall.ts +74 -167
- package/src/ObservedCertificate.ts +43 -0
- package/src/ObservedClient.ts +173 -1085
- package/src/ObservedCodec.ts +49 -0
- package/src/ObservedDataChannel.ts +36 -178
- package/src/ObservedIceCandidate.ts +64 -0
- package/src/ObservedIceCandidatePair.ts +92 -0
- package/src/ObservedIceTransport.ts +70 -0
- package/src/ObservedInboundRtp.ts +205 -0
- package/src/ObservedInboundTrack.ts +70 -0
- package/src/ObservedMediaPlayout.ts +49 -0
- package/src/ObservedMediaSource.ts +60 -0
- package/src/ObservedOutboundRtp.ts +156 -0
- package/src/ObservedOutboundTrack.ts +69 -0
- package/src/ObservedPeerConnection.ts +743 -401
- package/src/ObservedPeerConnectionTransport.ts +38 -0
- package/src/ObservedRemoteInboundRtp.ts +64 -0
- package/src/ObservedRemoteOutboundRtp.ts +65 -0
- package/src/Observer.ts +65 -285
- package/src/common/Middleware.ts +70 -42
- package/src/common/logger.ts +60 -69
- package/src/common/types.ts +0 -42
- package/src/common/utils.ts +12 -0
- package/src/detectors/Detector.ts +6 -0
- package/src/detectors/Detectors.ts +38 -0
- package/src/index.ts +23 -90
- package/src/monitors/TurnUsageMonitor.ts +164 -164
- package/src/schema/ClientEventTypes.ts +36 -0
- package/src/schema/ClientMetaTypes.ts +41 -0
- package/src/schema/ClientSample.ts +1682 -0
- package/src/scores/CalculatedScore.ts +6 -0
- package/src/scores/DefaultCallScoreCalculator.ts +23 -0
- package/src/scores/ScoreCalculator.ts +3 -0
- package/src/utils/CallUpdater.ts +5 -0
- package/src/utils/OnAllClientCallUpdater.ts +68 -0
- package/src/utils/RemoteTrackResolver.ts +12 -0
- package/src/utils/onAnyClientCallUpdater.ts +59 -0
- package/tsconfig.json +3 -3
- package/lib/ObservedICE.d.ts +0 -64
- package/lib/ObservedICE.d.ts.map +0 -1
- package/lib/ObservedICE.js +0 -141
- package/lib/ObservedInboundAudioTrack.d.ts +0 -96
- package/lib/ObservedInboundAudioTrack.d.ts.map +0 -1
- package/lib/ObservedInboundAudioTrack.js +0 -217
- package/lib/ObservedInboundVideoTrack.d.ts +0 -101
- package/lib/ObservedInboundVideoTrack.d.ts.map +0 -1
- package/lib/ObservedInboundVideoTrack.js +0 -232
- package/lib/ObservedOutboundAudioTrack.d.ts +0 -87
- package/lib/ObservedOutboundAudioTrack.d.ts.map +0 -1
- package/lib/ObservedOutboundAudioTrack.js +0 -186
- package/lib/ObservedOutboundVideoTrack.d.ts +0 -91
- package/lib/ObservedOutboundVideoTrack.d.ts.map +0 -1
- package/lib/ObservedOutboundVideoTrack.js +0 -193
- package/lib/ObservedSfu.d.ts +0 -59
- package/lib/ObservedSfu.d.ts.map +0 -1
- package/lib/ObservedSfu.js +0 -157
- package/lib/ObservedSfuInboundRtpPad.d.ts +0 -42
- package/lib/ObservedSfuInboundRtpPad.d.ts.map +0 -1
- package/lib/ObservedSfuInboundRtpPad.js +0 -71
- package/lib/ObservedSfuOutboundRtpPad.d.ts +0 -42
- package/lib/ObservedSfuOutboundRtpPad.d.ts.map +0 -1
- package/lib/ObservedSfuOutboundRtpPad.js +0 -72
- package/lib/ObservedSfuSctpChannel.d.ts +0 -42
- package/lib/ObservedSfuSctpChannel.d.ts.map +0 -1
- package/lib/ObservedSfuSctpChannel.js +0 -57
- package/lib/ObservedSfuTransport.d.ts +0 -58
- package/lib/ObservedSfuTransport.d.ts.map +0 -1
- package/lib/ObservedSfuTransport.js +0 -120
- package/lib/RemoteTrackAssigner.d.ts +0 -14
- package/lib/RemoteTrackAssigner.d.ts.map +0 -1
- package/lib/RemoteTrackAssigner.js +0 -52
- package/lib/ReportsCollector.d.ts +0 -109
- package/lib/ReportsCollector.d.ts.map +0 -1
- package/lib/ReportsCollector.js +0 -207
- package/lib/common/CalculatedScore.d.ts +0 -80
- package/lib/common/CalculatedScore.d.ts.map +0 -1
- package/lib/common/CalculatedScore.js +0 -162
- package/lib/common/CallEventType.d.ts +0 -63
- package/lib/common/CallEventType.d.ts.map +0 -1
- package/lib/common/CallEventType.js +0 -39
- package/lib/common/CallMetaReports.d.ts +0 -42
- package/lib/common/CallMetaReports.d.ts.map +0 -1
- package/lib/common/CallMetaReports.js +0 -36
- package/lib/common/QualityScoreUtils.d.ts +0 -1
- package/lib/common/QualityScoreUtils.d.ts.map +0 -1
- package/lib/common/QualityScoreUtils.js +0 -1
- package/lib/common/TypedEventEmitter.d.ts +0 -22
- package/lib/common/TypedEventEmitter.d.ts.map +0 -1
- package/lib/common/TypedEventEmitter.js +0 -11
- package/lib/common/callEventReports.d.ts +0 -10
- package/lib/common/callEventReports.d.ts.map +0 -1
- package/lib/common/callEventReports.js +0 -112
- package/lib/monitors/CallSummary.d.ts +0 -37
- package/lib/monitors/CallSummary.d.ts.map +0 -1
- package/lib/monitors/CallSummaryMonitor.d.ts +0 -32
- package/lib/monitors/CallSummaryMonitor.d.ts.map +0 -1
- package/lib/monitors/CallSummaryMonitor.js +0 -152
- package/lib/monitors/ClientIssueMonitor.d.ts +0 -32
- package/lib/monitors/ClientIssueMonitor.d.ts.map +0 -1
- package/lib/monitors/ClientIssueMonitor.js +0 -49
- package/lib/monitors/SfuServerMonitor.d.ts +0 -79
- package/lib/monitors/SfuServerMonitor.d.ts.map +0 -1
- package/lib/monitors/SfuServerMonitor.js +0 -198
- package/src/ObservedICE.ts +0 -237
- package/src/ObservedInboundAudioTrack.ts +0 -344
- package/src/ObservedInboundVideoTrack.ts +0 -361
- package/src/ObservedOutboundAudioTrack.ts +0 -307
- package/src/ObservedOutboundVideoTrack.ts +0 -319
- package/src/ObservedSfu.ts +0 -231
- package/src/ObservedSfuInboundRtpPad.ts +0 -106
- package/src/ObservedSfuOutboundRtpPad.ts +0 -107
- package/src/ObservedSfuSctpChannel.ts +0 -103
- package/src/ObservedSfuTransport.ts +0 -185
- package/src/RemoteTrackAssigner.ts +0 -68
- package/src/ReportsCollector.ts +0 -336
- package/src/common/CalculatedScore.ts +0 -188
- package/src/common/CallEventType.ts +0 -69
- package/src/common/CallMetaReports.ts +0 -82
- package/src/common/QualityScoreUtils.ts +0 -0
- package/src/common/TypedEventEmitter.ts +0 -32
- package/src/common/callEventReports.ts +0 -178
- package/src/monitors/CallSummary.ts +0 -38
- package/src/monitors/CallSummaryMonitor.ts +0 -192
- package/src/monitors/ClientIssueMonitor.ts +0 -88
- package/src/monitors/SfuServerMonitor.ts +0 -285
- /package/lib/{monitors/CallSummary.js → scores/CalculatedScore.js} +0 -0
|
@@ -1,60 +1,111 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { PeerConnectionTransport } from '@observertc/sample-schemas-js';
|
|
3
2
|
import { ObservedClient } from './ObservedClient';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { CertificateStats, CodecStats, DataChannelStats, IceCandidateStats, InboundRtpStats, InboundTrackSample, MediaPlayoutStats, MediaSourceStats, OutboundRtpStats, OutboundTrackSample, PeerConnectionSample, PeerConnectionTransportStats, RemoteInboundRtpStats, RemoteOutboundRtpStats } from './schema/ClientSample';
|
|
4
|
+
import { ObservedInboundRtp } from './ObservedInboundRtp';
|
|
5
|
+
import { createLogger } from './common/logger';
|
|
6
|
+
import { MediaKind } from './common/types';
|
|
7
|
+
import { ObservedOutboundRtp } from './ObservedOutboundRtp';
|
|
8
|
+
import { ObservedCertificate } from './ObservedCertificate';
|
|
9
|
+
import { ObservedCodec } from './ObservedCodec';
|
|
6
10
|
import { ObservedDataChannel } from './ObservedDataChannel';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { ObservedIceCandidate } from './ObservedIceCandidate';
|
|
12
|
+
import { ObservedIceCandidatePair } from './ObservedIceCandidatePair';
|
|
13
|
+
import { ObservedIceTransport } from './ObservedIceTransport';
|
|
14
|
+
import { ObservedMediaSource } from './ObservedMediaSource';
|
|
15
|
+
import { ObservedPeerConnectionTransport } from './ObservedPeerConnectionTransport';
|
|
16
|
+
import { ObservedMediaPlayout } from './ObservedMediaPlayout';
|
|
17
|
+
import { ObservedRemoteInboundRtp } from './ObservedRemoteInboundRtp';
|
|
18
|
+
import { ObservedRemoteOutboundRtp } from './ObservedRemoteOutboundRtp';
|
|
19
|
+
import { ObservedInboundTrack } from './ObservedInboundTrack';
|
|
20
|
+
import { ObservedOutboundTrack } from './ObservedOutboundTrack';
|
|
21
|
+
import { CalculatedScore } from './scores/CalculatedScore';
|
|
22
|
+
|
|
23
|
+
const logger = createLogger('ObservedPeerConnection');
|
|
14
24
|
|
|
15
25
|
export type ObservedPeerConnectionEvents = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
iceconnectionstatechange: [
|
|
27
|
+
{
|
|
28
|
+
state: string;
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
icegatheringstatechange: [
|
|
32
|
+
{
|
|
33
|
+
state: string;
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
connectionstatechange: [
|
|
37
|
+
{
|
|
38
|
+
state: string;
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
selectedcandidatepair: [];
|
|
42
|
+
|
|
43
|
+
'added-certificate': [ObservedCertificate];
|
|
44
|
+
'added-codec': [ObservedCodec];
|
|
45
|
+
'added-data-channel': [ObservedDataChannel];
|
|
46
|
+
'added-ice-candidate': [ObservedIceCandidate];
|
|
47
|
+
'added-ice-candidate-pair': [ObservedIceCandidatePair];
|
|
48
|
+
'added-ice-transport': [ObservedIceTransport];
|
|
49
|
+
'added-inbound-rtp': [ObservedInboundRtp];
|
|
50
|
+
'added-inbound-track': [ObservedInboundTrack];
|
|
51
|
+
'added-media-playout': [ObservedMediaPlayout];
|
|
52
|
+
'added-media-source': [ObservedMediaSource];
|
|
53
|
+
'added-outbound-rtp': [ObservedOutboundRtp];
|
|
54
|
+
'added-outbound-track': [ObservedOutboundTrack];
|
|
55
|
+
'added-peer-connection-transport': [ObservedPeerConnectionTransport];
|
|
56
|
+
'added-remote-inbound-rtp': [ObservedRemoteInboundRtp];
|
|
57
|
+
'added-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
|
|
58
|
+
'removed-certificate': [ObservedCertificate];
|
|
59
|
+
'removed-codec': [ObservedCodec];
|
|
60
|
+
'removed-data-channel': [ObservedDataChannel];
|
|
61
|
+
'removed-ice-candidate': [ObservedIceCandidate];
|
|
62
|
+
'removed-ice-candidate-pair': [ObservedIceCandidatePair];
|
|
63
|
+
'removed-ice-transport': [ObservedIceTransport];
|
|
64
|
+
'removed-inbound-rtp': [ObservedInboundRtp];
|
|
65
|
+
'removed-inbound-track': [ObservedInboundTrack];
|
|
66
|
+
'removed-media-playout': [ObservedMediaPlayout];
|
|
67
|
+
'removed-media-source': [ObservedMediaSource];
|
|
68
|
+
'removed-outbound-rtp': [ObservedOutboundRtp];
|
|
69
|
+
'removed-outbound-track': [ObservedOutboundTrack];
|
|
70
|
+
'removed-peer-connection-transport': [ObservedPeerConnectionTransport];
|
|
71
|
+
'removed-remote-inbound-rtp': [ObservedRemoteInboundRtp];
|
|
72
|
+
'removed-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
|
|
73
|
+
|
|
74
|
+
close: [];
|
|
31
75
|
};
|
|
32
76
|
|
|
33
|
-
export type ObservedPeerConnectionStats = Omit<PeerConnectionTransport, 'transportId' | 'label'>;
|
|
34
|
-
|
|
35
77
|
export declare interface ObservedPeerConnection {
|
|
36
|
-
on<U extends keyof ObservedPeerConnectionEvents>(
|
|
37
|
-
|
|
38
|
-
|
|
78
|
+
on<U extends keyof ObservedPeerConnectionEvents>(
|
|
79
|
+
event: U,
|
|
80
|
+
listener: (...args: ObservedPeerConnectionEvents[U]) => void
|
|
81
|
+
): this;
|
|
82
|
+
off<U extends keyof ObservedPeerConnectionEvents>(
|
|
83
|
+
event: U,
|
|
84
|
+
listener: (...args: ObservedPeerConnectionEvents[U]) => void
|
|
85
|
+
): this;
|
|
86
|
+
once<U extends keyof ObservedPeerConnectionEvents>(
|
|
87
|
+
event: U,
|
|
88
|
+
listener: (...args: ObservedPeerConnectionEvents[U]) => void
|
|
89
|
+
): this;
|
|
39
90
|
emit<U extends keyof ObservedPeerConnectionEvents>(event: U, ...args: ObservedPeerConnectionEvents[U]): boolean;
|
|
40
91
|
}
|
|
41
92
|
|
|
42
93
|
export class ObservedPeerConnection extends EventEmitter {
|
|
43
|
-
|
|
44
|
-
public visited = true;
|
|
94
|
+
private _visited = true;
|
|
45
95
|
|
|
96
|
+
public appData?: Record<string, unknown>;
|
|
97
|
+
public readonly calculatedScore: CalculatedScore = {
|
|
98
|
+
weight: 1,
|
|
99
|
+
value: undefined,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
public closed = false;
|
|
46
103
|
// timestamp of the PEER_CONNECTION_OPENED event
|
|
47
|
-
public
|
|
104
|
+
public openedAt?: number;
|
|
48
105
|
// timestamp of the PEER_CONNECTION_CLOSED event
|
|
49
|
-
public
|
|
106
|
+
public closedAt?: number;
|
|
107
|
+
public updated = Date.now();
|
|
50
108
|
|
|
51
|
-
private _elapsedTimeSinceLastUpdate?: number;
|
|
52
|
-
private _statsTimestamp?: number;
|
|
53
|
-
private _stabilityScores: number[] = [];
|
|
54
|
-
|
|
55
|
-
public ωpendingIssuesForScores: ClientIssue[] = [];
|
|
56
|
-
|
|
57
|
-
public score?: CalculatedScore;
|
|
58
109
|
public totalInboundPacketsLost = 0;
|
|
59
110
|
public totalInboundPacketsReceived = 0;
|
|
60
111
|
public totalOutboundPacketsSent = 0;
|
|
@@ -77,180 +128,144 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
77
128
|
public deltaDataChannelBytesReceived = 0;
|
|
78
129
|
public deltaInboundReceivedBytes = 0;
|
|
79
130
|
public deltaOutboundSentBytes = 0;
|
|
80
|
-
|
|
131
|
+
|
|
81
132
|
public deltaReceivedAudioBytes = 0;
|
|
82
133
|
public deltaReceivedVideoBytes = 0;
|
|
83
134
|
public deltaReceivedAudioPackets = 0;
|
|
84
135
|
public deltaReceivedVideoPackets = 0;
|
|
85
136
|
public deltaSentAudioBytes = 0;
|
|
86
137
|
public deltaSentVideoBytes = 0;
|
|
138
|
+
|
|
87
139
|
public receivingPacketsPerSecond = 0;
|
|
88
140
|
public sendingPacketsPerSecond = 0;
|
|
89
141
|
public sendingAudioBitrate = 0;
|
|
90
142
|
public sendingVideoBitrate = 0;
|
|
91
143
|
public receivingAudioBitrate = 0;
|
|
92
144
|
public receivingVideoBitrate = 0;
|
|
93
|
-
|
|
145
|
+
|
|
94
146
|
public avgRttInMs?: number;
|
|
95
147
|
public avgJitter?: number;
|
|
96
148
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
public readonly
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
public
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
149
|
+
public usingTCP = false;
|
|
150
|
+
public usingTURN = false;
|
|
151
|
+
|
|
152
|
+
public readonly observedCertificates = new Map<string, ObservedCertificate>();
|
|
153
|
+
public readonly observedCodecs = new Map<string, ObservedCodec>();
|
|
154
|
+
public readonly observedDataChannels = new Map<string, ObservedDataChannel>();
|
|
155
|
+
public readonly observedIceCandidates = new Map<string, ObservedIceCandidate>();
|
|
156
|
+
public readonly observedIceCandidatesPair = new Map<string, ObservedIceCandidatePair>();
|
|
157
|
+
public readonly observedIceTransports = new Map<string, ObservedIceTransport>();
|
|
158
|
+
public readonly observedInboundRtps = new Map<number, ObservedInboundRtp>();
|
|
159
|
+
public readonly observedInboundTracks = new Map<string, ObservedInboundTrack>();
|
|
160
|
+
public readonly observedMediaPlayouts = new Map<string, ObservedMediaPlayout>();
|
|
161
|
+
public readonly observedMediaSources = new Map<string, ObservedMediaSource>();
|
|
162
|
+
public readonly observedOutboundRtps = new Map<number, ObservedOutboundRtp>();
|
|
163
|
+
public readonly observedOutboundTracks = new Map<string, ObservedOutboundTrack>();
|
|
164
|
+
public readonly observedPeerConnectionTransports = new Map<string, ObservedPeerConnectionTransport>();
|
|
165
|
+
public readonly observedRemoteInboundRtps = new Map<number, ObservedRemoteInboundRtp>();
|
|
166
|
+
public readonly observedRemoteOutboundRtps = new Map<number, ObservedRemoteOutboundRtp>();
|
|
167
|
+
|
|
168
|
+
public constructor(public readonly peerConnectionId: string, public readonly client: ObservedClient) {
|
|
113
169
|
super();
|
|
114
170
|
this.setMaxListeners(Infinity);
|
|
115
171
|
}
|
|
116
172
|
|
|
117
|
-
public get
|
|
118
|
-
return this.
|
|
173
|
+
public get score() {
|
|
174
|
+
return this.calculatedScore.value;
|
|
119
175
|
}
|
|
120
176
|
|
|
121
|
-
public get
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
public get callId() {
|
|
126
|
-
return this.client.callId;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public get clientId() {
|
|
130
|
-
return this.client.clientId;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
public get mediaUnitId() {
|
|
134
|
-
return this.client.mediaUnitId;
|
|
135
|
-
}
|
|
177
|
+
public get visited() {
|
|
178
|
+
const visited = this._visited;
|
|
136
179
|
|
|
137
|
-
|
|
138
|
-
return this._model.label;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public set label(value: string | undefined) {
|
|
142
|
-
this._model.label = value;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public get usingTURN() {
|
|
146
|
-
return this.ICE.usingTURN;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public get availableOutgoingBitrate() {
|
|
150
|
-
return this.ICE.stats?.availableOutgoingBitrate;
|
|
151
|
-
}
|
|
180
|
+
this._visited = false;
|
|
152
181
|
|
|
153
|
-
|
|
154
|
-
return this.ICE.stats?.availableIncomingBitrate;
|
|
182
|
+
return visited;
|
|
155
183
|
}
|
|
156
184
|
|
|
157
|
-
public get
|
|
158
|
-
|
|
159
|
-
return this._model.peerConnectionId!;
|
|
185
|
+
public get codecs() {
|
|
186
|
+
return [ ...this.observedCodecs.values() ];
|
|
160
187
|
}
|
|
161
188
|
|
|
162
|
-
public get
|
|
163
|
-
return this.
|
|
189
|
+
public get inboundRtps() {
|
|
190
|
+
return [ ...this.observedInboundRtps.values() ];
|
|
164
191
|
}
|
|
165
192
|
|
|
166
|
-
public get
|
|
167
|
-
return this.
|
|
193
|
+
public get remoteOutboundRtps() {
|
|
194
|
+
return [ ...this.observedRemoteOutboundRtps.values() ];
|
|
168
195
|
}
|
|
169
196
|
|
|
170
|
-
public get
|
|
171
|
-
return this.
|
|
197
|
+
public get outboundRtps() {
|
|
198
|
+
return [ ...this.observedOutboundRtps.values() ];
|
|
172
199
|
}
|
|
173
200
|
|
|
174
|
-
public get
|
|
175
|
-
return this.
|
|
201
|
+
public get remoteInboundRtps() {
|
|
202
|
+
return [ ...this.observedRemoteInboundRtps.values() ];
|
|
176
203
|
}
|
|
177
204
|
|
|
178
|
-
public get
|
|
179
|
-
return this.
|
|
205
|
+
public get mediaSources() {
|
|
206
|
+
return [ ...this.observedMediaSources.values() ];
|
|
180
207
|
}
|
|
181
208
|
|
|
182
|
-
public get
|
|
183
|
-
return this.
|
|
209
|
+
public get mediaPlayouts() {
|
|
210
|
+
return [ ...this.observedMediaPlayouts.values() ];
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
public get
|
|
187
|
-
return this.
|
|
213
|
+
public get dataChannels() {
|
|
214
|
+
return [ ...this.observedDataChannels.values() ];
|
|
188
215
|
}
|
|
189
216
|
|
|
190
|
-
public get
|
|
191
|
-
return this.
|
|
217
|
+
public get peerConnectionTransports() {
|
|
218
|
+
return [ ...this.observedPeerConnectionTransports.values() ];
|
|
192
219
|
}
|
|
193
220
|
|
|
194
|
-
public get
|
|
195
|
-
return this.
|
|
221
|
+
public get iceTransports() {
|
|
222
|
+
return [ ...this.observedIceTransports.values() ];
|
|
196
223
|
}
|
|
197
224
|
|
|
198
|
-
public get
|
|
199
|
-
return this.
|
|
225
|
+
public get iceCandidates() {
|
|
226
|
+
return [ ...this.observedIceCandidates.values() ];
|
|
200
227
|
}
|
|
201
228
|
|
|
202
|
-
public
|
|
203
|
-
this.
|
|
204
|
-
this._inboundAudioTracks.forEach((track) => (track.marker = value));
|
|
205
|
-
this._inboundVideoTracks.forEach((track) => (track.marker = value));
|
|
206
|
-
this._outboundAudioTracks.forEach((track) => (track.marker = value));
|
|
207
|
-
this._outboundVideoTracks.forEach((track) => (track.marker = value));
|
|
208
|
-
this._dataChannels.forEach((channel) => (channel.marker = value));
|
|
209
|
-
this.ICE.marker = value;
|
|
229
|
+
public get iceCandidatePairs() {
|
|
230
|
+
return [ ...this.observedIceCandidatesPair.values() ];
|
|
210
231
|
}
|
|
211
232
|
|
|
212
|
-
public
|
|
213
|
-
this.
|
|
214
|
-
...params,
|
|
215
|
-
serviceId: this.serviceId,
|
|
216
|
-
mediaUnitId: this.mediaUnitId,
|
|
217
|
-
roomId: this.roomId,
|
|
218
|
-
callId: this.callId,
|
|
219
|
-
clientId: this.clientId,
|
|
220
|
-
userId: this.client.userId,
|
|
221
|
-
peerConnectionId: this.peerConnectionId,
|
|
222
|
-
marker: this.client.marker,
|
|
223
|
-
timestamp: params.timestamp ?? Date.now(),
|
|
224
|
-
});
|
|
233
|
+
public get certificates() {
|
|
234
|
+
return [ ...this.observedCertificates.values() ];
|
|
225
235
|
}
|
|
226
236
|
|
|
227
|
-
public get
|
|
228
|
-
return this.
|
|
237
|
+
public get selectedIceCandidatePairs() {
|
|
238
|
+
return this.iceTransports.map((iceTransport) => iceTransport.getSelectedCandidatePair())
|
|
239
|
+
.filter((pair) => pair !== undefined) as ObservedIceCandidatePair[];
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
public close() {
|
|
232
|
-
if (this.
|
|
233
|
-
this.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
if (this.closed) return;
|
|
244
|
+
this.closed = true;
|
|
245
|
+
|
|
246
|
+
this.observedCertificates.clear();
|
|
247
|
+
this.observedCodecs.clear();
|
|
248
|
+
this.observedDataChannels.clear();
|
|
249
|
+
this.observedIceCandidates.clear();
|
|
250
|
+
this.observedIceCandidatesPair.clear();
|
|
251
|
+
this.observedIceTransports.clear();
|
|
252
|
+
this.observedInboundRtps.clear();
|
|
253
|
+
this.observedInboundTracks.clear();
|
|
254
|
+
this.observedMediaPlayouts.clear();
|
|
255
|
+
this.observedMediaSources.clear();
|
|
256
|
+
this.observedOutboundRtps.clear();
|
|
257
|
+
this.observedOutboundTracks.clear();
|
|
258
|
+
this.observedPeerConnectionTransports.clear();
|
|
259
|
+
this.observedRemoteInboundRtps.clear();
|
|
260
|
+
this.observedRemoteOutboundRtps.clear();
|
|
245
261
|
|
|
246
262
|
this.emit('close');
|
|
247
263
|
}
|
|
248
264
|
|
|
249
|
-
public
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
public accept(sample: PeerConnectionSample) {
|
|
266
|
+
if (this.closed) return;
|
|
267
|
+
this._visited = true;
|
|
252
268
|
|
|
253
|
-
public resetMetrics() {
|
|
254
269
|
this.deltaInboundPacketsLost = 0;
|
|
255
270
|
this.deltaInboundPacketsReceived = 0;
|
|
256
271
|
this.deltaOutboundPacketsSent = 0;
|
|
@@ -258,7 +273,6 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
258
273
|
this.deltaDataChannelBytesReceived = 0;
|
|
259
274
|
this.deltaInboundReceivedBytes = 0;
|
|
260
275
|
this.deltaOutboundSentBytes = 0;
|
|
261
|
-
|
|
262
276
|
this.deltaReceivedAudioBytes = 0;
|
|
263
277
|
this.deltaReceivedVideoBytes = 0;
|
|
264
278
|
this.deltaReceivedAudioPackets = 0;
|
|
@@ -270,304 +284,632 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
270
284
|
this.receivingAudioBitrate = 0;
|
|
271
285
|
this.receivingVideoBitrate = 0;
|
|
272
286
|
|
|
273
|
-
|
|
287
|
+
const now = Date.now();
|
|
288
|
+
const elapsedTimeInMs = now - this.updated;
|
|
289
|
+
const elapsedTimeInSec = elapsedTimeInMs / 1000;
|
|
290
|
+
|
|
291
|
+
if (sample.certificates) {
|
|
292
|
+
for (const certificate of sample.certificates) {
|
|
293
|
+
this._updateCertificateStats(certificate);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (sample.codecs) {
|
|
297
|
+
for (const codec of sample.codecs) {
|
|
298
|
+
this._updateCodecStats(codec);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (sample.dataChannels) {
|
|
302
|
+
for (const dataChannel of sample.dataChannels) {
|
|
303
|
+
this._updateDataChannelStats(dataChannel);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (sample.iceCandidates) {
|
|
307
|
+
for (const iceCandidate of sample.iceCandidates) {
|
|
308
|
+
this._updateIceCandidateStats(iceCandidate);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (sample.iceCandidatePairs) {
|
|
312
|
+
for (const iceCandidatePair of sample.iceCandidatePairs) {
|
|
313
|
+
this._updateIceCandidatePairStats(iceCandidatePair);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (sample.iceTransports) {
|
|
317
|
+
for (const iceTransport of sample.iceTransports) {
|
|
318
|
+
this._updateIceTransportStats(iceTransport);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (sample.inboundRtps) {
|
|
322
|
+
for (const inboundRtp of sample.inboundRtps) {
|
|
323
|
+
const observedInboundRtp = this._updateInboundRtpStats(inboundRtp);
|
|
324
|
+
|
|
325
|
+
if (!observedInboundRtp) continue;
|
|
326
|
+
|
|
327
|
+
this.deltaInboundPacketsLost += observedInboundRtp.deltaLostPackets;
|
|
328
|
+
this.deltaInboundPacketsReceived += observedInboundRtp.deltaReceivedPackets;
|
|
329
|
+
this.deltaInboundReceivedBytes += observedInboundRtp.deltaBytesReceived;
|
|
330
|
+
|
|
331
|
+
switch (inboundRtp.kind) {
|
|
332
|
+
case 'audio':
|
|
333
|
+
this.deltaReceivedAudioBytes += observedInboundRtp.deltaBytesReceived;
|
|
334
|
+
this.deltaReceivedAudioPackets += observedInboundRtp.deltaReceivedPackets;
|
|
335
|
+
break;
|
|
336
|
+
case 'video':
|
|
337
|
+
this.deltaReceivedVideoBytes += observedInboundRtp.deltaBytesReceived;
|
|
338
|
+
this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (sample.mediaPlayouts) {
|
|
344
|
+
for (const mediaPlayout of sample.mediaPlayouts) {
|
|
345
|
+
this._updateMediaPlayoutStats(mediaPlayout);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (sample.mediaSources) {
|
|
349
|
+
for (const mediaSource of sample.mediaSources) {
|
|
350
|
+
this._updateMediaSourceStats(mediaSource);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (sample.outboundRtps) {
|
|
354
|
+
for (const outboundRtp of sample.outboundRtps) {
|
|
355
|
+
const observedOutboundRtp = this._updateOutboundRtpStats(outboundRtp);
|
|
356
|
+
|
|
357
|
+
if (!observedOutboundRtp) continue;
|
|
358
|
+
|
|
359
|
+
this.deltaOutboundPacketsSent += observedOutboundRtp.deltaPacketsSent ?? 0;
|
|
360
|
+
this.deltaOutboundSentBytes += observedOutboundRtp.deltaBytesSent ?? 0;
|
|
361
|
+
|
|
362
|
+
switch (outboundRtp.kind) {
|
|
363
|
+
case 'audio':
|
|
364
|
+
this.deltaSentAudioBytes += observedOutboundRtp.deltaBytesSent;
|
|
365
|
+
this.deltaSentAudioBytes += observedOutboundRtp.deltaPacketsSent;
|
|
366
|
+
break;
|
|
367
|
+
case 'video':
|
|
368
|
+
this.deltaSentVideoBytes += observedOutboundRtp.deltaBytesSent;
|
|
369
|
+
this.deltaSentVideoBytes += observedOutboundRtp.deltaPacketsSent;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (sample.peerConnectionTransports) {
|
|
376
|
+
for (const peerConnectionTransport of sample.peerConnectionTransports) {
|
|
377
|
+
this._updatePeerConnectionTransportStats(peerConnectionTransport);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (sample.remoteInboundRtps) {
|
|
381
|
+
for (const remoteInboundRtp of sample.remoteInboundRtps) {
|
|
382
|
+
this._updateRemoteInboundRtpStats(remoteInboundRtp);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (sample.remoteOutboundRtps) {
|
|
386
|
+
for (const remoteOutboundRtp of sample.remoteOutboundRtps) {
|
|
387
|
+
const observedRemoteOutboundRtp = this._updateRemoteOutboundRtpStats(remoteOutboundRtp);
|
|
388
|
+
|
|
389
|
+
if (!observedRemoteOutboundRtp) continue;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// tracks should be updated last as they are derived stats
|
|
394
|
+
// and depends on base stats but they all received in the sample sample
|
|
395
|
+
if (sample.inboundTracks) {
|
|
396
|
+
for (const inboundTrack of sample.inboundTracks) {
|
|
397
|
+
this._updateInboundTrackSample(inboundTrack);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (sample.outboundTracks) {
|
|
401
|
+
for (const outboundTrack of sample.outboundTracks) {
|
|
402
|
+
this._updateOutboundTrackSample(outboundTrack);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
|
|
407
|
+
this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
|
|
408
|
+
this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
|
|
409
|
+
this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
|
|
410
|
+
this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
|
|
411
|
+
this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
|
|
412
|
+
this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
|
|
413
|
+
this.totalSentAudioBytes += this.deltaSentAudioBytes;
|
|
414
|
+
this.totalSentVideoBytes += this.deltaSentVideoBytes;
|
|
415
|
+
this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets;
|
|
416
|
+
this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets;
|
|
417
|
+
this.totalSentAudioPackets += this.deltaSentAudioBytes;
|
|
418
|
+
this.totalSentVideoPackets += this.deltaSentVideoBytes;
|
|
419
|
+
|
|
420
|
+
this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
|
|
421
|
+
this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec;
|
|
422
|
+
this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedTimeInSec;
|
|
423
|
+
this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedTimeInSec;
|
|
424
|
+
this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedTimeInSec;
|
|
425
|
+
this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedTimeInSec;
|
|
426
|
+
|
|
427
|
+
const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
|
|
428
|
+
|
|
429
|
+
this.usingTCP = selectedIceCandidatePairs.some((pair) => pair.getLocalCandidate()?.protocol === 'tcp');
|
|
430
|
+
this.usingTURN = selectedIceCandidatePairs.some((pair) => pair.getLocalCandidate()?.candidateType === 'relay') &&
|
|
431
|
+
selectedIceCandidatePairs.some((pair) => pair.getRemoteCandidate()?.url?.startsWith('turn:'));
|
|
432
|
+
|
|
433
|
+
this.calculatedScore.value = sample.score;
|
|
434
|
+
this.updated = now;
|
|
435
|
+
this._checkVisited();
|
|
274
436
|
}
|
|
275
437
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
438
|
+
private _checkVisited() {
|
|
439
|
+
for (const certificate of [ ...this.observedCertificates.values() ]) {
|
|
440
|
+
if (certificate.visited) continue;
|
|
279
441
|
|
|
280
|
-
|
|
281
|
-
if (this._model.label !== sample.label) {
|
|
282
|
-
this._model.label = sample.label;
|
|
442
|
+
this.observedCertificates.delete(certificate.id);
|
|
283
443
|
}
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
this.reports.addPeerConnectionTransportReports(report);
|
|
299
|
-
this._elapsedTimeSinceLastUpdate = now - this._updated;
|
|
300
|
-
this.visited = true;
|
|
301
|
-
this._updated = now;
|
|
302
|
-
this._statsTimestamp = timestamp;
|
|
303
|
-
this.emit('update', {
|
|
304
|
-
elapsedTimeInMs: this._elapsedTimeSinceLastUpdate,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
public createInboundAudioTrack(config: ObservedInboundAudioTrackModel): ObservedInboundAudioTrack {
|
|
309
|
-
if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
|
|
310
|
-
|
|
311
|
-
const result = new ObservedInboundAudioTrack(config, this);
|
|
444
|
+
for (const codec of [ ...this.observedCodecs.values() ]) {
|
|
445
|
+
if (codec.visited) continue;
|
|
446
|
+
|
|
447
|
+
this.observedCodecs.delete(codec.id);
|
|
448
|
+
this.emit('removed-codec', codec);
|
|
449
|
+
}
|
|
450
|
+
for (const dataChannel of [ ...this.observedDataChannels.values() ]) {
|
|
451
|
+
if (dataChannel.visited) continue;
|
|
452
|
+
|
|
453
|
+
this.observedDataChannels.delete(dataChannel.id);
|
|
454
|
+
this.emit('removed-data-channel', dataChannel);
|
|
455
|
+
}
|
|
456
|
+
for (const iceCandidate of [ ...this.observedIceCandidates.values() ]) {
|
|
457
|
+
if (iceCandidate.visited) continue;
|
|
312
458
|
|
|
313
|
-
|
|
314
|
-
this.
|
|
315
|
-
}
|
|
316
|
-
this.
|
|
459
|
+
this.observedIceCandidates.delete(iceCandidate.id);
|
|
460
|
+
this.emit('removed-ice-candidate', iceCandidate);
|
|
461
|
+
}
|
|
462
|
+
for (const iceCandidatePair of [ ...this.observedIceCandidatesPair.values() ]) {
|
|
463
|
+
if (iceCandidatePair.visited) continue;
|
|
464
|
+
|
|
465
|
+
this.observedIceCandidatesPair.delete(iceCandidatePair.id);
|
|
466
|
+
this.emit('removed-ice-candidate-pair', iceCandidatePair);
|
|
467
|
+
}
|
|
468
|
+
for (const iceTransport of [ ...this.observedIceTransports.values() ]) {
|
|
469
|
+
if (iceTransport.visited) continue;
|
|
470
|
+
|
|
471
|
+
this.observedIceTransports.delete(iceTransport.id);
|
|
472
|
+
this.emit('removed-ice-transport', iceTransport);
|
|
473
|
+
}
|
|
474
|
+
for (const inboundRtp of [ ...this.observedInboundRtps.values() ]) {
|
|
475
|
+
if (inboundRtp.visited) continue;
|
|
476
|
+
|
|
477
|
+
this.observedInboundRtps.delete(inboundRtp.ssrc);
|
|
478
|
+
this.emit('removed-inbound-rtp', inboundRtp);
|
|
479
|
+
}
|
|
480
|
+
for (const inboundTrack of [ ...this.observedInboundTracks.values() ]) {
|
|
481
|
+
if (inboundTrack.visited) continue;
|
|
317
482
|
|
|
318
|
-
|
|
483
|
+
this.observedInboundTracks.delete(inboundTrack.id);
|
|
484
|
+
this.emit('removed-inbound-track', inboundTrack);
|
|
485
|
+
}
|
|
486
|
+
for (const mediaPlayout of [ ...this.observedMediaPlayouts.values() ]) {
|
|
487
|
+
if (mediaPlayout.visited) continue;
|
|
488
|
+
|
|
489
|
+
this.observedMediaPlayouts.delete(mediaPlayout.id);
|
|
490
|
+
this.emit('removed-media-playout', mediaPlayout);
|
|
491
|
+
}
|
|
492
|
+
for (const mediaSource of [ ...this.observedMediaSources.values() ]) {
|
|
493
|
+
if (mediaSource.visited) continue;
|
|
494
|
+
|
|
495
|
+
this.observedMediaSources.delete(mediaSource.id);
|
|
496
|
+
this.emit('removed-media-source', mediaSource);
|
|
497
|
+
}
|
|
498
|
+
for (const outboundRtp of [ ...this.observedOutboundRtps.values() ]) {
|
|
499
|
+
if (outboundRtp.visited) continue;
|
|
500
|
+
|
|
501
|
+
this.observedOutboundRtps.delete(outboundRtp.ssrc);
|
|
502
|
+
this.emit('removed-outbound-rtp', outboundRtp);
|
|
503
|
+
}
|
|
504
|
+
for (const outboundTrack of [ ...this.observedOutboundTracks.values() ]) {
|
|
505
|
+
if (outboundTrack.visited) continue;
|
|
506
|
+
|
|
507
|
+
this.observedOutboundTracks.delete(outboundTrack.id);
|
|
508
|
+
this.emit('removed-outbound-track', outboundTrack);
|
|
509
|
+
}
|
|
510
|
+
for (const peerConnectionTransport of [ ...this.observedPeerConnectionTransports.values() ]) {
|
|
511
|
+
if (peerConnectionTransport.visited) continue;
|
|
512
|
+
|
|
513
|
+
this.observedPeerConnectionTransports.delete(peerConnectionTransport.id);
|
|
514
|
+
this.emit('removed-peer-connection-transport', peerConnectionTransport);
|
|
515
|
+
}
|
|
516
|
+
for (const remoteInboundRtp of [ ...this.observedRemoteInboundRtps.values() ]) {
|
|
517
|
+
if (remoteInboundRtp.visited) continue;
|
|
518
|
+
|
|
519
|
+
this.observedRemoteInboundRtps.delete(remoteInboundRtp.ssrc);
|
|
520
|
+
this.emit('removed-remote-inbound-rtp', remoteInboundRtp);
|
|
521
|
+
}
|
|
522
|
+
for (const remoteOutboundRtp of [ ...this.observedRemoteOutboundRtps.values() ]) {
|
|
523
|
+
if (remoteOutboundRtp.visited) continue;
|
|
319
524
|
|
|
320
|
-
|
|
525
|
+
this.observedRemoteOutboundRtps.delete(remoteOutboundRtp.ssrc);
|
|
526
|
+
this.emit('removed-remote-outbound-rtp', remoteOutboundRtp);
|
|
527
|
+
}
|
|
321
528
|
}
|
|
322
529
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const result = new ObservedInboundVideoTrack(config, this);
|
|
530
|
+
private _updateCertificateStats(stats: CertificateStats) {
|
|
531
|
+
let observedCertificate = this.observedCertificates.get(stats.id);
|
|
327
532
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
533
|
+
if (!observedCertificate) {
|
|
534
|
+
if (!stats.timestamp || !stats.id || !stats.fingerprint) {
|
|
535
|
+
return logger.warn(
|
|
536
|
+
`ObservedPeerConnection received an invalid CertificateStats (missing timestamp OR id OR fingerprint field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
537
|
+
stats
|
|
538
|
+
);
|
|
539
|
+
}
|
|
332
540
|
|
|
333
|
-
|
|
541
|
+
observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
|
|
334
542
|
|
|
335
|
-
|
|
543
|
+
this.observedCertificates.set(stats.id, observedCertificate);
|
|
544
|
+
this.emit('added-certificate', observedCertificate);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
observedCertificate.update(stats);
|
|
548
|
+
|
|
549
|
+
return observedCertificate;
|
|
336
550
|
}
|
|
337
551
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const result = new ObservedOutboundAudioTrack(config, this);
|
|
552
|
+
private _updateCodecStats(stats: CodecStats) {
|
|
553
|
+
let observedCodec = this.observedCodecs.get(stats.id);
|
|
342
554
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
555
|
+
if (!observedCodec) {
|
|
556
|
+
if (!stats.timestamp || !stats.id || !stats.mimeType) {
|
|
557
|
+
return logger.warn(
|
|
558
|
+
`ObservedPeerConnection received an invalid CodecStats (missing timestamp OR id OR mimeType field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
559
|
+
stats
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
|
|
564
|
+
|
|
565
|
+
this.observedCodecs.set(stats.id, observedCodec);
|
|
566
|
+
this.emit('added-codec', observedCodec);
|
|
567
|
+
}
|
|
347
568
|
|
|
348
|
-
|
|
569
|
+
observedCodec.update(stats);
|
|
349
570
|
|
|
350
|
-
return
|
|
571
|
+
return observedCodec;
|
|
351
572
|
}
|
|
352
573
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
574
|
+
private _updateDataChannelStats(stats: DataChannelStats) {
|
|
575
|
+
let observedDataChannel = this.observedDataChannels.get(stats.id);
|
|
576
|
+
|
|
577
|
+
if (!observedDataChannel) {
|
|
578
|
+
if (!stats.timestamp || !stats.id) {
|
|
579
|
+
return logger.warn(
|
|
580
|
+
`ObservedPeerConnection received an invalid DataChannelStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
581
|
+
stats
|
|
582
|
+
);
|
|
583
|
+
}
|
|
357
584
|
|
|
358
|
-
|
|
359
|
-
this._outboundVideoTracks.delete(result.trackId);
|
|
360
|
-
});
|
|
361
|
-
this._outboundVideoTracks.set(result.trackId, result);
|
|
585
|
+
observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
|
|
362
586
|
|
|
363
|
-
|
|
587
|
+
this.observedDataChannels.set(stats.id, observedDataChannel);
|
|
588
|
+
this.emit('added-data-channel', observedDataChannel);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
observedDataChannel.update(stats);
|
|
364
592
|
|
|
365
|
-
return
|
|
593
|
+
return observedDataChannel;
|
|
366
594
|
}
|
|
367
595
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const result = new ObservedDataChannel({
|
|
372
|
-
channelId,
|
|
373
|
-
}, this);
|
|
596
|
+
private _updateIceCandidateStats(stats: IceCandidateStats) {
|
|
597
|
+
let observedIceCandidate = this.observedIceCandidates.get(stats.id);
|
|
374
598
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
599
|
+
if (!observedIceCandidate) {
|
|
600
|
+
if (!stats.timestamp || !stats.id) {
|
|
601
|
+
return logger.warn(
|
|
602
|
+
`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
603
|
+
stats
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
|
|
608
|
+
|
|
609
|
+
this.observedIceCandidates.set(stats.id, observedIceCandidate);
|
|
610
|
+
this.emit('added-ice-candidate', observedIceCandidate);
|
|
611
|
+
}
|
|
379
612
|
|
|
380
|
-
|
|
613
|
+
observedIceCandidate.update(stats);
|
|
381
614
|
|
|
382
|
-
return
|
|
615
|
+
return observedIceCandidate;
|
|
383
616
|
}
|
|
384
617
|
|
|
385
|
-
|
|
386
|
-
let
|
|
387
|
-
let sumJitter = 0;
|
|
388
|
-
const trackScores: CalculatedScore[] = [];
|
|
618
|
+
private _updateIceCandidatePairStats(stats: IceCandidateStats) {
|
|
619
|
+
let observedIceCandidatePair = this.observedIceCandidatesPair.get(stats.id);
|
|
389
620
|
|
|
390
|
-
|
|
391
|
-
|
|
621
|
+
if (!observedIceCandidatePair) {
|
|
622
|
+
if (!stats.timestamp || !stats.id) {
|
|
623
|
+
return logger.warn(
|
|
624
|
+
`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
625
|
+
stats
|
|
626
|
+
);
|
|
627
|
+
}
|
|
392
628
|
|
|
393
|
-
|
|
394
|
-
this.deltaInboundPacketsReceived += track.deltaReceivedPackets;
|
|
395
|
-
this.deltaInboundReceivedBytes += track.deltaBytesReceived;
|
|
396
|
-
|
|
397
|
-
this.deltaReceivedAudioBytes += track.deltaBytesReceived;
|
|
398
|
-
this.deltaReceivedAudioPackets += track.deltaReceivedPackets;
|
|
399
|
-
|
|
400
|
-
this.receivingAudioBitrate += track.bitrate;
|
|
629
|
+
observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
|
|
401
630
|
|
|
402
|
-
|
|
403
|
-
|
|
631
|
+
this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
|
|
632
|
+
this.emit('added-ice-candidate-pair', observedIceCandidatePair);
|
|
633
|
+
}
|
|
404
634
|
|
|
405
|
-
|
|
406
|
-
});
|
|
635
|
+
observedIceCandidatePair.update(stats);
|
|
407
636
|
|
|
408
|
-
|
|
409
|
-
|
|
637
|
+
return observedIceCandidatePair;
|
|
638
|
+
}
|
|
410
639
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.deltaInboundReceivedBytes += track.deltaBytesReceived;
|
|
414
|
-
|
|
415
|
-
this.deltaReceivedVideoBytes += track.deltaBytesReceived;
|
|
416
|
-
this.deltaReceivedVideoPackets += track.deltaReceivedPackets;
|
|
417
|
-
|
|
418
|
-
this.receivingVideoBitrate += track.bitrate;
|
|
640
|
+
private _updateIceTransportStats(stats: IceCandidateStats) {
|
|
641
|
+
let observedIceTransport = this.observedIceTransports.get(stats.id);
|
|
419
642
|
|
|
420
|
-
|
|
421
|
-
|
|
643
|
+
if (!observedIceTransport) {
|
|
644
|
+
if (!stats.timestamp || !stats.id) {
|
|
645
|
+
return logger.warn(
|
|
646
|
+
`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
647
|
+
stats
|
|
648
|
+
);
|
|
649
|
+
}
|
|
422
650
|
|
|
423
|
-
|
|
424
|
-
});
|
|
651
|
+
observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
|
|
425
652
|
|
|
426
|
-
|
|
427
|
-
|
|
653
|
+
this.observedIceTransports.set(stats.id, observedIceTransport);
|
|
654
|
+
this.emit('added-ice-transport', observedIceTransport);
|
|
655
|
+
}
|
|
428
656
|
|
|
429
|
-
|
|
430
|
-
this.deltaOutboundSentBytes += track.deltaSentBytes;
|
|
431
|
-
|
|
432
|
-
this.deltaSentAudioBytes += track.deltaSentBytes;
|
|
433
|
-
this.deltaSentAudioBytes += track.deltaSentPackets;
|
|
434
|
-
|
|
435
|
-
this.sendingAudioBitrate += track.bitrate;
|
|
657
|
+
observedIceTransport.update(stats);
|
|
436
658
|
|
|
437
|
-
|
|
438
|
-
|
|
659
|
+
return observedIceTransport;
|
|
660
|
+
}
|
|
439
661
|
|
|
440
|
-
|
|
441
|
-
|
|
662
|
+
private _updateInboundRtpStats(stats: InboundRtpStats) {
|
|
663
|
+
let observedInboundRtp = this.observedInboundRtps.get(stats.ssrc);
|
|
442
664
|
|
|
443
|
-
|
|
444
|
-
|
|
665
|
+
if (!observedInboundRtp) {
|
|
666
|
+
if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind || !stats.trackIdentifier) {
|
|
667
|
+
return logger.warn(
|
|
668
|
+
`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR ssrc OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
669
|
+
stats
|
|
670
|
+
);
|
|
671
|
+
}
|
|
445
672
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
673
|
+
observedInboundRtp = new ObservedInboundRtp(
|
|
674
|
+
stats.timestamp,
|
|
675
|
+
stats.id,
|
|
676
|
+
stats.ssrc,
|
|
677
|
+
stats.kind as MediaKind,
|
|
678
|
+
stats.trackIdentifier,
|
|
679
|
+
this
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
|
|
683
|
+
this.emit('added-inbound-rtp', observedInboundRtp);
|
|
684
|
+
}
|
|
453
685
|
|
|
454
|
-
|
|
455
|
-
sumJitter += (track.jitter ?? 0);
|
|
686
|
+
observedInboundRtp.update(stats);
|
|
456
687
|
|
|
457
|
-
|
|
458
|
-
|
|
688
|
+
return observedInboundRtp;
|
|
689
|
+
}
|
|
459
690
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this.deltaDataChannelBytesReceived += channel.deltaBytesReceived;
|
|
463
|
-
});
|
|
691
|
+
private _updateInboundTrackSample(stats: InboundTrackSample) {
|
|
692
|
+
let observedInboundTrack = this.observedInboundTracks.get(stats.id);
|
|
464
693
|
|
|
465
|
-
|
|
466
|
-
|
|
694
|
+
if (!observedInboundTrack) {
|
|
695
|
+
if (!stats.timestamp || !stats.id || !stats.kind) {
|
|
696
|
+
return logger.warn(
|
|
697
|
+
`ObservedPeerConnection received an invalid InboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
698
|
+
stats
|
|
699
|
+
);
|
|
700
|
+
}
|
|
467
701
|
|
|
468
|
-
|
|
702
|
+
const inboundRtp = [ ...this.observedInboundRtps.values() ].find((inbRtp) => inbRtp.trackIdentifier === stats.id);
|
|
703
|
+
const mediaPlayout = inboundRtp ?
|
|
704
|
+
[ ...this.observedMediaPlayouts.values() ].find((mp) => mp.id === inboundRtp.playoutId) : undefined;
|
|
705
|
+
|
|
706
|
+
observedInboundTrack = new ObservedInboundTrack(
|
|
707
|
+
stats.timestamp,
|
|
708
|
+
stats.id,
|
|
709
|
+
stats.kind as MediaKind,
|
|
710
|
+
this,
|
|
711
|
+
inboundRtp,
|
|
712
|
+
mediaPlayout,
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
this.observedInboundTracks.set(stats.id, observedInboundTrack);
|
|
716
|
+
this.emit('added-inbound-track', observedInboundTrack);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
observedInboundTrack.update(stats);
|
|
469
720
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
721
|
+
return observedInboundTrack;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
private _updateMediaPlayoutStats(stats: MediaPlayoutStats) {
|
|
725
|
+
let observedMediaPlayout = this.observedMediaPlayouts.get(stats.id);
|
|
726
|
+
|
|
727
|
+
if (!observedMediaPlayout) {
|
|
728
|
+
if (!stats.timestamp || !stats.id || !stats.kind) {
|
|
729
|
+
return logger.warn(
|
|
730
|
+
`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
731
|
+
stats
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
observedMediaPlayout = new ObservedMediaPlayout(
|
|
736
|
+
stats.timestamp,
|
|
737
|
+
stats.id,
|
|
738
|
+
stats.kind as MediaKind,
|
|
739
|
+
this
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
|
|
743
|
+
this.emit('added-media-playout', observedMediaPlayout);
|
|
473
744
|
}
|
|
474
745
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
746
|
+
observedMediaPlayout.update(stats);
|
|
747
|
+
|
|
748
|
+
return observedMediaPlayout;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
private _updateMediaSourceStats(stats: MediaSourceStats) {
|
|
752
|
+
let observedMediaSource = this.observedMediaSources.get(stats.id);
|
|
753
|
+
|
|
754
|
+
if (!observedMediaSource) {
|
|
755
|
+
if (!stats.timestamp || !stats.id || !stats.kind) {
|
|
756
|
+
return logger.warn(
|
|
757
|
+
`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
758
|
+
stats
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
observedMediaSource = new ObservedMediaSource(
|
|
763
|
+
stats.timestamp,
|
|
764
|
+
stats.id,
|
|
765
|
+
stats.kind as MediaKind,
|
|
766
|
+
this
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
this.observedMediaSources.set(stats.id, observedMediaSource);
|
|
770
|
+
this.emit('added-media-source', observedMediaSource);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
observedMediaSource.update(stats);
|
|
774
|
+
|
|
775
|
+
return observedMediaSource;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
private _updateOutboundRtpStats(stats: OutboundRtpStats) {
|
|
779
|
+
let observedOutboundRtp = this.observedOutboundRtps.get(stats.ssrc);
|
|
780
|
+
|
|
781
|
+
if (!observedOutboundRtp) {
|
|
782
|
+
if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
|
|
783
|
+
return logger.warn(
|
|
784
|
+
`ObservedPeerConnection received an invalid OutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
785
|
+
stats
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
observedOutboundRtp = new ObservedOutboundRtp(
|
|
790
|
+
stats.timestamp,
|
|
791
|
+
stats.id,
|
|
792
|
+
stats.ssrc,
|
|
793
|
+
stats.kind as MediaKind,
|
|
794
|
+
this
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
|
|
798
|
+
this.emit('added-outbound-rtp', observedOutboundRtp);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
observedOutboundRtp.update(stats);
|
|
802
|
+
|
|
803
|
+
return observedOutboundRtp;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
public _updateOutboundTrackSample(stats: OutboundTrackSample) {
|
|
807
|
+
let observedOutboundTrack = this.observedOutboundTracks.get(stats.id);
|
|
808
|
+
|
|
809
|
+
if (!observedOutboundTrack) {
|
|
810
|
+
if (!stats.timestamp || !stats.id || !stats.kind) {
|
|
811
|
+
return logger.warn(
|
|
812
|
+
`ObservedPeerConnection received an invalid OutboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
813
|
+
stats
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
const observedMediaSource = [ ...this.observedMediaSources.values() ].find((mediaSource) => mediaSource.trackIdentifier === stats.id);
|
|
817
|
+
const outboundRtps = observedMediaSource
|
|
818
|
+
? [ ...this.observedOutboundRtps.values() ].filter((outboundRtp) => outboundRtp.mediaSourceId === observedMediaSource?.id) : undefined;
|
|
819
|
+
|
|
820
|
+
observedOutboundTrack = new ObservedOutboundTrack(
|
|
821
|
+
stats.timestamp,
|
|
822
|
+
stats.id,
|
|
823
|
+
stats.kind as MediaKind,
|
|
824
|
+
this,
|
|
825
|
+
outboundRtps,
|
|
826
|
+
observedMediaSource,
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
|
|
830
|
+
this.emit('added-outbound-track', observedOutboundTrack);
|
|
831
|
+
}
|
|
489
832
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
// we use RTT and lost packets to calculate the base score for the connection
|
|
505
|
-
const rttInMs = this.avgRttInMs ?? 0;
|
|
506
|
-
const latencyFactor = rttInMs < 150 ? 1.0 : getRttScore(rttInMs);
|
|
507
|
-
const totalPackets = Math.max(1, (this.totalInboundPacketsReceived ?? 0) + (this.totalOutboundPacketsSent ?? 0));
|
|
508
|
-
const lostPackets = (this.totalInboundPacketsLost ?? 0) + (this.deltaOutboundPacketsSent ?? 0);
|
|
509
|
-
const deliveryFactor = 1.0 - ((lostPackets) / (lostPackets + totalPackets));
|
|
510
|
-
// let's push the actual stability score
|
|
511
|
-
const stabilityScore = ((latencyFactor * 0.5) + (deliveryFactor * 0.5)) ** 2;
|
|
512
|
-
|
|
513
|
-
this._stabilityScores.push(stabilityScore);
|
|
514
|
-
if (10 < this._stabilityScores.length) {
|
|
515
|
-
this._stabilityScores.shift();
|
|
516
|
-
} else if (this._stabilityScores.length < 5) {
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
let counter = 0;
|
|
520
|
-
let weight = 0;
|
|
521
|
-
let totalScore = 0;
|
|
522
|
-
|
|
523
|
-
for (const score of this._stabilityScores) {
|
|
524
|
-
weight += 1;
|
|
525
|
-
counter += weight;
|
|
526
|
-
totalScore += weight * score;
|
|
527
|
-
}
|
|
528
|
-
const weightedStabilityScore = totalScore / counter;
|
|
529
|
-
let sumTrackScores = 0;
|
|
530
|
-
|
|
531
|
-
for (const trackScore of trackScores) {
|
|
532
|
-
sumTrackScores += trackScore.score;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const avgTrackScores = sumTrackScores / trackScores.length;
|
|
536
|
-
|
|
537
|
-
const score: CalculatedScore = {
|
|
538
|
-
remarks: [ {
|
|
539
|
-
severity: 'none',
|
|
540
|
-
text: `Peer Connection stability score: ${weightedStabilityScore}`,
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
severity: 'none',
|
|
544
|
-
text: `Avg. Track score: ${avgTrackScores}`,
|
|
545
|
-
} ],
|
|
546
|
-
score: Math.round(((weightedStabilityScore + avgTrackScores) * 5 * 100)) / 100.0,
|
|
547
|
-
timestamp: this._statsTimestamp ?? Date.now(),
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
for (const pendingIssue of this.ωpendingIssuesForScores) {
|
|
551
|
-
switch (pendingIssue.severity) {
|
|
552
|
-
case 'critical':
|
|
553
|
-
score.score = 0.0;
|
|
554
|
-
break;
|
|
555
|
-
case 'major':
|
|
556
|
-
score.score *= 0.5;
|
|
557
|
-
break;
|
|
558
|
-
case 'minor':
|
|
559
|
-
score.score *= 0.8;
|
|
560
|
-
break;
|
|
833
|
+
observedOutboundTrack.update(stats);
|
|
834
|
+
|
|
835
|
+
return observedOutboundTrack;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
private _updatePeerConnectionTransportStats(stats: PeerConnectionTransportStats) {
|
|
839
|
+
let observedPeerConnectionTransport = this.observedPeerConnectionTransports.get(stats.id);
|
|
840
|
+
|
|
841
|
+
if (!observedPeerConnectionTransport) {
|
|
842
|
+
if (!stats.timestamp || !stats.id) {
|
|
843
|
+
return logger.warn(
|
|
844
|
+
`ObservedPeerConnection received an invalid PeerConnectionTransportStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
845
|
+
stats
|
|
846
|
+
);
|
|
561
847
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
848
|
+
|
|
849
|
+
observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
|
|
850
|
+
|
|
851
|
+
this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
|
|
852
|
+
this.emit('added-peer-connection-transport', observedPeerConnectionTransport);
|
|
566
853
|
}
|
|
567
854
|
|
|
568
|
-
|
|
569
|
-
this.score = score;
|
|
855
|
+
observedPeerConnectionTransport.update(stats);
|
|
570
856
|
|
|
571
|
-
|
|
857
|
+
return observedPeerConnectionTransport;
|
|
572
858
|
}
|
|
573
|
-
|
|
859
|
+
|
|
860
|
+
private _updateRemoteInboundRtpStats(stats: RemoteInboundRtpStats) {
|
|
861
|
+
let observedRemoteInboundRtp = this.observedRemoteInboundRtps.get(stats.ssrc);
|
|
862
|
+
|
|
863
|
+
if (!observedRemoteInboundRtp) {
|
|
864
|
+
if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
|
|
865
|
+
return logger.warn(
|
|
866
|
+
`ObservedPeerConnection received an invalid RemoteInboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
867
|
+
stats
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
observedRemoteInboundRtp = new ObservedRemoteInboundRtp(
|
|
872
|
+
stats.timestamp,
|
|
873
|
+
stats.id,
|
|
874
|
+
stats.ssrc,
|
|
875
|
+
stats.kind as MediaKind,
|
|
876
|
+
this
|
|
877
|
+
);
|
|
878
|
+
|
|
879
|
+
this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
|
|
880
|
+
this.emit('added-remote-inbound-rtp', observedRemoteInboundRtp);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
observedRemoteInboundRtp.update(stats);
|
|
884
|
+
|
|
885
|
+
return observedRemoteInboundRtp;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
private _updateRemoteOutboundRtpStats(stats: RemoteOutboundRtpStats) {
|
|
889
|
+
let observedRemoteOutboundRtp = this.observedRemoteOutboundRtps.get(stats.ssrc);
|
|
890
|
+
|
|
891
|
+
if (!observedRemoteOutboundRtp) {
|
|
892
|
+
if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
|
|
893
|
+
return logger.warn(
|
|
894
|
+
`ObservedPeerConnection received an invalid RemoteOutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
|
|
895
|
+
stats
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
observedRemoteOutboundRtp = new ObservedRemoteOutboundRtp(
|
|
900
|
+
stats.timestamp,
|
|
901
|
+
stats.id,
|
|
902
|
+
stats.ssrc,
|
|
903
|
+
stats.kind as MediaKind,
|
|
904
|
+
this
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
|
|
908
|
+
this.emit('added-remote-outbound-rtp', observedRemoteOutboundRtp);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
observedRemoteOutboundRtp.update(stats);
|
|
912
|
+
|
|
913
|
+
return observedRemoteOutboundRtp;
|
|
914
|
+
}
|
|
915
|
+
}
|