@observertc/observer-js 1.0.0-beta.5 → 1.0.0-beta.7

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/dist/index.js CHANGED
@@ -55,10 +55,13 @@ __export(src_exports, {
55
55
  ObservedRemoteInboundRtp: () => ObservedRemoteInboundRtp,
56
56
  ObservedRemoteOutboundRtp: () => ObservedRemoteOutboundRtp,
57
57
  Observer: () => Observer,
58
+ RemoteTrackResolver: () => RemoteTrackResolver,
59
+ createDefaultMediasoupRemoteTrackResolverFactory: () => createDefaultMediasoupRemoteTrackResolverFactory,
58
60
  createInMemorySink: () => createInMemorySink,
59
61
  createJsonlFileSink: () => createJsonlFileSink,
60
62
  createJsonlFileSinkFactory: () => createJsonlFileSinkFactory,
61
63
  createLogger: () => createLogger,
64
+ createP2pRemoteTrackResolverFactory: () => createP2pRemoteTrackResolverFactory,
62
65
  setObserverLogger: () => setObserverLogger
63
66
  });
64
67
  module.exports = __toCommonJS(src_exports);
@@ -1128,20 +1131,6 @@ var ObservedInboundTrack = class {
1128
1131
  this._peerConnection = _peerConnection;
1129
1132
  this._inboundRtp = _inboundRtp;
1130
1133
  this._mediaPlayout = _mediaPlayout;
1131
- this.report = {
1132
- trackId: this.id,
1133
- kind: this.kind,
1134
- fractionLostDistribution: {
1135
- gtOrEq050: 0,
1136
- lt001: 0,
1137
- lt005: 0,
1138
- lt010: 0,
1139
- lt020: 0,
1140
- lt050: 0,
1141
- count: 0,
1142
- sum: 0
1143
- }
1144
- };
1145
1134
  }
1146
1135
  timestamp;
1147
1136
  id;
@@ -1154,7 +1143,7 @@ var ObservedInboundTrack = class {
1154
1143
  value: void 0
1155
1144
  };
1156
1145
  appData;
1157
- report;
1146
+ remoteOutboundTrack;
1158
1147
  _visited = false;
1159
1148
  addedAt;
1160
1149
  removedAt;
@@ -1177,25 +1166,11 @@ var ObservedInboundTrack = class {
1177
1166
  getMediaPlayout() {
1178
1167
  return this._mediaPlayout;
1179
1168
  }
1180
- getRemoteOutboundTrack() {
1181
- return this._peerConnection.client.call.remoteTrackResolver?.resolveRemoteOutboundTrack(this);
1182
- }
1183
1169
  update(stats) {
1184
1170
  this._visited = true;
1185
1171
  this.timestamp = stats.timestamp;
1186
1172
  this.calculatedScore.value = stats.score;
1187
1173
  this.attachments = stats.attachments;
1188
- const fl = this._inboundRtp?.fractionLost;
1189
- if (fl !== void 0) {
1190
- if (fl < 0.01) this.report.fractionLostDistribution.lt001 += 1;
1191
- else if (fl < 0.05) this.report.fractionLostDistribution.lt005 += 1;
1192
- else if (fl < 0.1) this.report.fractionLostDistribution.lt010 += 1;
1193
- else if (fl < 0.2) this.report.fractionLostDistribution.lt020 += 1;
1194
- else if (fl < 0.5) this.report.fractionLostDistribution.lt050 += 1;
1195
- else this.report.fractionLostDistribution.gtOrEq050 += 1;
1196
- this.report.fractionLostDistribution.count += 1;
1197
- this.report.fractionLostDistribution.sum += fl;
1198
- }
1199
1174
  }
1200
1175
  };
1201
1176
 
@@ -1208,10 +1183,6 @@ var ObservedOutboundTrack = class {
1208
1183
  this._peerConnection = _peerConnection;
1209
1184
  this._outboundRtps = _outboundRtps;
1210
1185
  this._mediaSource = _mediaSource;
1211
- this.report = {
1212
- trackId: this.id,
1213
- kind: this.kind
1214
- };
1215
1186
  }
1216
1187
  timestamp;
1217
1188
  id;
