@observertc/observer-js 1.0.1-72a904d.0 → 1.0.1-9341fcd.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/lib/ObservedCall.d.ts +4 -8
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +16 -72
  4. package/lib/ObservedCallEventMonitor.d.ts +104 -0
  5. package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
  6. package/lib/ObservedCallEventMonitor.js +314 -0
  7. package/lib/ObservedClient.d.ts +16 -8
  8. package/lib/ObservedClient.d.ts.map +1 -1
  9. package/lib/ObservedClient.js +257 -99
  10. package/lib/ObservedClientEventMonitor.d.ts +89 -0
  11. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  12. package/lib/ObservedClientEventMonitor.js +247 -0
  13. package/lib/ObservedDataChannel.d.ts +2 -0
  14. package/lib/ObservedDataChannel.d.ts.map +1 -1
  15. package/lib/ObservedDataChannel.js +2 -0
  16. package/lib/ObservedIceCandidatePair.d.ts +2 -1
  17. package/lib/ObservedIceCandidatePair.d.ts.map +1 -1
  18. package/lib/ObservedIceCandidatePair.js +17 -1
  19. package/lib/ObservedInboundTrack.d.ts +2 -0
  20. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  21. package/lib/ObservedInboundTrack.js +2 -0
  22. package/lib/ObservedOutboundTrack.d.ts +2 -0
  23. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  24. package/lib/ObservedOutboundTrack.js +2 -0
  25. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  26. package/lib/ObservedPeerConnection.js +22 -6
  27. package/lib/Observer.d.ts +9 -7
  28. package/lib/Observer.d.ts.map +1 -1
  29. package/lib/Observer.js +16 -22
  30. package/lib/ObserverEventMonitor.d.ts +106 -0
  31. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  32. package/lib/ObserverEventMonitor.js +320 -0
  33. package/lib/ObserverSummary.d.ts +2 -0
  34. package/lib/ObserverSummary.d.ts.map +1 -1
  35. package/lib/common/utils.d.ts +1 -0
  36. package/lib/common/utils.d.ts.map +1 -1
  37. package/lib/common/utils.js +7 -1
  38. package/lib/index.d.ts +2 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +5 -1
  41. package/lib/schema/ClientEventTypes.d.ts +60 -0
  42. package/lib/schema/ClientEventTypes.d.ts.map +1 -1
  43. package/lib/schema/ClientSample.d.ts +1 -1
  44. package/lib/schema/ClientSample.d.ts.map +1 -1
  45. package/lib/updaters/{OnAllCallObserverUpdater_.d.ts → OnAllCallObserverUpdater.d.ts} +3 -1
  46. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  47. package/lib/updaters/{OnAllCallObserverUpdater_.js → OnAllCallObserverUpdater.js} +13 -1
  48. package/lib/updaters/OnAllClientCallUpdater.d.ts +2 -0
  49. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -1
  50. package/lib/updaters/OnAllClientCallUpdater.js +16 -0
  51. package/lib/updaters/{OnAnyCallObserverUpdater_.d.ts → OnAnyCallObserverUpdater.d.ts} +2 -1
  52. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  53. package/lib/updaters/{OnAnyCallObserverUpdater_.js → OnAnyCallObserverUpdater.js} +11 -1
  54. package/lib/updaters/OnAnyClientCallUpdater.d.ts +1 -0
  55. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -1
  56. package/lib/updaters/OnAnyClientCallUpdater.js +11 -2
  57. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  58. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  59. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  60. package/package.json +1 -1
  61. package/src/ObservedCall.ts +56 -82
  62. package/src/ObservedCallEventMonitor.ts +392 -0
  63. package/src/ObservedClient.ts +335 -114
  64. package/src/ObservedClientEventMonitor.ts +301 -0
  65. package/src/ObservedDataChannel.ts +3 -0
  66. package/src/ObservedIceCandidatePair.ts +18 -2
  67. package/src/ObservedInboundTrack.ts +3 -0
  68. package/src/ObservedOutboundTrack.ts +3 -0
  69. package/src/ObservedPeerConnection.ts +25 -10
  70. package/src/Observer.ts +47 -29
  71. package/src/ObserverEventMonitor.ts +398 -0
  72. package/src/ObserverSummary.ts +3 -1
  73. package/src/common/utils.ts +6 -0
  74. package/src/index.ts +2 -0
  75. package/src/schema/ClientEventTypes.ts +64 -0
  76. package/src/schema/ClientSample.ts +1 -1
  77. package/src/updaters/{OnAllCallObserverUpdater_.ts → OnAllCallObserverUpdater.ts} +14 -1
  78. package/src/updaters/OnAllClientCallUpdater.ts +15 -0
  79. package/src/updaters/{OnAnyCallObserverUpdater_.ts → OnAnyCallObserverUpdater.ts} +11 -2
  80. package/src/updaters/OnAnyClientCallUpdater.ts +9 -2
  81. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  82. package/lib/updaters/OnAllCallObserverUpdater_.d.ts.map +0 -1
  83. package/lib/updaters/OnAnyCallObserverUpdater_.d.ts.map +0 -1
