@observertc/observer-js 0.30.1-beta → 0.30.3-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.
Files changed (39) hide show
  1. package/lib/ObservedClient.d.ts +15 -6
  2. package/lib/ObservedClient.d.ts.map +1 -1
  3. package/lib/ObservedClient.js +146 -73
  4. package/lib/ObservedICE.d.ts +47 -0
  5. package/lib/ObservedICE.d.ts.map +1 -0
  6. package/lib/ObservedICE.js +105 -0
  7. package/lib/ObservedInboundTrack.d.ts +29 -1
  8. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  9. package/lib/ObservedInboundTrack.js +78 -8
  10. package/lib/ObservedOutboundTrack.d.ts +20 -3
  11. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  12. package/lib/ObservedOutboundTrack.js +53 -9
  13. package/lib/ObservedPeerConnection.d.ts +47 -1
  14. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  15. package/lib/ObservedPeerConnection.js +155 -0
  16. package/lib/common/CallEventType.d.ts +22 -0
  17. package/lib/common/CallEventType.d.ts.map +1 -0
  18. package/lib/common/CallEventType.js +26 -0
  19. package/lib/common/callEventReports.d.ts +0 -10
  20. package/lib/common/callEventReports.d.ts.map +1 -1
  21. package/lib/common/callEventReports.js +10 -21
  22. package/lib/models/Models.d.ts +3 -70
  23. package/lib/models/Models.d.ts.map +1 -1
  24. package/lib/models/Models.js +2 -72
  25. package/lib/models/samples_pb.js +1 -1
  26. package/package.json +1 -1
  27. package/src/ObservedClient.ts +146 -89
  28. package/src/ObservedICE.ts +176 -0
  29. package/src/ObservedInboundTrack.ts +97 -3
  30. package/src/ObservedOutboundTrack.ts +70 -9
  31. package/src/ObservedPeerConnection.ts +205 -1
  32. package/src/common/CallEventType.ts +23 -0
  33. package/src/common/callEventReports.ts +1 -11
  34. package/src/models/Models.ts +4 -113
  35. package/src/models/samples_pb.ts +1 -1
  36. package/lib/common/ReportsCollector.d.ts +0 -23
  37. package/lib/common/ReportsCollector.d.ts.map +0 -1
  38. package/lib/common/ReportsCollector.js +0 -26
  39. package/src/common/ReportsCollector.ts +0 -71
@@ -28,6 +28,14 @@ export type ObservedInboundTrackStats<K extends MediaKind> = ObservedInboundTrac
28
28
  bitrate: number;
29
29
  fractionLost: number;
30
30
  rttInMs?: number;
31
+ lostPackets?: number;
32
+ receivedPackets?: number;
33
+ receivedFrames?: number;
34
+ decodedFrames?: number;
35
+ droppedFrames?: number;
36
+ receivedSamples?: number;
37
+ silentConcealedSamples?: number;
38
+ fractionLoss?: number;
31
39
  };
32
40
 
33
41
  // {
@@ -67,6 +75,8 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
67
75
 
68
76
  return new ObservedInboundTrack<K>(model, peerConnection, storageProvider);
69
77
  }
78
+
79
+ public created = Date.now();
70
80
 
71
81
  private readonly _stats = new Map<number, ObservedInboundTrackStats<Kind>>();
72
82
  private readonly _execute = createSingleExecutor();
@@ -74,8 +84,27 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
74
84
  private _closed = false;
75
85
  private _updated = Date.now();
76
86
  private _remoteOutboundTrack?: ObservedOutboundTrack<Kind>;
77
- public bitrate = -1;
78
- public rttInMs = -1;
87
+ public bitrate = 0;
88
+ public rttInMs?: number;
89
+ public fractionLoss = 0;
90
+
91
+ public totalLostPackets = 0;
92
+ public totalReceivedPackets = 0;
93
+ public totalBytesReceived = 0;
94
+ public totalReceivedFrames = 0;
95
+ public totalDecodedFrames = 0;
96
+ public totalDroppedFrames = 0;
97
+ public totalReceivedSamples = 0;
98
+ public totalSilentConcealedSamples = 0;
99
+
100
+ public deltaLostPackets = 0;
101
+ public deltaReceivedPackets = 0;
102
+ public deltaBytesReceived = 0;
103
+ public deltaReceivedFrames = 0;
104
+ public deltaDecodedFrames = 0;
105
+ public deltaDroppedFrames = 0;
106
+ public deltaReceivedSamples = 0;
107
+ public deltaSilentConcealedSamples = 0;
79
108
 
