@observertc/observer-js 1.0.1-c3edea3.0 → 1.0.1-ec27289.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 (125) hide show
  1. package/lib/ObservedCall.d.ts +41 -7
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +125 -13
  4. package/lib/ObservedCallSummary.d.ts +18 -0
  5. package/lib/ObservedCallSummary.d.ts.map +1 -0
  6. package/lib/ObservedClient.d.ts +70 -11
  7. package/lib/ObservedClient.d.ts.map +1 -1
  8. package/lib/ObservedClient.js +394 -72
  9. package/lib/ObservedClientEventMonitor.d.ts +89 -0
  10. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  11. package/lib/ObservedClientEventMonitor.js +247 -0
  12. package/lib/ObservedClientSummary.d.ts +23 -0
  13. package/lib/ObservedClientSummary.d.ts.map +1 -0
  14. package/lib/ObservedClientSummary.js +2 -0
  15. package/lib/ObservedDataChannel.d.ts +6 -0
  16. package/lib/ObservedDataChannel.d.ts.map +1 -1
  17. package/lib/ObservedDataChannel.js +30 -0
  18. package/lib/ObservedIceCandidatePair.d.ts +4 -0
  19. package/lib/ObservedIceCandidatePair.d.ts.map +1 -1
  20. package/lib/ObservedIceCandidatePair.js +28 -0
  21. package/lib/ObservedIceTransport.d.ts +4 -0
  22. package/lib/ObservedIceTransport.d.ts.map +1 -1
  23. package/lib/ObservedIceTransport.js +28 -0
  24. package/lib/ObservedInboundTrack.d.ts +2 -0
  25. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  26. package/lib/ObservedInboundTrack.js +2 -0
  27. package/lib/ObservedOutboundTrack.d.ts +2 -0
  28. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  29. package/lib/ObservedOutboundTrack.js +2 -0
  30. package/lib/ObservedPeerConnection.d.ts +14 -2
  31. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  32. package/lib/ObservedPeerConnection.js +106 -14
  33. package/lib/ObservedTURN.d.ts +31 -0
  34. package/lib/ObservedTURN.d.ts.map +1 -0
  35. package/lib/ObservedTURN.js +58 -0
  36. package/lib/ObservedTurnServer.d.ts +25 -0
  37. package/lib/ObservedTurnServer.d.ts.map +1 -0
  38. package/lib/ObservedTurnServer.js +59 -0
  39. package/lib/Observer.d.ts +38 -21
  40. package/lib/Observer.d.ts.map +1 -1
  41. package/lib/Observer.js +114 -40
  42. package/lib/ObserverSummary.d.ts +13 -0
  43. package/lib/ObserverSummary.d.ts.map +1 -0
  44. package/lib/ObserverSummary.js +2 -0
  45. package/lib/common/utils.d.ts +2 -1
  46. package/lib/common/utils.d.ts.map +1 -1
  47. package/lib/common/utils.js +11 -3
  48. package/lib/index.d.ts +5 -0
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +7 -1
  51. package/lib/schema/ClientEventTypes.d.ts +100 -0
  52. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  53. package/lib/schema/ClientEventTypes.js +38 -0
  54. package/lib/schema/{metaTypes.d.ts → ClientMetaTypes.d.ts} +2 -2
  55. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  56. package/lib/schema/ClientMetaTypes.js +16 -0
  57. package/lib/updaters/CallUpdater.d.ts.map +1 -0
  58. package/lib/updaters/CallUpdater.js +2 -0
  59. package/lib/updaters/ObserverUpdater.d.ts +5 -0
  60. package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
  61. package/lib/updaters/ObserverUpdater.js +2 -0
  62. package/lib/updaters/OnAllCallObserverUpdater_.d.ts +13 -0
  63. package/lib/updaters/OnAllCallObserverUpdater_.d.ts.map +1 -0
  64. package/lib/updaters/OnAllCallObserverUpdater_.js +38 -0
  65. package/lib/updaters/OnAllClientCallUpdater.d.ts +13 -0
  66. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
  67. package/lib/updaters/OnAllClientCallUpdater.js +37 -0
  68. package/lib/updaters/OnAnyCallObserverUpdater_.d.ts +11 -0
  69. package/lib/updaters/OnAnyCallObserverUpdater_.d.ts.map +1 -0
  70. package/lib/updaters/OnAnyCallObserverUpdater_.js +27 -0
  71. package/lib/updaters/OnAnyClientCallUpdater.d.ts +11 -0
  72. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
  73. package/lib/updaters/OnAnyClientCallUpdater.js +27 -0
  74. package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
  75. package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
  76. package/lib/updaters/OnIntervalUpdater.js +17 -0
  77. package/lib/updaters/Updater.d.ts +6 -0
  78. package/lib/updaters/Updater.d.ts.map +1 -0
  79. package/lib/updaters/Updater.js +2 -0
  80. package/package.json +1 -1
  81. package/src/ObservedCall.ts +205 -16
  82. package/src/ObservedCallSummary.ts +22 -0
  83. package/src/ObservedClient.ts +492 -78
  84. package/src/ObservedClientEventMonitor.ts +301 -0
  85. package/src/ObservedClientSummary.ts +24 -0
  86. package/src/ObservedDataChannel.ts +32 -0
  87. package/src/ObservedIceCandidatePair.ts +26 -0
  88. package/src/ObservedIceTransport.ts +29 -0
  89. package/src/ObservedInboundTrack.ts +3 -0
  90. package/src/ObservedOutboundTrack.ts +3 -0
  91. package/src/ObservedPeerConnection.ts +120 -16
  92. package/src/ObservedTURN.ts +86 -0
  93. package/src/ObservedTurnServer.ts +72 -0
  94. package/src/Observer.ts +164 -64
  95. package/src/ObserverSummary.ts +15 -0
  96. package/src/common/utils.ts +12 -2
  97. package/src/index.ts +5 -0
  98. package/src/schema/ClientEventTypes.ts +112 -0
  99. package/src/schema/{metaTypes.ts → ClientMetaTypes.ts} +1 -1
  100. package/src/updaters/ObserverUpdater.ts +5 -0
  101. package/src/updaters/OnAllCallObserverUpdater_.ts +46 -0
  102. package/src/updaters/OnAllClientCallUpdater.ts +46 -0
  103. package/src/updaters/OnAnyCallObserverUpdater_.ts +32 -0
  104. package/src/updaters/OnAnyClientCallUpdater.ts +32 -0
  105. package/src/updaters/OnIntervalUpdater.ts +19 -0
  106. package/src/updaters/Updater.ts +5 -0
  107. package/tsconfig.json +1 -1
  108. package/lib/schema/eventTypes.d.ts +0 -33
  109. package/lib/schema/eventTypes.d.ts.map +0 -1
  110. package/lib/schema/eventTypes.js +0 -38
  111. package/lib/schema/metaTypes.d.ts.map +0 -1
  112. package/lib/schema/metaTypes.js +0 -16
  113. package/lib/utils/CallUpdater.d.ts.map +0 -1
  114. package/lib/utils/OnAllClientCallUpdater.d.ts +0 -16
  115. package/lib/utils/OnAllClientCallUpdater.d.ts.map +0 -1
  116. package/lib/utils/OnAllClientCallUpdater.js +0 -58
  117. package/lib/utils/onAnyClientCallUpdater.d.ts +0 -14
  118. package/lib/utils/onAnyClientCallUpdater.d.ts.map +0 -1
  119. package/lib/utils/onAnyClientCallUpdater.js +0 -51
  120. package/src/schema/eventTypes.ts +0 -36
  121. package/src/utils/OnAllClientCallUpdater.ts +0 -68
  122. package/src/utils/onAnyClientCallUpdater.ts +0 -59
  123. /package/lib/{utils/CallUpdater.js → ObservedCallSummary.js} +0 -0
  124. /package/lib/{utils → updaters}/CallUpdater.d.ts +0 -0
  125. /package/src/{utils → updaters}/CallUpdater.ts +0 -0