@@ -7,10 +7,9 @@ import * as MetaData from './schema/ClientMetaTypes';
7
7
  import { ClientEventTypes } from './schema/ClientEventTypes';
8
8
  import { ObservedCall } from './ObservedCall';
9
9
  import { ClientMetaTypes } from './schema/ClientMetaTypes';
10
- import { getMedian, parseJsonAs } from './common/utils';
10
+ import { parseJsonAs } from './common/utils';
11
11
  import { CalculatedScore } from './scores/CalculatedScore';
12
12
  import { Detectors } from './detectors/Detectors';
13
- import { ObservedClientSummary } from './ObservedClientSummary';
14
13
 
15
14
  const logger = createLogger('ObservedClient');
16
15
 
@@ -34,8 +33,9 @@ export type ObservedClientEvents = {
34
33
  left: [];
35
34
  usingturn: [boolean];
36
35
  usermediaerror: [string];
36
+ extensionStats: [ExtensionStat];
37
+ clientEvent: [ClientEvent];
37
38
 
38
- // do we need this?
39
39
  newpeerconnection: [ObservedPeerConnection];
40
40
  };
41
41
 
@@ -65,6 +65,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
65
65
 
66
66
  public joinedAt?: number;
67
67
  public leftAt?: number;
68
+ public closedAt?: number;
68
69
  // the timestamp of the CLIENT_JOINED event
69
70
  public operationSystem?: MetaData.OperationSystem;
70
71
  public engine?: MetaData.Engine;
@@ -73,8 +74,10 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
73
74
  public mediaConstraints: string[] = [];
74
75
 
75
76
  public usingTURN = false;
77
+ public usingTCP = false;
76
78
  public availableOutgoingBitrate = 0;
77
79
  public availableIncomingBitrate = 0;
80
+
78
81
  public totalInboundPacketsLost = 0;
79
82
  public totalInboundPacketsReceived = 0;
80
83
  public totalOutboundPacketsSent = 0;
@@ -100,7 +103,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
100
103
  public deltaInboundPacketsLost = 0;
101
104
  public deltaInboundPacketsReceived = 0;
102
105
  public deltaOutboundPacketsSent = 0;
103
- public deltaRttInMsMeasurements: number[] = [];
104
106
  public deltaTransportSentBytes = 0;
105
107
  public deltaTransportReceivedBytes = 0;
106
108
  public deltaRttLt50Measurements = 0;
@@ -108,7 +110,10 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
108
110
  public deltaRttLt300Measurements = 0;
109
111
  public deltaRttGtOrEq300Measurements = 0;
110
112
 
