@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.js CHANGED
@@ -48,6 +48,7 @@ __export(src_exports, {
48
48
  ObservedInboundTrack: () => ObservedInboundTrack,
49
49
  ObservedMediaPlayout: () => ObservedMediaPlayout,
50
50
  ObservedMediaSource: () => ObservedMediaSource,
51
+ ObservedMediasoupRouter: () => ObservedMediasoupRouter,
51
52
  ObservedOutboundRtp: () => ObservedOutboundRtp,
52
53
  ObservedOutboundTrack: () => ObservedOutboundTrack,
53
54
  ObservedPeerConnection: () => ObservedPeerConnection,
@@ -105,8 +106,8 @@ function createLogger(moduleName) {
105
106
  }
106
107
  }();
107
108
  }
108
- function setObserverLogger(logger7) {
109
- mainLogger = logger7;
109
+ function setObserverLogger(logger8) {
110
+ mainLogger = logger8;
110
111
  }
111
112
 
112
113
  // src/ObservedCall.ts
@@ -2879,6 +2880,8 @@ var ObservedCall = class extends import_events3.EventEmitter {
2879
2880
  case "update-when-all-client-updated":
2880
2881
  this.updater = new OnAllClientCallUpdater(this);
2881
2882
  break;
2883
+ case "none":
2884
+ break;
2882
2885
  }
2883
2886
  this.settings = {
2884
2887
  closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs
@@ -3051,7 +3054,7 @@ var ObservedCall = class extends import_events3.EventEmitter {
3051
3054
  };
3052
3055
 
3053
3056
  // src/Observer.ts
3054
- var import_events5 = require("events");
3057
+ var import_events6 = require("events");
3055
3058
 
3056
3059
  // src/ObservedTURN.ts
3057
3060
  var import_events4 = require("events");
@@ -3297,9 +3300,344 @@ var MiddlewareProcessor = class {
3297
3300
  }
3298
3301
  };
3299
3302
 
3303
+ // src/ObservedMediasoupRouter.ts
3304
+ var import_events5 = require("events");
3305
+ var logger6 = createLogger("ObservedMediasoupRouter");
3306
+ var ObservedMediasoupRouter = class extends import_events5.EventEmitter {
3307
+ router;
3308
+ sample;
3309
+ appData;
3310
+ webrtcTransportIds = /* @__PURE__ */ new Set();
3311
+ get attachments() {
3312
+ return this.sample.attachments;
3313
+ }
3314
+ closed = false;
3315
+ constructor(settings) {
3316
+ super();
3317
+ this.router = settings.router;
3318
+ this.appData = settings.appData ?? {};
3319
+ this.sample = {
3320
+ routerId: this.router.id,
3321
+ attachments: settings.attachments ?? {},
3322
+ createdAt: Date.now(),
3323
+ producers: [],
3324
+ consumers: [],
3325
+ dataProducers: [],
3326
+ dataConsumers: [],
3327
+ transports: []
3328
+ };
3329
+ this.attachRouterListeners();
3330
+ }
3331
+ get id() {
3332
+ return this.router.id;
3333
+ }
3334
+ addTransport = (transport) => {
3335
+ let transportType = transport.type;
3336
+ if (!transportType) {
3337
+ if (transport.constructor.name === "WebRtcTransport") transportType = "webrtc";
3338
+ else if (transport.constructor.name === "PlainTransport") transportType = "plain";
3339
+ else if (transport.constructor.name === "PipeTransport") transportType = "pipe";
3340
+ else if (transport.constructor.name === "DirectTransport") transportType = "direct";
3341
+ }
3342
+ if (!transportType) return logger6.warn(`Unknown transport type for transport ${transport.id}: ${transport.constructor.name}`);
3343
+ switch (transportType) {
3344
+ case "webrtc":
3345
+ return this.addWebRtcTransport(transport);
3346
+ case "plain":
3347
+ return this.addPlainTransport(transport);
3348
+ case "pipe":
3349
+ return this.addPipeTransport(transport);
3350
+ case "direct":
3351
+ return this.addDirectTransport(transport);
3352
+ default:
3353
+ return logger6.warn(`Unsupported transport type for transport ${transport.id}: ${transportType}`);
3354
+ }
3355
+ };
3356
+ addWebRtcTransport(transport) {
3357
+ const history = [];
3358
+ const transportSample = {
3359
+ id: transport.id,
3360
+ type: "webrtc",
3361
+ createdAt: Date.now(),
3362
+ tuple: transport.iceSelectedTuple,
3363
+ history
3364
+ };
3365
+ this.sample.transports.push(transportSample);
3366
+ const onIceStateChange = (iceState) => {
3367
+ history.push({
3368
+ type: `icestate-changed-to-${iceState}`,
3369
+ timestamp: Date.now()
3370
+ });
3371
+ };
3372
+ const onIceSelectedTupleChange = (tuple) => {
3373
+ transportSample.tuple = tuple;
3374
+ history.push({
3375
+ type: "iceselectedtuple-changed",
3376
+ timestamp: Date.now(),
3377
+ ...tuple
3378
+ });
3379
+ };
3380
+ const onDtlsStateChange = (dtlsState) => {
3381
+ history.push({
3382
+ type: `dtlsstate-changed-to-${dtlsState}`,
3383
+ timestamp: Date.now()
3384
+ });
3385
+ if (dtlsState === "connected" && transportSample.connectedAt === void 0) {
3386
+ transportSample.connectedAt = Date.now();
3387
+ }
3388
+ };
3389
+ const onSctpStateChange = (sctpState) => {
3390
+ history.push({
3391
+ type: `sctpstate-changed-to-${sctpState}`,
3392
+ timestamp: Date.now()
3393
+ });
3394
+ };
3395
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3396
+ transport.observer.off("icestatechange", onIceStateChange);
3397
+ transport.observer.off("iceselectedtuplechange", onIceSelectedTupleChange);
3398
+ transport.observer.off("dtlsstatechange", onDtlsStateChange);
3399
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3400
+ this.webrtcTransportIds.delete(transport.id);
3401
+ });
3402
+ transport.observer.on("icestatechange", onIceStateChange);
3403
+ transport.observer.on("iceselectedtuplechange", onIceSelectedTupleChange);
3404
+ transport.observer.on("dtlsstatechange", onDtlsStateChange);
3405
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3406
+ this.webrtcTransportIds.add(transport.id);
3407
+ }
3408
+ addPlainTransport(transport) {
3409
+ const history = [];
3410
+ const transportSample = {
3411
+ id: transport.id,
3412
+ type: "plain",
3413
+ createdAt: Date.now(),
3414
+ tuple: transport.tuple,
3415
+ rtcpTuple: transport.rtcpTuple,
3416
+ history
3417
+ };
3418
+ this.sample.transports.push(transportSample);
3419
+ const onTuple = (tuple) => {
3420
+ transportSample.tuple = tuple;
3421
+ if (transportSample.connectedAt === void 0) transportSample.connectedAt = Date.now();
3422
+ history.push({ type: "tuple-changed", timestamp: Date.now(), ...tuple });
3423
+ };
3424
+ const onRtcpTuple = (rtcpTuple) => {
3425
+ transportSample.rtcpTuple = rtcpTuple;
3426
+ history.push({ type: "rtcptuple-changed", timestamp: Date.now(), ...rtcpTuple });
3427
+ };
3428
+ const onSctpStateChange = (sctpState) => {
3429
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3430
+ transportSample.connectedAt = Date.now();
3431
+ }
3432
+ history.push({
3433
+ type: `sctpstate-changed-to-${sctpState}`,
3434
+ timestamp: Date.now()
3435
+ });
3436
+ };
3437
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3438
+ transport.observer.off("tuple", onTuple);
3439
+ transport.observer.off("rtcptuple", onRtcpTuple);
3440
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3441
+ });
3442
+ transport.observer.on("tuple", onTuple);
3443
+ transport.observer.on("rtcptuple", onRtcpTuple);
3444
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3445
+ }
3446
+ addPipeTransport(transport) {
3447
+ const history = [];
3448
+ const transportSample = {
3449
+ id: transport.id,
3450
+ type: "pipe",
3451
+ createdAt: Date.now(),
3452
+ tuple: transport.tuple,
3453
+ history
3454
+ };
3455
+ this.sample.transports.push(transportSample);
3456
+ const onSctpStateChange = (sctpState) => {
3457
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3458
+ transportSample.connectedAt = Date.now();
3459
+ }
3460
+ history.push({
3461
+ type: `sctpstate-changed-to-${sctpState}`,
3462
+ timestamp: Date.now()
3463
+ });
3464
+ };
3465
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3466
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3467
+ });
3468
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3469
+ }
3470
+ addDirectTransport(transport) {
3471
+ const now = Date.now();
3472
+ const transportSample = {
3473
+ id: transport.id,
3474
+ type: "direct",
3475
+ createdAt: now,
3476
+ // A DirectTransport has no network layer — it is connected the instant it exists.
3477
+ connectedAt: now,
3478
+ history: []
3479
+ };
3480
+ this.sample.transports.push(transportSample);
3481
+ this._attachTransportObserverListeners(transport, transportSample);
3482
+ }
3483
+ addProducer(transport, producer) {
3484
+ const firstCodec = producer.rtpParameters.codecs[0];
3485
+ const codecInfo = {
3486
+ mimeType: firstCodec?.mimeType ?? "unknown/unknown",
3487
+ payloadType: firstCodec?.payloadType ?? 0,
3488
+ clockRate: firstCodec?.clockRate ?? 0,
3489
+ channels: firstCodec?.channels,
3490
+ parameters: firstCodec?.parameters,
3491
+ rtcpFeedback: firstCodec?.rtcpFeedback
3492
+ };
3493
+ const producerSample = {
3494
+ id: producer.id,
3495
+ createdAt: Date.now(),
3496
+ kind: producer.kind,
3497
+ codecInfo,
3498
+ ssrcs: producer.rtpParameters.encodings?.map((encoding) => encoding.ssrc).filter((ssrc) => typeof ssrc === "number"),
3499
+ rids: producer.rtpParameters.encodings?.map((encoding) => encoding.rid).filter((rid) => typeof rid === "string"),
3500
+ history: [],
3501
+ transportId: transport.id
3502
+ };
3503
+ this.sample.producers.push(producerSample);
3504
+ const onPause = () => {
3505
+ producerSample.history.push({
3506
+ type: "pause",
3507
+ timestamp: Date.now()
3508
+ });
3509
+ };
3510
+ const onResume = () => {
3511
+ producerSample.history.push({
3512
+ type: "resume",
3513
+ timestamp: Date.now()
3514
+ });
3515
+ };
3516
+ producer.observer.once("close", () => {
3517
+ producer.observer.off("pause", onPause);
3518
+ producer.observer.off("resume", onResume);
3519
+ producerSample.closedAt = Date.now();
3520
+ });
3521
+ producer.observer.on("pause", onPause);
3522
+ producer.observer.on("resume", onResume);
3523
+ }
3524
+ addConsumer(transport, consumer) {
3525
+ const consumerSample = {
3526
+ id: consumer.id,
3527
+ producerId: consumer.producerId,
3528
+ transportId: transport.id,
3529
+ createdAt: Date.now(),
3530
+ kind: consumer.kind,
3531
+ history: []
3532
+ };
3533
+ this.sample.consumers.push(consumerSample);
3534
+ const onPause = () => {
3535
+ consumerSample.history.push({
3536
+ type: "pause",
3537
+ timestamp: Date.now()
3538
+ });
3539
+ };
3540
+ const onResume = () => {
3541
+ consumerSample.history.push({
3542
+ type: "resume",
3543
+ timestamp: Date.now()
3544
+ });
3545
+ };
3546
+ const onProducerPause = () => {
3547
+ consumerSample.history.push({
3548
+ type: "producerPaused",
3549
+ timestamp: Date.now()
3550
+ });
3551
+ };
3552
+ const onProducerResume = () => {
3553
+ consumerSample.history.push({
3554
+ type: "producerResumed",
3555
+ timestamp: Date.now()
3556
+ });
3557
+ };
3558
+ consumer.observer.once("close", () => {
3559
+ consumer.observer.off("pause", onPause);
3560
+ consumer.observer.off("resume", onResume);
3561
+ consumer.off("producerresume", onProducerResume);
3562
+ consumer.off("producerpause", onProducerPause);
3563
+ consumerSample.closedAt = Date.now();
3564
+ });
3565
+ consumer.observer.on("pause", onPause);
3566
+ consumer.observer.on("resume", onResume);
3567
+ consumer.on("producerpause", onProducerPause);
3568
+ consumer.on("producerresume", onProducerResume);
3569
+ }
3570
+ addDataProducer(transport, dataProducer) {
3571
+ const dataProducerSample = {
3572
+ id: dataProducer.id,
3573
+ transportId: transport.id,
3574
+ createdAt: Date.now(),
3575
+ label: dataProducer.label,
3576
+ protocol: dataProducer.protocol
3577
+ };
3578
+ this.sample.dataProducers.push(dataProducerSample);
3579
+ dataProducer.observer.once("close", () => {
3580
+ dataProducerSample.closedAt = Date.now();
3581
+ });
3582
+ }
3583
+ addDataConsumer(transport, dataConsumer) {
3584
+ const dataConsumerSample = {
3585
+ id: dataConsumer.id,
3586
+ dataProducerId: dataConsumer.dataProducerId,
3587
+ transportId: transport.id,
3588
+ createdAt: Date.now(),
3589
+ label: dataConsumer.label,
3590
+ protocol: dataConsumer.protocol
3591
+ };
3592
+ this.sample.dataConsumers.push(dataConsumerSample);
3593
+ dataConsumer.observer.once("close", () => {
3594
+ dataConsumerSample.closedAt = Date.now();
3595
+ });
3596
+ }
3597
+ close() {
3598
+ if (this.closed) return;
3599
+ this.closed = true;
3600
+ this.emit("close");
3601
+ }
3602
+ attachRouterListeners() {
3603
+ this.router.observer.once("close", () => {
3604
+ this.router.observer.off("newtransport", this.addTransport);
3605
+ this.sample.closedAt = Date.now();
3606
+ this.close();
3607
+ });
3608
+ this.router.observer.on("newtransport", this.addTransport);
3609
+ }
3610
+ _attachTransportObserverListeners(transport, transportSample, onClose) {
3611
+ const onNewProducer = (producer) => {
3612
+ this.addProducer(transport, producer);
3613
+ };
3614
+ const onNewConsumer = (consumer) => {
3615
+ this.addConsumer(transport, consumer);
3616
+ };
3617
+ const onNewDataProducer = (dataProducer) => {
3618
+ this.addDataProducer(transport, dataProducer);
3619
+ };
3620
+ const onNewDataConsumer = (dataConsumer) => {
3621
+ this.addDataConsumer(transport, dataConsumer);
3622
+ };
3623
+ transport.observer.once("close", () => {
3624
+ transport.observer.off("newproducer", onNewProducer);
3625
+ transport.observer.off("newconsumer", onNewConsumer);
3626
+ transport.observer.off("newdataproducer", onNewDataProducer);
3627
+ transport.observer.off("newdataconsumer", onNewDataConsumer);
3628
+ onClose?.();
3629
+ transportSample.closedAt = Date.now();
3630
+ });
3631
+ transport.observer.on("newproducer", onNewProducer);
3632
+ transport.observer.on("newconsumer", onNewConsumer);
3633
+ transport.observer.on("newdataproducer", onNewDataProducer);
3634
+ transport.observer.on("newdataconsumer", onNewDataConsumer);
3635
+ }
3636
+ };
3637
+
3300
3638
  // src/Observer.ts