@@ -3,13 +3,14 @@ import { ObservedPeerConnection } from './ObservedPeerConnection';
3
3
  import { createLogger } from './common/logger';
4
4
  // eslint-disable-next-line camelcase
5
5
  import { ClientEvent, ClientMetaData, ClientSample, PeerConnectionSample, ClientIssue, ExtensionStat } from './schema/ClientSample';
6
- import * as MetaData from './schema/metaTypes';
7
- import { ClientEventType } from './schema/eventTypes';
6
+ import * as MetaData from './schema/ClientMetaTypes';
7
+ import { ClientEventTypes } from './schema/ClientEventTypes';
8
8
  import { ObservedCall } from './ObservedCall';
9
- import { ClientMetaType } from './schema/metaTypes';
9
+ import { ClientMetaTypes } from './schema/ClientMetaTypes';
10
10
  import { parseJsonAs } from './common/utils';
11
11
  import { CalculatedScore } from './scores/CalculatedScore';
12
12
  import { Detectors } from './detectors/Detectors';
13
+ import { ObservedClientEventMonitor } from './ObservedClientEventMonitor';
13
14
 
14
15
  const logger = createLogger('ObservedClient');
15
16
 
@@ -27,6 +28,8 @@ export type ObservedClientEvents = {
27
28
  ];