80
109
  private constructor(
81
110
  private readonly _model: Models.InboundTrack,
@@ -145,6 +174,10 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
145
174
  return this.peerConnection.reports;
146
175
  }
147
176
 
177
+ public get uptimeInMs() {
178
+ return this._updated - this.created;
179
+ }
180
+
148
181
  public set remoteOutboundTrack(track: ObservedOutboundTrack<Kind> | undefined) {
149
182
  if (this._closed) return;
150
183
  if (this._remoteOutboundTrack) return;
@@ -174,6 +207,19 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
174
207
  .finally(() => this.emit('close'));
175
208
  }
176
209
 
210
+ public resetMetrics() {
211
+ this.bitrate = 0;
212
+ this.rttInMs = undefined;
213
+ this.deltaBytesReceived = 0;
214
+ this.deltaLostPackets = 0;
215
+ this.deltaReceivedFrames = 0;
216
+ this.deltaDecodedFrames = 0;
217
+ this.deltaDroppedFrames = 0;
218
+ this.deltaReceivedSamples = 0;
219
+ this.deltaSilentConcealedSamples = 0;
220
+ this.fractionLoss = 0;
221
+ }
222
+
177
223
  public async update(sample: ObservedInboundTrackStatsUpdate<Kind>, timestamp: number) {
178
224
  if (this._closed) return;
179
225
  let executeSave = false;
@@ -189,6 +235,11 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
189
235
  ...sample,
190
236
  timestamp,
191
237
  sampleSeq: -1,
238
+
239
+ remoteClientId: sample.remoteClientId ?? this.remoteOutboundTrack?.peerConnection.client.clientId,
240
+ remoteUserId: this.remoteOutboundTrack?.peerConnection.client.userId,
241
+ remoteTrackId: this.remoteOutboundTrack?.trackId,
242
+ remotePeerConnectionId: this.remoteOutboundTrack?.peerConnection.peerConnectionId,
192
243
  };
193
244
 
194
245
  if (this.kind === 'audio') this.reports.addInboundAudioTrackReport(report);
@@ -211,19 +262,62 @@ export class ObservedInboundTrack<Kind extends MediaKind> extends EventEmitter {
211
262
  const lostPackets = (sample.packetsLost ?? 0) - (lastStat?.packetsLost ?? 0);
212
263
  const receivedPackets = (sample.packetsReceived ?? 0) - (lastStat?.packetsReceived ?? 0);
213
264
  const fractionLost = 0 < receivedPackets && 0 < lostPackets ? (lostPackets / (receivedPackets + lostPackets)) : 0;
265
+ let decodedFrames: number | undefined;
266
+ let droppedFrames: number | undefined;
267
+ let receivedFrames: number | undefined;
268
+ let silentConcealedSamples: number | undefined;
269
+
270
+ if (this.kind === 'video') {
271
+ const videoSample = sample as InboundVideoTrack;
272
+ const lastVideoStat = lastStat as InboundVideoTrack | undefined;
273
+
274
+ decodedFrames = (videoSample.framesDecoded ?? 0) - (lastVideoStat?.framesDecoded ?? 0);
275
+ droppedFrames = (videoSample.framesDropped ?? 0) - (lastVideoStat?.framesDropped ?? 0);
276
+ receivedFrames = (videoSample.framesReceived ?? 0) - (lastVideoStat?.framesReceived ?? 0);
277
+ } else if (this.kind === 'audio') {
278
+ const audioSample = sample as InboundAudioTrack;
279
+ const lastAudioStat = lastStat as InboundAudioTrack | undefined;
280
+
281
+ silentConcealedSamples = (audioSample.silentConcealedSamples ?? 0) - (lastAudioStat?.silentConcealedSamples ?? 0);
282
+ }
283
+
214
284
  const stats: ObservedInboundTrackStats<Kind> = {
215
285
  ...sample,
216
286
  fractionLost,
217
287
  rttInMs,
218
288
  bitrate,
219
289
  ssrc: sample.ssrc,
290
+ lostPackets,
291
+ receivedPackets,
292
+ receivedFrames,
293
+ decodedFrames,
294
+ droppedFrames,
295
+ silentConcealedSamples,
220
296
  };
221
297
 
222
298
  this._stats.set(sample.ssrc, stats);
223
299
 
224
300
  this.bitrate = [ ...this._stats.values() ].reduce((acc, stat) => acc + stat.bitrate, 0);
225
301
  this.rttInMs = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.rttInMs ?? 0), 0) / (this._stats.size || 1);