3301
- var logger6 = createLogger("Observer");
3302
- var Observer = class extends import_events5.EventEmitter {
3639
+ var logger7 = createLogger("Observer");
3640
+ var Observer = class extends import_events6.EventEmitter {
3303
3641
  constructor(config = {
3304
3642
  updatePolicy: "update-when-all-call-updated",
3305
3643
  appData: {}
@@ -3316,11 +3654,14 @@ var Observer = class extends import_events5.EventEmitter {
3316
3654
  case "update-when-all-call-updated":
3317
3655
  this.updater = new OnAllCallObserverUpdater(this);
3318
3656
  break;
3657
+ case "none":
3658
+ break;
3319
3659
  }
3320
3660
  }
3321
3661
  config;
3322
3662
  observedTURN = new ObservedTURN();
3323
3663
  observedCalls = /* @__PURE__ */ new Map();
3664
+ observedMediasoupRouters = /* @__PURE__ */ new Map();
3324
3665
  updater;
3325
3666
  /** Ancestry base shared by all Observer-bus events originating at the observer. */
3326
3667
  eventScope = { observer: this };
@@ -3347,11 +3688,11 @@ var Observer = class extends import_events5.EventEmitter {
3347
3688
  }
3348
3689
  createObservedCall(settings) {
3349
3690
  if (this.closed) {
3350
- logger6.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3691
+ logger7.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3351
3692
  return void 0;
3352
3693
  }
3353
3694
  if (this.observedCalls.has(settings.callId)) {
3354
- logger6.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3695
+ logger7.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3355
3696
  return this.observedCalls.get(settings.callId);
3356
3697
  }
3357
3698
  const callSettings = {
@@ -3374,9 +3715,53 @@ var Observer = class extends import_events5.EventEmitter {
3374
3715
  getOrCreateObservedCall(settings) {
3375
3716
  return this.getObservedCall(settings.callId) ?? this.createObservedCall(settings);
3376
3717
  }
3718
+ createObservedMediasoupRouter(settings) {
3719
+ if (this.closed) {
3720
+ logger7.warn("Attempted to create mediasoup router (id: %d) on a closed observer", settings.router.id);
3721
+ return void 0;
3722
+ }
3723
+ if (this.observedMediasoupRouters.has(settings.router.id)) {
3724
+ logger7.warn("Observed Mediasoup Router (id %s) already exists; returning the existing instance", settings.router.id);
3725
+ return this.observedMediasoupRouters.get(settings.router.id);
3726
+ }
3727
+ const observedMediasoupRouter = new ObservedMediasoupRouter(settings);
3728
+ const observedMediasoupRouterScope = {
3729
+ observedMediasoupRouter,
3730
+ observer: this
3731
+ };
3732
+ const directCall = settings.callId ? this.observedCalls.get(settings.callId) : void 0;
3733
+ if (directCall) {
3734
+ this.emit("mediasoup-router-matched-with-call", {
3735
+ ...observedMediasoupRouterScope,
3736
+ observedCall: directCall
3737
+ });
3738
+ } else if (settings.bindCallByWebRtcTransportId) {
3739
+ const matchedCallIds = /* @__PURE__ */ new Set();
3740
+ const onPeerConnectionAdded = ({ observedPeerConnection, observedCall }) => {
3741
+ if (!observedMediasoupRouter.webrtcTransportIds.has(observedPeerConnection.peerConnectionId)) return;
3742
+ if (matchedCallIds.has(observedCall.callId)) return;
3743
+ matchedCallIds.add(observedCall.callId);
3744
+ this.emit("mediasoup-router-matched-with-call", {
3745
+ ...observedMediasoupRouterScope,
3746
+ observedCall
3747
+ });
3748
+ };
3749
+ const stopMatching = () => this.off("peer-connection-added", onPeerConnectionAdded);
3750
+ this.on("peer-connection-added", onPeerConnectionAdded);
3751
+ this.once("observer-closed", stopMatching);
3752
+ observedMediasoupRouter.once("close", stopMatching);
3753
+ }
3754
+ observedMediasoupRouter.once("close", () => {
3755
+ this.observedMediasoupRouters.delete(observedMediasoupRouter.id);
3756
+ this.emit("mediasoup-router-removed", observedMediasoupRouterScope);
3757
+ });
3758
+ this.observedMediasoupRouters.set(observedMediasoupRouter.id, observedMediasoupRouter);
3759
+ this.emit("mediasoup-router-added", observedMediasoupRouterScope);
3760
+ return observedMediasoupRouter;
3761
+ }
3377
3762
  close() {
3378
3763
  if (this.closed) {
3379
- return logger6.debug("Attempted to close twice");
3764
+ return logger7.debug("Attempted to close twice");
3380
3765
  }
3381
3766
  this.closed = true;
3382
3767
  this.observedCalls.forEach((call) => call.close());
@@ -3389,7 +3774,7 @@ var Observer = class extends import_events5.EventEmitter {
3389
3774
  try {
3390
3775
  this.acceptMiddlewares.process({ sample, context });
3391
3776
  } catch (err) {
3392
- logger6.warn("An accept middleware threw; dropping the sample. %o", err);
3777
+ logger7.warn("An accept middleware threw; dropping the sample. %o", err);
3393
3778
  }
3394
3779
  if (!sample.callId) {
3395
3780
  this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
@@ -3445,8 +3830,8 @@ var Observer = class extends import_events5.EventEmitter {
3445
3830
  };
3446
3831
 
3447
3832
  // src/sinks/ClientSampleSink.ts
3448
- var import_events6 = require("events");
3449
- var ClientSampleSink = class extends import_events6.EventEmitter {
3833
+ var import_events7 = require("events");
3834
+ var ClientSampleSink = class extends import_events7.EventEmitter {
3450
3835
  };
3451
3836
 
3452
3837
  // src/sinks/JsonlFileSink.ts
@@ -3646,6 +4031,7 @@ function createP2pRemoteTrackResolverFactory() {
3646
4031
  ObservedInboundTrack,
3647
4032
  ObservedMediaPlayout,
3648
4033
  ObservedMediaSource,
4034
+ ObservedMediasoupRouter,
3649
4035
  ObservedOutboundRtp,
3650
4036
  ObservedOutboundTrack,
3651
4037
  ObservedPeerConnection,