28
29
  close: [];
29
30
  joined: [];
31
+ issue: [ClientIssue];
32
+ metaData: [ClientMetaData];
30
33
  rejoined: [timestamp: number];
31
34
  left: [];
32
35
  usingturn: [boolean];
@@ -53,7 +56,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
53
56
  value: undefined,
54
57
  };
55
58
 
56
- public appData?: AppData;
59
+ public appData: AppData;
57
60
  public attachments?: Record<string, unknown>;
58
61
 
59
62
  public updated = Date.now();
@@ -62,6 +65,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
62
65
 
63
66
  public joinedAt?: number;
64
67
  public leftAt?: number;
68
+ public closedAt?: number;
65
69
  // the timestamp of the CLIENT_JOINED event
66
70
  public operationSystem?: MetaData.OperationSystem;
67
71
  public engine?: MetaData.Engine;
@@ -70,47 +74,85 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
70
74
  public mediaConstraints: string[] = [];
71
75
 
72
76
  public usingTURN = false;
77
+ public usingTCP = false;
73
78
  public availableOutgoingBitrate = 0;
74
79
  public availableIncomingBitrate = 0;
75
- // public totalInboundPacketsLost = 0;
76
- // public totalInboundPacketsReceived = 0;
77
- // public totalOutboundPacketsSent = 0;
78
- // public totalDataChannelBytesSent = 0;
79
- // public totalDataChannelBytesReceived = 0;
80
- // public totalSentBytes = 0;
81
- // public totalReceivedBytes = 0;
82
- // public totalReceivedAudioBytes = 0;
83
- // public totalReceivedVideoBytes = 0;
84
- // public totalSentAudioBytes = 0;
85
- // public totalSentVideoBytes = 0;
86
-
87
- // public deltaReceivedAudioBytes = 0;
88
- // public deltaReceivedVideoBytes = 0;
89
- // public deltaSentAudioBytes = 0;
90
- // public deltaSentVideoBytes = 0;
91
- // public deltaDataChannelBytesSent = 0;
92
- // public deltaDataChannelBytesReceived = 0;
93
- // public deltaInboundPacketsLost = 0;
94
- // public deltaInboundPacketsReceived = 0;
95
- // public deltaOutboundPacketsSent = 0;
96
-
97
- public avgRttInMs?: number;
98
- public outboundAudioBitrate?: number;
99
- public outboundVideoBitrate?: number;
100
- public inboundAudioBitrate?: number;
101
- public inboundVideoBitrate?: number;
80
+
81
+ public totalInboundPacketsLost = 0;
82
+ public totalInboundPacketsReceived = 0;
83
+ public totalOutboundPacketsSent = 0;
84
+ public totalDataChannelBytesSent = 0;
85
+ public totalDataChannelBytesReceived = 0;
86
+ public totalDataChannelMessagesSent = 0;
87
+ public totalDataChannelMessagesReceived = 0;
88
+ public totalReceivedAudioBytes = 0;
89
+ public totalReceivedVideoBytes = 0;
90
+ public totalSentAudioBytes = 0;
91
+ public totalSentVideoBytes = 0;
92
+ public totalSentBytes = 0;
93
+ public totalReceivedBytes = 0;
94
+
95
+ public deltaReceivedAudioBytes = 0;
96
+ public deltaReceivedVideoBytes = 0;
97
+ public deltaSentAudioBytes = 0;
98
+ public deltaSentVideoBytes = 0;
99
+ public deltaDataChannelBytesSent = 0;
100
+ public deltaDataChannelBytesReceived = 0;
101
+ public deltaDataChannelMessagesSent = 0;
102
+ public deltaDataChannelMessagesReceived = 0;
103
+ public deltaInboundPacketsLost = 0;
104
+ public deltaInboundPacketsReceived = 0;
105
+ public deltaOutboundPacketsSent = 0;
106
+ public deltaTransportSentBytes = 0;
107
+ public deltaTransportReceivedBytes = 0;
108
+ public deltaRttLt50Measurements = 0;
109
+ public deltaRttLt150Measurements = 0;
110
+ public deltaRttLt300Measurements = 0;
111
+ public deltaRttGtOrEq300Measurements = 0;
112
+
113
+ public currentMaxRttInMs?: number;
114
+ public currentMinRttInMs?: number;
115
+ public currentAvgRttInMs?: number;
116
+
117
+ public sendingAudioBitrate = 0;
118
+ public sendingVideoBitrate = 0;
119
+ public receivingAudioBitrate = 0;
120
+ public receivingVideoBitrate = 0;
121
+
122
+ public numberOfInboundRtpStreams = 0;
123
+ public numberOfInbundTracks = 0;
124
+ public numberOfOutboundRtpStreams = 0;
125
+ public numberOfOutboundTracks = 0;
126
+ public numberOfDataChannels = 0;
127
+
128
+ public totalRttLt50Measurements = 0;
129
+ public totalRttLt150Measurements = 0;
130
+ public totalRttLt300Measurements = 0;
131
+ public totalRttGtOrEq300Measurements = 0;
132
+ public deltaNumberOfIssues = 0;
133
+
134
+ public totalScoreSum = 0;
135
+ public numberOfScoreMeasurements = 0;
136
+ public totalNumberOfIssues = 0;
102
137
 
