@camstack/addon-pipeline 1.2.220 → 1.2.221

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 (32) hide show
  1. package/dist/audio-analyzer/index.js +614 -4
  2. package/dist/audio-analyzer/index.mjs +614 -4
  3. package/dist/{default-detection-model-x6oNMBDH.js → default-detection-model-B1NT1A38.js} +1 -1
  4. package/dist/{default-detection-model-DBNEc3iw.mjs → default-detection-model-DkVFgubU.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-ByPsWlTa.js → dist-ATDfcxgL.js} +280 -2
  8. package/dist/{dist-ClihWGOZ.mjs → dist-CEFC3PkP.mjs} +263 -3
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-CNPnaux4.js → node-C5jnTgpv.js} +1 -1
  12. package/dist/{node-DiaTmz84.mjs → node-DR3fHqUq.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +3 -3
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/{process-memory-9KqRtUHt.js → process-memory-DQbCPnMA.js} +1 -1
  16. package/dist/{process-memory-D4t8hpJd.mjs → process-memory-DqYj5IAb.mjs} +1 -1
  17. package/dist/recorder/index.js +12 -6
  18. package/dist/recorder/index.mjs +12 -6
  19. package/dist/{segment-demux-js-CFFXVae4.mjs → segment-demux-js-CddDwD69.mjs} +1 -1
  20. package/dist/{segment-demux-js-CZefXDLx.js → segment-demux-js-o9KBAz9p.js} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CtdpN7Sz.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D5XnLZWR.mjs} +3 -3
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CyMYDKfM.mjs +26 -0
  24. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DICVQkNi.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CKUGpDns.mjs} +1 -1
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-McoUiljf.mjs → hostInit-C-VdcJS2.mjs} +3 -3
  28. package/dist/stream-broker/index.js +222 -102
  29. package/dist/stream-broker/index.mjs +222 -102
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-7hqrrd88.mjs +0 -26
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-ByPsWlTa.js");
7
- const require_node = require("../node-CNPnaux4.js");
6
+ const require_dist = require("../dist-ATDfcxgL.js");
7
+ const require_node = require("../node-C5jnTgpv.js");
8
8
  const require_restream_intent = require("../restream-intent-B1Difcnq.js");
9
9
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
11
- const require_segment_demux_js = require("../segment-demux-js-CZefXDLx.js");
11
+ const require_segment_demux_js = require("../segment-demux-js-o9KBAz9p.js");
12
12
  let node_os = require("node:os");
13
13
  node_os = require_chunk.__toESM(node_os);
14
14
  let node_crypto = require("node:crypto");
