@observertc/observer-js 0.41.6 → 0.42.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.
Files changed (61) hide show
  1. package/lib/ObservedCall.d.ts +10 -3
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +28 -0
  4. package/lib/ObservedClient.d.ts +4 -0
  5. package/lib/ObservedClient.d.ts.map +1 -1
  6. package/lib/ObservedClient.js +81 -32
  7. package/lib/ObservedInboundAudioTrack.d.ts +94 -0
  8. package/lib/ObservedInboundAudioTrack.d.ts.map +1 -0
  9. package/lib/ObservedInboundAudioTrack.js +215 -0
  10. package/lib/ObservedInboundVideoTrack.d.ts +98 -0
  11. package/lib/ObservedInboundVideoTrack.d.ts.map +1 -0
  12. package/lib/{ObservedInboundTrack.js → ObservedInboundVideoTrack.js} +53 -44
  13. package/lib/ObservedOutboundAudioTrack.d.ts +85 -0
  14. package/lib/ObservedOutboundAudioTrack.d.ts.map +1 -0
  15. package/lib/{ObservedOutboundTrack.js → ObservedOutboundAudioTrack.js} +35 -58
  16. package/lib/ObservedOutboundVideoTrack.d.ts +88 -0
  17. package/lib/ObservedOutboundVideoTrack.d.ts.map +1 -0
  18. package/lib/ObservedOutboundVideoTrack.js +204 -0
  19. package/lib/ObservedPeerConnection.d.ts +26 -16
  20. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  21. package/lib/ObservedPeerConnection.js +157 -86
  22. package/lib/RemoteTrackAssigner.d.ts +14 -0
  23. package/lib/RemoteTrackAssigner.d.ts.map +1 -0
  24. package/lib/RemoteTrackAssigner.js +52 -0
  25. package/lib/common/CalculatedScore.d.ts +79 -0
  26. package/lib/common/CalculatedScore.d.ts.map +1 -0
  27. package/lib/common/CalculatedScore.js +155 -0
  28. package/lib/common/CallEventType.d.ts +13 -1
  29. package/lib/common/CallEventType.d.ts.map +1 -1
  30. package/lib/common/CallEventType.js +12 -0
  31. package/lib/common/QualityScoreUtils.d.ts +1 -0
  32. package/lib/common/QualityScoreUtils.d.ts.map +1 -0
  33. package/lib/common/QualityScoreUtils.js +1 -0
  34. package/lib/common/utils.d.ts +1 -0
  35. package/lib/common/utils.d.ts.map +1 -1
  36. package/lib/common/utils.js +14 -1
  37. package/lib/index.d.ts +4 -2
  38. package/lib/index.d.ts.map +1 -1
  39. package/lib/monitors/ClientQualityMonitor.d.ts +1 -0
  40. package/lib/monitors/ClientQualityMonitor.d.ts.map +1 -0
  41. package/lib/monitors/ClientQualityMonitor.js +1 -0
  42. package/package.json +1 -1
  43. package/src/ObservedCall.ts +40 -4
  44. package/src/ObservedClient.ts +77 -17
  45. package/src/ObservedInboundAudioTrack.ts +336 -0
  46. package/src/{ObservedInboundTrack.ts → ObservedInboundVideoTrack.ts} +85 -66
  47. package/src/{ObservedOutboundTrack.ts → ObservedOutboundAudioTrack.ts} +59 -74
  48. package/src/ObservedOutboundVideoTrack.ts +326 -0
  49. package/src/ObservedPeerConnection.ts +205 -131
  50. package/src/RemoteTrackAssigner.ts +68 -0
  51. package/src/common/CalculatedScore.ts +180 -0
  52. package/src/common/CallEventType.ts +14 -1
  53. package/src/common/QualityScoreUtils.ts +0 -0
  54. package/src/common/utils.ts +17 -1
  55. package/src/index.ts +4 -2
  56. package/src/monitors/CallSummaryMonitor.ts +3 -3
  57. package/src/monitors/ClientQualityMonitor.ts +0 -0
  58. package/lib/ObservedInboundTrack.d.ts +0 -96
  59. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  60. package/lib/ObservedOutboundTrack.d.ts +0 -84
  61. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ObservedInboundAudioTrack = void 0;
