@observertc/observer-js 0.40.29 → 0.40.31
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/lib/ObservedCall.d.ts +9 -0
- package/lib/ObservedCall.d.ts.map +1 -1
- package/lib/ObservedCall.js +20 -2
- package/lib/ObservedClient.d.ts +21 -2
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +25 -6
- package/lib/common/Middleware.d.ts +8 -0
- package/lib/common/Middleware.d.ts.map +1 -0
- package/lib/common/Middleware.js +41 -0
- package/lib/monitors/ClientIssueMonitor.d.ts +1 -4
- package/lib/monitors/ClientIssueMonitor.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ObservedCall.ts +49 -4
- package/src/ObservedClient.ts +48 -6
- package/src/common/Middleware.ts +56 -0
- package/src/monitors/ClientIssueMonitor.ts +1 -2
package/lib/ObservedCall.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ import { ObservedOutboundTrack } from './ObservedOutboundTrack';
|
|
|
5
5
|
import { Observer } from './Observer';
|
|
6
6
|
import { PartialBy } from './common/utils';
|
|
7
7
|
import { CallEventReport } from '@observertc/report-schemas-js';
|
|
8
|
+
import { ClientSample } from '@observertc/sample-schemas-js';
|
|
9
|
+
import { ClientIssue } from './monitors/CallSummary';
|
|
10
|
+
type ClientIssueDetectionConfig = Pick<ClientIssue, 'severity'>;
|
|
8
11
|
export type ObservedCallModel = {
|
|
9
12
|
serviceId: string;
|
|
10
13
|
roomId: string;
|
|
@@ -13,6 +16,7 @@ export type ObservedCallModel = {
|
|
|
13
16
|
export type ObservedCallEvents = {
|
|
14
17
|
update: [];
|
|
15
18
|
newclient: [ObservedClient];
|
|
19
|
+
callevent: [Omit<CallEventReport, 'callId' | 'serviceId' | 'roomId'>];
|
|
16
20
|
close: [];
|
|
17
21
|
};
|
|
18
22
|
export declare interface ObservedCall {
|
|
@@ -26,6 +30,7 @@ export declare class ObservedCall<AppData extends Record<string, unknown> = Reco
|
|
|
26
30
|
readonly observer: Observer;
|
|
27
31
|
readonly appData: AppData;
|
|
28
32
|
readonly created: number;
|
|
33
|
+
readonly processor: import("./common/Middleware").Processor<ClientSample>;
|
|
29
34
|
private readonly _clients;
|
|
30
35
|
readonly sfuStreamIdToOutboundAudioTrack: Map<string | number, ObservedOutboundTrack<"audio">>;
|
|
31
36
|
readonly sfuStreamIdToOutboundVideoTrack: Map<string | number, ObservedOutboundTrack<"video">>;
|
|
@@ -47,6 +52,10 @@ export declare class ObservedCall<AppData extends Record<string, unknown> = Reco
|
|
|
47
52
|
appData: ClientAppData;
|
|
48
53
|
generateClientJoinedReport?: boolean;
|
|
49
54
|
joined?: number;
|
|
55
|
+
detectIssues?: {
|
|
56
|
+
rejoin: ClientIssue['severity'] | ClientIssueDetectionConfig;
|
|
57
|
+
};
|
|
50
58
|
}): ObservedClient<ClientAppData>;
|
|
51
59
|
}
|
|
60
|
+
export {};
|
|
52
61
|
//# sourceMappingURL=ObservedCall.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedCall.d.ts","sourceRoot":"","sources":["../src/ObservedCall.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"ObservedCall.d.ts","sourceRoot":"","sources":["../src/ObservedCall.ts"],"names":[],"mappings":"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"}
|
package/lib/ObservedCall.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.ObservedCall = void 0;
|
|
|
15
15
|
const events_1 = require("events");
|
|
16
16
|
const ObservedClient_1 = require("./ObservedClient");
|
|
17
17
|
const callEventReports_1 = require("./common/callEventReports");
|
|
18
|
+
const Middleware_1 = require("./common/Middleware");
|
|
18
19
|
class ObservedCall extends events_1.EventEmitter {
|
|
19
20
|
constructor(_model, observer, appData) {
|
|
20
21
|
super();
|
|
@@ -22,6 +23,7 @@ class ObservedCall extends events_1.EventEmitter {
|
|
|
22
23
|
this.observer = observer;
|
|
23
24
|
this.appData = appData;
|
|
24
25
|
this.created = Date.now();
|
|
26
|
+
this.processor = (0, Middleware_1.createProcessor)();
|
|
25
27
|
this._clients = new Map();
|
|
26
28
|
this.sfuStreamIdToOutboundAudioTrack = new Map();
|
|
27
29
|
this.sfuStreamIdToOutboundVideoTrack = new Map();
|
|
@@ -71,11 +73,14 @@ class ObservedCall extends events_1.EventEmitter {
|
|
|
71
73
|
createClient(config) {
|
|
72
74
|
if (this._closed)
|
|
73
75
|
throw new Error(`Call ${this.callId} is closed`);
|
|
74
|
-
const { appData, generateClientJoinedReport, joined = Date.now()
|
|
76
|
+
const { appData, generateClientJoinedReport, joined = Date.now(), detectIssues = {
|
|
77
|
+
rejoin: 'minor'
|
|
78
|
+
} } = config, model = __rest(config, ["appData", "generateClientJoinedReport", "joined", "detectIssues"]);
|
|
75
79
|
const result = new ObservedClient_1.ObservedClient(model, this, appData);
|
|
76
|
-
const onUpdate = () => {
|
|
80
|
+
const onUpdate = ({ sample }) => {
|
|
77
81
|
this._updated = Date.now();
|
|
78
82
|
this.emit('update');
|
|
83
|
+
this.processor.process(sample);
|
|
79
84
|
};
|
|
80
85
|
result.once('close', () => {
|
|
81
86
|
result.off('update', onUpdate);
|
|
@@ -86,6 +91,19 @@ class ObservedCall extends events_1.EventEmitter {
|
|
|
86
91
|
if (generateClientJoinedReport) {
|
|
87
92
|
this.reports.addCallEventReport((0, callEventReports_1.createClientJoinedEventReport)(this.serviceId, result.mediaUnitId, this.roomId, this.callId, result.clientId, joined, result.userId, result.marker));
|
|
88
93
|
}
|
|
94
|
+
if (detectIssues === null || detectIssues === void 0 ? void 0 : detectIssues.rejoin) {
|
|
95
|
+
const issueBaseConfig = typeof detectIssues.rejoin === 'object' ? detectIssues.rejoin : {
|
|
96
|
+
severity: detectIssues.rejoin
|
|
97
|
+
};
|
|
98
|
+
const rejoinedClientIssueListener = (event) => {
|
|
99
|
+
event.lastJoined;
|
|
100
|
+
result.addIssue(Object.assign({ timestamp: Date.now() }, issueBaseConfig));
|
|
101
|
+
};
|
|
102
|
+
result.once('close', () => {
|
|
103
|
+
result.off('rejoined', rejoinedClientIssueListener);
|
|
104
|
+
});
|
|
105
|
+
result.on('rejoined', rejoinedClientIssueListener);
|
|
106
|
+
}
|
|
89
107
|
this.emit('newclient', result);
|
|
90
108
|
return result;
|
|
91
109
|
}
|
package/lib/ObservedClient.d.ts
CHANGED
|
@@ -16,10 +16,15 @@ export type ObservedClientModel = {
|
|
|
16
16
|
engine?: Engine;
|
|
17
17
|
platform?: Platform;
|
|
18
18
|
browser?: Browser;
|
|
19
|
+
coordinates?: {
|
|
20
|
+
latitude: number;
|
|
21
|
+
longitude: number;
|
|
22
|
+
};
|
|
19
23
|
};
|
|
20
24
|
export type ObservedClientEvents = {
|
|
21
25
|
update: [
|
|
22
26
|
{
|
|
27
|
+
sample: ClientSample;
|
|
23
28
|
elapsedTimeInMs: number;
|
|
24
29
|
}
|
|
25
30
|
];
|
|
@@ -53,6 +58,11 @@ export type ObservedClientEvents = {
|
|
|
53
58
|
issue: [ClientIssue];
|
|
54
59
|
usingturn: [boolean];
|
|
55
60
|
usermediaerror: [string];
|
|
61
|
+
rejoined: [
|
|
62
|
+
{
|
|
63
|
+
lastJoined: number;
|
|
64
|
+
}
|
|
65
|
+
];
|
|
56
66
|
};
|
|
57
67
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
58
68
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
@@ -70,7 +80,8 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
70
80
|
sfuId?: string;
|
|
71
81
|
private readonly _peerConnections;
|
|
72
82
|
private _closed;
|
|
73
|
-
private
|
|
83
|
+
private _joinedEventAt?;
|
|
84
|
+
private _acceptedSamples;
|
|
74
85
|
private _timeZoneOffsetInHours?;
|
|
75
86
|
private _left?;
|
|
76
87
|
usingTURN: boolean;
|
|
@@ -110,6 +121,14 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
110
121
|
constructor(_model: ObservedClientModel, call: ObservedCall, appData: AppData);
|
|
111
122
|
getSfu<T extends Record<string, unknown> = Record<string, unknown>>(): ObservedSfu<T> | undefined;
|
|
112
123
|
get sfu(): ObservedSfu | undefined;
|
|
124
|
+
get coordinates(): {
|
|
125
|
+
latitude: number;
|
|
126
|
+
longitude: number;
|
|
127
|
+
} | undefined;
|
|
128
|
+
set coordinates(value: {
|
|
129
|
+
latitude: number;
|
|
130
|
+
longitude: number;
|
|
131
|
+
} | undefined);
|
|
113
132
|
get clientId(): string;
|
|
114
133
|
get roomId(): string;
|
|
115
134
|
get serviceId(): string;
|
|
@@ -124,7 +143,7 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
124
143
|
get engine(): Engine | undefined;
|
|
125
144
|
get operationSystem(): OperationSystem | undefined;
|
|
126
145
|
get platform(): Platform | undefined;
|
|
127
|
-
get
|
|
146
|
+
get acceptedSamples(): number;
|
|
128
147
|
get updated(): number;
|
|
129
148
|
get closed(): boolean;
|
|
130
149
|
get peerConnections(): ReadonlyMap<string, ObservedPeerConnection>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAChJ,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,OAAO,EAAE,SAAS,EAAe,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIhE,MAAM,MAAM,mBAAmB,GAAE;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":"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"}
|
package/lib/ObservedClient.js
CHANGED
|
@@ -31,7 +31,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
31
31
|
this._updated = Date.now();
|
|
32
32
|
this._peerConnections = new Map();
|
|
33
33
|
this._closed = false;
|
|
34
|
-
this.
|
|
34
|
+
this._acceptedSamples = 0;
|
|
35
35
|
this.usingTURN = false;
|
|
36
36
|
this.availableOutgoingBitrate = 0;
|
|
37
37
|
this.availableIncomingBitrate = 0;
|
|
@@ -75,6 +75,12 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
75
75
|
return;
|
|
76
76
|
return this.call.observer.observedSfus.get(this.sfuId);
|
|
77
77
|
}
|
|
78
|
+
get coordinates() {
|
|
79
|
+
return this._model.coordinates;
|
|
80
|
+
}
|
|
81
|
+
set coordinates(value) {
|
|
82
|
+
this._model.coordinates = value;
|
|
83
|
+
}
|
|
78
84
|
get clientId() {
|
|
79
85
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
80
86
|
return this._model.clientId;
|
|
@@ -122,8 +128,8 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
122
128
|
get platform() {
|
|
123
129
|
return this._model.platform;
|
|
124
130
|
}
|
|
125
|
-
get
|
|
126
|
-
return this.
|
|
131
|
+
get acceptedSamples() {
|
|
132
|
+
return this._acceptedSamples;
|
|
127
133
|
}
|
|
128
134
|
get updated() {
|
|
129
135
|
return this._updated;
|
|
@@ -196,7 +202,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
196
202
|
throw new Error(`Sample client id (${sample.clientId}) does not match the client id of the observed client (${this.clientId})`);
|
|
197
203
|
}
|
|
198
204
|
const now = Date.now();
|
|
199
|
-
++this.
|
|
205
|
+
++this._acceptedSamples;
|
|
200
206
|
for (const peerConnection of this._peerConnections.values()) {
|
|
201
207
|
if (peerConnection.closed)
|
|
202
208
|
continue;
|
|
@@ -277,7 +283,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
277
283
|
callId: this.callId,
|
|
278
284
|
clientId: this.clientId,
|
|
279
285
|
userId: this.userId,
|
|
280
|
-
timestamp:
|
|
286
|
+
timestamp: now,
|
|
281
287
|
payload: extensionStats.payload,
|
|
282
288
|
extensionType: extensionStats.type,
|
|
283
289
|
});
|
|
@@ -285,6 +291,14 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
285
291
|
for (let _21 of (_p = sample.customCallEvents) !== null && _p !== void 0 ? _p : []) {
|
|
286
292
|
const { timestamp } = _21, callEvent = __rest(_21, ["timestamp"]);
|
|
287
293
|
switch (callEvent.name) {
|
|
294
|
+
case CallEventType_1.CallEventType.CLIENT_JOINED: {
|
|
295
|
+
const lastJoined = this._joinedEventAt;
|
|
296
|
+
this._joinedEventAt = timestamp !== null && timestamp !== void 0 ? timestamp : sample.timestamp;
|
|
297
|
+
if (lastJoined && this._joinedEventAt && lastJoined !== this._joinedEventAt) {
|
|
298
|
+
this.emit('rejoined', { lastJoined });
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
288
302
|
case CallEventType_1.CallEventType.CLIENT_LEFT: {
|
|
289
303
|
this._left = timestamp;
|
|
290
304
|
break;
|
|
@@ -306,9 +320,11 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
306
320
|
catch (err) {
|
|
307
321
|
logger.warn(`Error parsing client issue: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
308
322
|
}
|
|
323
|
+
break;
|
|
309
324
|
}
|
|
310
325
|
}
|
|
311
|
-
this.reports.addCallEventReport(Object.assign({ serviceId: this.serviceId, mediaUnitId: this.mediaUnitId, roomId: this.roomId, callId: this.callId, clientId: this.clientId, userId: this.userId, timestamp: timestamp !== null && timestamp !== void 0 ? timestamp :
|
|
326
|
+
this.reports.addCallEventReport(Object.assign({ serviceId: this.serviceId, mediaUnitId: this.mediaUnitId, roomId: this.roomId, callId: this.callId, clientId: this.clientId, userId: this.userId, timestamp: timestamp !== null && timestamp !== void 0 ? timestamp : now }, callEvent));
|
|
327
|
+
this.call.emit('callevent', Object.assign({ mediaUnitId: this.mediaUnitId, clientId: this.clientId, userId: this.userId, timestamp: timestamp !== null && timestamp !== void 0 ? timestamp : now }, callEvent));
|
|
312
328
|
}
|
|
313
329
|
for (const userMediaError of (_q = sample.userMediaErrors) !== null && _q !== void 0 ? _q : []) {
|
|
314
330
|
const callMetaReport = (0, callMetaReports_1.createCallMetaReport)(this.serviceId, this.mediaUnitId, this.roomId, this.callId, this.clientId, {
|
|
@@ -600,8 +616,11 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
600
616
|
this.inboundAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
601
617
|
this.inboundVideoBitrate = (this.deltaReceivedVideoBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
|
|
602
618
|
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
619
|
+
// to make sure when sample is emitted it can be associated to this client
|
|
620
|
+
sample.clientId = this.clientId;
|
|
603
621
|
this._updated = now;
|
|
604
622
|
this.emit('update', {
|
|
623
|
+
sample,
|
|
605
624
|
elapsedTimeInMs,
|
|
606
625
|
});
|
|
607
626
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Middleware<T> = (input: T, next: (nextInput: T) => void) => void;
|
|
2
|
+
export interface Processor<T> {
|
|
3
|
+
process(value: T): void;
|
|
4
|
+
addMiddleware(...middlewares: Middleware<T>[]): Processor<T>;
|
|
5
|
+
removeMiddleware(...middlewares: Middleware<T>[]): Processor<T>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createProcessor<T>(): Processor<T>;
|
|
8
|
+
//# sourceMappingURL=Middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Middleware.d.ts","sourceRoot":"","sources":["../../src/common/Middleware.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAC3B,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,KACxB,IAAI,CAAC;AAEV,MAAM,WAAW,SAAS,CAAC,CAAC;IAC3B,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,GAAG,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7D,gBAAgB,CAAC,GAAG,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;CAChE;AAED,wBAAgB,eAAe,CAAC,CAAC,KAC9B,SAAS,CAAC,CAAC,CAAC,CA2Cd"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createProcessor = void 0;
|
|
4
|
+
function createProcessor() {
|
|
5
|
+
const stack = [];
|
|
6
|
+
const result = {
|
|
7
|
+
addMiddleware: (...middlewares) => {
|
|
8
|
+
if (middlewares && 0 < middlewares.length) {
|
|
9
|
+
stack.push(...middlewares);
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
},
|
|
13
|
+
removeMiddleware: (...middlewares) => {
|
|
14
|
+
if (middlewares && 0 < middlewares.length) {
|
|
15
|
+
for (const process of middlewares) {
|
|
16
|
+
const index = stack.indexOf(process);
|
|
17
|
+
if (0 < index)
|
|
18
|
+
stack.splice(index, 1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
},
|
|
23
|
+
process: (value) => {
|
|
24
|
+
let prevIndex = -1;
|
|
25
|
+
const execute = (index, input) => {
|
|
26
|
+
if (index <= prevIndex) {
|
|
27
|
+
throw new Error('middleware must call next() only once!');
|
|
28
|
+
}
|
|
29
|
+
prevIndex = index;
|
|
30
|
+
const middleware = stack[index];
|
|
31
|
+
if (!middleware)
|
|
32
|
+
return;
|
|
33
|
+
const next = (nextInput) => execute(index + 1, nextInput);
|
|
34
|
+
return middleware(input, next);
|
|
35
|
+
};
|
|
36
|
+
return execute(0, value);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
exports.createProcessor = createProcessor;
|
|
@@ -13,10 +13,7 @@ export type ClientIssueMonitorEvents = {
|
|
|
13
13
|
minor: [ClientIssueMonitorEmittedIssueEvent];
|
|
14
14
|
critical: [ClientIssueMonitorEmittedIssueEvent];
|
|
15
15
|
};
|
|
16
|
-
export type ClientIssueMonitorConfig = {
|
|
17
|
-
detectUserMediaClientIssues?: boolean;
|
|
18
|
-
detectMediaTrackQualityLimitationClientIssues?: boolean;
|
|
19
|
-
};
|
|
16
|
+
export type ClientIssueMonitorConfig = {};
|
|
20
17
|
export declare interface ClientIssueMonitor {
|
|
21
18
|
on<U extends keyof ClientIssueMonitorEvents>(event: U, listener: (...args: ClientIssueMonitorEvents[U]) => void): this;
|
|
22
19
|
off<U extends keyof ClientIssueMonitorEvents>(event: U, listener: (...args: ClientIssueMonitorEvents[U]) => void): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientIssueMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/ClientIssueMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,mCAAmC,GAAG,WAAW,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE,EAAE,CAAC;IACV,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,QAAQ,EAAE,CAAC,mCAAmC,CAAC,CAAC;CAEhD,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG
|
|
1
|
+
{"version":3,"file":"ClientIssueMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/ClientIssueMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,mCAAmC,GAAG,WAAW,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE,EAAE,CAAC;IACV,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,mCAAmC,CAAC,CAAC;IAC7C,QAAQ,EAAE,CAAC,mCAAmC,CAAC,CAAC;CAEhD,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,EAEtC,CAAA;AAED,MAAM,CAAC,OAAO,WAAW,kBAAkB;IAC1C,EAAE,CAAC,CAAC,SAAS,MAAM,wBAAwB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACvH,GAAG,CAAC,CAAC,SAAS,MAAM,wBAAwB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACxH,IAAI,CAAC,CAAC,SAAS,MAAM,wBAAwB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACzH,IAAI,CAAC,CAAC,SAAS,MAAM,wBAAwB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CACxG;AAED,qBAAa,kBAAmB,SAAQ,YAAY;aAGlC,MAAM,EAAE,wBAAwB;IAFjD,OAAO,CAAC,OAAO,CAAS;gBAEP,MAAM,GAAE,wBAA6B;IAM/C,OAAO,CAAC,IAAI,EAAE,YAAY;IAajC,OAAO,CAAC,UAAU;IAyBlB,IAAW,MAAM,YAEhB;IAEM,KAAK;CAMZ"}
|
package/package.json
CHANGED
package/src/ObservedCall.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { ObservedClient, ObservedClientModel } from './ObservedClient';
|
|
2
|
+
import { ObservedClient, ObservedClientEvents, ObservedClientModel } from './ObservedClient';
|
|
3
3
|
import { ObservedOutboundTrack } from './ObservedOutboundTrack';
|
|
4
4
|
import { Observer } from './Observer';
|
|
5
5
|
import { createClientJoinedEventReport } from './common/callEventReports';
|
|
6
6
|
import { PartialBy } from './common/utils';
|
|
7
7
|
import { CallEventReport } from '@observertc/report-schemas-js';
|
|
8
|
+
import { createProcessor } from './common/Middleware';
|
|
9
|
+
import { ClientSample } from '@observertc/sample-schemas-js';
|
|
10
|
+
import { ClientIssue } from './monitors/CallSummary';
|
|
11
|
+
|
|
12
|
+
type ClientIssueDetectionConfig = Pick<ClientIssue, 'severity'>;
|
|
8
13
|
|
|
9
14
|
export type ObservedCallModel = {
|
|
10
15
|
serviceId: string;
|
|
@@ -15,6 +20,7 @@ export type ObservedCallModel = {
|
|
|
15
20
|
export type ObservedCallEvents = {
|
|
16
21
|
update: [],
|
|
17
22
|
newclient: [ObservedClient],
|
|
23
|
+
callevent: [Omit<CallEventReport, 'callId' | 'serviceId' | 'roomId'>],
|
|
18
24
|
close: [],
|
|
19
25
|
}
|
|
20
26
|
|
|
@@ -28,6 +34,8 @@ export declare interface ObservedCall {
|
|
|
28
34
|
export class ObservedCall<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
29
35
|
public readonly created = Date.now();
|
|
30
36
|
|
|
37
|
+
public readonly processor = createProcessor<ClientSample>();
|
|
38
|
+
|
|
31
39
|
private readonly _clients = new Map<string, ObservedClient>();
|
|
32
40
|
|
|
33
41
|
public readonly sfuStreamIdToOutboundAudioTrack = new Map<string | number, ObservedOutboundTrack<'audio'>>();
|
|
@@ -100,21 +108,39 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
public createClient<ClientAppData extends Record<string, unknown> = Record<string, unknown>>(config: ObservedClientModel & {
|
|
111
|
+
public createClient<ClientAppData extends Record<string, unknown> = Record<string, unknown>>(config: ObservedClientModel & {
|
|
112
|
+
appData: ClientAppData,
|
|
113
|
+
generateClientJoinedReport?: boolean,
|
|
114
|
+
joined?: number,
|
|
115
|
+
detectIssues?: {
|
|
116
|
+
rejoin: ClientIssue['severity'] | ClientIssueDetectionConfig,
|
|
117
|
+
},
|
|
118
|
+
}) {
|
|
104
119
|
if (this._closed) throw new Error(`Call ${this.callId} is closed`);
|
|
105
120
|
|
|
106
|
-
const {
|
|
121
|
+
const {
|
|
122
|
+
appData,
|
|
123
|
+
generateClientJoinedReport,
|
|
124
|
+
joined = Date.now(),
|
|
125
|
+
detectIssues = {
|
|
126
|
+
rejoin: 'minor'
|
|
127
|
+
},
|
|
128
|
+
...model
|
|
129
|
+
} = config;
|
|
107
130
|
|
|
108
131
|
const result = new ObservedClient(model, this, appData);
|
|
109
|
-
const onUpdate = () => {
|
|
132
|
+
const onUpdate = ({ sample }: { sample: ClientSample }) => {
|
|
110
133
|
this._updated = Date.now();
|
|
111
134
|
this.emit('update');
|
|
135
|
+
|
|
136
|
+
this.processor.process(sample);
|
|
112
137
|
};
|
|
113
138
|
|
|
114
139
|
result.once('close', () => {
|
|
115
140
|
result.off('update', onUpdate);
|
|
116
141
|
this._clients.delete(result.clientId);
|
|
117
142
|
});
|
|
143
|
+
|
|
118
144
|
result.on('update', onUpdate);
|
|
119
145
|
this._clients.set(result.clientId, result);
|
|
120
146
|
|
|
@@ -131,6 +157,25 @@ export class ObservedCall<AppData extends Record<string, unknown> = Record<strin
|
|
|
131
157
|
));
|
|
132
158
|
}
|
|
133
159
|
|
|
160
|
+
if (detectIssues?.rejoin) {
|
|
161
|
+
const issueBaseConfig = typeof detectIssues.rejoin === 'object' ? detectIssues.rejoin : {
|
|
162
|
+
severity: detectIssues.rejoin
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const rejoinedClientIssueListener = (event: ObservedClientEvents['rejoined'][0]) => {
|
|
166
|
+
event.lastJoined;
|
|
167
|
+
result.addIssue({
|
|
168
|
+
timestamp: Date.now(),
|
|
169
|
+
...issueBaseConfig,
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
result.once('close', () => {
|
|
174
|
+
result.off('rejoined', rejoinedClientIssueListener);
|
|
175
|
+
});
|
|
176
|
+
result.on('rejoined', rejoinedClientIssueListener);
|
|
177
|
+
}
|
|
178
|
+
|
|
134
179
|
this.emit('newclient', result);
|
|
135
180
|
|
|
136
181
|
return result;
|
package/src/ObservedClient.ts
CHANGED
|
@@ -23,10 +23,15 @@ export type ObservedClientModel= {
|
|
|
23
23
|
engine?: Engine,
|
|
24
24
|
platform?: Platform,
|
|
25
25
|
browser?: Browser,
|
|
26
|
+
coordinates?: {
|
|
27
|
+
latitude: number;
|
|
28
|
+
longitude: number;
|
|
29
|
+
},
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
export type ObservedClientEvents = {
|
|
29
33
|
update: [{
|
|
34
|
+
sample: ClientSample,
|
|
30
35
|
elapsedTimeInMs: number,
|
|
31
36
|
}],
|
|
32
37
|
close: [],
|
|
@@ -51,6 +56,9 @@ export type ObservedClientEvents = {
|
|
|
51
56
|
issue: [ClientIssue],
|
|
52
57
|
usingturn: [boolean],
|
|
53
58
|
usermediaerror: [string],
|
|
59
|
+
rejoined: [{
|
|
60
|
+
lastJoined: number,
|
|
61
|
+
}],
|
|
54
62
|
};
|
|
55
63
|
|
|
56
64
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
@@ -71,7 +79,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
71
79
|
|
|
72
80
|
private _closed = false;
|
|
73
81
|
|
|
74
|
-
private
|
|
82
|
+
private _joinedEventAt?: number;
|
|
83
|
+
private _acceptedSamples = 0;
|
|
75
84
|
private _timeZoneOffsetInHours?: number;
|
|
76
85
|
private _left?: number;
|
|
77
86
|
|
|
@@ -137,6 +146,14 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
137
146
|
return this.call.observer.observedSfus.get(this.sfuId);
|
|
138
147
|
}
|
|
139
148
|
|
|
149
|
+
public get coordinates() {
|
|
150
|
+
return this._model.coordinates;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public set coordinates(value: { latitude: number; longitude: number } | undefined) {
|
|
154
|
+
this._model.coordinates = value;
|
|
155
|
+
}
|
|
156
|
+
|
|
140
157
|
public get clientId(): string {
|
|
141
158
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
142
159
|
return this._model.clientId!;
|
|
@@ -198,8 +215,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
198
215
|
return this._model.platform;
|
|
199
216
|
}
|
|
200
217
|
|
|
201
|
-
public get
|
|
202
|
-
return this.
|
|
218
|
+
public get acceptedSamples() {
|
|
219
|
+
return this._acceptedSamples;
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
public get updated() {
|
|
@@ -296,7 +313,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
296
313
|
}
|
|
297
314
|
const now = Date.now();
|
|
298
315
|
|
|
299
|
-
++this.
|
|
316
|
+
++this._acceptedSamples;
|
|
300
317
|
|
|
301
318
|
for (const peerConnection of this._peerConnections.values()) {
|
|
302
319
|
if (peerConnection.closed) continue;
|
|
@@ -464,7 +481,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
464
481
|
callId: this.callId,
|
|
465
482
|
clientId: this.clientId,
|
|
466
483
|
userId: this.userId,
|
|
467
|
-
timestamp:
|
|
484
|
+
timestamp: now,
|
|
468
485
|
payload: extensionStats.payload,
|
|
469
486
|
extensionType: extensionStats.type,
|
|
470
487
|
});
|
|
@@ -472,6 +489,16 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
472
489
|
|
|
473
490
|
for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
|
|
474
491
|
switch (callEvent.name) {
|
|
492
|
+
case CallEventType.CLIENT_JOINED: {
|
|
493
|
+
const lastJoined = this._joinedEventAt;
|
|
494
|
+
|
|
495
|
+
this._joinedEventAt = timestamp ?? sample.timestamp;
|
|
496
|
+
|
|
497
|
+
if (lastJoined && this._joinedEventAt && lastJoined !== this._joinedEventAt) {
|
|
498
|
+
this.emit('rejoined', { lastJoined });
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
475
502
|
case CallEventType.CLIENT_LEFT: {
|
|
476
503
|
this._left = timestamp;
|
|
477
504
|
break;
|
|
@@ -494,8 +521,10 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
494
521
|
} catch (err) {
|
|
495
522
|
logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
|
|
496
523
|
}
|
|
524
|
+
break;
|
|
497
525
|
}
|
|
498
526
|
}
|
|
527
|
+
|
|
499
528
|
this.reports.addCallEventReport({
|
|
500
529
|
serviceId: this.serviceId,
|
|
501
530
|
mediaUnitId: this.mediaUnitId,
|
|
@@ -503,9 +532,18 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
503
532
|
callId: this.callId,
|
|
504
533
|
clientId: this.clientId,
|
|
505
534
|
userId: this.userId,
|
|
506
|
-
timestamp: timestamp ??
|
|
535
|
+
timestamp: timestamp ?? now,
|
|
536
|
+
...callEvent,
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
this.call.emit('callevent', {
|
|
540
|
+
mediaUnitId: this.mediaUnitId,
|
|
541
|
+
clientId: this.clientId,
|
|
542
|
+
userId: this.userId,
|
|
543
|
+
timestamp: timestamp ?? now,
|
|
507
544
|
...callEvent,
|
|
508
545
|
});
|
|
546
|
+
|
|
509
547
|
}
|
|
510
548
|
|
|
511
549
|
for (const userMediaError of sample.userMediaErrors ?? []) {
|
|
@@ -903,8 +941,12 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
903
941
|
|
|
904
942
|
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
905
943
|
|
|
944
|
+
// to make sure when sample is emitted it can be associated to this client
|
|
945
|
+
sample.clientId = this.clientId;
|
|
946
|
+
|
|
906
947
|
this._updated = now;
|
|
907
948
|
this.emit('update', {
|
|
949
|
+
sample,
|
|
908
950
|
elapsedTimeInMs,
|
|
909
951
|
});
|
|
910
952
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type Middleware<T> = (
|
|
2
|
+
input: T,
|
|
3
|
+
next: (nextInput: T) => void,
|
|
4
|
+
) => void;
|
|
5
|
+
|
|
6
|
+
export interface Processor<T> {
|
|
7
|
+
process(value: T): void;
|
|
8
|
+
addMiddleware(...middlewares: Middleware<T>[]): Processor<T>;
|
|
9
|
+
removeMiddleware(...middlewares: Middleware<T>[]): Processor<T>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createProcessor<T>(
|
|
13
|
+
): Processor<T> {
|
|
14
|
+
const stack: Middleware<T>[] = [];
|
|
15
|
+
const result: Processor<T> = {
|
|
16
|
+
addMiddleware: (...middlewares: Middleware<T>[]) => {
|
|
17
|
+
if (middlewares && 0 < middlewares.length) {
|
|
18
|
+
stack.push(...middlewares);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
removeMiddleware: (...middlewares: Middleware<T>[]) => {
|
|
25
|
+
if (middlewares && 0 < middlewares.length) {
|
|
26
|
+
for (const process of middlewares) {
|
|
27
|
+
const index = stack.indexOf(process);
|
|
28
|
+
|
|
29
|
+
if (0 < index) stack.splice(index, 1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
},
|
|
35
|
+
process: (value: T) => {
|
|
36
|
+
let prevIndex = -1;
|
|
37
|
+
const execute = (index: number, input: T): void => {
|
|
38
|
+
if (index <= prevIndex) {
|
|
39
|
+
throw new Error('middleware must call next() only once!');
|
|
40
|
+
}
|
|
41
|
+
prevIndex = index;
|
|
42
|
+
const middleware = stack[index];
|
|
43
|
+
|
|
44
|
+
if (!middleware) return;
|
|
45
|
+
|
|
46
|
+
const next = (nextInput: T) => execute(index + 1, nextInput);
|
|
47
|
+
|
|
48
|
+
return middleware(input, next);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return execute(0, value);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
@@ -18,8 +18,7 @@ export type ClientIssueMonitorEvents = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type ClientIssueMonitorConfig = {
|
|
21
|
-
|
|
22
|
-
detectMediaTrackQualityLimitationClientIssues?: boolean;
|
|
21
|
+
// empty
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export declare interface ClientIssueMonitor {
|