@observertc/observer-js 0.30.3-beta → 0.30.5-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/ObservedClient.d.ts +35 -3
- package/lib/ObservedClient.d.ts.map +1 -1
- package/lib/ObservedClient.js +60 -16
- package/lib/ObservedPeerConnection.d.ts +7 -11
- package/lib/ObservedPeerConnection.d.ts.map +1 -1
- package/lib/ObservedPeerConnection.js +43 -26
- package/lib/ObservedSfu.d.ts +26 -0
- package/lib/ObservedSfu.d.ts.map +1 -0
- package/lib/ObservedSfu.js +61 -0
- package/package.json +1 -1
- package/src/ObservedClient.ts +85 -17
- package/src/ObservedICE.ts +1 -1
- package/src/ObservedPeerConnection.ts +35 -30
- package/src/ObservedSfu.ts +55 -0
package/lib/ObservedClient.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { ClientSample } from '@observertc/sample-schemas-js';
|
|
2
|
+
import { ClientSample, IceLocalCandidate, IceRemoteCandidate } from '@observertc/sample-schemas-js';
|
|
3
3
|
import { ObservedCall } from './ObservedCall';
|
|
4
4
|
import { StorageProvider } from './storages/StorageProvider';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
@@ -22,6 +22,32 @@ export type ObservedClientEvents = {
|
|
|
22
22
|
close: [];
|
|
23
23
|
'processing-sample-overflow': [];
|
|
24
24
|
newpeerconnection: [ObservedPeerConnection];
|
|
25
|
+
iceconnectionstatechange: [
|
|
26
|
+
{
|
|
27
|
+
peerConnection: ObservedPeerConnection;
|
|
28
|
+
state: string;
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
icegatheringstatechange: [
|
|
32
|
+
{
|
|
33
|
+
peerConnection: ObservedPeerConnection;
|
|
34
|
+
state: string;
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
connectionstatechange: [
|
|
38
|
+
{
|
|
39
|
+
peerConnection: ObservedPeerConnection;
|
|
40
|
+
state: string;
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
selectedcandidatepair: [
|
|
44
|
+
{
|
|
45
|
+
peerConnection: ObservedPeerConnection;
|
|
46
|
+
localCandidate: IceLocalCandidate;
|
|
47
|
+
remoteCandidate: IceRemoteCandidate;
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
usingturn: [boolean];
|
|
25
51
|
};
|
|
26
52
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
27
53
|
on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
|
|
@@ -45,7 +71,11 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
45
71
|
private _acceptedSample;
|
|
46
72
|
private _processingSample;
|
|
47
73
|
private _timeZoneOffsetInHours?;
|
|
48
|
-
private
|
|
74
|
+
private _left?;
|
|
75
|
+
private _numberOfAcceptedUpdates;
|
|
76
|
+
usingTURN: boolean;
|
|
77
|
+
availableOutgoingBitrate: number;
|
|
78
|
+
availableIncomingBitrate: number;
|
|
49
79
|
totalInboundPacketsLost: number;
|
|
50
80
|
totalInboundPacketsReceived: number;
|
|
51
81
|
totalOutboundPacketsSent: number;
|
|
@@ -53,7 +83,7 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
53
83
|
totalDataChannelBytesReceived: number;
|
|
54
84
|
totalSentBytes: number;
|
|
55
85
|
totalReceivedBytes: number;
|
|
56
|
-
|
|
86
|
+
avgRttInMs?: number;
|
|
57
87
|
sendingBitrate?: number;
|
|
58
88
|
receivingBitrate?: number;
|
|
59
89
|
readonly mediaDevices: string[];
|
|
@@ -78,7 +108,9 @@ export declare class ObservedClient<AppData extends Record<string, unknown> = Re
|
|
|
78
108
|
get platform(): Samples_ClientSample_Platform | undefined;
|
|
79
109
|
get acceptedSample(): number;
|
|
80
110
|
get updated(): number;
|
|
111
|
+
get numberOfAcceptedUpdates(): number;
|
|
81
112
|
get uptimeInMs(): number;
|
|
113
|
+
get durationInMs(): number | undefined;
|
|
82
114
|
get closed(): boolean;
|
|
83
115
|
get peerConnections(): ReadonlyMap<string, ObservedPeerConnection>;
|
|
84
116
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObservedClient.d.ts","sourceRoot":"","sources":["../src/ObservedClient.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,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,24 +295,37 @@ 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
|
-
peerConnection && callEvent.value &&
|
|
313
|
+
peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
|
|
314
|
+
peerConnection,
|
|
315
|
+
state: callEvent.value,
|
|
316
|
+
});
|
|
298
317
|
break;
|
|
299
318
|
case CallEventType_1.CallEventType.ICE_GATHERING_STATE_CHANGED:
|
|
300
|
-
peerConnection && callEvent.value &&
|
|
319
|
+
peerConnection && callEvent.value && this.emit('icegatheringstatechange', {
|
|
320
|
+
peerConnection,
|
|
321
|
+
state: callEvent.value,
|
|
322
|
+
});
|
|
301
323
|
break;
|
|
302
324
|
case CallEventType_1.CallEventType.PEER_CONNECTION_STATE_CHANGED:
|
|
303
|
-
peerConnection && callEvent.value &&
|
|
325
|
+
peerConnection && callEvent.value && this.emit('connectionstatechange', {
|
|
326
|
+
peerConnection,
|
|
327
|
+
state: callEvent.value,
|
|
328
|
+
});
|
|
304
329
|
break;
|
|
305
330
|
case CallEventType_1.CallEventType.PEER_CONNECTION_OPENED: {
|
|
306
331
|
if (!callEvent.peerConnectionId)
|
|
@@ -375,7 +400,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
375
400
|
for (const transport of (_x = sample.pcTransports) !== null && _x !== void 0 ? _x : []) {
|
|
376
401
|
try {
|
|
377
402
|
const peerConnection = (_y = this._peerConnections.get(transport.transportId)) !== null && _y !== void 0 ? _y : yield this._createPeerConnection(transport.peerConnectionId);
|
|
378
|
-
peerConnection.update(transport, sample.timestamp);
|
|
403
|
+
yield peerConnection.update(transport, sample.timestamp);
|
|
379
404
|
}
|
|
380
405
|
catch (err) {
|
|
381
406
|
logger.error(`Error creating peer connection: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
@@ -535,6 +560,10 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
535
560
|
}
|
|
536
561
|
}
|
|
537
562
|
// update metrics
|
|
563
|
+
const wasUsingTURN = this.usingTURN;
|
|
564
|
+
this.usingTURN = false;
|
|
565
|
+
this.availableIncomingBitrate = 0;
|
|
566
|
+
this.availableOutgoingBitrate = 0;
|
|
538
567
|
this.totalSentBytes = 0;
|
|
539
568
|
this.totalReceivedBytes = 0;
|
|
540
569
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -544,7 +573,7 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
544
573
|
this.totalDataChannelBytesReceived = 0;
|
|
545
574
|
this.sendingBitrate = 0;
|
|
546
575
|
this.receivingBitrate = 0;
|
|
547
|
-
let
|
|
576
|
+
let sumRttInMs = 0;
|
|
548
577
|
for (const peerConnection of this._peerConnections.values()) {
|
|
549
578
|
if (peerConnection.closed)
|
|
550
579
|
continue;
|
|
@@ -558,11 +587,17 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
558
587
|
this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
|
|
559
588
|
this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
|
|
560
589
|
this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
|
|
561
|
-
|
|
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;
|
|
562
595
|
}
|
|
563
|
-
this.
|
|
596
|
+
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
564
597
|
if (executeSave)
|
|
565
598
|
yield this._save();
|
|
599
|
+
if (wasUsingTURN !== this.usingTURN)
|
|
600
|
+
this.emit('usingturn', this.usingTURN);
|
|
566
601
|
this._updated = sample.timestamp;
|
|
567
602
|
this.emit('update');
|
|
568
603
|
})).finally(() => {
|
|
@@ -580,11 +615,20 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
580
615
|
result.created = pendingTracksTimestamp;
|
|
581
616
|
this.ωpendingCreatedTracksTimestamp.delete(peerConnectionId);
|
|
582
617
|
}
|
|
618
|
+
const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }) => {
|
|
619
|
+
this.emit('selectedcandidatepair', {
|
|
620
|
+
peerConnection: result,
|
|
621
|
+
localCandidate,
|
|
622
|
+
remoteCandidate,
|
|
623
|
+
});
|
|
624
|
+
};
|
|
583
625
|
result.once('close', () => {
|
|
626
|
+
result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
584
627
|
this._peerConnections.delete(peerConnectionId);
|
|
585
628
|
this._model.peerConnectionIds = this._model.peerConnectionIds.filter((id) => id !== peerConnectionId);
|
|
586
629
|
this._save().catch(() => void 0);
|
|
587
630
|
});
|
|
631
|
+
result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
588
632
|
this._peerConnections.set(peerConnectionId, result);
|
|
589
633
|
this._model.peerConnectionIds.push(peerConnectionId);
|
|
590
634
|
yield this._save();
|
|
@@ -600,9 +644,9 @@ class ObservedClient extends events_1.EventEmitter {
|
|
|
600
644
|
});
|
|
601
645
|
}
|
|
602
646
|
_addClientLeftReport() {
|
|
603
|
-
if (this.
|
|
647
|
+
if (this._left)
|
|
604
648
|
return;
|
|
605
|
-
this.
|
|
649
|
+
this._left = Date.now();
|
|
606
650
|
this.reports.addCallEventReport((0, callEventReports_1.createClientLeftEventReport)(this.serviceId, this.mediaUnitId, this.roomId, this.callId, this.clientId, this.joined, this.userId, this.marker));
|
|
607
651
|
}
|
|
608
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';
|
|
@@ -13,9 +13,6 @@ export type ObservedPeerConnectionEvents = {
|
|
|
13
13
|
newinboudvideotrack: [ObservedInboundTrack<'video'>];
|
|
14
14
|
newoutboundaudiotrack: [ObservedOutboundTrack<'audio'>];
|
|
15
15
|
newoutboundvideotrack: [ObservedOutboundTrack<'video'>];
|
|
16
|
-
iceconnectionstatechange: [string];
|
|
17
|
-
icegatheringstatechange: [string];
|
|
18
|
-
connectionstatechange: [string];
|
|
19
16
|
};
|
|
20
17
|
export type ObservedPeerConnectionConfig = {
|
|
21
18
|
peerConnectionId: string;
|
|
@@ -63,12 +60,10 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
63
60
|
sendingVideoBitrate: number;
|
|
64
61
|
receivingAudioBitrate: number;
|
|
65
62
|
receivingVideoBitrate: number;
|
|
66
|
-
|
|
63
|
+
avgRttInMs?: number;
|
|
67
64
|
private _closed;
|
|
68
65
|
private _updated;
|
|
69
66
|
private _sample?;
|
|
70
|
-
private _selectedLocalIceCandidate?;
|
|
71
|
-
private _selectedRemoteIceCandidate?;
|
|
72
67
|
readonly ICE: ObservedICE;
|
|
73
68
|
private readonly _inboundAudioTracks;
|
|
74
69
|
private readonly _inboundVideoTracks;
|
|
@@ -76,12 +71,14 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
76
71
|
private readonly _outboundVideoTracks;
|
|
77
72
|
private readonly _execute;
|
|
78
73
|
private constructor();
|
|
74
|
+
get label(): string | undefined;
|
|
75
|
+
get usingTURN(): boolean;
|
|
76
|
+
get availableOutgoingBitrate(): number | undefined;
|
|
77
|
+
get availableIncomingBitrate(): number | undefined;
|
|
79
78
|
get peerConnectionId(): string;
|
|
80
79
|
get reports(): import("./ReportsCollector").ReportsCollector;
|
|
81
80
|
get stats(): ObservedPeerConnectionStats | undefined;
|
|
82
81
|
get updated(): number;
|
|
83
|
-
get selectedLocalIceCandidate(): IceLocalCandidate | undefined;
|
|
84
|
-
get selectedRemoteIceCandidate(): IceRemoteCandidate | undefined;
|
|
85
82
|
get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundTrack<'audio'>>;
|
|
86
83
|
get inboundVideoTracks(): ReadonlyMap<string, ObservedInboundTrack<'video'>>;
|
|
87
84
|
get outboundAudioTracks(): ReadonlyMap<string, ObservedOutboundTrack<'audio'>>;
|
|
@@ -92,8 +89,7 @@ export declare class ObservedPeerConnection extends EventEmitter {
|
|
|
92
89
|
getTrack(trackId: string): ObservedInboundTrack<'audio'> | ObservedInboundTrack<'video'> | ObservedOutboundTrack<'audio'> | ObservedOutboundTrack<'video'> | undefined;
|
|
93
90
|
resetMetrics(): void;
|
|
94
91
|
updateMetrics(): void;
|
|
95
|
-
update(sample: PeerConnectionTransport, timestamp: number): void
|
|
96
|
-
updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate): void;
|
|
92
|
+
update(sample: PeerConnectionTransport, timestamp: number): Promise<void>;
|
|
97
93
|
createInboundAudioTrack(config: Omit<ObservedInboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedInboundTrack<'audio'>>;
|
|
98
94
|
createInboundVideoTrack(config: Omit<ObservedInboundTrackConfig<'video'>, 'kind'>): Promise<ObservedInboundTrack<'video'>>;
|
|
99
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,26 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { Observer } from './Observer';
|
|
4
|
+
export type ObservedSfuConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
5
|
+
serviceId: string;
|
|
6
|
+
sfuId: string;
|
|
7
|
+
joined?: number;
|
|
8
|
+
appData: AppData;
|
|
9
|
+
};
|
|
10
|
+
export type ObservedSfuEvents = {
|
|
11
|
+
update: [];
|
|
12
|
+
close: [];
|
|
13
|
+
};
|
|
14
|
+
export declare interface ObservedSfu {
|
|
15
|
+
on<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
16
|
+
off<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
17
|
+
once<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
18
|
+
emit<U extends keyof ObservedSfuEvents>(event: U, ...args: ObservedSfuEvents[U]): boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class ObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
21
|
+
private readonly _model;
|
|
22
|
+
readonly appData: AppData;
|
|
23
|
+
static create<T extends Record<string, unknown> = Record<string, unknown>>(config: ObservedSfuConfig<T>, observer: Observer): Promise<ObservedSfu<T>>;
|
|
24
|
+
private constructor();
|
|
25
|
+
}
|
|
26
|
+
//# 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,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;IAyB9G,OAAO,CAAC,QAAQ,CAAC,MAAM;aACP,OAAO,EAAE,OAAO;WAzBb,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;IAqBnB,OAAO;CAOP"}
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
class ObservedSfu extends events_1.EventEmitter {
|
|
39
|
+
static create(config, observer) {
|
|
40
|
+
var _a;
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const model = new Models.Sfu({
|
|
43
|
+
serviceId: config.serviceId,
|
|
44
|
+
sfuId: config.sfuId,
|
|
45
|
+
sfuTransportIds: [],
|
|
46
|
+
joined: BigInt((_a = config.joined) !== null && _a !== void 0 ? _a : Date.now()),
|
|
47
|
+
});
|
|
48
|
+
const result = new ObservedSfu(model, config.appData);
|
|
49
|
+
const alreadyInserted = yield observer.storage.sfuStorage.insert(config.sfuId, model);
|
|
50
|
+
if (alreadyInserted)
|
|
51
|
+
throw new Error(`Sfu with id ${config.sfuId} already exists`);
|
|
52
|
+
return result;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
constructor(_model, appData) {
|
|
56
|
+
super();
|
|
57
|
+
this._model = _model;
|
|
58
|
+
this.appData = appData;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ObservedSfu = ObservedSfu;
|
package/package.json
CHANGED
package/src/ObservedClient.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClientSample } from '@observertc/sample-schemas-js';
|
|
1
|
+
import { ClientSample, IceLocalCandidate, IceRemoteCandidate } from '@observertc/sample-schemas-js';
|
|
2
2
|
import { ObservedCall } from './ObservedCall';
|
|
3
3
|
import * as Models from './models/Models';
|
|
4
4
|
import { StorageProvider } from './storages/StorageProvider';
|
|
@@ -34,6 +34,24 @@ export type ObservedClientEvents = {
|
|
|
34
34
|
close: [],
|
|
35
35
|
'processing-sample-overflow': [],
|
|
36
36
|
newpeerconnection: [ObservedPeerConnection],
|
|
37
|
+
iceconnectionstatechange: [{
|
|
38
|
+
peerConnection: ObservedPeerConnection,
|
|
39
|
+
state: string,
|
|
40
|
+
}],
|
|
41
|
+
icegatheringstatechange: [{
|
|
42
|
+
peerConnection: ObservedPeerConnection,
|
|
43
|
+
state: string,
|
|
44
|
+
}],
|
|
45
|
+
connectionstatechange: [{
|
|
46
|
+
peerConnection: ObservedPeerConnection,
|
|
47
|
+
state: string,
|
|
48
|
+
}],
|
|
49
|
+
selectedcandidatepair: [{
|
|
50
|
+
peerConnection: ObservedPeerConnection,
|
|
51
|
+
localCandidate: IceLocalCandidate,
|
|
52
|
+
remoteCandidate: IceRemoteCandidate,
|
|
53
|
+
}],
|
|
54
|
+
usingturn: [boolean],
|
|
37
55
|
};
|
|
38
56
|
|
|
39
57
|
export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
|
|
@@ -83,8 +101,6 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
83
101
|
));
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
result.once('close', () => result._addClientLeftReport());
|
|
87
|
-
|
|
88
104
|
return result;
|
|
89
105
|
}
|
|
90
106
|
|
|
@@ -99,8 +115,12 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
99
115
|
private _acceptedSample = 0;
|
|
100
116
|
private _processingSample = 0;
|
|
101
117
|
private _timeZoneOffsetInHours?: number;
|
|
102
|
-
private
|
|
118
|
+
private _left?: number;
|
|
119
|
+
private _numberOfAcceptedUpdates = 0;
|
|
103
120
|
|
|
121
|
+
public usingTURN = false;
|
|
122
|
+
public availableOutgoingBitrate = 0;
|
|
123
|
+
public availableIncomingBitrate = 0;
|
|
104
124
|
public totalInboundPacketsLost = 0;
|
|
105
125
|
public totalInboundPacketsReceived = 0;
|
|
106
126
|
public totalOutboundPacketsSent = 0;
|
|
@@ -109,7 +129,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
109
129
|
public totalSentBytes = 0;
|
|
110
130
|
public totalReceivedBytes = 0;
|
|
111
131
|
|
|
112
|
-
public
|
|
132
|
+
public avgRttInMs?: number;
|
|
113
133
|
public sendingBitrate?: number;
|
|
114
134
|
public receivingBitrate?: number;
|
|
115
135
|
|
|
@@ -119,7 +139,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
119
139
|
|
|
120
140
|
public readonly ωpendingCreatedTracksTimestamp = new Map<string, number>();
|
|
121
141
|
public readonly ωpendingCreatedPeerConnectionTimestamp = new Map<string, number>();
|
|
122
|
-
|
|
142
|
+
|
|
123
143
|
private constructor(
|
|
124
144
|
private readonly _model: Models.Client,
|
|
125
145
|
public readonly call: ObservedCall,
|
|
@@ -199,10 +219,20 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
199
219
|
return this._updated;
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
public get numberOfAcceptedUpdates() {
|
|
223
|
+
return this._numberOfAcceptedUpdates;
|
|
224
|
+
}
|
|
225
|
+
|
|
202
226
|
public get uptimeInMs() {
|
|
203
227
|
return this._updated - this.created;
|
|
204
228
|
}
|
|
205
229
|
|
|
230
|
+
public get durationInMs(): number | undefined {
|
|
231
|
+
if (!this._left) return;
|
|
232
|
+
|
|
233
|
+
return this._left - this.joined;
|
|
234
|
+
}
|
|
235
|
+
|
|
206
236
|
public get closed() {
|
|
207
237
|
return this._closed;
|
|
208
238
|
}
|
|
@@ -215,6 +245,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
215
245
|
if (this._closed) return;
|
|
216
246
|
this._closed = true;
|
|
217
247
|
|
|
248
|
+
!this._left && this._addClientLeftReport();
|
|
249
|
+
|
|
218
250
|
Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
|
|
219
251
|
|
|
220
252
|
this._execute(() => this._storageProvider.clientStorage.remove(this.clientId))
|
|
@@ -235,6 +267,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
235
267
|
}
|
|
236
268
|
|
|
237
269
|
return this._execute(async () => {
|
|
270
|
+
++this._numberOfAcceptedUpdates;
|
|
271
|
+
|
|
238
272
|
for (const peerConnection of this._peerConnections.values()) {
|
|
239
273
|
if (peerConnection.closed) continue;
|
|
240
274
|
peerConnection.resetMetrics();
|
|
@@ -399,19 +433,33 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
399
433
|
|
|
400
434
|
switch (callEvent.name) {
|
|
401
435
|
case CallEventType.CLIENT_JOINED:
|
|
402
|
-
|
|
436
|
+
if (timestamp) {
|
|
437
|
+
this.created = timestamp;
|
|
438
|
+
this._model.joined = BigInt(timestamp);
|
|
439
|
+
await this._save();
|
|
440
|
+
}
|
|
441
|
+
|
|
403
442
|
break;
|
|
404
443
|
case CallEventType.CLIENT_LEFT:
|
|
405
|
-
this.
|
|
444
|
+
this._left = timestamp ?? Date.now();
|
|
406
445
|
break;
|
|
407
446
|
case CallEventType.ICE_CONNECTION_STATE_CHANGED:
|
|
408
|
-
peerConnection && callEvent.value &&
|
|
447
|
+
peerConnection && callEvent.value && this.emit('iceconnectionstatechange', {
|
|
448
|
+
peerConnection,
|
|
449
|
+
state: callEvent.value,
|
|
450
|
+
});
|
|
409
451
|
break;
|
|
410
452
|
case CallEventType.ICE_GATHERING_STATE_CHANGED:
|
|
411
|
-
peerConnection && callEvent.value &&
|
|
453
|
+
peerConnection && callEvent.value && this.emit('icegatheringstatechange', {
|
|
454
|
+
peerConnection,
|
|
455
|
+
state: callEvent.value,
|
|
456
|
+
});
|
|
412
457
|
break;
|
|
413
458
|
case CallEventType.PEER_CONNECTION_STATE_CHANGED:
|
|
414
|
-
peerConnection && callEvent.value &&
|
|
459
|
+
peerConnection && callEvent.value && this.emit('connectionstatechange', {
|
|
460
|
+
peerConnection,
|
|
461
|
+
state: callEvent.value,
|
|
462
|
+
});
|
|
415
463
|
break;
|
|
416
464
|
case CallEventType.PEER_CONNECTION_OPENED: {
|
|
417
465
|
if (!callEvent.peerConnectionId) break;
|
|
@@ -538,7 +586,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
538
586
|
try {
|
|
539
587
|
const peerConnection = this._peerConnections.get(transport.transportId) ?? await this._createPeerConnection(transport.peerConnectionId);
|
|
540
588
|
|
|
541
|
-
peerConnection.update(transport, sample.timestamp);
|
|
589
|
+
await peerConnection.update(transport, sample.timestamp);
|
|
542
590
|
} catch (err) {
|
|
543
591
|
logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
|
|
544
592
|
}
|
|
@@ -733,6 +781,11 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
733
781
|
}
|
|
734
782
|
|
|
735
783
|
// update metrics
|
|
784
|
+
const wasUsingTURN = this.usingTURN;
|
|
785
|
+
|
|
786
|
+
this.usingTURN = false;
|
|
787
|
+
this.availableIncomingBitrate = 0;
|
|
788
|
+
this.availableOutgoingBitrate = 0;
|
|
736
789
|
this.totalSentBytes = 0;
|
|
737
790
|
this.totalReceivedBytes = 0;
|
|
738
791
|
this.totalOutboundPacketsSent = 0;
|
|
@@ -742,7 +795,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
742
795
|
this.totalDataChannelBytesReceived = 0;
|
|
743
796
|
this.sendingBitrate = 0;
|
|
744
797
|
this.receivingBitrate = 0;
|
|
745
|
-
let
|
|
798
|
+
let sumRttInMs = 0;
|
|
746
799
|
|
|
747
800
|
for (const peerConnection of this._peerConnections.values()) {
|
|
748
801
|
if (peerConnection.closed) continue;
|
|
@@ -756,12 +809,17 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
756
809
|
this.totalDataChannelBytesReceived += peerConnection.totalDataChannelBytesReceived;
|
|
757
810
|
this.sendingBitrate += peerConnection.sendingAudioBitrate + peerConnection.sendingVideoBitrate;
|
|
758
811
|
this.receivingBitrate += peerConnection.receivingAudioBitrate + peerConnection.receivingVideoBitrate;
|
|
759
|
-
|
|
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;
|
|
760
817
|
}
|
|
761
818
|
|
|
762
|
-
this.
|
|
819
|
+
this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
|
|
763
820
|
|
|
764
821
|
if (executeSave) await this._save();
|
|
822
|
+
if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
|
|
765
823
|
|
|
766
824
|
this._updated = sample.timestamp;
|
|
767
825
|
this.emit('update');
|
|
@@ -782,11 +840,21 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
782
840
|
this.ωpendingCreatedTracksTimestamp.delete(peerConnectionId);
|
|
783
841
|
}
|
|
784
842
|
|
|
843
|
+
const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }: { localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate }) => {
|
|
844
|
+
this.emit('selectedcandidatepair', {
|
|
845
|
+
peerConnection: result,
|
|
846
|
+
localCandidate,
|
|
847
|
+
remoteCandidate,
|
|
848
|
+
});
|
|
849
|
+
};
|
|
850
|
+
|
|
785
851
|
result.once('close', () => {
|
|
852
|
+
result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
786
853
|
this._peerConnections.delete(peerConnectionId);
|
|
787
854
|
this._model.peerConnectionIds = this._model.peerConnectionIds.filter((id) => id !== peerConnectionId);
|
|
788
855
|
this._save().catch(() => void 0);
|
|
789
856
|
});
|
|
857
|
+
result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
|
|
790
858
|
this._peerConnections.set(peerConnectionId, result);
|
|
791
859
|
this._model.peerConnectionIds.push(peerConnectionId);
|
|
792
860
|
await this._save();
|
|
@@ -803,8 +871,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
|
|
|
803
871
|
}
|
|
804
872
|
|
|
805
873
|
private _addClientLeftReport() {
|
|
806
|
-
if (this.
|
|
807
|
-
this.
|
|
874
|
+
if (this._left) return;
|
|
875
|
+
this._left = Date.now();
|
|
808
876
|
|
|
809
877
|
this.reports.addCallEventReport(createClientLeftEventReport(
|
|
810
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';
|
|
@@ -16,10 +16,6 @@ export type ObservedPeerConnectionEvents = {
|
|
|
16
16
|
newinboudvideotrack: [ObservedInboundTrack<'video'>],
|
|
17
17
|
newoutboundaudiotrack: [ObservedOutboundTrack<'audio'>],
|
|
18
18
|
newoutboundvideotrack: [ObservedOutboundTrack<'video'>],
|
|
19
|
-
|
|
20
|
-
iceconnectionstatechange: [string],
|
|
21
|
-
icegatheringstatechange: [string],
|
|
22
|
-
connectionstatechange: [string],
|
|
23
19
|
};
|
|
24
20
|
|
|
25
21
|
export type ObservedPeerConnectionConfig = {
|
|
@@ -92,13 +88,11 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
92
88
|
public receivingAudioBitrate = 0;
|
|
93
89
|
public receivingVideoBitrate = 0;
|
|
94
90
|
|
|
95
|
-
public
|
|
91
|
+
public avgRttInMs?: number;
|
|
96
92
|
|
|
97
93
|
private _closed = false;
|
|
98
94
|
private _updated = Date.now();
|
|
99
95
|
private _sample?: ObservedPeerConnectionStats;
|
|
100
|
-
private _selectedLocalIceCandidate?: IceLocalCandidate;
|
|
101
|
-
private _selectedRemoteIceCandidate?: IceRemoteCandidate;
|
|
102
96
|
|
|
103
97
|
public readonly ICE = ObservedICE.create(this);
|
|
104
98
|
private readonly _inboundAudioTracks = new Map<string, ObservedInboundTrack<'audio'>>();
|
|
@@ -115,6 +109,22 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
115
109
|
super();
|
|
116
110
|
}
|
|
117
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
|
+
|
|
118
128
|
public get peerConnectionId(): string {
|
|
119
129
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
120
130
|
return this._model.peerConnectionId!;
|
|
@@ -132,14 +142,6 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
132
142
|
return this._updated;
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
public get selectedLocalIceCandidate() {
|
|
136
|
-
return this._selectedLocalIceCandidate;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public get selectedRemoteIceCandidate() {
|
|
140
|
-
return this._selectedRemoteIceCandidate;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
145
|
public get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundTrack<'audio'>> {
|
|
144
146
|
return this._inboundAudioTracks;
|
|
145
147
|
}
|
|
@@ -216,7 +218,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
216
218
|
this.receivingAudioBitrate = 0;
|
|
217
219
|
this.receivingVideoBitrate = 0;
|
|
218
220
|
|
|
219
|
-
let
|
|
221
|
+
let sumRttInMs = 0;
|
|
220
222
|
|
|
221
223
|
this._inboundAudioTracks.forEach((track) => {
|
|
222
224
|
this.deltaInboundLostPackets += track.deltaLostPackets;
|
|
@@ -233,7 +235,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
233
235
|
this.totalReceivedAudioBytes += track.totalBytesReceived;
|
|
234
236
|
this.totalReceivedAudioPacktes += track.totalReceivedPackets;
|
|
235
237
|
|
|
236
|
-
|
|
238
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
237
239
|
});
|
|
238
240
|
|
|
239
241
|
this._inboundVideoTracks.forEach((track) => {
|
|
@@ -251,7 +253,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
251
253
|
this.totalReceivedVideoBytes += track.totalBytesReceived;
|
|
252
254
|
this.totalReceivedVideoPackets += track.totalReceivedPackets;
|
|
253
255
|
|
|
254
|
-
|
|
256
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
255
257
|
});
|
|
256
258
|
|
|
257
259
|
this._outboundAudioTracks.forEach((track) => {
|
|
@@ -267,7 +269,7 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
267
269
|
this.totalSentAudioBytes += track.totalSentBytes;
|
|
268
270
|
this.totalSentAudioPackets += track.totalSentPackets;
|
|
269
271
|
|
|
270
|
-
|
|
272
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
271
273
|
});
|
|
272
274
|
|
|
273
275
|
this._outboundVideoTracks.forEach((track) => {
|
|
@@ -283,19 +285,27 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
283
285
|
this.totalSentVideoBytes += track.totalSentBytes;
|
|
284
286
|
this.totalSentVideoPackets += track.totalSentPackets;
|
|
285
287
|
|
|
286
|
-
|
|
288
|
+
sumRttInMs = (track.rttInMs ?? 0);
|
|
287
289
|
});
|
|
290
|
+
const iceRttInMs = this.ICE.stats?.currentRoundTripTime;
|
|
291
|
+
let nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
|
|
288
292
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
293
|
+
if (iceRttInMs) {
|
|
294
|
+
sumRttInMs += iceRttInMs;
|
|
295
|
+
nrOfBelongings += 1;
|
|
296
|
+
}
|
|
297
|
+
this.avgRttInMs = 0 < nrOfBelongings ? sumRttInMs / nrOfBelongings : undefined;
|
|
292
298
|
}
|
|
293
299
|
|
|
294
|
-
public update(sample: PeerConnectionTransport, timestamp: number) {
|
|
300
|
+
public async update(sample: PeerConnectionTransport, timestamp: number) {
|
|
295
301
|
if (this._closed) return;
|
|
296
302
|
if (sample.peerConnectionId !== this._model.peerConnectionId) throw new Error(`TransportId mismatch. PeerConnectionId: ${ this._model.peerConnectionId } TransportId: ${ sample.transportId}`);
|
|
297
303
|
|
|
298
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
|
+
}
|
|
299
309
|
|
|
300
310
|
const report: PeerConnectionTransportReport = {
|
|
301
311
|
serviceId: this.client.call.serviceId,
|
|
@@ -315,11 +325,6 @@ export class ObservedPeerConnection extends EventEmitter {
|
|
|
315
325
|
this.emit('update');
|
|
316
326
|
}
|
|
317
327
|
|
|
318
|
-
public updateSelectedCandidate(localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate) {
|
|
319
|
-
this._selectedLocalIceCandidate = localCandidate;
|
|
320
|
-
this._selectedRemoteIceCandidate = remoteCandidate;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
328
|
public async createInboundAudioTrack(config: Omit<ObservedInboundTrackConfig<'audio'>, 'kind'>): Promise<ObservedInboundTrack<'audio'>> {
|
|
324
329
|
if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
|
|
325
330
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import * as Models from './models/Models';
|
|
3
|
+
import { Observer } from './Observer';
|
|
4
|
+
|
|
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
|
+
|
|
12
|
+
export type ObservedSfuEvents = {
|
|
13
|
+
update: [],
|
|
14
|
+
close: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export declare interface ObservedSfu {
|
|
18
|
+
on<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
19
|
+
off<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
20
|
+
once<U extends keyof ObservedSfuEvents>(event: U, listener: (...args: ObservedSfuEvents[U]) => void): this;
|
|
21
|
+
emit<U extends keyof ObservedSfuEvents>(event: U, ...args: ObservedSfuEvents[U]): boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class ObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
|
|
25
|
+
public static async create<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
26
|
+
config: ObservedSfuConfig<T>,
|
|
27
|
+
observer: Observer,
|
|
28
|
+
// reportsCollector: ReportsCollector,
|
|
29
|
+
) {
|
|
30
|
+
const model = new Models.Sfu({
|
|
31
|
+
serviceId: config.serviceId,
|
|
32
|
+
sfuId: config.sfuId,
|
|
33
|
+
sfuTransportIds: [],
|
|
34
|
+
joined: BigInt(config.joined ?? Date.now()),
|
|
35
|
+
});
|
|
36
|
+
const result = new ObservedSfu(
|
|
37
|
+
model,
|
|
38
|
+
config.appData,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const alreadyInserted = await observer.storage.sfuStorage.insert(config.sfuId, model);
|
|
42
|
+
|
|
43
|
+
if (alreadyInserted) throw new Error(`Sfu with id ${config.sfuId} already exists`);
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private constructor(
|
|
49
|
+
private readonly _model: Models.Sfu,
|
|
50
|
+
public readonly appData: AppData,
|
|
51
|
+
) {
|
|
52
|
+
super();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|