@observertc/observer-js 0.30.2-beta → 0.30.4-beta

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.
@@ -61,6 +61,17 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
61
61
  this._model = _model;
62
62
  this.peerConnection = peerConnection;
63
63
  this._storageProvider = _storageProvider;
64
+ this.bitrate = 0;
65
+ this.totalLostPackets = 0;
66
+ this.totalSentPackets = 0;
67
+ this.totalSentBytes = 0;
68
+ this.totalSentFrames = 0;
69
+ this.deltaLostPackets = 0;
70
+ this.deltaSentPackets = 0;
71
+ this.deltaSentBytes = 0;
72
+ this.deltaSentFrames = 0;
73
+ this.deltaEncodedFrames = 0;
74
+ this.created = Date.now();
64
75
  this._stats = new Map();
65
76
  this._execute = (0, SingleExecutor_1.createSingleExecutor)();
66
77
  this._closed = false;
@@ -78,9 +89,6 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
78
89
  get sfuStreamId() {
79
90
  return this._model.sfuStreamId;
80
91
  }
81
- get closed() {
82
- return this._closed;
83
- }
84
92
  get remoteInboundTracks() {
85
93
  return this._remoteInboundTracks;
86
94
  }
@@ -90,6 +98,15 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
90
98
  get updated() {
91
99
  return this._updated;
92
100
  }
101
+ get stats() {
102
+ return this._stats;
103
+ }
104
+ get uptimeInMs() {
105
+ return this._updated - this.created;
106
+ }
107
+ get closed() {
108
+ return this._closed;
109
+ }
93
110
  close() {
94
111
  if (this._closed)
95
112
  return;
@@ -98,11 +115,17 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
98
115
  .catch(() => void 0)
99
116
  .finally(() => this.emit('close'));
100
117
  }
101
- get stats() {
102
- return this._stats;
118
+ resetMetrics() {
119
+ this.bitrate = 0;
120
+ this.rttInMs = 0;
121
+ this.deltaLostPackets = 0;
122
+ this.deltaSentPackets = 0;
123
+ this.deltaSentBytes = 0;
124
+ this.deltaSentFrames = 0;
125
+ this.deltaEncodedFrames = 0;
103
126
  }
104
127
  update(sample, timestamp) {
105
- var _a, _b;
128
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
106
129
  return __awaiter(this, void 0, void 0, function* () {
107
130
  if (this._closed)
108
131
  return;
@@ -116,13 +139,34 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
116
139
  const lastStat = this._stats.get(sample.ssrc);
117
140
  const rttInMs = sample.roundTripTime ? sample.roundTripTime * 1000 : undefined;
118
141
  const bitrate = (((_a = sample.bytesSent) !== null && _a !== void 0 ? _a : 0) - ((_b = lastStat === null || lastStat === void 0 ? void 0 : lastStat.bytesSent) !== null && _b !== void 0 ? _b : 0)) * 8 / (elapsedTimeInMs / 1000);
119
- // const lostPackets = (sample.packetsLost ?? 0) - (lastStat?.packetsLost ?? 0);
120
- // const sentPackets = (sample.packetsSent ?? 0) - (lastStat?.packetsSent ?? 0);
142
+ const deltaLostPackets = ((_c = sample.packetsLost) !== null && _c !== void 0 ? _c : 0) - ((_d = lastStat === null || lastStat === void 0 ? void 0 : lastStat.packetsLost) !== null && _d !== void 0 ? _d : 0);
143
+ const deltaSentPackets = ((_e = sample.packetsSent) !== null && _e !== void 0 ? _e : 0) - ((_f = lastStat === null || lastStat === void 0 ? void 0 : lastStat.packetsSent) !== null && _f !== void 0 ? _f : 0);
144
+ const deltaSentBytes = ((_g = sample.bytesSent) !== null && _g !== void 0 ? _g : 0) - ((_h = lastStat === null || lastStat === void 0 ? void 0 : lastStat.bytesSent) !== null && _h !== void 0 ? _h : 0);
145
+ let deltaEncodedFrames;
146
+ let deltaSentFrames;
147
+ if (this.kind === 'video') {
148
+ const videoSample = sample;
149
+ const lastVideoStats = lastStat;
150
+ deltaEncodedFrames = ((_j = videoSample.framesEncoded) !== null && _j !== void 0 ? _j : 0) - ((_k = lastVideoStats === null || lastVideoStats === void 0 ? void 0 : lastVideoStats.framesEncoded) !== null && _k !== void 0 ? _k : 0);
151
+ deltaSentFrames = ((_l = videoSample.framesSent) !== null && _l !== void 0 ? _l : 0) - ((_m = lastVideoStats === null || lastVideoStats === void 0 ? void 0 : lastVideoStats.framesSent) !== null && _m !== void 0 ? _m : 0);
152
+ }
121
153
  const stats = Object.assign(Object.assign({}, sample), { rttInMs,
122
- bitrate, ssrc: sample.ssrc });
154
+ bitrate, ssrc: sample.ssrc, deltaLostPackets,
155
+ deltaSentPackets,
156
+ deltaSentBytes,
157
+ deltaEncodedFrames,
158
+ deltaSentFrames });
123
159
  this._stats.set(sample.ssrc, stats);
124
160
  this.bitrate = [...this._stats.values()].reduce((acc, stat) => acc + stat.bitrate, 0);
125
161
  this.rttInMs = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.rttInMs) !== null && _a !== void 0 ? _a : 0); }, 0) / (this._stats.size || 1);