226
-
302
+ this.totalLostPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.lostPackets ?? 0), 0);
303
+ this.totalReceivedPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedPackets ?? 0), 0);
304
+ this.totalBytesReceived = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.bytesReceived ?? 0), 0);
305
+ this.totalReceivedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedFrames ?? 0), 0);
306
+ this.totalDecodedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.decodedFrames ?? 0), 0);
307
+ this.totalDroppedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.droppedFrames ?? 0), 0);
308
+ this.totalReceivedSamples = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedSamples ?? 0), 0);
309
+ this.totalSilentConcealedSamples = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.silentConcealedSamples ?? 0), 0);
310
+
311
+ this.deltaLostPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.lostPackets ?? 0), 0);
312
+ this.deltaReceivedPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedPackets ?? 0), 0);
313
+ this.deltaBytesReceived = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.bytesReceived ?? 0), 0);
314
+ this.deltaReceivedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedFrames ?? 0), 0);
315
+ this.deltaDecodedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.decodedFrames ?? 0), 0);
316
+ this.deltaDroppedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.droppedFrames ?? 0), 0);
317
+ this.deltaReceivedSamples = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.receivedSamples ?? 0), 0);
318
+ this.deltaSilentConcealedSamples = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.silentConcealedSamples ?? 0), 0);
319
+ this.fractionLoss = 0 < this.deltaReceivedPackets && 0 < this.deltaLostPackets ? (this.deltaLostPackets / (this.deltaReceivedPackets + this.deltaLostPackets)) : 0;
320
+
227
321
  if (executeSave) await this._save();
228
322
 
229
323
  this._updated = timestamp;
@@ -25,6 +25,12 @@ export type ObservedOutboundTrackStats<K extends MediaKind> = ObservedOutboundTr
25
25
  ssrc: number;
26
26
  bitrate: number;
27
27
  rttInMs?: number;
28
+
29
+ deltaLostPackets: number;
30
+ deltaSentPackets: number;
31
+ deltaSentBytes: number;
32
+ deltaSentFrames?: number;
33
+ deltaEncodedFrames?: number;
28
34
  };
29
35
 
30
36
  // export type ObservedOutboundTrackStatsUpdate<K extends MediaKind> = {
@@ -64,9 +70,22 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
64
70
  return new ObservedOutboundTrack<K>(model, peerConnection, storageProvider);
65
71
  }
66
72
 
67
- public bitrate?: number;
73
+ public bitrate = 0;
68
74
  public rttInMs?: number;
69
75
 
76
+ public totalLostPackets = 0;
77
+ public totalSentPackets = 0;
78
+ public totalSentBytes = 0;
79
+ public totalSentFrames = 0;
80
+
81
+ public deltaLostPackets = 0;
82
+ public deltaSentPackets = 0;
83
+ public deltaSentBytes = 0;
84
+ public deltaSentFrames = 0;
85
+ public deltaEncodedFrames = 0;
86
+
87
+ public created = Date.now();
88
+
70
89
  private readonly _stats = new Map<number, ObservedOutboundTrackStats<Kind>>();
71
90
  private readonly _execute = createSingleExecutor();
72
91
 
@@ -96,10 +115,6 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
96
115
  return this._model.sfuStreamId;
97
116
  }
98
117
 
99
- public get closed() {
100
- return this._closed;
101
- }
102
-
103
118
  public get remoteInboundTracks(): ReadonlyMap<string, ObservedInboundTrack<Kind>> {
104
119
  return this._remoteInboundTracks;
105
120
  }
@@ -111,7 +126,19 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
111
126
  public get updated() {
112
127
  return this._updated;
113
128
  }
129
+
130
+ public get stats(): ReadonlyMap<number, ObservedOutboundTrackStats<Kind>> {
131
+ return this._stats;
132
+ }
114
133
 
134
+ public get uptimeInMs() {
135
+ return this._updated - this.created;
136
+ }
137
+
138
+ public get closed() {
139
+ return this._closed;
140
+ }
141
+
115
142
  public close() {
116
143
  if (this._closed) return;
117
144
 
@@ -122,8 +149,15 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
122
149
  .finally(() => this.emit('close'));
123
150
  }