111
- public currentRttInMs?: number;
113
+ public currentMaxRttInMs?: number;
114
+ public currentMinRttInMs?: number;
115
+ public currentAvgRttInMs?: number;
116
+
112
117
  public sendingAudioBitrate = 0;
113
118
  public sendingVideoBitrate = 0;
114
119
  public receivingAudioBitrate = 0;
@@ -128,11 +133,13 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
128
133
 
129
134
  public totalScoreSum = 0;
130
135
  public numberOfScoreMeasurements = 0;
131
- public numberOfIssues = 0;
136
+ public totalNumberOfIssues = 0;
132
137
 
133
138
  public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
134
139
  public issues: ClientIssue[] = [];
135
140
 
141
+ private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
142
+
136
143
  public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
137
144
  super();
138
145
  this.setMaxListeners(Infinity);
@@ -157,6 +164,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
157
164
 
158
165
  Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
159
166
 
167
+ this.closedAt = Date.now();
168
+
160
169
  this.emit('close');
161
170
  }
162
171
 
@@ -166,7 +175,9 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
166
175
  const now = Date.now();
167
176
  const elapsedInMs = now - this.updated;
168
177
  const elapsedInSeconds = elapsedInMs / 1000;
169
-
178
+ let sumOfRtts = 0;
179
+ let numberOfRttMeasurements = 0;
180
+
170
181
  ++this.acceptedSamples;
171
182
 
172
183
  this.availableIncomingBitrate = 0;
@@ -184,7 +195,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
184
195
  this.deltaSentVideoBytes = 0;
185
196
  this.deltaTransportReceivedBytes = 0;
186
197
  this.deltaTransportSentBytes = 0;
187
- this.deltaRttInMsMeasurements = [];
188
198
  this.deltaRttLt50Measurements = 0;
189
199
  this.deltaRttLt150Measurements = 0;
190
200
  this.deltaRttLt300Measurements = 0;
@@ -196,14 +206,95 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
196
206
  this.numberOfInbundTracks = 0;
197
207
  this.numberOfOutboundRtpStreams = 0;
198
208
  this.numberOfOutboundTracks = 0;
199
-
200
- sample.clientEvents?.forEach(this._processClientEvent.bind(this));
201
- sample.clientMetaItems?.forEach(this.addMetadata.bind(this));
202
- sample.clientIssues?.forEach(this.addIssue.bind(this));
203
- sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
204
- sample.extensionStats?.forEach(this.addExtensionStats.bind(this));
209
+ this.usingTURN = false;
210
+ this.usingTCP = false;
211
+ this.currentMinRttInMs = undefined;
212
+ this.currentMaxRttInMs = undefined;
213
+
214
+ this._mergeInjections(sample);
205
215
 
