@camstack/addon-pipeline 1.1.26 → 1.1.27
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/constants-CAqYVigN.js +6615 -0
- package/dist/constants-oYgW36Ty.mjs +3587 -0
- package/dist/decoder-ffmpeg/index.js +33 -272
- package/dist/decoder-ffmpeg/index.mjs +32 -271
- package/dist/decoder-nodeav/index.js +1213 -0
- package/dist/decoder-nodeav/index.mjs +1205 -0
- package/dist/dist-D3Ytt-_Y.js +41111 -0
- package/dist/dist-DluJuxV5.mjs +37976 -0
- package/dist/frame-ring-sink-B_NvPTJZ.mjs +316 -0
- package/dist/frame-ring-sink-BbzrqTWj.js +339 -0
- package/dist/stream-broker/index.js +20 -15
- package/dist/stream-broker/index.mjs +20 -15
- package/package.json +31 -3
- package/python/inference_pool.py +34 -38
- package/python/test_inference_pool_device_selection.py +9 -10
- package/python/yamnet_audio.py +1 -1
|
@@ -3,6 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_dist = require("../dist-DAIlCdAx.js");
|
|
6
|
+
const require_frame_ring_sink = require("../frame-ring-sink-BbzrqTWj.js");
|
|
6
7
|
const require_frame_dropper = require("../frame-dropper-7RTo_YyG.js");
|
|
7
8
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
8
9
|
let node_crypto = require("node:crypto");
|
|
@@ -300,270 +301,6 @@ function rankDecodeHwAccels(preferred, supportedMethods) {
|
|
|
300
301
|
return candidates.sort((a, b) => decodeHwAccelRankIndex(a) - decodeHwAccelRankIndex(b));
|
|
301
302
|
}
|
|
302
303
|
//#endregion
|
|
303
|
-
//#region src/decoder-ffmpeg/frame-ring-sink.ts
|
|
304
|
-
/**
|
|
305
|
-
* `DecoderFrameRingSink` — the decoder's shared-memory write side (Phase 5 / D9).
|
|
306
|
-
*
|
|
307
|
-
* When a decoder session is configured with `frameSink: 'shm'`, the decoder
|
|
308
|
-
* **owns** the shared-memory ring segment for that stream: it creates the
|
|
309
|
-
* segment on the first decoded frame (when the output geometry is known),
|
|
310
|
-
* writes every subsequent decoded frame into the ring via a `FrameRingWriter`,
|
|
311
|
-
* and closes + unlinks the segment when the session is destroyed.
|
|
312
|
-
*
|
|
313
|
-
* What leaves the decoder is no longer the pixel `Buffer` — it is a tiny,
|
|
314
|
-
* serialisable `FrameHandle` (`FrameRingWriter.writeFrame`'s return value).
|
|
315
|
-
* Same-host consumers (motion, detection, the WebRTC encoder) open the same
|
|
316
|
-
* segment with a `FrameRingReader` and read the pixels zero-copy.
|
|
317
|
-
*
|
|
318
|
-
* ## Lazy segment creation
|
|
319
|
-
*
|
|
320
|
-
* The segment cannot be sized until the first frame: `slotByteLength` is
|
|
321
|
-
* `width × height × bytesPerPixel`, and the output dimensions are only known
|
|
322
|
-
* once the scaler has produced its first `dstFrame`. So `writeFrame` is a
|
|
323
|
-
* no-op-until-armed: the first call sizes + creates the segment, every later
|
|
324
|
-
* call writes into it.
|
|
325
|
-
*
|
|
326
|
-
* ## Resolution-change decision
|
|
327
|
-
*
|
|
328
|
-
* A live camera stream can change resolution mid-stream (the decoder's scaler
|
|
329
|
-
* is rebuilt on a config toggle, or the source renegotiates). The slot is
|
|
330
|
-
* sized for the **first** frame's geometry. A later frame that no longer fits
|
|
331
|
-
* the slot triggers a **segment re-create**: the old segment is closed +
|
|
332
|
-
* unlinked and a fresh, larger segment is created under a new generation-tagged
|
|
333
|
-
* name. This is simpler and leak-free versus over-allocating slots for a
|
|
334
|
-
* worst-case 4K frame on every stream; resolution changes on a live camera are
|
|
335
|
-
* rare, and a brief gap while consumers re-open the segment is acceptable
|
|
336
|
-
* (latest-wins — a missed frame is correct behaviour).
|
|
337
|
-
*/
|
|
338
|
-
/**
|
|
339
|
-
* Per-ring shared-memory budget (MB) — the slot count is derived per-resolution
|
|
340
|
-
* from this budget via {@link deriveSlotCount}, so a 360p stream gets many
|
|
341
|
-
* slots and a 4K stream a few, both inside the same memory footprint.
|
|
342
|
-
*
|
|
343
|
-
* Read ONCE at module load from `CAMSTACK_SHM_RING_BUDGET_MB`; a non-finite or
|
|
344
|
-
* non-positive value falls back to the 16 MB default.
|
|
345
|
-
*
|
|
346
|
-
* The default is deliberately small (16 MB) so many concurrent per-camera rings
|
|
347
|
-
* fit inside a bounded `/dev/shm`. The decoder output is capped at 640px wide, so
|
|
348
|
-
* a slot is ≤ ~920 KB and 16 MB still yields ~17–70 latest-wins slots. A ring
|
|
349
|
-
* segment larger than the container's `/dev/shm` tmpfs backing (Docker default is
|
|
350
|
-
* only 64 MB) faults an **uncatchable SIGBUS** on write past `st_size` — the old
|
|
351
|
-
* 128 MB default overflowed a 64 MB `/dev/shm` and crashed the decoder on 8MP
|
|
352
|
-
* streams. Pair this with a `--shm-size` that scales with concurrent-decode load.
|
|
353
|
-
*/
|
|
354
|
-
var RING_BUDGET_MB = (() => {
|
|
355
|
-
const raw = Number(process.env["CAMSTACK_SHM_RING_BUDGET_MB"]);
|
|
356
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 16;
|
|
357
|
-
})();
|
|
358
|
-
/** {@link RING_BUDGET_MB} in bytes — the budget passed to `deriveSlotCount`. */
|
|
359
|
-
var RING_BUDGET_BYTES = RING_BUDGET_MB * 1024 * 1024;
|
|
360
|
-
/** A unique, stable shared-memory segment name for a decoder stream.
|
|
361
|
-
*
|
|
362
|
-
* macOS POSIX shm names are capped at ~31 characters (`PSHMNAMLEN`). A
|
|
363
|
-
* `camstack.frames.<deviceId>.<streamId>` scheme overflows that for realistic
|
|
364
|
-
* ids, so the sink uses a short, collision-resistant scheme instead:
|
|
365
|
-
* `csf.<base36 hash>.<gen>`. The hash folds the device id, the session tag
|
|
366
|
-
* and a per-process random salt; the generation suffix makes a re-created
|
|
367
|
-
* segment (resolution change) a distinct name so a stale consumer mapping is
|
|
368
|
-
* never silently reused.
|
|
369
|
-
*/
|
|
370
|
-
function makeSegmentName(seed, generation) {
|
|
371
|
-
let hash = 5381;
|
|
372
|
-
for (let i = 0; i < seed.length; i += 1) hash = (hash << 5) + hash + seed.charCodeAt(i) | 0;
|
|
373
|
-
return `csf.${(hash >>> 0).toString(36)}.${generation}`;
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* The decoder-side owner of one stream's shared-memory frame ring.
|
|
377
|
-
*
|
|
378
|
-
* Not constructed until a session actually uses the shm sink; the segment
|
|
379
|
-
* itself is created lazily on the first `writeFrame`.
|
|
380
|
-
*/
|
|
381
|
-
var DecoderFrameRingSink = class {
|
|
382
|
-
seed;
|
|
383
|
-
logger;
|
|
384
|
-
nodeId;
|
|
385
|
-
segment = null;
|
|
386
|
-
writer = null;
|
|
387
|
-
segmentName = null;
|
|
388
|
-
slotByteLength = 0;
|
|
389
|
-
generation = 0;
|
|
390
|
-
destroyed = false;
|
|
391
|
-
/** Frames committed into the ring across this sink's lifetime (all generations). */
|
|
392
|
-
framesWritten = 0;
|
|
393
|
-
constructor(options) {
|
|
394
|
-
const salt = Math.random().toString(36).slice(2, 8);
|
|
395
|
-
this.seed = `${options.seed}.${salt}`;
|
|
396
|
-
this.logger = options.logger;
|
|
397
|
-
this.nodeId = options.nodeId;
|
|
398
|
-
}
|
|
399
|
-
/** Whether a segment has been created (i.e. at least one frame written). */
|
|
400
|
-
get isArmed() {
|
|
401
|
-
return this.writer !== null;
|
|
402
|
-
}
|
|
403
|
-
/** The current segment name, or `null` before the first frame. */
|
|
404
|
-
get currentSegmentName() {
|
|
405
|
-
return this.segmentName;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Write one decoded frame into the ring and return its `FrameHandle`.
|
|
409
|
-
*
|
|
410
|
-
* On the first call (or after a geometry change that overflows the current
|
|
411
|
-
* slot) the segment is created / re-created sized for this frame. Returns
|
|
412
|
-
* `null` only when the sink has been destroyed.
|
|
413
|
-
*
|
|
414
|
-
* This is the copy-in convenience form (it copies `pixels` into the slot).
|
|
415
|
-
* The decoder's hot path uses the zero-copy {@link beginFrame} /
|
|
416
|
-
* {@link commitFrame} scatter-write pair instead — the scaler produces its
|
|
417
|
-
* packed output directly into the slot, eliminating the write-side memcpy.
|
|
418
|
-
*/
|
|
419
|
-
writeFrame(pixels, meta) {
|
|
420
|
-
if (this.destroyed) return null;
|
|
421
|
-
if (this.writer === null || (0, _camstack_shm_ring.computeSlotByteLength)(meta.width, meta.height, meta.format) > this.slotByteLength) this.recreateSegment((0, _camstack_shm_ring.computeSlotByteLength)(meta.width, meta.height, meta.format));
|
|
422
|
-
const writer = this.writer;
|
|
423
|
-
if (writer === null) return null;
|
|
424
|
-
const handle = writer.writeFrame(pixels, meta);
|
|
425
|
-
this.framesWritten += 1;
|
|
426
|
-
return handle;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Reserve a ring slot for a frame of the given geometry — the **zero-copy**
|
|
430
|
-
* scatter-write entry point (Phase 5 / D9 Task 7c).
|
|
431
|
-
*
|
|
432
|
-
* The segment is created / re-created here if this is the first frame or the
|
|
433
|
-
* geometry overflows the current slot capacity, so the slot is correctly
|
|
434
|
-
* sized before the caller fills it. The returned `buffer` is a writable view
|
|
435
|
-
* **directly over the mapped segment** — the node-av scaler scatters its
|
|
436
|
-
* packed output straight into it, with no intermediate copy. The caller MUST
|
|
437
|
-
* call {@link commitFrame} with the returned `slot` once the slot is filled.
|
|
438
|
-
*
|
|
439
|
-
* Returns `null` when the sink is destroyed or the segment cannot be created.
|
|
440
|
-
*/
|
|
441
|
-
beginFrame(width, height, format) {
|
|
442
|
-
if (this.destroyed) return null;
|
|
443
|
-
const requiredSlotBytes = (0, _camstack_shm_ring.computeSlotByteLength)(width, height, format);
|
|
444
|
-
if (this.writer === null || requiredSlotBytes > this.slotByteLength) this.recreateSegment(requiredSlotBytes);
|
|
445
|
-
const writer = this.writer;
|
|
446
|
-
if (writer === null) return null;
|
|
447
|
-
const { slot, buffer } = writer.beginFrame();
|
|
448
|
-
return {
|
|
449
|
-
slot,
|
|
450
|
-
buffer
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Publish the frame whose slot was reserved by {@link beginFrame} and filled
|
|
455
|
-
* in place by the caller. `slot` MUST be the value from the matching
|
|
456
|
-
* `beginFrame`. Returns the published `FrameHandle`, or `null` if the sink
|
|
457
|
-
* was destroyed (or the segment lost) between begin and commit.
|
|
458
|
-
*/
|
|
459
|
-
commitFrame(slot, meta) {
|
|
460
|
-
if (this.destroyed) return null;
|
|
461
|
-
const writer = this.writer;
|
|
462
|
-
if (writer === null) return null;
|
|
463
|
-
const handle = writer.commitFrame(slot, meta);
|
|
464
|
-
this.framesWritten += 1;
|
|
465
|
-
return handle;
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Current shm ring usage — `null` until the first frame arms the segment.
|
|
469
|
-
* Surfaced through `decoder.getShmStats` so a downstream consumer can
|
|
470
|
-
* observe ring pressure (slot depth, byte budget, frames written).
|
|
471
|
-
*/
|
|
472
|
-
getShmStats() {
|
|
473
|
-
if (this.writer === null) return null;
|
|
474
|
-
return {
|
|
475
|
-
slotCount: this.writer.slotCount,
|
|
476
|
-
slotByteLength: this.slotByteLength,
|
|
477
|
-
segmentBytes: (0, _camstack_shm_ring.computeSegmentSize)(this.writer.slotCount, this.slotByteLength),
|
|
478
|
-
framesWritten: this.framesWritten
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Abandon a slot reserved by {@link beginFrame} **without publishing it** —
|
|
483
|
-
* the degenerate-path counterpart of {@link commitFrame}.
|
|
484
|
-
*
|
|
485
|
-
* A caller that reserved a slot but then could not produce valid pixels (no
|
|
486
|
-
* decoded source planes, or the scaler threw) MUST call this instead of
|
|
487
|
-
* `commitFrame`: it closes the open seqlock without advancing `writeIndex`,
|
|
488
|
-
* so no reader ever sees the slot's uninitialised bytes as a real frame, and
|
|
489
|
-
* no `FrameHandle` is handed downstream. `slot` MUST be the value from the
|
|
490
|
-
* matching `beginFrame`. A no-op if the sink was destroyed (or the segment
|
|
491
|
-
* lost) between begin and abort.
|
|
492
|
-
*/
|
|
493
|
-
abortFrame(slot) {
|
|
494
|
-
if (this.destroyed) return;
|
|
495
|
-
const writer = this.writer;
|
|
496
|
-
if (writer === null) return;
|
|
497
|
-
writer.abortFrame(slot);
|
|
498
|
-
}
|
|
499
|
-
/** Close + unlink the segment. Idempotent. */
|
|
500
|
-
destroy() {
|
|
501
|
-
if (this.destroyed) return;
|
|
502
|
-
this.destroyed = true;
|
|
503
|
-
this.releaseSegment();
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Create a fresh segment sized for at least `slotByteLength` bytes per slot,
|
|
507
|
-
* replacing any prior one. A re-create bumps the generation so the new
|
|
508
|
-
* segment has a distinct name — a consumer holding the old mapping is never
|
|
509
|
-
* silently handed a resized segment.
|
|
510
|
-
*/
|
|
511
|
-
recreateSegment(slotByteLength) {
|
|
512
|
-
this.releaseSegment();
|
|
513
|
-
this.generation += 1;
|
|
514
|
-
const name = makeSegmentName(this.seed, this.generation);
|
|
515
|
-
const slotCount = (0, _camstack_shm_ring.deriveSlotCount)(RING_BUDGET_BYTES, slotByteLength);
|
|
516
|
-
if (slotCount === _camstack_shm_ring.MIN_RING_SLOTS && _camstack_shm_ring.MIN_RING_SLOTS * slotByteLength > RING_BUDGET_BYTES) this.logger.warn("decoder shm ring: budget too small for resolution — using MIN slots", { meta: {
|
|
517
|
-
slotByteLength,
|
|
518
|
-
budgetMb: RING_BUDGET_MB
|
|
519
|
-
} });
|
|
520
|
-
const totalBytes = (0, _camstack_shm_ring.computeSegmentSize)(slotCount, slotByteLength);
|
|
521
|
-
try {
|
|
522
|
-
const segment = (0, _camstack_shm_ring.createSegment)(name, totalBytes);
|
|
523
|
-
this.segment = segment;
|
|
524
|
-
this.segmentName = name;
|
|
525
|
-
this.slotByteLength = slotByteLength;
|
|
526
|
-
this.writer = new _camstack_shm_ring.FrameRingWriter(segment.buffer, name, slotCount, slotByteLength, this.nodeId);
|
|
527
|
-
this.logger.info("decoder shm ring: segment created", { meta: {
|
|
528
|
-
segment: name,
|
|
529
|
-
slotCount,
|
|
530
|
-
slotByteLength,
|
|
531
|
-
totalBytes,
|
|
532
|
-
generation: this.generation
|
|
533
|
-
} });
|
|
534
|
-
} catch (err) {
|
|
535
|
-
this.segment = null;
|
|
536
|
-
this.writer = null;
|
|
537
|
-
this.segmentName = null;
|
|
538
|
-
this.slotByteLength = 0;
|
|
539
|
-
this.logger.error("decoder shm ring: segment create failed", { meta: {
|
|
540
|
-
segment: name,
|
|
541
|
-
slotByteLength,
|
|
542
|
-
error: err instanceof Error ? err.message : String(err)
|
|
543
|
-
} });
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
/** Unmap + unlink the current segment, if any. */
|
|
547
|
-
releaseSegment() {
|
|
548
|
-
const segment = this.segment;
|
|
549
|
-
if (segment === null) return;
|
|
550
|
-
this.segment = null;
|
|
551
|
-
this.writer = null;
|
|
552
|
-
const name = this.segmentName;
|
|
553
|
-
this.segmentName = null;
|
|
554
|
-
try {
|
|
555
|
-
segment.close();
|
|
556
|
-
segment.unlink();
|
|
557
|
-
this.logger.info("decoder shm ring: segment released", { meta: { segment: name } });
|
|
558
|
-
} catch (err) {
|
|
559
|
-
this.logger.warn("decoder shm ring: segment release failed", { meta: {
|
|
560
|
-
segment: name,
|
|
561
|
-
error: err instanceof Error ? err.message : String(err)
|
|
562
|
-
} });
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
//#endregion
|
|
567
304
|
//#region src/decoder-ffmpeg/frame-stream-splitter.ts
|
|
568
305
|
var FrameStreamSplitter = class {
|
|
569
306
|
frameSize;
|
|
@@ -699,6 +436,14 @@ var FfmpegDecoderSession = class {
|
|
|
699
436
|
process = null;
|
|
700
437
|
spawnedAtMs = 0;
|
|
701
438
|
/**
|
|
439
|
+
* Whether a spawn has ever been attempted. Drives the LAZY first spawn
|
|
440
|
+
* (first `pushPacket` / `openStream`) and preserves the recovery semantics
|
|
441
|
+
* afterwards: a push-mode child that later dies is NOT respawned by the
|
|
442
|
+
* packet feed (the consumer / frame-plane self-heal recreates the session),
|
|
443
|
+
* and `updateConfig` only restarts a session that has already spawned.
|
|
444
|
+
*/
|
|
445
|
+
spawnAttempted = false;
|
|
446
|
+
/**
|
|
702
447
|
* PULL-MODE input URL (P2b). `null` = push mode (packets via stdin — the
|
|
703
448
|
* default). Set by {@link openStream}; every (re)spawn since reads this URL
|
|
704
449
|
* directly instead of `pipe:0`. A session is push-mode or pull-mode for its
|
|
@@ -754,7 +499,6 @@ var FfmpegDecoderSession = class {
|
|
|
754
499
|
this.channels = channelsForPixel(this.pixel);
|
|
755
500
|
this.frameFormat = this.pixel === "gray" ? "gray" : "rgb";
|
|
756
501
|
this.frameDropper = new require_frame_dropper.FrameDropper(config.maxFps);
|
|
757
|
-
this.spawnFfmpeg();
|
|
758
502
|
}
|
|
759
503
|
/** Exposed so the owning addon can surface ring stats via `getShmStats`. */
|
|
760
504
|
get frameRingSinkOrNull() {
|
|
@@ -787,7 +531,7 @@ var FfmpegDecoderSession = class {
|
|
|
787
531
|
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
788
532
|
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
789
533
|
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
790
|
-
this.frameRingSink = new DecoderFrameRingSink({
|
|
534
|
+
this.frameRingSink = new require_frame_ring_sink.DecoderFrameRingSink({
|
|
791
535
|
seed,
|
|
792
536
|
logger: this.logger,
|
|
793
537
|
nodeId: this.nodeId
|
|
@@ -797,6 +541,7 @@ var FfmpegDecoderSession = class {
|
|
|
797
541
|
}
|
|
798
542
|
spawnFfmpeg() {
|
|
799
543
|
if (this.destroyed) return;
|
|
544
|
+
this.spawnAttempted = true;
|
|
800
545
|
this.clearPullRedial();
|
|
801
546
|
this.killFfmpeg();
|
|
802
547
|
this.abortOpenSlot();
|
|
@@ -827,12 +572,19 @@ var FfmpegDecoderSession = class {
|
|
|
827
572
|
this.process = child;
|
|
828
573
|
this.spawnedAtMs = Date.now();
|
|
829
574
|
child.stdin?.on("error", () => {});
|
|
830
|
-
child.stdout?.on("data", (chunk) =>
|
|
831
|
-
|
|
575
|
+
child.stdout?.on("data", (chunk) => {
|
|
576
|
+
if (child === this.process) this.handleStdout(chunk);
|
|
577
|
+
});
|
|
578
|
+
child.stderr?.on("data", (data) => {
|
|
579
|
+
if (child === this.process) this.handleStderr(data);
|
|
580
|
+
});
|
|
832
581
|
child.on("error", (err) => {
|
|
582
|
+
if (child !== this.process) return;
|
|
833
583
|
this.logger.error("ffmpeg decoder: process error", { meta: { error: err.message } });
|
|
834
584
|
});
|
|
835
|
-
child.on("exit", (code, signal) =>
|
|
585
|
+
child.on("exit", (code, signal) => {
|
|
586
|
+
if (child === this.process) this.handleExit(code, signal);
|
|
587
|
+
});
|
|
836
588
|
this.logger.info("ffmpeg decoder: spawned", { meta: {
|
|
837
589
|
hwaccel: this.hwaccel,
|
|
838
590
|
gpuScale: this.activeGpuScaleFilter ?? "cpu",
|
|
@@ -845,6 +597,9 @@ var FfmpegDecoderSession = class {
|
|
|
845
597
|
const child = this.process;
|
|
846
598
|
if (!child) return;
|
|
847
599
|
this.process = null;
|
|
600
|
+
child.stdout?.removeAllListeners("data");
|
|
601
|
+
child.stderr?.removeAllListeners("data");
|
|
602
|
+
child.removeAllListeners("exit");
|
|
848
603
|
try {
|
|
849
604
|
child.stdin?.end();
|
|
850
605
|
} catch {}
|
|
@@ -1028,6 +783,7 @@ var FfmpegDecoderSession = class {
|
|
|
1028
783
|
pushPacket(packet) {
|
|
1029
784
|
if (this.destroyed) return;
|
|
1030
785
|
if (this.pullUrl !== null) return;
|
|
786
|
+
if (this.process === null && !this.spawnAttempted) this.spawnFfmpeg();
|
|
1031
787
|
const stdin = this.process?.stdin;
|
|
1032
788
|
if (!stdin) return;
|
|
1033
789
|
this.inputPackets++;
|
|
@@ -1055,7 +811,7 @@ var FfmpegDecoderSession = class {
|
|
|
1055
811
|
...update
|
|
1056
812
|
};
|
|
1057
813
|
if (update.maxFps !== void 0) this.frameDropper.setMaxFps(update.maxFps);
|
|
1058
|
-
if (needsRestart) this.spawnFfmpeg();
|
|
814
|
+
if (needsRestart && this.spawnAttempted) this.spawnFfmpeg();
|
|
1059
815
|
}
|
|
1060
816
|
async destroy() {
|
|
1061
817
|
if (this.destroyed) return;
|
|
@@ -1201,7 +957,12 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1201
957
|
}] });
|
|
1202
958
|
}
|
|
1203
959
|
async onInitialize() {
|
|
1204
|
-
this.ctx.
|
|
960
|
+
const backend = await require_frame_ring_sink.resolveDecoderBackend(this.ctx.settings, this.resolveLocalNodeId(), this.ctx.logger);
|
|
961
|
+
if (backend !== "ffmpeg") {
|
|
962
|
+
this.ctx.logger.info("ffmpeg decoder: this node selects a different decoder backend — standing down (no decoder provider registered)", { meta: { selectedBackend: backend } });
|
|
963
|
+
return [];
|
|
964
|
+
}
|
|
965
|
+
this.ctx.logger.info("ffmpeg decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
1205
966
|
this.frameReaders = new _camstack_shm_ring.FrameRingReaderCache(this.ctx.logger);
|
|
1206
967
|
this.ffmpegPath = await this.resolveFfmpegBinaryPath();
|
|
1207
968
|
this.probedGpuScaleFilters = await probeGpuScaleFilters(this.ffmpegPath, this.ctx.logger);
|
|
@@ -1466,7 +1227,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1466
1227
|
return {
|
|
1467
1228
|
sessionId: input.sessionId,
|
|
1468
1229
|
...stats,
|
|
1469
|
-
budgetMb: RING_BUDGET_MB,
|
|
1230
|
+
budgetMb: require_frame_ring_sink.RING_BUDGET_MB,
|
|
1470
1231
|
getFrameHits: this.getFrameHits,
|
|
1471
1232
|
getFrameMisses: this.getFrameMisses
|
|
1472
1233
|
};
|