@observertc/observer-js 0.40.11-beta → 0.40.13
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 +52 -24
- package/lib/ObservedClient.d.ts +10 -0
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +41 -0
- package/lib/ObservedOutboundTrack.d.ts +1 -0
- package/lib/ObservedOutboundTrack.d.ts.map +1 -1
- package/lib/ObservedOutboundTrack.js +7 -0
- package/lib/Observer.d.ts +4 -2
- package/lib/Observer.d.ts.map +1 -1
- package/lib/Observer.js +4 -4
- package/lib/common/CallEventType.d.ts +2 -1
- package/lib/common/CallEventType.d.ts.map +1 -1
- package/lib/common/CallEventType.js +2 -1
- package/lib/monitors/CallSummary.d.ts +5 -0
- package/lib/monitors/CallSummary.d.ts.map +1 -1
- package/lib/monitors/CallSummaryMonitor.d.ts +8 -1
- package/lib/monitors/CallSummaryMonitor.d.ts.map +1 -1
- package/lib/monitors/CallSummaryMonitor.js +60 -2
- package/lib/monitors/TurnUsageMonitor.d.ts +2 -0
- package/lib/monitors/TurnUsageMonitor.d.ts.map +1 -1
- package/lib/monitors/TurnUsageMonitor.js +9 -0
- package/package.json +1 -1
- package/src/ObservedClient.ts +53 -0
- package/src/ObservedOutboundTrack.ts +9 -0
- package/src/Observer.ts +13 -7
- package/src/common/CallEventType.ts +1 -1
- package/src/monitors/CallSummary.ts +6 -0
- package/src/monitors/CallSummaryMonitor.ts +81 -5
- package/src/monitors/TurnUsageMonitor.ts +13 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Server side component for monitoring WebRTC
|
|
1
|
+
Server side component for monitoring WebRTC applications and services
|
|
2
2
|
---
|
|
3
3
|
|
|
4
4
|
Table of Contents:
|
|
@@ -6,9 +6,7 @@ Table of Contents:
|
|
|
6
6
|
* [Quick Start](#quick-start)
|
|
7
7
|
* [Configurations](#configurations)
|
|
8
8
|
* [NPM package](#npm-package)
|
|
9
|
-
* [API docs](#api-docs)
|
|
10
9
|
* [Schemas](#schemas)
|
|
11
|
-
* [Getting Involved](#getting-involved)
|
|
12
10
|
* [License](#license)
|
|
13
11
|
|
|
14
12
|
## Qucik Start
|
|
@@ -25,29 +23,23 @@ Use it in your server side NodeJS app.
|
|
|
25
23
|
import { createObserver, ClientSample } from "@observertc/observer-js";
|
|
26
24
|
|
|
27
25
|
const observer = createObserver({
|
|
28
|
-
|
|
26
|
+
defaultServiceId: 'my-service-name',
|
|
27
|
+
defaultMediaUnitId: 'my-reporting-component',
|
|
29
28
|
});
|
|
30
29
|
|
|
31
|
-
const
|
|
30
|
+
const observedCall = observer.createObservedCall({
|
|
32
31
|
roomId: 'roomId',
|
|
33
32
|
callId: 'room-session-id',
|
|
34
|
-
clientId: 'reporting-client-id',
|
|
35
33
|
});
|
|
36
34
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
observer.addEvaluator(async context => {
|
|
42
|
-
const { endedCalls } = context;
|
|
43
|
-
|
|
44
|
-
// Observe call durations
|
|
45
|
-
for (const endedCall of endedCalls) {
|
|
46
|
-
const elapsedTimeInMins = (endedCall.ended - Number(endedCall.started)) / (60 * 1000);
|
|
47
|
-
console.log(`Call ${endedCall.callId} duration was ${elapsedTimeInMins} minutes`);
|
|
48
|
-
}
|
|
35
|
+
const observedClient = observedCall.createObservedClient({
|
|
36
|
+
clientId: 'client-id',
|
|
37
|
+
mediaUnitId: 'media-unit-id',
|
|
49
38
|
});
|
|
50
39
|
|
|
40
|
+
const clientSample: ClientSample; // Receive your samples, for example, from a WebSocket
|
|
41
|
+
|
|
42
|
+
observedClient.accept(clientSample);
|
|
51
43
|
```
|
|
52
44
|
|
|
53
45
|
The above example do as follows:
|
|
@@ -55,9 +47,49 @@ The above example do as follows:
|
|
|
55
47
|
2. create a client source object to accept client samples
|
|
56
48
|
3. add an evaluator process to evaluate ended calls
|
|
57
49
|
|
|
58
|
-
|
|
50
|
+
### Get a Summary of a call when it ends
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
|
|
54
|
+
const monitor = observer.createCallSummaryMonitor('summary', (summary) => {
|
|
55
|
+
console.log('Call Summary', summary);
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### How Many Clients are using TURN?
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const monitor = observer.createTurnUsageMonitor('turn', (turn) => {
|
|
63
|
+
console.log('TURN', turn);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// at any point of time you can get the current state of the turn usage
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
console.log('Currently ', monitor.clients.size, 'clients are using TURN');
|
|
69
|
+
|
|
70
|
+
// you can get the incoming and outgoing bytes of the TURN server
|
|
71
|
+
console.log(`${YOUR_TURN_SERVER_ADDRESS} usage:`, monitor.getUsage(YOUR_TURN_SERVER_ADDRESS));
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Monitor Calls and Clients as they updated
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
observer.on('newcall', (call) => {
|
|
79
|
+
call.on('update', () => {
|
|
80
|
+
console.log('Call Updated', call.callId);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
call.on('newclient', (client) => {
|
|
84
|
+
|
|
85
|
+
client.on('update', () => {
|
|
86
|
+
console.log('Client Updated', client.clientId);
|
|
87
|
+
|
|
88
|
+
console.log(`The avaialble incoming bitrate for the client ${client.clientId} is: ${client.availableIncomingBitrate}`)
|
|
89
|
+
});
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
```
|
|
61
93
|
|
|
62
94
|
## NPM package
|
|
63
95
|
|
|
@@ -69,10 +101,6 @@ https://www.npmjs.com/package/@observertc/observer-js
|
|
|
69
101
|
https://github.com/observertc/schemas
|
|
70
102
|
|
|
71
103
|
|
|
72
|
-
## Getting Involved
|
|
73
|
-
|
|
74
|
-
The repository is open for contributions.
|
|
75
|
-
|
|
76
104
|
## License
|
|
77
105
|
|
|
78
106
|
Apache-2.0
|
package/lib/ObservedClient.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ import { ObservedCall } from './ObservedCall';
|
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import { ObservedPeerConnection } from './ObservedPeerConnection';
|
|
6
6
|
import { ObservedSfu } from './ObservedSfu';
|
|
7
|
+
export interface ClientIssue {
|
|
8
|
+
severity: 'critical' | 'major' | 'minor';
|
|
9
|
+
timestamp: number;
|
|
10
|
+
description?: string;
|
|
11
|
+
peerConnectionId?: string;
|
|
12
|
+
trackId?: string;
|
|
13
|
+
attachments?: Record<string, unknown>;
|
|
14
|
+
}
|
|
7
15
|
export type ObservedClientModel = {
|
|
8
16
|
clientId: string;
|
|
9
17
|
mediaUnitId: string;
|
|
@@ -47,6 +55,7 @@ export type ObservedClientEvents = {
|
|
|
47
55
|
remoteCandidate: IceRemoteCandidate;
|
|
48
56
|
}
|
|
49
57
|
];
|
|
58
|
+
issue: [ClientIssue];
|
|
50
59
|
usingturn: [boolean];
|
|
51
60
|
usermediaerror: [string];
|
|
52
61
|
};
|
|
@@ -122,6 +131,7 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
122
131
|
get closed(): boolean;
|
|
123
132
|
get peerConnections(): ReadonlyMap<string, ObservedPeerConnection>;
|
|
124
133
|
close(): void;
|
|
134
|
+
addIssue(issue: ClientIssue): void;
|
|
125
135
|
accept(sample: ClientSample): void;
|
|
126
136
|
private _createPeerConnection;
|
|
127
137
|
private _addClientLeftReport;
|
|
@@ -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;AAOlE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5C,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;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,MAAM,EAAE;QAAC;YACR,eAAe,EAAE,MAAM,CAAC;SACxB;KAAC,CAAC;IACH,KAAK,EAAE,EAAE,CAAC;IACV,iBAAiB,EAAE,CAAC,sBAAsB,CAAC,CAAC;IAC5C,wBAAwB,EAAE;QAAC;YAC1B,cAAc,EAAE,sBAAsB,CAAC;YACvC,KAAK,EAAE,MAAM,CAAC;SACd;KAAC,CAAC;IACH,uBAAuB,EAAE;QAAC;YACzB,cAAc,EAAE,sBAAsB,CAAC;YACvC,KAAK,EAAE,MAAM,CAAC;SACd;KAAC,CAAC;IACH,qBAAqB,EAAE;QAAC;YACvB,cAAc,EAAE,sBAAsB,CAAC;YACvC,KAAK,EAAE,MAAM,CAAC;SACd;KAAC,CAAC;IACH,qBAAqB,EAAE;QAAC;YACvB,cAAc,EAAE,sBAAsB,CAAC;YACvC,cAAc,EAAE,iBAAiB,CAAC;YAClC,eAAe,EAAE,kBAAkB,CAAC;SACpC;KAAC,CAAC;IACH,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC;IACrB,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,CAAC,OAAO,WAAW,cAAc,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxG,EAAE,CAAC,CAAC,SAAS,MAAM,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAC/G,GAAG,CAAC,CAAC,SAAS,MAAM,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAChH,IAAI,CAAC,CAAC,SAAS,MAAM,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACjH,IAAI,CAAC,CAAC,SAAS,MAAM,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAChG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AAED,qBAAa,cAAc,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,SAAQ,YAAY;IAqDjH,OAAO,CAAC,QAAQ,CAAC,MAAM;aACP,IAAI,EAAE,YAAY;aAClB,OAAO,EAAE,OAAO;IArDjC,SAAgB,OAAO,SAAc;IAC9B,QAAQ,SAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6C;IAE9E,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,sBAAsB,CAAC,CAAS;IACxC,OAAO,CAAC,KAAK,CAAC,CAAS;IAEhB,SAAS,UAAS;IAClB,wBAAwB,SAAK;IAC7B,wBAAwB,SAAK;IAC7B,uBAAuB,SAAK;IAC5B,2BAA2B,SAAK;IAChC,wBAAwB,SAAK;IAC7B,yBAAyB,SAAK;IAC9B,6BAA6B,SAAK;IAClC,cAAc,SAAK;IACnB,kBAAkB,SAAK;IACvB,uBAAuB,SAAK;IAC5B,uBAAuB,SAAK;IAC5B,mBAAmB,SAAK;IACxB,mBAAmB,SAAK;IAExB,uBAAuB,SAAK;IAC5B,uBAAuB,SAAK;IAC5B,mBAAmB,SAAK;IACxB,mBAAmB,SAAK;IACxB,yBAAyB,SAAK;IAC9B,6BAA6B,SAAK;IAClC,uBAAuB,SAAK;IAC5B,2BAA2B,SAAK;IAChC,wBAAwB,SAAK;IAE7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAEpC,SAAgB,YAAY,EAAE,MAAM,EAAE,CAAM;IAC5C,SAAgB,MAAM,EAAE,MAAM,EAAE,CAAM;IACtC,SAAgB,eAAe,EAAE,MAAM,EAAE,CAAM;IAE/C,SAAgB,8BAA8B,sBAA6B;IAC3E,SAAgB,sCAAsC,sBAA6B;gBAGjE,MAAM,EAAE,mBAAmB,EAC5B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,OAAO;IAMjC,IAAW,GAAG,IAAI,WAAW,GAAG,SAAS,CAIxC;IAED,IAAW,QAAQ,IAAI,MAAM,CAG5B;IAED,IAAW,MAAM,WAGhB;IAED,IAAW,SAAS,WAGnB;IAED,IAAW,MAAM,WAGhB;IAED,IAAW,WAAW,WAGrB;IAED,IAAW,OAAO,kDAEjB;IAED,IAAW,MAAM,uBAEhB;IAED,IAAW,qBAAqB,uBAE/B;IAED,IAAW,MAAM,uBAEhB;IAED,IAAW,OAAO,wBAEjB;IAED,IAAW,MAAM,uBAEhB;IAED,IAAW,eAAe,gCAEzB;IAED,IAAW,QAAQ,yBAElB;IAED,IAAW,cAAc,WAExB;IAED,IAAW,OAAO,WAEjB;IAED,IAAW,MAAM,YAEhB;IAED,IAAW,eAAe,IAAI,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAExE;IAEM,KAAK;IAaL,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":"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"}
|
package/lib/ObservedClient.js
CHANGED
|
@@ -133,6 +133,30 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
133
133
|
Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
134
134
|
this.emit('close');
|
|
135
135
|
}
|
|
136
|
+
addIssue(issue) {
|
|
137
|
+
var _a;
|
|
138
|
+
try {
|
|
139
|
+
this.reports.addCallEventReport({
|
|
140
|
+
serviceId: this.serviceId,
|
|
141
|
+
mediaUnitId: this.mediaUnitId,
|
|
142
|
+
roomId: this.roomId,
|
|
143
|
+
callId: this.callId,
|
|
144
|
+
clientId: this.clientId,
|
|
145
|
+
userId: this.userId,
|
|
146
|
+
name: CallEventType_1.CallEventType.CLIENT_ISSUE,
|
|
147
|
+
value: issue.severity,
|
|
148
|
+
peerConnectionId: issue.peerConnectionId,
|
|
149
|
+
mediaTrackId: issue.trackId,
|
|
150
|
+
message: issue.description,
|
|
151
|
+
timestamp: (_a = issue.timestamp) !== null && _a !== void 0 ? _a : Date.now(),
|
|
152
|
+
attachments: issue.attachments ? JSON.stringify(issue.attachments) : undefined,
|
|
153
|
+
});
|
|
154
|
+
this.emit('issue', issue);
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
logger.warn(`Error adding client issue: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
136
160
|
accept(sample) {
|
|
137
161
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20;
|
|
138
162
|
if (this._closed)
|
|
@@ -229,6 +253,23 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
229
253
|
this._left = timestamp;
|
|
230
254
|
break;
|
|
231
255
|
}
|
|
256
|
+
case CallEventType_1.CallEventType.CLIENT_ISSUE: {
|
|
257
|
+
const severity = callEvent.value ? callEvent.value : 'minor';
|
|
258
|
+
try {
|
|
259
|
+
const issue = {
|
|
260
|
+
severity,
|
|
261
|
+
timestamp: timestamp !== null && timestamp !== void 0 ? timestamp : Date.now(),
|
|
262
|
+
description: callEvent.message,
|
|
263
|
+
peerConnectionId: callEvent.peerConnectionId,
|
|
264
|
+
trackId: callEvent.mediaTrackId,
|
|
265
|
+
attachments: callEvent.attachments ? JSON.parse(callEvent.attachments) : undefined,
|
|
266
|
+
};
|
|
267
|
+
this.emit('issue', issue);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
logger.warn(`Error parsing client issue: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
232
273
|
}
|
|
233
274
|
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 : Date.now() }, callEvent));
|
|
234
275
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedOutboundTrack.d.ts","sourceRoot":"","sources":["../src/ObservedOutboundTrack.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ObservedOutboundTrack.d.ts","sourceRoot":"","sources":["../src/ObservedOutboundTrack.ts"],"names":[],"mappings":"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"}
|
|
@@ -112,6 +112,13 @@ class ObservedOutboundTrack extends events_1.EventEmitter {
|
|
|
112
112
|
if ((videoSample === null || videoSample === void 0 ? void 0 : videoSample.framesSent) && (lastVideoStats === null || lastVideoStats === void 0 ? void 0 : lastVideoStats.framesSent) && lastVideoStats.framesSent < videoSample.framesSent) {
|
|
113
113
|
deltaSentFrames = videoSample.framesSent - lastVideoStats.framesSent;
|
|
114
114
|
}
|
|
115
|
+
if (videoSample.qualityLimitationReason && (lastVideoStats === null || lastVideoStats === void 0 ? void 0 : lastVideoStats.qualityLimitationReason) !== videoSample.qualityLimitationReason) {
|
|
116
|
+
this.emit('qualitylimitationchanged', videoSample.qualityLimitationReason);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (this.kind === 'audio') {
|
|
120
|
+
// const audioSample = sample as OutboundAudioTrack;
|
|
121
|
+
// const lastAudioStats = lastStat as OutboundAudioTrack | undefined;
|
|
115
122
|
}
|
|
116
123
|
const stats = Object.assign(Object.assign({}, sample), { rttInMs,
|
|
117
124
|
bitrate, ssrc: sample.ssrc, deltaLostPackets,
|
package/lib/Observer.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { EventEmitter } from 'events';
|
|
|
5
5
|
import { PartialBy } from './common/utils';
|
|
6
6
|
import { ObserverSinkContext } from './common/types';
|
|
7
7
|
import { ObservedSfu, ObservedSfuModel } from './ObservedSfu';
|
|
8
|
-
import { CallSummaryMonitor } from './monitors/CallSummaryMonitor';
|
|
8
|
+
import { CallSummaryMonitor, CallSummaryMonitorConfig } from './monitors/CallSummaryMonitor';
|
|
9
9
|
import { TurnUsageMonitor } from './monitors/TurnUsageMonitor';
|
|
10
10
|
export type ObserverEvents = {
|
|
11
11
|
'newcall': [ObservedCall];
|
|
@@ -51,9 +51,11 @@ export declare class Observer extends EventEmitter {
|
|
|
51
51
|
createObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(config: PartialBy<ObservedCallModel, 'serviceId'> & {
|
|
52
52
|
appData: T;
|
|
53
53
|
started?: number;
|
|
54
|
+
reportCallStarted?: boolean;
|
|
55
|
+
reportCallEnded?: boolean;
|
|
54
56
|
}): ObservedCall<T>;
|
|
55
57
|
createObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>>(model: ObservedSfuModel, appData: AppData): ObservedSfu<AppData>;
|
|
56
|
-
createCallSummaryMonitor(options?: {
|
|
58
|
+
createCallSummaryMonitor(options?: CallSummaryMonitorConfig & {
|
|
57
59
|
timeoutAfterCallClose?: number;
|
|
58
60
|
}): CallSummaryMonitor;
|
|
59
61
|
createTurnUsageMonitor(): TurnUsageMonitor;
|
package/lib/Observer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Observer.d.ts","sourceRoot":"","sources":["../src/Observer.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"Observer.d.ts","sourceRoot":"","sources":["../src/Observer.ts"],"names":[],"mappings":"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"}
|
package/lib/Observer.js
CHANGED
|
@@ -65,7 +65,7 @@ class Observer extends events_1.EventEmitter {
|
|
|
65
65
|
if (this._closed) {
|
|
66
66
|
throw new Error('Attempted to create a call source on a closed observer');
|
|
67
67
|
}
|
|
68
|
-
const { appData, started = Date.now() } = config, model = __rest(config, ["appData", "started"]);
|
|
68
|
+
const { appData, started = Date.now(), reportCallEnded = true, reportCallStarted = true } = config, model = __rest(config, ["appData", "started", "reportCallEnded", "reportCallStarted"]);
|
|
69
69
|
const call = new ObservedCall_1.ObservedCall(Object.assign(Object.assign({}, model), { serviceId: this.config.defaultServiceId }), this, appData);
|
|
70
70
|
if (this._closed)
|
|
71
71
|
throw new Error('Cannot create an observed call on a closed observer');
|
|
@@ -73,10 +73,10 @@ class Observer extends events_1.EventEmitter {
|
|
|
73
73
|
throw new Error(`Observed Call with id ${call.callId} already exists`);
|
|
74
74
|
call.once('close', () => {
|
|
75
75
|
this._observedCalls.delete(call.callId);
|
|
76
|
-
this.reports.addCallEventReport((0, callEventReports_1.createCallEndedEventReport)(call.serviceId, call.roomId, call.callId, Date.now()));
|
|
76
|
+
reportCallEnded && this.reports.addCallEventReport((0, callEventReports_1.createCallEndedEventReport)(call.serviceId, call.roomId, call.callId, Date.now()));
|
|
77
77
|
});
|
|
78
78
|
this._observedCalls.set(call.callId, call);
|
|
79
|
-
this.reports.addCallEventReport((0, callEventReports_1.createCallStartedEventReport)(call.serviceId, call.roomId, call.callId, started));
|
|
79
|
+
reportCallStarted && this.reports.addCallEventReport((0, callEventReports_1.createCallStartedEventReport)(call.serviceId, call.roomId, call.callId, started));
|
|
80
80
|
this.emit('newcall', call);
|
|
81
81
|
return call;
|
|
82
82
|
}
|
|
@@ -102,7 +102,7 @@ class Observer extends events_1.EventEmitter {
|
|
|
102
102
|
const existingMonitor = this._monitors.get(CallSummaryMonitor_1.CallSummaryMonitor.name);
|
|
103
103
|
if (existingMonitor)
|
|
104
104
|
return existingMonitor;
|
|
105
|
-
const monitor = new CallSummaryMonitor_1.CallSummaryMonitor();
|
|
105
|
+
const monitor = new CallSummaryMonitor_1.CallSummaryMonitor(options);
|
|
106
106
|
const onNewCall = (call) => {
|
|
107
107
|
monitor.addCall(call);
|
|
108
108
|
call.once('close', () => {
|
|
@@ -17,6 +17,7 @@ export declare enum CallEventType {
|
|
|
17
17
|
DATA_CHANNEL_CLOSED = "DATA_CHANNEL_CLOSED",
|
|
18
18
|
DATA_CHANNEL_ERROR = "DATA_CHANNEL_ERROR",
|
|
19
19
|
NEGOTIATION_NEEDED = "NEGOTIATION_NEEDED",
|
|
20
|
-
SIGNALING_STATE_CHANGE = "SIGNALING_STATE_CHANGE"
|
|
20
|
+
SIGNALING_STATE_CHANGE = "SIGNALING_STATE_CHANGE",
|
|
21
|
+
CLIENT_ISSUE = "CLIENT_ISSUE"
|
|
21
22
|
}
|
|
22
23
|
//# sourceMappingURL=CallEventType.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../src/common/CallEventType.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../src/common/CallEventType.ts"],"names":[],"mappings":"AACA,oBAAY,aAAa;IACxB,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,aAAa,kBAAkB;IAC/B,WAAW,gBAAgB;IAC3B,sBAAsB,2BAA2B;IACjD,sBAAsB,2BAA2B;IACjD,iBAAiB,sBAAsB;IACvC,mBAAmB,wBAAwB;IAC3C,mBAAmB,wBAAwB;IAC3C,iBAAiB,sBAAsB;IACvC,mBAAmB,wBAAwB;IAC3C,2BAA2B,gCAAgC;IAC3D,6BAA6B,kCAAkC;IAC/D,4BAA4B,iCAAiC;IAC7D,iBAAiB,sBAAsB;IACvC,mBAAmB,wBAAwB;IAC3C,kBAAkB,uBAAuB;IACzC,kBAAkB,uBAAuB;IACzC,sBAAsB,2BAA2B;IACjD,YAAY,iBAAiB;CAC7B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// eslint-disable-next-line no-shadow
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.CallEventType = void 0;
|
|
4
|
+
// eslint-disable-next-line no-shadow
|
|
5
5
|
var CallEventType;
|
|
6
6
|
(function (CallEventType) {
|
|
7
7
|
CallEventType["CALL_STARTED"] = "CALL_STARTED";
|
|
@@ -23,4 +23,5 @@ var CallEventType;
|
|
|
23
23
|
CallEventType["DATA_CHANNEL_ERROR"] = "DATA_CHANNEL_ERROR";
|
|
24
24
|
CallEventType["NEGOTIATION_NEEDED"] = "NEGOTIATION_NEEDED";
|
|
25
25
|
CallEventType["SIGNALING_STATE_CHANGE"] = "SIGNALING_STATE_CHANGE";
|
|
26
|
+
CallEventType["CLIENT_ISSUE"] = "CLIENT_ISSUE";
|
|
26
27
|
})(CallEventType = exports.CallEventType || (exports.CallEventType = {}));
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import { ClientIssue } from '../ObservedClient';
|
|
1
2
|
export interface ClientSummary {
|
|
2
3
|
clientId: string;
|
|
3
4
|
mediaUnitId: string;
|
|
4
5
|
userId?: string;
|
|
5
6
|
joined: number;
|
|
7
|
+
left?: number;
|
|
6
8
|
durationInMs: number;
|
|
7
9
|
avgOutboundAudioBitrate: number;
|
|
8
10
|
avgOutboundVideoBitrate: number;
|
|
9
11
|
avgInboundAudioBitrate: number;
|
|
10
12
|
avgInboundVideoBitrate: number;
|
|
11
13
|
ewmaRttInMs: number;
|
|
14
|
+
usedTurn: boolean;
|
|
15
|
+
issues: ClientIssue[];
|
|
12
16
|
}
|
|
13
17
|
export interface CallSummary {
|
|
14
18
|
serviceId: string;
|
|
@@ -16,6 +20,7 @@ export interface CallSummary {
|
|
|
16
20
|
callId: string;
|
|
17
21
|
started: number;
|
|
18
22
|
durationInMs: number;
|
|
23
|
+
maxNumberOfParticipants: number;
|
|
19
24
|
clients: ClientSummary[];
|
|
20
25
|
}
|
|
21
26
|
//# sourceMappingURL=CallSummary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallSummary.d.ts","sourceRoot":"","sources":["../../src/monitors/CallSummary.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"CallSummary.d.ts","sourceRoot":"","sources":["../../src/monitors/CallSummary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;CACzB"}
|
|
@@ -6,6 +6,10 @@ export type CallSummaryMonitorEvents = {
|
|
|
6
6
|
close: [];
|
|
7
7
|
summary: [CallSummary];
|
|
8
8
|
};
|
|
9
|
+
export type CallSummaryMonitorConfig = {
|
|
10
|
+
detectUserMediaIssues?: boolean;
|
|
11
|
+
detectMediaTrackQualityLimitationIssues?: boolean;
|
|
12
|
+
};
|
|
9
13
|
export declare interface CallSummaryMonitor {
|
|
10
14
|
on<U extends keyof CallSummaryMonitorEvents>(event: U, listener: (...args: CallSummaryMonitorEvents[U]) => void): this;
|
|
11
15
|
off<U extends keyof CallSummaryMonitorEvents>(event: U, listener: (...args: CallSummaryMonitorEvents[U]) => void): this;
|
|
@@ -13,11 +17,14 @@ export declare interface CallSummaryMonitor {
|
|
|
13
17
|
emit<U extends keyof CallSummaryMonitorEvents>(event: U, ...args: CallSummaryMonitorEvents[U]): boolean;
|
|
14
18
|
}
|
|
15
19
|
export declare class CallSummaryMonitor extends EventEmitter {
|
|
20
|
+
readonly config: CallSummaryMonitorConfig;
|
|
16
21
|
private readonly _summaries;
|
|
17
22
|
private _closed;
|
|
18
|
-
constructor();
|
|
23
|
+
constructor(config?: CallSummaryMonitorConfig);
|
|
19
24
|
addCall(call: ObservedCall): void;
|
|
20
25
|
private _addClient;
|
|
26
|
+
private _addPeerConnection;
|
|
27
|
+
private _addOutboundVideoTrack;
|
|
21
28
|
takeSummary(callId: string): CallSummary | undefined;
|
|
22
29
|
get closed(): boolean;
|
|
23
30
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallSummaryMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/CallSummaryMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"CallSummaryMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/CallSummaryMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAK/C,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC;CACvB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uCAAuC,CAAC,EAAE,OAAO,CAAC;CAClD,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;aAKlC,MAAM,EAAE,wBAAwB;IAJjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAE7D,OAAO,CAAC,OAAO,CAAS;gBAEP,MAAM,GAAE,wBAA6B;IAK/C,OAAO,CAAC,IAAI,EAAE,YAAY;IA6BjC,OAAO,CAAC,UAAU;IAuElB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,sBAAsB;IAmBvB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU3D,IAAW,MAAM,YAEhB;IAEM,KAAK;CAMZ"}
|
|
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CallSummaryMonitor = void 0;
|
|
4
4
|
const events_1 = require("events");
|
|
5
5
|
class CallSummaryMonitor extends events_1.EventEmitter {
|
|
6
|
-
constructor() {
|
|
6
|
+
constructor(config = {}) {
|
|
7
7
|
super();
|
|
8
|
+
this.config = config;
|
|
8
9
|
this._summaries = new Map();
|
|
9
10
|
this._closed = false;
|
|
10
11
|
}
|
|
@@ -19,12 +20,16 @@ class CallSummaryMonitor extends events_1.EventEmitter {
|
|
|
19
20
|
serviceId: call.serviceId,
|
|
20
21
|
clients: [],
|
|
21
22
|
durationInMs: 0,
|
|
23
|
+
maxNumberOfParticipants: 0,
|
|
22
24
|
started: call.created,
|
|
23
25
|
};
|
|
24
26
|
this._summaries.set(call.callId, callSummary);
|
|
25
27
|
}
|
|
26
28
|
const constCallSummary = callSummary;
|
|
27
|
-
const onNewClient = (client) =>
|
|
29
|
+
const onNewClient = (client) => {
|
|
30
|
+
constCallSummary.maxNumberOfParticipants = Math.max(constCallSummary.maxNumberOfParticipants, call.clients.size);
|
|
31
|
+
this._addClient(constCallSummary, client);
|
|
32
|
+
};
|
|
28
33
|
call.once('close', () => {
|
|
29
34
|
call.off('newclient', onNewClient);
|
|
30
35
|
});
|
|
@@ -44,6 +49,8 @@ class CallSummaryMonitor extends events_1.EventEmitter {
|
|
|
44
49
|
ewmaRttInMs: 0,
|
|
45
50
|
joined: client.created,
|
|
46
51
|
userId: client.userId,
|
|
52
|
+
usedTurn: false,
|
|
53
|
+
issues: [],
|
|
47
54
|
};
|
|
48
55
|
callSummary.clients.push(clientSummary);
|
|
49
56
|
const updateClient = () => {
|
|
@@ -51,7 +58,29 @@ class CallSummaryMonitor extends events_1.EventEmitter {
|
|
|
51
58
|
clientSummary.ewmaRttInMs = (clientSummary.ewmaRttInMs * 0.9) + (client.avgRttInMs * 0.1);
|
|
52
59
|
}
|
|
53
60
|
};
|
|
61
|
+
const onUsingTurn = (usingTurn) => {
|
|
62
|
+
clientSummary.usedTurn || (clientSummary.usedTurn = usingTurn);
|
|
63
|
+
};
|
|
64
|
+
const onIssue = (issue) => {
|
|
65
|
+
clientSummary.issues.push(issue);
|
|
66
|
+
};
|
|
67
|
+
const onUserMediaError = (error) => {
|
|
68
|
+
if (!this.config.detectUserMediaIssues)
|
|
69
|
+
return;
|
|
70
|
+
// maybe the client also sned this issue, in which case the timestamp can be more accurate
|
|
71
|
+
const alreadyDetected = clientSummary.issues.find((issue) => issue.severity === 'critical' && issue.description === error);
|
|
72
|
+
!alreadyDetected && clientSummary.issues.push({
|
|
73
|
+
severity: 'critical',
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
description: error,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const onNewPeerConnection = (peerConnection) => this._addPeerConnection(clientSummary, peerConnection);
|
|
54
79
|
client.on('update', updateClient);
|
|
80
|
+
client.on('usingturn', onUsingTurn);
|
|
81
|
+
client.on('issue', onIssue);
|
|
82
|
+
client.on('usermediaerror', onUserMediaError);
|
|
83
|
+
client.on('newpeerconnection', onNewPeerConnection);
|
|
55
84
|
client.once('close', () => {
|
|
56
85
|
const elapsedTimeInS = (Date.now() - client.created) / 1000;
|
|
57
86
|
clientSummary.avgInboundAudioBitrate = (client.totalReceivedBytes * 8) / elapsedTimeInS;
|
|
@@ -60,7 +89,36 @@ class CallSummaryMonitor extends events_1.EventEmitter {
|
|
|
60
89
|
clientSummary.avgOutboundVideoBitrate = (client.totalSentBytes * 8) / elapsedTimeInS;
|
|
61
90
|
clientSummary.durationInMs = elapsedTimeInS * 1000;
|
|
62
91
|
client.off('update', updateClient);
|
|
92
|
+
client.off('usingturn', onUsingTurn);
|
|
93
|
+
client.off('issue', onIssue);
|
|
94
|
+
client.off('usermediaerror', onUserMediaError);
|
|
95
|
+
client.off('newpeerconnection', onNewPeerConnection);
|
|
96
|
+
callSummary.durationInMs += clientSummary.durationInMs;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
_addPeerConnection(clientSummary, peerConnection) {
|
|
100
|
+
const onOutboundVideoTrack = (track) => this._addOutboundVideoTrack(clientSummary, track);
|
|
101
|
+
peerConnection.on('newoutboundvideotrack', onOutboundVideoTrack);
|
|
102
|
+
peerConnection.once('close', () => {
|
|
103
|
+
peerConnection.off('newoutboundvideotrack', onOutboundVideoTrack);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
_addOutboundVideoTrack(clientSummary, track) {
|
|
107
|
+
const onQualityLimitationChanged = (reason) => {
|
|
108
|
+
if (!this.config.detectMediaTrackQualityLimitationIssues)
|
|
109
|
+
return;
|
|
110
|
+
clientSummary.issues.push({
|
|
111
|
+
severity: 'minor',
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
description: reason,
|
|
114
|
+
peerConnectionId: track.peerConnectionId,
|
|
115
|
+
trackId: track.trackId,
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
track.once('close', () => {
|
|
119
|
+
track.off('qualitylimitationchanged', onQualityLimitationChanged);
|
|
63
120
|
});
|
|
121
|
+
track.on('qualitylimitationchanged', onQualityLimitationChanged);
|
|
64
122
|
}
|
|
65
123
|
takeSummary(callId) {
|
|
66
124
|
if (this.closed)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import { ObservedPeerConnection } from '../ObservedPeerConnection';
|
|
4
|
+
import { ObservedClient } from '../ObservedClient';
|
|
4
5
|
export type TurnUsageMonitorEvents = {
|
|
5
6
|
close: [];
|
|
6
7
|
'newturnip': [string];
|
|
@@ -31,6 +32,7 @@ export declare class TurnUsageMonitor extends EventEmitter {
|
|
|
31
32
|
get turnIps(): string[];
|
|
32
33
|
get peerConnections(): ObservedPeerConnection[];
|
|
33
34
|
getUsage(turnIp: string): TurnUsage | undefined;
|
|
35
|
+
get clients(): ReadonlyMap<string, ObservedClient>;
|
|
34
36
|
get closed(): boolean;
|
|
35
37
|
close(): void;
|
|
36
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TurnUsageMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/TurnUsageMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"TurnUsageMonitor.d.ts","sourceRoot":"","sources":["../../src/monitors/TurnUsageMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,EAAE,CAAC;IACV,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC;IACtB,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC;IACpB,iBAAiB,EAAE,CAAC,sBAAsB,CAAC,CAAC;IAC5C,oBAAoB,EAAE,CAAC,sBAAsB,CAAC,CAAC;CAC/C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;CAC7B,CAAA;AAQD,MAAM,CAAC,OAAO,WAAW,gBAAgB;IACxC,EAAE,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACnH,GAAG,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACpH,IAAI,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACrH,IAAI,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CACpG;AAED,qBAAa,gBAAiB,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IACtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;IAE3D,OAAO,CAAC,OAAO,CAAS;;IAKjB,iBAAiB,CAAC,cAAc,EAAE,sBAAsB;IA4CxD,oBAAoB,CAAC,cAAc,EAAE,sBAAsB;IAelE,IAAW,OAAO,aAEjB;IAED,IAAW,eAAe,6BAEzB;IAEM,QAAQ,CAAC,MAAM,EAAE,MAAM;IAI9B,IAAW,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAUxD;IAED,IAAW,MAAM,YAEhB;IAEM,KAAK;CAMZ"}
|
|
@@ -67,6 +67,15 @@ class TurnUsageMonitor extends events_1.EventEmitter {
|
|
|
67
67
|
getUsage(turnIp) {
|
|
68
68
|
return this._turnUsage.get(turnIp);
|
|
69
69
|
}
|
|
70
|
+
get clients() {
|
|
71
|
+
const result = new Map();
|
|
72
|
+
for (const pc of this.peerConnections) {
|
|
73
|
+
if (result.has(pc.clientId))
|
|
74
|
+
continue;
|
|
75
|
+
result.set(pc.clientId, pc.client);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
70
79
|
get closed() {
|
|
71
80
|
return this._closed;
|
|
72
81
|
}
|
package/package.json
CHANGED
package/src/ObservedClient.ts
CHANGED
|
@@ -12,6 +12,15 @@ import { ObservedSfu } from './ObservedSfu';
|
|
|
12
12
|
|
|
13
13
|
const logger = createLogger('ObservedClient');
|
|
14
14
|
|
|
15
|
+
export interface ClientIssue {
|
|
16
|
+
severity: 'critical' | 'major' | 'minor';
|
|
17
|
+
timestamp: number;
|
|
18
|
+
description?: string;
|
|
19
|
+
peerConnectionId?: string,
|
|
20
|
+
trackId?: string,
|
|
21
|
+
attachments?: Record<string, unknown>,
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
export type ObservedClientModel= {
|
|
16
25
|
clientId: string;
|
|
17
26
|
mediaUnitId: string;
|
|
@@ -46,6 +55,7 @@ export type ObservedClientEvents = {
|
|
|
46
55
|
localCandidate: IceLocalCandidate,
|
|
47
56
|
remoteCandidate: IceRemoteCandidate,
|
|
48
57
|
}],
|
|
58
|
+
issue: [ClientIssue],
|
|
49
59
|
usingturn: [boolean],
|
|
50
60
|
usermediaerror: [string],
|
|
51
61
|
};
|
|
@@ -211,6 +221,31 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
211
221
|
this.emit('close');
|
|
212
222
|
}
|
|
213
223
|
|
|
224
|
+
public addIssue(issue: ClientIssue) {
|
|
225
|
+
try {
|
|
226
|
+
this.reports.addCallEventReport({
|
|
227
|
+
serviceId: this.serviceId,
|
|
228
|
+
mediaUnitId: this.mediaUnitId,
|
|
229
|
+
roomId: this.roomId,
|
|
230
|
+
callId: this.callId,
|
|
231
|
+
clientId: this.clientId,
|
|
232
|
+
userId: this.userId,
|
|
233
|
+
|
|
234
|
+
name: CallEventType.CLIENT_ISSUE,
|
|
235
|
+
value: issue.severity,
|
|
236
|
+
peerConnectionId: issue.peerConnectionId,
|
|
237
|
+
mediaTrackId: issue.trackId,
|
|
238
|
+
message: issue.description,
|
|
239
|
+
timestamp: issue.timestamp ?? Date.now(),
|
|
240
|
+
attachments: issue.attachments ? JSON.stringify(issue.attachments): undefined,
|
|
241
|
+
});
|
|
242
|
+
this.emit('issue', issue);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
logger.warn(`Error adding client issue: ${(err as Error)?.message}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
214
249
|
public accept(sample: ClientSample): void {
|
|
215
250
|
if (this._closed) throw new Error(`Client ${this.clientId} is closed`);
|
|
216
251
|
if (sample.clientId && sample.clientId !== 'NULL' && sample.clientId !== this.clientId) {
|
|
@@ -393,6 +428,24 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
393
428
|
this._left = timestamp;
|
|
394
429
|
break;
|
|
395
430
|
}
|
|
431
|
+
case CallEventType.CLIENT_ISSUE: {
|
|
432
|
+
const severity = callEvent.value ? callEvent.value as ClientIssue['severity'] : 'minor';
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
const issue: ClientIssue = {
|
|
436
|
+
severity,
|
|
437
|
+
timestamp: timestamp ?? Date.now(),
|
|
438
|
+
description: callEvent.message,
|
|
439
|
+
peerConnectionId: callEvent.peerConnectionId,
|
|
440
|
+
trackId: callEvent.mediaTrackId,
|
|
441
|
+
attachments: callEvent.attachments ? JSON.parse(callEvent.attachments) : undefined,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
this.emit('issue', issue);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
396
449
|
}
|
|
397
450
|
this.reports.addCallEventReport({
|
|
398
451
|
serviceId: this.serviceId,
|
|
@@ -12,6 +12,7 @@ export type ObservedOutboundTrackModel<K extends MediaKind> = {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export type ObservedOutboundTrackEvents = {
|
|
15
|
+
qualitylimitationchanged: [string];
|
|
15
16
|
update: [{
|
|
16
17
|
elapsedTimeInMs: number;
|
|
17
18
|
}],
|
|
@@ -197,6 +198,14 @@ export class ObservedOutboundTrack<Kind extends MediaKind> extends EventEmitter
|
|
|
197
198
|
if (videoSample?.framesSent && lastVideoStats?.framesSent && lastVideoStats.framesSent < videoSample.framesSent) {
|
|
198
199
|
deltaSentFrames = videoSample.framesSent - lastVideoStats.framesSent;
|
|
199
200
|
}
|
|
201
|
+
|
|
202
|
+
if (videoSample.qualityLimitationReason && lastVideoStats?.qualityLimitationReason !== videoSample.qualityLimitationReason) {
|
|
203
|
+
this.emit('qualitylimitationchanged', videoSample.qualityLimitationReason);
|
|
204
|
+
}
|
|
205
|
+
} else if (this.kind === 'audio') {
|
|
206
|
+
// const audioSample = sample as OutboundAudioTrack;
|
|
207
|
+
// const lastAudioStats = lastStat as OutboundAudioTrack | undefined;
|
|
208
|
+
|
|
200
209
|
}
|
|
201
210
|
const stats: ObservedOutboundTrackStats<Kind> = {
|
|
202
211
|
...sample,
|
package/src/Observer.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { PartialBy } from './common/utils';
|
|
|
6
6
|
import { createCallEndedEventReport, createCallStartedEventReport } from './common/callEventReports';
|
|
7
7
|
import { ObserverSinkContext } from './common/types';
|
|
8
8
|
import { ObservedSfu, ObservedSfuModel } from './ObservedSfu';
|
|
9
|
-
import { CallSummaryMonitor } from './monitors/CallSummaryMonitor';
|
|
9
|
+
import { CallSummaryMonitor, CallSummaryMonitorConfig } from './monitors/CallSummaryMonitor';
|
|
10
10
|
import { TurnUsageMonitor } from './monitors/TurnUsageMonitor';
|
|
11
11
|
import { ObservedClient } from './ObservedClient';
|
|
12
12
|
import { ObservedPeerConnection } from './ObservedPeerConnection';
|
|
@@ -104,13 +104,19 @@ export class Observer extends EventEmitter {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
public createObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
107
|
-
config: PartialBy<ObservedCallModel, 'serviceId'> & { appData: T, started?: number }
|
|
107
|
+
config: PartialBy<ObservedCallModel, 'serviceId'> & { appData: T, started?: number, reportCallStarted?: boolean, reportCallEnded?: boolean }
|
|
108
108
|
): ObservedCall<T> {
|
|
109
109
|
if (this._closed) {
|
|
110
110
|
throw new Error('Attempted to create a call source on a closed observer');
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
const {
|
|
113
|
+
const {
|
|
114
|
+
appData,
|
|
115
|
+
started = Date.now(),
|
|
116
|
+
reportCallEnded = true,
|
|
117
|
+
reportCallStarted = true,
|
|
118
|
+
...model
|
|
119
|
+
} = config;
|
|
114
120
|
const call = new ObservedCall({
|
|
115
121
|
...model,
|
|
116
122
|
serviceId: this.config.defaultServiceId,
|
|
@@ -121,7 +127,7 @@ export class Observer extends EventEmitter {
|
|
|
121
127
|
|
|
122
128
|
call.once('close', () => {
|
|
123
129
|
this._observedCalls.delete(call.callId);
|
|
124
|
-
this.reports.addCallEventReport(createCallEndedEventReport(
|
|
130
|
+
reportCallEnded && this.reports.addCallEventReport(createCallEndedEventReport(
|
|
125
131
|
call.serviceId,
|
|
126
132
|
call.roomId,
|
|
127
133
|
call.callId,
|
|
@@ -130,7 +136,7 @@ export class Observer extends EventEmitter {
|
|
|
130
136
|
});
|
|
131
137
|
|
|
132
138
|
this._observedCalls.set(call.callId, call);
|
|
133
|
-
this.reports.addCallEventReport(createCallStartedEventReport(
|
|
139
|
+
reportCallStarted && this.reports.addCallEventReport(createCallStartedEventReport(
|
|
134
140
|
call.serviceId,
|
|
135
141
|
call.roomId,
|
|
136
142
|
call.callId,
|
|
@@ -165,14 +171,14 @@ export class Observer extends EventEmitter {
|
|
|
165
171
|
return sfu;
|
|
166
172
|
}
|
|
167
173
|
|
|
168
|
-
public createCallSummaryMonitor(options?: { timeoutAfterCallClose?: number }): CallSummaryMonitor {
|
|
174
|
+
public createCallSummaryMonitor(options?: CallSummaryMonitorConfig & { timeoutAfterCallClose?: number }): CallSummaryMonitor {
|
|
169
175
|
if (this._closed) throw new Error('Cannot create a call summary monitor on a closed observer');
|
|
170
176
|
|
|
171
177
|
const existingMonitor = this._monitors.get(CallSummaryMonitor.name);
|
|
172
178
|
|
|
173
179
|
if (existingMonitor) return existingMonitor as CallSummaryMonitor;
|
|
174
180
|
|
|
175
|
-
const monitor = new CallSummaryMonitor();
|
|
181
|
+
const monitor = new CallSummaryMonitor(options);
|
|
176
182
|
const onNewCall = (call: ObservedCall) => {
|
|
177
183
|
monitor.addCall(call);
|
|
178
184
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// eslint-disable-next-line no-shadow
|
|
2
|
-
|
|
3
2
|
export enum CallEventType {
|
|
4
3
|
CALL_STARTED = 'CALL_STARTED',
|
|
5
4
|
CALL_ENDED = 'CALL_ENDED',
|
|
@@ -20,4 +19,5 @@ export enum CallEventType {
|
|
|
20
19
|
DATA_CHANNEL_ERROR = 'DATA_CHANNEL_ERROR',
|
|
21
20
|
NEGOTIATION_NEEDED = 'NEGOTIATION_NEEDED',
|
|
22
21
|
SIGNALING_STATE_CHANGE = 'SIGNALING_STATE_CHANGE',
|
|
22
|
+
CLIENT_ISSUE = 'CLIENT_ISSUE',
|
|
23
23
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import { ClientIssue } from '../ObservedClient';
|
|
2
|
+
|
|
1
3
|
export interface ClientSummary {
|
|
2
4
|
clientId: string;
|
|
3
5
|
mediaUnitId: string;
|
|
4
6
|
userId?: string;
|
|
5
7
|
joined: number;
|
|
8
|
+
left?: number;
|
|
6
9
|
durationInMs: number;
|
|
7
10
|
avgOutboundAudioBitrate: number,
|
|
8
11
|
avgOutboundVideoBitrate: number,
|
|
9
12
|
avgInboundAudioBitrate: number,
|
|
10
13
|
avgInboundVideoBitrate: number,
|
|
11
14
|
ewmaRttInMs: number,
|
|
15
|
+
usedTurn: boolean;
|
|
16
|
+
issues: ClientIssue[];
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export interface CallSummary {
|
|
@@ -17,5 +22,6 @@ export interface CallSummary {
|
|
|
17
22
|
callId: string;
|
|
18
23
|
started: number;
|
|
19
24
|
durationInMs: number;
|
|
25
|
+
maxNumberOfParticipants: number;
|
|
20
26
|
clients: ClientSummary[];
|
|
21
27
|
}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { CallSummary } from './CallSummary';
|
|
2
|
+
import { CallSummary, ClientSummary } from './CallSummary';
|
|
3
3
|
import { ObservedCall } from '../ObservedCall';
|
|
4
|
-
import { ObservedClient } from '../ObservedClient';
|
|
4
|
+
import { ClientIssue, ObservedClient } from '../ObservedClient';
|
|
5
|
+
import { ObservedOutboundTrack } from '../ObservedOutboundTrack';
|
|
6
|
+
import { ObservedPeerConnection } from '../ObservedPeerConnection';
|
|
5
7
|
|
|
6
8
|
export type CallSummaryMonitorEvents = {
|
|
7
9
|
close: [],
|
|
8
10
|
summary: [CallSummary],
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
export type CallSummaryMonitorConfig = {
|
|
14
|
+
detectUserMediaIssues?: boolean;
|
|
15
|
+
detectMediaTrackQualityLimitationIssues?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
export declare interface CallSummaryMonitor {
|
|
12
19
|
on<U extends keyof CallSummaryMonitorEvents>(event: U, listener: (...args: CallSummaryMonitorEvents[U]) => void): this;
|
|
13
20
|
off<U extends keyof CallSummaryMonitorEvents>(event: U, listener: (...args: CallSummaryMonitorEvents[U]) => void): this;
|
|
@@ -19,7 +26,9 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
19
26
|
private readonly _summaries = new Map<string, CallSummary>();
|
|
20
27
|
|
|
21
28
|
private _closed = false;
|
|
22
|
-
public constructor(
|
|
29
|
+
public constructor(
|
|
30
|
+
public readonly config: CallSummaryMonitorConfig = {},
|
|
31
|
+
) {
|
|
23
32
|
super();
|
|
24
33
|
}
|
|
25
34
|
|
|
@@ -35,12 +44,16 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
35
44
|
serviceId: call.serviceId,
|
|
36
45
|
clients: [],
|
|
37
46
|
durationInMs: 0,
|
|
47
|
+
maxNumberOfParticipants: 0,
|
|
38
48
|
started: call.created,
|
|
39
49
|
};
|
|
40
50
|
this._summaries.set(call.callId, callSummary);
|
|
41
51
|
}
|
|
42
52
|
const constCallSummary = callSummary;
|
|
43
|
-
const onNewClient = (client: ObservedClient) =>
|
|
53
|
+
const onNewClient = (client: ObservedClient) => {
|
|
54
|
+
constCallSummary.maxNumberOfParticipants = Math.max(constCallSummary.maxNumberOfParticipants, call.clients.size);
|
|
55
|
+
this._addClient(constCallSummary, client);
|
|
56
|
+
};
|
|
44
57
|
|
|
45
58
|
call.once('close', () => {
|
|
46
59
|
call.off('newclient', onNewClient);
|
|
@@ -50,7 +63,7 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
50
63
|
|
|
51
64
|
private _addClient(callSummary: CallSummary, client: ObservedClient) {
|
|
52
65
|
if (this.closed) return;
|
|
53
|
-
const clientSummary = {
|
|
66
|
+
const clientSummary: ClientSummary = {
|
|
54
67
|
clientId: client.clientId,
|
|
55
68
|
mediaUnitId: client.mediaUnitId,
|
|
56
69
|
durationInMs: 0,
|
|
@@ -61,6 +74,8 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
61
74
|
ewmaRttInMs: 0,
|
|
62
75
|
joined: client.created,
|
|
63
76
|
userId: client.userId,
|
|
77
|
+
usedTurn: false,
|
|
78
|
+
issues: [],
|
|
64
79
|
};
|
|
65
80
|
|
|
66
81
|
callSummary.clients.push(clientSummary);
|
|
@@ -71,7 +86,34 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
71
86
|
}
|
|
72
87
|
};
|
|
73
88
|
|
|
89
|
+
const onUsingTurn = (usingTurn: boolean) => {
|
|
90
|
+
clientSummary.usedTurn ||= usingTurn;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const onIssue = (issue: ClientIssue) => {
|
|
94
|
+
clientSummary.issues.push(issue);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const onUserMediaError = (error: string) => {
|
|
98
|
+
if (!this.config.detectUserMediaIssues) return;
|
|
99
|
+
|
|
100
|
+
// maybe the client also sned this issue, in which case the timestamp can be more accurate
|
|
101
|
+
const alreadyDetected = clientSummary.issues.find((issue) => issue.severity === 'critical' && issue.description === error);
|
|
102
|
+
|
|
103
|
+
!alreadyDetected && clientSummary.issues.push({
|
|
104
|
+
severity: 'critical',
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
description: error,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const onNewPeerConnection = (peerConnection: ObservedPeerConnection) => this._addPeerConnection(clientSummary, peerConnection);
|
|
111
|
+
|
|
74
112
|
client.on('update', updateClient);
|
|
113
|
+
client.on('usingturn', onUsingTurn);
|
|
114
|
+
client.on('issue', onIssue);
|
|
115
|
+
client.on('usermediaerror', onUserMediaError);
|
|
116
|
+
client.on('newpeerconnection', onNewPeerConnection);
|
|
75
117
|
client.once('close', () => {
|
|
76
118
|
const elapsedTimeInS = (Date.now() - client.created) / 1000;
|
|
77
119
|
|
|
@@ -81,7 +123,41 @@ export class CallSummaryMonitor extends EventEmitter {
|
|
|
81
123
|
clientSummary.avgOutboundVideoBitrate = (client.totalSentBytes * 8) / elapsedTimeInS;
|
|
82
124
|
clientSummary.durationInMs = elapsedTimeInS * 1000;
|
|
83
125
|
client.off('update', updateClient);
|
|
126
|
+
client.off('usingturn', onUsingTurn);
|
|
127
|
+
client.off('issue', onIssue);
|
|
128
|
+
client.off('usermediaerror', onUserMediaError);
|
|
129
|
+
client.off('newpeerconnection', onNewPeerConnection);
|
|
130
|
+
|
|
131
|
+
callSummary.durationInMs += clientSummary.durationInMs;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private _addPeerConnection(clientSummary: ClientSummary, peerConnection: ObservedPeerConnection) {
|
|
136
|
+
const onOutboundVideoTrack = (track: ObservedOutboundTrack<'video'>) => this._addOutboundVideoTrack(clientSummary, track);
|
|
137
|
+
|
|
138
|
+
peerConnection.on('newoutboundvideotrack', onOutboundVideoTrack);
|
|
139
|
+
peerConnection.once('close', () => {
|
|
140
|
+
peerConnection.off('newoutboundvideotrack', onOutboundVideoTrack);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private _addOutboundVideoTrack(clientSummary: ClientSummary, track: ObservedOutboundTrack<'video'>) {
|
|
145
|
+
const onQualityLimitationChanged = (reason: string) => {
|
|
146
|
+
if (!this.config.detectMediaTrackQualityLimitationIssues) return;
|
|
147
|
+
|
|
148
|
+
clientSummary.issues.push({
|
|
149
|
+
severity: 'minor',
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
description: reason,
|
|
152
|
+
peerConnectionId: track.peerConnectionId,
|
|
153
|
+
trackId: track.trackId,
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
track.once('close', () => {
|
|
158
|
+
track.off('qualitylimitationchanged', onQualityLimitationChanged);
|
|
84
159
|
});
|
|
160
|
+
track.on('qualitylimitationchanged', onQualityLimitationChanged);
|
|
85
161
|
}
|
|
86
162
|
|
|
87
163
|
public takeSummary(callId: string): CallSummary | undefined {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import { ObservedPeerConnection } from '../ObservedPeerConnection';
|
|
3
3
|
import { ObservedICEEvents } from '../ObservedICE';
|
|
4
|
+
import { ObservedClient } from '../ObservedClient';
|
|
4
5
|
|
|
5
6
|
export type TurnUsageMonitorEvents = {
|
|
6
7
|
close: [],
|
|
@@ -111,6 +112,18 @@ export class TurnUsageMonitor extends EventEmitter {
|
|
|
111
112
|
return this._turnUsage.get(turnIp);
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
public get clients(): ReadonlyMap<string, ObservedClient> {
|
|
116
|
+
const result = new Map<string, ObservedClient>();
|
|
117
|
+
|
|
118
|
+
for (const pc of this.peerConnections) {
|
|
119
|
+
if (result.has(pc.clientId)) continue;
|
|
120
|
+
|
|
121
|
+
result.set(pc.clientId, pc.client);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
114
127
|
public get closed() {
|
|
115
128
|
return this._closed;
|
|
116
129
|
}
|