@observertc/observer-js 1.0.1-c3edea3.0 → 1.0.1-ea70eab.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 (140) hide show
  1. package/lib/ObservedCall.d.ts +43 -7
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +129 -13
  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/ObservedCallSummary.d.ts +18 -0
  8. package/lib/ObservedCallSummary.d.ts.map +1 -0
  9. package/lib/ObservedClient.d.ts +70 -11
  10. package/lib/ObservedClient.d.ts.map +1 -1
  11. package/lib/ObservedClient.js +392 -72
  12. package/lib/ObservedClientEventMonitor.d.ts +89 -0
  13. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  14. package/lib/ObservedClientEventMonitor.js +247 -0
  15. package/lib/ObservedClientSummary.d.ts +23 -0
  16. package/lib/ObservedClientSummary.d.ts.map +1 -0
  17. package/lib/ObservedClientSummary.js +2 -0
  18. package/lib/ObservedDataChannel.d.ts +6 -0
  19. package/lib/ObservedDataChannel.d.ts.map +1 -1
  20. package/lib/ObservedDataChannel.js +30 -0
  21. package/lib/ObservedIceCandidatePair.d.ts +6 -1
  22. package/lib/ObservedIceCandidatePair.d.ts.map +1 -1
  23. package/lib/ObservedIceCandidatePair.js +45 -1
  24. package/lib/ObservedIceTransport.d.ts +4 -0
  25. package/lib/ObservedIceTransport.d.ts.map +1 -1
  26. package/lib/ObservedIceTransport.js +28 -0
  27. package/lib/ObservedInboundTrack.d.ts +2 -0
  28. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  29. package/lib/ObservedInboundTrack.js +2 -0
  30. package/lib/ObservedOutboundTrack.d.ts +2 -0
  31. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  32. package/lib/ObservedOutboundTrack.js +2 -0
  33. package/lib/ObservedPeerConnection.d.ts +29 -2
  34. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  35. package/lib/ObservedPeerConnection.js +181 -29
  36. package/lib/ObservedTURN.d.ts +31 -0
  37. package/lib/ObservedTURN.d.ts.map +1 -0
  38. package/lib/ObservedTURN.js +58 -0
  39. package/lib/ObservedTurnServer.d.ts +25 -0
  40. package/lib/ObservedTurnServer.d.ts.map +1 -0
  41. package/lib/ObservedTurnServer.js +59 -0
  42. package/lib/Observer.d.ts +43 -22
  43. package/lib/Observer.d.ts.map +1 -1
  44. package/lib/Observer.js +126 -40
  45. package/lib/ObserverEventMonitor.d.ts +130 -0
  46. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  47. package/lib/ObserverEventMonitor.js +404 -0
  48. package/lib/ObserverSummary.d.ts +13 -0
  49. package/lib/ObserverSummary.d.ts.map +1 -0
  50. package/lib/ObserverSummary.js +2 -0
  51. package/lib/common/utils.d.ts +2 -1
  52. package/lib/common/utils.d.ts.map +1 -1
  53. package/lib/common/utils.js +11 -3
  54. package/lib/index.d.ts +6 -0
  55. package/lib/index.d.ts.map +1 -1
  56. package/lib/index.js +9 -1
  57. package/lib/schema/ClientEventTypes.d.ts +160 -0
  58. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  59. package/lib/schema/ClientEventTypes.js +38 -0
  60. package/lib/schema/{metaTypes.d.ts → ClientMetaTypes.d.ts} +2 -2
  61. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  62. package/lib/schema/ClientMetaTypes.js +16 -0
  63. package/lib/schema/ClientSample.d.ts +1 -1
  64. package/lib/schema/ClientSample.d.ts.map +1 -1
  65. package/lib/updaters/CallUpdater.d.ts.map +1 -0
  66. package/lib/updaters/CallUpdater.js +2 -0
  67. package/lib/updaters/ObserverUpdater.d.ts +5 -0
  68. package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
  69. package/lib/updaters/ObserverUpdater.js +2 -0
  70. package/lib/updaters/OnAllCallObserverUpdater.d.ts +15 -0
  71. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  72. package/lib/updaters/OnAllCallObserverUpdater.js +50 -0
  73. package/lib/updaters/OnAllClientCallUpdater.d.ts +15 -0
  74. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
  75. package/lib/updaters/OnAllClientCallUpdater.js +53 -0
  76. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +12 -0
  77. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  78. package/lib/updaters/OnAnyCallObserverUpdater.js +37 -0
  79. package/lib/updaters/OnAnyClientCallUpdater.d.ts +12 -0
  80. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
  81. package/lib/updaters/OnAnyClientCallUpdater.js +36 -0
  82. package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
  83. package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
  84. package/lib/updaters/OnIntervalUpdater.js +17 -0
  85. package/lib/updaters/Updater.d.ts +6 -0
  86. package/lib/updaters/Updater.d.ts.map +1 -0
  87. package/lib/updaters/Updater.js +2 -0
  88. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  89. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  90. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  91. package/package.json +1 -1
  92. package/src/ObservedCall.ts +210 -16
  93. package/src/ObservedCallEventMonitor.ts +392 -0
  94. package/src/ObservedCallSummary.ts +22 -0
  95. package/src/ObservedClient.ts +490 -79
  96. package/src/ObservedClientEventMonitor.ts +301 -0
  97. package/src/ObservedClientSummary.ts +24 -0
  98. package/src/ObservedDataChannel.ts +32 -0
  99. package/src/ObservedIceCandidatePair.ts +44 -2
  100. package/src/ObservedIceTransport.ts +29 -0
  101. package/src/ObservedInboundTrack.ts +3 -0
  102. package/src/ObservedOutboundTrack.ts +3 -0
  103. package/src/ObservedPeerConnection.ts +210 -46
  104. package/src/ObservedTURN.ts +86 -0
  105. package/src/ObservedTurnServer.ts +72 -0
  106. package/src/Observer.ts +179 -64
  107. package/src/ObserverEventMonitor.ts +518 -0
  108. package/src/ObserverSummary.ts +15 -0
  109. package/src/common/utils.ts +12 -2
  110. package/src/index.ts +6 -0
  111. package/src/schema/ClientEventTypes.ts +176 -0
  112. package/src/schema/{metaTypes.ts → ClientMetaTypes.ts} +1 -1
  113. package/src/schema/ClientSample.ts +1 -1
  114. package/src/updaters/ObserverUpdater.ts +5 -0
  115. package/src/updaters/OnAllCallObserverUpdater.ts +59 -0
  116. package/src/updaters/OnAllClientCallUpdater.ts +61 -0
  117. package/src/updaters/OnAnyCallObserverUpdater.ts +41 -0
  118. package/src/updaters/OnAnyClientCallUpdater.ts +39 -0
  119. package/src/updaters/OnIntervalUpdater.ts +19 -0
  120. package/src/updaters/Updater.ts +5 -0
  121. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  122. package/tsconfig.json +1 -1
  123. package/lib/schema/eventTypes.d.ts +0 -33
  124. package/lib/schema/eventTypes.d.ts.map +0 -1
  125. package/lib/schema/eventTypes.js +0 -38
  126. package/lib/schema/metaTypes.d.ts.map +0 -1
  127. package/lib/schema/metaTypes.js +0 -16
  128. package/lib/utils/CallUpdater.d.ts.map +0 -1
  129. package/lib/utils/OnAllClientCallUpdater.d.ts +0 -16
  130. package/lib/utils/OnAllClientCallUpdater.d.ts.map +0 -1
  131. package/lib/utils/OnAllClientCallUpdater.js +0 -58
  132. package/lib/utils/onAnyClientCallUpdater.d.ts +0 -14
  133. package/lib/utils/onAnyClientCallUpdater.d.ts.map +0 -1
  134. package/lib/utils/onAnyClientCallUpdater.js +0 -51
  135. package/src/schema/eventTypes.ts +0 -36
  136. package/src/utils/OnAllClientCallUpdater.ts +0 -68
  137. package/src/utils/onAnyClientCallUpdater.ts +0 -59
  138. /package/lib/{utils/CallUpdater.js → ObservedCallSummary.js} +0 -0
  139. /package/lib/{utils → updaters}/CallUpdater.d.ts +0 -0
  140. /package/src/{utils → updaters}/CallUpdater.ts +0 -0
