@observertc/observer-js 0.9.22-beta → 0.9.24-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 (53) hide show
  1. package/lib/Observer.d.ts +26 -0
  2. package/lib/Observer.d.ts.map +1 -1
  3. package/lib/Observer.js +8 -0
  4. package/lib/entries/CallEntry.d.ts +8 -0
  5. package/lib/entries/CallEntry.d.ts.map +1 -1
  6. package/lib/entries/ClientEntry.d.ts +4 -0
  7. package/lib/entries/ClientEntry.d.ts.map +1 -1
  8. package/lib/entries/ClientEntry.js +13 -0
  9. package/lib/entries/InboundTrackEntry.d.ts +12 -0
  10. package/lib/entries/InboundTrackEntry.d.ts.map +1 -1
  11. package/lib/entries/OutboundTrackEntry.d.ts +12 -0
  12. package/lib/entries/OutboundTrackEntry.d.ts.map +1 -1
  13. package/lib/entries/PeerConnectionEntry.d.ts +36 -0
  14. package/lib/entries/PeerConnectionEntry.d.ts.map +1 -1
  15. package/lib/evaluators/SetClientsCoordinateEvaluator.d.ts +9 -0
  16. package/lib/evaluators/SetClientsCoordinateEvaluator.d.ts.map +1 -0
  17. package/lib/evaluators/SetClientsCoordinateEvaluator.js +95 -0
  18. package/lib/index.d.ts +1 -0
  19. package/lib/index.d.ts.map +1 -1
  20. package/lib/middlewares/visitors/visitInboundAudioTrack.d.ts.map +1 -1
  21. package/lib/middlewares/visitors/visitInboundAudioTrack.js +2 -2
  22. package/lib/middlewares/visitors/visitInboundVideoTrack.d.ts.map +1 -1
  23. package/lib/middlewares/visitors/visitInboundVideoTrack.js +1 -0
  24. package/lib/middlewares/visitors/visitPeerConnection.d.ts.map +1 -1
  25. package/lib/middlewares/visitors/visitPeerConnection.js +24 -21
  26. package/lib/models/Models.d.ts +83 -0
  27. package/lib/models/Models.d.ts.map +1 -1
  28. package/lib/models/Models.js +88 -1
  29. package/lib/processes/joinClientProcess.d.ts.map +1 -1
  30. package/lib/processes/joinClientProcess.js +3 -0
  31. package/lib/samples/ObservedCall.d.ts +1 -0
  32. package/lib/samples/ObservedCall.d.ts.map +1 -1
  33. package/lib/sinks/ObserverSink.d.ts.map +1 -1
  34. package/lib/sinks/ObserverSink.js +1 -0
  35. package/lib/sources/ObservedCallSource.d.ts +4 -2
  36. package/lib/sources/ObservedCallSource.d.ts.map +1 -1
  37. package/lib/sources/Sources.d.ts +2 -2
  38. package/lib/sources/Sources.d.ts.map +1 -1
  39. package/lib/sources/Sources.js +17 -5
  40. package/package.json +2 -1
  41. package/src/Observer.ts +10 -0
  42. package/src/entries/ClientEntry.ts +16 -0
  43. package/src/evaluators/SetClientsCoordinateEvaluator.ts +78 -0
  44. package/src/index.ts +1 -0
  45. package/src/middlewares/visitors/visitInboundAudioTrack.ts +4 -2
  46. package/src/middlewares/visitors/visitInboundVideoTrack.ts +1 -0
  47. package/src/middlewares/visitors/visitPeerConnection.ts +64 -59
  48. package/src/models/Models.ts +155 -0
  49. package/src/processes/joinClientProcess.ts +3 -0
  50. package/src/samples/ObservedCall.ts +2 -1
  51. package/src/sinks/ObserverSink.ts +1 -0
  52. package/src/sources/ObservedCallSource.ts +6 -2
  53. package/src/sources/Sources.ts +22 -11
