@observertc/observer-js 0.40.3-beta → 0.40.4-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.
Files changed (55) hide show
  1. package/lib/CallSummary.d.ts +2 -9
  2. package/lib/CallSummary.d.ts.map +1 -1
  3. package/lib/ObservedCall.d.ts.map +1 -1
  4. package/lib/ObservedCall.js +3 -0
  5. package/lib/ObservedClient.d.ts +3 -0
  6. package/lib/ObservedClient.d.ts.map +1 -1
  7. package/lib/ObservedClient.js +6 -0
  8. package/lib/ObservedDataChannel.d.ts +1 -0
  9. package/lib/ObservedDataChannel.d.ts.map +1 -1
  10. package/lib/ObservedDataChannel.js +1 -1
  11. package/lib/ObservedICE.d.ts +1 -0
  12. package/lib/ObservedICE.d.ts.map +1 -1
  13. package/lib/ObservedICE.js +1 -1
  14. package/lib/ObservedInboundTrack.d.ts +1 -0
  15. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  16. package/lib/ObservedInboundTrack.js +1 -1
  17. package/lib/ObservedOutboundTrack.d.ts +1 -0
  18. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  19. package/lib/ObservedOutboundTrack.js +1 -1
  20. package/lib/ObservedPeerConnection.d.ts +3 -0
  21. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  22. package/lib/ObservedPeerConnection.js +13 -1
  23. package/lib/ObservedSfu.d.ts +12 -0
  24. package/lib/ObservedSfu.d.ts.map +1 -1
  25. package/lib/ObservedSfu.js +76 -0
  26. package/lib/ObservedSfuInboundRtpPad.d.ts +38 -0
  27. package/lib/ObservedSfuInboundRtpPad.d.ts.map +1 -0
  28. package/lib/ObservedSfuInboundRtpPad.js +67 -0
  29. package/lib/ObservedSfuOutboundRtpPad.d.ts +38 -0
  30. package/lib/ObservedSfuOutboundRtpPad.d.ts.map +1 -0
  31. package/lib/ObservedSfuOutboundRtpPad.js +68 -0
  32. package/lib/ObservedSfuSctpChannel.d.ts +38 -0
  33. package/lib/ObservedSfuSctpChannel.d.ts.map +1 -0
  34. package/lib/ObservedSfuSctpChannel.js +53 -0
  35. package/lib/ObservedSfuTransport.d.ts +54 -0
  36. package/lib/ObservedSfuTransport.d.ts.map +1 -0
  37. package/lib/ObservedSfuTransport.js +116 -0
  38. package/lib/Observer.d.ts +5 -0
  39. package/lib/Observer.d.ts.map +1 -1
  40. package/lib/Observer.js +21 -0
  41. package/package.json +1 -1
  42. package/src/CallSummary.ts +2 -10
  43. package/src/ObservedCall.ts +3 -0
  44. package/src/ObservedClient.ts +10 -1
  45. package/src/ObservedDataChannel.ts +2 -0
  46. package/src/ObservedICE.ts +3 -0
  47. package/src/ObservedInboundTrack.ts +2 -0
  48. package/src/ObservedOutboundTrack.ts +2 -0
  49. package/src/ObservedPeerConnection.ts +16 -0
  50. package/src/ObservedSfu.ts +118 -2
  51. package/src/ObservedSfuInboundRtpPad.ts +100 -0
  52. package/src/ObservedSfuOutboundRtpPad.ts +101 -0
  53. package/src/ObservedSfuSctpChannel.ts +97 -0
  54. package/src/ObservedSfuTransport.ts +179 -0
  55. package/src/Observer.ts +31 -1
@@ -1,9 +1,12 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { Observer } from './Observer';
3
3
  import { SfuSample } from '@observertc/sample-schemas-js';
4
+ import { ObservedSfuTransport, ObservedSfuTransportModel } from './ObservedSfuTransport';
5
+ import { SfuEventReport, SfuExtensionReport } from '@observertc/report-schemas-js';
4
6
 
5
7
  export type ObservedSfuModel= {
6
8
  serviceId: string;
9
+ mediaUnitId: string;
7
10
  sfuId: string;
8
11
  joined?: number;
9
12
  };