@@ -8383,106 +8383,72 @@ function depacketizeRfc3640(payload, opts = {
8383
8383
  //#region src/stream-broker/rtsp/audio-rtp-decoder.ts
8384
8384
  /** G.711 is 8 kHz by definition (RFC 3551): the plane carries that rate. */
8385
8385
  var G711_SAMPLE_RATE = 8e3;
8386
- var CHUNK_SAMPLES = G711_SAMPLE_RATE * 500 / 1e3;
8386
+ /** One G.711 byte is one sample, so samples and bytes are the same count. */
8387
+ var CHUNK_BYTES = G711_SAMPLE_RATE * 500 / 1e3;
8388
+ /** The chunk `format` each RTP codec produces. */
8389
+ var CODEC_FORMAT = {
8390
+ PCMU: "pcmu",
8391
+ PCMA: "pcma"
8392
+ };
8387
8393
  /**
8388
- * Decodes G.711 audio RTP packets into PCM f32le chunks at 8 kHz.
8394
+ * Chunks G.711 audio RTP payloads into ~0.5 s coded chunks at 8 kHz.
8389
8395
  *
8390
- * Buffers decoded samples and emits a DecodedAudioChunk when ~0.5 s has
8391
- * accumulated (4000 samples, 16 000 bytes).
8396
+ * Buffers payload bytes and emits a `DecodedAudioChunk` carrying the source
8397
+ * codec once ~0.5 s has accumulated (4000 bytes).
8392
8398
  */
8393
8399
  var AudioRtpDecoder = class {
8394
- codec;
8400
+ format;
8395
8401
  onChunk;
8396
8402
  buffer;
8397
8403
  bufferOffset = 0;
8398
8404
  constructor(codec, onChunk) {
8399
- this.codec = codec;
8405
+ this.format = CODEC_FORMAT[codec];
8400
8406
  this.onChunk = onChunk;
8401
- this.buffer = new Float32Array(CHUNK_SAMPLES);
8407
+ this.buffer = Buffer.allocUnsafe(CHUNK_BYTES);
8402
8408
  }
8403
8409
  /**
8404
- * Process a raw RTP packet (header + payload).
8405
- * Strips the 12-byte RTP header and decodes the G.711 payload: one byte,
8406
- * one 8 kHz sample. Nothing is interpolated here.
8410
+ * Process a raw RTP packet (header + payload). Strips the 12-byte RTP header
8411
+ * and appends the payload bytes verbatim, emitting whenever a chunk fills.
8412
+ * A payload larger than the remaining room spans chunks.
8407
8413
  */
8408
8414
  processPacket(rtpData) {
8409
8415
  if (rtpData.length <= 12) return;
8410
8416
  const payload = rtpData.subarray(12);
8411
- const decode = this.codec === "PCMU" ? decodeUlaw : decodeAlaw;
8412
- for (let i = 0; i < payload.length; i++) this.pushSample(decode(payload[i]));
8417
+ let consumed = 0;
8418
+ while (consumed < payload.length) {
8419
+ const room = CHUNK_BYTES - this.bufferOffset;
8420
+ const take = Math.min(room, payload.length - consumed);
8421
+ payload.copy(this.buffer, this.bufferOffset, consumed, consumed + take);
8422
+ this.bufferOffset += take;
8423
+ consumed += take;
8424
+ if (this.bufferOffset >= CHUNK_BYTES) this.emit();
8425
+ }
8413
8426
  }
8414
- /** Flush any remaining buffered samples as a partial chunk. */
8427
+ /** Flush any remaining buffered bytes as a partial chunk. */
8415
8428
  flush() {
8416
8429
  if (this.bufferOffset === 0) return;
8417
- const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, this.bufferOffset * 4);
8430
+ this.emit();
8431
+ }
8432
+ /** Reset the chunker state. */
8433
+ reset() {
8434
+ this.bufferOffset = 0;
8435
+ }
8436
+ /**
8437
+ * Emit what is buffered and start a fresh chunk. The bytes are COPIED out:
8438
+ * the accumulator is reused, and the plane holds every queued chunk by
8439
+ * reference until its consumer polls.
8440
+ */
8441
+ emit() {
8418
8442
  this.onChunk({
8419
- data: Buffer.from(data),
8443
+ data: Buffer.from(this.buffer.subarray(0, this.bufferOffset)),
8420
8444
  sampleRate: G711_SAMPLE_RATE,
8421
8445
  channels: 1,
8422
- timestamp: Date.now()
8446
+ timestamp: Date.now(),
8447
+ format: this.format
8423
8448
  });
8424
8449
  this.bufferOffset = 0;
8425
8450
  }
8426
- /** Reset the decoder state. */
8427
- reset() {
8428
- this.bufferOffset = 0;
8429
- }
8430
- pushSample(sample) {
8431
- this.buffer[this.bufferOffset++] = sample;
8432
- if (this.bufferOffset >= CHUNK_SAMPLES) {
8433
- const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, CHUNK_SAMPLES * 4);
8434
- this.onChunk({
8435
- data: Buffer.from(data),
8436
- sampleRate: G711_SAMPLE_RATE,
8437
- channels: 1,
8438
- timestamp: Date.now()
8439
- });
8440
- this.bufferOffset = 0;
8441
- }
8442
- }
8443
8451
  };