103
138
  public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
104
139
  public issues: ClientIssue[] = [];
105
140
 
141
+ private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
142
+
106
143
  public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
107
144
  super();
108
145
  this.setMaxListeners(Infinity);
109
146
 
110
147
  this.clientId = settings.clientId;
111
- this.appData = settings.appData;
148
+ this.appData = settings.appData ?? {} as AppData;
149
+
112
150
  this.detectors = new Detectors();
113
151
  }
152
+
153
+ public get numberOfPeerConnections() {
154
+ return this.observedPeerConnections.size;
155
+ }
114
156
 
115
157
  public get score() {
116
158
  return this.calculatedScore.value;
@@ -122,6 +164,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
122
164
 
123
165
  Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
124
166
 
167
+ this.closedAt = Date.now();
168
+
125
169
  this.emit('close');
126
170
  }
127
171
 
@@ -129,16 +173,157 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
129
173
  if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
130
174
 
131
175
  const now = Date.now();
176
+ const elapsedInMs = now - this.updated;
177
+ const elapsedInSeconds = elapsedInMs / 1000;
178
+ let sumOfRtts = 0;
179
+ let numberOfRttMeasurements = 0;
180
+
181
+ ++this.acceptedSamples;
182
+
183
+ this.availableIncomingBitrate = 0;
184
+ this.availableOutgoingBitrate = 0;
185
+ this.deltaDataChannelBytesReceived = 0;
186
+ this.deltaDataChannelBytesSent = 0;
187
+ this.deltaDataChannelMessagesReceived = 0;
188
+ this.deltaDataChannelMessagesSent = 0;
189
+ this.deltaInboundPacketsLost = 0;
190
+ this.deltaInboundPacketsReceived = 0;
191
+ this.deltaOutboundPacketsSent = 0;
192
+ this.deltaReceivedAudioBytes = 0;
193
+ this.deltaReceivedVideoBytes = 0;
194
+ this.deltaSentAudioBytes = 0;
195
+ this.deltaSentVideoBytes = 0;
196
+ this.deltaTransportReceivedBytes = 0;
197
+ this.deltaTransportSentBytes = 0;
198
+ this.deltaRttLt50Measurements = 0;
199
+ this.deltaRttLt150Measurements = 0;
200
+ this.deltaRttLt300Measurements = 0;
201
+ this.deltaRttGtOrEq300Measurements = 0;
202
+ this.deltaNumberOfIssues = 0;
203
+
204
+ this.numberOfDataChannels = 0;
205
+ this.numberOfInboundRtpStreams = 0;
206
+ this.numberOfInbundTracks = 0;
207
+ this.numberOfOutboundRtpStreams = 0;
208
+ this.numberOfOutboundTracks = 0;
209
+ this.usingTURN = false;
210
+ this.usingTCP = false;
211
+ this.currentMinRttInMs = undefined;
212
+ this.currentMaxRttInMs = undefined;
213
+
214
+ this._mergeInjections(sample);
215
+
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
+ }
132
272
 