4
+ const events_1 = require("events");
5
+ const CalculatedScore_1 = require("./common/CalculatedScore");
6
+ class ObservedInboundAudioTrack extends events_1.EventEmitter {
7
+ constructor(_model, peerConnection) {
8
+ super();
9
+ this._model = _model;
10
+ this.peerConnection = peerConnection;
11
+ this.created = Date.now();
12
+ this.visited = false;
13
+ this._stats = new Map();
14
+ this._closed = false;
15
+ this._updated = Date.now();
16
+ this._lastMaxStatsTimestamp = 0;
17
+ this.bitrate = 0;
18
+ this.fractionLoss = 0;
19
+ this.totalLostPackets = 0;
20
+ this.totalReceivedPackets = 0;
21
+ this.totalBytesReceived = 0;
22
+ this.totalReceivedSamples = 0;
23
+ this.totalSilentConcealedSamples = 0;
24
+ this.deltaLostPackets = 0;
25
+ this.deltaReceivedPackets = 0;
26
+ this.deltaBytesReceived = 0;
27
+ this.deltaReceivedSamples = 0;
28
+ this.deltaSilentConcealedSamples = 0;
29
+ this.ωpendingIssuesForScores = [];
30
+ this.setMaxListeners(Infinity);
31
+ }
32
+ get serviceId() {
33
+ return this.peerConnection.serviceId;
34
+ }
35
+ get roomId() {
36
+ return this.peerConnection.roomId;
37
+ }
38
+ get callId() {
39
+ return this.peerConnection.callId;
40
+ }
41
+ get clientId() {
42
+ return this.peerConnection.clientId;
43
+ }
44
+ get mediaUnitId() {
45
+ return this.peerConnection.mediaUnitId;
46
+ }
47
+ get peerConnectionId() {
48
+ return this.peerConnection.peerConnectionId;
49
+ }
50
+ get trackId() {
51
+ return this._model.trackId;
52
+ }
53
+ get sfuStreamId() {
54
+ return this._model.sfuStreamId;
55
+ }
56
+ get sfuSinkId() {
57
+ return this._model.sfuSinkId;
58
+ }
59
+ get updated() {
60
+ return this._updated;
61
+ }
62
+ get statsTimestamp() {
63
+ return this._lastMaxStatsTimestamp;
64
+ }
65
+ get stats() {
66
+ return this._stats;
67
+ }
68
+ get reports() {
69
+ return this.peerConnection.reports;
70
+ }
71
+ set remoteOutboundTrack(track) {
72
+ if (this._closed)
73
+ return;
74
+ if (this._remoteOutboundTrack)
75
+ return;
76
+ if (!track)
77
+ return;
78
+ track.once('close', () => {
79
+ this._remoteOutboundTrack = undefined;
80
+ this.emit('remoteoutboundtrack', undefined);
81
+ });
82
+ this._remoteOutboundTrack = track;
83
+ this.emit('remoteoutboundtrack', track);
84
+ }
85
+ get remoteOutboundTrack() {
86
+ return this._remoteOutboundTrack;
87
+ }
88
+ get closed() {
89
+ return this._closed;
90
+ }
91
+ close() {
92
+ if (this._closed)
93
+ return;
94
+ this._closed = true;
95
+ this.emit('close');
96
+ }
97
+ update(sample, statsTimestamp) {
98
+ var _a, _b, _c, _d, _e;
99
+ if (this._closed)
100
+ return;
101
+ const now = Date.now();
102
+ const report = Object.assign(Object.assign({ serviceId: this.peerConnection.client.call.serviceId, roomId: this.peerConnection.client.call.roomId, callId: this.peerConnection.client.call.callId, clientId: this.peerConnection.client.clientId, userId: this.peerConnection.client.userId, mediaUnitId: this.peerConnection.client.mediaUnitId, peerConnectionId: this.peerConnection.peerConnectionId }, sample), { timestamp: statsTimestamp, sampleSeq: -1, remoteClientId: (_a = sample.remoteClientId) !== null && _a !== void 0 ? _a : (_b = this.remoteOutboundTrack) === null || _b === void 0 ? void 0 : _b.peerConnection.client.clientId, remoteUserId: (_c = this.remoteOutboundTrack) === null || _c === void 0 ? void 0 : _c.peerConnection.client.userId, remoteTrackId: (_d = this.remoteOutboundTrack) === null || _d === void 0 ? void 0 : _d.trackId, remotePeerConnectionId: (_e = this.remoteOutboundTrack) === null || _e === void 0 ? void 0 : _e.peerConnection.peerConnectionId, marker: this.marker });
103
+ this.reports.addInboundAudioTrackReport(report);
104
+ if (!this._model.sfuStreamId && sample.sfuStreamId) {
105
+ this._model.sfuStreamId = sample.sfuStreamId;
106
+ }
107
+ if (!this._model.sfuSinkId && sample.sfuSinkId) {
108
+ this._model.sfuSinkId = sample.sfuSinkId;
109
+ }
110
+ const elapsedTimeInMs = Math.max(1, now - this._updated);
111
+ const lastStat = this._stats.get(sample.ssrc);
112
+ const rttInMs = sample.roundTripTime ? sample.roundTripTime * 1000 : undefined;
113
+ let bitrate = 0;
114
+ let deltaReceivedBytes = 0;
115
+ let fractionLost = 0;
116
+ let deltaLostPackets = 0;
117
+ let deltaReceivedPackets = 0;
118
+ if ((lastStat === null || lastStat === void 0 ? void 0 : lastStat.bytesReceived) && sample.bytesReceived && lastStat.bytesReceived < sample.bytesReceived) {
119
+ deltaReceivedBytes = sample.bytesReceived - lastStat.bytesReceived;
120
+ bitrate = deltaReceivedBytes / (elapsedTimeInMs / 1000);
121
+ }
122
+ if ((lastStat === null || lastStat === void 0 ? void 0 : lastStat.packetsReceived) && (sample === null || sample === void 0 ? void 0 : sample.packetsReceived) && lastStat.packetsReceived < sample.packetsReceived) {
123
+ deltaReceivedPackets = sample.packetsReceived - lastStat.packetsReceived;
124
+ }
125
+ if ((lastStat === null || lastStat === void 0 ? void 0 : lastStat.packetsLost) && sample.packetsLost && lastStat.packetsLost < sample.packetsLost) {
126
+ deltaLostPackets = sample.packetsLost - lastStat.packetsLost;
127
+ if (0 < deltaReceivedPackets) {
128
+ fractionLost = deltaLostPackets / (deltaReceivedPackets + deltaLostPackets);
129
+ }
130
+ }
131
+ let deltaDecodedFrames;
132
+ let deltaDroppedFrames;
133
+ let deltaReceivedFrames;
134
+ let deltaSilentConcealedSamples;
135
+ const audioSample = sample;
136
+ const lastAudioStat = lastStat;
137
+ if (audioSample.silentConcealedSamples && (lastAudioStat === null || lastAudioStat === void 0 ? void 0 : lastAudioStat.silentConcealedSamples) && lastAudioStat.silentConcealedSamples < audioSample.silentConcealedSamples) {
138
+ deltaSilentConcealedSamples = audioSample.silentConcealedSamples - lastAudioStat.silentConcealedSamples;
139
+ }
140
+ const stats = Object.assign(Object.assign({}, sample), { fractionLost,
141
+ rttInMs,
142
+ bitrate, ssrc: sample.ssrc, deltaReceivedBytes,
143
+ deltaLostPackets,
144
+ deltaReceivedPackets,
145
+ deltaReceivedFrames,
146
+ deltaDecodedFrames,
147
+ deltaDroppedFrames,
148
+ deltaSilentConcealedSamples,
149
+ statsTimestamp });
150
+ this._stats.set(sample.ssrc, stats);
151
+ this.visited = true;
152
+ this._updated = now;
153
+ this.emit('update', {
154
+ elapsedTimeInMs,
155
+ });
156
+ }
157
+ updateMetrics() {
158
+ var _a, _b, _c, _d, _e, _f, _g;
159
+ let maxStatsTimestamp = 0;
160
+ let rttInMsSum = 0;
161
+ let jitterSum = 0;
162
+ let size = 0;
163
+ this.bitrate = 0;
164
+ this.rttInMs = undefined;
165
+ this.deltaBytesReceived = 0;
166
+ this.deltaLostPackets = 0;
167
+ this.deltaReceivedSamples = 0;
168
+ this.deltaSilentConcealedSamples = 0;
169
+ this.fractionLoss = 0;
170
+ for (const [, stats] of this._stats) {
171
+ if (stats.statsTimestamp <= this._lastMaxStatsTimestamp)
172
+ continue;
173
+ this.deltaBytesReceived += (_a = stats.deltaReceivedBytes) !== null && _a !== void 0 ? _a : 0;
174
+ this.deltaLostPackets += (_b = stats.deltaLostPackets) !== null && _b !== void 0 ? _b : 0;
175
+ this.deltaReceivedPackets += (_c = stats.deltaReceivedFrames) !== null && _c !== void 0 ? _c : 0;
176
+ this.deltaReceivedSamples += (_d = stats.deltaReceivedSamples) !== null && _d !== void 0 ? _d : 0;
177
+ this.deltaSilentConcealedSamples += (_e = stats.deltaSilentConcealedSamples) !== null && _e !== void 0 ? _e : 0;
178
+ this.bitrate += stats.bitrate;
179
+ maxStatsTimestamp = Math.max(maxStatsTimestamp, stats.statsTimestamp);
180
+ rttInMsSum += (_f = stats.rttInMs) !== null && _f !== void 0 ? _f : 0;
181
+ jitterSum += (_g = stats.jitter) !== null && _g !== void 0 ? _g : 0;
182
+ ++size;
183
+ }
184
+ this.totalBytesReceived += this.deltaBytesReceived;
185
+ this.totalLostPackets += this.deltaLostPackets;
186
+ this.totalReceivedPackets += this.deltaReceivedPackets;
187
+ this.totalReceivedSamples += this.deltaReceivedSamples;
188
+ this.totalSilentConcealedSamples += this.deltaSilentConcealedSamples;
189
+ this.rttInMs = rttInMsSum / Math.max(size, 1);
190
+ this.jitter = jitterSum / Math.max(size, 1);
191
+ this.fractionLoss = 0 < this.deltaReceivedPackets && 0 < this.deltaLostPackets ? (this.deltaLostPackets / (this.deltaReceivedPackets + this.deltaLostPackets)) : 0;
192
+ this._lastMaxStatsTimestamp = maxStatsTimestamp;
193
+ this._updateQualityScore(maxStatsTimestamp);
194
+ }
195
+ _updateQualityScore(timestamp) {
196
+ const newIssues = this.ωpendingIssuesForScores;
197
+ const score = (0, CalculatedScore_1.calculateBaseAudioScore)(this, newIssues);
198
+ if (0 < newIssues.length) {
199
+ this.ωpendingIssuesForScores = [];
200
+ }
201
+ if (!score)
202
+ return (this.score = undefined);
203
+ if (0.05 < this.fractionLoss) {
204
+ score.score *= 0.5;
205
+ score.remarks.push({
206
+ severity: 'major',
207
+ text: 'Fraction loss is too high',
208
+ });
209
+ }
210
+ score.timestamp = timestamp;
211
+ this.score = score;
212
+ this.emit('score', this.score);
213
+ }
214
+ }
215
+ exports.ObservedInboundAudioTrack = ObservedInboundAudioTrack;
@@ -0,0 +1,98 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from 'events';
3
+ import { ObservedPeerConnection } from './ObservedPeerConnection';
4
+ import { InboundVideoTrack } from '@observertc/sample-schemas-js';
5
+ import { CalculatedScore } from './common/CalculatedScore';
6
+ import { ClientIssue } from './monitors/CallSummary';
7
+ import { ObservedOutboundVideoTrack } from './ObservedOutboundVideoTrack';
8
+ export type ObservedInboundVideoTrackModel = {
9
+ trackId: string;
10
+ sfuStreamId?: string;
11
+ sfuSinkId?: string;
12
+ };
13
+ export type ObservedInboundVideoTrackEvents = {
14
+ update: [
15
+ {
16
+ elapsedTimeInMs: number;
17
+ }
18
+ ];
19
+ score: [CalculatedScore];
20
+ close: [];
21
+ remoteoutboundtrack: [ObservedOutboundVideoTrack | undefined];
22
+ };
23
+ export type ObservedInboundVideoTrackStatsUpdate = InboundVideoTrack;
24
+ export type ObservedInboundVideoTrackStats = InboundVideoTrack & {
25
+ ssrc: number;
26
+ bitrate: number;
27
+ fractionLost: number;
28
+ rttInMs?: number;
29
+ deltaReceivedBytes?: number;
30
+ deltaLostPackets?: number;
31
+ deltaReceivedPackets?: number;
32
+ deltaReceivedFrames?: number;
33
+ deltaDecodedFrames?: number;
34
+ deltaDroppedFrames?: number;
35
+ fractionLoss?: number;
36
+ statsTimestamp: number;
37
+ };
38
+ export declare interface ObservedInboundVideoTrack {
39
+ on<U extends keyof ObservedInboundVideoTrackEvents>(event: U, listener: (...args: ObservedInboundVideoTrackEvents[U]) => void): this;
40
+ off<U extends keyof ObservedInboundVideoTrackEvents>(event: U, listener: (...args: ObservedInboundVideoTrackEvents[U]) => void): this;
41
+ once<U extends keyof ObservedInboundVideoTrackEvents>(event: U, listener: (...args: ObservedInboundVideoTrackEvents[U]) => void): this;
42
+ emit<U extends keyof ObservedInboundVideoTrackEvents>(event: U, ...args: ObservedInboundVideoTrackEvents[U]): boolean;
43
+ update(sample: InboundVideoTrack, timestamp: number): void;
44
+ }
45
+ export declare class ObservedInboundVideoTrack extends EventEmitter {
46
+ private readonly _model;
47
+ readonly peerConnection: ObservedPeerConnection;
48
+ readonly created: number;
49
+ visited: boolean;
50
+ contentType: 'lowmotion' | 'standard' | 'highmotion';
51
+ codec: 'vp8' | 'vp9' | 'h264';
52
+ private readonly _stats;
53
+ private _closed;
54
+ private _updated;
55
+ private _remoteOutboundTrack?;
56
+ private _lastMaxStatsTimestamp;
57
+ bitrate: number;
58
+ rttInMs?: number;
59
+ jitter?: number;
60
+ fractionLoss: number;
61
+ marker?: string;
62
+ totalLostPackets: number;
63
+ totalReceivedPackets: number;
64
+ totalBytesReceived: number;
65
+ totalReceivedFrames: number;
66
+ totalDecodedFrames: number;
67
+ totalDroppedFrames: number;
68
+ deltaLostPackets: number;
69
+ deltaReceivedPackets: number;
70
+ deltaBytesReceived: number;
71
+ deltaReceivedFrames: number;
72
+ deltaDecodedFrames: number;
73
+ deltaDroppedFrames: number;
74
+ highestLayer?: ObservedInboundVideoTrackStats;
75
+ score?: CalculatedScore;
76
+ ωpendingIssuesForScores: ClientIssue[];
77
+ constructor(_model: ObservedInboundVideoTrackModel, peerConnection: ObservedPeerConnection);
78
+ get serviceId(): string;
79
+ get roomId(): string;
80
+ get callId(): string;
81
+ get clientId(): string;
82
+ get mediaUnitId(): string;
83
+ get peerConnectionId(): string;
84
+ get trackId(): string;
85
+ get sfuStreamId(): string | undefined;
86
+ get sfuSinkId(): string | undefined;
87
+ get updated(): number;
88
+ get statsTimestamp(): number;
89
+ get stats(): ReadonlyMap<number, ObservedInboundVideoTrackStats>;
90
+ get reports(): import("./ReportsCollector").ReportsCollector;
91
+ set remoteOutboundTrack(track: ObservedOutboundVideoTrack | undefined);
92
+ get remoteOutboundTrack(): ObservedOutboundVideoTrack | undefined;
93
+ get closed(): boolean;
94
+ close(): void;
95
+ updateMetrics(): void;
96
+ private _updateQualityScore;
97
+ }
98
+ //# sourceMappingURL=ObservedInboundVideoTrack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObservedInboundVideoTrack.d.ts","sourceRoot":"","sources":["../src/ObservedInboundVideoTrack.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ObservedInboundTrack = void 0;
3
+ exports.ObservedInboundVideoTrack = void 0;
4
4
  const events_1 = require("events");