206
- this._updateRttMeasurements();
216
+ const clientEventsPostBuffer: ClientEvent[] = [];
217
+
218
+ for (const clientEvent of sample.clientEvents ?? []) {
219
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
220
+
221
+ this.call.observer.emit('client-event', this, clientEvent);
222
+ }
223
+
224
+ for (const metaData of sample.clientMetaItems ?? []) {
225
+ this.addMetadata(metaData);
226
+ }
227
+
228
+ for (const issue of sample.clientIssues ?? []) {
229
+ this.addIssue(issue);
230
+
231
+ ++this.deltaNumberOfIssues;
232
+ }
233
+
234
+ for (const extensionStat of sample.extensionStats ?? []) {
235
+ this.addExtensionStats(extensionStat);
236
+ }
237
+
238
+ for (const pcSample of sample.peerConnections ?? []) {
239
+ const observedPeerConnection = this._updatePeerConnection(pcSample);
240
+
241
+ if (!observedPeerConnection) continue;
242
+
243
+ this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
244
+ this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
245
+ this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
246
+ this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
247
+ this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
248
+ this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
249
+ this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
250
+ this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
251
+ this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
252
+ this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
253
+ this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
254
+ this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
255
+ this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
256
+
257
+ this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
258
+ this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
259
+
260
+ this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
261
+ this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
262
+ this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
263
+ this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
264
+ this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
265
+
266
+ if (observedPeerConnection.usingTURN) {
267
+ this.usingTURN = true;
268
+ }
269
+ if (observedPeerConnection.usingTCP) {
270
+ this.usingTCP = true;
271
+ }
272
+
273
+ if (observedPeerConnection.currentRttInMs) {
274
+ if (this.currentMinRttInMs === undefined || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
275
+ this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
276
+ }
277
+ if (this.currentMaxRttInMs === undefined || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
278
+ this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
279
+ }
280
+ if (observedPeerConnection.currentRttInMs < 50) {
281
+ this.deltaRttLt50Measurements += 1;
282
+ } else if (observedPeerConnection.currentRttInMs < 150) {
283
+ this.deltaRttLt150Measurements += 1;
284
+ } else if (observedPeerConnection.currentRttInMs < 300) {
285
+ this.deltaRttLt300Measurements += 1;
286
+ } else if (300 <= observedPeerConnection.currentRttInMs) {
287
+ this.deltaRttGtOrEq300Measurements += 1;
288
+ }
289
+
290
+ sumOfRtts += observedPeerConnection.currentRttInMs;
291
+ ++numberOfRttMeasurements;
292
+ }
293
+ }
294
+
295
+ for (const clientEvent of clientEventsPostBuffer) {
296
+ this._processClientEvent(clientEvent);
297
+ }
207
298
 
208
299
  // emit new attachments?
209
300
  this.attachments = sample.attachments;
@@ -225,12 +316,13 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
225
316
  this.totalRttLt150Measurements += this.deltaRttLt150Measurements;
226
317
  this.totalRttLt300Measurements += this.deltaRttLt300Measurements;
227
318
  this.totalRttGtOrEq300Measurements += this.deltaRttGtOrEq300Measurements;
228
- this.numberOfIssues += this.deltaNumberOfIssues;
319
+ this.totalNumberOfIssues += this.deltaNumberOfIssues;
229
320
 
230
321
  this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
231
322
  this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
232
323
  this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
233
324
  this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
325
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : undefined;
234
326
 
235
327
  this.calculatedScore.value = sample.score;
236
328
  this.detectors.update();
@@ -249,7 +341,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
249
341
  }
250
342
  }
251
343
 
252
- private _processClientEvent(event: ClientEvent) {
344
+ private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
253
345
  // eslint-disable-next-line no-console
254
346
  // console.warn('ClientEvent', event);
255
347
  switch (event.type) {
@@ -278,9 +370,150 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
278
370
  }
279
371
  break;
280
372
  }