@@ -0,0 +1,78 @@
1
+ import { createLogger } from '../common/logger';
2
+ import * as Models from '../models/Models';
3
+ import ipaddr from 'ipaddr.js';
4
+ import { EvaluatorProcess } from '..';
5
+
6
+ export const logger = createLogger('SetClientsCoordinateEvaluator');
7
+
8
+ export type ClientCoordinate = {
9
+ latitude: number;
10
+ longitude: number;
11
+ };
12
+
13
+ export type GetClientCoordinate = (ipAddress: string) => ClientCoordinate | undefined;
14
+
15
+ export function createSetClientsCoordinateEvaluator(
16
+ getCoordinate: GetClientCoordinate,
17
+ ): EvaluatorProcess {
18
+ const process: EvaluatorProcess = async (context) => {
19
+
20
+ const clientIds = new Set<string>();
21
+
22
+ for (const observedCall of context.observedCalls.observedCalls()) {
23
+ for (const observedClient of observedCall.observedClients()) {
24
+ clientIds.add(observedClient.clientId);
25
+ }
26
+ }
27
+ if (clientIds.size < 1) return;
28
+
29
+ const storedClients = await context.storages.clientStorage.getAll(clientIds);
30
+
31
+ for (const observedCall of context.observedCalls.observedCalls()) {
32
+ for (const observedClient of observedCall.observedClients()) {
33
+ const { clientId } = observedClient;
34
+ const storedClient = storedClients.get(clientId);
35
+
36
+ if (!storedClient || storedClient.coordinates) continue;
37
+
38
+ let ipAddress: string | undefined;
39
+
40
+ searchForIp: for (const observedPeerConnection of observedClient.observedPeerConnections()) {
41
+ for (const iceLocalCandidate of observedPeerConnection.iceLocalCandidates()) {
42
+ if (!iceLocalCandidate.address) continue;
43
+
44
+ try {
45
+ const addr = ipaddr.parse(iceLocalCandidate.address);
46
+
47
+ ipAddress = addr.toString();
48
+
49
+ break searchForIp;
50
+ } catch (e) {
51
+ logger.warn(`Failed to parse ip address: ${iceLocalCandidate.address}`, e);
52
+
53
+ continue;
54
+ }
55
+
56
+ }
57
+ }
58
+
59
+ if (!ipAddress) continue;
60
+
61
+ try {
62
+ const coordinate = getCoordinate(ipAddress);
63
+
64
+ if (!coordinate) continue;
65
+
66
+ storedClient.coordinates = new Models.ClientCoordinate({
67
+ latitude: coordinate.latitude,
68
+ longitude: coordinate.longitude,
69
+ });
70
+ } catch (e) {
71
+ logger.warn(`Failed to get coordinate for ip address: ${ipAddress}`, e);
72
+ }
73
+ }
74
+ }
75
+ };
76
+
77
+ return process;
78
+ }
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export type { SfuTransportEntry } from './entries/SfuTransportEntry';
23
23
  export type { SfuInboundRtpPadEntry } from './entries/SfuInboundRtpPadEntry';
24
24
  export type { SfuOutboundRtpPadEntry } from './entries/SfuOutboundRtpPadEntry';
25
25
  export type { SfuSctpChannelEntry } from './entries/SfuSctpChannelEntry';
26
+ export type { GetClientCoordinate, ClientCoordinate } from './evaluators/SetClientsCoordinateEvaluator';
26
27
 
