@observertc/observer-js 0.40.11-beta → 0.40.12

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,13 @@ 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
+ }
7
14
  export type ObservedClientModel = {
8
15
  clientId: string;
9
16
  mediaUnitId: string;
@@ -47,6 +54,7 @@ export type ObservedClientEvents = {
47
54
  remoteCandidate: IceRemoteCandidate;
48
55
  }
49
56
  ];
57
+ issue: [ClientIssue];
50
58
  usingturn: [boolean];
51
59
  usermediaerror: [string];
52
60
  };
@@ -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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"}
@@ -229,6 +229,16 @@ class ObservedClient extends events_1.EventEmitter {
229
229
  this._left = timestamp;
230
230
  break;
231
231
  }
232
+ case CallEventType_1.CallEventType.CLIENT_ISSUE: {
233
+ const severity = callEvent.value ? callEvent.value : 'minor';
234
+ this.emit('issue', {
235
+ severity,
236
+ timestamp: timestamp !== null && timestamp !== void 0 ? timestamp : Date.now(),
237
+ description: callEvent.message,
238
+ peerConnectionId: callEvent.peerConnectionId,
239
+ trackId: callEvent.mediaTrackId,
240
+ });
241
+ }
232
242
  }
233
243
  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
244
  }
@@ -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":"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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":"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;
@@ -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,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;IAyBjC,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
  }
@@ -44,6 +45,8 @@ class CallSummaryMonitor extends events_1.EventEmitter {
44
45
  ewmaRttInMs: 0,
45
46
  joined: client.created,
46
47
  userId: client.userId,
48
+ usedTurn: false,
49
+ issues: [],
47
50
  };
48
51
  callSummary.clients.push(clientSummary);
49
52
  const updateClient = () => {
@@ -51,7 +54,29 @@ class CallSummaryMonitor extends events_1.EventEmitter {
51
54
  clientSummary.ewmaRttInMs = (clientSummary.ewmaRttInMs * 0.9) + (client.avgRttInMs * 0.1);
52
55
  }
53
56
  };
57
+ const onUsingTurn = (usingTurn) => {
58
+ clientSummary.usedTurn || (clientSummary.usedTurn = usingTurn);
59
+ };
60
+ const onIssue = (issue) => {
61
+ clientSummary.issues.push(issue);
62
+ };
63
+ const onUserMediaError = (error) => {
64
+ if (!this.config.detectUserMediaIssues)
65
+ return;
66
+ // maybe the client also sned this issue, in which case the timestamp can be more accurate
67
+ const alreadyDetected = clientSummary.issues.find((issue) => issue.severity === 'critical' && issue.description === error);
68
+ !alreadyDetected && clientSummary.issues.push({
69
+ severity: 'critical',
70
+ timestamp: Date.now(),
71
+ description: error,
72
+ });
73
+ };
74
+ const onNewPeerConnection = (peerConnection) => this._addPeerConnection(clientSummary, peerConnection);
54
75
  client.on('update', updateClient);
76
+ client.on('usingturn', onUsingTurn);
77
+ client.on('issue', onIssue);
78
+ client.on('usermediaerror', onUserMediaError);
79
+ client.on('newpeerconnection', onNewPeerConnection);
55
80
  client.once('close', () => {
56
81
  const elapsedTimeInS = (Date.now() - client.created) / 1000;
57
82
  clientSummary.avgInboundAudioBitrate = (client.totalReceivedBytes * 8) / elapsedTimeInS;
@@ -60,7 +85,36 @@ class CallSummaryMonitor extends events_1.EventEmitter {
60
85
  clientSummary.avgOutboundVideoBitrate = (client.totalSentBytes * 8) / elapsedTimeInS;
61
86
  clientSummary.durationInMs = elapsedTimeInS * 1000;
62
87
  client.off('update', updateClient);
88
+ client.off('usingturn', onUsingTurn);
89
+ client.off('issue', onIssue);
90
+ client.off('usermediaerror', onUserMediaError);
91
+ client.off('newpeerconnection', onNewPeerConnection);
92
+ callSummary.durationInMs += clientSummary.durationInMs;
93
+ });
94
+ }
95
+ _addPeerConnection(clientSummary, peerConnection) {
96
+ const onOutboundVideoTrack = (track) => this._addOutboundVideoTrack(clientSummary, track);
97
+ peerConnection.on('newoutboundvideotrack', onOutboundVideoTrack);
98
+ peerConnection.once('close', () => {
99
+ peerConnection.off('newoutboundvideotrack', onOutboundVideoTrack);
100
+ });
101
+ }
102
+ _addOutboundVideoTrack(clientSummary, track) {
103
+ const onQualityLimitationChanged = (reason) => {
104
+ if (!this.config.detectMediaTrackQualityLimitationIssues)
105
+ return;
106
+ clientSummary.issues.push({
107
+ severity: 'minor',
108
+ timestamp: Date.now(),
109
+ description: reason,
110
+ peerConnectionId: track.peerConnectionId,
111
+ trackId: track.trackId,
112
+ });
113
+ };
114
+ track.once('close', () => {
115
+ track.off('qualitylimitationchanged', onQualityLimitationChanged);
63
116
  });
117
+ track.on('qualitylimitationchanged', onQualityLimitationChanged);
64
118
  }