373
+ case ClientEventTypes.PEER_CONNECTION_OPENED: {
374
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
375
+
376
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
377
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
378
+
379
+ if (observedPeerConnection) {
380
+ observedPeerConnection.openedAt = event.timestamp;
381
+ } else if (postBuffer) {
382
+ postBuffer.push(event);
383
+ } else {
384
+ logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection', event);
385
+ }
386
+
387
+ }
388
+ break;
389
+ }
390
+ case ClientEventTypes.PEER_CONNECTION_CLOSED: {
391
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
392
+
393
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
394
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
395
+
396
+ if (observedPeerConnection) {
397
+ observedPeerConnection.closedAt = event.timestamp;
398
+ } else if (postBuffer) {
399
+ postBuffer.push(event);
400
+ } else {
401
+ logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection', event);
402
+ }
403
+ }
404
+ break;
405
+ }
406
+ case ClientEventTypes.MEDIA_TRACK_ADDED: {
407
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
408
+
409
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
410
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
411
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
412
+
413
+ if (observedTrack) {
414
+ observedTrack.addedAt = event.timestamp;
415
+ } else if (postBuffer) {
416
+ postBuffer.push(event);
417
+ } else {
418
+ logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack', event);
419
+ }
420
+ }
421
+ break;
422
+ }
423
+ case ClientEventTypes.MEDIA_TRACK_REMOVED: {
424
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
425
+
426
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
427
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
428
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
429
+
430
+ if (observedTrack) {
431
+ observedTrack.removedAt = event.timestamp;
432
+ } else if (postBuffer) {
433
+ postBuffer.push(event);
434
+ } else {
435
+ logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack', event);
436
+ }
437
+ }
438
+ break;
439
+ }
440
+ case ClientEventTypes.DATA_CHANNEL_OPEN: {
441
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
442
+
443
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
444
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
445
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
446
+
447
+ if (observedDataChannel) {
448
+ observedDataChannel.addedAt = event.timestamp;
449
+ } else if (postBuffer) {
450
+ postBuffer.push(event);
451
+ } else {
452
+ logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel', event);
453
+ }
454
+ }
455
+ break;
456
+ }
457
+ case ClientEventTypes.DATA_CHANNEL_CLOSED: {
458
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
459
+
460
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
461
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
462
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
463
+
464
+ if (observedDataChannel) {
465
+ observedDataChannel.removedAt = event.timestamp;
466
+ } else if (postBuffer) {
467
+ postBuffer.push(event);
468
+ } else {
469
+ logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel', event);
470
+ }
471
+ }
472
+ break;
473
+ }
281
474
  }
475
+ this.emit('clientEvent', event);
476
+ }
477
+
478
+ public injectMetaData(metaData: ClientMetaData) {
479
+ if (this.closed) return;
480
+
481
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
482
+
483
+ this._injections.clientMetaItems.push(metaData);
484
+ }
485
+
486
+ public injectEvent(event: ClientEvent) {
487
+ if (this.closed) return;
488
+
489
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
490
+
491
+ this._injections.clientEvents.push(event);
492
+ }
493
+
494
+ public injectIssue(issue: ClientIssue) {
495
+ if (this.closed) return;
496
+
497
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
498
+
499
+ this._injections.clientIssues.push(issue);
500
+ }
282
501
 
283
- this.call.observer.emit('client-event', this, event);
502
+ public injectExtensionStat(stat: ExtensionStat) {
503
+ if (this.closed) return;
504
+
505
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
506
+
507
+ this._injections.extensionStats.push(stat);
508
+ }
509
+
510
+ public injectAttachment(key: string, value: unknown) {
511
+ if (this.closed) return;
512
+
513
+ if (!this._injections.attachments) this._injections.attachments = {};
514
+
515
+ this._injections.attachments[key] = value;
516
+
284
517
  }
285
518
 
