@observertc/observer-js 1.0.1-4daea92.0 → 1.0.1-5a1b94b.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.
- package/README.md +535 -1
- package/lib/ObservedCall.d.ts +18 -2
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +65 -11
- package/lib/ObservedCallEventMonitor.d.ts +14 -13
- package/lib/ObservedCallEventMonitor.d.ts.map +1 -1
- package/lib/ObservedCallEventMonitor.js +3 -1
- package/lib/ObservedClient.d.ts +9 -9
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +181 -15
- package/lib/ObservedClientEventMonitor.d.ts +54 -52
- package/lib/ObservedClientEventMonitor.d.ts.map +1 -1
- package/lib/ObservedClientEventMonitor.js +7 -0
- package/lib/ObservedTURN.d.ts +1 -1
- package/lib/ObservedTURN.d.ts.map +1 -1
- package/lib/ObservedTURN.js +1 -0
- package/lib/ObservedTurnServer.d.ts +2 -2
- package/lib/ObservedTurnServer.d.ts.map +1 -1
- package/lib/Observer.d.ts +6 -5
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +13 -20
- package/lib/ObserverEventMonitor.d.ts +45 -13
- package/lib/ObserverEventMonitor.d.ts.map +1 -1
- package/lib/ObserverEventMonitor.js +114 -1
- package/lib/common/ObserverSummary.d.ts.map +1 -0
- package/lib/common/types.d.ts +20 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/index.d.ts +17 -17
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +16 -16
- package/lib/mediasoup/ObservedMediasoupRouter.d.ts +7 -0
- package/lib/mediasoup/ObservedMediasoupRouter.d.ts.map +1 -0
- package/lib/mediasoup/ObservedMediasoupRouter.js +12 -0
- package/lib/schema/ClientEventTypes.d.ts +147 -79
- package/lib/schema/ClientEventTypes.d.ts.map +1 -1
- package/lib/schema/ClientEventTypes.js +5 -2
- package/lib/schema/ClientSample.d.ts +1 -1
- package/lib/schema/ClientSample.d.ts.map +1 -1
- package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -1
- package/lib/utils/MediasoupRemoteTrackResolver.d.ts +2 -2
- package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -1
- package/lib/utils/RemoteTrackResolver.d.ts +2 -2
- package/lib/utils/RemoteTrackResolver.d.ts.map +1 -1
- package/lib/{ObservedCertificate.d.ts → webrtc/ObservedCertificate.d.ts} +1 -1
- package/lib/webrtc/ObservedCertificate.d.ts.map +1 -0
- package/lib/{ObservedCodec.d.ts → webrtc/ObservedCodec.d.ts} +1 -1
- package/lib/webrtc/ObservedCodec.d.ts.map +1 -0
- package/lib/{ObservedDataChannel.d.ts → webrtc/ObservedDataChannel.d.ts} +1 -1
- package/lib/webrtc/ObservedDataChannel.d.ts.map +1 -0
- package/lib/{ObservedIceCandidate.d.ts → webrtc/ObservedIceCandidate.d.ts} +1 -1
- package/lib/webrtc/ObservedIceCandidate.d.ts.map +1 -0
- package/lib/{ObservedIceCandidatePair.d.ts → webrtc/ObservedIceCandidatePair.d.ts} +3 -2
- package/lib/webrtc/ObservedIceCandidatePair.d.ts.map +1 -0
- package/lib/{ObservedIceCandidatePair.js → webrtc/ObservedIceCandidatePair.js} +17 -1
- package/lib/{ObservedIceTransport.d.ts → webrtc/ObservedIceTransport.d.ts} +1 -1
- package/lib/webrtc/ObservedIceTransport.d.ts.map +1 -0
- package/lib/{ObservedInboundRtp.d.ts → webrtc/ObservedInboundRtp.d.ts} +2 -2
- package/lib/webrtc/ObservedInboundRtp.d.ts.map +1 -0
- package/lib/{ObservedInboundTrack.d.ts → webrtc/ObservedInboundTrack.d.ts} +5 -4
- package/lib/webrtc/ObservedInboundTrack.d.ts.map +1 -0
- package/lib/{ObservedInboundTrack.js → webrtc/ObservedInboundTrack.js} +2 -1
- package/lib/{ObservedMediaPlayout.d.ts → webrtc/ObservedMediaPlayout.d.ts} +2 -2
- package/lib/webrtc/ObservedMediaPlayout.d.ts.map +1 -0
- package/lib/{ObservedMediaSource.d.ts → webrtc/ObservedMediaSource.d.ts} +2 -2
- package/lib/webrtc/ObservedMediaSource.d.ts.map +1 -0
- package/lib/{ObservedOutboundRtp.d.ts → webrtc/ObservedOutboundRtp.d.ts} +2 -2
- package/lib/webrtc/ObservedOutboundRtp.d.ts.map +1 -0
- package/lib/{ObservedOutboundTrack.d.ts → webrtc/ObservedOutboundTrack.d.ts} +5 -4
- package/lib/webrtc/ObservedOutboundTrack.d.ts.map +1 -0
- package/lib/{ObservedOutboundTrack.js → webrtc/ObservedOutboundTrack.js} +2 -1
- package/lib/{ObservedPeerConnection.d.ts → webrtc/ObservedPeerConnection.d.ts} +26 -4
- package/lib/webrtc/ObservedPeerConnection.d.ts.map +1 -0
- package/lib/{ObservedPeerConnection.js → webrtc/ObservedPeerConnection.js} +80 -17
- package/lib/{ObservedPeerConnectionTransport.d.ts → webrtc/ObservedPeerConnectionTransport.d.ts} +1 -1
- package/lib/webrtc/ObservedPeerConnectionTransport.d.ts.map +1 -0
- package/lib/{ObservedRemoteInboundRtp.d.ts → webrtc/ObservedRemoteInboundRtp.d.ts} +2 -2
- package/lib/webrtc/ObservedRemoteInboundRtp.d.ts.map +1 -0
- package/lib/{ObservedRemoteOutboundRtp.d.ts → webrtc/ObservedRemoteOutboundRtp.d.ts} +2 -2
- package/lib/webrtc/ObservedRemoteOutboundRtp.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/ObservedCall.ts +70 -17
- package/src/ObservedCallEventMonitor.ts +17 -14
- package/src/ObservedClient.ts +195 -22
- package/src/ObservedClientEventMonitor.ts +59 -51
- package/src/ObservedTURN.ts +2 -1
- package/src/ObservedTurnServer.ts +2 -2
- package/src/Observer.ts +22 -27
- package/src/ObserverEventMonitor.ts +167 -14
- package/src/common/types.ts +20 -1
- package/src/index.ts +18 -19
- package/src/mediasoup/ObservedMediasoupRouter.ts +12 -0
- package/src/schema/ClientEventTypes.ts +187 -83
- package/src/schema/ClientSample.ts +1 -1
- package/src/updaters/OnAnyCallObserverUpdater.ts +1 -1
- package/src/utils/MediasoupRemoteTrackResolver.ts +2 -2
- package/src/utils/RemoteTrackResolver.ts +2 -2
- package/src/{ObservedCertificate.ts → webrtc/ObservedCertificate.ts} +1 -1
- package/src/{ObservedCodec.ts → webrtc/ObservedCodec.ts} +1 -1
- package/src/{ObservedDataChannel.ts → webrtc/ObservedDataChannel.ts} +1 -1
- package/src/{ObservedIceCandidate.ts → webrtc/ObservedIceCandidate.ts} +1 -1
- package/src/{ObservedIceCandidatePair.ts → webrtc/ObservedIceCandidatePair.ts} +19 -3
- package/src/{ObservedIceTransport.ts → webrtc/ObservedIceTransport.ts} +1 -1
- package/src/{ObservedInboundRtp.ts → webrtc/ObservedInboundRtp.ts} +2 -2
- package/src/{ObservedInboundTrack.ts → webrtc/ObservedInboundTrack.ts} +6 -4
- package/src/{ObservedMediaPlayout.ts → webrtc/ObservedMediaPlayout.ts} +2 -2
- package/src/{ObservedMediaSource.ts → webrtc/ObservedMediaSource.ts} +2 -2
- package/src/{ObservedOutboundRtp.ts → webrtc/ObservedOutboundRtp.ts} +2 -2
- package/src/{ObservedOutboundTrack.ts → webrtc/ObservedOutboundTrack.ts} +6 -4
- package/src/{ObservedPeerConnection.ts → webrtc/ObservedPeerConnection.ts} +122 -37
- package/src/{ObservedPeerConnectionTransport.ts → webrtc/ObservedPeerConnectionTransport.ts} +1 -1
- package/src/{ObservedRemoteInboundRtp.ts → webrtc/ObservedRemoteInboundRtp.ts} +2 -2
- package/src/{ObservedRemoteOutboundRtp.ts → webrtc/ObservedRemoteOutboundRtp.ts} +2 -2
- package/lib/ObservedCertificate.d.ts.map +0 -1
- package/lib/ObservedCodec.d.ts.map +0 -1
- package/lib/ObservedDataChannel.d.ts.map +0 -1
- package/lib/ObservedIceCandidate.d.ts.map +0 -1
- package/lib/ObservedIceCandidatePair.d.ts.map +0 -1
- package/lib/ObservedIceTransport.d.ts.map +0 -1
- package/lib/ObservedInboundRtp.d.ts.map +0 -1
- package/lib/ObservedInboundTrack.d.ts.map +0 -1
- package/lib/ObservedMediaPlayout.d.ts.map +0 -1
- package/lib/ObservedMediaSource.d.ts.map +0 -1
- package/lib/ObservedOutboundRtp.d.ts.map +0 -1
- package/lib/ObservedOutboundTrack.d.ts.map +0 -1
- package/lib/ObservedPeerConnection.d.ts.map +0 -1
- package/lib/ObservedPeerConnectionTransport.d.ts.map +0 -1
- package/lib/ObservedRemoteInboundRtp.d.ts.map +0 -1
- package/lib/ObservedRemoteOutboundRtp.d.ts.map +0 -1
- package/lib/ObserverSummary.d.ts.map +0 -1
- /package/lib/{ObserverSummary.d.ts → common/ObserverSummary.d.ts} +0 -0
- /package/lib/{ObserverSummary.js → common/ObserverSummary.js} +0 -0
- /package/lib/{ObservedCertificate.js → webrtc/ObservedCertificate.js} +0 -0
- /package/lib/{ObservedCodec.js → webrtc/ObservedCodec.js} +0 -0
- /package/lib/{ObservedDataChannel.js → webrtc/ObservedDataChannel.js} +0 -0
- /package/lib/{ObservedIceCandidate.js → webrtc/ObservedIceCandidate.js} +0 -0
- /package/lib/{ObservedIceTransport.js → webrtc/ObservedIceTransport.js} +0 -0
- /package/lib/{ObservedInboundRtp.js → webrtc/ObservedInboundRtp.js} +0 -0
- /package/lib/{ObservedMediaPlayout.js → webrtc/ObservedMediaPlayout.js} +0 -0
- /package/lib/{ObservedMediaSource.js → webrtc/ObservedMediaSource.js} +0 -0
- /package/lib/{ObservedOutboundRtp.js → webrtc/ObservedOutboundRtp.js} +0 -0
- /package/lib/{ObservedPeerConnectionTransport.js → webrtc/ObservedPeerConnectionTransport.js} +0 -0
- /package/lib/{ObservedRemoteInboundRtp.js → webrtc/ObservedRemoteInboundRtp.js} +0 -0
- /package/lib/{ObservedRemoteOutboundRtp.js → webrtc/ObservedRemoteOutboundRtp.js} +0 -0
- /package/src/{ObserverSummary.ts → common/ObserverSummary.ts} +0 -0
package/src/ObservedCall.ts
CHANGED
|
@@ -11,6 +11,9 @@ import { Updater } from './updaters/Updater';
|
|
|
11
11
|
import { OnIntervalUpdater } from './updaters/OnIntervalUpdater';
|
|
12
12
|
import { OnAnyClientCallUpdater } from './updaters/OnAnyClientCallUpdater';
|
|
13
13
|
import { ObservedCallEventMonitor } from './ObservedCallEventMonitor';
|
|
14
|
+
import { createLogger } from './common/logger';
|
|
15
|
+
|
|
16
|
+
const logger = createLogger('ObservedCall');
|
|
14
17
|
|
|
15
18
|
export type ObservedCallSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
16
19
|
callId: string;
|
|
@@ -18,6 +21,7 @@ export type ObservedCallSettings<AppData extends Record<string, unknown> = Recor
|
|
|
18
21
|
remoteTrackResolvePolicy?: 'p2p' | 'mediasoup-sfu',
|
|
19
22
|
updatePolicy?: 'update-on-any-client-updated' | 'update-when-all-client-updated' | 'update-on-interval',
|
|
20
23
|
updateIntervalInMs?: number,
|
|
24
|
+
maxIdleIfEmptyMs?: number,
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
export type ObservedCallEvents = {
|
|
@@ -75,6 +79,20 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
75
79
|
public maxNumberOfClients = 0;
|
|
76
80
|
public deltaNumberOfIssues = 0;
|
|
77
81
|
|
|
82
|
+
public deltaDataChannelBytesReceived = 0;
|
|
83
|
+
public deltaDataChannelBytesSent = 0;
|
|
84
|
+
public deltaDataChannelMessagesReceived = 0;
|
|
85
|
+
public deltaDataChannelMessagesSent = 0;
|
|
86
|
+
public deltaInboundPacketsLost = 0;
|
|
87
|
+
public deltaInboundPacketsReceived = 0;
|
|
88
|
+
public deltaOutboundPacketsSent = 0;
|
|
89
|
+
public deltaReceivedAudioBytes = 0;
|
|
90
|
+
public deltaReceivedVideoBytes = 0;
|
|
91
|
+
public deltaSentAudioBytes = 0;
|
|
92
|
+
public deltaSentVideoBytes = 0;
|
|
93
|
+
public deltaTransportReceivedBytes = 0;
|
|
94
|
+
public deltaTransportSentBytes = 0;
|
|
95
|
+
|
|
78
96
|
public deltaRttLt50Measurements = 0;
|
|
79
97
|
public deltaRttLt150Measurements = 0;
|
|
80
98
|
public deltaRttLt300Measurements = 0;
|
|
@@ -85,14 +103,10 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
85
103
|
public startedAt?: number;
|
|
86
104
|
public endedAt?: number;
|
|
87
105
|
public closedAt?: number;
|
|
106
|
+
public lastClientTimestamp = 0;
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
timer?: ReturnType<typeof setTimeout>,
|
|
92
|
-
};
|
|
93
|
-
private _callEndedEvent: {
|
|
94
|
-
emitted: boolean
|
|
95
|
-
};
|
|
108
|
+
public maxIdleIfEmptyMs?: number;
|
|
109
|
+
private _autoCloseTimer?: ReturnType<typeof setTimeout>;
|
|
96
110
|
|
|
97
111
|
public constructor(
|
|
98
112
|
settings: ObservedCallSettings<AppData>,
|
|
@@ -104,6 +118,7 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
104
118
|
this.callId = settings.callId;
|
|
105
119
|
this.appData = settings.appData ?? {} as AppData;
|
|
106
120
|
this.scoreCalculator = new DefaultCallScoreCalculator(this);
|
|
121
|
+
this.maxIdleIfEmptyMs = settings.maxIdleIfEmptyMs;
|
|
107
122
|
this.detectors = new Detectors();
|
|
108
123
|
|
|
109
124
|
if (settings.updateIntervalInMs) {
|
|
@@ -113,10 +128,10 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
113
128
|
}
|
|
114
129
|
switch (settings.updatePolicy) {
|
|
115
130
|
case 'update-on-any-client-updated':
|
|
116
|
-
this.updater = new
|
|
131
|
+
this.updater = new OnAnyClientCallUpdater(this);
|
|
117
132
|
break;
|
|
118
133
|
case 'update-when-all-client-updated':
|
|
119
|
-
this.updater = new
|
|
134
|
+
this.updater = new OnAllClientCallUpdater(this);
|
|
120
135
|
break;
|
|
121
136
|
case 'update-on-interval':
|
|
122
137
|
if (!settings.updateIntervalInMs) {
|
|
@@ -133,13 +148,6 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
133
148
|
case 'mediasoup-sfu':
|
|
134
149
|
break;
|
|
135
150
|
}
|
|
136
|
-
|
|
137
|
-
this._callStartedEvent = {
|
|
138
|
-
emitted: false,
|
|
139
|
-
};
|
|
140
|
-
this._callEndedEvent = {
|
|
141
|
-
emitted: false,
|
|
142
|
-
};
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
public get numberOfClients() {
|
|
@@ -157,7 +165,18 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
157
165
|
|
|
158
166
|
this.updater?.close();
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
let minSampleTimestamps: number | undefined;
|
|
169
|
+
let maxSampleTimestamps: number | undefined;
|
|
170
|
+
|
|
171
|
+
for (const client of this.observedClients.values()) {
|
|
172
|
+
client.close();
|
|
173
|
+
|
|
174
|
+
if (client.joinedAt) minSampleTimestamps = Math.min(minSampleTimestamps ?? client.joinedAt, client.joinedAt);
|
|
175
|
+
if (client.leftAt) maxSampleTimestamps = Math.max(maxSampleTimestamps ?? client.leftAt, client.leftAt);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (this.startedAt === undefined) this.startedAt = minSampleTimestamps;
|
|
179
|
+
if (this.endedAt === undefined) this.endedAt = maxSampleTimestamps;
|
|
161
180
|
|
|
162
181
|
this.closedAt = Date.now();
|
|
163
182
|
this.emit('close');
|
|
@@ -187,6 +206,8 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
187
206
|
|
|
188
207
|
if (this.observedClients.size === 0) {
|
|
189
208
|
this.emit('empty');
|
|
209
|
+
|
|
210
|
+
this._createAutoClose();
|
|
190
211
|
}
|
|
191
212
|
++this.totalRemovedClients;
|
|
192
213
|
});
|
|
@@ -202,6 +223,9 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
202
223
|
|
|
203
224
|
if (wasEmpty) {
|
|
204
225
|
this.emit('not-empty');
|
|
226
|
+
|
|
227
|
+
clearTimeout(this._autoCloseTimer);
|
|
228
|
+
this._autoCloseTimer = undefined;
|
|
205
229
|
}
|
|
206
230
|
|
|
207
231
|
return result;
|
|
@@ -229,6 +253,8 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
229
253
|
this.detectors.update();
|
|
230
254
|
this.scoreCalculator.update();
|
|
231
255
|
|
|
256
|
+
this.lastClientTimestamp = Math.max(...Array.from(this.observedClients.values()).map((c) => c.lastSampleTimestamp ?? 0));
|
|
257
|
+
|
|
232
258
|
this.emit('update');
|
|
233
259
|
|
|
234
260
|
this.deltaNumberOfIssues = 0;
|
|
@@ -236,6 +262,20 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
236
262
|
this.deltaRttLt150Measurements = 0;
|
|
237
263
|
this.deltaRttLt300Measurements = 0;
|
|
238
264
|
this.deltaRttGtOrEq300Measurements = 0;
|
|
265
|
+
|
|
266
|
+
this.deltaDataChannelBytesReceived = 0;
|
|
267
|
+
this.deltaDataChannelBytesSent = 0;
|
|
268
|
+
this.deltaDataChannelMessagesReceived = 0;
|
|
269
|
+
this.deltaDataChannelMessagesSent = 0;
|
|
270
|
+
this.deltaInboundPacketsLost = 0;
|
|
271
|
+
this.deltaInboundPacketsReceived = 0;
|
|
272
|
+
this.deltaOutboundPacketsSent = 0;
|
|
273
|
+
this.deltaReceivedAudioBytes = 0;
|
|
274
|
+
this.deltaReceivedVideoBytes = 0;
|
|
275
|
+
this.deltaSentAudioBytes = 0;
|
|
276
|
+
this.deltaSentVideoBytes = 0;
|
|
277
|
+
this.deltaTransportReceivedBytes = 0;
|
|
278
|
+
this.deltaTransportSentBytes = 0;
|
|
239
279
|
}
|
|
240
280
|
|
|
241
281
|
private _onClientUpdate(client: ObservedClient) {
|
|
@@ -278,6 +318,19 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
278
318
|
this.endedAt = Math.max(this.endedAt ?? client.leftAt, client.leftAt);
|
|
279
319
|
}
|
|
280
320
|
|
|
321
|
+
private _createAutoClose() {
|
|
322
|
+
if (this.closed) return;
|
|
323
|
+
if (this.maxIdleIfEmptyMs === undefined || this.maxIdleIfEmptyMs < 1) return;
|
|
324
|
+
|
|
325
|
+
this._autoCloseTimer = setTimeout(() => {
|
|
326
|
+
if (this.closed) return;
|
|
327
|
+
if (this.observedClients.size > 0) return;
|
|
328
|
+
|
|
329
|
+
logger.debug(`Call ${this.callId} is empty for ${this.maxIdleIfEmptyMs}ms, closing...`);
|
|
330
|
+
this.close();
|
|
331
|
+
}, this.maxIdleIfEmptyMs);
|
|
332
|
+
}
|
|
333
|
+
|
|
281
334
|
// public resetSummaryMetrics() {
|
|
282
335
|
// this.totalAddedClients = 0;
|
|
283
336
|
// this.totalRemovedClients = 0;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { ObservedCall } from './ObservedCall';
|
|
2
|
-
import { ObservedCertificate } from './ObservedCertificate';
|
|
2
|
+
import { ObservedCertificate } from './webrtc/ObservedCertificate';
|
|
3
3
|
import { ObservedClient, ObservedClientEvents } from './ObservedClient';
|
|
4
|
-
import { ObservedCodec } from './ObservedCodec';
|
|
5
|
-
import { ObservedDataChannel } from './ObservedDataChannel';
|
|
6
|
-
import { ObservedIceCandidate } from './ObservedIceCandidate';
|
|
7
|
-
import { ObservedIceCandidatePair } from './ObservedIceCandidatePair';
|
|
8
|
-
import { ObservedIceTransport } from './ObservedIceTransport';
|
|
9
|
-
import { ObservedInboundRtp } from './ObservedInboundRtp';
|
|
10
|
-
import { ObservedInboundTrack } from './ObservedInboundTrack';
|
|
11
|
-
import { ObservedMediaPlayout } from './ObservedMediaPlayout';
|
|
12
|
-
import { ObservedMediaSource } from './ObservedMediaSource';
|
|
13
|
-
import { ObservedOutboundRtp } from './ObservedOutboundRtp';
|
|
14
|
-
import { ObservedOutboundTrack } from './ObservedOutboundTrack';
|
|
15
|
-
import { ObservedPeerConnection } from './ObservedPeerConnection';
|
|
4
|
+
import { ObservedCodec } from './webrtc/ObservedCodec';
|
|
5
|
+
import { ObservedDataChannel } from './webrtc/ObservedDataChannel';
|
|
6
|
+
import { ObservedIceCandidate } from './webrtc/ObservedIceCandidate';
|
|
7
|
+
import { ObservedIceCandidatePair } from './webrtc/ObservedIceCandidatePair';
|
|
8
|
+
import { ObservedIceTransport } from './webrtc/ObservedIceTransport';
|
|
9
|
+
import { ObservedInboundRtp } from './webrtc/ObservedInboundRtp';
|
|
10
|
+
import { ObservedInboundTrack } from './webrtc/ObservedInboundTrack';
|
|
11
|
+
import { ObservedMediaPlayout } from './webrtc/ObservedMediaPlayout';
|
|
12
|
+
import { ObservedMediaSource } from './webrtc/ObservedMediaSource';
|
|
13
|
+
import { ObservedOutboundRtp } from './webrtc/ObservedOutboundRtp';
|
|
14
|
+
import { ObservedOutboundTrack } from './webrtc/ObservedOutboundTrack';
|
|
15
|
+
import { ObservedPeerConnection } from './webrtc/ObservedPeerConnection';
|
|
16
16
|
import { ClientEvent, ClientIssue, ClientMetaData, ClientSample, ExtensionStat } from './schema/ClientSample';
|
|
17
17
|
|
|
18
18
|
export class ObservedCallEventMonitor<Context> {
|
|
@@ -61,6 +61,7 @@ export class ObservedCallEventMonitor<Context> {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// Public event handlers
|
|
64
|
+
public onCallClosed?: (call: ObservedCall, ctx: Context) => void;
|
|
64
65
|
public onCallEmpty?: (call: ObservedCall, ctx: Context) => void;
|
|
65
66
|
public onCallNotEmpty?: (call: ObservedCall, ctx: Context) => void;
|
|
66
67
|
public onCallUpdated?: (call: ObservedCall, ctx: Context) => void;
|
|
@@ -132,6 +133,8 @@ export class ObservedCallEventMonitor<Context> {
|
|
|
132
133
|
call.off('not-empty', onCallNotEmpty);
|
|
133
134
|
call.off('update', onCallUpdated);
|
|
134
135
|
|
|
136
|
+
this.onCallClosed?.(call, this.context);
|
|
137
|
+
|
|
135
138
|
});
|
|
136
139
|
call.on('newclient', this._onClientAdded);
|
|
137
140
|
call.on('empty', onCallEmpty);
|
|
@@ -149,7 +152,7 @@ export class ObservedCallEventMonitor<Context> {
|
|
|
149
152
|
const onClientExtensionStats = (extensionStats: ExtensionStat) => this._onClientExtensionStats(observedClient, extensionStats);
|
|
150
153
|
const onUsingTurn = (usingTurn: boolean) => this._onUsingTurn(observedClient, usingTurn);
|
|
151
154
|
const onUserMediaError = (error: string) => this._onUserMediaError(observedClient, error);
|
|
152
|
-
const onClientUpdated = (...args: ObservedClientEvents['update']) => this.onClientUpdated?.(observedClient, args[0]
|
|
155
|
+
const onClientUpdated = (...args: ObservedClientEvents['update']) => this.onClientUpdated?.(observedClient, args[0], this.context);
|
|
153
156
|
const onClientEvent = (event: ClientEvent) => this.onClientEvent?.(observedClient, event, this.context);
|
|
154
157
|
|
|
155
158
|
observedClient.once('close', () => {
|
package/src/ObservedClient.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { ObservedPeerConnection } from './ObservedPeerConnection';
|
|
2
|
+
import { ObservedPeerConnection } from './webrtc/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';
|
|
@@ -10,21 +10,19 @@ 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 { clear } from 'console';
|
|
14
|
+
import { ParsedUserAgent } from './common/types';
|
|
13
15
|
|
|
14
16
|
const logger = createLogger('ObservedClient');
|
|
15
17
|
|
|
16
18
|
export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
17
19
|
clientId: string;
|
|
20
|
+
maxIdleTimeMs?: number;
|
|
18
21
|
appData?: AppData;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
export type ObservedClientEvents = {
|
|
22
|
-
update: [
|
|
23
|
-
{
|
|
24
|
-
sample: ClientSample;
|
|
25
|
-
elapsedTimeInMs: number;
|
|
26
|
-
}
|
|
27
|
-
];
|
|
25
|
+
update: [sample: ClientSample, elapsedTimeInMs: number];
|
|
28
26
|
close: [];
|
|
29
27
|
joined: [];
|
|
30
28
|
issue: [ClientIssue];
|
|
@@ -49,14 +47,12 @@ export declare interface ObservedClient {
|
|
|
49
47
|
export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
50
48
|
public readonly detectors: Detectors;
|
|
51
49
|
|
|
52
|
-
public readonly clientId: string;
|
|
53
50
|
public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
|
|
54
51
|
public readonly calculatedScore: CalculatedScore = {
|
|
55
52
|
weight: 1,
|
|
56
53
|
value: undefined,
|
|
57
54
|
};
|
|
58
55
|
|
|
59
|
-
public appData: AppData;
|
|
60
56
|
public attachments?: Record<string, unknown>;
|
|
61
57
|
|
|
62
58
|
public updated = Date.now();
|
|
@@ -66,6 +62,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
66
62
|
public joinedAt?: number;
|
|
67
63
|
public leftAt?: number;
|
|
68
64
|
public closedAt?: number;
|
|
65
|
+
public lastSampleTimestamp?: number;
|
|
69
66
|
// the timestamp of the CLIENT_JOINED event
|
|
70
67
|
public operationSystem?: MetaData.OperationSystem;
|
|
71
68
|
public engine?: MetaData.Engine;
|
|
@@ -139,16 +136,26 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
139
136
|
public issues: ClientIssue[] = [];
|
|
140
137
|
|
|
141
138
|
private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
|
|
139
|
+
private _autoCloseTimer?: ReturnType<typeof setTimeout>;
|
|
142
140
|
|
|
143
|
-
public constructor(
|
|
141
|
+
public constructor(
|
|
142
|
+
public readonly settings: ObservedClientSettings<AppData>,
|
|
143
|
+
public readonly call: ObservedCall
|
|
144
|
+
) {
|
|
144
145
|
super();
|
|
145
146
|
this.setMaxListeners(Infinity);
|
|
147
|
+
this._createAutoClose();
|
|
146
148
|
|
|
147
|
-
this.clientId = settings.clientId;
|
|
148
|
-
this.appData = settings.appData ?? {} as AppData;
|
|
149
|
-
|
|
150
149
|
this.detectors = new Detectors();
|
|
151
150
|
}
|
|
151
|
+
|
|
152
|
+
public get clientId() {
|
|
153
|
+
return this.settings.clientId;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public get appData(): AppData {
|
|
157
|
+
return this.settings.appData ?? {} as AppData;
|
|
158
|
+
}
|
|
152
159
|
|
|
153
160
|
public get numberOfPeerConnections() {
|
|
154
161
|
return this.observedPeerConnections.size;
|
|
@@ -162,15 +169,42 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
162
169
|
if (this.closed) return;
|
|
163
170
|
this.closed = true;
|
|
164
171
|
|
|
172
|
+
this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
|
|
173
|
+
this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
|
|
174
|
+
this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
|
|
175
|
+
this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
|
|
176
|
+
|
|
165
177
|
Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
178
|
+
if (!this.leftAt) {
|
|
179
|
+
this.leftAt = this.lastSampleTimestamp;
|
|
166
180
|
|
|
181
|
+
if (this.leftAt) {
|
|
182
|
+
this.emit('left');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
167
185
|
this.closedAt = Date.now();
|
|
168
186
|
|
|
169
187
|
this.emit('close');
|
|
170
188
|
}
|
|
171
189
|
|
|
190
|
+
private _createAutoClose() {
|
|
191
|
+
if (this.closed) return;
|
|
192
|
+
if (this.settings.maxIdleTimeMs === undefined || this.settings.maxIdleTimeMs < 1) return;
|
|
193
|
+
|
|
194
|
+
clearTimeout(this._autoCloseTimer);
|
|
195
|
+
|
|
196
|
+
this._autoCloseTimer = setTimeout(() => {
|
|
197
|
+
if (this.closed) return;
|
|
198
|
+
|
|
199
|
+
logger.debug(`Client ${this.clientId} is idle for ${this.settings.maxIdleTimeMs}ms, closing...`);
|
|
200
|
+
this.close();
|
|
201
|
+
}, this.settings.maxIdleTimeMs);
|
|
202
|
+
}
|
|
203
|
+
|
|
172
204
|
public accept(sample: ClientSample): void {
|
|
173
205
|
if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
|
|
206
|
+
|
|
207
|
+
this._createAutoClose();
|
|
174
208
|
|
|
175
209
|
const now = Date.now();
|
|
176
210
|
const elapsedInMs = now - this.updated;
|
|
@@ -327,11 +361,12 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
327
361
|
this.calculatedScore.value = sample.score;
|
|
328
362
|
this.detectors.update();
|
|
329
363
|
|
|
364
|
+
this.lastSampleTimestamp = sample.timestamp;
|
|
330
365
|
// emit update
|
|
331
|
-
this.emit('update',
|
|
366
|
+
this.emit('update',
|
|
332
367
|
sample,
|
|
333
|
-
|
|
334
|
-
|
|
368
|
+
now - this.updated,
|
|
369
|
+
);
|
|
335
370
|
this.updated = now;
|
|
336
371
|
|
|
337
372
|
// if result changed after update
|
|
@@ -357,6 +392,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
357
392
|
logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
|
|
358
393
|
}
|
|
359
394
|
}
|
|
395
|
+
logger.debug('Client %s joined at %o', this.clientId, event);
|
|
360
396
|
break;
|
|
361
397
|
}
|
|
362
398
|
case ClientEventTypes.CLIENT_LEFT: {
|
|
@@ -367,7 +403,9 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
367
403
|
} else {
|
|
368
404
|
logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
|
|
369
405
|
}
|
|
406
|
+
|
|
370
407
|
}
|
|
408
|
+
logger.debug('Client %s left at %o', this.clientId, event);
|
|
371
409
|
break;
|
|
372
410
|
}
|
|
373
411
|
case ClientEventTypes.PEER_CONNECTION_OPENED: {
|
|
@@ -381,7 +419,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
381
419
|
} else if (postBuffer) {
|
|
382
420
|
postBuffer.push(event);
|
|
383
421
|
} else {
|
|
384
|
-
logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection', event);
|
|
422
|
+
logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o', event);
|
|
385
423
|
}
|
|
386
424
|
|
|
387
425
|
}
|
|
@@ -398,7 +436,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
398
436
|
} else if (postBuffer) {
|
|
399
437
|
postBuffer.push(event);
|
|
400
438
|
} else {
|
|
401
|
-
logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection', event);
|
|
439
|
+
logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o', event);
|
|
402
440
|
}
|
|
403
441
|
}
|
|
404
442
|
break;
|
|
@@ -415,7 +453,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
415
453
|
} else if (postBuffer) {
|
|
416
454
|
postBuffer.push(event);
|
|
417
455
|
} else {
|
|
418
|
-
logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack', event);
|
|
456
|
+
logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
419
457
|
}
|
|
420
458
|
}
|
|
421
459
|
break;
|
|
@@ -432,7 +470,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
432
470
|
} else if (postBuffer) {
|
|
433
471
|
postBuffer.push(event);
|
|
434
472
|
} else {
|
|
435
|
-
logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack', event);
|
|
473
|
+
logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
436
474
|
}
|
|
437
475
|
}
|
|
438
476
|
break;
|
|
@@ -449,7 +487,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
449
487
|
} else if (postBuffer) {
|
|
450
488
|
postBuffer.push(event);
|
|
451
489
|
} else {
|
|
452
|
-
logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel', event);
|
|
490
|
+
logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
|
|
453
491
|
}
|
|
454
492
|
}
|
|
455
493
|
break;
|
|
@@ -466,12 +504,119 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
466
504
|
} else if (postBuffer) {
|
|
467
505
|
postBuffer.push(event);
|
|
468
506
|
} else {
|
|
469
|
-
logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel', event);
|
|
507
|
+
logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
case ClientEventTypes.MEDIA_TRACK_MUTED: {
|
|
513
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
514
|
+
|
|
515
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
516
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
517
|
+
const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
|
|
518
|
+
const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
519
|
+
|
|
520
|
+
if (observedPeerConnection) {
|
|
521
|
+
if (observedInboundTrack) {
|
|
522
|
+
observedInboundTrack.muted = true;
|
|
523
|
+
observedPeerConnection?.emit('muted-inbound-track', observedInboundTrack);
|
|
524
|
+
} else if (observedOutboundTrack) {
|
|
525
|
+
observedOutboundTrack.muted = true;
|
|
526
|
+
observedPeerConnection?.emit('muted-outbound-track', observedOutboundTrack);
|
|
527
|
+
}
|
|
528
|
+
} else if (postBuffer) {
|
|
529
|
+
postBuffer.push(event);
|
|
530
|
+
} else {
|
|
531
|
+
logger.warn('Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
case ClientEventTypes.MEDIA_TRACK_UNMUTED: {
|
|
538
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
539
|
+
|
|
540
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
|
|
541
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
542
|
+
const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
|
|
543
|
+
const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
|
|
544
|
+
|
|
545
|
+
if (observedPeerConnection) {
|
|
546
|
+
if (observedInboundTrack) {
|
|
547
|
+
observedInboundTrack.muted = false;
|
|
548
|
+
observedPeerConnection?.emit('unmuted-inbound-track', observedInboundTrack);
|
|
549
|
+
} else if (observedOutboundTrack) {
|
|
550
|
+
observedOutboundTrack.muted = false;
|
|
551
|
+
observedPeerConnection?.emit('unmuted-outbound-track', observedOutboundTrack);
|
|
552
|
+
}
|
|
553
|
+
} else if (postBuffer) {
|
|
554
|
+
postBuffer.push(event);
|
|
555
|
+
} else {
|
|
556
|
+
logger.warn('Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
case ClientEventTypes.ICE_CONNECTION_STATE_CHANGED: {
|
|
562
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
563
|
+
|
|
564
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceConnectionState && typeof payload.iceConnectionState === 'string') {
|
|
565
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
566
|
+
|
|
567
|
+
if (observedPeerConnection) {
|
|
568
|
+
observedPeerConnection.iceConnectionState = payload.iceConnectionState;
|
|
569
|
+
observedPeerConnection.emit('iceconnectionstatechange', {
|
|
570
|
+
state: payload.iceConnectionState,
|
|
571
|
+
});
|
|
572
|
+
} else if (postBuffer) {
|
|
573
|
+
postBuffer.push(event);
|
|
574
|
+
} else {
|
|
575
|
+
logger.warn('Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
case ClientEventTypes.ICE_GATHERING_STATE_CHANGED: {
|
|
581
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
582
|
+
|
|
583
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceGatheringState && typeof payload.iceGatheringState === 'string') {
|
|
584
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
585
|
+
|
|
586
|
+
if (observedPeerConnection) {
|
|
587
|
+
observedPeerConnection.iceGatheringState = payload.iceGatheringState;
|
|
588
|
+
observedPeerConnection.emit('icegatheringstatechange', {
|
|
589
|
+
state: payload.iceGatheringState,
|
|
590
|
+
});
|
|
591
|
+
} else if (postBuffer) {
|
|
592
|
+
postBuffer.push(event);
|
|
593
|
+
} else {
|
|
594
|
+
logger.warn('Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case ClientEventTypes.PEER_CONNECTION_STATE_CHANGED: {
|
|
600
|
+
const payload = parseJsonAs<Record<string, unknown>>(event.payload);
|
|
601
|
+
|
|
602
|
+
if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.peerConnectionState && typeof payload.peerConnectionState === 'string') {
|
|
603
|
+
const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
|
|
604
|
+
|
|
605
|
+
if (observedPeerConnection) {
|
|
606
|
+
observedPeerConnection.connectionState = payload.peerConnectionState;
|
|
607
|
+
observedPeerConnection.emit('connectionstatechange', {
|
|
608
|
+
state: payload.peerConnectionState,
|
|
609
|
+
});
|
|
610
|
+
} else if (postBuffer) {
|
|
611
|
+
postBuffer.push(event);
|
|
612
|
+
} else {
|
|
613
|
+
logger.warn('Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
|
|
470
614
|
}
|
|
471
615
|
}
|
|
472
616
|
break;
|
|
473
617
|
}
|
|
474
618
|
}
|
|
619
|
+
|
|
475
620
|
this.emit('clientEvent', event);
|
|
476
621
|
}
|
|
477
622
|
|
|
@@ -536,6 +681,34 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
536
681
|
this.operationSystem = parseJsonAs(metadata.payload);
|
|
537
682
|
break;
|
|
538
683
|
}
|
|
684
|
+
case 'USER_AGENT_DATA': {
|
|
685
|
+
try {
|
|
686
|
+
const userAgentData = parseJsonAs<ParsedUserAgent>(metadata.payload);
|
|
687
|
+
if (userAgentData) {
|
|
688
|
+
if (this.operationSystem === undefined) {
|
|
689
|
+
this.operationSystem = {
|
|
690
|
+
name: userAgentData.os.name.toLowerCase(),
|
|
691
|
+
version: userAgentData.os.version,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (this.engine === undefined) {
|
|
695
|
+
this.engine = {
|
|
696
|
+
name: userAgentData.engine.name.toLowerCase(),
|
|
697
|
+
version: userAgentData.engine.version,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (this.browser === undefined) {
|
|
701
|
+
this.browser = {
|
|
702
|
+
name: userAgentData.browser.name.toLowerCase(),
|
|
703
|
+
version: userAgentData.browser.version,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
} catch (error) {
|
|
709
|
+
logger.warn('Failed to parse USER_AGENT_DATA metadata: %o', error);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
539
712
|
}
|
|
540
713
|
|
|
541
714
|
this.call.observer.emit('client-metadata', this, metadata);
|