8444
- /** μ-law compressed byte → normalized float [-1.0, 1.0]. */
8445
- function decodeUlaw(byte) {
8446
- return ULAW_TABLE[byte];
8447
- }
8448
- /** A-law compressed byte → normalized float [-1.0, 1.0]. */
8449
- function decodeAlaw(byte) {
8450
- return ALAW_TABLE[byte];
8451
- }
8452
- /**
8453
- * Build the μ-law decode table (ITU-T G.711).
8454
- * Each of the 256 possible byte values maps to a 16-bit PCM sample,
8455
- * which we normalize to [-1.0, 1.0] for f32le output.
8456
- */
8457
- function buildUlawTable() {
8458
- const table = new Float32Array(256);
8459
- for (let i = 0; i < 256; i++) {
8460
- const complemented = ~i & 255;
8461
- const sign = (complemented & 128) !== 0 ? -1 : 1;
8462
- const exponent = complemented >> 4 & 7;
8463
- table[i] = sign * ((2 * (complemented & 15) + 33 << exponent) - 33) / 32768;
8464
- }
8465
- return table;
8466
- }
8467
- /**
8468
- * Build the A-law decode table (ITU-T G.711).
8469
- */
8470
- function buildAlawTable() {
8471
- const table = new Float32Array(256);
8472
- for (let i = 0; i < 256; i++) {
8473
- const xored = i ^ 85;
8474
- const sign = (xored & 128) !== 0 ? -1 : 1;
8475
- const exponent = xored >> 4 & 7;
8476
- const mantissa = xored & 15;
8477
- let magnitude;
8478
- if (exponent === 0) magnitude = 2 * mantissa + 1;
8479
- else magnitude = 2 * mantissa + 33 << exponent - 1;
8480
- table[i] = sign * magnitude / 32768;
8481
- }
8482
- return table;
8483
- }
8484
- var ULAW_TABLE = buildUlawTable();
8485
- var ALAW_TABLE = buildAlawTable();
8486
8452
  //#endregion
8487
8453
  //#region src/stream-broker/rtsp/ffmpeg-source-reader.ts
8488
8454
  /**
@@ -9480,6 +9446,47 @@ function extractQuoted(header, key) {
9480
9446
  */
9481
9447
  var AUDIO_QUEUE_CAPACITY = 64;
9482
9448
  /**
9449
+ * How long a subscription may go without a `pull` before the plane releases it
9450
+ * (D461).
9451
+ *
9452
+ * Until 2026-09-11 the one subscriber was `pipeline-orchestrator`, which the
9453
+ * hub restarts alongside the broker, and an abandoned subscription was a
9454
+ * theoretical leak. D461 moved the poller into `audio-analyzer`, which may sit
9455
+ * on another node and may die without the orchestrator's detach ever
9456
+ * arriving — and on THIS plane an orphan is not merely a queue nobody drains.
9457
+ * `subscriberCount` is what `hasDemand()` reads, so one orphan pins the
9458
+ * broker's source dial and its decode session open for the broker's whole
9459
+ * life: a camera nobody is listening to, kept streaming and decoding by a dead
9460
+ * process.
9461
+ *
9462
+ * The bound has to clear the longest LEGITIMATE gap between two polls. The
9463
+ * poller awaits its consumer per chunk, and the consumer is now the classifier
9464
+ * itself, whose `classify` carries a 30 s timeout — so a gap of tens of
9465
+ * seconds is healthy. Two minutes is past every one of those and well past the
9466
+ * ~30 s of audio the FIFO can hold, so a subscription this idle has already
9467
+ * lost audio and cannot be serving anyone.
9468
+ */
9469
+ var AUDIO_SUBSCRIPTION_IDLE_MS = 12e4;
9470
+ /** `format` is optional on the wire and its absence means `f32le` (D455). */
9471
+ function formatOf(chunk) {
9472
+ return chunk.format ?? "f32le";
9473
+ }
9474
+ /**
9475
+ * Build the f32le twin of a coded chunk. Everything but `data` and `format`
9476
+ * is carried over — the rate, the channel count and the timestamp describe the
9477
+ * audio, not its encoding.
9478
+ */
9479
+ function expandChunk(chunk, sourceFormat) {
9480
+ const bytes = require_dist.expandAudioChunkToF32le(chunk.data, sourceFormat);
9481
+ return {
9482
+ data: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength),
9483
+ sampleRate: chunk.sampleRate,
9484
+ channels: chunk.channels,
9485
+ timestamp: chunk.timestamp,
9486
+ format: "f32le"
9487
+ };
9488
+ }
9489
+ /**
9483
9490
  * Owns the broker's decoded audio-chunk subscriptions. The broker feeds every
9484
9491
  * decoded `DecodedAudioChunk` into `fanout`; each subscription's poll loop
9485
9492
  * drains its own FIFO queue via `pull`.
@@ -9487,8 +9494,12 @@ var AUDIO_QUEUE_CAPACITY = 64;
9487
9494
  var AudioChunkPlane = class {
9488
9495
  logger;
9489
9496
  subscriptions = /* @__PURE__ */ new Map();