@@ -3,10 +3,10 @@ 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';
@@ -27,12 +27,15 @@ export type ObservedClientEvents = {
27
27
  ];
28
28
  close: [];
29
29
  joined: [];
30
+ issue: [ClientIssue];
31
+ metaData: [ClientMetaData];
30
32
  rejoined: [timestamp: number];
31
33
  left: [];
32
34
  usingturn: [boolean];
33
35
  usermediaerror: [string];
36
+ extensionStats: [ExtensionStat];
37
+ clientEvent: [ClientEvent];
34
38
 
35
- // do we need this?
36
39
  newpeerconnection: [ObservedPeerConnection];
37
40
  };
38
41
 
@@ -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,209 @@ 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);
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);
182
470
  }
183
471
  }
184
472
  break;
185
473
  }
186
474
  }
475
+ this.emit('clientEvent', event);
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);
193
492
  }
194
493
 
195
- public processMetadata(metadata: ClientMetaData) {
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
+
517
+ }
518
+
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);
553
+ this.emit('extensionStats', stats);
236
554
  }
237
555
 
238
- private _updatePeerConnection(sample: PeerConnectionSample) {
556
+ private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
239
557
  let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
240
558
 
241
559
  if (!observedPeerConnection) {
242
560
  if (!sample.peerConnectionId) {
243
- return logger.warn(
561
+ return (logger.warn(
244
562
  `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
245
563
  sample
246
- );
564
+ ), void 0);
247
565
  }
248
566
 
249
567
  observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
@@ -252,8 +570,101 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
252
570
  this.observedPeerConnections.delete(sample.peerConnectionId);
253
571
  });
254
572
  this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
573
+
574
+ this.emit('newpeerconnection', observedPeerConnection);
255
575
  }
256
576
 
257
577
  observedPeerConnection.accept(sample);
578
+
579
+ return observedPeerConnection;
258
580
  }
581
+
582
+ private _mergeInjections(sample: ClientSample): ClientSample {
583
+ if (this.closed) return sample;
584
+
585
+ if (this._injections.clientEvents) {
586
+ if (!sample.clientEvents) sample.clientEvents = [];
587
+ sample.clientEvents.push(...this._injections.clientEvents);
588
+
589
+ this._injections.clientEvents = undefined;
590
+ }
591
+
592
+ if (this._injections.clientIssues) {
593
+ if (!sample.clientIssues) sample.clientIssues = [];
594
+ sample.clientIssues.push(...this._injections.clientIssues);
595
+
596
+ this._injections.clientIssues = undefined;
597
+ }
598
+
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
+ }
605
+
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;
611
+ }
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;
618
+ }
619
+
620
+ return sample;
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
+ // }
259
670
  }