27
28
  export type {
28
29
  Samples,
@@ -92,7 +92,7 @@ export function visitInboundAudioTrack(
92
92
  } = inboundAudioSample;
93
93
 
94
94
  const ssrc = BigInt(sample_ssrc);
95
- const videoStats = new Samples_ClientSample_InboundAudioTrack({
95
+ const audioStats = new Samples_ClientSample_InboundAudioTrack({
96
96
  ssrc,
97
97
  bytesReceived: BigInt(sample_bytesReceived ?? -1),
98
98
  bytesSent: BigInt(sample_bytesSent ?? -1),
@@ -103,9 +103,11 @@ export function visitInboundAudioTrack(
103
103
  ...sample,
104
104
  });
105
105
 
106
- statsMap.set(ssrc, videoStats);
106
+ statsMap.set(ssrc, audioStats);
107
107
  }
108
108
  }
109
109
  storedInboundAudioTrack.ssrc = [ ...statsMap.keys() ];
110
110
  storedInboundAudioTrack.videoStats = Array.from(statsMap.values());
111
+
112
+
111
113
  }
@@ -96,6 +96,7 @@ export function visitInboundVideoTrack(
96
96
  } = inboundVideoSample;
97
97
 
98
98
  const ssrc = BigInt(sample_ssrc);
99
+ // const lastVideoStats = storedInboundVideoTrack.videoStats.find((stats) => stats.ssrc === ssrc);
99
100
  const videoStats = new Samples_ClientSample_InboundVideoTrack({
100
101
  ssrc,
101
102
  bytesReceived: BigInt(sample_bytesReceived ?? -1),
@@ -80,67 +80,72 @@ export function visitPeerConnection(
80
80
  reports.addIceCandidatePairReport(report);
81
81
  }
82
82
 
83
- if (fetchSamples) {
84
- storedPeerConnection.icelocalCandidates = [];
85
- for (const iceLocalCandidate of observedPeerConnection.iceLocalCandidates()) {
86
- const {
87
- priority,
88
- ...sample
89
- } = iceLocalCandidate;
90
-
91
- storedPeerConnection.icelocalCandidates.push(new Samples_ClientSample_IceLocalCandidate({
92
- ...sample,
93
- priority: BigInt(priority ?? -1)
94
- }));
95
- }
83
+ if (!fetchSamples) return;
96
84
 
97
- storedPeerConnection.iceRemoteCandidates = [];
98
- for (const iceRemoteCandidate of observedPeerConnection.iceRemoteCandidates()) {
99
- const {
100
- priority,
101
- ...sample
102
- } = iceRemoteCandidate;
103
-
104
- storedPeerConnection.iceRemoteCandidates.push(new Samples_ClientSample_IceRemoteCandidate({
105
- ...sample,
106
- priority: BigInt(priority ?? -1)
107
- }));
108
- }
85
+ // restricted area
109
86
 
110
- storedPeerConnection.iceCandidatePairs = [];
111
- for (const iceCandidatePair of observedPeerConnection.iceCandidatePairs()) {
112
- const {
113
- bytesDiscardedOnSend,
114
- bytesReceived,
115
- bytesSent,
116
- lastPacketReceivedTimestamp,
117
- lastPacketSentTimestamp,
118
- ...sample
119
- } = iceCandidatePair;
120
-
121
- storedPeerConnection.iceCandidatePairs.push(new Samples_ClientSample_IceCandidatePair({
122
- ...sample,
123
- bytesDiscardedOnSend: BigInt(bytesDiscardedOnSend ?? -1),
124
- bytesReceived: BigInt(bytesReceived ?? -1),
125
- bytesSent: BigInt(bytesSent ?? -1),
126
- lastPacketReceivedTimestamp: BigInt(lastPacketReceivedTimestamp ?? -1),
127
- lastPacketSentTimestamp: BigInt(lastPacketSentTimestamp ?? -1),
128
- }));
129
- }
87
+ storedPeerConnection.icelocalCandidates = [];
88
+ for (const iceLocalCandidate of observedPeerConnection.iceLocalCandidates()) {
89
+ const {
90
+ priority,
91
+ ...sample
92
+ } = iceLocalCandidate;
130
93
 
131
- storedPeerConnection.transports = [];
132
- for (const pcTransport of observedPeerConnection.transportSamples()) {
133
- const {
134
- bytesReceived,
135
- bytesSent,
136
- ...sample
137
- } = pcTransport;
138
-
139
- storedPeerConnection.transports.push(new Samples_ClientSample_PeerConnectionTransport({
140
- ...sample,
141
- bytesReceived: BigInt(bytesReceived ?? -1),
142
- bytesSent: BigInt(bytesSent ?? -1),
143
- }));
144
- }
94
+ storedPeerConnection.icelocalCandidates.push(new Samples_ClientSample_IceLocalCandidate({
95
+ ...sample,
96
+ priority: BigInt(priority ?? -1)
97
+ }));
145
98
  }
99
+
100
+ storedPeerConnection.iceRemoteCandidates = [];
101
+ for (const iceRemoteCandidate of observedPeerConnection.iceRemoteCandidates()) {
102
+ const {
103
+ priority,
104
+ ...sample
105
+ } = iceRemoteCandidate;
106
+
107
+ storedPeerConnection.iceRemoteCandidates.push(new Samples_ClientSample_IceRemoteCandidate({
108
+ ...sample,
109
+ priority: BigInt(priority ?? -1)
110
+ }));
111
+ }
112
+
113
+ storedPeerConnection.iceCandidatePairs = [];
114
+ for (const iceCandidatePair of observedPeerConnection.iceCandidatePairs()) {
115
+ const {
116
+ bytesDiscardedOnSend,
117
+ bytesReceived,
118
+ bytesSent,
119
+ lastPacketReceivedTimestamp,
120
+ lastPacketSentTimestamp,
121
+ ...sample
122
+ } = iceCandidatePair;
123
+
124
+ storedPeerConnection.iceCandidatePairs.push(new Samples_ClientSample_IceCandidatePair({
125
+ ...sample,
126
+ bytesDiscardedOnSend: BigInt(bytesDiscardedOnSend ?? -1),
127
+ bytesReceived: BigInt(bytesReceived ?? -1),
128
+ bytesSent: BigInt(bytesSent ?? -1),
129
+ lastPacketReceivedTimestamp: BigInt(lastPacketReceivedTimestamp ?? -1),
130
+ lastPacketSentTimestamp: BigInt(lastPacketSentTimestamp ?? -1),
131
+ }));
132
+ }
133
+
134
+ storedPeerConnection.transports = [];
135
+ for (const pcTransport of observedPeerConnection.transportSamples()) {
136
+ const {
137
+ bytesReceived,
138
+ bytesSent,
139
+ ...sample
140
+ } = pcTransport;
141
+
142
+ storedPeerConnection.transports.push(new Samples_ClientSample_PeerConnectionTransport({
143
+ ...sample,
144
+ bytesReceived: BigInt(bytesReceived ?? -1),
145
+ bytesSent: BigInt(bytesSent ?? -1),
146
+ }));
147
+ }
148
+
149
+ // storedPeerConnection.stats = new Models.PeerConnectionStats({
150
+ // });
146
151
  }
@@ -68,6 +68,49 @@ export class Call extends Message<Call> {
68
68
  }
69
69
  }
70
70
 
71
+ /**
72
+ * @generated from message org.observertc.observer.models.ClientCoordinate
73
+ */
74
+ export class ClientCoordinate extends Message<ClientCoordinate> {
75
+ /**
76
+ * @generated from field: required float latitude = 1;
77
+ */
78
+ latitude?: number;
79
+
80
+ /**
81
+ * @generated from field: required float longitude = 2;
82
+ */
83
+ longitude?: number;
84
+
85
+ constructor(data?: PartialMessage<ClientCoordinate>) {
86
+ super();
87
+ proto2.util.initPartial(data, this);
88
+ }
89
+
90
+ static readonly runtime: typeof proto2 = proto2;
91
+ static readonly typeName = "org.observertc.observer.models.ClientCoordinate";
92
+ static readonly fields: FieldList = proto2.util.newFieldList(() => [
93
+ { no: 1, name: "latitude", kind: "scalar", T: 2 /* ScalarType.FLOAT */ },
94
+ { no: 2, name: "longitude", kind: "scalar", T: 2 /* ScalarType.FLOAT */ },
95
+ ]);
96
+
97
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ClientCoordinate {
98
+ return new ClientCoordinate().fromBinary(bytes, options);
99
+ }
100
+
101
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ClientCoordinate {
102
+ return new ClientCoordinate().fromJson(jsonValue, options);
103
+ }
104
+
105
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ClientCoordinate {
106
+ return new ClientCoordinate().fromJsonString(jsonString, options);
107
+ }
108
+
109
+ static equals(a: ClientCoordinate | PlainMessage<ClientCoordinate> | undefined, b: ClientCoordinate | PlainMessage<ClientCoordinate> | undefined): boolean {
110
+ return proto2.util.equals(ClientCoordinate, a, b);
111
+ }
112
+ }
113
+
71
114
  /**
72
115
  * @generated from message org.observertc.observer.models.Client
73
116
  */
@@ -147,6 +190,19 @@ export class Client extends Message<Client> {
147
190
  */
148
191
  engine?: Samples_ClientSample_Engine;
149
192
 
193
+ /**
194
+ * message Stats {
195
+ * optional double maxRttInS = 1;
196
+ * optional double minRttInS = 2;
197
+ * optional string location = 3;
198
+ * }
199
+ *
200
+ * optional Stats stats = 13;
201
+ *
202
+ * @generated from field: optional org.observertc.observer.models.ClientCoordinate coordinates = 16;
203
+ */
204
+ coordinates?: ClientCoordinate;
205
+
150
206
  constructor(data?: PartialMessage<Client>) {
151
207
  super();
152
208
  proto2.util.initPartial(data, this);
@@ -170,6 +226,7 @@ export class Client extends Message<Client> {
170
226
  { no: 13, name: "operationSystem", kind: "message", T: Samples_ClientSample_OperationSystem, opt: true },
171
227
  { no: 14, name: "platform", kind: "message", T: Samples_ClientSample_Platform, opt: true },
172
228
  { no: 15, name: "engine", kind: "message", T: Samples_ClientSample_Engine, opt: true },
229
+ { no: 16, name: "coordinates", kind: "message", T: ClientCoordinate, opt: true },
173
230
  ]);
174
231
 
175
232
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Client {
@@ -322,6 +379,104 @@ export class PeerConnection extends Message<PeerConnection> {
322
379
  }
323
380
  }
324
381
 
382
+ /**
383
+ * @generated from message org.observertc.observer.models.InboundTrackVideoStats
384
+ */
385
+ export class InboundTrackVideoStats extends Message<InboundTrackVideoStats> {
386
+ /**
387
+ * @generated from field: required int32 ssrc = 1;
388
+ */
389
+ ssrc?: number;
390
+
391
+ /**
392
+ * @generated from field: optional float fps = 2;
393
+ */
394
+ fps?: number;
395
+
396
+ /**
397
+ * @generated from field: optional org.observertc.schemas.protobuf.Samples.ClientSample.InboundVideoTrack sample = 4;
398
+ */
399
+ sample?: Samples_ClientSample_InboundVideoTrack;
400
+
401
+ constructor(data?: PartialMessage<InboundTrackVideoStats>) {
402
+ super();
403
+ proto2.util.initPartial(data, this);
404
+ }
405
+
406
+ static readonly runtime: typeof proto2 = proto2;
407
+ static readonly typeName = "org.observertc.observer.models.InboundTrackVideoStats";
408
+ static readonly fields: FieldList = proto2.util.newFieldList(() => [
409
+ { no: 1, name: "ssrc", kind: "scalar", T: 5 /* ScalarType.INT32 */ },
410
+ { no: 2, name: "fps", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true },
411
+ { no: 4, name: "sample", kind: "message", T: Samples_ClientSample_InboundVideoTrack, opt: true },
412
+ ]);
413
+
414
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): InboundTrackVideoStats {
415
+ return new InboundTrackVideoStats().fromBinary(bytes, options);
416
+ }
417
+
418
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): InboundTrackVideoStats {
419
+ return new InboundTrackVideoStats().fromJson(jsonValue, options);
420
+ }
421
+
422
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): InboundTrackVideoStats {
423
+ return new InboundTrackVideoStats().fromJsonString(jsonString, options);
424
+ }
425
+
426
+ static equals(a: InboundTrackVideoStats | PlainMessage<InboundTrackVideoStats> | undefined, b: InboundTrackVideoStats | PlainMessage<InboundTrackVideoStats> | undefined): boolean {
427
+ return proto2.util.equals(InboundTrackVideoStats, a, b);
428
+ }
429
+ }
430
+
431
+ /**
432
+ * @generated from message org.observertc.observer.models.InboundTrackStats
433
+ */
434
+ export class InboundTrackStats extends Message<InboundTrackStats> {
435
+ /**
436
+ * @generated from field: optional float bitrate = 1;
437
+ */
438
+ bitrate?: number;
439
+
440
+ /**
441
+ * @generated from field: optional float fractionalLoss = 2;
442
+ */
443
+ fractionalLoss?: number;
444
+
445
+ /**
446
+ * @generated from field: optional float avgRttInS = 3;
447
+ */
448
+ avgRttInS?: number;
449
+
450
+ constructor(data?: PartialMessage<InboundTrackStats>) {
451
+ super();
452
+ proto2.util.initPartial(data, this);
453
+ }
454
+
455
+ static readonly runtime: typeof proto2 = proto2;
456
+ static readonly typeName = "org.observertc.observer.models.InboundTrackStats";
457
+ static readonly fields: FieldList = proto2.util.newFieldList(() => [
458
+ { no: 1, name: "bitrate", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true },
459
+ { no: 2, name: "fractionalLoss", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true },
460
+ { no: 3, name: "avgRttInS", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true },
461
+ ]);
462
+
463
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): InboundTrackStats {
464
+ return new InboundTrackStats().fromBinary(bytes, options);
465
+ }
466
+
467
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): InboundTrackStats {
468
+ return new InboundTrackStats().fromJson(jsonValue, options);
469
+ }
470
+
471
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): InboundTrackStats {
472
+ return new InboundTrackStats().fromJsonString(jsonString, options);
473
+ }
474
+
475
+ static equals(a: InboundTrackStats | PlainMessage<InboundTrackStats> | undefined, b: InboundTrackStats | PlainMessage<InboundTrackStats> | undefined): boolean {
476
+ return proto2.util.equals(InboundTrackStats, a, b);
477
+ }
478
+ }
479
+
325
480
  /**
326
481
  * @generated from message org.observertc.observer.models.InboundTrack
327
482
  */