@@ -1221,7 +1192,7 @@ var ObservedOutboundTrack = class {
1221
1192
  _mediaSource;
1222
1193
  _visited = false;
1223
1194
  appData;
1224
- report;
1195
+ remoteInboundTracks = /* @__PURE__ */ new Set();
1225
1196
  calculatedScore = {
1226
1197
  weight: 1,
1227
1198
  value: void 0
@@ -1247,9 +1218,6 @@ var ObservedOutboundTrack = class {
1247
1218
  getMediaSource() {
1248
1219
  return this._mediaSource;
1249
1220
  }
1250
- getRemoteInboundTracks() {
1251
- return this._peerConnection.client.call.remoteTrackResolver?.resolveRemoteInboundTracks(this);
1252
- }
1253
1221
  update(stats) {
1254
1222
  this._visited = true;
1255
1223
  this.timestamp = stats.timestamp;
@@ -2209,43 +2177,6 @@ var ObservedClient = class extends import_events2.EventEmitter {
2209
2177
  this.settings = {
2210
2178
  closeClientIfIdleForMs: settings.closeClientIfIdleForMs
2211
2179
  };
2212
- this.report = {
2213
- callId: this.call.callId,
2214
- clientId: this.clientId,
2215
- rttDistribution: {
2216
- gtOrEq300ms: 0,
2217
- lt50ms: 0,
2218
- lt150ms: 0,
2219
- lt300ms: 0,
2220
- count: 0,
2221
- sum: 0
2222
- },
2223
- scoreDistribution: {
2224
- "0": 0,
2225
- "1": 0,
2226
- "2": 0,
2227
- "3": 0,
2228
- "4": 0,
2229
- "5": 0,
2230
- count: 0,
2231
- sum: 0
2232
- },
2233
- issues: {},
2234
- totalAudioBytesReceived: 0,
2235
- totalVideoBytesReceived: 0,
2236
- totalAudioBytesSent: 0,
2237
- totalVideoBytesSent: 0,
2238
- totalDataChannelBytesReceived: 0,
2239
- totalDataChannelBytesSent: 0,
2240
- totalDataChannelMessagesReceived: 0,
2241
- totalDataChannelMessagesSent: 0,
2242
- totalInboundRtpBytesReceived: 0,
2243
- totalInboundRtpPacketsLost: 0,
2244
- totalInboundRtpPacketsReceived: 0,
2245
- totalOutboundRtpBytesSent: 0,
2246
- totalOutboundRtpPacketsSent: 0,
2247
- totalNumberOfIssues: 0
2248
- };
2249
2180
  if (this.sink) {
2250
2181
  const onError = (err) => logger2.warn("ClientSampleSink error for client %s: %o", this.clientId, err);
2251
2182
  this.sink.once("close", () => this.sink?.off("error", onError));
@@ -2316,7 +2247,6 @@ var ObservedClient = class extends import_events2.EventEmitter {
2316
2247
  totalScoreSum = 0;
2317
2248
  numberOfScoreMeasurements = 0;
2318
2249
  // public totalNumberOfIssues = 0;
2319
- report;
2320
2250
  mediaDevices = [];
2321
2251
  issues = [];
2322
2252
  _injections = {};
@@ -2449,19 +2379,13 @@ var ObservedClient = class extends import_events2.EventEmitter {
2449
2379
  }
2450
2380
  if (observedPeerConnection.currentRttInMs < 50) {
2451
2381
  this.deltaRttLt50Measurements += 1;
2452
- this.report.rttDistribution.lt50ms += 1;
2453
2382
  } else if (observedPeerConnection.currentRttInMs < 150) {
2454
2383
  this.deltaRttLt150Measurements += 1;
2455
- this.report.rttDistribution.lt150ms += 1;
2456
2384
  } else if (observedPeerConnection.currentRttInMs < 300) {
2457
2385
  this.deltaRttLt300Measurements += 1;
2458
- this.report.rttDistribution.lt300ms += 1;
2459
2386
  } else if (300 <= observedPeerConnection.currentRttInMs) {
2460
2387
  this.deltaRttGtOrEq300Measurements += 1;
2461
- this.report.rttDistribution.gtOrEq300ms += 1;
2462
2388
  }
2463
- this.report.rttDistribution.count += 1;
2464
- this.report.rttDistribution.sum += observedPeerConnection.currentRttInMs;
2465
2389
  sumOfRtts += observedPeerConnection.currentRttInMs;
2466
2390
  ++numberOfRttMeasurements;
2467
2391
  }
@@ -2470,32 +2394,12 @@ var ObservedClient = class extends import_events2.EventEmitter {
2470
2394
  this._processClientEvent(clientEvent);
2471
2395
  }
2472
2396
  this.attachments = sample.attachments;
2473
- this.report.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
2474
- this.report.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
2475
- this.report.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
2476
- this.report.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
2477
- this.report.totalInboundRtpPacketsLost += this.deltaInboundPacketsLost;
2478
- this.report.totalInboundRtpPacketsReceived += this.deltaInboundPacketsReceived;
2479
- this.report.totalOutboundRtpPacketsSent += this.deltaOutboundPacketsSent;
2480
- this.report.totalAudioBytesReceived += this.deltaReceivedAudioBytes;
2481
- this.report.totalVideoBytesReceived += this.deltaReceivedVideoBytes;
2482
- this.report.totalAudioBytesSent += this.deltaSentAudioBytes;
2483
- this.report.totalVideoBytesSent += this.deltaSentVideoBytes;
2484
- this.report.totalInboundRtpBytesReceived += this.deltaTransportReceivedBytes;
2485
- this.report.totalOutboundRtpBytesSent += this.deltaTransportSentBytes;
2486
- this.report.totalNumberOfIssues += this.deltaNumberOfIssues;
2487
2397
  this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedInSeconds;
2488
2398
  this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedInSeconds;
2489
2399
  this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedInSeconds;
2490
2400
  this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedInSeconds;
2491
2401
  this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : void 0;
2492
2402
  this.calculatedScore.value = sample.score;
2493
- if (sample.score !== void 0) {
2494
- const bucket = Math.floor(sample.score).toString();
2495
- this.report.scoreDistribution[bucket] = (this.report.scoreDistribution[bucket] ?? 0) + 1;
2496
- this.report.scoreDistribution.count += 1;
2497
- this.report.scoreDistribution.sum += sample.score;
2498
- }
2499
2403
  this.lastSampleTimestamp = sample.timestamp;
2500
2404
  const elapsedTimeInMs = now - this.updated;
2501
2405
  this.emit("update", sample, elapsedTimeInMs, context);
@@ -2560,7 +2464,6 @@ var ObservedClient = class extends import_events2.EventEmitter {
2560
2464
  }
2561
2465
  addIssue(issue) {
2562
2466
  if (this.closed) return;
2563
- this.report.issues[issue.type] = (this.report.issues[issue.type] ?? 0) + 1;
2564
2467
  this._notify("client-issue", { ...this.eventScope, issue });
2565
2468
  }
2566
2469
  addExtensionStats(stats) {
@@ -2787,31 +2690,9 @@ var ObservedClient = class extends import_events2.EventEmitter {
2787
2690
  ), void 0;
2788
2691
  }
2789
2692
  const newObservedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
2790
- const onInboundTrackRemoved = (track) => {
2791
- this._notify("client-track-report", {
2792
- ...this.eventScope,
2793
- report: {
2794
- direction: "inbound",
2795
- ...track.report
2796
- }
2797
- });
2798
- };
2799
- const onOutboundTrackRemoved = (track) => {
2800
- this._notify("client-track-report", {
2801
- ...this.eventScope,
2802
- report: {
2803
- direction: "outbound",
2804
- ...track.report
2805
- }
2806
- });
2807
- };
2808
2693
  newObservedPeerConnection.once("close", () => {
2809
- newObservedPeerConnection?.off("removed-inbound-track", onInboundTrackRemoved);
2810
- newObservedPeerConnection?.off("removed-outbound-track", onOutboundTrackRemoved);
2811
2694
  this.observedPeerConnections.delete(sample.peerConnectionId);
2812
2695
  });
2813
- newObservedPeerConnection.on("removed-inbound-track", onInboundTrackRemoved);
2814
- newObservedPeerConnection.on("removed-outbound-track", onOutboundTrackRemoved);
2815
2696
  this.observedPeerConnections.set(sample.peerConnectionId, newObservedPeerConnection);
2816
2697
  this._notify("peer-connection-added", { ...this.eventScope, observedPeerConnection: newObservedPeerConnection });
2817
2698
  observedPeerConnection = newObservedPeerConnection;
@@ -2918,21 +2799,6 @@ var OnAllClientCallUpdater = class {
2918
2799
  }
2919
2800
  };
2920
2801
 
2921
- // src/updaters/OnIntervalUpdater.ts
2922
- var OnIntervalUpdater = class {
2923
- constructor(intervalInMs, _update) {
2924
- this._update = _update;
2925
- this.timer = setInterval(() => this._update(), intervalInMs);
2926
- }
2927
- _update;
2928
- name = "OnIntervalUpdater";
2929
- description = "Call the update() method given in the constructor once the interval elapsed";
2930
- timer;
2931
- close() {
2932
- clearInterval(this.timer);
2933
- }
2934
- };
2935
-
2936
2802
  // src/updaters/OnAnyClientCallUpdater.ts
2937
2803
  var OnAnyClientCallUpdater = class {
2938
2804
  constructor(_observedCall) {
@@ -3006,9 +2872,6 @@ var ObservedCall = class extends import_events3.EventEmitter {
3006
2872
  this.appData = settings.appData ?? {};
3007
2873
  this.scoreCalculator = new DefaultCallScoreCalculator(this);
3008
2874
  this.detectors = new Detectors();
3009
- if (settings.updateIntervalInMs && settings.updatePolicy !== "update-on-interval") {
3010
- logger4.warn('updateIntervalInMs is ignored unless updatePolicy is "update-on-interval" (callId: %s)', settings.callId);
3011
- }
3012
2875
  switch (settings.updatePolicy) {
3013
2876
  case "update-on-any-client-updated":
3014
2877
  this.updater = new OnAnyClientCallUpdater(this);
@@ -3016,17 +2879,6 @@ var ObservedCall = class extends import_events3.EventEmitter {
3016
2879
  case "update-when-all-client-updated":
3017
2880
  this.updater = new OnAllClientCallUpdater(this);
3018
2881
  break;
3019
- case "update-on-interval":
3020
- if (!settings.updateIntervalInMs) {
3021
- logger4.warn('updateIntervalInMs must be set when updatePolicy is "update-on-interval"; falling back to "update-when-all-client-updated" (callId: %s)', settings.callId);
3022
- this.updater = new OnAllClientCallUpdater(this);
3023
- } else {
3024
- this.updater = new OnIntervalUpdater(
3025
- settings.updateIntervalInMs,
3026
- this.update.bind(this)
3027
- );
3028
- }
3029
- break;
3030
2882
  }
3031
2883
  this.settings = {
3032
2884
  closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs
@@ -3388,122 +3240,60 @@ var OnAnyCallObserverUpdater = class {
3388
3240
  }
3389
3241
  };
3390
3242
 
3391
- // src/utils/MediasoupRemoteTrackResolver.ts
3392
- var MediasoupRemoteTrackResolver = class {
3393
- constructor(observedCall) {
3394
- this.observedCall = observedCall;
3395
- const observer = observedCall.observer;
3396
- const onInboundAdded = (p) => {
3397
- if (p.observedCall === this.observedCall) this._addInboundTrack(p.observedInboundTrack);
3398
- };
3399
- const onInboundRemoved = (p) => {
3400
- if (p.observedCall === this.observedCall) this._removeInboundTrack(p.observedInboundTrack);
3401
- };
3402
- const onOutboundAdded = (p) => {
3403
- if (p.observedCall === this.observedCall) this._addOutboundTrack(p.observedOutboundTrack);
3404
- };
3405
- const onOutboundRemoved = (p) => {
3406
- if (p.observedCall === this.observedCall) this._removeOutboundTrack(p.observedOutboundTrack);
3407
- };
3408
- observer.on("inbound-track-added", onInboundAdded);
3409
- observer.on("inbound-track-removed", onInboundRemoved);
3410
- observer.on("outbound-track-added", onOutboundAdded);
3411
- observer.on("outbound-track-removed", onOutboundRemoved);
3412
- observedCall.once("close", () => {
3413
- observer.off("inbound-track-added", onInboundAdded);
3414
- observer.off("inbound-track-removed", onInboundRemoved);
3415
- observer.off("outbound-track-added", onOutboundAdded);
3416
- observer.off("outbound-track-removed", onOutboundRemoved);
3417
- });
3418
- }
3419
- observedCall;
3420
- _consumerIdToProducerId = /* @__PURE__ */ new Map();
3421
- _producerIdToOutboundTrack = /* @__PURE__ */ new Map();
3422
- _consumerIdToInboundTrack = /* @__PURE__ */ new Map();
3423
- // private _producerIdToOutboundTrack = new Map<string, ObservedOutboundTrack>();
3424
- _inboundTrackToConsumerId = /* @__PURE__ */ new Map();
3425
- _producerIdToConsumerIds = /* @__PURE__ */ new Map();
3426
- _outboundTrackToProducerId = /* @__PURE__ */ new Map();
3427
- resolveRemoteOutboundTrack(inboundTrack) {
3428
- const consumerId = this._inboundTrackToConsumerId.get(inboundTrack.id);
3429
- if (!consumerId) return;
3430
- const producerId = this._consumerIdToProducerId.get(consumerId);
3431
- if (!producerId) return;
3432
- return this._producerIdToOutboundTrack.get(producerId);
3243
+ // src/common/Middleware.ts
3244
+ var Executor = class {
3245
+ constructor(stack, finalCallback) {
3246
+ this.stack = stack;
3247
+ this.finalCallback = finalCallback;
3248
+ }
3249
+ stack;
3250
+ finalCallback;
3251
+ done = false;
3252
+ index = 0;
3253
+ prevIndex = -1;
3254
+ // Executes the middleware stack
3255
+ execute(input) {
3256
+ if (this.index <= this.prevIndex) {
3257
+ throw new Error("Middleware must call next() only once!");
3258
+ } else if (this.done) {
3259
+ throw new Error("Middleware stack has already been executed!");
3260
+ }
3261
+ this.prevIndex = this.index;
3262
+ const middleware = this.stack[this.index];
3263
+ this.index++;
3264
+ if (middleware) {
3265
+ return middleware(input, (nextInput) => this.execute(nextInput));
3266
+ }
3267
+ this.done = true;
3268
+ this.finalCallback?.(input);
3433
3269
  }
3434
- resolveRemoteInboundTracks(outboundTrack) {
3435
- const producerId = this._outboundTrackToProducerId.get(outboundTrack.id);
3436
- if (!producerId) return;
3437
- const consumerIds = this._producerIdToConsumerIds.get(producerId);
3438
- if (!consumerIds) return;
3439
- return consumerIds.map((consumerId) => this._consumerIdToInboundTrack.get(consumerId)).filter((inboundTrack) => Boolean(inboundTrack));
3440
- }
3441
- _addInboundTrack(inboundTrack) {
3442
- const attachments = this._getInboundTrackAttachments(inboundTrack);
3443
- if (!attachments) return;
3444
- const {
3445
- producerId,
3446
- consumerId
3447
- } = attachments;
3448
- this._inboundTrackToConsumerId.set(inboundTrack.id, consumerId);
3449
- this._consumerIdToProducerId.set(consumerId, producerId);
3450
- this._consumerIdToInboundTrack.set(consumerId, inboundTrack);
3451
- let producerConsumers = this._producerIdToConsumerIds.get(producerId);
3452
- if (!producerConsumers) {
3453
- producerConsumers = [];
3454
- this._producerIdToConsumerIds.set(producerId, producerConsumers);
3455
- }
3456
- producerConsumers.push(consumerId);
3457
- }
3458
- _removeInboundTrack(inboundTrack) {
3459
- const consumerId = this._inboundTrackToConsumerId.get(inboundTrack.id);
3460
- if (!consumerId) return;
3461
- this._inboundTrackToConsumerId.delete(inboundTrack.id);
3462
- this._consumerIdToProducerId.delete(consumerId);
3463
- this._consumerIdToInboundTrack.delete(consumerId);
3464
- const producerConsumers = this._producerIdToConsumerIds.get(consumerId);
3465
- if (!producerConsumers) return;
3466
- const filteredProducerConsumers = producerConsumers.filter((id) => id !== consumerId);
3467
- if (filteredProducerConsumers.length === 0) {
3468
- this._producerIdToConsumerIds.delete(consumerId);
3469
- } else {
3470
- this._producerIdToConsumerIds.set(consumerId, filteredProducerConsumers);
3270
+ };
3271
+ var MiddlewareProcessor = class {
3272
+ stack = [];
3273
+ finalCallback;
3274
+ addMiddleware(...middlewares) {
3275
+ if (middlewares && middlewares.length > 0) {
3276
+ this.stack.push(...middlewares);
3277
+ }
3278
+ return this;
3279
+ }
3280
+ removeMiddleware(...middlewares) {
3281
+ if (middlewares && middlewares.length > 0) {
3282
+ for (const middleware of middlewares) {
3283
+ const index = this.stack.indexOf(middleware);
3284
+ if (index >= 0) {
3285
+ this.stack.splice(index, 1);
3286
+ }
3287
+ }
3471
3288
  }
3289
+ return this;
3472
3290
  }
3473
- _addOutboundTrack(outboundTrack) {
3474
- const attachments = this._getOutboundTrackAttachments(outboundTrack);
3475
- if (!attachments) return;
3476
- const {
3477
- producerId
3478
- } = attachments;
3479
- this._producerIdToOutboundTrack.set(producerId, outboundTrack);
3480
- this._outboundTrackToProducerId.set(outboundTrack.id, producerId);
3481
- }
3482
- _removeOutboundTrack(outboundTrack) {
3483
- const producerId = this._outboundTrackToProducerId.get(outboundTrack.id);
3484
- if (!producerId) return;
3485
- this._producerIdToOutboundTrack.delete(producerId);
3486
- this._outboundTrackToProducerId.delete(outboundTrack.id);
3487
- }
3488
- _getInboundTrackAttachments(inboundTrack) {
3489
- const {
3490
- producerId,
3491
- consumerId
3492
- } = inboundTrack.attachments ?? {};
3493
- if (!producerId || !consumerId || typeof producerId !== "string" || typeof consumerId !== "string") return;
3494
- return {
3495
- producerId,
3496
- consumerId
3497
- };
3498
- }
3499
- _getOutboundTrackAttachments(outboundTrack) {
3500
- const {
3501
- producerId
3502
- } = outboundTrack.attachments ?? {};
3503
- if (!producerId || typeof producerId !== "string") return;
3504
- return {
3505
- producerId
3506
- };
3291
+ process(value) {
3292
+ if (this.stack.length === 0) {
3293
+ return this.finalCallback?.(value);
3294
+ }
3295
+ const executor = new Executor(this.stack, this.finalCallback);
3296
+ executor.execute(value);
3507
3297
  }
3508
3298
  };
3509
3299
 
@@ -3526,19 +3316,6 @@ var Observer = class extends import_events5.EventEmitter {
3526
3316
  case "update-when-all-call-updated":
3527
3317
  this.updater = new OnAllCallObserverUpdater(this);
3528
3318
  break;
3529
- case "update-on-interval": {
3530
- const interval = config?.updateIntervalInMs;
3531
- if (!interval) {
3532
- logger6.warn('updateIntervalInMs must be set when updatePolicy is "update-on-interval"; falling back to "update-when-all-call-updated"');
3533
- this.updater = new OnAllCallObserverUpdater(this);
3534
- } else {
3535
- this.updater = new OnIntervalUpdater(
3536
- interval,
3537
- this.update.bind(this)
3538
- );
3539
- }
3540
- break;
3541
- }
3542
3319
  }
3543
3320
  }
3544
3321
  config;
@@ -3556,7 +3333,8 @@ var Observer = class extends import_events5.EventEmitter {
3556
3333
  numberOfOutboundRtpStreams = 0;
3557
3334
  numberOfDataChannels = 0;
3558
3335
  numberOfPeerConnections = 0;
3559
- _timer;
3336
+ /** Global, pre-dispatch middleware chain run on every accepted sample. */
3337
+ acceptMiddlewares = new MiddlewareProcessor();
3560
3338
  get numberOfCalls() {
3561
3339
  return this.observedCalls.size;
3562
3340
  }
@@ -3579,14 +3357,11 @@ var Observer = class extends import_events5.EventEmitter {
3579
3357
  const callSettings = {
3580
3358
  ...settings,
3581
3359
  updatePolicy: settings.updatePolicy ?? this.config.defaultCallUpdatePolicy,
3582
- updateIntervalInMs: settings.updateIntervalInMs ?? this.config.defaultCallUpdateIntervalInMs,
3583
3360
  closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs ?? this.config.closeCallIfEmptyForMs,
3584
3361
  appData: settings.appData ?? this.config.createCallAppData?.({ callId: settings.callId, observer: this })
3585
3362
  };
3586
3363
  const observedCall = new ObservedCall(callSettings, this);
3587
- if (callSettings.remoteTrackResolvePolicy === "mediasoup-sfu") {
3588
- observedCall.remoteTrackResolver = new MediasoupRemoteTrackResolver(observedCall);
3589
- }
3364
+ observedCall.remoteTrackResolver = this.config.createTrackResolver?.(observedCall);
3590
3365
  observedCall.once("close", () => {
3591
3366
  this.observedCalls.delete(observedCall.callId);
3592
3367
  ++this.totalRemovedCall;
@@ -3604,15 +3379,17 @@ var Observer = class extends import_events5.EventEmitter {
3604
3379
  return logger6.debug("Attempted to close twice");
3605
3380
  }
3606
3381
  this.closed = true;
3607
- clearInterval(this._timer);
3608
- this._timer = void 0;
3609
3382
  this.observedCalls.forEach((call) => call.close());
3610
3383
  this._notify("observer-closed", { ...this.eventScope });
3611
3384
  }
3612
3385
  accept(sample, context) {
3613
3386
  if (this.closed) {
3614
- this._notify("sample-rejected", { ...this.eventScope, reason: "observer-closed", sample });
3615
- return;
3387
+ return this._notify("sample-rejected", { ...this.eventScope, reason: "observer-closed", sample });
3388
+ }
3389
+ try {
3390
+ this.acceptMiddlewares.process({ sample, context });
3391
+ } catch (err) {
3392
+ logger6.warn("An accept middleware threw; dropping the sample. %o", err);
3616
3393
  }
3617
3394
  if (!sample.callId) {
3618
3395
  this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
@@ -3729,6 +3506,126 @@ var InMemorySink = class extends ClientSampleSink {
3729
3506
  function createInMemorySink(samples) {
3730
3507
  return new InMemorySink(samples);
3731
3508
  }
3509
+
3510
+ // src/utils/RemoteTrackResolver.ts
3511
+ var RemoteTrackResolver = class {
3512
+ constructor(observedCall, resolvers) {
3513
+ this.observedCall = observedCall;
3514
+ this.resolvers = resolvers;
3515
+ const observer = observedCall.observer;
3516
+ const onInboundAdded = (p) => {
3517
+ if (p.observedCall === this.observedCall) this._addInboundTrack(p.observedInboundTrack);
3518
+ };
3519
+ const onInboundRemoved = (p) => {
3520
+ if (p.observedCall === this.observedCall) this._removeInboundTrack(p.observedInboundTrack);
3521
+ };
3522
+ const onOutboundAdded = (p) => {
3523
+ if (p.observedCall === this.observedCall) this._addOutboundTrack(p.observedOutboundTrack);
3524
+ };
3525
+ const onOutboundRemoved = (p) => {
3526
+ if (p.observedCall === this.observedCall) this._removeOutboundTrack(p.observedOutboundTrack);
3527
+ };
3528
+ observedCall.once("close", () => {
3529
+ observer.off("inbound-track-added", onInboundAdded);
3530
+ observer.off("inbound-track-removed", onInboundRemoved);
3531
+ observer.off("outbound-track-added", onOutboundAdded);
3532
+ observer.off("outbound-track-removed", onOutboundRemoved);
3533
+ });
3534
+ observer.on("inbound-track-added", onInboundAdded);
3535
+ observer.on("inbound-track-removed", onInboundRemoved);
3536
+ observer.on("outbound-track-added", onOutboundAdded);
3537
+ observer.on("outbound-track-removed", onOutboundRemoved);
3538
+ }
3539
+ observedCall;
3540
+ resolvers;
3541
+ _publisherIdToOutboundTrack = /* @__PURE__ */ new Map();
3542
+ _subscriberIdToInboundTrack = /* @__PURE__ */ new Map();
3543
+ /** The published (outbound) track for a publisher id, if any. */
3544
+ getOutboundTrackByPublisherId(publisherId) {
3545
+ return this._publisherIdToOutboundTrack.get(publisherId);
3546
+ }
3547
+ /** The subscribed (inbound) track for a subscriber id, if the strategy resolves subscriber ids. */
3548
+ getInboundTrackBySubscriberId(subscriberId) {
3549
+ return this._subscriberIdToInboundTrack.get(subscriberId);
3550
+ }
3551
+ // RemoteTrackResolver interface — thin readers over the links maintained on the tracks.
3552
+ resolveRemoteOutboundTrack(inboundTrack) {
3553
+ return inboundTrack.remoteOutboundTrack;
3554
+ }
3555
+ resolveRemoteInboundTracks(outboundTrack) {
3556
+ return [...outboundTrack.remoteInboundTracks];
3557
+ }
3558
+ _addInboundTrack(inboundTrack) {
3559
+ const publisherId = this.resolvers.resolveInboundTrackPublisherId(inboundTrack);
3560
+ if (!publisherId) return;
3561
+ const subscriberId = this.resolvers.resolveInboundTrackSubscriberId?.(inboundTrack);
3562
+ if (subscriberId) {
3563
+ this._subscriberIdToInboundTrack.set(subscriberId, inboundTrack);
3564
+ }
3565
+ const outboundTrack = this._publisherIdToOutboundTrack.get(publisherId);
3566
+ if (!outboundTrack) return;
3567
+ outboundTrack.remoteInboundTracks.add(inboundTrack);
3568
+ inboundTrack.remoteOutboundTrack = outboundTrack;
3569
+ }
3570
+ _removeInboundTrack(inboundTrack) {
3571
+ inboundTrack.remoteOutboundTrack?.remoteInboundTracks.delete(inboundTrack);
3572
+ inboundTrack.remoteOutboundTrack = void 0;
3573
+ const subscriberId = this.resolvers.resolveInboundTrackSubscriberId?.(inboundTrack);
3574
+ if (subscriberId) {
3575
+ this._subscriberIdToInboundTrack.delete(subscriberId);
3576
+ }
3577
+ }
3578
+ _addOutboundTrack(outboundTrack) {
3579
+ const publisherId = this.resolvers.resolveOutboundTrackPublisherId(outboundTrack);
3580
+ if (!publisherId) return;
3581
+ this._publisherIdToOutboundTrack.set(publisherId, outboundTrack);
3582
+ for (const observedClient of this.observedCall.observedClients.values()) {
3583
+ for (const observedPeerConnection of observedClient.observedPeerConnections.values()) {
3584
+ for (const inboundTrack of observedPeerConnection.observedInboundTracks.values()) {
3585
+ if (this.resolvers.resolveInboundTrackPublisherId(inboundTrack) !== publisherId) continue;
3586
+ outboundTrack.remoteInboundTracks.add(inboundTrack);
3587
+ inboundTrack.remoteOutboundTrack = outboundTrack;
3588
+ }
3589
+ }
3590
+ }
3591
+ }
3592
+ _removeOutboundTrack(outboundTrack) {
3593
+ const publisherId = this.resolvers.resolveOutboundTrackPublisherId(outboundTrack);
3594
+ if (publisherId && this._publisherIdToOutboundTrack.get(publisherId) === outboundTrack) {
3595
+ this._publisherIdToOutboundTrack.delete(publisherId);
3596
+ }
3597
+ for (const inboundTrack of outboundTrack.remoteInboundTracks) {
3598
+ inboundTrack.remoteOutboundTrack = void 0;
3599
+ }
3600
+ outboundTrack.remoteInboundTracks.clear();
3601
+ }
3602
+ };
3603
+
3604
+ // src/utils/RemoteTrackResolverFactories.ts
3605
+ function createDefaultMediasoupRemoteTrackResolverFactory() {
3606
+ return (observedCall) => new RemoteTrackResolver(observedCall, {
3607
+ resolveInboundTrackPublisherId: (inboundTrack) => inboundTrack.attachments?.producerId,
3608
+ resolveInboundTrackSubscriberId: (inboundTrack) => inboundTrack.attachments?.consumerId,
3609
+ resolveOutboundTrackPublisherId: (outboundTrack) => outboundTrack.attachments?.producerId
3610
+ });
3611
+ }
3612
+ function createP2pRemoteTrackResolverFactory() {
3613
+ return (observedCall) => new RemoteTrackResolver(observedCall, {
3614
+ resolveInboundTrackPublisherId: (inboundTrack) => {
3615
+ const ssrc = inboundTrack.getInboundRtp()?.ssrc;
3616
+ return ssrc ? `${ssrc}` : void 0;
3617
+ },
3618
+ resolveInboundTrackSubscriberId: (inboundTrack) => {
3619
+ const ssrc = inboundTrack.getInboundRtp()?.ssrc;
3620
+ return ssrc ? `${ssrc}` : void 0;
3621
+ },
3622
+ resolveOutboundTrackPublisherId: (outboundTrack) => {
3623
+ const rtps = outboundTrack.getOutboundRtps();
3624
+ if (!rtps || rtps.length === 0) return void 0;
3625
+ return rtps.map((rtp) => rtp.ssrc).join(",");
3626
+ }
3627
+ });
3628
+ }
3732
3629
  // Annotate the CommonJS export names for ESM import in node:
3733
3630
  0 && (module.exports = {
3734
3631
  ClientEventTypes,
@@ -3756,10 +3653,13 @@ function createInMemorySink(samples) {
3756
3653
  ObservedRemoteInboundRtp,
3757
3654
  ObservedRemoteOutboundRtp,
3758
3655
  Observer,
3656
+ RemoteTrackResolver,
3657
+ createDefaultMediasoupRemoteTrackResolverFactory,
3759
3658
  createInMemorySink,
3760
3659
  createJsonlFileSink,
3761
3660
  createJsonlFileSinkFactory,
3762
3661
  createLogger,
3662
+ createP2pRemoteTrackResolverFactory,
3763
3663
  setObserverLogger
3764
3664
  });
3765
3665
  //# sourceMappingURL=index.js.map