286
519
  public addMetadata(metadata: ClientMetaData) {
@@ -311,24 +544,24 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
311
544
  public addIssue(issue: ClientIssue) {
312
545
  if (this.closed) return;
313
546
 
314
- ++this.deltaNumberOfIssues;
315
547
  this.emit('issue', issue);
316
548
  this.call.observer.emit('client-issue', this, issue);
317
549
  }
318
550
 
319
551
  public addExtensionStats(stats: ExtensionStat) {
320
552
  this.call.observer.emit('client-extension-stats', this, stats);
553
+ this.emit('extensionStats', stats);
321
554
  }
322
555
 
323
- private _updatePeerConnection(sample: PeerConnectionSample) {
556
+ private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
324
557
  let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
325
558
 
326
559
  if (!observedPeerConnection) {
327
560
  if (!sample.peerConnectionId) {
328
- return logger.warn(
561
+ return (logger.warn(
329
562
  `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
330
563
  sample
331
- );
564
+ ), void 0);
332
565
  }
333
566
 
334
567
  observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
@@ -343,107 +576,95 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
343
576
 
344
577
  observedPeerConnection.accept(sample);
345
578
 
346
- observedPeerConnection.currentRttInMs;
347
-
348
- this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
349
- this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
350
- this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
351
- this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
352
- this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
353
- this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
354
- this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
355
- this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
356
- this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
357
- this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
358
- this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
359
- this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
360
- this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
361
-
362
- this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
363
- this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
364
-
365
- this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
366
- this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
367
- this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
368
- this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
369
- this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
370
-
371
- if (observedPeerConnection.currentRttInMs) {
372
- this.deltaRttInMsMeasurements.push(observedPeerConnection.currentRttInMs);
373
- }
579
+ return observedPeerConnection;
374
580
  }
375
581
 
376
- public resetSummaryMetrics() {
377
- this.totalDataChannelBytesReceived = 0;
378
- this.totalDataChannelBytesSent = 0;
379
- this.totalDataChannelMessagesReceived = 0;
380
- this.totalDataChannelMessagesSent = 0;
381
- this.totalInboundPacketsLost = 0;
382
- this.totalInboundPacketsReceived = 0;
383
- this.totalOutboundPacketsSent = 0;
384
- this.totalReceivedAudioBytes = 0;
385
- this.totalReceivedVideoBytes = 0;
386
- this.totalSentAudioBytes = 0;
387
- this.totalSentVideoBytes = 0;
388
- this.totalSentBytes = 0;
389
- this.totalReceivedBytes = 0;
390
-
391
- this.numberOfIssues = 0;
392
-
393
- this.totalScoreSum = 0;
394
- this.numberOfScoreMeasurements = 0;
395
- }
582
+ private _mergeInjections(sample: ClientSample): ClientSample {
583
+ if (this.closed) return sample;
396
584
 
397
- public createSummary(): ObservedClientSummary {
398
- return {
399
- totalRttLt50Measurements: this.totalRttLt50Measurements,
400
- totalRttLt150Measurements: this.totalRttLt150Measurements,
401
- totalRttLt300Measurements: this.totalRttLt300Measurements,
402
- totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
403
- totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
404
- totalDataChannelBytesSent: this.totalDataChannelBytesSent,
405
- totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
406
- totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
407
- totalInboundPacketsLost: this.totalInboundPacketsLost,
408
- totalInboundPacketsReceived: this.totalInboundPacketsReceived,
409
- totalOutboundPacketsSent: this.totalOutboundPacketsSent,
410
- totalReceivedAudioBytes: this.totalReceivedAudioBytes,
411
- totalReceivedVideoBytes: this.totalReceivedVideoBytes,
412
- totalSentAudioBytes: this.totalSentAudioBytes,
413
- totalSentVideoBytes: this.totalSentVideoBytes,
414
- totalSentBytes: this.totalSentBytes,
415
- totalReceivedBytes: this.totalReceivedBytes,
585
+ if (this._injections.clientEvents) {
586
+ if (!sample.clientEvents) sample.clientEvents = [];
587
+ sample.clientEvents.push(...this._injections.clientEvents);
416
588
 
417
- numberOfIssues: this.numberOfIssues,
418
-
419
- totalScoreSum: this.totalScoreSum,
420
- numberOfScoreMeasurements: this.numberOfScoreMeasurements,
421
- };
422
- }
423
-
424
- private _updateRttMeasurements() {
425
- if (this.deltaRttInMsMeasurements.length < 1) {
426
- this.currentRttInMs = undefined;
589
+ this._injections.clientEvents = undefined;
590
+ }
427
591
 
428
- return;
592
+ if (this._injections.clientIssues) {
593
+ if (!sample.clientIssues) sample.clientIssues = [];
594
+ sample.clientIssues.push(...this._injections.clientIssues);
595
+
596
+ this._injections.clientIssues = undefined;
429
597
  }
430
- this.currentRttInMs = getMedian(this.deltaRttInMsMeasurements, false);
431
598
 
432
- if (this.currentRttInMs < 0) {
433
- logger.warn(`Current Rtt for client ${this.clientId} is smaller than 0`);
434
- this.currentRttInMs = undefined;
599
+ if (this._injections.extensionStats) {
600
+ if (!sample.extensionStats) sample.extensionStats = [];
601
+ sample.extensionStats.push(...this._injections.extensionStats);
602
+
603
+ this._injections.extensionStats = undefined;
604
+ }
435
605
 
436
- return;
606
+ if (this._injections.attachments) {
607
+ if (!sample.attachments) sample.attachments = {};
608
+ Object.assign(sample.attachments, this._injections.attachments);
609
+
610
+ this._injections.attachments = undefined;
437
611
  }
438
-
439
- if (this.currentRttInMs < 50) {
440
- this.deltaRttLt50Measurements += 1;
441
- } else if (this.currentRttInMs < 150) {
442
- this.deltaRttLt150Measurements += 1;
443
- } else if (this.currentRttInMs < 300) {
444
- this.deltaRttLt300Measurements += 1;
445
- } else if (300 <= this.currentRttInMs) {
446
- this.deltaRttGtOrEq300Measurements += 1;
612
+
613
+ if (this._injections.clientMetaItems) {
614
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
615
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
616
+
617
+ this._injections.clientMetaItems = undefined;
447
618
  }
619
+
620
+ return sample;
448
621
  }
622
+
623
+ // public resetSummaryMetrics() {
624
+ // this.totalDataChannelBytesReceived = 0;
625
+ // this.totalDataChannelBytesSent = 0;
626
+ // this.totalDataChannelMessagesReceived = 0;
627
+ // this.totalDataChannelMessagesSent = 0;
628
+ // this.totalInboundPacketsLost = 0;
629
+ // this.totalInboundPacketsReceived = 0;
630
+ // this.totalOutboundPacketsSent = 0;
631
+ // this.totalReceivedAudioBytes = 0;
632
+ // this.totalReceivedVideoBytes = 0;
633
+ // this.totalSentAudioBytes = 0;
634
+ // this.totalSentVideoBytes = 0;
635
+ // this.totalSentBytes = 0;
636
+ // this.totalReceivedBytes = 0;
637
+
638
+ // this.totalNumberOfIssues = 0;
639
+
640
+ // this.totalScoreSum = 0;
641
+ // this.numberOfScoreMeasurements = 0;
642
+ // }
643
+
644
+ // public createSummary(): ObservedClientSummary {
645
+ // return {
646
+ // totalRttLt50Measurements: this.totalRttLt50Measurements,
647
+ // totalRttLt150Measurements: this.totalRttLt150Measurements,
648
+ // totalRttLt300Measurements: this.totalRttLt300Measurements,
649
+ // totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
650
+ // totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
651
+ // totalDataChannelBytesSent: this.totalDataChannelBytesSent,
652
+ // totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
653
+ // totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
654
+ // totalInboundPacketsLost: this.totalInboundPacketsLost,
655
+ // totalInboundPacketsReceived: this.totalInboundPacketsReceived,
656
+ // totalOutboundPacketsSent: this.totalOutboundPacketsSent,
657
+ // totalReceivedAudioBytes: this.totalReceivedAudioBytes,
658
+ // totalReceivedVideoBytes: this.totalReceivedVideoBytes,
659
+ // totalSentAudioBytes: this.totalSentAudioBytes,
660
+ // totalSentVideoBytes: this.totalSentVideoBytes,
661
+ // totalSentBytes: this.totalSentBytes,
662
+ // totalReceivedBytes: this.totalReceivedBytes,
663
+
664
+ // numberOfIssues: this.totalNumberOfIssues,
665
+
666
+ // totalScoreSum: this.totalScoreSum,
667
+ // numberOfScoreMeasurements: this.numberOfScoreMeasurements,
668
+ // };
669
+ // }
449
670
  }