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

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.mjs CHANGED
@@ -37,8 +37,8 @@ function createLogger(moduleName) {
37
37
  }
38
38
  }();
39
39
  }
40
- function setObserverLogger(logger7) {
41
- mainLogger = logger7;
40
+ function setObserverLogger(logger8) {
41
+ mainLogger = logger8;
42
42
  }
43
43
 
44
44
  // src/ObservedCall.ts
@@ -2811,6 +2811,8 @@ var ObservedCall = class extends EventEmitter3 {
2811
2811
  case "update-when-all-client-updated":
2812
2812
  this.updater = new OnAllClientCallUpdater(this);
2813
2813
  break;
2814
+ case "none":
2815
+ break;
2814
2816
  }
2815
2817
  this.settings = {
2816
2818
  closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs
@@ -2983,7 +2985,7 @@ var ObservedCall = class extends EventEmitter3 {
2983
2985
  };
2984
2986
 
2985
2987
  // src/Observer.ts
2986
- import { EventEmitter as EventEmitter5 } from "events";
2988
+ import { EventEmitter as EventEmitter6 } from "events";
2987
2989
 
2988
2990
  // src/ObservedTURN.ts
2989
2991
  import { EventEmitter as EventEmitter4 } from "events";
@@ -3229,9 +3231,344 @@ var MiddlewareProcessor = class {
3229
3231
  }
3230
3232
  };
3231
3233
 
3234
+ // src/ObservedMediasoupRouter.ts
3235
+ import { EventEmitter as EventEmitter5 } from "events";
3236
+ var logger6 = createLogger("ObservedMediasoupRouter");
3237
+ var ObservedMediasoupRouter = class extends EventEmitter5 {
3238
+ router;
3239
+ sample;
3240
+ appData;
3241
+ webrtcTransportIds = /* @__PURE__ */ new Set();
3242
+ get attachments() {
3243
+ return this.sample.attachments;
3244
+ }
3245
+ closed = false;
3246
+ constructor(settings) {
3247
+ super();
3248
+ this.router = settings.router;
3249
+ this.appData = settings.appData ?? {};
3250
+ this.sample = {
3251
+ routerId: this.router.id,
3252
+ attachments: settings.attachments ?? {},
3253
+ createdAt: Date.now(),
3254
+ producers: [],
3255
+ consumers: [],
3256
+ dataProducers: [],
3257
+ dataConsumers: [],
3258
+ transports: []
3259
+ };
3260
+ this.attachRouterListeners();
3261
+ }
3262
+ get id() {
3263
+ return this.router.id;
3264
+ }
3265
+ addTransport = (transport) => {
3266
+ let transportType = transport.type;
3267
+ if (!transportType) {
3268
+ if (transport.constructor.name === "WebRtcTransport") transportType = "webrtc";
3269
+ else if (transport.constructor.name === "PlainTransport") transportType = "plain";
3270
+ else if (transport.constructor.name === "PipeTransport") transportType = "pipe";
3271
+ else if (transport.constructor.name === "DirectTransport") transportType = "direct";
3272
+ }
3273
+ if (!transportType) return logger6.warn(`Unknown transport type for transport ${transport.id}: ${transport.constructor.name}`);
3274
+ switch (transportType) {
3275
+ case "webrtc":
3276
+ return this.addWebRtcTransport(transport);
3277
+ case "plain":
3278
+ return this.addPlainTransport(transport);
3279
+ case "pipe":
3280
+ return this.addPipeTransport(transport);
3281
+ case "direct":
3282
+ return this.addDirectTransport(transport);
3283
+ default:
3284
+ return logger6.warn(`Unsupported transport type for transport ${transport.id}: ${transportType}`);
3285
+ }
3286
+ };
3287
+ addWebRtcTransport(transport) {
3288
+ const history = [];
3289
+ const transportSample = {
3290
+ id: transport.id,
3291
+ type: "webrtc",
3292
+ createdAt: Date.now(),
3293
+ tuple: transport.iceSelectedTuple,
3294
+ history
3295
+ };
3296
+ this.sample.transports.push(transportSample);
3297
+ const onIceStateChange = (iceState) => {
3298
+ history.push({
3299
+ type: `icestate-changed-to-${iceState}`,
3300
+ timestamp: Date.now()
3301
+ });
3302
+ };
3303
+ const onIceSelectedTupleChange = (tuple) => {
3304
+ transportSample.tuple = tuple;
3305
+ history.push({
3306
+ type: "iceselectedtuple-changed",
3307
+ timestamp: Date.now(),
3308
+ ...tuple
3309
+ });
3310
+ };
3311
+ const onDtlsStateChange = (dtlsState) => {
3312
+ history.push({
3313
+ type: `dtlsstate-changed-to-${dtlsState}`,
3314
+ timestamp: Date.now()
3315
+ });
3316
+ if (dtlsState === "connected" && transportSample.connectedAt === void 0) {
3317
+ transportSample.connectedAt = Date.now();
3318
+ }
3319
+ };
3320
+ const onSctpStateChange = (sctpState) => {
3321
+ history.push({
3322
+ type: `sctpstate-changed-to-${sctpState}`,
3323
+ timestamp: Date.now()
3324
+ });
3325
+ };
3326
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3327
+ transport.observer.off("icestatechange", onIceStateChange);
3328
+ transport.observer.off("iceselectedtuplechange", onIceSelectedTupleChange);
3329
+ transport.observer.off("dtlsstatechange", onDtlsStateChange);
3330
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3331
+ this.webrtcTransportIds.delete(transport.id);
3332
+ });
3333
+ transport.observer.on("icestatechange", onIceStateChange);
3334
+ transport.observer.on("iceselectedtuplechange", onIceSelectedTupleChange);
3335
+ transport.observer.on("dtlsstatechange", onDtlsStateChange);
3336
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3337
+ this.webrtcTransportIds.add(transport.id);
3338
+ }
3339
+ addPlainTransport(transport) {
3340
+ const history = [];
3341
+ const transportSample = {
3342
+ id: transport.id,
3343
+ type: "plain",
3344
+ createdAt: Date.now(),
3345
+ tuple: transport.tuple,
3346
+ rtcpTuple: transport.rtcpTuple,
3347
+ history
3348
+ };
3349
+ this.sample.transports.push(transportSample);
3350
+ const onTuple = (tuple) => {
3351
+ transportSample.tuple = tuple;
3352
+ if (transportSample.connectedAt === void 0) transportSample.connectedAt = Date.now();
3353
+ history.push({ type: "tuple-changed", timestamp: Date.now(), ...tuple });
3354
+ };
3355
+ const onRtcpTuple = (rtcpTuple) => {
3356
+ transportSample.rtcpTuple = rtcpTuple;
3357
+ history.push({ type: "rtcptuple-changed", timestamp: Date.now(), ...rtcpTuple });
3358
+ };
3359
+ const onSctpStateChange = (sctpState) => {
3360
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3361
+ transportSample.connectedAt = Date.now();
3362
+ }
3363
+ history.push({
3364
+ type: `sctpstate-changed-to-${sctpState}`,
3365
+ timestamp: Date.now()
3366
+ });
3367
+ };
3368
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3369
+ transport.observer.off("tuple", onTuple);
3370
+ transport.observer.off("rtcptuple", onRtcpTuple);
3371
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3372
+ });
3373
+ transport.observer.on("tuple", onTuple);
3374
+ transport.observer.on("rtcptuple", onRtcpTuple);
3375
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3376
+ }
3377
+ addPipeTransport(transport) {
3378
+ const history = [];
3379
+ const transportSample = {
3380
+ id: transport.id,
3381
+ type: "pipe",
3382
+ createdAt: Date.now(),
3383
+ tuple: transport.tuple,
3384
+ history
3385
+ };
3386
+ this.sample.transports.push(transportSample);
3387
+ const onSctpStateChange = (sctpState) => {
3388
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3389
+ transportSample.connectedAt = Date.now();
3390
+ }
3391
+ history.push({
3392
+ type: `sctpstate-changed-to-${sctpState}`,
3393
+ timestamp: Date.now()
3394
+ });
3395
+ };
3396
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3397
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3398
+ });
3399
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3400
+ }
3401
+ addDirectTransport(transport) {
3402
+ const now = Date.now();
3403
+ const transportSample = {
3404
+ id: transport.id,
3405
+ type: "direct",
3406
+ createdAt: now,
3407
+ // A DirectTransport has no network layer — it is connected the instant it exists.
3408
+ connectedAt: now,
3409
+ history: []
3410
+ };
3411
+ this.sample.transports.push(transportSample);
3412
+ this._attachTransportObserverListeners(transport, transportSample);
3413
+ }
3414
+ addProducer(transport, producer) {
3415
+ const firstCodec = producer.rtpParameters.codecs[0];
3416
+ const codecInfo = {
3417
+ mimeType: firstCodec?.mimeType ?? "unknown/unknown",
3418
+ payloadType: firstCodec?.payloadType ?? 0,
3419
+ clockRate: firstCodec?.clockRate ?? 0,
3420
+ channels: firstCodec?.channels,
3421
+ parameters: firstCodec?.parameters,
3422
+ rtcpFeedback: firstCodec?.rtcpFeedback
3423
+ };
3424
+ const producerSample = {
3425
+ id: producer.id,
3426
+ createdAt: Date.now(),
3427
+ kind: producer.kind,
3428
+ codecInfo,
3429
+ ssrcs: producer.rtpParameters.encodings?.map((encoding) => encoding.ssrc).filter((ssrc) => typeof ssrc === "number"),
3430
+ rids: producer.rtpParameters.encodings?.map((encoding) => encoding.rid).filter((rid) => typeof rid === "string"),
3431
+ history: [],
3432
+ transportId: transport.id
3433
+ };
3434
+ this.sample.producers.push(producerSample);
3435
+ const onPause = () => {
3436
+ producerSample.history.push({
3437
+ type: "pause",
3438
+ timestamp: Date.now()
3439
+ });
3440
+ };
3441
+ const onResume = () => {
3442
+ producerSample.history.push({
3443
+ type: "resume",
3444
+ timestamp: Date.now()
3445
+ });
3446
+ };
3447
+ producer.observer.once("close", () => {
3448
+ producer.observer.off("pause", onPause);
3449
+ producer.observer.off("resume", onResume);
3450
+ producerSample.closedAt = Date.now();
3451
+ });
3452
+ producer.observer.on("pause", onPause);
3453
+ producer.observer.on("resume", onResume);
3454
+ }
3455
+ addConsumer(transport, consumer) {
3456
+ const consumerSample = {
3457
+ id: consumer.id,
3458
+ producerId: consumer.producerId,
3459
+ transportId: transport.id,
3460
+ createdAt: Date.now(),
3461
+ kind: consumer.kind,
3462
+ history: []
3463
+ };
3464
+ this.sample.consumers.push(consumerSample);
3465
+ const onPause = () => {
3466
+ consumerSample.history.push({
3467
+ type: "pause",
3468
+ timestamp: Date.now()
3469
+ });
3470
+ };
3471
+ const onResume = () => {
3472
+ consumerSample.history.push({
3473
+ type: "resume",
3474
+ timestamp: Date.now()
3475
+ });
3476
+ };
3477
+ const onProducerPause = () => {
3478
+ consumerSample.history.push({
3479
+ type: "producerPaused",
3480
+ timestamp: Date.now()
3481
+ });
3482
+ };
3483
+ const onProducerResume = () => {
3484
+ consumerSample.history.push({
3485
+ type: "producerResumed",
3486
+ timestamp: Date.now()
3487
+ });
3488
+ };
3489
+ consumer.observer.once("close", () => {
3490
+ consumer.observer.off("pause", onPause);
3491
+ consumer.observer.off("resume", onResume);
3492
+ consumer.off("producerresume", onProducerResume);
3493
+ consumer.off("producerpause", onProducerPause);
3494
+ consumerSample.closedAt = Date.now();
3495
+ });
3496
+ consumer.observer.on("pause", onPause);
3497
+ consumer.observer.on("resume", onResume);
3498
+ consumer.on("producerpause", onProducerPause);
3499
+ consumer.on("producerresume", onProducerResume);
3500
+ }
3501
+ addDataProducer(transport, dataProducer) {
3502
+ const dataProducerSample = {
3503
+ id: dataProducer.id,
3504
+ transportId: transport.id,
3505
+ createdAt: Date.now(),
3506
+ label: dataProducer.label,
3507
+ protocol: dataProducer.protocol
3508
+ };
3509
+ this.sample.dataProducers.push(dataProducerSample);
3510
+ dataProducer.observer.once("close", () => {
3511
+ dataProducerSample.closedAt = Date.now();
3512
+ });
3513
+ }
3514
+ addDataConsumer(transport, dataConsumer) {
3515
+ const dataConsumerSample = {
3516
+ id: dataConsumer.id,
3517
+ dataProducerId: dataConsumer.dataProducerId,
3518
+ transportId: transport.id,
3519
+ createdAt: Date.now(),
3520
+ label: dataConsumer.label,
3521
+ protocol: dataConsumer.protocol
3522
+ };
3523
+ this.sample.dataConsumers.push(dataConsumerSample);
3524
+ dataConsumer.observer.once("close", () => {
3525
+ dataConsumerSample.closedAt = Date.now();
3526
+ });
3527
+ }
3528
+ close() {
3529
+ if (this.closed) return;
3530
+ this.closed = true;
3531
+ this.emit("close");
3532
+ }
3533
+ attachRouterListeners() {
3534
+ this.router.observer.once("close", () => {
3535
+ this.router.observer.off("newtransport", this.addTransport);
3536
+ this.sample.closedAt = Date.now();
3537
+ this.close();
3538
+ });
3539
+ this.router.observer.on("newtransport", this.addTransport);
3540
+ }
3541
+ _attachTransportObserverListeners(transport, transportSample, onClose) {
3542
+ const onNewProducer = (producer) => {
3543
+ this.addProducer(transport, producer);
3544
+ };
3545
+ const onNewConsumer = (consumer) => {
3546
+ this.addConsumer(transport, consumer);
3547
+ };
3548
+ const onNewDataProducer = (dataProducer) => {
3549
+ this.addDataProducer(transport, dataProducer);
3550
+ };
3551
+ const onNewDataConsumer = (dataConsumer) => {
3552
+ this.addDataConsumer(transport, dataConsumer);
3553
+ };
3554
+ transport.observer.once("close", () => {
3555
+ transport.observer.off("newproducer", onNewProducer);
3556
+ transport.observer.off("newconsumer", onNewConsumer);
3557
+ transport.observer.off("newdataproducer", onNewDataProducer);
3558
+ transport.observer.off("newdataconsumer", onNewDataConsumer);
3559
+ onClose?.();
3560
+ transportSample.closedAt = Date.now();
3561
+ });
3562
+ transport.observer.on("newproducer", onNewProducer);
3563
+ transport.observer.on("newconsumer", onNewConsumer);
3564
+ transport.observer.on("newdataproducer", onNewDataProducer);
3565
+ transport.observer.on("newdataconsumer", onNewDataConsumer);
3566
+ }
3567
+ };
3568
+
3232
3569
  // src/Observer.ts