124
151
 
125
- public get stats(): ReadonlyMap<number, ObservedOutboundTrackStats<Kind>> {
126
- return this._stats;
152
+ public resetMetrics() {
153
+ this.bitrate = 0;
154
+ this.rttInMs = 0;
155
+
156
+ this.deltaLostPackets = 0;
157
+ this.deltaSentPackets = 0;
158
+ this.deltaSentBytes = 0;
159
+ this.deltaSentFrames = 0;
160
+ this.deltaEncodedFrames = 0;
127
161
  }
128
162
 
129
163
  public async update(sample: ObservedOutboundTrackStatsUpdate<Kind>, timestamp: number): Promise<void> {
@@ -150,13 +184,30 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
150
184
  const lastStat = this._stats.get(sample.ssrc);
151
185
  const rttInMs = sample.roundTripTime ? sample.roundTripTime * 1000 : undefined;
152
186
  const bitrate = ((sample.bytesSent ?? 0) - (lastStat?.bytesSent ?? 0)) * 8 / (elapsedTimeInMs / 1000);
153
- // const lostPackets = (sample.packetsLost ?? 0) - (lastStat?.packetsLost ?? 0);
154
- // const sentPackets = (sample.packetsSent ?? 0) - (lastStat?.packetsSent ?? 0);
187
+ const deltaLostPackets = (sample.packetsLost ?? 0) - (lastStat?.packetsLost ?? 0);
188
+ const deltaSentPackets = (sample.packetsSent ?? 0) - (lastStat?.packetsSent ?? 0);
189
+ const deltaSentBytes = (sample.bytesSent ?? 0) - (lastStat?.bytesSent ?? 0);
190
+ let deltaEncodedFrames: number | undefined;
191
+ let deltaSentFrames: number | undefined;
192
+
193
+ if (this.kind === 'video') {
194
+ const videoSample = sample as OutboundVideoTrack;
195
+ const lastVideoStats = lastStat as OutboundVideoTrack | undefined;
196
+
197
+ deltaEncodedFrames = (videoSample.framesEncoded ?? 0) - (lastVideoStats?.framesEncoded ?? 0);
198
+ deltaSentFrames = (videoSample.framesSent ?? 0) - (lastVideoStats?.framesSent ?? 0);
199
+ }
155
200
  const stats: ObservedOutboundTrackStats<Kind> = {
156
201
  ...sample,
157
202
  rttInMs,
158
203
  bitrate,
159
204
  ssrc: sample.ssrc,
205
+
206
+ deltaLostPackets,
207
+ deltaSentPackets,
208
+ deltaSentBytes,
209
+ deltaEncodedFrames,
210
+ deltaSentFrames,
160
211
  };
161
212
 
162
213
  this._stats.set(sample.ssrc, stats);
@@ -164,6 +215,16 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
164
215
  this.bitrate = [ ...this._stats.values() ].reduce((acc, stat) => acc + stat.bitrate, 0);
165
216
  this.rttInMs = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.rttInMs ?? 0), 0) / (this._stats.size || 1);
166
217
 
218
+ this.totalLostPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.packetsLost ?? 0), 0);
219
+ this.totalSentPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.packetsSent ?? 0), 0);
220
+ this.totalSentBytes = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.bytesSent ?? 0), 0);
221
+
222
+ this.deltaEncodedFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.deltaEncodedFrames ?? 0), 0);
223
+ this.deltaSentFrames = [ ...this._stats.values() ].reduce((acc, stat) => acc + (stat.deltaSentFrames ?? 0), 0);
224
+ this.deltaLostPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + stat.deltaLostPackets, 0);
225
+ this.deltaSentPackets = [ ...this._stats.values() ].reduce((acc, stat) => acc + stat.deltaSentPackets, 0);
226
+ this.deltaSentBytes = [ ...this._stats.values() ].reduce((acc, stat) => acc + stat.deltaSentBytes, 0);
227
+
167
228
  // setting up sfu connection as it is not always available at the first sample
