@observertc/observer-js 0.30.6-beta → 0.40.1-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 (59) hide show
  1. package/lib/ObservedCall.d.ts +6 -12
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +24 -104
  4. package/lib/ObservedClient.d.ts +16 -32
  5. package/lib/ObservedClient.d.ts.map +1 -1
  6. package/lib/ObservedClient.js +381 -521
  7. package/lib/ObservedDataChannel.d.ts +68 -0
  8. package/lib/ObservedDataChannel.d.ts.map +1 -0
  9. package/lib/ObservedDataChannel.js +110 -0
  10. package/lib/ObservedInboundTrack.d.ts +3 -9
  11. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  12. package/lib/ObservedInboundTrack.js +75 -157
  13. package/lib/ObservedOutboundTrack.d.ts +9 -9
  14. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  15. package/lib/ObservedOutboundTrack.js +77 -133
  16. package/lib/ObservedPeerConnection.d.ts +21 -15
  17. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  18. package/lib/ObservedPeerConnection.js +85 -171
  19. package/lib/ObservedSfu.d.ts +5 -7
  20. package/lib/ObservedSfu.d.ts.map +1 -1
  21. package/lib/ObservedSfu.js +10 -74
  22. package/lib/Observer.d.ts +5 -6
  23. package/lib/Observer.d.ts.map +1 -1
  24. package/lib/Observer.js +28 -31
  25. package/lib/common/types.d.ts +0 -55
  26. package/lib/common/types.d.ts.map +1 -1
  27. package/lib/index.d.ts +5 -7
  28. package/lib/index.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/ObservedCall.ts +27 -87
  31. package/src/ObservedClient.ts +451 -586
  32. package/src/ObservedDataChannel.ts +186 -0
  33. package/src/ObservedInboundTrack.ts +18 -72
  34. package/src/ObservedOutboundTrack.ts +36 -60
  35. package/src/ObservedPeerConnection.ts +79 -112
  36. package/src/ObservedSfu.ts +11 -46
  37. package/src/Observer.ts +11 -16
  38. package/src/common/types.ts +0 -69
  39. package/src/index.ts +5 -7
  40. package/lib/models/Models.d.ts +0 -642
  41. package/lib/models/Models.d.ts.map +0 -1
  42. package/lib/models/Models.js +0 -481
  43. package/lib/models/samples_pb.d.ts +0 -2452
  44. package/lib/models/samples_pb.d.ts.map +0 -1
  45. package/lib/models/samples_pb.js +0 -1495
  46. package/lib/storages/ObserverStorage.d.ts +0 -87
  47. package/lib/storages/ObserverStorage.d.ts.map +0 -1
  48. package/lib/storages/ObserverStorage.js +0 -2
  49. package/lib/storages/SimpleStorage.d.ts +0 -19
  50. package/lib/storages/SimpleStorage.d.ts.map +0 -1
  51. package/lib/storages/SimpleStorage.js +0 -106
  52. package/lib/storages/StorageProvider.d.ts +0 -16
  53. package/lib/storages/StorageProvider.d.ts.map +0 -1
  54. package/lib/storages/StorageProvider.js +0 -29
  55. package/src/models/Models.ts +0 -1103
  56. package/src/models/samples_pb.ts +0 -4061
  57. package/src/storages/ObserverStorage.ts +0 -99
  58. package/src/storages/SimpleStorage.ts +0 -120
  59. package/src/storages/StorageProvider.ts +0 -42
@@ -1,38 +1,30 @@
1
- import { ClientSample, IceLocalCandidate, IceRemoteCandidate } from '@observertc/sample-schemas-js';
1
+ import { Browser, ClientSample, Engine, IceLocalCandidate, IceRemoteCandidate, OperationSystem, Platform } from '@observertc/sample-schemas-js';
2
2
  import { ObservedCall } from './ObservedCall';
3
- import * as Models from './models/Models';
4
- import { StorageProvider } from './storages/StorageProvider';
5
3
  import { EventEmitter } from 'events';
6
- import { createSingleExecutor } from './common/SingleExecutor';
7
4
  import { ObservedPeerConnection } from './ObservedPeerConnection';
8
5
  import { createLogger } from './common/logger';
9
6
  import { CallMetaType, createCallMetaReport } from './common/callMetaReports';
10
7
  // eslint-disable-next-line camelcase
11
- import { Samples_ClientSample_Browser, Samples_ClientSample_Engine, Samples_ClientSample_OperationSystem, Samples_ClientSample_Platform } from './models/samples_pb';
12
8
  import { isValidUuid } from './common/utils';
13
- import { createClientJoinedEventReport, createClientLeftEventReport } from './common/callEventReports';
9
+ import { createClientLeftEventReport } from './common/callEventReports';
14
10
  import { CallEventType } from './common/CallEventType';
15
11
 
16
12
  const logger = createLogger('ObservedClient');
17
13
 
18
- export type ObservedClientConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
19
- callId: string;
14
+ export type ObservedClientModel= {
20
15
  clientId: string;
21
16
  mediaUnitId: string;
22
- appData: AppData;
23
- overflowingProcessingThreshold?: number;
24
- joined?: number,
25
-
26
- /**
27
- * before client-monitor-js 3.4.0 the client joined event was generated by the observer, this flag is to keep the compatibility with the old behavior
28
- */
29
- addJoinAndDetachClientEvent?: boolean;
17
+ userId?: string,
18
+ marker?: string,
19
+ operationSystem?: OperationSystem,
20
+ engine?: Engine,
21
+ platform?: Platform,
22
+ browser?: Browser,
30
23
  };
31
24
 