133
- sample.clientEvents?.forEach(this._processClientEvent.bind(this));
134
- sample.clientMetaItems?.forEach(this.processMetadata.bind(this));
135
- sample.clientIssues?.forEach(this.processIssue.bind(this));
136
- sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
137
- sample.extensionStats?.forEach(this.processExtensionStats.bind(this));
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
+ }
138
298
 
139
299
  // emit new attachments?
140
300
  this.attachments = sample.attachments;
141
301
 
302
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
303
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
304
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
305
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
306
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
307
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
308
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
309
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
310
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
311
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
312
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
313
+ this.totalReceivedBytes += this.deltaTransportReceivedBytes;
314
+ this.totalSentBytes += this.deltaTransportSentBytes;
315
+ this.totalRttLt50Measurements += this.deltaRttLt50Measurements;
316
+ this.totalRttLt150Measurements += this.deltaRttLt150Measurements;
317
+ this.totalRttLt300Measurements += this.deltaRttLt300Measurements;
318
+ this.totalRttGtOrEq300Measurements += this.deltaRttGtOrEq300Measurements;
319
+ this.totalNumberOfIssues += this.deltaNumberOfIssues;
320
+
321
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
322
+ this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
323
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
324
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
325
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : undefined;
326
+
142
327
  this.calculatedScore.value = sample.score;
143
328
  this.detectors.update();
144
329
 
@@ -148,19 +333,19 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
148
333
  elapsedTimeInMs: now - this.updated,
149
334
  });
150
335
  this.updated = now;
336
+
337
+ // if result changed after update
338
+ if (this.calculatedScore.value) {
339
+ this.totalScoreSum += this.calculatedScore.value;
340
+ ++this.numberOfScoreMeasurements;
341
+ }
151
342
  }
152
343
 