@@ -86,6 +86,9 @@ export function createJoinClientProcess(
86
86
 
87
87
  const callEvent = createCallStartedEventReport(serviceId, roomId, callId, Number(started));
88
88
 
89
+ // if ((evaluatorContext?.observedCalls as ObservedCalls).getObservedCall(callId)) {
90
+ // reports.addCallEventReport(callEvent);
91
+ // }
89
92
  reports.addCallEventReport(callEvent);
90
93
  evaluatorContext?.startedCallIds.push(callId);
91
94
  }
@@ -4,6 +4,7 @@ export interface ObservedCall {
4
4
  readonly serviceId: string;
5
5
  readonly roomId: string;
6
6
  readonly callId: string;
7
+ readonly createCallStartedReport?: boolean;
7
8
  observedClients(): IterableIterator<ObservedClient>;
8
9
  getObservedClient(clientId: string): ObservedClient | undefined;
9
10
  }
@@ -36,7 +37,7 @@ export class ObservedCallBuilder {
36
37
  observedClients: () => observedClients.values(),
37
38
  getObservedClient: (clientId) => observedClients.get(clientId),
38
39
  };
39
-
40
+
40
41
  for (const observedClientBuilder of this._observedClientBuilders.values()) {
41
42
  const observedClient = observedClientBuilder.build(observedCall);
42
43
 
@@ -282,6 +282,7 @@ export class SinkImpl implements ReportsCollector, ObserverReportsEmitter {
282
282
 
283
283
  public emit(): number {
284
284
  let result = 0;
285
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
285
286
  const checkAndEmit = (eventName: keyof SinkEventsMap, reports: any[]) => {
286
287
  if (reports.length > 0) {
287
288
  this._emit(eventName, { reports });
@@ -7,15 +7,19 @@ export type ObservedCallSourceConfig<T extends Record<string, unknown>> = {
7
7
  readonly mediaUnitId: string;
8
8
  readonly roomId: string;
9
9
  readonly callId: string;
10
+
11
+ createCallStartedReport?: boolean,
10
12
  };
11
13
 
12
14
  export interface ObservedCallSource<T extends Record<string, unknown> = Record<string, unknown>> extends ObservedCallSourceConfig<T> {
13
15
  readonly closed: boolean;
14
- readonly clients: ReadonlyMap<string, ObservedClientSource>;
16
+ readonly clients: IterableIterator<ObservedClientSource>;
17
+
18
+ getClientSource<U extends Record<string, unknown>>(clientId: string): ObservedClientSource<U> | undefined;
15
19
 
16
20
  createClientSource<U extends Record<string, unknown> = Record<string, unknown>>(
17
21
  context: PartialBy<ObservedClientSourceConfig<U>, 'joined' | 'mediaUnitId' | 'serviceId' | 'callId' | 'roomId'>
18
- ): ObservedClientSource;
22
+ ): ObservedClientSource<U>;
19
23
 
20
24
  // automatically close every
21
25
  close(): void;
@@ -42,12 +42,16 @@ export class Sources {
42
42
 
43
43
  public constructor(public readonly config: SourcesConfig) {}
44
44
 
45
- public getCallSource(callId: string) {
46
- return this._callSources.get(callId);
45
+ public getCallSource<T extends Record<string, unknown> = Record<string, unknown>>(callId: string): ObservedCallSource<T> | undefined {
46
+ if (!this._callSources.has(callId)) return;
47
+
48
+ return this._callSources.get(callId) as ObservedCallSource<T>;
47
49
  }
48
50
 
49
- public getSfuSource(sfuId: string) {
50
- return this._sfuSources.get(sfuId);
51
+ public getSfuSource<T extends Record<string, unknown> = Record<string, unknown>>(sfuId: string) {
52
+ if (!this._sfuSources.has(sfuId)) return;
53
+
54
+ return this._sfuSources.get(sfuId) as ObservedSfuSource<T>;
51
55
  }
52
56
 
53
57
  public on<K extends keyof SourcesEvents>(event: K, listener: (data: SourcesEvents[K]) => void): this {
@@ -82,15 +86,22 @@ export class Sources {
82
86
  appData,
83
87
  serviceId,
84
88
  mediaUnitId,
85
- clients: clientSources,
89
+ get clients() {
90
+ return clientSources.values();
91
+ },
92
+
93
+ getClientSource: <U extends Record<string, unknown> = Record<string, unknown>>(clientId: string) => {
94
+ if (!clientSources.has(clientId)) return;
95
+ else return clientSources.get(clientId) as ObservedClientSource<U>;
96
+ },
86
97
 
87
98
  createClientSource: <U extends Record<string, unknown> = Record<string, unknown>>(context: ObservedClientSourceConfig<U>) => {
88
99
  const existingClientSource = clientSources.get(context.clientId);
89
100
 
90
- if (existingClientSource) return existingClientSource;
101
+ if (existingClientSource) return existingClientSource as ObservedClientSource<U>;
91
102
 
92
103
  const { appData: clientAppData, ...clientConfig } = context;
93
- const clientSource = this._createClientSource<U>({
104
+ const clientSource = this._createClientSource<U>(callSource, {
94
105
  ...callConfig,
95
106
  ...clientConfig,
96
107
  appData: clientAppData,
@@ -105,7 +116,7 @@ export class Sources {
105
116
  };
106
117
  clientSources.set(context.clientId, clientSource);
107
118
 
108
- return clientSource;
119
+ return clientSource as ObservedClientSource<U>;
109
120
  },
110
121
  close: () => {
111
122
  if (closed) {
@@ -129,7 +140,7 @@ export class Sources {
129
140
  return callSource;
130
141
  }
131
142
 
132
- private _createClientSource<T extends Record<string, unknown> = Record<string, unknown>>(config: PartialBy<ObservedClientSourceConfig<T>, 'joined'>): ObservedClientSource<T> {
143
+ private _createClientSource<T extends Record<string, unknown> = Record<string, unknown>>(callSource: ObservedCallSource, config: PartialBy<ObservedClientSourceConfig<T>, 'joined'>): ObservedClientSource<T> {
133
144
  let closed = false;
134
145
  const {
135
146
  appData,
@@ -145,9 +156,9 @@ export class Sources {
145
156
  if (closed) {
146
157
  throw new Error('Cannot accept ClientSample, because the ClientSource is closed');
147
158
  }
148
- const observedCallBuilder = this._observedCallsBuilder.getOrCreateObservedCallBuilder(config.callId, () => {
159
+ const observedCallBuilder = this._observedCallsBuilder.getOrCreateObservedCallBuilder(callSource.callId, () => {
149
160
  return {
150
- ...source,
161
+ ...callSource,
151
162
  };
152
163
  });
153
164
  const observedClientBuilder = observedCallBuilder.getOrCreateObservedClientBuilder(config.clientId, () => {