32
25
  export type ObservedClientEvents = {
33
26
  update: [],
34
27
  close: [],
35
- 'processing-sample-overflow': [],
36
28
  newpeerconnection: [ObservedPeerConnection],
37
29
  iceconnectionstatechange: [{
38
30
  peerConnection: ObservedPeerConnection,
@@ -63,61 +55,18 @@ export declare interface ObservedClient<AppData extends Record<string, unknown>
63
55
  }
64
56
 
65
57
  export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
66
- public static async create<T extends Record<string, unknown> = Record<string, unknown>>(
67
- config: ObservedClientConfig<T>,
68
- call: ObservedCall,
69
- storageProvider: StorageProvider,
70
- ) {
71
- const { callId, clientId, mediaUnitId } = config;
72
-
73
- const model = new Models.Client({
74
- roomId: call.roomId,
75
- serviceId: call.serviceId,
76
- callId,
77
- clientId,
78
- mediaUnitId,
79
- peerConnectionIds: [],
80
- joined: BigInt(config.joined ?? Date.now()),
81
- });
82
-
83
- const result = new ObservedClient<T>(model, call, storageProvider, config.appData);
84
-
85
- const alreadyInserted = await storageProvider.clientStorage.insert(clientId, model);
86
-
87
- if (alreadyInserted) throw new Error(`Client with id ${clientId} already exists`);
88
-
89
- result._overflowingProcessingThreshold = config.overflowingProcessingThreshold ?? 0;
90
-
91
- if (config.addJoinAndDetachClientEvent) {
92
- call.reports.addCallEventReport(createClientJoinedEventReport(
93
- call.serviceId,
94
- result.mediaUnitId,
95
- call.roomId,
96
- call.callId,
97
- result.clientId,
98
- result.joined,
99
- result.userId,
100
- result.marker,
101
- ));
102
- }
103
-
104
- return result;
105
- }
106
-
107
- public created = Date.now();
58
+
59
+ public readonly created = Date.now();
60
+ public _updated = Date.now();
108
61
 
109
62
  private readonly _peerConnections = new Map<string, ObservedPeerConnection>();
110
- private readonly _execute = createSingleExecutor();
111
63
 
112
- private _overflowingProcessingThreshold = 0;
113
64
  private _closed = false;
114
- private _updated = Date.now();
65
+
115
66
  private _acceptedSample = 0;
116
- private _processingSample = 0;
117
67
  private _timeZoneOffsetInHours?: number;
118
68
  private _left?: number;
119
- private _numberOfAcceptedUpdates = 0;
120
-
69
+
121
70
  public usingTURN = false;
122
71
  public availableOutgoingBitrate = 0;
123
72
  public availableIncomingBitrate = 0;
@@ -140,20 +89,14 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
140
89
  public readonly ωpendingCreatedTracksTimestamp = new Map<string, number>();
141
90
  public readonly ωpendingCreatedPeerConnectionTimestamp = new Map<string, number>();
142
91
 
143
- private constructor(
144
- private readonly _model: Models.Client,
92
+ public constructor(
93
+ private readonly _model: ObservedClientModel,
145
94
  public readonly call: ObservedCall,
146
- private readonly _storageProvider: StorageProvider,
147
95
  public readonly appData: AppData
148
96
  ) {
149
97
  super();
150
98
  }
151
99
 
152
- public get joined(): number {
153
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
154
- return Number(this._model.joined!);
155
- }
156
-
157
100
  public get clientId(): string {
158
101
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
102
  return this._model.clientId!;
@@ -161,17 +104,17 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
161
104
 
162
105
  public get roomId() {
163
106
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
164
- return this._model.roomId!;
107
+ return this.call.roomId!;
165
108
  }
166
109
 
167
110
  public get serviceId() {
168
111
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
169
- return this._model.serviceId!;
112
+ return this.call.serviceId!;
170
113
  }
171
114
 
172
115
  public get callId() {
173
116
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
174
- return this._model.callId!;
117
+ return this.call.callId!;
175
118
  }
176
119
 
177
120
  public get mediaUnitId() {
@@ -219,20 +162,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
219
162
  return this._updated;
220
163
  }
221
164
 
222
- public get numberOfAcceptedUpdates() {
223
- return this._numberOfAcceptedUpdates;
224
- }
225
-
226
- public get uptimeInMs() {
227
- return this._updated - this.created;
228
- }
229
-
230
- public get durationInMs(): number | undefined {
231
- if (!this._left) return;
232
-
233
- return this._left - this.joined;
234
- }
235
-
236
165
  public get closed() {
237
166
  return this._closed;
238
167
  }
@@ -245,600 +174,546 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
245
174
  if (this._closed) return;
246
175
  this._closed = true;
247
176
 
248
- !this._left && this._addClientLeftReport();
177
+ if (!this._left) {
178
+ this._addClientLeftReport();
179
+ }
249
180
 
250
181
  Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
251
182
 
252
- this._execute(() => this._storageProvider.clientStorage.remove(this.clientId))
253
- .catch(() => void 0)
254
- .finally(() => this.emit('close'));
183
+ this.emit('close');
255
184
  }
256
185
 
257
- public async accept(sample: ClientSample): Promise<void> {
186
+ public accept(sample: ClientSample): void {
258
187
  if (this._closed) throw new Error(`Client ${this.clientId} is closed`);
259
188
  if (sample.clientId && sample.clientId !== 'NULL' && sample.clientId !== this.clientId) {
260
189
  throw new Error(`Sample client id (${sample.clientId}) does not match the client id of the observed client (${this.clientId})`);
261
190
  }
191
+ const now = Date.now();
192
+
262
193
  ++this._acceptedSample;
263
- ++this._processingSample;
264
194
 
265
- if (0 < this._overflowingProcessingThreshold && this._overflowingProcessingThreshold < this._processingSample) {
266
- this.emit('processing-sample-overflow');
195
+ for (const peerConnection of this._peerConnections.values()) {
196
+ if (peerConnection.closed) continue;
197
+ peerConnection.resetMetrics();
267
198
  }
268
199
 
269
- return this._execute(async () => {
270
- ++this._numberOfAcceptedUpdates;
271
-
272
- for (const peerConnection of this._peerConnections.values()) {
273
- if (peerConnection.closed) continue;
274
- peerConnection.resetMetrics();
275
- }
276
-
277
- let executeSave = false;
278
-
279
- if (this.userId !== sample.userId) {
280
- this._model.userId = sample.userId;
281
- executeSave = true;
282
- }
283
- if (this._model.marker !== sample.marker) {
284
- this._model.marker = sample.marker;
285
- executeSave = true;
286
- }
200
+ if (this.userId !== sample.userId) {
201
+ this._model.userId = sample.userId;
202
+ }
203
+ if (this._model.marker !== sample.marker) {
204
+ this._model.marker = sample.marker;
205
+ }
287
206
 
288
- if (this._timeZoneOffsetInHours !== sample.timeZoneOffsetInHours) {
289
- this._timeZoneOffsetInHours = sample.timeZoneOffsetInHours;
290
- executeSave = true;
291
- }
207
+ if (this._timeZoneOffsetInHours !== sample.timeZoneOffsetInHours) {
208
+ this._timeZoneOffsetInHours = sample.timeZoneOffsetInHours;
209
+ }
292
210
 
293
- if (sample.os && (
294
- this._model.operationSystem?.name !== sample.os.name ||
211
+ if (sample.os && (
212
+ this._model.operationSystem?.name !== sample.os.name ||
295
213
  this._model.operationSystem?.version !== sample.os.version ||
296
214
  this._model.operationSystem?.versionName !== sample.os.versionName
297
- )) {
298
- this._model.operationSystem = new Samples_ClientSample_OperationSystem({
299
- ...sample.os,
300
- });
215
+ )) {
216
+ this._model.operationSystem = sample.os;
301
217
 
302
- const callMetaReport = createCallMetaReport(
303
- this.serviceId,
304
- this.mediaUnitId,
305
- this.roomId,
306
- this.callId,
307
- this.clientId,
308
- {
309
- type: CallMetaType.OPERATION_SYSTEM,
310
- payload: sample.os,
311
- }, this.userId);
218
+ const callMetaReport = createCallMetaReport(
219
+ this.serviceId,
220
+ this.mediaUnitId,
221
+ this.roomId,
222
+ this.callId,
223
+ this.clientId,
224
+ {
225
+ type: CallMetaType.OPERATION_SYSTEM,
226
+ payload: sample.os,
227
+ }, this.userId);
312
228
 
313
- this.reports.addCallMetaReport(callMetaReport);
314
- executeSave = true;
315
- }
229
+ this.reports.addCallMetaReport(callMetaReport);
230
+ }
316
231
 
317
- if (sample.engine && (
318
- this._model.engine?.name !== sample.engine.name ||
232
+ if (sample.engine && (
233
+ this._model.engine?.name !== sample.engine.name ||
319
234
  this._model.engine?.version !== sample.engine.version
320
- )) {
321
- this._model.engine = new Samples_ClientSample_Engine({
322
- ...sample.engine,
323
- });
235
+ )) {
236
+ this._model.engine = sample.engine;
324
237
 
325
- const callMetaReport = createCallMetaReport(
326
- this.serviceId,
327
- this.mediaUnitId,
328
- this.roomId,
329
- this.callId,
330
- this.clientId,
331
- {
332
- type: CallMetaType.ENGINE,
333
- payload: sample.engine,
334
- }, this.userId);
238
+ const callMetaReport = createCallMetaReport(
239
+ this.serviceId,
240
+ this.mediaUnitId,
241
+ this.roomId,
242
+ this.callId,
243
+ this.clientId,
244
+ {
245
+ type: CallMetaType.ENGINE,
246
+ payload: sample.engine,
247
+ }, this.userId);
335
248
 
336
- this.reports.addCallMetaReport(callMetaReport);
337
- executeSave = true;
338
- }
249
+ this.reports.addCallMetaReport(callMetaReport);
250
+ }
339
251
 
340
- if (sample.platform && (
341
- this._model.platform?.model !== sample.platform.model ||
252
+ if (sample.platform && (
253
+ this._model.platform?.model !== sample.platform.model ||
342
254
  this._model.platform?.type !== sample.platform.type ||
343
255
  this._model.platform?.vendor !== sample.platform.vendor
344
- )) {
345
- this._model.platform = new Samples_ClientSample_Platform({
346
- ...sample.platform,
347
- });
256
+ )) {
257
+ this._model.platform = sample.platform;
348
258
 
349
- const callMetaReport = createCallMetaReport(
350
- this.serviceId,
351
- this.mediaUnitId,
352
- this.roomId,
353
- this.callId,
354
- this.clientId,
355
- {
356
- type: CallMetaType.PLATFORM,
357
- payload: sample.platform,
358
- }, this.userId);
259
+ const callMetaReport = createCallMetaReport(
260
+ this.serviceId,
261
+ this.mediaUnitId,
262
+ this.roomId,
263
+ this.callId,
264
+ this.clientId,
265
+ {
266
+ type: CallMetaType.PLATFORM,
267
+ payload: sample.platform,
268
+ }, this.userId);
359
269
 
360
- this.reports.addCallMetaReport(callMetaReport);
361
- executeSave = true;
362
- }
270
+ this.reports.addCallMetaReport(callMetaReport);
271
+ }
363
272
 
364
- if (sample.browser && (
365
- this._model.browser?.name !== sample.browser.name ||
273
+ if (sample.browser && (
274
+ this._model.browser?.name !== sample.browser.name ||
366
275
  this._model.browser?.version !== sample.browser.version
367
- )) {
368
- this._model.browser = new Samples_ClientSample_Browser({
369
- ...sample.browser,
370
- });
276
+ )) {
277
+ this._model.browser = sample.browser;
371
278
 
372
- const callMetaReport = createCallMetaReport(
373
- this.serviceId,
374
- this.mediaUnitId,
375
- this.roomId,
376
- this.callId,
377
- this.clientId,
378
- {
379
- type: CallMetaType.BROWSER,
380
- payload: sample.browser,
381
- }, this.userId);
279
+ const callMetaReport = createCallMetaReport(
280
+ this.serviceId,
281
+ this.mediaUnitId,
282
+ this.roomId,
283
+ this.callId,
284
+ this.clientId,
285
+ {
286
+ type: CallMetaType.BROWSER,
287
+ payload: sample.browser,
288
+ }, this.userId);
382
289
 
383
- this.reports.addCallMetaReport(callMetaReport);
384
- executeSave = true;
385
- }
386
-
387
- for (const mediaConstraint of sample.mediaConstraints ?? []) {
388
- const callMetaReport = createCallMetaReport(
389
- this.serviceId,
390
- this.mediaUnitId,
391
- this.roomId,
392
- this.callId,
393
- this.clientId,
394
- {
395
- type: CallMetaType.MEDIA_CONSTRAINT,
396
- payload: mediaConstraint,
397
- }, this.userId);
398
-
399
- this.reports.addCallMetaReport(callMetaReport);
400
- }
290
+ this.reports.addCallMetaReport(callMetaReport);
291
+ }
401
292
 
402
- for (const localSDP of sample.localSDPs ?? []) {
403
- const callMetaReport = createCallMetaReport(
404
- this.serviceId,
405
- this.mediaUnitId,
406
- this.roomId,
407
- this.callId,
408
- this.clientId,
409
- {
410
- type: CallMetaType.LOCAL_SDP,
411
- payload: localSDP,
412
- }, this.userId);
413
-
414
- this.reports.addCallMetaReport(callMetaReport);
415
- }
293
+ for (const mediaConstraint of sample.mediaConstraints ?? []) {
294
+ const callMetaReport = createCallMetaReport(
295
+ this.serviceId,
296
+ this.mediaUnitId,
297
+ this.roomId,
298
+ this.callId,
299
+ this.clientId,
300
+ {
301
+ type: CallMetaType.MEDIA_CONSTRAINT,
302
+ payload: mediaConstraint,
303
+ }, this.userId);
304
+
305
+ this.reports.addCallMetaReport(callMetaReport);
306
+ }
416
307
 
417
- for (const extensionStats of sample.extensionStats ?? []) {
418
- this.reports.addClientExtensionReport({
419
- serviceId: this.serviceId,
420
- mediaUnitId: this.mediaUnitId,
421
- roomId: this.roomId,
422
- callId: this.callId,
423
- clientId: this.clientId,
424
- userId: this.userId,
425
- timestamp: Date.now(),
426
- payload: extensionStats.payload,
427
- extensionType: extensionStats.type,
428
- });
429
- }
308
+ for (const localSDP of sample.localSDPs ?? []) {
309
+ const callMetaReport = createCallMetaReport(
310
+ this.serviceId,
311
+ this.mediaUnitId,
312
+ this.roomId,
313
+ this.callId,
314
+ this.clientId,
315
+ {
316
+ type: CallMetaType.LOCAL_SDP,
317
+ payload: localSDP,
318
+ }, this.userId);
319
+
320
+ this.reports.addCallMetaReport(callMetaReport);
321
+ }
430
322
 
431
- for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
432
- const peerConnection = this._peerConnections.get(callEvent.peerConnectionId ?? '');
433
-
434
- switch (callEvent.name) {
435
- case CallEventType.CLIENT_JOINED:
436
- if (timestamp) {
437
- this.created = timestamp;
438
- this._model.joined = BigInt(timestamp);
439
- await this._save();
440
- }
441
-
442
- break;
443
- case CallEventType.CLIENT_LEFT:
444
- this._left = timestamp ?? Date.now();
445
- break;
446
- case CallEventType.ICE_CONNECTION_STATE_CHANGED:
447
- peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
448
- peerConnection,
449
- state: callEvent.value,
450
- });
451
- break;
452
- case CallEventType.ICE_GATHERING_STATE_CHANGED:
453
- peerConnection && callEvent.value && this.emit('icegatheringstatechange', {
454
- peerConnection,
455
- state: callEvent.value,
456
- });
457
- break;
458
- case CallEventType.PEER_CONNECTION_STATE_CHANGED:
459
- peerConnection && callEvent.value && this.emit('connectionstatechange', {
460
- peerConnection,
461
- state: callEvent.value,
462
- });
463
- break;
464
- case CallEventType.PEER_CONNECTION_OPENED: {
465
- if (!callEvent.peerConnectionId) break;
466
- if (peerConnection) {
467
- peerConnection.created = timestamp ?? peerConnection.created;
468
- } else {
469
- this.ωpendingCreatedPeerConnectionTimestamp.set(callEvent.peerConnectionId, timestamp ?? Date.now());
470
- }
471
- break;
472
- }
473
- case CallEventType.MEDIA_TRACK_ADDED: {
474
- if (!callEvent.mediaTrackId) break;
475
- const track = peerConnection?.getTrack(callEvent.mediaTrackId);
476
-
477
- if (track) {
478
- track.created = timestamp ?? track.created;
479
- } else {
480
- this.ωpendingCreatedTracksTimestamp.set(callEvent.mediaTrackId, timestamp ?? Date.now());
481
- }
482
- }
323
+ for (const extensionStats of sample.extensionStats ?? []) {
324
+ this.reports.addClientExtensionReport({
325
+ serviceId: this.serviceId,
326
+ mediaUnitId: this.mediaUnitId,
327
+ roomId: this.roomId,
328
+ callId: this.callId,
329
+ clientId: this.clientId,
330
+ userId: this.userId,
331
+ timestamp: Date.now(),
332
+ payload: extensionStats.payload,
333
+ extensionType: extensionStats.type,
334
+ });
335
+ }
483
336
 
337
+ for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
338
+ switch (callEvent.name) {
339
+ case CallEventType.CLIENT_LEFT: {
340
+ this._left = timestamp;
341
+ break;
484
342
  }
485
-
486
- this.reports.addCallEventReport({
487
- serviceId: this.serviceId,
488
- mediaUnitId: this.mediaUnitId,
489
- roomId: this.roomId,
490
- callId: this.callId,
491
- clientId: this.clientId,
492
- userId: this.userId,
493
- timestamp: timestamp ?? Date.now(),
494
- ...callEvent,
495
- });
496
343
  }
344
+ this.reports.addCallEventReport({
345
+ serviceId: this.serviceId,
346
+ mediaUnitId: this.mediaUnitId,
347
+ roomId: this.roomId,
348
+ callId: this.callId,
349
+ clientId: this.clientId,
350
+ userId: this.userId,
351
+ timestamp: timestamp ?? Date.now(),
352
+ ...callEvent,
353
+ });
354
+ }
497
355
 
498
- for (const userMediaError of sample.userMediaErrors ?? []) {
499
- const callMetaReport = createCallMetaReport(
500
- this.serviceId,
501
- this.mediaUnitId,
502
- this.roomId,
503
- this.callId,
504
- this.clientId, {
505
- type: CallMetaType.USER_MEDIA_ERROR,
506
- payload: userMediaError,
507
- }, this.userId);
508
-
509
- this.reports.addCallMetaReport(callMetaReport);
510
- this.userMediaErrors.push(userMediaError);
511
- }
356
+ for (const userMediaError of sample.userMediaErrors ?? []) {
357
+ const callMetaReport = createCallMetaReport(
358
+ this.serviceId,
359
+ this.mediaUnitId,
360
+ this.roomId,
361
+ this.callId,
362
+ this.clientId, {
363
+ type: CallMetaType.USER_MEDIA_ERROR,
364
+ payload: userMediaError,
365
+ }, this.userId);
366
+
367
+ this.reports.addCallMetaReport(callMetaReport);
368
+ this.userMediaErrors.push(userMediaError);
369
+ }
512
370
 
513
- for (const certificate of sample.certificates ?? []) {
514
- const callMetaReport = createCallMetaReport(
515
- this.serviceId,
516
- this.mediaUnitId,
517
- this.roomId,
518
- this.callId,
519
- this.clientId, {
520
- type: CallMetaType.CERTIFICATE,
521
- payload: certificate,
522
- }, this.userId);
523
-
524
- this.reports.addCallMetaReport(callMetaReport);
525
- }
371
+ for (const certificate of sample.certificates ?? []) {
372
+ const callMetaReport = createCallMetaReport(
373
+ this.serviceId,
374
+ this.mediaUnitId,
375
+ this.roomId,
376
+ this.callId,
377
+ this.clientId, {
378
+ type: CallMetaType.CERTIFICATE,
379
+ payload: certificate,
380
+ }, this.userId);
381
+
382
+ this.reports.addCallMetaReport(callMetaReport);
383
+ }
526
384
 
527
- for (const codec of sample.codecs ?? []) {
528
- const callMetaReport = createCallMetaReport(
529
- this.serviceId,
530
- this.mediaUnitId,
531
- this.roomId,
532
- this.callId,
533
- this.clientId, {
534
- type: CallMetaType.CODEC,
535
- payload: codec,
536
- }, this.userId);
537
-
538
- this.reports.addCallMetaReport(callMetaReport);
539
- codec.mimeType && this.codecs.push(codec.mimeType);
540
- }
385
+ for (const codec of sample.codecs ?? []) {
386
+ const callMetaReport = createCallMetaReport(
387
+ this.serviceId,
388
+ this.mediaUnitId,
389
+ this.roomId,
390
+ this.callId,
391
+ this.clientId, {
392
+ type: CallMetaType.CODEC,
393
+ payload: codec,
394
+ }, this.userId);
395
+
396
+ this.reports.addCallMetaReport(callMetaReport);
397
+ codec.mimeType && this.codecs.push(codec.mimeType);
398
+ }
541
399
 
542
- for (const iceServer of sample.iceServers ?? []) {
543
- const callMetaReport = createCallMetaReport(
544
- this.serviceId,
545
- this.mediaUnitId,
546
- this.roomId,
547
- this.callId,
548
- this.clientId, {
549
- type: CallMetaType.ICE_SERVER,
550
- payload: iceServer,
551
- }, this.userId);
552
-
553
- this.reports.addCallMetaReport(callMetaReport);
554
- }
400
+ for (const iceServer of sample.iceServers ?? []) {
401
+ const callMetaReport = createCallMetaReport(
402
+ this.serviceId,
403
+ this.mediaUnitId,
404
+ this.roomId,
405
+ this.callId,
406
+ this.clientId, {
407
+ type: CallMetaType.ICE_SERVER,
408
+ payload: iceServer,
409
+ }, this.userId);
410
+
411
+ this.reports.addCallMetaReport(callMetaReport);
412
+ }
555
413
 
556
- for (const mediaDevice of sample.mediaDevices ?? []) {
557
- const callMetaReport = createCallMetaReport(
558
- this.serviceId,
559
- this.mediaUnitId,
560
- this.roomId,
561
- this.callId,
562
- this.clientId, {
563
- type: CallMetaType.MEDIA_DEVICE,
564
- payload: mediaDevice,
565
- }, this.userId);
566
-
567
- this.reports.addCallMetaReport(callMetaReport);
568
- mediaDevice.label && this.mediaDevices.push(mediaDevice.label);
569
- }
414
+ for (const mediaDevice of sample.mediaDevices ?? []) {
415
+ const callMetaReport = createCallMetaReport(
416
+ this.serviceId,
417
+ this.mediaUnitId,
418
+ this.roomId,
419
+ this.callId,
420
+ this.clientId, {
421
+ type: CallMetaType.MEDIA_DEVICE,
422
+ payload: mediaDevice,
423
+ }, this.userId);
424
+
425
+ this.reports.addCallMetaReport(callMetaReport);
426
+ mediaDevice.label && this.mediaDevices.push(mediaDevice.label);
427
+ }
570
428
 
571
- for (const mediaSource of sample.mediaSources ?? []) {
572
- const callMetaReport = createCallMetaReport(
573
- this.serviceId,
574
- this.mediaUnitId,
575
- this.roomId,
576
- this.callId,
577
- this.clientId, {
578
- type: CallMetaType.MEDIA_SOURCE,
579
- payload: mediaSource,
580
- }, this.userId);
581
-
582
- this.reports.addCallMetaReport(callMetaReport);
583
- }
429
+ for (const mediaSource of sample.mediaSources ?? []) {
430
+ const callMetaReport = createCallMetaReport(
431
+ this.serviceId,
432
+ this.mediaUnitId,
433
+ this.roomId,
434
+ this.callId,
435
+ this.clientId, {
436
+ type: CallMetaType.MEDIA_SOURCE,
437
+ payload: mediaSource,
438
+ }, this.userId);
439
+
440
+ this.reports.addCallMetaReport(callMetaReport);
441
+ }
584
442
 
585
- for (const transport of sample.pcTransports ?? []) {
586
- try {
587
- const peerConnection = this._peerConnections.get(transport.transportId) ?? await this._createPeerConnection(transport.peerConnectionId);
443
+ for (const transport of sample.pcTransports ?? []) {
444
+ try {
445
+ const peerConnection = this._peerConnections.get(transport.transportId) ?? this._createPeerConnection(transport.peerConnectionId);
588
446
 
589
- await peerConnection.update(transport, sample.timestamp);
590
- } catch (err) {
591
- logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
592
- }
593
-
447
+ !peerConnection.label && transport.label && (peerConnection.label = transport.label);
448
+ peerConnection.update(transport, sample.timestamp);
449
+ } catch (err) {
450
+ logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
594
451
  }
452
+
453
+ }
595
454
 
596
- for (const track of sample.inboundAudioTracks ?? []) {
597
- if (!track.peerConnectionId || !track.trackId) {
598
- logger.warn(`InboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
455
+ for (const track of sample.inboundAudioTracks ?? []) {
456
+ if (!track.peerConnectionId || !track.trackId) {
457
+ logger.warn(`InboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
599
458
 
600
- continue;
601
- }
459
+ continue;
460
+ }
602
461
 
603
- try {
604
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? await this._createPeerConnection(track.peerConnectionId);
462
+ try {
463
+ const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
605
464
 
606
- if (!peerConnection) continue;
465
+ if (!peerConnection) continue;
607
466
 
608
- const inboundAudioTrack = peerConnection.inboundAudioTracks.get(track.trackId) ?? await peerConnection.createInboundAudioTrack({
609
- trackId: track.trackId,
610
- sfuStreamId: track.sfuStreamId,
611
- sfuSinkId: track.sfuSinkId,
612
- });
467
+ const inboundAudioTrack = peerConnection.inboundAudioTracks.get(track.trackId) ?? peerConnection.createInboundAudioTrack({
468
+ trackId: track.trackId,
469
+ sfuStreamId: track.sfuStreamId,
470
+ sfuSinkId: track.sfuSinkId,
471
+ });
613
472
 
614
- await inboundAudioTrack.update(track, sample.timestamp);
473
+ inboundAudioTrack.update(track, sample.timestamp);
615
474
 
616
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(track.sfuStreamId ?? '');
475
+ const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(track.sfuStreamId ?? '');
617
476
 
618
- if (remoteOutboundTrack && inboundAudioTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundAudioTrack.sfuStreamId)) {
619
- remoteOutboundTrack.connectInboundTrack(inboundAudioTrack);
620
- }
621
- } catch (err) {
622
- logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
477
+ if (remoteOutboundTrack && inboundAudioTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundAudioTrack.sfuStreamId)) {
478
+ remoteOutboundTrack.connectInboundTrack(inboundAudioTrack);
623
479
  }
480
+ } catch (err) {
481
+ logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
624
482
  }
483
+ }
625
484
 
626
- for (const track of sample.inboundVideoTracks ?? []) {
627
- if (!track.peerConnectionId || !track.trackId) {
628
- logger.warn(`InboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
485
+ for (const track of sample.inboundVideoTracks ?? []) {
486
+ if (!track.peerConnectionId || !track.trackId) {
487
+ logger.warn(`InboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
629
488
 
630
- continue;
631
- }
632
- if (isValidUuid(track.trackId) === false) {
633
- // mediasoup-probator trackId is not a valid UUID, no need to warn about it
634
- if (track.trackId === 'probator') continue;
635
- logger.warn(`InboundVideoTrack with invalid trackId: ${track.trackId}`);
636
- continue;
637
- }
489
+ continue;
490
+ }
491
+ if (isValidUuid(track.trackId) === false) {
492
+ // mediasoup-probator trackId is not a valid UUID, no need to warn about it
493
+ if (track.trackId === 'probator') continue;
494
+ logger.warn(`InboundVideoTrack with invalid trackId: ${track.trackId}`);
495
+ continue;
496
+ }
638
497
 
639
- try {
640
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? await this._createPeerConnection(track.peerConnectionId);
498
+ try {
499
+ const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
641
500
 
642
- if (!peerConnection) continue;
501
+ if (!peerConnection) continue;
643
502
 
644
- const inboundVideoTrack = peerConnection.inboundVideoTracks.get(track.trackId) ?? await peerConnection.createInboundVideoTrack({
645
- trackId: track.trackId,
646
- sfuStreamId: track.sfuStreamId,
647
- sfuSinkId: track.sfuSinkId,
648
- });
503
+ const inboundVideoTrack = peerConnection.inboundVideoTracks.get(track.trackId) ?? peerConnection.createInboundVideoTrack({
504
+ trackId: track.trackId,
505
+ sfuStreamId: track.sfuStreamId,
506
+ sfuSinkId: track.sfuSinkId,
507
+ });
649
508
 
650
- await inboundVideoTrack.update(track, sample.timestamp);
509
+ inboundVideoTrack.update(track, sample.timestamp);
651
510
 
652
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
511
+ const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
653
512
 
654
- if (remoteOutboundTrack && inboundVideoTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundVideoTrack.sfuStreamId)) {
655
- remoteOutboundTrack.connectInboundTrack(inboundVideoTrack);
656
- }
657
- } catch (err) {
658
- logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
513
+ if (remoteOutboundTrack && inboundVideoTrack.sfuStreamId && !remoteOutboundTrack.remoteInboundTracks.has(inboundVideoTrack.sfuStreamId)) {
514
+ remoteOutboundTrack.connectInboundTrack(inboundVideoTrack);
659
515
  }
516
+ } catch (err) {
517
+ logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
660
518
  }
519
+ }
661
520
 
662
- for (const track of sample.outboundAudioTracks ?? []) {
663
- if (!track.peerConnectionId || !track.trackId) {
664
- logger.warn(`OutboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
521
+ for (const track of sample.outboundAudioTracks ?? []) {
522
+ if (!track.peerConnectionId || !track.trackId) {
523
+ logger.warn(`OutboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
665
524
 
666
- continue;
667
- }
525
+ continue;
526
+ }
668
527
 
669
- try {
670
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? await this._createPeerConnection(track.peerConnectionId);
528
+ try {
529
+ const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
671
530
 
672
- if (!peerConnection) continue;
531
+ if (!peerConnection) continue;
673
532
 
674
- const outboundAudioTrack = peerConnection.outboundAudioTracks.get(track.trackId) ?? await peerConnection.createOutboundAudioTrack({
675
- trackId: track.trackId,
676
- sfuStreamId: track.sfuStreamId,
677
- });
678
-
679
- await outboundAudioTrack.update(track, sample.timestamp);
680
- } catch (err) {
681
- logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
682
- }
683
-
533
+ const outboundAudioTrack = peerConnection.outboundAudioTracks.get(track.trackId) ?? peerConnection.createOutboundAudioTrack({
534
+ trackId: track.trackId,
535
+ sfuStreamId: track.sfuStreamId,
536
+ });
537
+
538
+ outboundAudioTrack.update(track, sample.timestamp);
539
+ } catch (err) {
540
+ logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
684
541
  }
542
+
543
+ }
685
544
 
686
- for (const track of sample.outboundVideoTracks ?? []) {
687
- if (!track.peerConnectionId || !track.trackId) {
688
- logger.warn(`OutboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
545
+ for (const track of sample.outboundVideoTracks ?? []) {
546
+ if (!track.peerConnectionId || !track.trackId) {
547
+ logger.warn(`OutboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
689
548
 
690
- continue;
691
- }
549
+ continue;
550
+ }
692
551
 
693
- try {
694
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? await this._createPeerConnection(track.peerConnectionId);
552
+ try {
553
+ const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
695
554
 
696
- if (!peerConnection) continue;
555
+ if (!peerConnection) continue;
697
556
 
698
- const outboundVideoTrack = peerConnection.outboundVideoTracks.get(track.trackId) ?? await peerConnection.createOutboundVideoTrack({
699
- trackId: track.trackId,
700
- sfuStreamId: track.sfuStreamId,
701
- });
557
+ const outboundVideoTrack = peerConnection.outboundVideoTracks.get(track.trackId) ?? peerConnection.createOutboundVideoTrack({
558
+ trackId: track.trackId,
559
+ sfuStreamId: track.sfuStreamId,
560
+ });
702
561
 
703
- await outboundVideoTrack.update(track, sample.timestamp);
562
+ outboundVideoTrack.update(track, sample.timestamp);
704
563
 
705
- } catch (err) {
706
- logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
707
- }
564
+ } catch (err) {
565
+ logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
708
566
  }
567
+ }
709
568
 
710
- for (const iceLocalCandidate of sample.iceLocalCandidates ?? []) {
711
- if (!iceLocalCandidate.peerConnectionId) {
712
- logger.warn(`Local ice candidate without peerConnectionId: ${JSON.stringify(iceLocalCandidate)}`);
713
- continue;
714
- }
715
- const peerConnection = this._peerConnections.get(iceLocalCandidate.peerConnectionId);
716
-
717
- if (!peerConnection) {
718
- logger.debug(`Peer connection ${iceLocalCandidate.peerConnectionId} not found for ice local candidate ${iceLocalCandidate.id}`);
719
- continue;
720
- }
569
+ for (const iceLocalCandidate of sample.iceLocalCandidates ?? []) {
570
+ if (!iceLocalCandidate.peerConnectionId) {
571
+ logger.warn(`Local ice candidate without peerConnectionId: ${JSON.stringify(iceLocalCandidate)}`);
572
+ continue;
573
+ }
574
+ const peerConnection = this._peerConnections.get(iceLocalCandidate.peerConnectionId);
721
575
 
722
- peerConnection.ICE.addLocalCandidate(iceLocalCandidate);
576
+ if (!peerConnection) {
577
+ logger.debug(`Peer connection ${iceLocalCandidate.peerConnectionId} not found for ice local candidate ${iceLocalCandidate.id}`);
578
+ continue;
723
579
  }
580
+
581
+ peerConnection.ICE.addLocalCandidate(iceLocalCandidate);
582
+ }
724
583
 
725
- for (const iceRemoteCandidate of sample.iceRemoteCandidates ?? []) {
726
- if (!iceRemoteCandidate.peerConnectionId) {
727
- logger.warn(`Remote ice candidate without peerConnectionId: ${JSON.stringify(iceRemoteCandidate)}`);
728
- continue;
729
- }
730
- const peerConnection = this._peerConnections.get(iceRemoteCandidate.peerConnectionId);
584
+ for (const iceRemoteCandidate of sample.iceRemoteCandidates ?? []) {
585
+ if (!iceRemoteCandidate.peerConnectionId) {
586
+ logger.warn(`Remote ice candidate without peerConnectionId: ${JSON.stringify(iceRemoteCandidate)}`);
587
+ continue;
588
+ }
589
+ const peerConnection = this._peerConnections.get(iceRemoteCandidate.peerConnectionId);
731
590
 
732
- if (!peerConnection) {
733
- logger.debug(`Peer connection ${iceRemoteCandidate.peerConnectionId} not found for ice remote candidate ${iceRemoteCandidate.id}`);
734
- continue;
735
- }
591
+ if (!peerConnection) {
592
+ logger.debug(`Peer connection ${iceRemoteCandidate.peerConnectionId} not found for ice remote candidate ${iceRemoteCandidate.id}`);
593
+ continue;
594
+ }
595
+
596
+ peerConnection.ICE.addRemoteCandidate(iceRemoteCandidate);
597
+ }
736
598
 
737
- peerConnection.ICE.addRemoteCandidate(iceRemoteCandidate);
599
+ for (const candidatePair of sample.iceCandidatePairs ?? []) {
600
+ const peerConnection = this._peerConnections.get(candidatePair.peerConnectionId);
601
+
602
+ if (!peerConnection) {
603
+ logger.debug(`Peer connection ${candidatePair.peerConnectionId} not found for ice candidate pair ${candidatePair.localCandidateId}, ${candidatePair.remoteCandidateId}`);
604
+
605
+ continue;
738
606
  }
739
607
 
740
- for (const candidatePair of sample.iceCandidatePairs ?? []) {
741
- const peerConnection = this._peerConnections.get(candidatePair.peerConnectionId);
608
+ peerConnection.ICE.update(candidatePair, sample.timestamp);
609
+ }
742
610
 
743
- if (!peerConnection) {
744
- logger.debug(`Peer connection ${candidatePair.peerConnectionId} not found for ice candidate pair ${candidatePair.localCandidateId}, ${candidatePair.remoteCandidateId}`);
611
+ for (const dataChannel of sample.dataChannels ?? []) {
612
+ if (!dataChannel.peerConnectionId || !dataChannel.dataChannelIdentifier) {
613
+ logger.warn(`DataChannel without peerConnectionId or dataChannelIdentifier: ${JSON.stringify(dataChannel)}`);
745
614
 
746
- continue;
747
- }
615
+ continue;
616
+ }
617
+
618
+ try {
619
+ const peerConnection = this._peerConnections.get(dataChannel.peerConnectionId) ?? this._createPeerConnection(dataChannel.peerConnectionId);
748
620
 
749
- peerConnection.ICE.update(candidatePair, sample.timestamp);
621
+ if (!peerConnection) continue;
622
+
623
+ const observedDataChannel = peerConnection.dataChannels.get(dataChannel.dataChannelIdentifier) ?? peerConnection.createDataChannel(dataChannel.dataChannelIdentifier);
624
+
625
+ observedDataChannel.update(dataChannel, sample.timestamp);
626
+ } catch (err) {
627
+ logger.error(`Error creating data channel: ${(err as Error)?.message}`);
750
628
  }
629
+ }
751
630
 
752
- // close entries that are idle for too long
753
- if (this.call.observer.config.maxEntryIdleTimeInMs && this._updated < sample.timestamp) {
631
+ // close entries that are idle for too long
632
+ if (this.call.observer.config.maxEntryIdleTimeInMs && this._updated < sample.timestamp) {
754
633
 
755
- for (const peerConnection of this._peerConnections.values()) {
756
- if (this.call.observer.config.maxEntryIdleTimeInMs < sample.timestamp - peerConnection.updated) {
757
- logger.debug(`Closing peer connection ${peerConnection.peerConnectionId} due to inactivity`);
758
- peerConnection.close();
634
+ for (const peerConnection of this._peerConnections.values()) {
635
+ if (this.call.observer.config.maxEntryIdleTimeInMs < peerConnection.created - peerConnection.updated) {
636
+ logger.debug(`Closing peer connection ${peerConnection.peerConnectionId} due to inactivity`);
637
+ peerConnection.close();
759
638
 
760
- continue;
639
+ continue;
640
+ }
641
+ for (const track of peerConnection.inboundAudioTracks.values()) {
642
+ if (this.call.observer.config.maxEntryIdleTimeInMs < track.created - track.updated) {
643
+ logger.debug(`Closing inbound audio track ${track.trackId} due to inactivity`);
644
+ track.close();
761
645
  }
762
- for (const track of peerConnection.inboundAudioTracks.values()) {
763
- if (this.call.observer.config.maxEntryIdleTimeInMs < sample.timestamp - track.updated) {
764
- logger.debug(`Closing inbound audio track ${track.trackId} due to inactivity`);
765
- track.close();
766
- }
646
+ }
647
+ for (const track of peerConnection.inboundVideoTracks.values()) {
648
+ if (this.call.observer.config.maxEntryIdleTimeInMs < track.created - track.updated) {
649
+ logger.debug(`Closing inbound video track ${track.trackId} due to inactivity`);
650
+ track.close();
767
651
  }
768
- for (const track of peerConnection.inboundVideoTracks.values()) {
769
- if (this.call.observer.config.maxEntryIdleTimeInMs < sample.timestamp - track.updated) {
770
- logger.debug(`Closing inbound video track ${track.trackId} due to inactivity`);
771
- track.close();
772
- }
652
+ }
653
+ for (const track of peerConnection.outboundAudioTracks.values()) {
654
+ if (this.call.observer.config.maxEntryIdleTimeInMs < track.created - track.updated) {
655
+ logger.debug(`Closing outbound audio track ${track.trackId} due to inactivity`);
656
+ track.close();
773
657
  }
774
- for (const track of peerConnection.outboundAudioTracks.values()) {
775
- if (this.call.observer.config.maxEntryIdleTimeInMs < sample.timestamp - track.updated) {
776
- logger.debug(`Closing outbound audio track ${track.trackId} due to inactivity`);
777
- track.close();
778
- }
658
+ }
659
+ for (const dataChannel of peerConnection.dataChannels.values()) {
660
+ if (this.call.observer.config.maxEntryIdleTimeInMs < dataChannel.created - dataChannel.updated) {
661
+ logger.debug(`Closing DataChannel ${dataChannel.channelId} due to inactivity`);
662
+ dataChannel.close();
779
663
  }
780
664
  }
781
665
  }
666
+ }
782
667
 
783
- // update metrics
784
- const wasUsingTURN = this.usingTURN;
785
-
786
- this.usingTURN = false;
787
- this.availableIncomingBitrate = 0;
788
- this.availableOutgoingBitrate = 0;
789
- this.totalSentBytes = 0;
790
- this.totalReceivedBytes = 0;
791
- this.totalOutboundPacketsSent = 0;
792
- this.totalInboundPacketsReceived = 0;
793
- this.totalInboundPacketsLost = 0;
794
- this.totalDataChannelBytesSent = 0;
795
- this.totalDataChannelBytesReceived = 0;
796
- this.sendingBitrate = 0;
797
- this.receivingBitrate = 0;
798
- let sumRttInMs = 0;
799
-
800
- for (const peerConnection of this._peerConnections.values()) {
801
- if (peerConnection.closed) continue;
802
- peerConnection.updateMetrics();
803
- this.totalSentBytes += peerConnection.totalSentAudioBytes + peerConnection.totalSentVideoBytes;
804
- this.totalReceivedBytes += peerConnection.totalReceivedAudioBytes + peerConnection.totalReceivedVideoBytes;
805
- this.totalOutboundPacketsSent += peerConnection.totalOutboundPacketsSent;
806
- this.totalInboundPacketsReceived += peerConnection.totalInboundPacketsReceived;
807
- this.totalInboundPacketsLost += peerConnection.totalInboundPacketsLost;
808
- this.totalDataChannelBytesSent += peerConnection.totalDataChannelBytesSent;
809
- this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
810
- this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
811
- this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
812
- this.availableIncomingBitrate += peerConnection.availableIncomingBitrate ?? 0;
813
- this.availableOutgoingBitrate += peerConnection.availableOutgoingBitrate ?? 0;
814
- sumRttInMs += peerConnection.avgRttInMs ?? 0;
815
-
816
- if (peerConnection.usingTURN) this.usingTURN = true;
817
- }
668
+ // update metrics
669
+ const wasUsingTURN = this.usingTURN;
670
+
671
+ this.usingTURN = false;
672
+ this.availableIncomingBitrate = 0;
673
+ this.availableOutgoingBitrate = 0;
674
+ this.totalSentBytes = 0;
675
+ this.totalReceivedBytes = 0;
676
+ this.totalOutboundPacketsSent = 0;
677
+ this.totalInboundPacketsReceived = 0;
678
+ this.totalInboundPacketsLost = 0;
679
+ this.totalDataChannelBytesSent = 0;
680
+ this.totalDataChannelBytesReceived = 0;
681
+ this.sendingBitrate = 0;
682
+ this.receivingBitrate = 0;
683
+ let sumRttInMs = 0;
684
+
685
+ for (const peerConnection of this._peerConnections.values()) {
686
+ if (peerConnection.closed) continue;
687
+ peerConnection.updateMetrics();
688
+ this.totalSentBytes += peerConnection.totalSentAudioBytes + peerConnection.totalSentVideoBytes;
689
+ this.totalReceivedBytes += peerConnection.totalReceivedAudioBytes + peerConnection.totalReceivedVideoBytes;
690
+ this.totalOutboundPacketsSent += peerConnection.totalOutboundPacketsSent;
691
+ this.totalInboundPacketsReceived += peerConnection.totalInboundPacketsReceived;
692
+ this.totalInboundPacketsLost += peerConnection.totalInboundPacketsLost;
693
+ this.totalDataChannelBytesSent += peerConnection.totalDataChannelBytesSent;
694
+ this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
695
+ this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
696
+ this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
697
+ this.availableIncomingBitrate += peerConnection.availableIncomingBitrate ?? 0;
698
+ this.availableOutgoingBitrate += peerConnection.availableOutgoingBitrate ?? 0;
699
+ sumRttInMs += peerConnection.avgRttInMs ?? 0;
700
+
701
+ if (peerConnection.usingTURN) this.usingTURN = true;
702
+ }
818
703
 
819
- this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
704
+ this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
820
705
 
821
- if (executeSave) await this._save();
822
- if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
706
+ if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
823
707
 
824
- this._updated = sample.timestamp;
825
- this.emit('update');
826
- }).finally(() => {
827
- --this._processingSample;
828
- });
708
+ this._updated = now;
709
+ this.emit('update');
829
710
  }
830
711
 
831
- private async _createPeerConnection(peerConnectionId: string): Promise<ObservedPeerConnection> {
832
- const result = await ObservedPeerConnection.create({
712
+ private _createPeerConnection(peerConnectionId: string, label?: string): ObservedPeerConnection {
713
+ const result = new ObservedPeerConnection({
833
714
  peerConnectionId,
834
- }, this, this._storageProvider);
835
-
836
- const pendingTracksTimestamp = this.ωpendingCreatedTracksTimestamp.get(peerConnectionId);
837
-
838
- if (pendingTracksTimestamp) {
839
- result.created = pendingTracksTimestamp;
840
- this.ωpendingCreatedTracksTimestamp.delete(peerConnectionId);
841
- }
715
+ label,
716
+ }, this);
842
717
 
843
718
  const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }: { localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate }) => {
844
719
  this.emit('selectedcandidatepair', {
@@ -851,25 +726,15 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
851
726
  result.once('close', () => {
852
727
  result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
853
728
  this._peerConnections.delete(peerConnectionId);
854
- this._model.peerConnectionIds = this._model.peerConnectionIds.filter((id) => id !== peerConnectionId);
855
- this._save().catch(() => void 0);
856
729
  });
857
730
  result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
858
731
  this._peerConnections.set(peerConnectionId, result);
859
- this._model.peerConnectionIds.push(peerConnectionId);
860
- await this._save();
861
732
 
862
733
  this.emit('newpeerconnection', result);
863
734
 
864
735
  return result;
865
736
  }
866
737
 
867
- private async _save() {
868
- if (this._closed) throw new Error(`Client ${this.clientId} is closed`);
869
-
870
- return this._storageProvider.clientStorage.set(this.clientId, this._model);
871
- }
872
-
873
738
  private _addClientLeftReport() {
874
739
  if (this._left) return;
875
740
  this._left = Date.now();
@@ -880,7 +745,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
880
745
  this.roomId,
881
746
  this.callId,
882
747
  this.clientId,
883
- this.joined,
748
+ this._left,
884
749
  this.userId,
885
750
  this.marker,
886
751
  ));