3233
- var logger6 = createLogger("Observer");
3234
- var Observer = class extends EventEmitter5 {
3570
+ var logger7 = createLogger("Observer");
3571
+ var Observer = class extends EventEmitter6 {
3235
3572
  constructor(config = {
3236
3573
  updatePolicy: "update-when-all-call-updated",
3237
3574
  appData: {}
@@ -3248,11 +3585,14 @@ var Observer = class extends EventEmitter5 {
3248
3585
  case "update-when-all-call-updated":
3249
3586
  this.updater = new OnAllCallObserverUpdater(this);
3250
3587
  break;
3588
+ case "none":
3589
+ break;
3251
3590
  }
3252
3591
  }
3253
3592
  config;
3254
3593
  observedTURN = new ObservedTURN();
3255
3594
  observedCalls = /* @__PURE__ */ new Map();
3595
+ observedMediasoupRouters = /* @__PURE__ */ new Map();
3256
3596
  updater;
3257
3597
  /** Ancestry base shared by all Observer-bus events originating at the observer. */
3258
3598
  eventScope = { observer: this };
@@ -3279,11 +3619,11 @@ var Observer = class extends EventEmitter5 {
3279
3619
  }
3280
3620
  createObservedCall(settings) {
3281
3621
  if (this.closed) {
3282
- logger6.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3622
+ logger7.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3283
3623
  return void 0;
3284
3624
  }
3285
3625
  if (this.observedCalls.has(settings.callId)) {
3286
- logger6.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3626
+ logger7.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3287
3627
  return this.observedCalls.get(settings.callId);
3288
3628
  }
3289
3629
  const callSettings = {
@@ -3306,9 +3646,53 @@ var Observer = class extends EventEmitter5 {
3306
3646
  getOrCreateObservedCall(settings) {
3307
3647
  return this.getObservedCall(settings.callId) ?? this.createObservedCall(settings);
3308
3648
  }
3649
+ createObservedMediasoupRouter(settings) {
3650
+ if (this.closed) {
3651
+ logger7.warn("Attempted to create mediasoup router (id: %d) on a closed observer", settings.router.id);
3652
+ return void 0;
3653
+ }
3654
+ if (this.observedMediasoupRouters.has(settings.router.id)) {
3655
+ logger7.warn("Observed Mediasoup Router (id %s) already exists; returning the existing instance", settings.router.id);
3656
+ return this.observedMediasoupRouters.get(settings.router.id);
3657
+ }
3658
+ const observedMediasoupRouter = new ObservedMediasoupRouter(settings);
3659
+ const observedMediasoupRouterScope = {
3660
+ observedMediasoupRouter,
3661
+ observer: this
3662
+ };
3663
+ const directCall = settings.callId ? this.observedCalls.get(settings.callId) : void 0;
3664
+ if (directCall) {
3665
+ this.emit("mediasoup-router-matched-with-call", {
3666
+ ...observedMediasoupRouterScope,
3667
+ observedCall: directCall
3668
+ });
3669
+ } else if (settings.bindCallByWebRtcTransportId) {
3670
+ const matchedCallIds = /* @__PURE__ */ new Set();
3671
+ const onPeerConnectionAdded = ({ observedPeerConnection, observedCall }) => {
3672
+ if (!observedMediasoupRouter.webrtcTransportIds.has(observedPeerConnection.peerConnectionId)) return;
3673
+ if (matchedCallIds.has(observedCall.callId)) return;
3674
+ matchedCallIds.add(observedCall.callId);
3675
+ this.emit("mediasoup-router-matched-with-call", {
3676
+ ...observedMediasoupRouterScope,
3677
+ observedCall
3678
+ });
3679
+ };
3680
+ const stopMatching = () => this.off("peer-connection-added", onPeerConnectionAdded);
3681
+ this.on("peer-connection-added", onPeerConnectionAdded);
3682
+ this.once("observer-closed", stopMatching);
3683
+ observedMediasoupRouter.once("close", stopMatching);
3684
+ }
3685
+ observedMediasoupRouter.once("close", () => {
3686
+ this.observedMediasoupRouters.delete(observedMediasoupRouter.id);
3687
+ this.emit("mediasoup-router-removed", observedMediasoupRouterScope);
3688
+ });
3689
+ this.observedMediasoupRouters.set(observedMediasoupRouter.id, observedMediasoupRouter);
3690
+ this.emit("mediasoup-router-added", observedMediasoupRouterScope);
3691
+ return observedMediasoupRouter;
3692
+ }
3309
3693
  close() {
3310
3694
  if (this.closed) {
3311
- return logger6.debug("Attempted to close twice");
3695
+ return logger7.debug("Attempted to close twice");
3312
3696
  }
3313
3697
  this.closed = true;
3314
3698
  this.observedCalls.forEach((call) => call.close());
@@ -3321,7 +3705,7 @@ var Observer = class extends EventEmitter5 {
3321
3705
  try {
3322
3706
  this.acceptMiddlewares.process({ sample, context });
3323
3707
  } catch (err) {
3324
- logger6.warn("An accept middleware threw; dropping the sample. %o", err);
3708
+ logger7.warn("An accept middleware threw; dropping the sample. %o", err);
3325
3709
  }
3326
3710
  if (!sample.callId) {
3327
3711
  this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
@@ -3377,8 +3761,8 @@ var Observer = class extends EventEmitter5 {
3377
3761
  };
3378
3762
 
3379
3763
  // src/sinks/ClientSampleSink.ts
3380
- import { EventEmitter as EventEmitter6 } from "events";
3381
- var ClientSampleSink = class extends EventEmitter6 {
3764
+ import { EventEmitter as EventEmitter7 } from "events";
3765
+ var ClientSampleSink = class extends EventEmitter7 {
3382
3766
  };
3383
3767
 
3384
3768
  // src/sinks/JsonlFileSink.ts
@@ -3577,6 +3961,7 @@ export {
3577
3961
  ObservedInboundTrack,
3578
3962
  ObservedMediaPlayout,
3579
3963
  ObservedMediaSource,
3964
+ ObservedMediasoupRouter,
3580
3965
  ObservedOutboundRtp,
3581
3966
  ObservedOutboundTrack,
3582
3967
  ObservedPeerConnection,