153
- private _processClientEvent(event: ClientEvent) {
344
+ private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
345
+ // eslint-disable-next-line no-console
346
+ // console.warn('ClientEvent', event);
154
347
  switch (event.type) {
155
- case ClientEventType.CALL_STARTED: {
156
- // update call timestamp if it's the first time
157
- break;
158
- }
159
- case ClientEventType.CALL_ENDED: {
160
- // update call timestamp if it's the first time
161
- break;
162
- }
163
- case ClientEventType.CLIENT_JOINED: {
348
+ case ClientEventTypes.CLIENT_JOINED: {
164
349
  if (event.timestamp) {
165
350
  if (!this.joinedAt) {
166
351
  this.joinedAt = event.timestamp;
@@ -174,76 +359,208 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
174
359
  }
175
360
  break;
176
361
  }
177
- case ClientEventType.CLIENT_LEFT: {
362
+ case ClientEventTypes.CLIENT_LEFT: {
178
363
  if (event.timestamp) {
179
364
  if (!this.leftAt) {
180
365
  this.leftAt = event.timestamp;
181
366
  this.emit('left');
367
+ } else {
368
+ logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
369
+ }
370
+ }
371
+ break;
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);
182
453
  }
183
454
  }
184
455
  break;
185
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
+ }
474
+
186
475
  }
476
+ }
187
477
 
188
- this.call.observer.processors.events.process({
189
- call: this.call,
190
- client: this,
191
- data: event,
192
- });
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
+ }
501
+
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
+
193
517
  }
194
518
 