65
119
  takeSummary(callId) {
66
120
  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.12",
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,14 @@ 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
+ }
22
+
15
23
  export type ObservedClientModel= {
16
24
  clientId: string;
17
25
  mediaUnitId: string;
@@ -46,6 +54,7 @@ export type ObservedClientEvents = {
46
54
  localCandidate: IceLocalCandidate,
47
55
  remoteCandidate: IceRemoteCandidate,
48
56
  }],
57
+ issue: [ClientIssue],
49
58
  usingturn: [boolean],
50
59
  usermediaerror: [string],
51
60
  };
@@ -393,6 +402,17 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
393
402
  this._left = timestamp;
394
403
  break;
395
404
  }
405
+ case CallEventType.CLIENT_ISSUE: {
406
+ const severity = callEvent.value ? callEvent.value as ClientIssue['severity'] : 'minor';
407
+
408
+ this.emit('issue', {
409
+ severity,
410
+ timestamp: timestamp ?? Date.now(),
411
+ description: callEvent.message,
412
+ peerConnectionId: callEvent.peerConnectionId,
413
+ trackId: callEvent.mediaTrackId,
414
+ });
415
+ }
396
416
  }
397
417
  this.reports.addCallEventReport({
398
418
  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 {
@@ -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
 
@@ -50,7 +59,7 @@ export class CallSummaryMonitor extends EventEmitter {
50
59
 
51
60
  private _addClient(callSummary: CallSummary, client: ObservedClient) {
52
61
  if (this.closed) return;
53
- const clientSummary = {
62
+ const clientSummary: ClientSummary = {
54
63
  clientId: client.clientId,
55
64
  mediaUnitId: client.mediaUnitId,
56
65
  durationInMs: 0,
@@ -61,6 +70,8 @@ export class CallSummaryMonitor extends EventEmitter {
61
70
  ewmaRttInMs: 0,
62
71
  joined: client.created,
63
72
  userId: client.userId,
73
+ usedTurn: false,
74
+ issues: [],
64
75
  };
65
76
 
66
77
  callSummary.clients.push(clientSummary);
@@ -71,7 +82,34 @@ export class CallSummaryMonitor extends EventEmitter {
71
82
  }
72
83
  };
73
84
 
85
+ const onUsingTurn = (usingTurn: boolean) => {
86
+ clientSummary.usedTurn ||= usingTurn;
87
+ };
88
+
89
+ const onIssue = (issue: ClientIssue) => {
90
+ clientSummary.issues.push(issue);
91
+ };
92
+
93
+ const onUserMediaError = (error: string) => {
94
+ if (!this.config.detectUserMediaIssues) return;
95
+
96
+ // maybe the client also sned this issue, in which case the timestamp can be more accurate
97
+ const alreadyDetected = clientSummary.issues.find((issue) => issue.severity === 'critical' && issue.description === error);
98
+
99
+ !alreadyDetected && clientSummary.issues.push({
100
+ severity: 'critical',
101
+ timestamp: Date.now(),
102
+ description: error,
103
+ });
104
+ };
105
+
106
+ const onNewPeerConnection = (peerConnection: ObservedPeerConnection) => this._addPeerConnection(clientSummary, peerConnection);
107
+
74
108
  client.on('update', updateClient);
109
+ client.on('usingturn', onUsingTurn);
110
+ client.on('issue', onIssue);
111
+ client.on('usermediaerror', onUserMediaError);
112
+ client.on('newpeerconnection', onNewPeerConnection);
75
113
  client.once('close', () => {
76
114
  const elapsedTimeInS = (Date.now() - client.created) / 1000;
77
115
 
@@ -81,7 +119,41 @@ export class CallSummaryMonitor extends EventEmitter {
81
119
  clientSummary.avgOutboundVideoBitrate = (client.totalSentBytes * 8) / elapsedTimeInS;
82
120
  clientSummary.durationInMs = elapsedTimeInS * 1000;
83
121
  client.off('update', updateClient);
122
+ client.off('usingturn', onUsingTurn);
123
+ client.off('issue', onIssue);
124
+ client.off('usermediaerror', onUserMediaError);
125
+ client.off('newpeerconnection', onNewPeerConnection);
126
+
127
+ callSummary.durationInMs += clientSummary.durationInMs;
128
+ });
129
+ }
130
+
131
+ private _addPeerConnection(clientSummary: ClientSummary, peerConnection: ObservedPeerConnection) {
132
+ const onOutboundVideoTrack = (track: ObservedOutboundTrack<'video'>) => this._addOutboundVideoTrack(clientSummary, track);
133
+
134
+ peerConnection.on('newoutboundvideotrack', onOutboundVideoTrack);
135
+ peerConnection.once('close', () => {
136
+ peerConnection.off('newoutboundvideotrack', onOutboundVideoTrack);
137
+ });
138
+ }
139
+
140
+ private _addOutboundVideoTrack(clientSummary: ClientSummary, track: ObservedOutboundTrack<'video'>) {
141
+ const onQualityLimitationChanged = (reason: string) => {
142
+ if (!this.config.detectMediaTrackQualityLimitationIssues) return;
143
+
144
+ clientSummary.issues.push({
145
+ severity: 'minor',
146
+ timestamp: Date.now(),
147
+ description: reason,
148
+ peerConnectionId: track.peerConnectionId,
149
+ trackId: track.trackId,
150
+ });
151
+ };
152
+
153
+ track.once('close', () => {
154
+ track.off('qualitylimitationchanged', onQualityLimitationChanged);
84
155
  });
156
+ track.on('qualitylimitationchanged', onQualityLimitationChanged);
85
157
  }
86
158
 
87
159
  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
  }