5
- class ObservedInboundTrack extends events_1.EventEmitter {
5
+ const CalculatedScore_1 = require("./common/CalculatedScore");
6
+ class ObservedInboundVideoTrack extends events_1.EventEmitter {
6
7
  constructor(_model, peerConnection) {
7
8
  super();
8
9
  this._model = _model;
9
10
  this.peerConnection = peerConnection;
10
11
  this.created = Date.now();
11
12
  this.visited = false;
13
+ this.contentType = 'standard';
14
+ this.codec = 'vp8';
12
15
  this._stats = new Map();
13
16
  this._closed = false;
14
17
  this._updated = Date.now();
@@ -21,16 +24,13 @@ class ObservedInboundTrack extends events_1.EventEmitter {
21
24
  this.totalReceivedFrames = 0;
22
25
  this.totalDecodedFrames = 0;
23
26
  this.totalDroppedFrames = 0;
24
- this.totalReceivedSamples = 0;
25
- this.totalSilentConcealedSamples = 0;
26
27
  this.deltaLostPackets = 0;
27
28
  this.deltaReceivedPackets = 0;
28
29
  this.deltaBytesReceived = 0;
29
30
  this.deltaReceivedFrames = 0;
30
31
  this.deltaDecodedFrames = 0;
31
32
  this.deltaDroppedFrames = 0;
32
- this.deltaReceivedSamples = 0;
33
- this.deltaSilentConcealedSamples = 0;
33
+ this.ωpendingIssuesForScores = [];
34
34
  this.setMaxListeners(Infinity);
35
35
  }
36
36
  get serviceId() {
@@ -54,9 +54,6 @@ class ObservedInboundTrack extends events_1.EventEmitter {
54
54
  get trackId() {
55
55
  return this._model.trackId;
56
56
  }
57
- get kind() {
58
- return this._model.kind;
59
- }
60
57
  get sfuStreamId() {
61
58
  return this._model.sfuStreamId;
62
59
  }
@@ -66,6 +63,9 @@ class ObservedInboundTrack extends events_1.EventEmitter {
66
63
  get updated() {
67
64
  return this._updated;
68
65
  }
66
+ get statsTimestamp() {
67
+ return this._lastMaxStatsTimestamp;
68
+ }
69
69
  get stats() {
70
70
  return this._stats;
71
71
  }
@@ -79,8 +79,9 @@ class ObservedInboundTrack extends events_1.EventEmitter {
79
79
  return;
80
80
  if (!track)
81
81
  return;
82
- track === null || track === void 0 ? void 0 : track.once('close', () => {
82
+ track.once('close', () => {
83
83
  this._remoteOutboundTrack = undefined;
84
+ this.emit('remoteoutboundtrack', undefined);
84
85
  });
85
86
  this._remoteOutboundTrack = track;
86
87
  this.emit('remoteoutboundtrack', track);
@@ -103,10 +104,7 @@ class ObservedInboundTrack extends events_1.EventEmitter {
103
104
  return;
104
105
  const now = Date.now();
105
106
  const report = Object.assign(Object.assign({ serviceId: this.peerConnection.client.call.serviceId, roomId: this.peerConnection.client.call.roomId, callId: this.peerConnection.client.call.callId, clientId: this.peerConnection.client.clientId, userId: this.peerConnection.client.userId, mediaUnitId: this.peerConnection.client.mediaUnitId, peerConnectionId: this.peerConnection.peerConnectionId }, sample), { timestamp: statsTimestamp, sampleSeq: -1, remoteClientId: (_a = sample.remoteClientId) !== null && _a !== void 0 ? _a : (_b = this.remoteOutboundTrack) === null || _b === void 0 ? void 0 : _b.peerConnection.client.clientId, remoteUserId: (_c = this.remoteOutboundTrack) === null || _c === void 0 ? void 0 : _c.peerConnection.client.userId, remoteTrackId: (_d = this.remoteOutboundTrack) === null || _d === void 0 ? void 0 : _d.trackId, remotePeerConnectionId: (_e = this.remoteOutboundTrack) === null || _e === void 0 ? void 0 : _e.peerConnection.peerConnectionId, marker: this.marker });
106
- if (this.kind === 'audio')
107
- this.reports.addInboundAudioTrackReport(report);
108
- else if (this.kind === 'video')
109
- this.reports.addInboundVideoTrackReport(report);
107
+ this.reports.addInboundVideoTrackReport(report);
110
108
  if (!this._model.sfuStreamId && sample.sfuStreamId) {
111
109
  this._model.sfuStreamId = sample.sfuStreamId;
112
110
  }
@@ -137,26 +135,16 @@ class ObservedInboundTrack extends events_1.EventEmitter {
137
135
  let deltaDecodedFrames;
138
136
  let deltaDroppedFrames;
139
137
  let deltaReceivedFrames;
140
- let deltaSilentConcealedSamples;
141
- if (this.kind === 'video') {
142
- const videoSample = sample;
143
- const lastVideoStat = lastStat;
144
- if (videoSample.framesDecoded && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesDecoded) && lastVideoStat.framesDecoded < videoSample.framesDecoded) {
145
- deltaDecodedFrames = videoSample.framesDecoded - lastVideoStat.framesDecoded;
146
- }
147
- if (videoSample.framesDropped && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesDropped) && lastVideoStat.framesDropped < videoSample.framesDropped) {
148
- deltaDroppedFrames = videoSample.framesDropped - lastVideoStat.framesDropped;
149
- }
150
- if (videoSample.framesReceived && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesReceived) && lastVideoStat.framesReceived < videoSample.framesReceived) {
151
- deltaReceivedFrames = videoSample.framesReceived - lastVideoStat.framesReceived;
152
- }
138
+ const videoSample = sample;
139
+ const lastVideoStat = lastStat;
140
+ if (videoSample.framesDecoded && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesDecoded) && lastVideoStat.framesDecoded < videoSample.framesDecoded) {
141
+ deltaDecodedFrames = videoSample.framesDecoded - lastVideoStat.framesDecoded;
153
142
  }
154
- else if (this.kind === 'audio') {
155
- const audioSample = sample;
156
- const lastAudioStat = lastStat;
157
- if (audioSample.silentConcealedSamples && (lastAudioStat === null || lastAudioStat === void 0 ? void 0 : lastAudioStat.silentConcealedSamples) && lastAudioStat.silentConcealedSamples < audioSample.silentConcealedSamples) {
158
- deltaSilentConcealedSamples = audioSample.silentConcealedSamples - lastAudioStat.silentConcealedSamples;
159
- }
143
+ if (videoSample.framesDropped && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesDropped) && lastVideoStat.framesDropped < videoSample.framesDropped) {
144
+ deltaDroppedFrames = videoSample.framesDropped - lastVideoStat.framesDropped;
145
+ }
146
+ if (videoSample.framesReceived && (lastVideoStat === null || lastVideoStat === void 0 ? void 0 : lastVideoStat.framesReceived) && lastVideoStat.framesReceived < videoSample.framesReceived) {
147
+ deltaReceivedFrames = videoSample.framesReceived - lastVideoStat.framesReceived;
160
148
  }
161
149
  const stats = Object.assign(Object.assign({}, sample), { fractionLost,
162
150
  rttInMs,
@@ -166,7 +154,6 @@ class ObservedInboundTrack extends events_1.EventEmitter {
166
154
  deltaReceivedFrames,
167
155
  deltaDecodedFrames,
168
156
  deltaDroppedFrames,
169
- deltaSilentConcealedSamples,
170
157
  statsTimestamp });
171
158
  this._stats.set(sample.ssrc, stats);
172
159
  this.visited = true;
@@ -176,9 +163,10 @@ class ObservedInboundTrack extends events_1.EventEmitter {
176
163
  });
177
164
  }
178
165
  updateMetrics() {
179
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
166
+ var _a, _b, _c, _d, _e, _f, _g, _h;
180
167
  let maxStatsTimestamp = 0;
181
168
  let rttInMsSum = 0;
169
+ let jitterSum = 0;
182
170
  let size = 0;
183
171
  this.bitrate = 0;
184
172
  this.rttInMs = undefined;
@@ -187,10 +175,9 @@ class ObservedInboundTrack extends events_1.EventEmitter {
187
175
  this.deltaReceivedFrames = 0;
188
176
  this.deltaDecodedFrames = 0;
189
177
  this.deltaDroppedFrames = 0;
190
- this.deltaReceivedSamples = 0;
191
- this.deltaSilentConcealedSamples = 0;
192
178
  this.fractionLoss = 0;
193
- for (const [, stats] of this._stats) {
179
+ const highestLayer = { ssrc: -1, bitrate: -1 };
180
+ for (const [ssrc, stats] of this._stats) {
194
181
  if (stats.statsTimestamp <= this._lastMaxStatsTimestamp)
195
182
  continue;
196
183
  this.deltaBytesReceived += (_a = stats.deltaReceivedBytes) !== null && _a !== void 0 ? _a : 0;
@@ -199,24 +186,46 @@ class ObservedInboundTrack extends events_1.EventEmitter {
199
186
  this.deltaReceivedFrames += (_d = stats.deltaReceivedFrames) !== null && _d !== void 0 ? _d : 0;
200
187
  this.deltaDecodedFrames += (_e = stats.deltaDecodedFrames) !== null && _e !== void 0 ? _e : 0;
201
188
  this.deltaDroppedFrames += (_f = stats.deltaDroppedFrames) !== null && _f !== void 0 ? _f : 0;
202
- this.deltaReceivedSamples += (_g = stats.deltaReceivedSamples) !== null && _g !== void 0 ? _g : 0;
203
- this.deltaSilentConcealedSamples += (_h = stats.deltaSilentConcealedSamples) !== null && _h !== void 0 ? _h : 0;
204
189
  this.bitrate += stats.bitrate;
205
190
  maxStatsTimestamp = Math.max(maxStatsTimestamp, stats.statsTimestamp);
206
- rttInMsSum += (_j = stats.rttInMs) !== null && _j !== void 0 ? _j : 0;
191
+ if (highestLayer.bitrate < stats.bitrate) {
192
+ highestLayer.ssrc = ssrc;
193
+ }
194
+ rttInMsSum += (_g = stats.rttInMs) !== null && _g !== void 0 ? _g : 0;
195
+ jitterSum += (_h = stats.jitter) !== null && _h !== void 0 ? _h : 0;
207
196
  ++size;
208
197
  }
198
+ this.highestLayer = this.stats.get(highestLayer.ssrc);
209
199
  this.totalBytesReceived += this.deltaBytesReceived;
210
200
  this.totalLostPackets += this.deltaLostPackets;
211
201
  this.totalReceivedPackets += this.deltaReceivedPackets;
212
202
  this.totalReceivedFrames += this.deltaReceivedFrames;
213
203
  this.totalDecodedFrames += this.deltaDecodedFrames;
214
204
  this.totalDroppedFrames += this.deltaDroppedFrames;
215
- this.totalReceivedSamples += this.deltaReceivedSamples;
216
- this.totalSilentConcealedSamples += this.deltaSilentConcealedSamples;
217
205
  this.rttInMs = rttInMsSum / Math.max(size, 1);
206
+ this.jitter = jitterSum / Math.max(size, 1);
218
207
  this.fractionLoss = 0 < this.deltaReceivedPackets && 0 < this.deltaLostPackets ? (this.deltaLostPackets / (this.deltaReceivedPackets + this.deltaLostPackets)) : 0;
219
208
  this._lastMaxStatsTimestamp = maxStatsTimestamp;
209
+ this._updateQualityScore(maxStatsTimestamp);
210
+ }
211
+ _updateQualityScore(timestamp) {
212
+ const newIssues = this.ωpendingIssuesForScores;
213
+ const score = (0, CalculatedScore_1.calculateBaseVideoScore)(this, newIssues);
214
+ if (0 < this.ωpendingIssuesForScores.length) {
215
+ this.ωpendingIssuesForScores = [];
216
+ }
217
+ if (!score)
218
+ return (this.score = undefined);
219
+ if (0.05 < this.fractionLoss) {
220
+ score.score *= 0.5;
221
+ score.remarks.push({
222
+ severity: 'major',
223
+ text: 'Fraction loss is too high',
224
+ });
225
+ }
226
+ score.timestamp = timestamp;
227
+ this.score = score;
228
+ this.emit('score', this.score);
220
229
  }
221
230
  }
222
- exports.ObservedInboundTrack = ObservedInboundTrack;
231
+ exports.ObservedInboundVideoTrack = ObservedInboundVideoTrack;
@@ -0,0 +1,85 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from 'events';
3
+ import { ObservedPeerConnection } from './ObservedPeerConnection';
4
+ import { OutboundAudioTrack } from '@observertc/sample-schemas-js';
5
+ import { ObservedInboundAudioTrack } from './ObservedInboundAudioTrack';
6
+ import { CalculatedScore } from './common/CalculatedScore';
7
+ import { ClientIssue } from './monitors/CallSummary';
8
+ export type ObservedOutboundAudioTrackModel = {
9
+ trackId: string;
10
+ sfuStreamId?: string;
11
+ };
12
+ export type ObservedOutboundAudioTrackEvents = {
13
+ qualitylimitationchanged: [string];
14
+ update: [
15
+ {
16
+ elapsedTimeInMs: number;
17
+ }
18
+ ];
19
+ score: [CalculatedScore];
20
+ close: [];
21
+ };
22
+ export type ObservedOutboundAudioTrackStats = OutboundAudioTrack & {
23
+ ssrc: number;
24
+ bitrate: number;
25
+ rttInMs?: number;
26
+ deltaLostPackets: number;
27
+ deltaSentPackets: number;
28
+ deltaSentBytes: number;
29
+ deltaSentFrames?: number;
30
+ deltaEncodedFrames?: number;
31
+ statsTimestamp: number;
32
+ };
33
+ export declare interface ObservedOutboundAudioTrack {
34
+ on<U extends keyof ObservedOutboundAudioTrackEvents>(event: U, listener: (...args: ObservedOutboundAudioTrackEvents[U]) => void): this;
35
+ off<U extends keyof ObservedOutboundAudioTrackEvents>(event: U, listener: (...args: ObservedOutboundAudioTrackEvents[U]) => void): this;
36
+ once<U extends keyof ObservedOutboundAudioTrackEvents>(event: U, listener: (...args: ObservedOutboundAudioTrackEvents[U]) => void): this;
37
+ emit<U extends keyof ObservedOutboundAudioTrackEvents>(event: U, ...args: ObservedOutboundAudioTrackEvents[U]): boolean;
38
+ update(sample: OutboundAudioTrack, timestamp: number): void;
39
+ }
40
+ export declare class ObservedOutboundAudioTrack extends EventEmitter {
41
+ private readonly _model;
42
+ readonly peerConnection: ObservedPeerConnection;
43
+ readonly created: number;
44
+ visited: boolean;
45
+ bitrate: number;
46
+ rttInMs?: number;
47
+ jitter?: number;
48
+ marker?: string;
49
+ totalLostPackets: number;
50
+ totalSentPackets: number;
51
+ totalSentBytes: number;
52
+ totalSentFrames: number;
53
+ deltaLostPackets: number;
54
+ deltaSentPackets: number;
55
+ deltaSentBytes: number;
56
+ deltaSentFrames: number;
57
+ deltaEncodedFrames: number;
58
+ sendingBitrate: number;
59
+ private readonly _stats;
60
+ private _lastMaxStatsTimestamp;
61
+ private _closed;
62
+ private _updated;
63
+ private _lastUpdateMetrics?;
64
+ score?: CalculatedScore;
65
+ ωpendingIssuesForScores: ClientIssue[];
66
+ readonly remoteInboundTracks: Map<string, ObservedInboundAudioTrack>;
67
+ constructor(_model: ObservedOutboundAudioTrackModel, peerConnection: ObservedPeerConnection);
68
+ get serviceId(): string;
69
+ get roomId(): string;
70
+ get callId(): string;
71
+ get clientId(): string;
72
+ get mediaUnitId(): string;
73
+ get peerConnectionId(): string;
74
+ get trackId(): string;
75
+ get sfuStreamId(): string | undefined;
76
+ get reports(): import("./ReportsCollector").ReportsCollector;
77
+ get statsTimestamp(): number;
78
+ get updated(): number;
79
+ get stats(): ReadonlyMap<number, ObservedOutboundAudioTrackStats>;
80
+ get closed(): boolean;
81
+ close(): void;
82
+ updateMetrics(): void;
83
+ private _updateQualityScore;
84
+ }
85
+ //# sourceMappingURL=ObservedOutboundAudioTrack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObservedOutboundAudioTrack.d.ts","sourceRoot":"","sources":["../src/ObservedOutboundAudioTrack.ts"],"names":[],"mappings":"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"}