195
- public processMetadata(metadata: ClientMetaData) {
519
+ public addMetadata(metadata: ClientMetaData) {
520
+ if (this.closed) return;
521
+
196
522
  switch (metadata.type) {
197
- case ClientMetaType.BROWSER: {
523
+ case ClientMetaTypes.BROWSER: {
198
524
  this.browser = parseJsonAs(metadata.payload);
199
525
  break;
200
526
  }
201
- case ClientMetaType.ENGINE: {
527
+ case ClientMetaTypes.ENGINE: {
202
528
  this.engine = parseJsonAs(metadata.payload);
203
529
  break;
204
530
  }
205
- case ClientMetaType.PLATFORM: {
531
+ case ClientMetaTypes.PLATFORM: {
206
532
  this.platform = parseJsonAs(metadata.payload);
207
533
  break;
208
534
  }
209
- case ClientMetaType.OPERATION_SYSTEM: {
535
+ case ClientMetaTypes.OPERATION_SYSTEM: {
210
536
  this.operationSystem = parseJsonAs(metadata.payload);
211
537
  break;
212
538
  }
213
539
  }
214
540
 
215
- this.call.observer.processors.metaData.process({
216
- call: this.call,
217
- client: this,
218
- data: metadata,
219
- });
541
+ this.call.observer.emit('client-metadata', this, metadata);
220
542
  }
221
543
 
222
- public processIssue(issue: ClientIssue) {
223
- this.call.observer.processors.issues.process({
224
- call: this.call,
225
- client: this,
226
- data: issue,
227
- });
544
+ public addIssue(issue: ClientIssue) {
545
+ if (this.closed) return;
546
+
547
+ this.emit('issue', issue);
548
+ this.call.observer.emit('client-issue', this, issue);
228
549
  }
229
550
 
230
- public processExtensionStats(stats: ExtensionStat) {
231
- this.call.observer.processors.extensionStats.process({
232
- call: this.call,
233
- client: this,
234
- data: stats,
235
- });
551
+ public addExtensionStats(stats: ExtensionStat) {
552
+ this.call.observer.emit('client-extension-stats', this, stats);
236
553
  }
237
554
 
238
- private _updatePeerConnection(sample: PeerConnectionSample) {
555
+ private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
239
556
  let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
240
557
 
241
558
  if (!observedPeerConnection) {
242
559
  if (!sample.peerConnectionId) {
243
- return logger.warn(
560
+ return (logger.warn(
244
561
  `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
245
562
  sample
246
- );
563
+ ), void 0);
247
564
  }
248
565
 
249
566
  observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
@@ -252,8 +569,105 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
252
569
  this.observedPeerConnections.delete(sample.peerConnectionId);
253
570
  });
254
571
  this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
572
+
573
+ this.emit('newpeerconnection', observedPeerConnection);
255
574
  }
256
575
 
257
576
  observedPeerConnection.accept(sample);
577
+
578
+ return observedPeerConnection;
579
+ }
580
+
581
+ public createEventMonitor<CTX = unknown>(ctx?: CTX): ObservedClientEventMonitor<CTX> {
582
+ return new ObservedClientEventMonitor<CTX>(this, ctx ?? {} as CTX);
258
583
  }
584
+
585
+ private _mergeInjections(sample: ClientSample): ClientSample {
586
+ if (this.closed) return sample;
587
+
588
+ if (this._injections.clientEvents) {
589
+ if (!sample.clientEvents) sample.clientEvents = [];
590
+ sample.clientEvents.push(...this._injections.clientEvents);
591
+
592
+ this._injections.clientEvents = undefined;
593
+ }
594
+
595
+ if (this._injections.clientIssues) {
596
+ if (!sample.clientIssues) sample.clientIssues = [];
597
+ sample.clientIssues.push(...this._injections.clientIssues);
598
+
599
+ this._injections.clientIssues = undefined;
600
+ }
601
+
602
+ if (this._injections.extensionStats) {
603
+ if (!sample.extensionStats) sample.extensionStats = [];
604
+ sample.extensionStats.push(...this._injections.extensionStats);
605
+
606
+ this._injections.extensionStats = undefined;
607
+ }
608
+
609
+ if (this._injections.attachments) {
610
+ if (!sample.attachments) sample.attachments = {};
611
+ Object.assign(sample.attachments, this._injections.attachments);
612
+
613
+ this._injections.attachments = undefined;
614
+ }
615
+
616
+ if (this._injections.clientMetaItems) {
617
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
618
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
619
+
620
+ this._injections.clientMetaItems = undefined;
621
+ }
622
+
623
+ return sample;
624
+ }
625
+
626
+ // public resetSummaryMetrics() {
627
+ // this.totalDataChannelBytesReceived = 0;
628
+ // this.totalDataChannelBytesSent = 0;
629
+ // this.totalDataChannelMessagesReceived = 0;
630
+ // this.totalDataChannelMessagesSent = 0;
631
+ // this.totalInboundPacketsLost = 0;
632
+ // this.totalInboundPacketsReceived = 0;
633
+ // this.totalOutboundPacketsSent = 0;
634
+ // this.totalReceivedAudioBytes = 0;
635
+ // this.totalReceivedVideoBytes = 0;
636
+ // this.totalSentAudioBytes = 0;
637
+ // this.totalSentVideoBytes = 0;
638
+ // this.totalSentBytes = 0;
639
+ // this.totalReceivedBytes = 0;
640
+
641
+ // this.totalNumberOfIssues = 0;
642
+
643
+ // this.totalScoreSum = 0;
644
+ // this.numberOfScoreMeasurements = 0;
645
+ // }
646
+
647
+ // public createSummary(): ObservedClientSummary {
648
+ // return {
649
+ // totalRttLt50Measurements: this.totalRttLt50Measurements,
650
+ // totalRttLt150Measurements: this.totalRttLt150Measurements,
651
+ // totalRttLt300Measurements: this.totalRttLt300Measurements,
652
+ // totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
653
+ // totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
654
+ // totalDataChannelBytesSent: this.totalDataChannelBytesSent,
655
+ // totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
656
+ // totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
657
+ // totalInboundPacketsLost: this.totalInboundPacketsLost,
658
+ // totalInboundPacketsReceived: this.totalInboundPacketsReceived,
659
+ // totalOutboundPacketsSent: this.totalOutboundPacketsSent,
660
+ // totalReceivedAudioBytes: this.totalReceivedAudioBytes,
661
+ // totalReceivedVideoBytes: this.totalReceivedVideoBytes,
662
+ // totalSentAudioBytes: this.totalSentAudioBytes,
663
+ // totalSentVideoBytes: this.totalSentVideoBytes,
664
+ // totalSentBytes: this.totalSentBytes,
665
+ // totalReceivedBytes: this.totalReceivedBytes,
666
+
667
+ // numberOfIssues: this.totalNumberOfIssues,
668
+
669
+ // totalScoreSum: this.totalScoreSum,
670
+ // numberOfScoreMeasurements: this.numberOfScoreMeasurements,
671
+ // };
672
+ // }
259
673
  }