@observertc/observer-js 0.30.4-beta → 0.30.6-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/CallSummary.d.ts +22 -0
- package/lib/CallSummary.d.ts.map +1 -0
- package/lib/CallSummary.js +2 -0
- package/lib/ObservedClient.d.ts +9 -2
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +39 -13
- package/lib/ObservedPeerConnection.d.ts +7 -8
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +43 -26
- package/lib/ObservedSfu.d.ts +38 -0
- package/lib/ObservedSfu.d.ts.map +1 -0
- package/lib/ObservedSfu.js +112 -0
- package/package.json +1 -1
- package/src/CallSummary.ts +23 -0
- package/src/ObservedClient.ts +45 -13
- package/src/ObservedICE.ts +1 -1
- package/src/ObservedPeerConnection.ts +35 -26
- package/src/ObservedSfu.ts +108 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ClientSummary {
|
|
2
|
+
clientId: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
joined: number;
|
|
5
|
+
left: number;
|
|
6
|
+
}
|
|
7
|
+
export interface DetectedIssues {
|
|
8
|
+
clientId: string;
|
|
9
|
+
issue: string;
|
|
10
|
+
started: number;
|
|
11
|
+
ended: number;
|
|
12
|
+
}
|
|
13
|
+
export interface CallSummary {
|
|
14
|
+
serviceId: string;
|
|
15
|
+
roomId: string;
|
|
16
|
+
callId: string;
|
|
17
|
+
started: number;
|
|
18
|
+
ended: number;
|
|
19
|
+
clients: ClientSummary[];
|
|
20
|
+
detectedIssues: DetectedIssues[];
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=CallSummary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CallSummary.d.ts","sourceRoot":"","sources":["../src/CallSummary.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACd;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,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,cAAc,EAAE,cAAc,EAAE,CAAC;CACjC"}
|
package/lib/ObservedClient.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export type ObservedClientEvents = {
|
|
|
47
47
|
remoteCandidate: IceRemoteCandidate;
|
|
48
48
|
}
|
|
49
49
|
];
|
|
50
|
+
usingturn: [boolean];
|
|
50
51
|
};
|
|
51
52
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
52
53
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
@@ -70,7 +71,11 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
70
71
|
private _acceptedSample;
|
|
71
72
|
private _processingSample;
|
|
72
73
|
private _timeZoneOffsetInHours?;
|
|
73
|
-
private
|
|
74
|
+
private _left?;
|
|
75
|
+
private _numberOfAcceptedUpdates;
|
|
76
|
+
usingTURN: boolean;
|
|
77
|
+
availableOutgoingBitrate: number;
|
|
78
|
+
availableIncomingBitrate: number;
|
|
74
79
|
totalInboundPacketsLost: number;
|
|
75
80
|
totalInboundPacketsReceived: number;
|
|
76
81
|
totalOutboundPacketsSent: number;
|
|
@@ -78,7 +83,7 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
78
83
|
totalDataChannelBytesReceived: number;
|
|
79
84
|
totalSentBytes: number;
|
|
80
85
|
totalReceivedBytes: number;
|
|
81
|
-
|
|
86
|
+
avgRttInMs?: number;
|
|
82
87
|
sendingBitrate?: number;
|
|
83
88
|
receivingBitrate?: number;
|
|
84
89
|
readonly mediaDevices: string[];
|
|
@@ -103,7 +108,9 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
103
108
|
get platform(): Samples_ClientSample_Platform | undefined;
|
|
104
109
|
get acceptedSample(): number;
|
|
105
110
|
get updated(): number;
|
|
111
|
+
get numberOfAcceptedUpdates(): number;
|
|
106
112
|
get uptimeInMs(): number;
|
|
113
|
+
get durationInMs(): number | undefined;
|
|
107
114
|
get closed(): boolean;
|
|
108
115
|
get peerConnections(): ReadonlyMap<string, ObservedPeerConnection>;
|
|
109
116
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,OAAO,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAOrK,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACrG,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,MAAM,EAAE,EAAE,CAAC;IACX,KAAK,EAAE,EAAE,CAAC;IACV,4BAA4B,EAAE,EAAE,CAAC;IACjC,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;
|
|
1
|
+
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":"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"}
|
package/lib/ObservedClient.js
CHANGED
|
@@ -78,7 +78,6 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
78
78
|
if (config.addJoinAndDetachClientEvent) {
|
|
79
79
|
call.reports.addCallEventReport((0, callEventReports_1.createClientJoinedEventReport)(call.serviceId, result.mediaUnitId, call.roomId, call.callId, result.clientId, result.joined, result.userId, result.marker));
|
|
80
80
|
}
|
|
81
|
-
result.once('close', () => result._addClientLeftReport());
|
|
82
81
|
return result;
|
|
83
82
|
});
|
|
84
83
|
}
|
|
@@ -96,7 +95,10 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
96
95
|
this._updated = Date.now();
|
|
97
96
|
this._acceptedSample = 0;
|
|
98
97
|
this._processingSample = 0;
|
|
99
|
-
this.
|
|
98
|
+
this._numberOfAcceptedUpdates = 0;
|
|
99
|
+
this.usingTURN = false;
|
|
100
|
+
this.availableOutgoingBitrate = 0;
|
|
101
|
+
this.availableIncomingBitrate = 0;
|
|
100
102
|
this.totalInboundPacketsLost = 0;
|
|
101
103
|
this.totalInboundPacketsReceived = 0;
|
|
102
104
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -164,9 +166,17 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
164
166
|
get updated() {
|
|
165
167
|
return this._updated;
|
|
166
168
|
}
|
|
169
|
+
get numberOfAcceptedUpdates() {
|
|
170
|
+
return this._numberOfAcceptedUpdates;
|
|
171
|
+
}
|
|
167
172
|
get uptimeInMs() {
|
|
168
173
|
return this._updated - this.created;
|
|
169
174
|
}
|
|
175
|
+
get durationInMs() {
|
|
176
|
+
if (!this._left)
|
|
177
|
+
return;
|
|
178
|
+
return this._left - this.joined;
|
|
179
|
+
}
|
|
170
180
|
get closed() {
|
|
171
181
|
return this._closed;
|
|
172
182
|
}
|
|
@@ -177,6 +187,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
177
187
|
if (this._closed)
|
|
178
188
|
return;
|
|
179
189
|
this._closed = true;
|
|
190
|
+
!this._left && this._addClientLeftReport();
|
|
180
191
|
Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
181
192
|
this._execute(() => this._storageProvider.clientStorage.remove(this.clientId))
|
|
182
193
|
.catch(() => void 0)
|
|
@@ -195,7 +206,8 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
195
206
|
this.emit('processing-sample-overflow');
|
|
196
207
|
}
|
|
197
208
|
return this._execute(() => __awaiter(this, void 0, void 0, function* () {
|
|
198
|
-
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;
|
|
209
|
+
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;
|
|
210
|
+
++this._numberOfAcceptedUpdates;
|
|
199
211
|
for (const peerConnection of this._peerConnections.values()) {
|
|
200
212
|
if (peerConnection.closed)
|
|
201
213
|
continue;
|
|
@@ -283,15 +295,19 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
283
295
|
extensionType: extensionStats.type,
|
|
284
296
|
});
|
|
285
297
|
}
|
|
286
|
-
for (let
|
|
287
|
-
const { timestamp } =
|
|
298
|
+
for (let _19 of (_p = sample.customCallEvents) !== null && _p !== void 0 ? _p : []) {
|
|
299
|
+
const { timestamp } = _19, callEvent = __rest(_19, ["timestamp"]);
|
|
288
300
|
const peerConnection = this._peerConnections.get((_q = callEvent.peerConnectionId) !== null && _q !== void 0 ? _q : '');
|
|
289
301
|
switch (callEvent.name) {
|
|
290
302
|
case CallEventType_1.CallEventType.CLIENT_JOINED:
|
|
291
|
-
|
|
303
|
+
if (timestamp) {
|
|
304
|
+
this.created = timestamp;
|
|
305
|
+
this._model.joined = BigInt(timestamp);
|
|
306
|
+
yield this._save();
|
|
307
|
+
}
|
|
292
308
|
break;
|
|
293
309
|
case CallEventType_1.CallEventType.CLIENT_LEFT:
|
|
294
|
-
this.
|
|
310
|
+
this._left = timestamp !== null && timestamp !== void 0 ? timestamp : Date.now();
|
|
295
311
|
break;
|
|
296
312
|
case CallEventType_1.CallEventType.ICE_CONNECTION_STATE_CHANGED:
|
|
297
313
|
peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
|
|
@@ -384,7 +400,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
384
400
|
for (const transport of (_x = sample.pcTransports) !== null && _x !== void 0 ? _x : []) {
|
|
385
401
|
try {
|
|
386
402
|
const peerConnection = (_y = this._peerConnections.get(transport.transportId)) !== null && _y !== void 0 ? _y : yield this._createPeerConnection(transport.peerConnectionId);
|
|
387
|
-
peerConnection.update(transport, sample.timestamp);
|
|
403
|
+
yield peerConnection.update(transport, sample.timestamp);
|
|
388
404
|
}
|
|
389
405
|
catch (err) {
|
|
390
406
|
logger.error(`Error creating peer connection: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
@@ -544,6 +560,10 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
544
560
|
}
|
|
545
561
|
}
|
|
546
562
|
// update metrics
|
|
563
|
+
const wasUsingTURN = this.usingTURN;
|
|
564
|
+
this.usingTURN = false;
|
|
565
|
+
this.availableIncomingBitrate = 0;
|
|
566
|
+
this.availableOutgoingBitrate = 0;
|
|
547
567
|
this.totalSentBytes = 0;
|
|
548
568
|
this.totalReceivedBytes = 0;
|
|
549
569
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -553,7 +573,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
553
573
|
this.totalDataChannelBytesReceived = 0;
|
|
554
574
|
this.sendingBitrate = 0;
|
|
555
575
|
this.receivingBitrate = 0;
|
|
556
|
-
let
|
|
576
|
+
let sumRttInMs = 0;
|
|
557
577
|
for (const peerConnection of this._peerConnections.values()) {
|
|
558
578
|
if (peerConnection.closed)
|
|
559
579
|
continue;
|
|
@@ -567,11 +587,17 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
567
587
|
this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
|
|
568
588
|
this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
|
|
569
589
|
this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
|
|
570
|
-
|
|
590
|
+
this.availableIncomingBitrate += (_16 = peerConnection.availableIncomingBitrate) !== null && _16 !== void 0 ? _16 : 0;
|
|
591
|
+
this.availableOutgoingBitrate += (_17 = peerConnection.availableOutgoingBitrate) !== null && _17 !== void 0 ? _17 : 0;
|
|
592
|
+
sumRttInMs += (_18 = peerConnection.avgRttInMs) !== null && _18 !== void 0 ? _18 : 0;
|
|
593
|
+
if (peerConnection.usingTURN)
|
|
594
|
+
this.usingTURN = true;
|
|
571
595
|
}
|
|
572
|
-
this.
|
|
596
|
+
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
573
597
|
if (executeSave)
|
|
574
598
|
yield this._save();
|
|
599
|
+
if (wasUsingTURN !== this.usingTURN)
|
|
600
|
+
this.emit('usingturn', this.usingTURN);
|
|
575
601
|
this._updated = sample.timestamp;
|
|
576
602
|
this.emit('update');
|
|
577
603
|
})).finally(() => {
|
|
@@ -618,9 +644,9 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
618
644
|
});
|
|
619
645
|
}
|
|
620
646
|
_addClientLeftReport() {
|
|
621
|
-
if (this.
|
|
647
|
+
if (this._left)
|
|
622
648
|
return;
|
|
623
|
-
this.
|
|
649
|
+
this._left = Date.now();
|
|
624
650
|
this.reports.addCallEventReport((0, callEventReports_1.createClientLeftEventReport)(this.serviceId, this.mediaUnitId, this.roomId, this.callId, this.clientId, this.joined, this.userId, this.marker));
|
|
625
651
|
}
|
|
626
652
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import { StorageProvider } from './storages/StorageProvider';
|
|
4
|
-
import {
|
|
4
|
+
import { PeerConnectionTransport } from '@observertc/sample-schemas-js';
|
|
5
5
|
import { ObservedClient } from './ObservedClient';
|
|
6
6
|
import { ObservedInboundTrack, ObservedInboundTrackConfig } from './ObservedInboundTrack';
|
|
7
7
|
import { ObservedOutboundTrack, ObservedOutboundTrackConfig } from './ObservedOutboundTrack';
|
|
@@ -60,12 +60,10 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
60
60
|
sendingVideoBitrate: number;
|
|
61
61
|
receivingAudioBitrate: number;
|
|
62
62
|
receivingVideoBitrate: number;
|
|
63
|
-
|
|
63
|
+
avgRttInMs?: number;
|
|
64
64
|
private _closed;
|
|
65
65
|
private _updated;
|
|
66
66
|
private _sample?;
|
|
67
|
-
private _selectedLocalIceCandidate?;
|
|
68
|
-
private _selectedRemoteIceCandidate?;
|
|
69
67
|
readonly ICE: ObservedICE;
|
|
70
68
|
private readonly _inboundAudioTracks;
|
|
71
69
|
private readonly _inboundVideoTracks;
|
|
@@ -73,12 +71,14 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
73
71
|
private readonly _outboundVideoTracks;
|
|
74
72
|
private readonly _execute;
|
|
75
73
|
private constructor();
|
|
74
|
+
get label(): string | undefined;
|
|
75
|
+
get usingTURN(): boolean;
|
|
76
|
+
get availableOutgoingBitrate(): number | undefined;
|
|
77
|
+
get availableIncomingBitrate(): number | undefined;
|
|
76
78
|
get peerConnectionId(): string;
|
|
77
79
|
get reports(): import("./ReportsCollector").ReportsCollector;
|
|
78
80
|
get stats(): ObservedPeerConnectionStats | undefined;
|
|
79
81
|
get updated(): number;
|
|
80
|
-
get selectedLocalIceCandidate(): IceLocalCandidate | undefined;
|
|
81
|
-
get selectedRemoteIceCandidate(): IceRemoteCandidate | undefined;
|
|
82
82
|
get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundTrack<'audio'>>;
|
|
83
83
|
get inboundVideoTracks(): ReadonlyMap<string, ObservedInboundTrack<'video'>>;
|
|
84
84
|
get outboundAudioTracks(): ReadonlyMap<string, ObservedOutboundTrack<'audio'>>;
|
|
@@ -89,8 +89,7 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
89
89
|
getTrack(trackId: string): ObservedInboundTrack<'audio'> | ObservedInboundTrack<'video'> | ObservedOutboundTrack<'audio'> | ObservedOutboundTrack<'video'> | undefined;
|
|
90
90
|
resetMetrics(): void;
|
|
91
91
|
updateMetrics(): void;
|
|
92
|
-
update(sample: PeerConnectionTransport, timestamp: number): void
|
|
93
|
-
updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate): void;
|
|
92
|
+
update(sample: PeerConnectionTransport, timestamp: number): Promise<void>;
|
|
94
93
|
createInboundAudioTrack(config: Omit<ObservedInboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedInboundTrack<'audio'>>;
|
|
95
94
|
createInboundVideoTrack(config: Omit<ObservedInboundTrackConfig<'video'>, 'kind'>): Promise<ObservedInboundTrack<'video'>>;
|
|
96
95
|
createOutboundAudioTrack(config: Omit<ObservedOutboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedOutboundTrack<'audio'>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedPeerConnection.d.ts","sourceRoot":"","sources":["../src/ObservedPeerConnection.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ObservedPeerConnection.d.ts","sourceRoot":"","sources":["../src/ObservedPeerConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -100,6 +100,21 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
100
100
|
this._outboundVideoTracks = new Map();
|
|
101
101
|
this._execute = (0, SingleExecutor_1.createSingleExecutor)();
|
|
102
102
|
}
|
|
103
|
+
get label() {
|
|
104
|
+
return this._model.label;
|
|
105
|
+
}
|
|
106
|
+
get usingTURN() {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
return ((_b = (_a = this.ICE.selectedRemoteCandidate) === null || _a === void 0 ? void 0 : _a.candidateType) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'relay';
|
|
109
|
+
}
|
|
110
|
+
get availableOutgoingBitrate() {
|
|
111
|
+
var _a;
|
|
112
|
+
return (_a = this.ICE.stats) === null || _a === void 0 ? void 0 : _a.availableOutgoingBitrate;
|
|
113
|
+
}
|
|
114
|
+
get availableIncomingBitrate() {
|
|
115
|
+
var _a;
|
|
116
|
+
return (_a = this.ICE.stats) === null || _a === void 0 ? void 0 : _a.availableIncomingBitrate;
|
|
117
|
+
}
|
|
103
118
|
get peerConnectionId() {
|
|
104
119
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
105
120
|
return this._model.peerConnectionId;
|
|
@@ -113,12 +128,6 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
113
128
|
get updated() {
|
|
114
129
|
return this._updated;
|
|
115
130
|
}
|
|
116
|
-
get selectedLocalIceCandidate() {
|
|
117
|
-
return this._selectedLocalIceCandidate;
|
|
118
|
-
}
|
|
119
|
-
get selectedRemoteIceCandidate() {
|
|
120
|
-
return this._selectedRemoteIceCandidate;
|
|
121
|
-
}
|
|
122
131
|
get inboundAudioTracks() {
|
|
123
132
|
return this._inboundAudioTracks;
|
|
124
133
|
}
|
|
@@ -169,6 +178,7 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
169
178
|
this._outboundVideoTracks.forEach((track) => track.resetMetrics());
|
|
170
179
|
}
|
|
171
180
|
updateMetrics() {
|
|
181
|
+
var _a;
|
|
172
182
|
this.totalInboundPacketsLost = 0;
|
|
173
183
|
this.totalInboundPacketsReceived = 0;
|
|
174
184
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -182,7 +192,7 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
182
192
|
this.sendingVideoBitrate = 0;
|
|
183
193
|
this.receivingAudioBitrate = 0;
|
|
184
194
|
this.receivingVideoBitrate = 0;
|
|
185
|
-
let
|
|
195
|
+
let sumRttInMs = 0;
|
|
186
196
|
this._inboundAudioTracks.forEach((track) => {
|
|
187
197
|
var _a;
|
|
188
198
|
this.deltaInboundLostPackets += track.deltaLostPackets;
|
|
@@ -195,7 +205,7 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
195
205
|
this.totalInboundPacketsReceived += track.totalReceivedPackets;
|
|
196
206
|
this.totalReceivedAudioBytes += track.totalBytesReceived;
|
|
197
207
|
this.totalReceivedAudioPacktes += track.totalReceivedPackets;
|
|
198
|
-
|
|
208
|
+
sumRttInMs = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0);
|
|
199
209
|
});
|
|
200
210
|
this._inboundVideoTracks.forEach((track) => {
|
|
201
211
|
var _a;
|
|
@@ -209,7 +219,7 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
209
219
|
this.totalInboundPacketsReceived += track.totalReceivedPackets;
|
|
210
220
|
this.totalReceivedVideoBytes += track.totalBytesReceived;
|
|
211
221
|
this.totalReceivedVideoPackets += track.totalReceivedPackets;
|
|
212
|
-
|
|
222
|
+
sumRttInMs = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0);
|
|
213
223
|
});
|
|
214
224
|
this._outboundAudioTracks.forEach((track) => {
|
|
215
225
|
var _a;
|
|
@@ -221,7 +231,7 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
221
231
|
this.totalOutboundPacketsSent += track.totalSentPackets;
|
|
222
232
|
this.totalSentAudioBytes += track.totalSentBytes;
|
|
223
233
|
this.totalSentAudioPackets += track.totalSentPackets;
|
|
224
|
-
|
|
234
|
+
sumRttInMs = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0);
|
|
225
235
|
});
|
|
226
236
|
this._outboundVideoTracks.forEach((track) => {
|
|
227
237
|
var _a;
|
|
@@ -233,25 +243,32 @@ class ObservedPeerConnection extends events_1.EventEmitter {
|
|
|
233
243
|
this.totalOutboundPacketsSent += track.totalSentPackets;
|
|
234
244
|
this.totalSentVideoBytes += track.totalSentBytes;
|
|
235
245
|
this.totalSentVideoPackets += track.totalSentPackets;
|
|
236
|
-
|
|
246
|
+
sumRttInMs = ((_a = track.rttInMs) !== null && _a !== void 0 ? _a : 0);
|
|
237
247
|
});
|
|
238
|
-
const
|
|
239
|
-
|
|
248
|
+
const iceRttInMs = (_a = this.ICE.stats) === null || _a === void 0 ? void 0 : _a.currentRoundTripTime;
|
|
249
|
+
let nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
|
|
250
|
+
if (iceRttInMs) {
|
|
251
|
+
sumRttInMs += iceRttInMs;
|
|
252
|
+
nrOfBelongings += 1;
|
|
253
|
+
}
|
|
254
|
+
this.avgRttInMs = 0 < nrOfBelongings ? sumRttInMs / nrOfBelongings : undefined;
|
|
240
255
|
}
|
|
241
256
|
update(sample, timestamp) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
258
|
+
if (this._closed)
|
|
259
|
+
return;
|
|
260
|
+
if (sample.peerConnectionId !== this._model.peerConnectionId)
|
|
261
|
+
throw new Error(`TransportId mismatch. PeerConnectionId: ${this._model.peerConnectionId} TransportId: ${sample.transportId}`);
|
|
262
|
+
this._sample = sample;
|
|
263
|
+
if (this._model.label !== sample.label) {
|
|
264
|
+
this._model.label = sample.label;
|
|
265
|
+
yield this._save().catch(() => void 0);
|
|
266
|
+
}
|
|
267
|
+
const report = Object.assign(Object.assign({ serviceId: this.client.call.serviceId, roomId: this.client.call.roomId, callId: this.client.call.callId, clientId: this.client.clientId, userId: this.client.userId, mediaUnitId: this.client.mediaUnitId }, sample), { timestamp, sampleSeq: -1 });
|
|
268
|
+
this.reports.addPeerConnectionTransportReports(report);
|
|
269
|
+
this._updated = timestamp;
|
|
270
|
+
this.emit('update');
|
|
271
|
+
});
|
|
255
272
|
}
|
|
256
273
|
createInboundAudioTrack(config) {
|
|
257
274
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { Observer } from './Observer';
|
|
4
|
+
import { SfuSample } from '@observertc/sample-schemas-js';
|
|
5
|
+
export type ObservedSfuConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
6
|
+
serviceId: string;
|
|
7
|
+
sfuId: string;
|
|
8
|
+
joined?: number;
|
|
9
|
+
appData: AppData;
|
|
10
|
+
};
|
|
11
|
+
export type ObservedSfuEvents = {
|
|
12
|
+
update: [];
|
|
13
|
+
close: [];
|
|
14
|
+
};
|
|
15
|
+
export declare interface ObservedSfu {
|
|
16
|
+
on<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
17
|
+
off<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
18
|
+
once<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
19
|
+
emit<U extends keyof ObservedSfuEvents>(event: U, ...args: ObservedSfuEvents[U]): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class ObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
22
|
+
private readonly _model;
|
|
23
|
+
readonly observer: Observer;
|
|
24
|
+
readonly appData: AppData;
|
|
25
|
+
static create<T extends Record<string, unknown> = Record<string, unknown>>(config: ObservedSfuConfig<T>, observer: Observer): Promise<ObservedSfu<T>>;
|
|
26
|
+
private _updated;
|
|
27
|
+
private readonly _execute;
|
|
28
|
+
private _closed;
|
|
29
|
+
private constructor();
|
|
30
|
+
get serviceId(): string;
|
|
31
|
+
get sfuId(): string;
|
|
32
|
+
get joined(): number;
|
|
33
|
+
get closed(): boolean;
|
|
34
|
+
close(): void;
|
|
35
|
+
update(sample: SfuSample): Promise<void>;
|
|
36
|
+
private _save;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ObservedSfu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObservedSfu.d.ts","sourceRoot":"","sources":["../src/ObservedSfu.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE1D,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAClG,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,MAAM,EAAE,EAAE,CAAC;IACX,KAAK,EAAE,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,CAAC,OAAO,WAAW,WAAW;IACnC,EAAE,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IACzG,GAAG,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1G,IAAI,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3G,IAAI,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CAC1F;AAED,qBAAa,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,SAAQ,YAAY;IA8B9G,OAAO,CAAC,QAAQ,CAAC,MAAM;aACP,QAAQ,EAAE,QAAQ;aAClB,OAAO,EAAE,OAAO;WA/Bb,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrF,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC5B,QAAQ,EAAE,QAAQ;IAsBnB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;IACnD,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO;IAQP,IAAW,SAAS,WAGnB;IAED,IAAW,KAAK,WAGf;IAED,IAAW,MAAM,WAGhB;IAED,IAAW,MAAM,YAEhB;IAEM,KAAK;IAMC,MAAM,CAAC,MAAM,EAAE,SAAS;YAWvB,KAAK;CASnB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.ObservedSfu = void 0;
|
|
36
|
+
const events_1 = require("events");
|
|
37
|
+
const Models = __importStar(require("./models/Models"));
|
|
38
|
+
const SingleExecutor_1 = require("./common/SingleExecutor");
|
|
39
|
+
class ObservedSfu extends events_1.EventEmitter {
|
|
40
|
+
static create(config, observer) {
|
|
41
|
+
var _a;
|
|
42
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const model = new Models.Sfu({
|
|
44
|
+
serviceId: config.serviceId,
|
|
45
|
+
sfuId: config.sfuId,
|
|
46
|
+
sfuTransportIds: [],
|
|
47
|
+
joined: BigInt((_a = config.joined) !== null && _a !== void 0 ? _a : Date.now()),
|
|
48
|
+
});
|
|
49
|
+
const result = new ObservedSfu(model, observer, config.appData);
|
|
50
|
+
const alreadyInserted = yield observer.storage.sfuStorage.insert(config.sfuId, model);
|
|
51
|
+
if (alreadyInserted)
|
|
52
|
+
throw new Error(`Sfu with id ${config.sfuId} already exists`);
|
|
53
|
+
return result;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
constructor(_model, observer, appData) {
|
|
57
|
+
super();
|
|
58
|
+
this._model = _model;
|
|
59
|
+
this.observer = observer;
|
|
60
|
+
this.appData = appData;
|
|
61
|
+
this._updated = Date.now();
|
|
62
|
+
this._execute = (0, SingleExecutor_1.createSingleExecutor)();
|
|
63
|
+
this._closed = false;
|
|
64
|
+
}
|
|
65
|
+
get serviceId() {
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
67
|
+
return this._model.serviceId;
|
|
68
|
+
}
|
|
69
|
+
get sfuId() {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
71
|
+
return this._model.sfuId;
|
|
72
|
+
}
|
|
73
|
+
get joined() {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
75
|
+
return Number(this._model.joined);
|
|
76
|
+
}
|
|
77
|
+
get closed() {
|
|
78
|
+
return this._closed;
|
|
79
|
+
}
|
|
80
|
+
close() {
|
|
81
|
+
if (this._closed)
|
|
82
|
+
return;
|
|
83
|
+
this._closed = true;
|
|
84
|
+
this.emit('close');
|
|
85
|
+
}
|
|
86
|
+
update(sample) {
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
if (this._closed)
|
|
89
|
+
throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
90
|
+
if (sample.sfuId !== this.sfuId)
|
|
91
|
+
throw new Error(`Sfu ${this.sfuId} is not the same as sample.sfuId`);
|
|
92
|
+
return this._execute(() => __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
if (this._closed)
|
|
94
|
+
throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
95
|
+
yield this._save();
|
|
96
|
+
this.emit('update');
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
_save() {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
if (this._closed)
|
|
103
|
+
throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
104
|
+
return this._execute(() => __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
if (this._closed)
|
|
106
|
+
throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
107
|
+
yield this.observer.storage.sfuStorage.set(this.sfuId, this._model);
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.ObservedSfu = ObservedSfu;
|
package/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ClientSummary {
|
|
2
|
+
clientId: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
joined: number;
|
|
5
|
+
left: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface DetectedIssues {
|
|
9
|
+
clientId: string,
|
|
10
|
+
issue: string,
|
|
11
|
+
started: number,
|
|
12
|
+
ended: number,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CallSummary {
|
|
16
|
+
serviceId: string;
|
|
17
|
+
roomId: string;
|
|
18
|
+
callId: string;
|
|
19
|
+
started: number;
|
|
20
|
+
ended: number;
|
|
21
|
+
clients: ClientSummary[];
|
|
22
|
+
detectedIssues: DetectedIssues[];
|
|
23
|
+
}
|
package/src/ObservedClient.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type ObservedClientEvents = {
|
|
|
51
51
|
localCandidate: IceLocalCandidate,
|
|
52
52
|
remoteCandidate: IceRemoteCandidate,
|
|
53
53
|
}],
|
|
54
|
+
usingturn: [boolean],
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
@@ -100,8 +101,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
100
101
|
));
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
result.once('close', () => result._addClientLeftReport());
|
|
104
|
-
|
|
105
104
|
return result;
|
|
106
105
|
}
|
|
107
106
|
|
|
@@ -116,8 +115,12 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
116
115
|
private _acceptedSample = 0;
|
|
117
116
|
private _processingSample = 0;
|
|
118
117
|
private _timeZoneOffsetInHours?: number;
|
|
119
|
-
private
|
|
118
|
+
private _left?: number;
|
|
119
|
+
private _numberOfAcceptedUpdates = 0;
|
|
120
120
|
|
|
121
|
+
public usingTURN = false;
|
|
122
|
+
public availableOutgoingBitrate = 0;
|
|
123
|
+
public availableIncomingBitrate = 0;
|
|
121
124
|
public totalInboundPacketsLost = 0;
|
|
122
125
|
public totalInboundPacketsReceived = 0;
|
|
123
126
|
public totalOutboundPacketsSent = 0;
|
|
@@ -126,7 +129,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
126
129
|
public totalSentBytes = 0;
|
|
127
130
|
public totalReceivedBytes = 0;
|
|
128
131
|
|
|
129
|
-
public
|
|
132
|
+
public avgRttInMs?: number;
|
|
130
133
|
public sendingBitrate?: number;
|
|
131
134
|
public receivingBitrate?: number;
|
|
132
135
|
|
|
@@ -136,7 +139,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
136
139
|
|
|
137
140
|
public readonly ωpendingCreatedTracksTimestamp = new Map<string, number>();
|
|
138
141
|
public readonly ωpendingCreatedPeerConnectionTimestamp = new Map<string, number>();
|
|
139
|
-
|
|
142
|
+
|
|
140
143
|
private constructor(
|
|
141
144
|
private readonly _model: Models.Client,
|
|
142
145
|
public readonly call: ObservedCall,
|
|
@@ -216,10 +219,20 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
216
219
|
return this._updated;
|
|
217
220
|
}
|
|
218
221
|
|
|
222
|
+
public get numberOfAcceptedUpdates() {
|
|
223
|
+
return this._numberOfAcceptedUpdates;
|
|
224
|
+
}
|
|
225
|
+
|
|
219
226
|
public get uptimeInMs() {
|
|
220
227
|
return this._updated - this.created;
|
|
221
228
|
}
|
|
222
229
|
|
|
230
|
+
public get durationInMs(): number | undefined {
|
|
231
|
+
if (!this._left) return;
|
|
232
|
+
|
|
233
|
+
return this._left - this.joined;
|
|
234
|
+
}
|
|
235
|
+
|
|
223
236
|
public get closed() {
|
|
224
237
|
return this._closed;
|
|
225
238
|
}
|
|
@@ -232,6 +245,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
232
245
|
if (this._closed) return;
|
|
233
246
|
this._closed = true;
|
|
234
247
|
|
|
248
|
+
!this._left && this._addClientLeftReport();
|
|
249
|
+
|
|
235
250
|
Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
236
251
|
|
|
237
252
|
this._execute(() => this._storageProvider.clientStorage.remove(this.clientId))
|
|
@@ -252,6 +267,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
252
267
|
}
|
|
253
268
|
|
|
254
269
|
return this._execute(async () => {
|
|
270
|
+
++this._numberOfAcceptedUpdates;
|
|
271
|
+
|
|
255
272
|
for (const peerConnection of this._peerConnections.values()) {
|
|
256
273
|
if (peerConnection.closed) continue;
|
|
257
274
|
peerConnection.resetMetrics();
|
|
@@ -416,10 +433,15 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
416
433
|
|
|
417
434
|
switch (callEvent.name) {
|
|
418
435
|
case CallEventType.CLIENT_JOINED:
|
|
419
|
-
|
|
436
|
+
if (timestamp) {
|
|
437
|
+
this.created = timestamp;
|
|
438
|
+
this._model.joined = BigInt(timestamp);
|
|
439
|
+
await this._save();
|
|
440
|
+
}
|
|
441
|
+
|
|
420
442
|
break;
|
|
421
443
|
case CallEventType.CLIENT_LEFT:
|
|
422
|
-
this.
|
|
444
|
+
this._left = timestamp ?? Date.now();
|
|
423
445
|
break;
|
|
424
446
|
case CallEventType.ICE_CONNECTION_STATE_CHANGED:
|
|
425
447
|
peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
|
|
@@ -564,7 +586,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
564
586
|
try {
|
|
565
587
|
const peerConnection = this._peerConnections.get(transport.transportId) ?? await this._createPeerConnection(transport.peerConnectionId);
|
|
566
588
|
|
|
567
|
-
peerConnection.update(transport, sample.timestamp);
|
|
589
|
+
await peerConnection.update(transport, sample.timestamp);
|
|
568
590
|
} catch (err) {
|
|
569
591
|
logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
|
|
570
592
|
}
|
|
@@ -759,6 +781,11 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
759
781
|
}
|
|
760
782
|
|
|
761
783
|
// update metrics
|
|
784
|
+
const wasUsingTURN = this.usingTURN;
|
|
785
|
+
|
|
786
|
+
this.usingTURN = false;
|
|
787
|
+
this.availableIncomingBitrate = 0;
|
|
788
|
+
this.availableOutgoingBitrate = 0;
|
|
762
789
|
this.totalSentBytes = 0;
|
|
763
790
|
this.totalReceivedBytes = 0;
|
|
764
791
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -768,7 +795,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
768
795
|
this.totalDataChannelBytesReceived = 0;
|
|
769
796
|
this.sendingBitrate = 0;
|
|
770
797
|
this.receivingBitrate = 0;
|
|
771
|
-
let
|
|
798
|
+
let sumRttInMs = 0;
|
|
772
799
|
|
|
773
800
|
for (const peerConnection of this._peerConnections.values()) {
|
|
774
801
|
if (peerConnection.closed) continue;
|
|
@@ -782,12 +809,17 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
782
809
|
this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
|
|
783
810
|
this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
|
|
784
811
|
this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
|
|
785
|
-
|
|
812
|
+
this.availableIncomingBitrate += peerConnection.availableIncomingBitrate ?? 0;
|
|
813
|
+
this.availableOutgoingBitrate += peerConnection.availableOutgoingBitrate ?? 0;
|
|
814
|
+
sumRttInMs += peerConnection.avgRttInMs ?? 0;
|
|
815
|
+
|
|
816
|
+
if (peerConnection.usingTURN) this.usingTURN = true;
|
|
786
817
|
}
|
|
787
818
|
|
|
788
|
-
this.
|
|
819
|
+
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
789
820
|
|
|
790
821
|
if (executeSave) await this._save();
|
|
822
|
+
if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
|
|
791
823
|
|
|
792
824
|
this._updated = sample.timestamp;
|
|
793
825
|
this.emit('update');
|
|
@@ -839,8 +871,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
839
871
|
}
|
|
840
872
|
|
|
841
873
|
private _addClientLeftReport() {
|
|
842
|
-
if (this.
|
|
843
|
-
this.
|
|
874
|
+
if (this._left) return;
|
|
875
|
+
this._left = Date.now();
|
|
844
876
|
|
|
845
877
|
this.reports.addCallEventReport(createClientLeftEventReport(
|
|
846
878
|
this.serviceId,
|
package/src/ObservedICE.ts
CHANGED
|
@@ -147,7 +147,7 @@ export class ObservedICE extends EventEmitter {
|
|
|
147
147
|
if (!candidatePair.nominated) return;
|
|
148
148
|
|
|
149
149
|
this._stats = candidatePair;
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
if (
|
|
152
152
|
candidatePair.localCandidateId &&
|
|
153
153
|
candidatePair.localCandidateId !== this._selectedLocalCandidateId &&
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import * as Models from './models/Models';
|
|
3
3
|
import { StorageProvider } from './storages/StorageProvider';
|
|
4
|
-
import {
|
|
4
|
+
import { PeerConnectionTransport } from '@observertc/sample-schemas-js';
|
|
5
5
|
import { ObservedClient } from './ObservedClient';
|
|
6
6
|
import { ObservedInboundTrack, ObservedInboundTrackConfig } from './ObservedInboundTrack';
|
|
7
7
|
import { ObservedOutboundTrack, ObservedOutboundTrackConfig } from './ObservedOutboundTrack';
|
|
@@ -88,13 +88,11 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
88
88
|
public receivingAudioBitrate = 0;
|
|
89
89
|
public receivingVideoBitrate = 0;
|
|
90
90
|
|
|
91
|
-
public
|
|
91
|
+
public avgRttInMs?: number;
|
|
92
92
|
|
|
93
93
|
private _closed = false;
|
|
94
94
|
private _updated = Date.now();
|
|
95
95
|
private _sample?: ObservedPeerConnectionStats;
|
|
96
|
-
private _selectedLocalIceCandidate?: IceLocalCandidate;
|
|
97
|
-
private _selectedRemoteIceCandidate?: IceRemoteCandidate;
|
|
98
96
|
|
|
99
97
|
public readonly ICE = ObservedICE.create(this);
|
|
100
98
|
private readonly _inboundAudioTracks = new Map<string, ObservedInboundTrack<'audio'>>();
|
|
@@ -111,6 +109,22 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
111
109
|
super();
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
public get label() {
|
|
113
|
+
return this._model.label;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public get usingTURN() {
|
|
117
|
+
return this.ICE.selectedRemoteCandidate?.candidateType?.toLowerCase() === 'relay';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public get availableOutgoingBitrate() {
|
|
121
|
+
return this.ICE.stats?.availableOutgoingBitrate;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public get availableIncomingBitrate() {
|
|
125
|
+
return this.ICE.stats?.availableIncomingBitrate;
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
public get peerConnectionId(): string {
|
|
115
129
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
116
130
|
return this._model.peerConnectionId!;
|
|
@@ -128,14 +142,6 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
128
142
|
return this._updated;
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
public get selectedLocalIceCandidate() {
|
|
132
|
-
return this._selectedLocalIceCandidate;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
public get selectedRemoteIceCandidate() {
|
|
136
|
-
return this._selectedRemoteIceCandidate;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
145
|
public get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundTrack<'audio'>> {
|
|
140
146
|
return this._inboundAudioTracks;
|
|
141
147
|
}
|
|
@@ -212,7 +218,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
212
218
|
this.receivingAudioBitrate = 0;
|
|
213
219
|
this.receivingVideoBitrate = 0;
|
|
214
220
|
|
|
215
|
-
let
|
|
221
|
+
let sumRttInMs = 0;
|
|
216
222
|
|
|
217
223
|
this._inboundAudioTracks.forEach((track) => {
|
|
218
224
|
this.deltaInboundLostPackets += track.deltaLostPackets;
|
|
@@ -229,7 +235,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
229
235
|
this.totalReceivedAudioBytes += track.totalBytesReceived;
|
|
230
236
|
this.totalReceivedAudioPacktes += track.totalReceivedPackets;
|
|
231
237
|
|
|
232
|
-
|
|
238
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
233
239
|
});
|
|
234
240
|
|
|
235
241
|
this._inboundVideoTracks.forEach((track) => {
|
|
@@ -247,7 +253,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
247
253
|
this.totalReceivedVideoBytes += track.totalBytesReceived;
|
|
248
254
|
this.totalReceivedVideoPackets += track.totalReceivedPackets;
|
|
249
255
|
|
|
250
|
-
|
|
256
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
251
257
|
});
|
|
252
258
|
|
|
253
259
|
this._outboundAudioTracks.forEach((track) => {
|
|
@@ -263,7 +269,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
263
269
|
this.totalSentAudioBytes += track.totalSentBytes;
|
|
264
270
|
this.totalSentAudioPackets += track.totalSentPackets;
|
|
265
271
|
|
|
266
|
-
|
|
272
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
267
273
|
});
|
|
268
274
|
|
|
269
275
|
this._outboundVideoTracks.forEach((track) => {
|
|
@@ -279,19 +285,27 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
279
285
|
this.totalSentVideoBytes += track.totalSentBytes;
|
|
280
286
|
this.totalSentVideoPackets += track.totalSentPackets;
|
|
281
287
|
|
|
282
|
-
|
|
288
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
283
289
|
});
|
|
290
|
+
const iceRttInMs = this.ICE.stats?.currentRoundTripTime;
|
|
291
|
+
let nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
|
|
284
292
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
293
|
+
if (iceRttInMs) {
|
|
294
|
+
sumRttInMs += iceRttInMs;
|
|
295
|
+
nrOfBelongings += 1;
|
|
296
|
+
}
|
|
297
|
+
this.avgRttInMs = 0 < nrOfBelongings ? sumRttInMs / nrOfBelongings : undefined;
|
|
288
298
|
}
|
|
289
299
|
|
|
290
|
-
public update(sample: PeerConnectionTransport, timestamp: number) {
|
|
300
|
+
public async update(sample: PeerConnectionTransport, timestamp: number) {
|
|
291
301
|
if (this._closed) return;
|
|
292
302
|
if (sample.peerConnectionId !== this._model.peerConnectionId) throw new Error(`TransportId mismatch. PeerConnectionId: ${ this._model.peerConnectionId } TransportId: ${ sample.transportId}`);
|
|
293
303
|
|
|
294
304
|
this._sample = sample;
|
|
305
|
+
if (this._model.label !== sample.label) {
|
|
306
|
+
this._model.label = sample.label;
|
|
307
|
+
await this._save().catch(() => void 0);
|
|
308
|
+
}
|
|
295
309
|
|
|
296
310
|
const report: PeerConnectionTransportReport = {
|
|
297
311
|
serviceId: this.client.call.serviceId,
|
|
@@ -311,11 +325,6 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
311
325
|
this.emit('update');
|
|
312
326
|
}
|
|
313
327
|
|
|
314
|
-
public updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate) {
|
|
315
|
-
this._selectedLocalIceCandidate = localCandidate;
|
|
316
|
-
this._selectedRemoteIceCandidate = remoteCandidate;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
328
|
public async createInboundAudioTrack(config: Omit<ObservedInboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedInboundTrack<'audio'>> {
|
|
320
329
|
if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
|
|
321
330
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import * as Models from './models/Models';
|
|
3
|
+
import { Observer } from './Observer';
|
|
4
|
+
import { createSingleExecutor } from './common/SingleExecutor';
|
|
5
|
+
import { SfuSample } from '@observertc/sample-schemas-js';
|
|
6
|
+
|
|
7
|
+
export type ObservedSfuConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
8
|
+
serviceId: string;
|
|
9
|
+
sfuId: string;
|
|
10
|
+
joined?: number;
|
|
11
|
+
appData: AppData;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ObservedSfuEvents = {
|
|
15
|
+
update: [],
|
|
16
|
+
close: [],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export declare interface ObservedSfu {
|
|
20
|
+
on<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
21
|
+
off<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
22
|
+
once<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
23
|
+
emit<U extends keyof ObservedSfuEvents>(event: U, ...args: ObservedSfuEvents[U]): boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class ObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
27
|
+
public static async create<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
28
|
+
config: ObservedSfuConfig<T>,
|
|
29
|
+
observer: Observer,
|
|
30
|
+
// reportsCollector: ReportsCollector,
|
|
31
|
+
) {
|
|
32
|
+
const model = new Models.Sfu({
|
|
33
|
+
serviceId: config.serviceId,
|
|
34
|
+
sfuId: config.sfuId,
|
|
35
|
+
sfuTransportIds: [],
|
|
36
|
+
joined: BigInt(config.joined ?? Date.now()),
|
|
37
|
+
});
|
|
38
|
+
const result = new ObservedSfu(
|
|
39
|
+
model,
|
|
40
|
+
observer,
|
|
41
|
+
config.appData,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const alreadyInserted = await observer.storage.sfuStorage.insert(config.sfuId, model);
|
|
45
|
+
|
|
46
|
+
if (alreadyInserted) throw new Error(`Sfu with id ${config.sfuId} already exists`);
|
|
47
|
+
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private _updated = Date.now();
|
|
52
|
+
private readonly _execute = createSingleExecutor();
|
|
53
|
+
private _closed = false;
|
|
54
|
+
|
|
55
|
+
private constructor(
|
|
56
|
+
private readonly _model: Models.Sfu,
|
|
57
|
+
public readonly observer: Observer,
|
|
58
|
+
public readonly appData: AppData,
|
|
59
|
+
) {
|
|
60
|
+
super();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public get serviceId() {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
65
|
+
return this._model.serviceId!;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get sfuId() {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
70
|
+
return this._model.sfuId!;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public get joined() {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
75
|
+
return Number(this._model.joined!);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public get closed() {
|
|
79
|
+
return this._closed;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public close() {
|
|
83
|
+
if (this._closed) return;
|
|
84
|
+
this._closed = true;
|
|
85
|
+
this.emit('close');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public async update(sample: SfuSample) {
|
|
89
|
+
if (this._closed) throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
90
|
+
if (sample.sfuId !== this.sfuId) throw new Error(`Sfu ${this.sfuId} is not the same as sample.sfuId`);
|
|
91
|
+
|
|
92
|
+
return this._execute(async () => {
|
|
93
|
+
if (this._closed) throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
94
|
+
await this._save();
|
|
95
|
+
this.emit('update');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private async _save() {
|
|
100
|
+
if (this._closed) throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
101
|
+
|
|
102
|
+
return this._execute(async () => {
|
|
103
|
+
if (this._closed) throw new Error(`Sfu ${this.sfuId} is closed`);
|
|
104
|
+
await this.observer.storage.sfuStorage.set(this.sfuId, this._model);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
}
|