9490
- constructor(logger) {
9497
+ /** Last `reapIdle` sweep — the lease is checked on a cadence, not per chunk. */
9498
+ lastReapAt = Date.now();
9499
+ onSubscriptionsReaped;
9500
+ constructor(logger, onSubscriptionsReaped) {
9491
9501
  this.logger = logger;
9502
+ this.onSubscriptionsReaped = onSubscriptionsReaped;
9492
9503
  }
9493
9504
  /** Number of active audio-chunk subscriptions — surfaced as audio demand. */
9494
9505
  get subscriberCount() {
@@ -9505,12 +9516,16 @@ var AudioChunkPlane = class {
9505
9516
  tag: input?.tag ?? "unknown",
9506
9517
  subscribedAt: Date.now(),
9507
9518
  queue: new require_dist.RingBuffer(AUDIO_QUEUE_CAPACITY),
9508
- chunksDelivered: 0
9519
+ accept: new Set(input?.accept ?? []),
9520
+ servedKey: null,
9521
+ chunksDelivered: 0,
9522
+ lastPulledAt: Date.now()
9509
9523
  };
9510
9524
  this.subscriptions.set(subscriptionId, subscription);
9511
9525
  this.logger?.info("audio-chunk subscription added", { meta: {
9512
9526
  subscriptionId,
9513
- tag: subscription.tag
9527
+ tag: subscription.tag,
9528
+ accept: input?.accept ?? []
9514
9529
  } });
9515
9530
  return { subscriptionId };
9516
9531
  }
@@ -9522,10 +9537,44 @@ var AudioChunkPlane = class {
9522
9537
  pull(subscriptionId, maxCount) {
9523
9538
  const subscription = this.subscriptions.get(subscriptionId);
9524
9539
  if (!subscription) return [];
9540
+ subscription.lastPulledAt = Date.now();
9525
9541
  const chunks = subscription.queue.drain(maxCount);
9526
9542
  subscription.chunksDelivered += chunks.length;
9527
9543
  return chunks;
9528
9544
  }
9545
+ /**
9546
+ * Release every subscription nobody has drained for
9547
+ * {@link AUDIO_SUBSCRIPTION_IDLE_MS}, and NAME each one (D461, D427).
9548
+ *
9549
+ * The lease is the net under the ownership split: the subscriber lives in a
9550
+ * process the orchestrator does not supervise, so a missed `detachDevice`
9551
+ * has to be survivable rather than trusted. It is deliberately driven from
9552
+ * `fanout` — the plane has no clock of its own, and the moment a dead
9553
+ * consumer costs anything is the moment there is a chunk to put in its
9554
+ * queue. A plane with nothing flowing has nothing to reclaim.
9555
+ *
9556
+ * Returns the ids released so the broker can re-evaluate its demand; an
9557
+ * orphan that is dropped but still counted would have fixed nothing.
9558
+ */
9559
+ reapIdle(now = Date.now()) {
9560
+ const reaped = [];
9561
+ for (const subscription of [...this.subscriptions.values()]) {
9562
+ const idleMs = now - subscription.lastPulledAt;
9563
+ if (idleMs < 12e4) continue;
9564
+ this.subscriptions.delete(subscription.id);
9565
+ reaped.push(subscription.id);
9566
+ this.logger?.warn("audio-chunk subscription REAPED — nobody drained it", { meta: {
9567
+ subscriptionId: subscription.id,
9568
+ tag: subscription.tag,
9569
+ idleMs,
9570
+ leaseMs: AUDIO_SUBSCRIPTION_IDLE_MS,
9571
+ queued: subscription.queue.size,
9572
+ chunksDelivered: subscription.chunksDelivered,
9573
+ hint: "the consumer process died without detaching — its subscription was holding this broker in demand"
9574
+ } });
9575
+ }
9576
+ return reaped;
9577
+ }
9529
9578
  /** Release a subscription. Returns `true` when a known subscription was released. */
9530
9579
  unsubscribe(subscriptionId) {
9531
9580
  const existed = this.subscriptions.delete(subscriptionId);
@@ -9536,9 +9585,67 @@ var AudioChunkPlane = class {
9536
9585
  * Fan one decoded `DecodedAudioChunk` into every active subscription's FIFO
9537
9586
  * queue. A full queue drops its oldest chunk (bounded safety valve for a
9538
9587
  * stalled consumer). No-op when there are no subscriptions.
9588
+ *
9589
+ * ## The format each subscription gets (D455)
9590
+ *
9591
+ * The decoder now hands this plane the SOURCE bytes — one G.711 byte per
9592
+ * sample instead of four f32le ones — because the plane crosses hub-main
9593
+ * twice on its way to the analyzer and every leg was paying the expansion.
9594
+ * A subscription receives those bytes only if it DECLARED it can read them;
9595
+ * otherwise the plane expands to `f32le` here, which is byte-for-byte what
9596
+ * every consumer received before this existed.
9597
+ *
9598
+ * The expansion is done ONCE per fanout and shared by every subscription
9599
+ * that needs it: the chunk is immutable and the queue holds references.
9539
9600
  */
9540
9601
  fanout(chunk) {
9541
- for (const subscription of this.subscriptions.values()) subscription.queue.push(chunk);
9602
+ this.maybeReap();
9603
+ const sourceFormat = formatOf(chunk);
9604
+ let expanded = null;
9605
+ for (const subscription of this.subscriptions.values()) {
9606
+ if (sourceFormat === "f32le" || subscription.accept.has(sourceFormat)) {
9607
+ this.noteServed(subscription, sourceFormat, sourceFormat);
9608
+ subscription.queue.push(chunk);
9609
+ continue;
9610
+ }
9611
+ expanded ??= expandChunk(chunk, sourceFormat);
9612
+ this.noteServed(subscription, sourceFormat, "f32le");
9613
+ subscription.queue.push(expanded);
9614
+ }
9615
+ }
9616
+ /**
9617
+ * Run the idle-lease sweep on a cadence rather than on every chunk. A quarter
9618
+ * of the lease bounds how long an orphan outlives its bound by, and keeps
9619
+ * the per-chunk cost of `fanout` a single clock read.
9620
+ */
9621
+ maybeReap() {
9622
+ const now = Date.now();
9623
+ if (now - this.lastReapAt < 12e4 / 4) return;
9624
+ this.lastReapAt = now;
9625
+ if (this.reapIdle(now).length > 0) this.onSubscriptionsReaped?.();
9626
+ }
9627
+ /**
9628
+ * Say what a subscription is being served — once, and again on every change.
9629
+ *
9630
+ * The audio decision of a relay is never silent (D427). Two things this line
9631
+ * answers that nothing else can: a consumer receiving a format it did not
9632
+ * expect (the failure that would otherwise surface as a level of +43 dBFS
9633
+ * with no cause), and a leg still paying 4x because its consumer declared no
9634
+ * `accept` — `expanded: true` is the byte cost, named where it is incurred.
9635
+ */
9636
+ noteServed(subscription, sourceFormat, servedFormat) {
9637
+ const key = `${sourceFormat}>${servedFormat}`;
9638
+ if (subscription.servedKey === key) return;
9639
+ const previous = subscription.servedKey;
9640
+ subscription.servedKey = key;
9641
+ this.logger?.info("audio-chunk subscription serving", { meta: {
9642
+ subscriptionId: subscription.id,
9643
+ tag: subscription.tag,
9644
+ sourceFormat,
9645
+ servedFormat,
9646
+ expanded: servedFormat !== sourceFormat,
9647
+ previous
9648
+ } });
9542
9649
  }
9543
9650
  /** Diagnostic snapshot of every active audio-chunk subscription. */
9544
9651
  listSubscribers() {
@@ -11836,7 +11943,7 @@ var StreamBroker = class StreamBroker {
11836
11943
  });
11837
11944
  this.preBuffer = new EncodedRingBuffer(StreamBroker.DEFAULT_PRE_BUFFER_SEC);
11838
11945
  this.decoderSeedBuffer = new EncodedRingBuffer(StreamBroker.DECODER_SEED_BUFFER_SEC);
11839
- this.audioChunkPlane = new AudioChunkPlane(logger?.child("audio-chunk-plane"));
11946
+ this.audioChunkPlane = new AudioChunkPlane(logger?.child("audio-chunk-plane"), () => this.checkDemand());
11840
11947
  this.rtspRestreamer.onSessionCountChanged(() => this.checkDemand());
11841
11948
  this.pipeServer.onClientCountChanged(() => this.checkDemand());
11842
11949
  }
@@ -13191,7 +13298,10 @@ var StreamBroker = class StreamBroker {
13191
13298
  subscribeAudioChunks(options) {
13192
13299
  const tag = options?.tag;
13193
13300
  if (!tag) this.logger?.warn(`subscribeAudioChunks called without tag — listClients will show 'unknown'`);
13194
- const result = this.audioChunkPlane.subscribe({ tag });
13301
+ const result = this.audioChunkPlane.subscribe({
13302
+ tag,
13303
+ accept: options?.accept
13304
+ });
13195
13305
  this.checkDemand();
13196
13306
  return result;
13197
13307
  }
@@ -13671,16 +13781,19 @@ var StreamBroker = class StreamBroker {
13671
13781
  const codecUpper = audioTrack.codec.toUpperCase();
13672
13782
  this.audioCodec = codecUpper;
13673
13783
  const supportedNative = codecUpper === "PCMU" || codecUpper === "PCMA";
13674
- this.logger?.info("audio: track detected", { meta: {
13675
- codec: audioTrack.codec,
13676
- codecUpper,
13677
- clockRate: audioTrack.clockRate,
13678
- channels: audioTrack.channels,
13679
- fmtpKeys: Object.keys(audioTrack.fmtp),
13680
- hasConfigHex: "config" in audioTrack.fmtp || "CONFIG" in audioTrack.fmtp,
13681
- audioCodecApiAvailable: this.audioCodecApi !== null,
13682
- supportedNative
13683
- } });
13784
+ this.logger?.info("audio: track detected", {
13785
+ tags: { deviceId: this.numericDeviceId },
13786
+ meta: {
13787
+ codec: audioTrack.codec,
13788
+ codecUpper,
13789
+ clockRate: audioTrack.clockRate,
13790
+ channels: audioTrack.channels,
13791
+ fmtpKeys: Object.keys(audioTrack.fmtp),
13792
+ hasConfigHex: "config" in audioTrack.fmtp || "CONFIG" in audioTrack.fmtp,
13793
+ audioCodecApiAvailable: this.audioCodecApi !== null,
13794
+ supportedNative
13795
+ }
13796
+ });
13684
13797
  let supported = supportedNative;
13685
13798
  let effectiveSampleRate = audioTrack.clockRate;
13686
13799
  let effectiveChannels = audioTrack.channels;
@@ -13688,13 +13801,17 @@ var StreamBroker = class StreamBroker {
13688
13801
  this.audioRtpDecoder = new AudioRtpDecoder(codecUpper, (chunk) => {
13689
13802
  this.audioChunkPlane.fanout(chunk);
13690
13803
  });
13691
- this.logger?.info("native: audio decoder initialized", { meta: {
13692
- codec: codecUpper,
13693
- sampleRate: audioTrack.clockRate,
13694
- channels: audioTrack.channels,
13695
- planeSampleRate: G711_SAMPLE_RATE,
13696
- planeFormat: "f32le"
13697
- } });
13804
+ this.logger?.info("native: audio decoder initialized", {
13805
+ tags: { deviceId: this.numericDeviceId },
13806
+ meta: {
13807
+ codec: codecUpper,
13808
+ sampleRate: audioTrack.clockRate,
13809
+ channels: audioTrack.channels,
13810
+ planeSampleRate: G711_SAMPLE_RATE,
13811
+ planeFormat: codecUpper === "PCMA" ? "pcma" : "pcmu",
13812
+ preD459LevelDeficitDb: Math.round(require_dist.G711_SCALE_CORRECTION_DB[codecUpper === "PCMA" ? "PCMA" : "PCMU"] * 100) / 100
13813
+ }
13814
+ });
13698
13815
  } else if (this.audioCodecApi) {
13699
13816
  const capCfg = this.buildAudioCodecSessionConfig(audioTrack);
13700
13817
  if (capCfg) {
@@ -17471,7 +17588,10 @@ var StreamBrokerManager = class StreamBrokerManager {
17471
17588
  await this.ensureBrokerForId(input.brokerId);
17472
17589
  const broker = this.brokers.get(input.brokerId);
17473
17590
  if (!broker) throw new Error(`stream-broker: no broker for "${input.brokerId}"`);
17474
- const result = broker.subscribeAudioChunks({ tag: input.tag });
17591
+ const result = broker.subscribeAudioChunks({
17592
+ tag: input.tag,
17593
+ accept: input.accept
17594
+ });
17475
17595
  this.audioSubscriptionBroker.set(result.subscriptionId, input.brokerId);
17476
17596
  return result;
17477
17597
  }