168
229
  if (sample.sfuStreamId && !this._model.sfuStreamId) {
169
230
  this._model.sfuStreamId = sample.sfuStreamId;
@@ -1,12 +1,13 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import * as Models from './models/Models';
3
3
  import { StorageProvider } from './storages/StorageProvider';
4
- import { PeerConnectionTransport } from '@observertc/sample-schemas-js';
4
+ import { IceLocalCandidate, IceRemoteCandidate, PeerConnectionTransport } from '@observertc/sample-schemas-js';
5
5
  import { ObservedClient } from './ObservedClient';
6
6
  import { ObservedInboundTrack, ObservedInboundTrackConfig } from './ObservedInboundTrack';
7
7
  import { ObservedOutboundTrack, ObservedOutboundTrackConfig } from './ObservedOutboundTrack';
8
8
  import { PeerConnectionTransportReport } from '@observertc/report-schemas-js';
9
9
  import { createSingleExecutor } from './common/SingleExecutor';
10
+ import { ObservedICE } from './ObservedICE';
10
11
 
11
12
  export type ObservedPeerConnectionEvents = {
12
13
  update: [],
@@ -15,6 +16,10 @@ export type ObservedPeerConnectionEvents = {
15
16
  newinboudvideotrack: [ObservedInboundTrack<'video'>],
16
17
  newoutboundaudiotrack: [ObservedOutboundTrack<'audio'>],
17
18
  newoutboundvideotrack: [ObservedOutboundTrack<'video'>],
19
+
20
+ iceconnectionstatechange: [string],
21
+ icegatheringstatechange: [string],
22
+ connectionstatechange: [string],
18
23
  };
19
24
 
20
25
  export type ObservedPeerConnectionConfig = {
@@ -51,10 +56,51 @@ export class ObservedPeerConnection extends EventEmitter {
51
56
  return new ObservedPeerConnection(model, client, storageProvider);
52
57
  }
53
58
 
59
+ public created = Date.now();
60
+
61
+ public totalInboundPacketsLost = 0;
62
+ public totalInboundPacketsReceived = 0;
63
+ public totalOutboundPacketsSent = 0;
64
+ public totalDataChannelBytesSent = 0;
65
+ public totalDataChannelBytesReceived = 0;
66
+
67
+ public totalSentAudioBytes = 0;
68
+ public totalSentVideoBytes = 0;
69
+ public totalSentAudioPackets = 0;
70
+ public totalSentVideoPackets = 0;
71
+ public totalReceivedAudioPacktes = 0;
72
+ public totalReceivedVideoPackets = 0;
73
+ public totalReceivedAudioBytes = 0;
74
+ public totalReceivedVideoBytes = 0;
75
+
76
+ public deltaInboundLostPackets = 0;
77
+ public deltaInboundReceivedPackets = 0;
78
+ public deltaOutboundSentPackets = 0;
79
+ public deltaDataChannelBytesSent = 0;
80
+ public deltaDataChannelBytesReceived = 0;
81
+ public deltaInboundReceivedBytes = 0;
82
+ public deltaOutboundSentBytes = 0;
83
+
84
+ public deltaReceivedAudioBytes = 0;
85
+ public deltaReceivedVideoBytes = 0;
86
+ public deltaReceivedAudioPackets = 0;
87
+ public deltaReceivedVideoPackets = 0;
88
+ public deltaSentAudioBytes = 0;
89
+ public deltaSentVideoBytes = 0;
90
+ public sendingAudioBitrate = 0;
91
+ public sendingVideoBitrate = 0;
92
+ public receivingAudioBitrate = 0;
93
+ public receivingVideoBitrate = 0;
94
+
95
+ public avgRttInS?: number;
96
+
54
97
  private _closed = false;
55
98
  private _updated = Date.now();
56
99
  private _sample?: ObservedPeerConnectionStats;
100
+ private _selectedLocalIceCandidate?: IceLocalCandidate;
101
+ private _selectedRemoteIceCandidate?: IceRemoteCandidate;
57
102
 
103
+ public readonly ICE = ObservedICE.create(this);
58
104
  private readonly _inboundAudioTracks = new Map<string, ObservedInboundTrack<'audio'>>();
59
105
  private readonly _inboundVideoTracks = new Map<string, ObservedInboundTrack<'video'>>();
60
106
  private readonly _outboundAudioTracks = new Map<string, ObservedOutboundTrack<'audio'>>();
@@ -86,6 +132,14 @@ export class ObservedPeerConnection extends EventEmitter {
86
132
  return this._updated;
87
133
  }
88
134
 
135
+ public get selectedLocalIceCandidate() {
136
+ return this._selectedLocalIceCandidate;
137
+ }
138
+
139
+ public get selectedRemoteIceCandidate() {
140
+ return this._selectedRemoteIceCandidate;
141
+ }
142
+
89
143
  public get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundTrack<'audio'>> {
90
144
  return this._inboundAudioTracks;
91
145
  }
@@ -102,6 +156,10 @@ export class ObservedPeerConnection extends EventEmitter {
102
156
  return this._outboundVideoTracks;
103
157
  }
104
158
 
159
+ public get uptimeInMs() {
160
+ return this._updated - this.created;
161
+ }
162
+
105
163
  public get closed() {
106
164
  return this._closed;
107
165
  }
@@ -120,6 +178,119 @@ export class ObservedPeerConnection extends EventEmitter {
120
178
  .finally(() => this.emit('close'));
121
179
  }
122
180
 
181
+ public getTrack(trackId: string): ObservedInboundTrack<'audio'> | ObservedInboundTrack<'video'> | ObservedOutboundTrack<'audio'> | ObservedOutboundTrack<'video'> | undefined {
182
+ return this._inboundAudioTracks.get(trackId) ?? this._inboundVideoTracks.get(trackId) ?? this._outboundAudioTracks.get(trackId) ?? this._outboundVideoTracks.get(trackId);
183
+ }
184
+
185
+ public resetMetrics() {
186
+ this.deltaInboundLostPackets = 0;
187
+ this.deltaInboundReceivedPackets = 0;
188
+ this.deltaOutboundSentPackets = 0;
189
+
190
+ this.deltaDataChannelBytesSent = 0;
191
+ this.deltaDataChannelBytesReceived = 0;
192
+ this.deltaReceivedAudioBytes = 0;
193
+ this.deltaReceivedVideoBytes = 0;
194
+ this.deltaSentAudioBytes = 0;
195
+ this.deltaSentVideoBytes = 0;
196
+
197
+ this._inboundAudioTracks.forEach((track) => track.resetMetrics());
198
+ this._inboundVideoTracks.forEach((track) => track.resetMetrics());
199
+ this._outboundAudioTracks.forEach((track) => track.resetMetrics());
200
+ this._outboundVideoTracks.forEach((track) => track.resetMetrics());
201
+ }
202
+
203
+ public updateMetrics() {
204
+ this.totalInboundPacketsLost = 0;
205
+ this.totalInboundPacketsReceived = 0;
206
+ this.totalOutboundPacketsSent = 0;
207
+ this.totalDataChannelBytesSent = 0;
208
+ this.totalDataChannelBytesReceived = 0;
209
+
210
+ this.totalSentAudioBytes = 0;
211
+ this.totalSentVideoBytes = 0;
212
+ this.totalReceivedAudioBytes = 0;
213
+ this.totalReceivedVideoBytes = 0;
214
+ this.sendingAudioBitrate = 0;
215
+ this.sendingVideoBitrate = 0;
216
+ this.receivingAudioBitrate = 0;
217
+ this.receivingVideoBitrate = 0;
218
+
219
+ let sumRttInS = 0;
220
+
221
+ this._inboundAudioTracks.forEach((track) => {
222
+ this.deltaInboundLostPackets += track.deltaLostPackets;
223
+ this.deltaInboundReceivedPackets += track.deltaReceivedPackets;
224
+ this.deltaInboundReceivedBytes += track.deltaBytesReceived;
225
+
226
+ this.deltaReceivedAudioBytes += track.deltaBytesReceived;
227
+ this.deltaReceivedAudioPackets += track.deltaReceivedPackets;
228
+
229
+ this.receivingAudioBitrate += track.bitrate;
230
+
231
+ this.totalInboundPacketsLost += track.totalLostPackets;
232
+ this.totalInboundPacketsReceived += track.totalReceivedPackets;
233
+ this.totalReceivedAudioBytes += track.totalBytesReceived;
234
+ this.totalReceivedAudioPacktes += track.totalReceivedPackets;
235
+
236
+ sumRttInS = (track.rttInMs ?? 0) / 1000.0;
237
+ });
238
+
239
+ this._inboundVideoTracks.forEach((track) => {
240
+ this.deltaInboundLostPackets += track.deltaLostPackets;
241
+ this.deltaInboundReceivedPackets += track.deltaReceivedPackets;
242
+ this.deltaInboundReceivedBytes += track.deltaBytesReceived;
243
+
244
+ this.deltaReceivedVideoBytes += track.deltaBytesReceived;
245
+ this.deltaReceivedVideoPackets += track.deltaReceivedPackets;
246
+
247
+ this.receivingVideoBitrate += track.bitrate;
248
+
249
+ this.totalInboundPacketsLost += track.totalLostPackets;
250
+ this.totalInboundPacketsReceived += track.totalReceivedPackets;
251
+ this.totalReceivedVideoBytes += track.totalBytesReceived;
252
+ this.totalReceivedVideoPackets += track.totalReceivedPackets;
253
+
254
+ sumRttInS = (track.rttInMs ?? 0) / 1000.0;
255
+ });
256
+
257
+ this._outboundAudioTracks.forEach((track) => {
258
+ this.deltaOutboundSentPackets += track.deltaSentPackets;
259
+ this.deltaOutboundSentBytes += track.deltaSentBytes;
260
+
261
+ this.deltaSentAudioBytes += track.deltaSentBytes;
262
+ this.deltaSentAudioBytes += track.deltaSentPackets;
263
+
264
+ this.sendingAudioBitrate += track.bitrate;
265
+
266
+ this.totalOutboundPacketsSent += track.totalSentPackets;
267
+ this.totalSentAudioBytes += track.totalSentBytes;
268
+ this.totalSentAudioPackets += track.totalSentPackets;
269
+
270
+ sumRttInS = (track.rttInMs ?? 0) / 1000.0;
271
+ });
272
+
273
+ this._outboundVideoTracks.forEach((track) => {
274
+ this.deltaOutboundSentPackets += track.deltaSentPackets;
275
+ this.deltaOutboundSentBytes += track.deltaSentBytes;
276
+
277
+ this.deltaSentVideoBytes += track.deltaSentBytes;
278
+ this.deltaSentVideoBytes += track.deltaSentPackets;
279
+
280
+ this.sendingVideoBitrate += track.bitrate;
281
+
282
+ this.totalOutboundPacketsSent += track.totalSentPackets;
283
+ this.totalSentVideoBytes += track.totalSentBytes;
284
+ this.totalSentVideoPackets += track.totalSentPackets;
285
+
286
+ sumRttInS = (track.rttInMs ?? 0) / 1000.0;
287
+ });
288
+
289
+ const nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
290
+
291
+ this.avgRttInS = 0 < nrOfBelongings ? sumRttInS / nrOfBelongings : undefined;
292
+ }
293
+
123
294
  public update(sample: PeerConnectionTransport, timestamp: number) {
124
295
  if (this._closed) return;
125
296
  if (sample.peerConnectionId !== this._model.peerConnectionId) throw new Error(`TransportId mismatch. PeerConnectionId: ${ this._model.peerConnectionId } TransportId: ${ sample.transportId}`);
@@ -144,6 +315,11 @@ export class ObservedPeerConnection extends EventEmitter {
144
315
  this.emit('update');
145
316
  }
146
317
 
318
+ public updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate) {
319
+ this._selectedLocalIceCandidate = localCandidate;
320
+ this._selectedRemoteIceCandidate = remoteCandidate;
321
+ }
322
+
147
323
  public async createInboundAudioTrack(config: Omit<ObservedInboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedInboundTrack<'audio'>> {
148
324
  if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
149
325
 
@@ -152,6 +328,13 @@ export class ObservedPeerConnection extends EventEmitter {
152
328
  trackId: config.trackId,
153
329
  }, this, this._storageProvider);
154
330
 
331
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
332
+
333
+ if (pendingTracksTimestamp) {
334
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
335
+ result.created = pendingTracksTimestamp;
336
+ }
337
+
155
338
  result.on('close', () => {
156
339
  this._inboundAudioTracks.delete(result.trackId);
157
340
  this._model.inboundTrackIds = this._model.inboundTrackIds.filter((id) => id !== result.trackId);
@@ -173,6 +356,13 @@ export class ObservedPeerConnection extends EventEmitter {
173
356
  kind: 'video',
174
357
  trackId: config.trackId,
175
358
  }, this, this._storageProvider);
359
+
360
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
361
+
362
+ if (pendingTracksTimestamp) {
363
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
364
+ result.created = pendingTracksTimestamp;
365
+ }
176
366
 
177
367
  result.on('close', () => {
178
368
  this._inboundVideoTracks.delete(result.trackId);
@@ -196,6 +386,13 @@ export class ObservedPeerConnection extends EventEmitter {
196
386
  trackId: config.trackId,
197
387
  }, this, this._storageProvider);
198
388
 
389
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
390
+
391
+ if (pendingTracksTimestamp) {
392
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
393
+ result.created = pendingTracksTimestamp;
394
+ }
395
+
199
396
  result.on('close', () => {
200
397
  this._outboundAudioTracks.delete(result.trackId);
201
398
  this._model.outboundTrackIds = this._model.outboundTrackIds.filter((id) => id !== result.trackId);
@@ -222,6 +419,13 @@ export class ObservedPeerConnection extends EventEmitter {
222
419
  trackId: config.trackId,
223
420
  }, this, this._storageProvider);
224
421
 
422
+ const pendingTracksTimestamp = this.client.ωpendingCreatedTracksTimestamp.get(result.trackId);
423
+
424
+ if (pendingTracksTimestamp) {
425
+ this.client.ωpendingCreatedTracksTimestamp.delete(result.trackId);
426
+ result.created = pendingTracksTimestamp;
427
+ }
428
+
225
429
  result.on('close', () => {
226
430
  this._outboundVideoTracks.delete(result.trackId);
227
431
  result.sfuStreamId && this.client.call.sfuStreamIdToOutboundVideoTrack.set(result.sfuStreamId, result);
@@ -0,0 +1,23 @@
1
+ // eslint-disable-next-line no-shadow
2
+
3
+ export enum CallEventType {
4
+ CALL_STARTED = 'CALL_STARTED',
5
+ CALL_ENDED = 'CALL_ENDED',
6
+ CLIENT_JOINED = 'CLIENT_JOINED',
7
+ CLIENT_LEFT = 'CLIENT_LEFT',
8
+ PEER_CONNECTION_OPENED = 'PEER_CONNECTION_OPENED',
9
+ PEER_CONNECTION_CLOSED = 'PEER_CONNECTION_CLOSED',
10
+ MEDIA_TRACK_ADDED = 'MEDIA_TRACK_ADDED',
11
+ MEDIA_TRACK_REMOVED = 'MEDIA_TRACK_REMOVED',
12
+ MEDIA_TRACK_RESUMED = 'MEDIA_TRACK_RESUMED',
13
+ MEDIA_TRACK_MUTED = 'MEDIA_TRACK_MUTED',
14
+ MEDIA_TRACK_UNMUTED = 'MEDIA_TRACK_UNMUTED',
15
+ ICE_GATHERING_STATE_CHANGED = 'ICE_GATHERING_STATE_CHANGED',
16
+ PEER_CONNECTION_STATE_CHANGED = 'PEER_CONNECTION_STATE_CHANGED',
17
+ ICE_CONNECTION_STATE_CHANGED = 'ICE_CONNECTION_STATE_CHANGED',
18
+ DATA_CHANNEL_OPEN = 'DATA_CHANNEL_OPEN',
19
+ DATA_CHANNEL_CLOSED = 'DATA_CHANNEL_CLOSED',
20
+ DATA_CHANNEL_ERROR = 'DATA_CHANNEL_ERROR',
21
+ NEGOTIATION_NEEDED = 'NEGOTIATION_NEEDED',
22
+ SIGNALING_STATE_CHANGE = 'SIGNALING_STATE_CHANGE',
23
+ }
@@ -1,16 +1,6 @@
1
1
  import { CallEventReport } from '@observertc/report-schemas-js';
2
+ import { CallEventType } from './CallEventType';
2
3
 
3
- // eslint-disable-next-line no-shadow
4
- export enum CallEventType {
5
- CALL_STARTED = 'CALL_STARTED',
6
- CALL_ENDED = 'CALL_ENDED',
7
- CLIENT_JOINED = 'CLIENT_JOINED',
8
- CLIENT_LEFT = 'CLIENT_LEFT',
9
- PEER_CONNECTION_OPENED = 'PEER_CONNECTION_OPENED',
10
- PEER_CONNECTION_CLOSED = 'PEER_CONNECTION_CLOSED',
11
- MEDIA_TRACK_ADDED = 'MEDIA_TRACK_ADDED',
12
- MEDIA_TRACK_REMOVED = 'MEDIA_TRACK_REMOVED',
13
- }
14
4
  export function createCallStartedEventReport(
15
5
  serviceId: string,
16
6
  roomId: string,