@@ -11,6 +14,7 @@ export type ObservedSfuModel= {
11
14
  export type ObservedSfuEvents = {
12
15
  update: [],
13
16
  close: [],
17
+ newtransport: [ObservedSfuTransport],
14
18
  };
15
19
 
16
20
  export declare interface ObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>> {
@@ -28,7 +32,12 @@ export class ObservedSfu<AppData extends Record<string, unknown> = Record<string
28
32
 
29
33
  private _updated = Date.now();
30
34
  private _closed = false;
31
-
35
+ private _marker?: string;
36
+ private _timeZoneOffsetInHours?: number;
37
+ private _left?: number;
38
+
39
+ private readonly _transports = new Map<string, ObservedSfuTransport>();
40
+
32
41
  public constructor(
33
42
  private readonly _model: ObservedSfuModel,
34
43
  public readonly observer: Observer,
@@ -38,11 +47,23 @@ export class ObservedSfu<AppData extends Record<string, unknown> = Record<string
38
47
  this.setMaxListeners(Infinity);
39
48
  }
40
49
 
50
+ public get marker() {
51
+ return this._marker;
52
+ }
53
+
54
+ public get reports() {
55
+ return this.observer.reports;
56
+ }
57
+
41
58
  public get serviceId() {
42
59
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
43
60
  return this._model.serviceId!;
44
61
  }
45
62
 
63
+ public get mediaUnitId() {
64
+ return this._model.mediaUnitId;
65
+ }
66
+
46
67
  public get sfuId() {
47
68
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
48
69
  return this._model.sfuId!;
@@ -53,6 +74,10 @@ export class ObservedSfu<AppData extends Record<string, unknown> = Record<string
53
74
  return Number(this._model.joined!);
54
75
  }
55
76
 
77
+ public get transports(): ReadonlyMap<string, ObservedSfuTransport> {
78
+ return this._transports;
79
+ }
80
+
56
81
  public get updated() {
57
82
  return this._updated;
58
83
  }
@@ -64,6 +89,9 @@ export class ObservedSfu<AppData extends Record<string, unknown> = Record<string
64
89
  public close() {
65
90
  if (this._closed) return;
66
91
  this._closed = true;
92
+
93
+ [ ...this._transports.values() ].forEach((transport) => transport.close());
94
+
67
95
  this.emit('close');
68
96
  }
69
97
 
@@ -71,7 +99,95 @@ export class ObservedSfu<AppData extends Record<string, unknown> = Record<string
71
99
  if (this._closed) throw new Error(`Sfu ${this.sfuId} is closed`);
72
100
  if (sample.sfuId !== this.sfuId) throw new Error(`Sfu ${this.sfuId} is not the same as sample.sfuId`);
73
101
 
102
+ if (!this._timeZoneOffsetInHours && sample.timeZoneOffsetInHours) {
103
+ this._timeZoneOffsetInHours = sample.timeZoneOffsetInHours;
104
+ }
105
+
106
+ for (const customSfuEvents of sample.customSfuEvents ?? []) {
107
+ const report: SfuEventReport = {
108
+ serviceId: this.serviceId,
109
+ mediaUnitId: this.mediaUnitId,
110
+ sfuId: this.sfuId,
111
+ ...customSfuEvents,
112
+ timestamp: sample.timestamp,
113
+ marker: this._marker,
114
+ };
115
+
116
+ this.reports.addSfuEventReport(report);
117
+ }
118
+
119
+ for (const extensionStats of sample.extensionStats ?? []) {
120
+
121
+ const report: SfuExtensionReport = {
122
+ serviceId: this.serviceId,
123
+ mediaUnitId: this.mediaUnitId,
124
+ sfuId: this.sfuId,
125
+ extensionType: extensionStats.type,
126
+ payload: extensionStats.payload,
127
+ timestamp: sample.timestamp,
128
+ };
129
+
130
+ this.reports.addSfuExtensionReport(report);
131
+ }
132
+
133
+ for (const transport of sample.transports ?? []) {
134
+ const observedSfuTransport = this._transports.get(transport.transportId) ?? this._createTransport({
135
+ sfuTransportId: transport.transportId,
136
+ });
137
+
138
+ observedSfuTransport.update(transport, sample.timestamp);
139
+ }
140
+
141
+ for (const inboundRtpPad of sample.inboundRtpPads ?? []) {
142
+ const transport = this._transports.get(inboundRtpPad.transportId) ?? this._createTransport({
143
+ sfuTransportId: inboundRtpPad.transportId,
144
+ });
145
+
146
+ const observedSfuInboundRtpPad = transport.inboundRtpPads.get(inboundRtpPad.padId) ?? transport.createSfuInboundRtpPad({
147
+ sfuInboundRtpPadId: inboundRtpPad.padId,
148
+ });
149
+
150
+ observedSfuInboundRtpPad.update(inboundRtpPad, sample.timestamp);
151
+ }
152
+
153
+ for (const outboundRtpPad of sample.outboundRtpPads ?? []) {
154
+ const transport = this._transports.get(outboundRtpPad.transportId) ?? this._createTransport({
155
+ sfuTransportId: outboundRtpPad.transportId,
156
+ });
157
+
158
+ const observedSfuOutboundRtpPad = transport.outboundRtpPads.get(outboundRtpPad.padId) ?? transport.createSfuOutboundRtpPad({
159
+ sfuOutboundRtpPadId: outboundRtpPad.padId,
160
+ });
161
+
162
+ observedSfuOutboundRtpPad.update(outboundRtpPad, sample.timestamp);
163
+ }
164
+
165
+ for (const sctpChannel of sample.sctpChannels ?? []) {
166
+ const transport = this._transports.get(sctpChannel.transportId) ?? this._createTransport({
167
+ sfuTransportId: sctpChannel.transportId,
168
+ });
169
+
170
+ const observedSfuSctpChannel = transport.sctpChannels.get(sctpChannel.channelId) ?? transport.createSfuSctpChannel({
171
+ sfuSctpChannelId: sctpChannel.channelId,
172
+ });
173
+
174
+ observedSfuSctpChannel.update(sctpChannel, sample.timestamp);
175
+ }
176
+
74
177
  this._updated = Date.now();
75
178
  this.emit('update');
76
179
  }
77
- }
180
+
181
+ private _createTransport(model: ObservedSfuTransportModel) {
182
+ const result = new ObservedSfuTransport(model, this);
183
+
184
+ result.once('close', () => {
185
+ this._transports.delete(model.sfuTransportId);
186
+ });
187
+ this._transports.set(model.sfuTransportId, result);
188
+
189
+ this.emit('newtransport', result);
190
+
191
+ return result;
192
+ }
193
+ }
@@ -0,0 +1,100 @@
1
+ import { EventEmitter } from 'events';
2
+ import { SfuInboundRtpPad } from '@observertc/sample-schemas-js';
3
+ import { ObservedSfuTransport } from './ObservedSfuTransport';
4
+ import { SfuInboundRtpPadReport } from '@observertc/report-schemas-js';
5
+
6
+ export type ObservedSfuInboundRtpPadModel= {
7
+ sfuInboundRtpPadId: string;
8
+ };
9
+
10
+ export type ObservedSfuInboundRtpPadEvents = {
11
+ update: [],
12
+ close: [],
13
+ };
14
+
15
+ export declare interface ObservedSfuInboundRtpPad {
16
+ on<U extends keyof ObservedSfuInboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuInboundRtpPadEvents[U]) => void): this;
17
+ off<U extends keyof ObservedSfuInboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuInboundRtpPadEvents[U]) => void): this;
18
+ once<U extends keyof ObservedSfuInboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuInboundRtpPadEvents[U]) => void): this;
19
+ emit<U extends keyof ObservedSfuInboundRtpPadEvents>(event: U, ...args: ObservedSfuInboundRtpPadEvents[U]): boolean;
20
+
21
+ update(sample: SfuInboundRtpPad, timestamp: number): void;
22
+ }
23
+
24
+ export class ObservedSfuInboundRtpPad extends EventEmitter {
25
+ public readonly created = Date.now();
26
+
27
+ public stats?: SfuInboundRtpPad;
28
+ public marker?: string;
29
+ private _updated = Date.now();
30
+ private _closed = false;
31
+
32
+ public constructor(
33
+ private readonly _model: ObservedSfuInboundRtpPadModel,
34
+ public readonly transport: ObservedSfuTransport,
35
+ ) {
36
+ super();
37
+ this.setMaxListeners(Infinity);
38
+ }
39
+
40
+ public get reports() {
41
+ return this.transport.reports;
42
+ }
43
+
44
+ public get serviceId() {
45
+ return this.transport.serviceId;
46
+ }
47
+
48
+ public get mediaUnitId() {
49
+ return this.transport.mediaUnitId;
50
+ }
51
+
52
+ public get sfuId() {
53
+ return this.transport.sfuId;
54
+ }
55
+
56
+ public get sfuTransportId() {
57
+ return this.transport.sfuTransportId;
58
+ }
59
+
60
+ public get sfuRtpPadId() {
61
+ return this._model.sfuInboundRtpPadId;
62
+ }
63
+
64
+ public get updated() {
65
+ return this._updated;
66
+ }
67
+
68
+ public get closed() {
69
+ return this._closed;
70
+ }
71
+
72
+ public close() {
73
+ if (this._closed) return;
74
+ this._closed = true;
75
+ this.emit('close');
76
+ }
77
+
78
+ public update(sample: SfuInboundRtpPad, timestamp: number) {
79
+
80
+ const { streamId: sfuStreamId, padId: rtpPadId, ...reportData } = sample;
81
+
82
+ const report: SfuInboundRtpPadReport = {
83
+ serviceId: this.serviceId,
84
+ mediaUnitId: this.mediaUnitId,
85
+ sfuId: this.sfuId,
86
+ callId: undefined, // TODO: where to get this from?
87
+ sfuStreamId,
88
+ rtpPadId,
89
+ ...reportData,
90
+ timestamp,
91
+ marker: this.marker,
92
+ };
93
+
94
+ this.reports.addSfuInboundRtpPadReport(report);
95
+ this.stats = sample;
96
+
97
+ this._updated = Date.now();
98
+ this.emit('update');
99
+ }
100
+ }
@@ -0,0 +1,101 @@
1
+ import { EventEmitter } from 'events';
2
+ import { SfuOutboundRtpPad } from '@observertc/sample-schemas-js';
3
+ import { SfuOutboundRtpPadReport } from '@observertc/report-schemas-js';
4
+ import { ObservedSfuTransport } from './ObservedSfuTransport';
5
+
6
+ export type ObservedSfuOutboundRtpPadModel= {
7
+ sfuOutboundRtpPadId: string;
8
+ };
9
+
10
+ export type ObservedSfuOutboundRtpPadEvents = {
11
+ update: [],
12
+ close: [],
13
+ };
14
+
15
+ export declare interface ObservedSfuOutboundRtpPad {
16
+ on<U extends keyof ObservedSfuOutboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuOutboundRtpPadEvents[U]) => void): this;
17
+ off<U extends keyof ObservedSfuOutboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuOutboundRtpPadEvents[U]) => void): this;
18
+ once<U extends keyof ObservedSfuOutboundRtpPadEvents>(event: U, listener: (...args: ObservedSfuOutboundRtpPadEvents[U]) => void): this;
19
+ emit<U extends keyof ObservedSfuOutboundRtpPadEvents>(event: U, ...args: ObservedSfuOutboundRtpPadEvents[U]): boolean;
20
+
21
+ update(sample: SfuOutboundRtpPad, timestamp: number): void;
22
+ }
23
+
24
+ export class ObservedSfuOutboundRtpPad extends EventEmitter {
25
+ public readonly created = Date.now();
26
+
27
+ public stats?: SfuOutboundRtpPad;
28
+ public marker?: string;
29
+ private _updated = Date.now();
30
+ private _closed = false;
31
+
32
+ public constructor(
33
+ private readonly _model: ObservedSfuOutboundRtpPadModel,
34
+ public readonly transport: ObservedSfuTransport,
35
+ ) {
36
+ super();
37
+ this.setMaxListeners(Infinity);
38
+ }
39
+
40
+ public get reports() {
41
+ return this.transport.reports;
42
+ }
43
+
44
+ public get serviceId() {
45
+ return this.transport.serviceId;
46
+ }
47
+
48
+ public get mediaUnitId() {
49
+ return this.transport.mediaUnitId;
50
+ }
51
+
52
+ public get sfuId() {
53
+ return this.transport.sfuId;
54
+ }
55
+
56
+ public get sfuTransportId() {
57
+ return this.transport.sfuTransportId;
58
+ }
59
+
60
+ public get sfuRtpPadId() {
61
+ return this._model.sfuOutboundRtpPadId;
62
+ }
63
+
64
+ public get updated() {
65
+ return this._updated;
66
+ }
67
+
68
+ public get closed() {
69
+ return this._closed;
70
+ }
71
+
72
+ public close() {
73
+ if (this._closed) return;
74
+ this._closed = true;
75
+ this.emit('close');
76
+ }
77
+
78
+ public update(sample: SfuOutboundRtpPad, timestamp: number) {
79
+
80
+ const { streamId: sfuStreamId, padId: rtpPadId, sinkId: sfuSinkId, ...reportData } = sample;
81
+
82
+ const report: SfuOutboundRtpPadReport = {
83
+ serviceId: this.serviceId,
84
+ mediaUnitId: this.mediaUnitId,
85
+ sfuId: this.sfuId,
86
+ callId: undefined, // TODO: where to get this from?
87
+ sfuStreamId,
88
+ sfuSinkId,
89
+ rtpPadId,
90
+ ...reportData,
91
+ timestamp,
92
+ marker: this.marker,
93
+ };
94
+
95
+ this.reports.addSfuOutboundRtpPadReport(report);
96
+ this.stats = sample;
97
+
98
+ this._updated = Date.now();
99
+ this.emit('update');
100
+ }
101
+ }
@@ -0,0 +1,97 @@
1
+ import { EventEmitter } from 'events';
2
+ import { SfuSctpChannel } from '@observertc/sample-schemas-js';
3
+ import { ObservedSfuTransport } from './ObservedSfuTransport';
4
+ import { SfuSctpStreamReport } from '@observertc/report-schemas-js';
5
+
6
+ export type ObservedSfuSctpChannelModel= {
7
+ sfuSctpChannelId: string;
8
+ };
9
+
10
+ export type ObservedSfuSctpChannelEvents = {
11
+ update: [],
12
+ close: [],
13
+ };
14
+
15
+ export declare interface ObservedSfuSctpChannel {
16
+ on<U extends keyof ObservedSfuSctpChannelEvents>(event: U, listener: (...args: ObservedSfuSctpChannelEvents[U]) => void): this;
17
+ off<U extends keyof ObservedSfuSctpChannelEvents>(event: U, listener: (...args: ObservedSfuSctpChannelEvents[U]) => void): this;
18
+ once<U extends keyof ObservedSfuSctpChannelEvents>(event: U, listener: (...args: ObservedSfuSctpChannelEvents[U]) => void): this;
19
+ emit<U extends keyof ObservedSfuSctpChannelEvents>(event: U, ...args: ObservedSfuSctpChannelEvents[U]): boolean;
20
+
21
+ update(sample: SfuSctpChannel, timestamp: number): void;
22
+ }
23
+
24
+ export class ObservedSfuSctpChannel extends EventEmitter {
25
+ public readonly created = Date.now();
26
+
27
+ public stats?: SfuSctpChannel;
28
+ public marker?: string;
29
+ private _updated = Date.now();
30
+ private _closed = false;
31
+
32
+ public constructor(
33
+ private readonly _model: ObservedSfuSctpChannelModel,
34
+ public readonly transport: ObservedSfuTransport,
35
+ ) {
36
+ super();
37
+ this.setMaxListeners(Infinity);
38
+ }
39
+
40
+ public get serviceId() {
41
+ return this.transport.serviceId;
42
+ }
43
+
44
+ public get sfuId() {
45
+ return this.transport.sfuId;
46
+ }
47
+
48
+ public get mediaUnitId() {
49
+ return this.transport.mediaUnitId;
50
+ }
51
+
52
+ public get sfuTransportId() {
53
+ return this.transport.sfuTransportId;
54
+ }
55
+
56
+ public get sfuSctpChannelId() {
57
+ return this._model.sfuSctpChannelId;
58
+ }
59
+
60
+ public get reports() {
61
+ return this.transport.reports;
62
+ }
63
+
64
+ public get updated() {
65
+ return this._updated;
66
+ }
67
+
68
+ public get closed() {
69
+ return this._closed;
70
+ }
71
+
72
+ public close() {
73
+ if (this._closed) return;
74
+ this._closed = true;
75
+ this.emit('close');
76
+ }
77
+
78
+ public update(sample: SfuSctpChannel, timestamp: number) {
79
+
80
+ const report: SfuSctpStreamReport = {
81
+ serviceId: this.serviceId,
82
+ mediaUnitId: this.mediaUnitId,
83
+ sfuId: this.sfuId,
84
+ callId: undefined, // TODO: where to get this from?
85
+ roomId: undefined, // TODO: where to get this from?
86
+ ...sample,
87
+ timestamp,
88
+ marker: this.marker,
89
+ };
90
+
91
+ this.reports.addSfuTransportReport(report);
92
+ this.stats = sample;
93
+
94
+ this._updated = Date.now();
95
+ this.emit('update');
96
+ }
97
+ }
@@ -0,0 +1,179 @@
1
+ import { EventEmitter } from 'events';
2
+ import { SfuTransport } from '@observertc/sample-schemas-js';
3
+ import { ObservedSfu } from './ObservedSfu';
4
+ import { SFUTransportReport } from '@observertc/report-schemas-js';
5
+ import { ObservedSfuInboundRtpPad, ObservedSfuInboundRtpPadModel } from './ObservedSfuInboundRtpPad';
6
+ import { ObservedSfuOutboundRtpPad, ObservedSfuOutboundRtpPadModel } from './ObservedSfuOutboundRtpPad';
7
+ import { ObservedSfuSctpChannel, ObservedSfuSctpChannelModel } from './ObservedSfuSctpChannel';
8
+
9
+ export type ObservedSfuTransportModel= {
10
+ sfuTransportId: string;
11
+ };
12
+
13
+ export type ObservedSfuTransportEvents = {
14
+ update: [],
15
+ close: [],
16
+ newsfuoutboundrtppad: [ObservedSfuOutboundRtpPad],
17
+ newsfuinboundrtppad: [ObservedSfuInboundRtpPad],
18
+ newsfusctpchannel: [ObservedSfuSctpChannel],
19
+ };
20
+
21
+ export declare interface ObservedSfuTransport {
22
+ on<U extends keyof ObservedSfuTransportEvents>(event: U, listener: (...args: ObservedSfuTransportEvents[U]) => void): this;
23
+ off<U extends keyof ObservedSfuTransportEvents>(event: U, listener: (...args: ObservedSfuTransportEvents[U]) => void): this;
24
+ once<U extends keyof ObservedSfuTransportEvents>(event: U, listener: (...args: ObservedSfuTransportEvents[U]) => void): this;
25
+ emit<U extends keyof ObservedSfuTransportEvents>(event: U, ...args: ObservedSfuTransportEvents[U]): boolean;
26
+
27
+ update(sample: SfuTransport, timestamp: number): void;
28
+ }
29
+
30
+ export class ObservedSfuTransport extends EventEmitter {
31
+ public readonly created = Date.now();
32
+
33
+ public stats?: SfuTransport;
34
+ private _updated = Date.now();
35
+ private _closed = false;
36
+ private _marker?: string;
37
+
38
+ private readonly _inboundRtpPads = new Map<string, ObservedSfuInboundRtpPad>();
39
+ private readonly _outboundRtpPads = new Map<string, ObservedSfuOutboundRtpPad>();
40
+ private readonly _sctpChannels = new Map<string, ObservedSfuSctpChannel>();
41
+
42
+ public constructor(
43
+ private readonly _model: ObservedSfuTransportModel,
44
+ public readonly sfu: ObservedSfu,
45
+ ) {
46
+ super();
47
+ this.setMaxListeners(Infinity);
48
+ }
49
+
50
+ public get inboundRtpPads(): ReadonlyMap<string, ObservedSfuInboundRtpPad> {
51
+ return this._inboundRtpPads;
52
+ }
53
+
54
+ public get outboundRtpPads(): ReadonlyMap<string, ObservedSfuOutboundRtpPad> {
55
+ return this._outboundRtpPads;
56
+ }
57
+
58
+ public get sctpChannels(): ReadonlyMap<string, ObservedSfuSctpChannel> {
59
+ return this._sctpChannels;
60
+ }
61
+
62
+ public get serviceId() {
63
+ return this.sfu.serviceId;
64
+ }
65
+
66
+ public get mediaUnitId() {
67
+ return this.sfu.mediaUnitId;
68
+ }
69
+
70
+ public get sfuId() {
71
+ return this.sfu.sfuId;
72
+ }
73
+
74
+ public get sfuTransportId() {
75
+ return this._model.sfuTransportId;
76
+ }
77
+
78
+ public get reports() {
79
+ return this.sfu.reports;
80
+ }
81
+
82
+ public get updated() {
83
+ return this._updated;
84
+ }
85
+
86
+ public get marker() {
87
+ return this._marker;
88
+ }
89
+
90
+ public set marker(value: string | undefined) {
91
+ this._marker = value;
92
+ this._inboundRtpPads.forEach((pad) => (pad.marker = value));
93
+ this._outboundRtpPads.forEach((pad) => (pad.marker = value));
94
+ this._sctpChannels.forEach((channel) => (channel.marker = value));
95
+ }
96
+
97
+ public get closed() {
98
+ return this._closed;
99
+ }
100
+
101
+ public close() {
102
+ if (this._closed) return;
103
+ this._closed = true;
104
+
105
+ [ ...this._inboundRtpPads.values() ].forEach((pad) => pad.close());
106
+ [ ...this._outboundRtpPads.values() ].forEach((pad) => pad.close());
107
+ [ ...this._sctpChannels.values() ].forEach((channel) => channel.close());
108
+
109
+ this.emit('close');
110
+ }
111
+
112
+ public update(sample: SfuTransport, timestamp: number) {
113
+
114
+ const report: SFUTransportReport = {
115
+ serviceId: this.serviceId,
116
+ mediaUnitId: this.mediaUnitId,
117
+ sfuId: this.sfuId,
118
+ callId: undefined, // TODO: where to get this from?
119
+ roomId: undefined, // TODO: where to get this from?
120
+ ...sample,
121
+ timestamp,
122
+ marker: this.marker,
123
+ };
124
+
125
+ this.reports.addSfuTransportReport(report);
126
+ this.stats = sample;
127
+
128
+ this._updated = Date.now();
129
+ this.emit('update');
130
+ }
131
+
132
+ public createSfuOutboundRtpPad(model: ObservedSfuOutboundRtpPadModel) {
133
+ if (this._closed) throw new Error('Cannot create SfuOutboundRtpPad on closed SfuTransport');
134
+ if (this._inboundRtpPads.has(model.sfuOutboundRtpPadId)) throw new Error(`SfuOutboundRtpPad with id ${model.sfuOutboundRtpPadId} already exists`);
135
+
136
+ const result = new ObservedSfuOutboundRtpPad(model, this);
137
+
138
+ result.once('close', () => {
139
+ this._outboundRtpPads.delete(model.sfuOutboundRtpPadId);
140
+ });
141
+ this._outboundRtpPads.set(model.sfuOutboundRtpPadId, result);
142
+
143
+ this.emit('newsfuoutboundrtppad', result);
144
+
145
+ return result;
146
+ }
147
+
148
+ public createSfuInboundRtpPad(model: ObservedSfuInboundRtpPadModel) {
149
+ if (this._closed) throw new Error('Cannot create SfuInboundRtpPad on closed SfuTransport');
150
+ if (this._inboundRtpPads.has(model.sfuInboundRtpPadId)) throw new Error(`SfuInboundRtpPad with id ${model.sfuInboundRtpPadId} already exists`);
151
+
152
+ const result = new ObservedSfuInboundRtpPad(model, this);
153
+
154
+ result.once('close', () => {
155
+ this._inboundRtpPads.delete(model.sfuInboundRtpPadId);
156
+ });
157
+ this._inboundRtpPads.set(model.sfuInboundRtpPadId, result);
158
+
159
+ this.emit('newsfuinboundrtppad', result);
160
+
161
+ return result;
162
+ }
163
+
164
+ public createSfuSctpChannel(model: ObservedSfuSctpChannelModel) {
165
+ if (this._closed) throw new Error('Cannot create SfuSctpChannel on closed SfuTransport');
166
+ if (this._sctpChannels.has(model.sfuSctpChannelId)) throw new Error(`SfuSctpChannel with id ${model.sfuSctpChannelId} already exists`);
167
+
168
+ const result = new ObservedSfuSctpChannel(model, this);
169
+
170
+ result.once('close', () => {
171
+ this._sctpChannels.delete(model.sfuSctpChannelId);
172
+ });
173
+ this._sctpChannels.set(model.sfuSctpChannelId, result);
174
+
175
+ this.emit('newsfusctpchannel', result);
176
+
177
+ return result;
178
+ }
179
+ }
package/src/Observer.ts CHANGED
@@ -5,12 +5,13 @@ import { EventEmitter } from 'events';
5
5
  import { PartialBy } from './common/utils';