162
+ this.totalLostPackets = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.packetsLost) !== null && _a !== void 0 ? _a : 0); }, 0);
163
+ this.totalSentPackets = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.packetsSent) !== null && _a !== void 0 ? _a : 0); }, 0);
164
+ this.totalSentBytes = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.bytesSent) !== null && _a !== void 0 ? _a : 0); }, 0);
165
+ this.deltaEncodedFrames = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.deltaEncodedFrames) !== null && _a !== void 0 ? _a : 0); }, 0);
166
+ this.deltaSentFrames = [...this._stats.values()].reduce((acc, stat) => { var _a; return acc + ((_a = stat.deltaSentFrames) !== null && _a !== void 0 ? _a : 0); }, 0);
167
+ this.deltaLostPackets = [...this._stats.values()].reduce((acc, stat) => acc + stat.deltaLostPackets, 0);
168
+ this.deltaSentPackets = [...this._stats.values()].reduce((acc, stat) => acc + stat.deltaSentPackets, 0);
169
+ this.deltaSentBytes = [...this._stats.values()].reduce((acc, stat) => acc + stat.deltaSentBytes, 0);
126
170
  // setting up sfu connection as it is not always available at the first sample
127
171
  if (sample.sfuStreamId && !this._model.sfuStreamId) {
128
172
  this._model.sfuStreamId = sample.sfuStreamId;
@@ -13,9 +13,6 @@ export type ObservedPeerConnectionEvents = {
13
13
  newinboudvideotrack: [ObservedInboundTrack<'video'>];
14
14
  newoutboundaudiotrack: [ObservedOutboundTrack<'audio'>];
15
15
  newoutboundvideotrack: [ObservedOutboundTrack<'video'>];
16
- iceconnectionstatechange: [string];
17
- icegatheringstatechange: [string];
18
- connectionstatechange: [string];
19
16
  };
20
17
  export type ObservedPeerConnectionConfig = {
21
18
  peerConnectionId: string;
@@ -32,26 +29,31 @@ export declare class ObservedPeerConnection extends EventEmitter {
32
29
  readonly client: ObservedClient;
33
30
  private readonly _storageProvider;
34
31
  static create(config: ObservedPeerConnectionConfig, client: ObservedClient, storageProvider: StorageProvider): Promise<ObservedPeerConnection>;
32
+ created: number;
35
33
  totalInboundPacketsLost: number;
36
34
  totalInboundPacketsReceived: number;
37
- totalOutboundPacketsLost: number;
38
- totalOutboundPacketsReceived: number;
39
35
  totalOutboundPacketsSent: number;
40
36
  totalDataChannelBytesSent: number;
41
37
  totalDataChannelBytesReceived: number;
42
38
  totalSentAudioBytes: number;
43
39
  totalSentVideoBytes: number;
40
+ totalSentAudioPackets: number;
41
+ totalSentVideoPackets: number;
42
+ totalReceivedAudioPacktes: number;
43
+ totalReceivedVideoPackets: number;
44
44
  totalReceivedAudioBytes: number;
45
45
  totalReceivedVideoBytes: number;
46
- deltaInboundPacketsLost: number;
47
- deltaInboundPacketsReceived: number;
48
- deltaOutboundPacketsLost: number;
49
- deltaOutboundPacketsReceived: number;
50
- deltaOutboundPacketsSent: number;
46
+ deltaInboundLostPackets: number;
47
+ deltaInboundReceivedPackets: number;
48
+ deltaOutboundSentPackets: number;
51
49
  deltaDataChannelBytesSent: number;
52
50
  deltaDataChannelBytesReceived: number;
51
+ deltaInboundReceivedBytes: number;
52
+ deltaOutboundSentBytes: number;
53
53
  deltaReceivedAudioBytes: number;
54
54
  deltaReceivedVideoBytes: number;
55
+ deltaReceivedAudioPackets: number;
56
+ deltaReceivedVideoPackets: number;
55
57
  deltaSentAudioBytes: number;
56
58
  deltaSentVideoBytes: number;
57
59
  sendingAudioBitrate: number;
@@ -81,8 +83,11 @@ export declare class ObservedPeerConnection extends EventEmitter {
81
83
  get inboundVideoTracks(): ReadonlyMap<string, ObservedInboundTrack<'video'>>;
82
84
  get outboundAudioTracks(): ReadonlyMap<string, ObservedOutboundTrack<'audio'>>;
83
85
  get outboundVideoTracks(): ReadonlyMap<string, ObservedOutboundTrack<'video'>>;
86
+ get uptimeInMs(): number;
84
87
  get closed(): boolean;
85
88
  close(): void;
89
+ getTrack(trackId: string): ObservedInboundTrack<'audio'> | ObservedInboundTrack<'video'> | ObservedOutboundTrack<'audio'> | ObservedOutboundTrack<'video'> | undefined;
90
+ resetMetrics(): void;
86
91
  updateMetrics(): void;
87
92
  update(sample: PeerConnectionTransport, timestamp: number): void;
88
93
  updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate): void;
@@ -1 +1 @@
1
- {"version":3,"file":"ObservedPeerConnection.d.ts","sourceRoot":"","sources":["../src/ObservedPeerConnection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObservedPeerConnection.d.ts","sourceRoot":"","sources":["../src/ObservedPeerConnection.ts"],"names":[],"mappings":"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"}
@@ -60,26 +60,31 @@ class ObservedPeerConnection extends events_1.EventEmitter {
60
60
  this._model = _model;
61
61
  this.client = client;
62
62
  this._storageProvider = _storageProvider;
63
+ this.created = Date.now();
63
64
  this.totalInboundPacketsLost = 0;
64
65
  this.totalInboundPacketsReceived = 0;
65
- this.totalOutboundPacketsLost = 0;
66
- this.totalOutboundPacketsReceived = 0;
67
66
  this.totalOutboundPacketsSent = 0;
68
67
  this.totalDataChannelBytesSent = 0;
69
68
  this.totalDataChannelBytesReceived = 0;
70
69
  this.totalSentAudioBytes = 0;
71
70
  this.totalSentVideoBytes = 0;
71
+ this.totalSentAudioPackets = 0;
72
+ this.totalSentVideoPackets = 0;
73
+ this.totalReceivedAudioPacktes = 0;
74
+ this.totalReceivedVideoPackets = 0;
72
75
  this.totalReceivedAudioBytes = 0;
73
76
  this.totalReceivedVideoBytes = 0;
74
- this.deltaInboundPacketsLost = 0;
75
- this.deltaInboundPacketsReceived = 0;
76
- this.deltaOutboundPacketsLost = 0;
77
- this.deltaOutboundPacketsReceived = 0;
78
- this.deltaOutboundPacketsSent = 0;
77
+ this.deltaInboundLostPackets = 0;
78
+ this.deltaInboundReceivedPackets = 0;
79
+ this.deltaOutboundSentPackets = 0;
79
80
  this.deltaDataChannelBytesSent = 0;
80
81
  this.deltaDataChannelBytesReceived = 0;
82
+ this.deltaInboundReceivedBytes = 0;
83
+ this.deltaOutboundSentBytes = 0;
81
84
  this.deltaReceivedAudioBytes = 0;
82
85
  this.deltaReceivedVideoBytes = 0;
86
+ this.deltaReceivedAudioPackets = 0;
87
+ this.deltaReceivedVideoPackets = 0;
83
88
  this.deltaSentAudioBytes = 0;
84
89
  this.deltaSentVideoBytes = 0;
85
90
  this.sendingAudioBitrate = 0;
@@ -126,6 +131,9 @@ class ObservedPeerConnection extends events_1.EventEmitter {
126
131
  get outboundVideoTracks() {
127
132
  return this._outboundVideoTracks;
128
133
  }
134
+ get uptimeInMs() {
135
+ return this._updated - this.created;
136
+ }
129
137
  get closed() {
130
138
  return this._closed;
131
139
  }
@@ -141,28 +149,94 @@ class ObservedPeerConnection extends events_1.EventEmitter {
141
149
  .catch(() => void 0)
142
150
  .finally(() => this.emit('close'));
143
151
  }
144
- updateMetrics() {
145
- this.deltaInboundPacketsLost = 0;
146
- this.deltaInboundPacketsReceived = 0;
147
- this.deltaOutboundPacketsLost = 0;
148
- this.deltaOutboundPacketsReceived = 0;
149
- this.deltaOutboundPacketsSent = 0;
150
- this.sendingAudioBitrate = 0;
151
- this.sendingVideoBitrate = 0;
152
- this.receivingAudioBitrate = 0;
153
- this.receivingVideoBitrate = 0;
152
+ getTrack(trackId) {
153
+ var _a, _b, _c;
154
+ return (_c = (_b = (_a = this._inboundAudioTracks.get(trackId)) !== null && _a !== void 0 ? _a : this._inboundVideoTracks.get(trackId)) !== null && _b !== void 0 ? _b : this._outboundAudioTracks.get(trackId)) !== null && _c !== void 0 ? _c : this._outboundVideoTracks.get(trackId);
155
+ }
156
+ resetMetrics() {
157
+ this.deltaInboundLostPackets = 0;
158
+ this.deltaInboundReceivedPackets = 0;
159
+ this.deltaOutboundSentPackets = 0;
154
160
  this.deltaDataChannelBytesSent = 0;
155
161
  this.deltaDataChannelBytesReceived = 0;
156
162
  this.deltaReceivedAudioBytes = 0;
157
163
  this.deltaReceivedVideoBytes = 0;
158
164
  this.deltaSentAudioBytes = 0;
159
165
  this.deltaSentVideoBytes = 0;
166
+ this._inboundAudioTracks.forEach((track) => track.resetMetrics());
167
+ this._inboundVideoTracks.forEach((track) => track.resetMetrics());
168
+ this._outboundAudioTracks.forEach((track) => track.resetMetrics());
169
+ this._outboundVideoTracks.forEach((track) => track.resetMetrics());
170
+ }
171
+ updateMetrics() {
172
+ this.totalInboundPacketsLost = 0;
173
+ this.totalInboundPacketsReceived = 0;
174
+ this.totalOutboundPacketsSent = 0;
175
+ this.totalDataChannelBytesSent = 0;
176
+ this.totalDataChannelBytesReceived = 0;
177
+ this.totalSentAudioBytes = 0;
178
+ this.totalSentVideoBytes = 0;
179
+ this.totalReceivedAudioBytes = 0;
180
+ this.totalReceivedVideoBytes = 0;
181
+ this.sendingAudioBitrate = 0;
182
+ this.sendingVideoBitrate = 0;
183
+ this.receivingAudioBitrate = 0;
184
+ this.receivingVideoBitrate = 0;
185
+ let sumRttInS = 0;
160
186
  this._inboundAudioTracks.forEach((track) => {
161
- this.deltaReceivedAudioBytes += track.receivedPackets;
187
+ var _a;
188
+ this.deltaInboundLostPackets += track.deltaLostPackets;
189
+ this.deltaInboundReceivedPackets += track.deltaReceivedPackets;
190
+ this.deltaInboundReceivedBytes += track.deltaBytesReceived;
191
+ this.deltaReceivedAudioBytes += track.deltaBytesReceived;
192
+ this.deltaReceivedAudioPackets += track.deltaReceivedPackets;
193
+ this.receivingAudioBitrate += track.bitrate;
194
+ this.totalInboundPacketsLost += track.totalLostPackets;
195
+ this.totalInboundPacketsReceived += track.totalReceivedPackets;
196
+ this.totalReceivedAudioBytes += track.totalBytesReceived;
197
+ this.totalReceivedAudioPacktes += track.totalReceivedPackets;
198
+ sumRttInS = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0) / 1000.0;
162
199
  });
163
200
  this._inboundVideoTracks.forEach((track) => {
164
- this.deltaReceivedVideoBytes += track.receivedPackets;
201
+ var _a;
202
+ this.deltaInboundLostPackets += track.deltaLostPackets;
203
+ this.deltaInboundReceivedPackets += track.deltaReceivedPackets;
204
+ this.deltaInboundReceivedBytes += track.deltaBytesReceived;
205
+ this.deltaReceivedVideoBytes += track.deltaBytesReceived;
206
+ this.deltaReceivedVideoPackets += track.deltaReceivedPackets;
207
+ this.receivingVideoBitrate += track.bitrate;
208
+ this.totalInboundPacketsLost += track.totalLostPackets;
209
+ this.totalInboundPacketsReceived += track.totalReceivedPackets;
210
+ this.totalReceivedVideoBytes += track.totalBytesReceived;
211
+ this.totalReceivedVideoPackets += track.totalReceivedPackets;
212
+ sumRttInS = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0) / 1000.0;
213
+ });
214
+ this._outboundAudioTracks.forEach((track) => {
215
+ var _a;
216
+ this.deltaOutboundSentPackets += track.deltaSentPackets;
217
+ this.deltaOutboundSentBytes += track.deltaSentBytes;
218
+ this.deltaSentAudioBytes += track.deltaSentBytes;
219
+ this.deltaSentAudioBytes += track.deltaSentPackets;
220
+ this.sendingAudioBitrate += track.bitrate;
221
+ this.totalOutboundPacketsSent += track.totalSentPackets;
222
+ this.totalSentAudioBytes += track.totalSentBytes;
223
+ this.totalSentAudioPackets += track.totalSentPackets;
224
+ sumRttInS = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0) / 1000.0;
225
+ });
226
+ this._outboundVideoTracks.forEach((track) => {
227
+ var _a;
228
+ this.deltaOutboundSentPackets += track.deltaSentPackets;
229
+ this.deltaOutboundSentBytes += track.deltaSentBytes;
230
+ this.deltaSentVideoBytes += track.deltaSentBytes;
231
+ this.deltaSentVideoBytes += track.deltaSentPackets;
232
+ this.sendingVideoBitrate += track.bitrate;
233
+ this.totalOutboundPacketsSent += track.totalSentPackets;
234
+ this.totalSentVideoBytes += track.totalSentBytes;
235
+ this.totalSentVideoPackets += track.totalSentPackets;
236
+ sumRttInS = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0) / 1000.0;
165
237
  });
238
+ const nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
239
+ this.avgRttInS = 0 < nrOfBelongings ? sumRttInS / nrOfBelongings : undefined;
166
240
  }
167
241
  update(sample, timestamp) {
168
242
  if (this._closed)
@@ -187,6 +261,11 @@ class ObservedPeerConnection extends events_1.EventEmitter {
187
261
  kind: 'audio',
188
262
  trackId: config.trackId,
189
263
  }, this, this._storageProvider);
264
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
265
+ if (pendingTracksTimestamp) {
266
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
267
+ result.created = pendingTracksTimestamp;
268
+ }
190
269
  result.on('close', () => {
191
270
  this._inboundAudioTracks.delete(result.trackId);
192
271
  this._model.inboundTrackIds = this._model.inboundTrackIds.filter((id) => id !== result.trackId);
@@ -207,6 +286,11 @@ class ObservedPeerConnection extends events_1.EventEmitter {
207
286
  kind: 'video',
208
287
  trackId: config.trackId,
209
288
  }, this, this._storageProvider);
289
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
290
+ if (pendingTracksTimestamp) {
291
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
292
+ result.created = pendingTracksTimestamp;
293
+ }
210
294
  result.on('close', () => {
211
295
  this._inboundVideoTracks.delete(result.trackId);
212
296
  this._model.inboundTrackIds = this._model.inboundTrackIds.filter((id) => id !== result.trackId);
@@ -227,6 +311,11 @@ class ObservedPeerConnection extends events_1.EventEmitter {
227
311
  kind: 'audio',
228
312
  trackId: config.trackId,
229
313
  }, this, this._storageProvider);
314
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
315
+ if (pendingTracksTimestamp) {
316
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
317
+ result.created = pendingTracksTimestamp;
318
+ }
230
319
  result.on('close', () => {
231
320
  this._outboundAudioTracks.delete(result.trackId);
232
321
  this._model.outboundTrackIds = this._model.outboundTrackIds.filter((id) => id !== result.trackId);
@@ -249,6 +338,11 @@ class ObservedPeerConnection extends events_1.EventEmitter {
249
338
  kind: 'video',
250
339
  trackId: config.trackId,
251
340
  }, this, this._storageProvider);
341
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
342
+ if (pendingTracksTimestamp) {
343
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
344
+ result.created = pendingTracksTimestamp;
345
+ }
252
346
  result.on('close', () => {
253
347
  this._outboundVideoTracks.delete(result.trackId);
254
348
  result.sfuStreamId && this.client.call.sfuStreamIdToOutboundVideoTrack.set(result.sfuStreamId, result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@observertc/observer-js",
3
- "version": "0.30.2-beta",
3
+ "version": "0.30.4-beta",
4
4
  "description": "Server Side NodeJS Library for processing ObserveRTC Samples",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { ClientSample } from '@observertc/sample-schemas-js';
1
+ import { ClientSample, IceLocalCandidate, IceRemoteCandidate } from '@observertc/sample-schemas-js';
2
2
  import { ObservedCall } from './ObservedCall';
3
3
  import * as Models from './models/Models';
4
4
  import { StorageProvider } from './storages/StorageProvider';
@@ -34,6 +34,23 @@ export type ObservedClientEvents = {
34
34
  close: [],
35
35
  'processing-sample-overflow': [],
36
36
  newpeerconnection: [ObservedPeerConnection],
37
+ iceconnectionstatechange: [{
38
+ peerConnection: ObservedPeerConnection,
39
+ state: string,
40
+ }],
41
+ icegatheringstatechange: [{
42
+ peerConnection: ObservedPeerConnection,
43
+ state: string,
44
+ }],
45
+ connectionstatechange: [{
46
+ peerConnection: ObservedPeerConnection,
47
+ state: string,
48
+ }],
49
+ selectedcandidatepair: [{
50
+ peerConnection: ObservedPeerConnection,
51
+ localCandidate: IceLocalCandidate,
52
+ remoteCandidate: IceRemoteCandidate,
53
+ }],
37
54
  };
38
55
 
39
56
  export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
@@ -88,6 +105,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
88
105
  return result;
89
106
  }
90
107
 
108
+ public created = Date.now();
109
+
91
110
  private readonly _peerConnections = new Map<string, ObservedPeerConnection>();
92
111
  private readonly _execute = createSingleExecutor();
93
112
 
@@ -101,38 +120,23 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
101
120
 
102
121
  public totalInboundPacketsLost = 0;
103
122
  public totalInboundPacketsReceived = 0;
104
- public totalOutboundPacketsLost = 0;
105
- public totalOutboundPacketsReceived = 0;
106
123
  public totalOutboundPacketsSent = 0;
107
124
  public totalDataChannelBytesSent = 0;
108
125
  public totalDataChannelBytesReceived = 0;
109
- public totalSentAudioBytes = 0;
110
- public totalSentVideoBytes = 0;
111
- public totalReceivedAudioBytes = 0;
112
- public totalReceivedVideoBytes = 0;
113
-
114
- public deltaInboundPacketsLost?: number;
115
- public deltaInboundPacketsReceived?: number;
116
- public deltaOutboundPacketsLost?: number;
117
- public deltaOutboundPacketsReceived?: number;
118
- public deltaOutboundPacketsSent?: number;
119
- public deltaDataChannelBytesSent?: number;
120
- public deltaDataChannelBytesReceived?: number;
121
- public deltaReceivedAudioBytes?: number;
122
- public deltaReceivedVideoBytes?: number;
123
- public deltaSentAudioBytes?: number;
124
- public deltaSentVideoBytes?: number;
126
+ public totalSentBytes = 0;
127
+ public totalReceivedBytes = 0;
125
128
 
126
129
  public avgRttInS?: number;
127
- public sendingAudioBitrate?: number;
128
- public sendingVideoBitrate?: number;
129
- public receivingAudioBitrate?: number;
130
- public receivingVideoBitrate?: number;
130
+ public sendingBitrate?: number;
131
+ public receivingBitrate?: number;
131
132
 
132
133
  public readonly mediaDevices: string[] = [];
133
134
  public readonly codecs: string[] = [];
134
135
  public readonly userMediaErrors: string[] = [];
135
136
 
137
+ public readonly ωpendingCreatedTracksTimestamp = new Map<string, number>();
138
+ public readonly ωpendingCreatedPeerConnectionTimestamp = new Map<string, number>();
139
+
136
140
  private constructor(
137
141
  private readonly _model: Models.Client,
138
142
  public readonly call: ObservedCall,
@@ -212,6 +216,10 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
212
216
  return this._updated;
213
217
  }
214
218
 
219
+ public get uptimeInMs() {
220
+ return this._updated - this.created;
221
+ }
222
+
215
223
  public get closed() {
216
224
  return this._closed;
217
225
  }
@@ -244,6 +252,11 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
244
252
  }
245
253
 
246
254
  return this._execute(async () => {
255
+ for (const peerConnection of this._peerConnections.values()) {
256
+ if (peerConnection.closed) continue;
257
+ peerConnection.resetMetrics();
258
+ }
259
+
247
260
  let executeSave = false;
248
261
 
249
262
  if (this.userId !== sample.userId) {
@@ -398,22 +411,54 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
398
411
  });
399
412
  }
400
413
 
401
- for (const { timestamp = Date.now(), ...callEvent } of sample.customCallEvents ?? []) {
414
+ for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
402
415
  const peerConnection = this._peerConnections.get(callEvent.peerConnectionId ?? '');
403
416
 
404
417
  switch (callEvent.name) {
418
+ case CallEventType.CLIENT_JOINED:
419
+ this.created = timestamp ?? this.created;
420
+ break;
405
421
  case CallEventType.CLIENT_LEFT:
406
422
  this._leaveReported = true;
407
423
  break;
408
424
  case CallEventType.ICE_CONNECTION_STATE_CHANGED:
409
- peerConnection && callEvent.value && peerConnection.emit('iceconnectionstatechange', callEvent.value);
425
+ peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
426
+ peerConnection,
427
+ state: callEvent.value,
428
+ });
410
429
  break;
411
430
  case CallEventType.ICE_GATHERING_STATE_CHANGED:
412
- peerConnection && callEvent.value && peerConnection.emit('icegatheringstatechange', callEvent.value);
431
+ peerConnection && callEvent.value && this.emit('icegatheringstatechange', {
432
+ peerConnection,
433
+ state: callEvent.value,
434
+ });
413
435
  break;
414
436
  case CallEventType.PEER_CONNECTION_STATE_CHANGED:
415
- peerConnection && callEvent.value && peerConnection.emit('connectionstatechange', callEvent.value);
437
+ peerConnection && callEvent.value && this.emit('connectionstatechange', {
438
+ peerConnection,
439
+ state: callEvent.value,
440
+ });
416
441
  break;
442
+ case CallEventType.PEER_CONNECTION_OPENED: {
443
+ if (!callEvent.peerConnectionId) break;
444
+ if (peerConnection) {
445
+ peerConnection.created = timestamp ?? peerConnection.created;
446
+ } else {
447
+ this.ωpendingCreatedPeerConnectionTimestamp.set(callEvent.peerConnectionId, timestamp ?? Date.now());
448
+ }
449
+ break;
450
+ }
451
+ case CallEventType.MEDIA_TRACK_ADDED: {
452
+ if (!callEvent.mediaTrackId) break;
453
+ const track = peerConnection?.getTrack(callEvent.mediaTrackId);
454
+
455
+ if (track) {
456
+ track.created = timestamp ?? track.created;
457
+ } else {
458
+ this.ωpendingCreatedTracksTimestamp.set(callEvent.mediaTrackId, timestamp ?? Date.now());
459
+ }
460
+ }
461
+
417
462
  }
418
463
 
419
464
  this.reports.addCallEventReport({
@@ -423,7 +468,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
423
468
  callId: this.callId,
424
469
  clientId: this.clientId,
425
470
  userId: this.userId,
426
- timestamp,
471
+ timestamp: timestamp ?? Date.now(),
427
472
  ...callEvent,
428
473
  });
429
474
  }
@@ -712,6 +757,35 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
712
757
  }
713
758
  }
714
759
  }
760
+
761
+ // update metrics
762
+ this.totalSentBytes = 0;
763
+ this.totalReceivedBytes = 0;
764
+ this.totalOutboundPacketsSent = 0;
765
+ this.totalInboundPacketsReceived = 0;
766
+ this.totalInboundPacketsLost = 0;
767
+ this.totalDataChannelBytesSent = 0;
768
+ this.totalDataChannelBytesReceived = 0;
769
+ this.sendingBitrate = 0;
770
+ this.receivingBitrate = 0;
771
+ let sumRttInS = 0;
772
+
773
+ for (const peerConnection of this._peerConnections.values()) {
774
+ if (peerConnection.closed) continue;
775
+ peerConnection.updateMetrics();
776
+ this.totalSentBytes += peerConnection.totalSentAudioBytes + peerConnection.totalSentVideoBytes;
777
+ this.totalReceivedBytes += peerConnection.totalReceivedAudioBytes + peerConnection.totalReceivedVideoBytes;
778
+ this.totalOutboundPacketsSent += peerConnection.totalOutboundPacketsSent;
779
+ this.totalInboundPacketsReceived += peerConnection.totalInboundPacketsReceived;
780
+ this.totalInboundPacketsLost += peerConnection.totalInboundPacketsLost;
781
+ this.totalDataChannelBytesSent += peerConnection.totalDataChannelBytesSent;
782
+ this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
783
+ this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
784
+ this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
785
+ sumRttInS += peerConnection.avgRttInS ?? 0;
786
+ }
787
+
788
+ this.avgRttInS = this._peerConnections.size ? sumRttInS / this._peerConnections.size : -1;
715
789
 
716
790
  if (executeSave) await this._save();
717
791
 
@@ -727,11 +801,28 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
727
801
  peerConnectionId,
728
802
  }, this, this._storageProvider);
729
803
 
804
+ const pendingTracksTimestamp = this.ωpendingCreatedTracksTimestamp.get(peerConnectionId);
805
+
806
+ if (pendingTracksTimestamp) {
807
+ result.created = pendingTracksTimestamp;
808
+ this.ωpendingCreatedTracksTimestamp.delete(peerConnectionId);
809
+ }
810
+
811
+ const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }: { localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate }) => {
812
+ this.emit('selectedcandidatepair', {
813
+ peerConnection: result,
814
+ localCandidate,
815
+ remoteCandidate,
816
+ });
817
+ };
818
+
730
819
  result.once('close', () => {
820
+ result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
731
821
  this._peerConnections.delete(peerConnectionId);
732
822
  this._model.peerConnectionIds = this._model.peerConnectionIds.filter((id) => id !== peerConnectionId);
733
823
  this._save().catch(() => void 0);
734
824
  });
825
+ result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
735
826
  this._peerConnections.set(peerConnectionId, result);
736
827
  this._model.peerConnectionIds.push(peerConnectionId);
737
828
  await this._save();