@camstack/addon-pipeline 1.1.24 → 1.1.26
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/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/audio-codec-ffmpeg/index.js +1 -1
- package/dist/audio-codec-ffmpeg/index.mjs +1 -1
- package/dist/decoder-ffmpeg/index.js +84 -12
- package/dist/decoder-ffmpeg/index.mjs +84 -12
- package/dist/detection-pipeline/index.js +149 -27
- package/dist/detection-pipeline/index.mjs +149 -27
- package/dist/{dist-pd_-3C0T.mjs → dist-CgEP_0OL.mjs} +794 -16
- package/dist/{dist-CE3a05qT.js → dist-DAIlCdAx.js} +799 -15
- package/dist/frame-handle-plane-Dq20KtKL.mjs +636 -0
- package/dist/frame-handle-plane-DtTRX_0n.js +647 -0
- package/dist/hub-hostname-DAJXlOgV.js +54 -0
- package/dist/hub-hostname-cCknRYKj.mjs +49 -0
- package/dist/{model-download-service-C-IHWnXx-BnQ_awK4.js → model-download-service-C-IHWnXx-DxM2DSns.js} +1 -2
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +341 -17
- package/dist/pipeline-runner/index.mjs +341 -17
- package/dist/recorder/index.js +267 -65
- package/dist/recorder/index.mjs +267 -65
- package/dist/stream-broker/_stub.js +39 -39
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BVFAEkIB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5tQlh9h4.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CkOPfV8r.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BJK0-svt.mjs +26 -0
- package/dist/stream-broker/{hostInit-DHej3Ep9.mjs → hostInit-DyLqyJaS.mjs} +2 -2
- package/dist/stream-broker/index.js +7 -639
- package/dist/stream-broker/index.mjs +5 -637
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BqXU75it.js → MaskShapeCanvas-DI4BY7W2-BDLNwJ_F.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-Ct2fpgCd.js → MotionZonesSettings-NcxxQN8r-CoLjNiUN.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-a9eFVPPl.js → PrivacyMaskSettings-APgPLF7p-DJE3OU-q.js} +1 -1
- package/embed-dist/assets/index-C-pL8ETk.js +81 -0
- package/embed-dist/assets/index-DrJ0ee3f.css +2 -0
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/inference_pool.py +522 -27
- package/python/test_inference_pool_backpressure.py +121 -0
- package/python/test_inference_pool_coreml_cache.py +416 -0
- package/python/test_inference_pool_device_selection.py +256 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BFeBlYRQ.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BC1Jt6G8.mjs +0 -26
- package/embed-dist/assets/index-C7SSikPl.css +0 -2
- package/embed-dist/assets/index-DhdVxWXL.js +0 -80
- package/dist/{model-download-service-C-IHWnXx-3Mmeob3l.mjs → model-download-service-C-IHWnXx-BPy6aoAx.mjs} +1 -1
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-
|
|
6
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-DxM2DSns.js");
|
|
6
|
+
const require_dist = require("../dist-DAIlCdAx.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { B as
|
|
3
|
-
import { r as downloadFile } from "../model-download-service-C-IHWnXx-
|
|
2
|
+
import { B as errMsg, D as mapAudioLabelToMacro, V as BaseAddon, Y as hydrateSchema, _ as audioAnalyzerCapability, g as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, u as HF_BASE_URL } from "../dist-CgEP_0OL.mjs";
|
|
3
|
+
import { r as downloadFile } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
//#region src/audio-analyzer/audio-pipeline.ts
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DAIlCdAx.js");
|
|
6
6
|
let node_crypto = require("node:crypto");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
//#region src/audio-codec-ffmpeg/ffmpeg-audio-process.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as
|
|
1
|
+
import { B as errMsg, V as BaseAddon, v as audioCodecCapability } from "../dist-CgEP_0OL.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region src/audio-codec-ffmpeg/ffmpeg-audio-process.ts
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DAIlCdAx.js");
|
|
6
6
|
const require_frame_dropper = require("../frame-dropper-7RTo_YyG.js");
|
|
7
7
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
@@ -145,8 +145,10 @@ function buildVideoFilter(hwaccel, pixel, outWidth, outHeight, maxW, gpuScaleFil
|
|
|
145
145
|
* stream line from stderr to learn the exact output W×H (the broker does not
|
|
146
146
|
* pass source geometry for the shm path). `-nostats` suppresses the periodic
|
|
147
147
|
* `frame= …` counter while keeping the one-shot Output block.
|
|
148
|
-
* - The input is `pipe:0` (Annex-B packets written to stdin)
|
|
149
|
-
*
|
|
148
|
+
* - The input is `pipe:0` (Annex-B packets written to stdin) in push mode, or
|
|
149
|
+
* `options.inputUrl` (the owner's restream, dialed by ffmpeg itself —
|
|
150
|
+
* `-rtsp_transport tcp` for rtsp URLs) in pull mode; the output is raw video
|
|
151
|
+
* on `pipe:1` either way.
|
|
150
152
|
*/
|
|
151
153
|
function buildFfmpegDecodeArgs(config, options) {
|
|
152
154
|
const inputFormat = codecToInputFormat(config.codec);
|
|
@@ -162,6 +164,16 @@ function buildFfmpegDecodeArgs(config, options) {
|
|
|
162
164
|
if (needsHwDownload(hwaccel)) hwaccelArgs.push("-hwaccel_device", options.renderDevice ?? "/dev/dri/renderD128");
|
|
163
165
|
if (keepFramesOnGpu(hwaccel, gpuScaleFilter)) hwaccelArgs.push("-hwaccel_output_format", hwaccel);
|
|
164
166
|
}
|
|
167
|
+
const inputArgs = options.inputUrl !== void 0 ? [
|
|
168
|
+
...options.inputUrl.startsWith("rtsp") ? ["-rtsp_transport", "tcp"] : [],
|
|
169
|
+
"-i",
|
|
170
|
+
options.inputUrl
|
|
171
|
+
] : [
|
|
172
|
+
"-f",
|
|
173
|
+
inputFormat,
|
|
174
|
+
"-i",
|
|
175
|
+
"pipe:0"
|
|
176
|
+
];
|
|
165
177
|
return [
|
|
166
178
|
...require_frame_dropper.logBannerArgs("info"),
|
|
167
179
|
"-nostats",
|
|
@@ -174,10 +186,7 @@ function buildFfmpegDecodeArgs(config, options) {
|
|
|
174
186
|
"-analyzeduration",
|
|
175
187
|
"0",
|
|
176
188
|
...hwaccelArgs,
|
|
177
|
-
|
|
178
|
-
inputFormat,
|
|
179
|
-
"-i",
|
|
180
|
-
"pipe:0",
|
|
189
|
+
...inputArgs,
|
|
181
190
|
"-vf",
|
|
182
191
|
vf,
|
|
183
192
|
"-f",
|
|
@@ -650,6 +659,16 @@ var noopLogger = {
|
|
|
650
659
|
* hwaccel init failure (missing render node, unsupported codec on GPU, the
|
|
651
660
|
* Intel `qsv` `code=171`) surfaces this early. */
|
|
652
661
|
var HWACCEL_FALLBACK_WINDOW_MS = 4e3;
|
|
662
|
+
/**
|
|
663
|
+
* Delay before a PULL-MODE session re-dials its input URL after the child
|
|
664
|
+
* exits (RTSP EOF, restream teardown, network blip). Push mode never
|
|
665
|
+
* self-respawns on a plain exit — the broker's packet feed drives recovery —
|
|
666
|
+
* but a pull-mode child owns its own input, so the session must re-dial or a
|
|
667
|
+
* single EOF would silence it forever. The delay turns a hard-down source
|
|
668
|
+
* into a slow probe instead of a spawn storm; the consumer tearing the
|
|
669
|
+
* session down (`destroy`) cancels the pending re-dial.
|
|
670
|
+
*/
|
|
671
|
+
var PULL_REDIAL_DELAY_MS = 3e3;
|
|
653
672
|
var FfmpegDecoderSession = class {
|
|
654
673
|
config;
|
|
655
674
|
logger;
|
|
@@ -680,6 +699,15 @@ var FfmpegDecoderSession = class {
|
|
|
680
699
|
process = null;
|
|
681
700
|
spawnedAtMs = 0;
|
|
682
701
|
/**
|
|
702
|
+
* PULL-MODE input URL (P2b). `null` = push mode (packets via stdin — the
|
|
703
|
+
* default). Set by {@link openStream}; every (re)spawn since reads this URL
|
|
704
|
+
* directly instead of `pipe:0`. A session is push-mode or pull-mode for its
|
|
705
|
+
* whole life — `openStream` on a packet-fed session throws.
|
|
706
|
+
*/
|
|
707
|
+
pullUrl = null;
|
|
708
|
+
/** Pending pull-mode re-dial timer (armed on a pull child exit). */
|
|
709
|
+
pullRedialTimer = null;
|
|
710
|
+
/**
|
|
683
711
|
* Splits ffmpeg's raw stdout byte stream into frames, filling each DIRECTLY
|
|
684
712
|
* into its shm slot (zero-copy) — created once the output geometry is known.
|
|
685
713
|
*/
|
|
@@ -733,7 +761,25 @@ var FfmpegDecoderSession = class {
|
|
|
733
761
|
return this.frameRingSink;
|
|
734
762
|
}
|
|
735
763
|
get isPullMode() {
|
|
736
|
-
return
|
|
764
|
+
return this.pullUrl !== null;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Switch the session to PULL MODE: (re)spawn ffmpeg reading `url` directly
|
|
768
|
+
* (`-rtsp_transport tcp -i <url>` for rtsp) instead of stdin. The whole
|
|
769
|
+
* output path (filtergraph, rawvideo pipe, shm ring sink) is unchanged.
|
|
770
|
+
*
|
|
771
|
+
* - Calling it again re-dials with the FRESH url (a hub restream bounce
|
|
772
|
+
* mints a new token URL — never cache the old one).
|
|
773
|
+
* - Calling it on a session already fed via `pushPacket` throws: a session
|
|
774
|
+
* is push-mode or pull-mode, never both (mixing inputs would corrupt the
|
|
775
|
+
* decode).
|
|
776
|
+
*/
|
|
777
|
+
async openStream(url) {
|
|
778
|
+
if (this.destroyed) return;
|
|
779
|
+
if (this.pullUrl === null && this.inputPackets > 0) throw new Error("ffmpeg decoder: openStream on a push-fed session — a session is push-mode or pull-mode, never both");
|
|
780
|
+
this.pullUrl = url;
|
|
781
|
+
this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
|
|
782
|
+
this.spawnFfmpeg();
|
|
737
783
|
}
|
|
738
784
|
ensureFrameRingSink() {
|
|
739
785
|
if (this.frameRingSink === null) {
|
|
@@ -751,6 +797,7 @@ var FfmpegDecoderSession = class {
|
|
|
751
797
|
}
|
|
752
798
|
spawnFfmpeg() {
|
|
753
799
|
if (this.destroyed) return;
|
|
800
|
+
this.clearPullRedial();
|
|
754
801
|
this.killFfmpeg();
|
|
755
802
|
this.abortOpenSlot();
|
|
756
803
|
this.splitter = null;
|
|
@@ -767,7 +814,8 @@ var FfmpegDecoderSession = class {
|
|
|
767
814
|
}, {
|
|
768
815
|
hwaccel: this.hwaccel,
|
|
769
816
|
renderDevice: this.renderDevice,
|
|
770
|
-
gpuScaleFilter: this.activeGpuScaleFilter
|
|
817
|
+
gpuScaleFilter: this.activeGpuScaleFilter,
|
|
818
|
+
...this.pullUrl !== null ? { inputUrl: this.pullUrl } : {}
|
|
771
819
|
});
|
|
772
820
|
let child;
|
|
773
821
|
try {
|
|
@@ -854,6 +902,27 @@ var FfmpegDecoderSession = class {
|
|
|
854
902
|
signal,
|
|
855
903
|
outputFrames: this.outputFrames
|
|
856
904
|
} });
|
|
905
|
+
if (this.pullUrl !== null) {
|
|
906
|
+
this.clearPullRedial();
|
|
907
|
+
const timer = setTimeout(() => {
|
|
908
|
+
this.pullRedialTimer = null;
|
|
909
|
+
this.spawnFfmpeg();
|
|
910
|
+
}, PULL_REDIAL_DELAY_MS);
|
|
911
|
+
timer.unref?.();
|
|
912
|
+
this.pullRedialTimer = timer;
|
|
913
|
+
this.logger.info("ffmpeg decoder: pull-mode child exited — re-dial scheduled", { meta: {
|
|
914
|
+
code,
|
|
915
|
+
signal,
|
|
916
|
+
redialInMs: PULL_REDIAL_DELAY_MS
|
|
917
|
+
} });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
/** Cancel a pending pull-mode re-dial, if armed. */
|
|
921
|
+
clearPullRedial() {
|
|
922
|
+
if (this.pullRedialTimer !== null) {
|
|
923
|
+
clearTimeout(this.pullRedialTimer);
|
|
924
|
+
this.pullRedialTimer = null;
|
|
925
|
+
}
|
|
857
926
|
}
|
|
858
927
|
handleStderr(data) {
|
|
859
928
|
const text = data.toString();
|
|
@@ -958,6 +1027,7 @@ var FfmpegDecoderSession = class {
|
|
|
958
1027
|
}
|
|
959
1028
|
pushPacket(packet) {
|
|
960
1029
|
if (this.destroyed) return;
|
|
1030
|
+
if (this.pullUrl !== null) return;
|
|
961
1031
|
const stdin = this.process?.stdin;
|
|
962
1032
|
if (!stdin) return;
|
|
963
1033
|
this.inputPackets++;
|
|
@@ -990,6 +1060,7 @@ var FfmpegDecoderSession = class {
|
|
|
990
1060
|
async destroy() {
|
|
991
1061
|
if (this.destroyed) return;
|
|
992
1062
|
this.destroyed = true;
|
|
1063
|
+
this.clearPullRedial();
|
|
993
1064
|
this.killFfmpeg();
|
|
994
1065
|
this.abortOpenSlot();
|
|
995
1066
|
this.splitter = null;
|
|
@@ -1235,7 +1306,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1235
1306
|
return {
|
|
1236
1307
|
id: "decoder-ffmpeg",
|
|
1237
1308
|
name: "Decoder (ffmpeg)",
|
|
1238
|
-
isPullMode:
|
|
1309
|
+
isPullMode: true,
|
|
1239
1310
|
priority: 20
|
|
1240
1311
|
};
|
|
1241
1312
|
}
|
|
@@ -1346,8 +1417,9 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1346
1417
|
});
|
|
1347
1418
|
}
|
|
1348
1419
|
async openStream(input) {
|
|
1349
|
-
|
|
1350
|
-
input.
|
|
1420
|
+
const session = this.sessions.get(input.sessionId);
|
|
1421
|
+
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1422
|
+
await session.openStream(input.url);
|
|
1351
1423
|
}
|
|
1352
1424
|
async pullFrames(input) {
|
|
1353
1425
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as
|
|
1
|
+
import { B as errMsg, V as BaseAddon, d as HWACCEL_OPTIONS, p as RingBuffer, s as DEFAULT_DECODER_HWACCEL_CONFIG, x as decoderCapability } from "../dist-CgEP_0OL.mjs";
|
|
2
2
|
import { r as logBannerArgs, t as FrameDropper } from "../frame-dropper-CwkBTPGV.mjs";
|
|
3
3
|
import { FrameRingReaderCache, FrameRingWriter, MIN_RING_SLOTS, computeSegmentSize, computeSlotByteLength, createSegment, deriveSlotCount } from "@camstack/shm-ring";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -141,8 +141,10 @@ function buildVideoFilter(hwaccel, pixel, outWidth, outHeight, maxW, gpuScaleFil
|
|
|
141
141
|
* stream line from stderr to learn the exact output W×H (the broker does not
|
|
142
142
|
* pass source geometry for the shm path). `-nostats` suppresses the periodic
|
|
143
143
|
* `frame= …` counter while keeping the one-shot Output block.
|
|
144
|
-
* - The input is `pipe:0` (Annex-B packets written to stdin)
|
|
145
|
-
*
|
|
144
|
+
* - The input is `pipe:0` (Annex-B packets written to stdin) in push mode, or
|
|
145
|
+
* `options.inputUrl` (the owner's restream, dialed by ffmpeg itself —
|
|
146
|
+
* `-rtsp_transport tcp` for rtsp URLs) in pull mode; the output is raw video
|
|
147
|
+
* on `pipe:1` either way.
|
|
146
148
|
*/
|
|
147
149
|
function buildFfmpegDecodeArgs(config, options) {
|
|
148
150
|
const inputFormat = codecToInputFormat(config.codec);
|
|
@@ -158,6 +160,16 @@ function buildFfmpegDecodeArgs(config, options) {
|
|
|
158
160
|
if (needsHwDownload(hwaccel)) hwaccelArgs.push("-hwaccel_device", options.renderDevice ?? "/dev/dri/renderD128");
|
|
159
161
|
if (keepFramesOnGpu(hwaccel, gpuScaleFilter)) hwaccelArgs.push("-hwaccel_output_format", hwaccel);
|
|
160
162
|
}
|
|
163
|
+
const inputArgs = options.inputUrl !== void 0 ? [
|
|
164
|
+
...options.inputUrl.startsWith("rtsp") ? ["-rtsp_transport", "tcp"] : [],
|
|
165
|
+
"-i",
|
|
166
|
+
options.inputUrl
|
|
167
|
+
] : [
|
|
168
|
+
"-f",
|
|
169
|
+
inputFormat,
|
|
170
|
+
"-i",
|
|
171
|
+
"pipe:0"
|
|
172
|
+
];
|
|
161
173
|
return [
|
|
162
174
|
...logBannerArgs("info"),
|
|
163
175
|
"-nostats",
|
|
@@ -170,10 +182,7 @@ function buildFfmpegDecodeArgs(config, options) {
|
|
|
170
182
|
"-analyzeduration",
|
|
171
183
|
"0",
|
|
172
184
|
...hwaccelArgs,
|
|
173
|
-
|
|
174
|
-
inputFormat,
|
|
175
|
-
"-i",
|
|
176
|
-
"pipe:0",
|
|
185
|
+
...inputArgs,
|
|
177
186
|
"-vf",
|
|
178
187
|
vf,
|
|
179
188
|
"-f",
|
|
@@ -646,6 +655,16 @@ var noopLogger = {
|
|
|
646
655
|
* hwaccel init failure (missing render node, unsupported codec on GPU, the
|
|
647
656
|
* Intel `qsv` `code=171`) surfaces this early. */
|
|
648
657
|
var HWACCEL_FALLBACK_WINDOW_MS = 4e3;
|
|
658
|
+
/**
|
|
659
|
+
* Delay before a PULL-MODE session re-dials its input URL after the child
|
|
660
|
+
* exits (RTSP EOF, restream teardown, network blip). Push mode never
|
|
661
|
+
* self-respawns on a plain exit — the broker's packet feed drives recovery —
|
|
662
|
+
* but a pull-mode child owns its own input, so the session must re-dial or a
|
|
663
|
+
* single EOF would silence it forever. The delay turns a hard-down source
|
|
664
|
+
* into a slow probe instead of a spawn storm; the consumer tearing the
|
|
665
|
+
* session down (`destroy`) cancels the pending re-dial.
|
|
666
|
+
*/
|
|
667
|
+
var PULL_REDIAL_DELAY_MS = 3e3;
|
|
649
668
|
var FfmpegDecoderSession = class {
|
|
650
669
|
config;
|
|
651
670
|
logger;
|
|
@@ -676,6 +695,15 @@ var FfmpegDecoderSession = class {
|
|
|
676
695
|
process = null;
|
|
677
696
|
spawnedAtMs = 0;
|
|
678
697
|
/**
|
|
698
|
+
* PULL-MODE input URL (P2b). `null` = push mode (packets via stdin — the
|
|
699
|
+
* default). Set by {@link openStream}; every (re)spawn since reads this URL
|
|
700
|
+
* directly instead of `pipe:0`. A session is push-mode or pull-mode for its
|
|
701
|
+
* whole life — `openStream` on a packet-fed session throws.
|
|
702
|
+
*/
|
|
703
|
+
pullUrl = null;
|
|
704
|
+
/** Pending pull-mode re-dial timer (armed on a pull child exit). */
|
|
705
|
+
pullRedialTimer = null;
|
|
706
|
+
/**
|
|
679
707
|
* Splits ffmpeg's raw stdout byte stream into frames, filling each DIRECTLY
|
|
680
708
|
* into its shm slot (zero-copy) — created once the output geometry is known.
|
|
681
709
|
*/
|
|
@@ -729,7 +757,25 @@ var FfmpegDecoderSession = class {
|
|
|
729
757
|
return this.frameRingSink;
|
|
730
758
|
}
|
|
731
759
|
get isPullMode() {
|
|
732
|
-
return
|
|
760
|
+
return this.pullUrl !== null;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Switch the session to PULL MODE: (re)spawn ffmpeg reading `url` directly
|
|
764
|
+
* (`-rtsp_transport tcp -i <url>` for rtsp) instead of stdin. The whole
|
|
765
|
+
* output path (filtergraph, rawvideo pipe, shm ring sink) is unchanged.
|
|
766
|
+
*
|
|
767
|
+
* - Calling it again re-dials with the FRESH url (a hub restream bounce
|
|
768
|
+
* mints a new token URL — never cache the old one).
|
|
769
|
+
* - Calling it on a session already fed via `pushPacket` throws: a session
|
|
770
|
+
* is push-mode or pull-mode, never both (mixing inputs would corrupt the
|
|
771
|
+
* decode).
|
|
772
|
+
*/
|
|
773
|
+
async openStream(url) {
|
|
774
|
+
if (this.destroyed) return;
|
|
775
|
+
if (this.pullUrl === null && this.inputPackets > 0) throw new Error("ffmpeg decoder: openStream on a push-fed session — a session is push-mode or pull-mode, never both");
|
|
776
|
+
this.pullUrl = url;
|
|
777
|
+
this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
|
|
778
|
+
this.spawnFfmpeg();
|
|
733
779
|
}
|
|
734
780
|
ensureFrameRingSink() {
|
|
735
781
|
if (this.frameRingSink === null) {
|
|
@@ -747,6 +793,7 @@ var FfmpegDecoderSession = class {
|
|
|
747
793
|
}
|
|
748
794
|
spawnFfmpeg() {
|
|
749
795
|
if (this.destroyed) return;
|
|
796
|
+
this.clearPullRedial();
|
|
750
797
|
this.killFfmpeg();
|
|
751
798
|
this.abortOpenSlot();
|
|
752
799
|
this.splitter = null;
|
|
@@ -763,7 +810,8 @@ var FfmpegDecoderSession = class {
|
|
|
763
810
|
}, {
|
|
764
811
|
hwaccel: this.hwaccel,
|
|
765
812
|
renderDevice: this.renderDevice,
|
|
766
|
-
gpuScaleFilter: this.activeGpuScaleFilter
|
|
813
|
+
gpuScaleFilter: this.activeGpuScaleFilter,
|
|
814
|
+
...this.pullUrl !== null ? { inputUrl: this.pullUrl } : {}
|
|
767
815
|
});
|
|
768
816
|
let child;
|
|
769
817
|
try {
|
|
@@ -850,6 +898,27 @@ var FfmpegDecoderSession = class {
|
|
|
850
898
|
signal,
|
|
851
899
|
outputFrames: this.outputFrames
|
|
852
900
|
} });
|
|
901
|
+
if (this.pullUrl !== null) {
|
|
902
|
+
this.clearPullRedial();
|
|
903
|
+
const timer = setTimeout(() => {
|
|
904
|
+
this.pullRedialTimer = null;
|
|
905
|
+
this.spawnFfmpeg();
|
|
906
|
+
}, PULL_REDIAL_DELAY_MS);
|
|
907
|
+
timer.unref?.();
|
|
908
|
+
this.pullRedialTimer = timer;
|
|
909
|
+
this.logger.info("ffmpeg decoder: pull-mode child exited — re-dial scheduled", { meta: {
|
|
910
|
+
code,
|
|
911
|
+
signal,
|
|
912
|
+
redialInMs: PULL_REDIAL_DELAY_MS
|
|
913
|
+
} });
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/** Cancel a pending pull-mode re-dial, if armed. */
|
|
917
|
+
clearPullRedial() {
|
|
918
|
+
if (this.pullRedialTimer !== null) {
|
|
919
|
+
clearTimeout(this.pullRedialTimer);
|
|
920
|
+
this.pullRedialTimer = null;
|
|
921
|
+
}
|
|
853
922
|
}
|
|
854
923
|
handleStderr(data) {
|
|
855
924
|
const text = data.toString();
|
|
@@ -954,6 +1023,7 @@ var FfmpegDecoderSession = class {
|
|
|
954
1023
|
}
|
|
955
1024
|
pushPacket(packet) {
|
|
956
1025
|
if (this.destroyed) return;
|
|
1026
|
+
if (this.pullUrl !== null) return;
|
|
957
1027
|
const stdin = this.process?.stdin;
|
|
958
1028
|
if (!stdin) return;
|
|
959
1029
|
this.inputPackets++;
|
|
@@ -986,6 +1056,7 @@ var FfmpegDecoderSession = class {
|
|
|
986
1056
|
async destroy() {
|
|
987
1057
|
if (this.destroyed) return;
|
|
988
1058
|
this.destroyed = true;
|
|
1059
|
+
this.clearPullRedial();
|
|
989
1060
|
this.killFfmpeg();
|
|
990
1061
|
this.abortOpenSlot();
|
|
991
1062
|
this.splitter = null;
|
|
@@ -1231,7 +1302,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1231
1302
|
return {
|
|
1232
1303
|
id: "decoder-ffmpeg",
|
|
1233
1304
|
name: "Decoder (ffmpeg)",
|
|
1234
|
-
isPullMode:
|
|
1305
|
+
isPullMode: true,
|
|
1235
1306
|
priority: 20
|
|
1236
1307
|
};
|
|
1237
1308
|
}
|
|
@@ -1342,8 +1413,9 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1342
1413
|
});
|
|
1343
1414
|
}
|
|
1344
1415
|
async openStream(input) {
|
|
1345
|
-
|
|
1346
|
-
input.
|
|
1416
|
+
const session = this.sessions.get(input.sessionId);
|
|
1417
|
+
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1418
|
+
await session.openStream(input.url);
|
|
1347
1419
|
}
|
|
1348
1420
|
async pullFrames(input) {
|
|
1349
1421
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|