@observertc/observer-js 1.0.0-beta.8 → 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
@@ -3053,7 +3054,7 @@ var ObservedCall = class extends import_events3.EventEmitter {
3053
3054
  };
3054
3055
 
3055
3056
  // src/Observer.ts
3056
- var import_events5 = require("events");
3057
+ var import_events6 = require("events");
3057
3058
 
3058
3059
  // src/ObservedTURN.ts
3059
3060
  var import_events4 = require("events");
@@ -3299,9 +3300,344 @@ var MiddlewareProcessor = class {
3299
3300
  }
3300
3301
  };
3301
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
+
3302
3638
  // src/Observer.ts
3303
- var logger6 = createLogger("Observer");
3304
- var Observer = class extends import_events5.EventEmitter {
3639
+ var logger7 = createLogger("Observer");
3640
+ var Observer = class extends import_events6.EventEmitter {
3305
3641
  constructor(config = {
3306
3642
  updatePolicy: "update-when-all-call-updated",
3307
3643
  appData: {}
@@ -3325,6 +3661,7 @@ var Observer = class extends import_events5.EventEmitter {
3325
3661
  config;
3326
3662
  observedTURN = new ObservedTURN();
3327
3663
  observedCalls = /* @__PURE__ */ new Map();
3664
+ observedMediasoupRouters = /* @__PURE__ */ new Map();
3328
3665
  updater;
3329
3666
  /** Ancestry base shared by all Observer-bus events originating at the observer. */
3330
3667
  eventScope = { observer: this };
@@ -3351,11 +3688,11 @@ var Observer = class extends import_events5.EventEmitter {
3351
3688
  }
3352
3689
  createObservedCall(settings) {
3353
3690
  if (this.closed) {
3354
- 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);
3355
3692
  return void 0;
3356
3693
  }
3357
3694
  if (this.observedCalls.has(settings.callId)) {
3358
- 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);
3359
3696
  return this.observedCalls.get(settings.callId);
3360
3697
  }
3361
3698
  const callSettings = {
@@ -3378,9 +3715,53 @@ var Observer = class extends import_events5.EventEmitter {
3378
3715
  getOrCreateObservedCall(settings) {
3379
3716
  return this.getObservedCall(settings.callId) ?? this.createObservedCall(settings);
3380
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
+ }
3381
3762
  close() {
3382
3763
  if (this.closed) {
3383
- return logger6.debug("Attempted to close twice");
3764
+ return logger7.debug("Attempted to close twice");
3384
3765
  }
3385
3766
  this.closed = true;
3386
3767
  this.observedCalls.forEach((call) => call.close());
@@ -3393,7 +3774,7 @@ var Observer = class extends import_events5.EventEmitter {
3393
3774
  try {
3394
3775
  this.acceptMiddlewares.process({ sample, context });
3395
3776
  } catch (err) {
3396
- logger6.warn("An accept middleware threw; dropping the sample. %o", err);
3777
+ logger7.warn("An accept middleware threw; dropping the sample. %o", err);
3397
3778
  }
3398
3779
  if (!sample.callId) {
3399
3780
  this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
@@ -3449,8 +3830,8 @@ var Observer = class extends import_events5.EventEmitter {
3449
3830
  };
3450
3831
 
3451
3832
  // src/sinks/ClientSampleSink.ts
3452
- var import_events6 = require("events");
3453
- var ClientSampleSink = class extends import_events6.EventEmitter {
3833
+ var import_events7 = require("events");
3834
+ var ClientSampleSink = class extends import_events7.EventEmitter {
3454
3835
  };
3455
3836
 
3456
3837
  // src/sinks/JsonlFileSink.ts
@@ -3650,6 +4031,7 @@ function createP2pRemoteTrackResolverFactory() {
3650
4031
  ObservedInboundTrack,
3651
4032
  ObservedMediaPlayout,
3652
4033
  ObservedMediaSource,
4034
+ ObservedMediasoupRouter,
3653
4035
  ObservedOutboundRtp,
3654
4036
  ObservedOutboundTrack,
3655
4037
  ObservedPeerConnection,