@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 CHANGED
@@ -1,4 +1,4 @@
1
- Server side component for monitoring WebRTC stack
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
- // see full config in Configuration section
26
+ defaultServiceId: 'my-service-name',
27
+ defaultMediaUnitId: 'my-reporting-component',
29
28
  });
30
29
 
31
- const clientSource = observer.createClientSource({
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 clientSample: ClientSample; // Receive your samples, for example, from a WebSocket
38
-
39
- clientSource.accept(clientSample);
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
- ## API documentation
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
- https://observertc.org/docs/api/observer-js
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
@@ -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":"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"}
1
+ {"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":"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"}
@@ -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
  }
@@ -10,6 +10,7 @@ export type ObservedOutboundTrackModel<K extends MediaKind> = {
10
10
  sfuStreamId?: string;
11
11
  };
12
12
  export type ObservedOutboundTrackEvents = {
13
+ qualitylimitationchanged: [string];
13
14
  update: [
14
15
  {
15
16
  elapsedTimeInMs: number;
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"Observer.d.ts","sourceRoot":"","sources":["../src/Observer.ts"],"names":[],"mappings":"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"}
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,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAM/D,MAAM,MAAM,cAAc,GAAG;IAC5B,SAAS,EAAE,CAAC,YAAY,CAAC,CAAC;IAC1B,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC;IACxB,SAAS,EAAE,CAAC,mBAAmB,CAAC,CAAC;IACjC,OAAO,EAAE,EAAE,CAAC;CACZ,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAE5B;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAE3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3C,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,CAAC,OAAO,WAAW,QAAQ;IAChC,EAAE,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACnG,GAAG,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACpG,IAAI,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACrG,IAAI,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CACpF;AAED,qBAAa,QAAS,SAAQ,YAAY;aA2BxB,MAAM,EAAE,cAAc;WA1BzB,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,QAAQ;IAkBvE,SAAgB,OAAO,mBAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsF;IAEhH,OAAO,CAAC,YAAY,CAAC,CAAgC;IACrD,OAAO,CAAC,OAAO,CAAS;gBAEP,MAAM,EAAE,cAAc;IAyBhC,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpF,MAAM,EAAE,SAAS,CAAC,iBAAiB,EAAE,WAAW,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1I,YAAY,CAAC,CAAC,CAAC;IA2CX,iBAAiB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzF,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,OAAO,GACd,WAAW,CAAC,OAAO,CAAC;IAoBhB,wBAAwB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG;QAAE,qBAAqB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB;IAkCrH,sBAAsB;IA+C7B,IAAW,aAAa,IAAI,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAE5D;IAED,IAAW,YAAY,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAE1D;IAED,IAAW,MAAM,YAEhB;IAEM,KAAK;IAWZ,OAAO,CAAC,YAAY;CAepB"}
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":"AAEA,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;CACjD"}
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;CACpB;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,OAAO,EAAE,aAAa,EAAE,CAAC;CACzB"}
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,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC;CACvB,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;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAE7D,OAAO,CAAC,OAAO,CAAS;;IAKjB,OAAO,CAAC,IAAI,EAAE,YAAY;IAyBjC,OAAO,CAAC,UAAU;IAoCX,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU3D,IAAW,MAAM,YAEhB;IAEM,KAAK;CAMZ"}
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) => this._addClient(constCallSummary, 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;AAGnE,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,MAAM,YAEhB;IAEM,KAAK;CAMZ"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@observertc/observer-js",
3
- "version": "0.40.11-beta",
3
+ "version": "0.40.13",
4
4
  "description": "Server Side NodeJS Library for processing ObserveRTC Samples",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -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 { appData, started = Date.now(), ...model } = config;
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) => this._addClient(constCallSummary, client);
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
  }