6
6
  import { createCallEndedEventReport, createCallStartedEventReport } from './common/callEventReports';
7
7
  import { ObserverSinkContext } from './common/types';
8
+ import { ObservedSfu, ObservedSfuModel } from './ObservedSfu';
8
9
 
9
10
  const logger = createLogger('Observer');
10
11
 
11
12
  export type ObserverEvents = {
12
13
  'newcall': [ObservedCall],
13
- // 'newsfu': [ObservedSfu],
14
+ 'newsfu': [ObservedSfu],
14
15
  'reports': [ObserverSinkContext],
15
16
  'close': [],
16
17
  }
@@ -67,6 +68,8 @@ export class Observer extends EventEmitter {
67
68
 
68
69
  public readonly reports = new ReportsCollector();
69
70
  private readonly _observedCalls = new Map<string, ObservedCall>();
71
+ private readonly _observedSfus = new Map<string, ObservedSfu>();
72
+
70
73
  private _reportTimer?: ReturnType<typeof setTimeout>;
71
74
  private _closed = false;
72
75
  public constructor(
@@ -135,10 +138,37 @@ export class Observer extends EventEmitter {
135
138
  return call;
136
139
  }
137
140
 
141
+ public createObservedSfu<AppData extends Record<string, unknown> = Record<string, unknown>>(
142
+ model: ObservedSfuModel,
143
+ appData: AppData,
144
+ ): ObservedSfu<AppData> {
145
+ if (this._closed) {
146
+ throw new Error('Attempted to create an sfu source on a closed observer');
147
+ }
148
+
149
+ const sfu = new ObservedSfu<AppData>(model, this, appData);
150
+
151
+ if (this._closed) throw new Error('Cannot create an observed sfu on a closed observer');
152
+ if (this._observedSfus.has(sfu.sfuId)) throw new Error(`Observed SFU with id ${sfu.sfuId} already exists`);
153
+
154
+ sfu.once('close', () => {
155
+ this._observedSfus.delete(sfu.sfuId);
156
+ });
157
+
158
+ this._observedSfus.set(sfu.sfuId, sfu);
159
+ this.emit('newsfu', sfu);
160
+
161
+ return sfu;
162
+ }
163
+
138
164
  public get observedCalls(): ReadonlyMap<string, ObservedCall> {
139
165
  return this._observedCalls;
140
166
  }
141
167
 
168
+ public get observedSfus(): ReadonlyMap<string, ObservedSfu> {
169
+ return this._observedSfus;
170
+ }
171
+
142
172
  public get closed() {
143
173
  return this._closed;
144
174
  }