@camstack/addon-post-analysis 1.2.51 → 1.2.52
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/{dist-DTeVOZBt.mjs → dist-CDLLzMJw.mjs} +104 -469
- package/dist/{dist-CnSzKo7x.js → dist-k21GqaWS.js} +104 -486
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/node-B5brepKo.js +151 -0
- package/dist/node-Co0YpEFr.mjs +148 -0
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-D-6lLgLp.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-C-EUVj-u.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-PkY2z2Wo.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-DPOdauxs.mjs → hostInit-DJUXj7Ya.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +1142 -752
- package/dist/pipeline-analytics/index.mjs +1132 -750
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/node-CnEKzHgS.js +0 -653
- package/dist/node-pIpcQ_aN.mjs +0 -649
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-JSo2V9IT.mjs +0 -26
|
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
19
19
|
value: mod,
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
|
-
|
|
22
|
+
//#endregion
|
|
23
23
|
//#region ../types/dist/event-category-41fKf-q9.mjs
|
|
24
24
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
25
25
|
EventCategory["SystemBoot"] = "system.boot";
|
|
@@ -7157,473 +7157,6 @@ function sleep(ms) {
|
|
|
7157
7157
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
7158
7158
|
}
|
|
7159
7159
|
//#endregion
|
|
7160
|
-
//#region ../types/dist/fmp4-box-splitter-B53u9-Nu.mjs
|
|
7161
|
-
var AUDIO_ENCODER_BY_CODEC = {
|
|
7162
|
-
opus: "libopus",
|
|
7163
|
-
aac: "aac",
|
|
7164
|
-
pcmu: "pcm_mulaw",
|
|
7165
|
-
pcma: "pcm_alaw"
|
|
7166
|
-
};
|
|
7167
|
-
/**
|
|
7168
|
-
* Camera-microphone audio, per codec. Lives HERE rather than in
|
|
7169
|
-
* `encode-defaults.ts` only to avoid an import cycle (`encode-defaults` depends
|
|
7170
|
-
* on these types); it is re-exported from there, which is where to read it.
|
|
7171
|
-
*
|
|
7172
|
-
* Every source in this repo is a mono camera mic. The former broker preset
|
|
7173
|
-
* encoded Opus at `channels: 2`, spending bitrate duplicating one channel —
|
|
7174
|
-
* that is the value this consolidation changed.
|
|
7175
|
-
*/
|
|
7176
|
-
var AUDIO_PRESETS = {
|
|
7177
|
-
aac: {
|
|
7178
|
-
kind: "encode",
|
|
7179
|
-
codec: "aac",
|
|
7180
|
-
bitrateKbps: 128,
|
|
7181
|
-
sampleRateHz: 48e3,
|
|
7182
|
-
channels: 1
|
|
7183
|
-
},
|
|
7184
|
-
opus: {
|
|
7185
|
-
kind: "encode",
|
|
7186
|
-
codec: "opus",
|
|
7187
|
-
bitrateKbps: 64,
|
|
7188
|
-
sampleRateHz: 48e3,
|
|
7189
|
-
channels: 1
|
|
7190
|
-
},
|
|
7191
|
-
pcmu: {
|
|
7192
|
-
kind: "encode",
|
|
7193
|
-
codec: "pcmu",
|
|
7194
|
-
sampleRateHz: 8e3,
|
|
7195
|
-
channels: 1
|
|
7196
|
-
}
|
|
7197
|
-
};
|
|
7198
|
-
/** `-hide_banner -loglevel <level>` — every ffmpeg site opens with this. */
|
|
7199
|
-
function logBannerArgs(level) {
|
|
7200
|
-
return [
|
|
7201
|
-
"-hide_banner",
|
|
7202
|
-
"-loglevel",
|
|
7203
|
-
level
|
|
7204
|
-
];
|
|
7205
|
-
}
|
|
7206
|
-
/** `true` when the resolved value means "decode in software" (⇒ no `-hwaccel`). */
|
|
7207
|
-
function isSoftwareDecode(decodeHwAccel) {
|
|
7208
|
-
return !decodeHwAccel || decodeHwAccel === "none" || decodeHwAccel === "copy";
|
|
7209
|
-
}
|
|
7210
|
-
/**
|
|
7211
|
-
* Every INPUT option, in order, terminated by `-i <url>`. Nothing may be
|
|
7212
|
-
* appended to this list by a caller — that is the whole point of the function.
|
|
7213
|
-
*/
|
|
7214
|
-
function buildInputArgs(input, decodeHwAccel) {
|
|
7215
|
-
const args = [];
|
|
7216
|
-
if (!isSoftwareDecode(decodeHwAccel)) args.push("-hwaccel", String(decodeHwAccel));
|
|
7217
|
-
if (input.extraArgs?.length) args.push(...input.extraArgs);
|
|
7218
|
-
if (input.analyzeDurationUs !== void 0) args.push("-analyzeduration", String(input.analyzeDurationUs));
|
|
7219
|
-
if (input.probeSizeBytes !== void 0) args.push("-probesize", String(input.probeSizeBytes));
|
|
7220
|
-
if (input.fflags?.length) for (const flag of input.fflags) args.push("-fflags", flag);
|
|
7221
|
-
if (input.rtspTransport) args.push("-rtsp_transport", input.rtspTransport);
|
|
7222
|
-
args.push("-i", input.url);
|
|
7223
|
-
return args;
|
|
7224
|
-
}
|
|
7225
|
-
/** The `-vf` filter args, or `[]` when a consumer `-vf` already claims the slot. */
|
|
7226
|
-
function buildVideoFilterArgs(scale, outputArgs) {
|
|
7227
|
-
if (!scale) return [];
|
|
7228
|
-
if (outputArgs.some((a) => a === "-vf")) return [];
|
|
7229
|
-
if (scale.mode === "exact") return ["-vf", `scale=${scale.width}:${scale.height}`];
|
|
7230
|
-
return ["-vf", `scale='min(${scale.width},iw)':'min(${scale.height},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2`];
|
|
7231
|
-
}
|
|
7232
|
-
/** Rate-control args for an encode plan. */
|
|
7233
|
-
function buildRateControlArgs(video) {
|
|
7234
|
-
const kbps = video.bitrateKbps;
|
|
7235
|
-
if (kbps === void 0) return [];
|
|
7236
|
-
const rc = video.rateControl ?? {
|
|
7237
|
-
kind: "cap",
|
|
7238
|
-
vbvSeconds: 2
|
|
7239
|
-
};
|
|
7240
|
-
const bufsize = Math.max(1, Math.round(kbps * rc.vbvSeconds));
|
|
7241
|
-
return [
|
|
7242
|
-
...rc.kind === "cbr" ? ["-b:v", `${kbps}k`] : [],
|
|
7243
|
-
"-maxrate",
|
|
7244
|
-
`${kbps}k`,
|
|
7245
|
-
"-bufsize",
|
|
7246
|
-
`${bufsize}k`
|
|
7247
|
-
];
|
|
7248
|
-
}
|
|
7249
|
-
/** The whole video block (`-vf` … `-c:v` … knobs), after `-i`. */
|
|
7250
|
-
function buildVideoArgs(video, outputArgs) {
|
|
7251
|
-
if (video.kind === "copy") return [
|
|
7252
|
-
"-c:v",
|
|
7253
|
-
"copy",
|
|
7254
|
-
...video.bitstreamFilter ? ["-bsf:v", video.bitstreamFilter] : []
|
|
7255
|
-
];
|
|
7256
|
-
const args = [
|
|
7257
|
-
...buildVideoFilterArgs(video.scale, outputArgs),
|
|
7258
|
-
"-c:v",
|
|
7259
|
-
video.encoder
|
|
7260
|
-
];
|
|
7261
|
-
if (video.preset !== void 0) args.push("-preset", video.preset);
|
|
7262
|
-
if (video.tune !== void 0) args.push("-tune", video.tune);
|
|
7263
|
-
if (video.profile !== void 0) args.push("-profile:v", video.profile);
|
|
7264
|
-
if (video.level !== void 0) args.push("-level", video.level);
|
|
7265
|
-
if (video.pixelFormat !== void 0) args.push("-pix_fmt", video.pixelFormat);
|
|
7266
|
-
if (video.fps !== void 0) args.push("-r", String(video.fps));
|
|
7267
|
-
if (video.gopFrames !== void 0) args.push("-g", String(video.gopFrames));
|
|
7268
|
-
if (video.forceKeyFramesSeconds !== void 0) args.push("-force_key_frames", `expr:gte(t,n_forced*${video.forceKeyFramesSeconds})`);
|
|
7269
|
-
if (video.bf !== void 0) args.push("-bf", String(video.bf));
|
|
7270
|
-
args.push(...buildRateControlArgs(video));
|
|
7271
|
-
if (video.bitstreamFilter !== void 0) args.push("-bsf:v", video.bitstreamFilter);
|
|
7272
|
-
return args;
|
|
7273
|
-
}
|
|
7274
|
-
/** The whole audio block, after `-i`. */
|
|
7275
|
-
function buildAudioArgs(audio) {
|
|
7276
|
-
if (audio.kind === "none") return ["-an"];
|
|
7277
|
-
if (audio.kind === "copy") return ["-c:a", "copy"];
|
|
7278
|
-
const args = [];
|
|
7279
|
-
if (audio.filter !== void 0) args.push("-af", audio.filter);
|
|
7280
|
-
args.push("-c:a", AUDIO_ENCODER_BY_CODEC[audio.codec]);
|
|
7281
|
-
if (audio.application !== void 0) args.push("-application", audio.application);
|
|
7282
|
-
if (audio.frameDurationMs !== void 0) args.push("-frame_duration", String(audio.frameDurationMs));
|
|
7283
|
-
if (audio.globalHeader === true) args.push("-flags", "+global_header");
|
|
7284
|
-
if (audio.sampleRateHz !== void 0) args.push("-ar", String(audio.sampleRateHz));
|
|
7285
|
-
if (audio.bitrateKbps !== void 0) args.push("-b:a", `${audio.bitrateKbps}k`);
|
|
7286
|
-
if (audio.vbvBufferKbits !== void 0) args.push("-bufsize", `${audio.vbvBufferKbits}k`);
|
|
7287
|
-
if (audio.channels !== void 0) args.push("-ac", String(audio.channels));
|
|
7288
|
-
return args;
|
|
7289
|
-
}
|
|
7290
|
-
/** RTP output-leg args (`-payload_type`, `-ssrc`, `-sdp_file`, `-f rtp <url>`). */
|
|
7291
|
-
function buildRtpOutputArgs(out) {
|
|
7292
|
-
const args = [];
|
|
7293
|
-
if (out.payloadType !== void 0) args.push("-payload_type", String(out.payloadType));
|
|
7294
|
-
if (out.ssrc !== void 0) args.push("-ssrc", String(out.ssrc));
|
|
7295
|
-
if (out.sdpFile !== void 0) args.push("-sdp_file", out.sdpFile);
|
|
7296
|
-
args.push("-f", "rtp", out.url);
|
|
7297
|
-
return args;
|
|
7298
|
-
}
|
|
7299
|
-
/** `true` when the sink is a raw elementary bytestream that cannot mux audio. */
|
|
7300
|
-
function isElementaryVideoSink(sink) {
|
|
7301
|
-
return sink.kind === "stdout" && (sink.container === "h264" || sink.container === "hevc");
|
|
7302
|
-
}
|
|
7303
|
-
/**
|
|
7304
|
-
* The fragmented-MP4 muxer flags, in the order the recorder has proven them
|
|
7305
|
-
* (`recorder/addon/ffmpeg-args.ts` passes the same `movflags` string through
|
|
7306
|
-
* `-segment_format_options`, across every vendor in the fleet):
|
|
7307
|
-
*
|
|
7308
|
-
* - `frag_keyframe` — cut a fragment at each key frame, so every fragment
|
|
7309
|
-
* opens on a sync sample. HKSV's whole requirement.
|
|
7310
|
-
* - `empty_moov` — write `ftyp`+`moov` up front with no samples in it, which
|
|
7311
|
-
* is what makes the head a standalone INITIALISATION segment.
|
|
7312
|
-
* - `default_base_moof` — fragment offsets are self-relative, so a fragment is
|
|
7313
|
-
* demuxable without the bytes that preceded it. D31's byte-range read path
|
|
7314
|
-
* depends on exactly this property of the recorder's segments.
|
|
7315
|
-
*/
|
|
7316
|
-
var FMP4_MOVFLAGS = "+frag_keyframe+empty_moov+default_base_moof";
|
|
7317
|
-
/**
|
|
7318
|
-
* The terminal sink args for every non-`rtp-outputs` sink. Exhaustive over the
|
|
7319
|
-
* union so a new member cannot fall through to `['-f', container, 'pipe:1']`,
|
|
7320
|
-
* which is what a plain `container` read would have done for `mp4` — a valid
|
|
7321
|
-
* argv that writes a NON-fragmented, unseekable-to-a-pipe MP4 and produces one
|
|
7322
|
-
* unusable byte stream.
|
|
7323
|
-
*/
|
|
7324
|
-
function buildStdoutOrRtspSinkArgs(sink) {
|
|
7325
|
-
if (sink.kind === "rtsp-listen") return [
|
|
7326
|
-
"-f",
|
|
7327
|
-
"rtsp",
|
|
7328
|
-
"-rtsp_transport",
|
|
7329
|
-
"tcp",
|
|
7330
|
-
"-rtsp_flags",
|
|
7331
|
-
"listen",
|
|
7332
|
-
sink.url
|
|
7333
|
-
];
|
|
7334
|
-
if (sink.kind === "rtp-outputs") return [];
|
|
7335
|
-
return sink.container === "mp4" ? buildFmp4SinkArgs(sink) : [
|
|
7336
|
-
"-f",
|
|
7337
|
-
sink.container,
|
|
7338
|
-
"pipe:1"
|
|
7339
|
-
];
|
|
7340
|
-
}
|
|
7341
|
-
/**
|
|
7342
|
-
* How far BELOW the negotiated fragment length `-min_frag_duration` is set.
|
|
7343
|
-
*
|
|
7344
|
-
* `-min_frag_duration` refuses to cut before that much media has accumulated,
|
|
7345
|
-
* and then waits for the next key frame. Set to exactly `fragmentMs`, the
|
|
7346
|
-
* commonest camera configuration in existence — a key-frame grid EQUAL to the
|
|
7347
|
-
* requested fragment length — lands the deadline on the same instant as the key
|
|
7348
|
-
* frame, loses the race, and skips to the following one: **every fragment comes
|
|
7349
|
-
* out at twice the requested length.**
|
|
7350
|
-
*
|
|
7351
|
-
* Measured on the live fleet 2026-08-07, camera 615, `-c:v copy` (D84):
|
|
7352
|
-
*
|
|
7353
|
-
* | slot | GOP | `-min_frag_duration` | median gap |
|
|
7354
|
-
* | --- | --- | --- | --- |
|
|
7355
|
-
* | 1280×720 | 40 f @ 10 fps = 4.0 s | 4000 ms | **7944 ms** |
|
|
7356
|
-
* | 1280×720 | 40 f @ 10 fps = 4.0 s | 3600 ms | 3973 ms |
|
|
7357
|
-
* | 3840×2160 | 100 f @ 25 fps = 4.0 s | 4000 ms | 8042 ms |
|
|
7358
|
-
* | 3840×2160 | 100 f @ 25 fps = 4.0 s | 3600 ms | 3998 ms |
|
|
7359
|
-
*
|
|
7360
|
-
* A doubled fragment is not a cosmetic overshoot: HKSV requires every fragment
|
|
7361
|
-
* to be no longer than the length the controller SELECTED, so the shipped-but-
|
|
7362
|
-
* inert phase-1 sink would have violated the contract on its first real clip.
|
|
7363
|
-
*
|
|
7364
|
-
* 10 % is chosen against the two failures either side of it. Too small and
|
|
7365
|
-
* ordinary jitter (measured spread 3953-4096 ms) re-loses the race; too large
|
|
7366
|
-
* and a source with a key frame slightly EARLY than the grid gets cut there,
|
|
7367
|
-
* yielding a short fragment for no reason.
|
|
7368
|
-
*/
|
|
7369
|
-
var FMP4_MIN_FRAG_MARGIN = .9;
|
|
7370
|
-
/** `-movflags … -min_frag_duration <us> -f mp4 pipe:1`. */
|
|
7371
|
-
function buildFmp4SinkArgs(sink) {
|
|
7372
|
-
return [
|
|
7373
|
-
"-movflags",
|
|
7374
|
-
FMP4_MOVFLAGS,
|
|
7375
|
-
"-min_frag_duration",
|
|
7376
|
-
String(Math.max(0, Math.round(sink.fragmentMs * FMP4_MIN_FRAG_MARGIN * 1e3))),
|
|
7377
|
-
"-f",
|
|
7378
|
-
"mp4",
|
|
7379
|
-
"pipe:1"
|
|
7380
|
-
];
|
|
7381
|
-
}
|
|
7382
|
-
/**
|
|
7383
|
-
* A second output mapping source audio to RTP-over-UDP. `0:a:0?` makes the
|
|
7384
|
-
* audio optional so a source with no audio skips it instead of failing the
|
|
7385
|
-
* whole invocation.
|
|
7386
|
-
*/
|
|
7387
|
-
function buildAudioSidecarArgs(sidecar) {
|
|
7388
|
-
return [
|
|
7389
|
-
"-map",
|
|
7390
|
-
"0:a:0?",
|
|
7391
|
-
...buildAudioArgs(sidecar.codec === "pcma" ? {
|
|
7392
|
-
kind: "encode",
|
|
7393
|
-
codec: "pcma",
|
|
7394
|
-
sampleRateHz: 8e3,
|
|
7395
|
-
channels: 1
|
|
7396
|
-
} : AUDIO_PRESETS[sidecar.codec]),
|
|
7397
|
-
...buildRtpOutputArgs({
|
|
7398
|
-
url: sidecar.rtpUrl,
|
|
7399
|
-
sdpFile: sidecar.sdpFile
|
|
7400
|
-
})
|
|
7401
|
-
];
|
|
7402
|
-
}
|
|
7403
|
-
/**
|
|
7404
|
-
* Assemble the full ffmpeg argument list. Layout:
|
|
7405
|
-
*
|
|
7406
|
-
* -hide_banner -loglevel <level>
|
|
7407
|
-
* [-hwaccel <backend|auto>] ─┐ INPUT options — strictly before -i.
|
|
7408
|
-
* [<input.extraArgs>] │
|
|
7409
|
-
* [-fflags <flag>…] │
|
|
7410
|
-
* [-rtsp_transport tcp] │
|
|
7411
|
-
* -i <url> ─┘
|
|
7412
|
-
* <video block> <threads> <audio block> ─┐ OUTPUT options.
|
|
7413
|
-
* <consumer outputArgs verbatim> │
|
|
7414
|
-
* <sink> ─┘ terminal
|
|
7415
|
-
*/
|
|
7416
|
-
function buildFfmpegArgs(inv) {
|
|
7417
|
-
const head = [...logBannerArgs(inv.logLevel), ...buildInputArgs(inv.input, inv.decodeHwAccel)];
|
|
7418
|
-
const threadArgs = inv.threadCount > 0 ? ["-threads", String(inv.threadCount)] : [];
|
|
7419
|
-
if (inv.sink.kind === "rtp-outputs") {
|
|
7420
|
-
const videoLeg = inv.sink.video ? [
|
|
7421
|
-
"-an",
|
|
7422
|
-
"-map",
|
|
7423
|
-
"0:v:0",
|
|
7424
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
7425
|
-
...threadArgs,
|
|
7426
|
-
...inv.outputArgs,
|
|
7427
|
-
...buildRtpOutputArgs(inv.sink.video)
|
|
7428
|
-
] : [];
|
|
7429
|
-
const audioLeg = inv.sink.audio ? [
|
|
7430
|
-
"-vn",
|
|
7431
|
-
"-map",
|
|
7432
|
-
"0:a:0?",
|
|
7433
|
-
...buildAudioArgs(inv.audio),
|
|
7434
|
-
...buildRtpOutputArgs(inv.sink.audio)
|
|
7435
|
-
] : [];
|
|
7436
|
-
return [
|
|
7437
|
-
...head,
|
|
7438
|
-
...videoLeg,
|
|
7439
|
-
...audioLeg
|
|
7440
|
-
];
|
|
7441
|
-
}
|
|
7442
|
-
const audioArgs = isElementaryVideoSink(inv.sink) ? ["-an"] : buildAudioArgs(inv.audio);
|
|
7443
|
-
const sinkArgs = buildStdoutOrRtspSinkArgs(inv.sink);
|
|
7444
|
-
return [
|
|
7445
|
-
...head,
|
|
7446
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
7447
|
-
...threadArgs,
|
|
7448
|
-
...audioArgs,
|
|
7449
|
-
...inv.outputArgs,
|
|
7450
|
-
...sinkArgs,
|
|
7451
|
-
...inv.audioSidecar ? buildAudioSidecarArgs(inv.audioSidecar) : []
|
|
7452
|
-
];
|
|
7453
|
-
}
|
|
7454
|
-
var DEFAULT_MAX_UNIT_BYTES = 16 * 1024 * 1024;
|
|
7455
|
-
/** Header size for a normal box, and for one carrying a 64-bit `largesize`. */
|
|
7456
|
-
var BOX_HEADER_BYTES = 8;
|
|
7457
|
-
var LARGE_BOX_HEADER_BYTES = 16;
|
|
7458
|
-
var Fmp4BoxSplitter = class {
|
|
7459
|
-
maxUnitBytes;
|
|
7460
|
-
/** Bytes of the CURRENT unit plus any partial box after it. */
|
|
7461
|
-
buffer = new Uint8Array(0);
|
|
7462
|
-
/** Where the current unit starts inside {@link buffer}. */
|
|
7463
|
-
unitStart = 0;
|
|
7464
|
-
/** Where the box scanner has reached inside {@link buffer}. */
|
|
7465
|
-
cursor = 0;
|
|
7466
|
-
state = "init";
|
|
7467
|
-
nextSequence = 0;
|
|
7468
|
-
faultReason = null;
|
|
7469
|
-
interstitial = /* @__PURE__ */ new Set();
|
|
7470
|
-
constructor(options = {}) {
|
|
7471
|
-
this.maxUnitBytes = options.maxUnitBytes ?? DEFAULT_MAX_UNIT_BYTES;
|
|
7472
|
-
}
|
|
7473
|
-
/**
|
|
7474
|
-
* Non-null once the stream cannot be split. The splitter emits nothing
|
|
7475
|
-
* further, so a caller polls this to kill the child rather than watching a
|
|
7476
|
-
* silent stall — a fragmenter that quietly stops producing looks exactly like
|
|
7477
|
-
* a camera with no motion.
|
|
7478
|
-
*/
|
|
7479
|
-
get fault() {
|
|
7480
|
-
return this.faultReason;
|
|
7481
|
-
}
|
|
7482
|
-
/** Bytes currently held. The memory bound, observable rather than asserted. */
|
|
7483
|
-
get pendingBytes() {
|
|
7484
|
-
return this.buffer.length - this.unitStart;
|
|
7485
|
-
}
|
|
7486
|
-
/**
|
|
7487
|
-
* Top-level box types seen BETWEEN fragments and discarded — `mfra`, `free`,
|
|
7488
|
-
* a stray `sidx`. Reported rather than dropped in silence: they are legal and
|
|
7489
|
-
* useless to a fragment consumer, but a type nobody expected showing up here
|
|
7490
|
-
* is the first symptom of a muxer that is not writing what we think it is.
|
|
7491
|
-
*/
|
|
7492
|
-
get discardedInterstitialTypes() {
|
|
7493
|
-
return [...this.interstitial];
|
|
7494
|
-
}
|
|
7495
|
-
/**
|
|
7496
|
-
* Feed bytes; get back whatever units completed. Returns `[]` once faulted.
|
|
7497
|
-
*/
|
|
7498
|
-
push(chunk) {
|
|
7499
|
-
if (this.faultReason !== null || chunk.length === 0) return [];
|
|
7500
|
-
this.append(chunk);
|
|
7501
|
-
if (this.pendingBytes > this.maxUnitBytes) return this.fail(`a single fMP4 unit exceeded ${this.maxUnitBytes} bytes — this stream is not fragmented`);
|
|
7502
|
-
return this.drainBoxes();
|
|
7503
|
-
}
|
|
7504
|
-
append(chunk) {
|
|
7505
|
-
if (this.buffer.length === 0) {
|
|
7506
|
-
this.buffer = chunk.slice();
|
|
7507
|
-
return;
|
|
7508
|
-
}
|
|
7509
|
-
const next = new Uint8Array(this.buffer.length + chunk.length);
|
|
7510
|
-
next.set(this.buffer, 0);
|
|
7511
|
-
next.set(chunk, this.buffer.length);
|
|
7512
|
-
this.buffer = next;
|
|
7513
|
-
}
|
|
7514
|
-
/** Consume every COMPLETE top-level box now in the buffer. */
|
|
7515
|
-
drainBoxes() {
|
|
7516
|
-
const units = [];
|
|
7517
|
-
for (;;) {
|
|
7518
|
-
const header = this.readHeader();
|
|
7519
|
-
if (this.faultReason !== null) return units;
|
|
7520
|
-
if (header === null) break;
|
|
7521
|
-
if (this.cursor + header.totalBytes > this.buffer.length) break;
|
|
7522
|
-
const boxStart = this.cursor;
|
|
7523
|
-
const boxEnd = boxStart + header.totalBytes;
|
|
7524
|
-
this.cursor = boxEnd;
|
|
7525
|
-
const unit = this.consumeBox(header.type, boxStart, boxEnd);
|
|
7526
|
-
if (this.faultReason !== null) return units;
|
|
7527
|
-
if (unit !== null) units.push(unit);
|
|
7528
|
-
}
|
|
7529
|
-
this.compact();
|
|
7530
|
-
return units;
|
|
7531
|
-
}
|
|
7532
|
-
/**
|
|
7533
|
-
* Apply one box to the state machine. Returns a unit when this box CLOSED
|
|
7534
|
-
* one, `null` otherwise.
|
|
7535
|
-
*/
|
|
7536
|
-
consumeBox(type, boxStart, boxEnd) {
|
|
7537
|
-
if (this.state === "init") {
|
|
7538
|
-
if (type !== "moof") return null;
|
|
7539
|
-
if (boxStart === this.unitStart) {
|
|
7540
|
-
this.fail("a moof arrived before any initialisation box — there is no ftyp/moov to send");
|
|
7541
|
-
return null;
|
|
7542
|
-
}
|
|
7543
|
-
const init = this.emit("init", this.unitStart, boxStart);
|
|
7544
|
-
this.unitStart = boxStart;
|
|
7545
|
-
this.state = "fragment";
|
|
7546
|
-
return init;
|
|
7547
|
-
}
|
|
7548
|
-
if (this.state === "idle") {
|
|
7549
|
-
if (type !== "moof") {
|
|
7550
|
-
this.interstitial.add(type);
|
|
7551
|
-
this.unitStart = boxEnd;
|
|
7552
|
-
return null;
|
|
7553
|
-
}
|
|
7554
|
-
this.unitStart = boxStart;
|
|
7555
|
-
this.state = "fragment";
|
|
7556
|
-
return null;
|
|
7557
|
-
}
|
|
7558
|
-
if (type !== "mdat") return null;
|
|
7559
|
-
const fragment = this.emit("fragment", this.unitStart, boxEnd);
|
|
7560
|
-
this.unitStart = boxEnd;
|
|
7561
|
-
this.state = "idle";
|
|
7562
|
-
return fragment;
|
|
7563
|
-
}
|
|
7564
|
-
/**
|
|
7565
|
-
* Parse the header at {@link cursor}, or `null` when too few bytes have
|
|
7566
|
-
* arrived to know. Faults on a size the splitter cannot honour.
|
|
7567
|
-
*/
|
|
7568
|
-
readHeader() {
|
|
7569
|
-
const available = this.buffer.length - this.cursor;
|
|
7570
|
-
if (available < BOX_HEADER_BYTES) return null;
|
|
7571
|
-
const view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
|
|
7572
|
-
const size = view.getUint32(this.cursor);
|
|
7573
|
-
const type = String.fromCharCode(this.buffer[this.cursor + 4] ?? 0, this.buffer[this.cursor + 5] ?? 0, this.buffer[this.cursor + 6] ?? 0, this.buffer[this.cursor + 7] ?? 0);
|
|
7574
|
-
if (size === 0) {
|
|
7575
|
-
this.fail(`box "${type}" declares size 0 (to EOF) — an unbounded box cannot be fragmented`);
|
|
7576
|
-
return null;
|
|
7577
|
-
}
|
|
7578
|
-
if (size === 1) {
|
|
7579
|
-
if (available < LARGE_BOX_HEADER_BYTES) return null;
|
|
7580
|
-
const large = view.getBigUint64(this.cursor + BOX_HEADER_BYTES);
|
|
7581
|
-
if (large > BigInt(this.maxUnitBytes)) {
|
|
7582
|
-
this.fail(`box "${type}" declares ${large} bytes, over the ${this.maxUnitBytes} byte bound`);
|
|
7583
|
-
return null;
|
|
7584
|
-
}
|
|
7585
|
-
return {
|
|
7586
|
-
type,
|
|
7587
|
-
totalBytes: Number(large)
|
|
7588
|
-
};
|
|
7589
|
-
}
|
|
7590
|
-
if (size < BOX_HEADER_BYTES) {
|
|
7591
|
-
this.fail(`box "${type}" declares an impossible size of ${size} bytes`);
|
|
7592
|
-
return null;
|
|
7593
|
-
}
|
|
7594
|
-
return {
|
|
7595
|
-
type,
|
|
7596
|
-
totalBytes: size
|
|
7597
|
-
};
|
|
7598
|
-
}
|
|
7599
|
-
emit(kind, start, end) {
|
|
7600
|
-
const sequence = this.nextSequence;
|
|
7601
|
-
this.nextSequence += 1;
|
|
7602
|
-
return {
|
|
7603
|
-
kind,
|
|
7604
|
-
data: this.buffer.slice(start, end),
|
|
7605
|
-
sequence
|
|
7606
|
-
};
|
|
7607
|
-
}
|
|
7608
|
-
/**
|
|
7609
|
-
* Drop everything already emitted or discarded. Without this the buffer is
|
|
7610
|
-
* the whole stream and the process dies in hours, not minutes.
|
|
7611
|
-
*/
|
|
7612
|
-
compact() {
|
|
7613
|
-
if (this.unitStart === 0) return;
|
|
7614
|
-
this.buffer = this.buffer.slice(this.unitStart);
|
|
7615
|
-
this.cursor -= this.unitStart;
|
|
7616
|
-
this.unitStart = 0;
|
|
7617
|
-
}
|
|
7618
|
-
fail(reason) {
|
|
7619
|
-
this.faultReason = reason;
|
|
7620
|
-
this.buffer = new Uint8Array(0);
|
|
7621
|
-
this.unitStart = 0;
|
|
7622
|
-
this.cursor = 0;
|
|
7623
|
-
return [];
|
|
7624
|
-
}
|
|
7625
|
-
};
|
|
7626
|
-
//#endregion
|
|
7627
7160
|
//#region ../types/dist/err-msg-IQTHeDzc.mjs
|
|
7628
7161
|
/**
|
|
7629
7162
|
import { errMsg } from '@camstack/types'
|
|
@@ -9645,6 +9178,69 @@ var StreamFormatSchema = _enum([
|
|
|
9645
9178
|
"mjpeg",
|
|
9646
9179
|
"rtsp"
|
|
9647
9180
|
]);
|
|
9181
|
+
/** A container `produceEventMedia` can emit. */
|
|
9182
|
+
var EventMediaKindSchema = _enum(["mp4", "gif"]);
|
|
9183
|
+
/**
|
|
9184
|
+
* One produced artifact, referenced by HANDLE.
|
|
9185
|
+
*
|
|
9186
|
+
* Never inline bytes: a produced clip is 200 KB–5 MB and every consumer of this
|
|
9187
|
+
* method is in another runner ([D9](../../../../docs/decisions/adr-0009.md),
|
|
9188
|
+
* [D18](../../../../docs/decisions/adr-0018.md) — cross-process media is fetched
|
|
9189
|
+
* on demand, compressed, by handle). `bytes` is here so a caller can decide
|
|
9190
|
+
* whether it wants the fetch at all.
|
|
9191
|
+
*/
|
|
9192
|
+
var EventMediaArtifactSchema = object({
|
|
9193
|
+
kind: EventMediaKindSchema,
|
|
9194
|
+
/** Opaque, single-camera, short-lived. Redeem with `fetchEventMedia`. */
|
|
9195
|
+
handle: string(),
|
|
9196
|
+
/**
|
|
9197
|
+
* The node holding the bytes — the ROUTING key for `fetchEventMedia`.
|
|
9198
|
+
*
|
|
9199
|
+
* `stream-broker` is a singleton cap and an unpinned call never leaves the
|
|
9200
|
+
* hub, so a handle produced on an agent's broker would be redeemed against
|
|
9201
|
+
* the hub's store and come back `null`. Same contract, same field name and
|
|
9202
|
+
* the same reason as `FrameHandleSchema.nodeId`: the producer stamps where it
|
|
9203
|
+
* lives and the consumer pins to it.
|
|
9204
|
+
*/
|
|
9205
|
+
nodeId: string(),
|
|
9206
|
+
mime: string(),
|
|
9207
|
+
bytes: number().int(),
|
|
9208
|
+
width: number().int(),
|
|
9209
|
+
height: number().int()
|
|
9210
|
+
});
|
|
9211
|
+
/**
|
|
9212
|
+
* What a production actually covered — the answer to the only question an
|
|
9213
|
+
* operator asks about a notification clip.
|
|
9214
|
+
*
|
|
9215
|
+
* `fromTs`/`toTs` are WALL CLOCK, derived from the ring's own packet timeline,
|
|
9216
|
+
* so a caller can state "this clip starts 4.1 s before the event" instead of
|
|
9217
|
+
* inferring it from a duration. A production whose `fromTs` is later than the
|
|
9218
|
+
* event is a production with no pre-roll, and that is exactly the defect this
|
|
9219
|
+
* method exists to make visible rather than plausible.
|
|
9220
|
+
*/
|
|
9221
|
+
var EventMediaCoverageSchema = object({
|
|
9222
|
+
fromTs: number(),
|
|
9223
|
+
toTs: number(),
|
|
9224
|
+
/** Encoded packets in the muxed window. */
|
|
9225
|
+
packets: number().int()
|
|
9226
|
+
});
|
|
9227
|
+
/**
|
|
9228
|
+
* The result of ONE cut, in every container the caller asked for.
|
|
9229
|
+
*
|
|
9230
|
+
* Every artifact in `media` came out of the SAME window of the SAME rendition —
|
|
9231
|
+
* that is the whole reason this is one method rather than one call per format.
|
|
9232
|
+
* A consumer attaching a gif and a video can no longer show two different
|
|
9233
|
+
* moments, because it never chose two sources.
|
|
9234
|
+
*/
|
|
9235
|
+
var EventMediaProductionSchema = object({
|
|
9236
|
+
media: array(EventMediaArtifactSchema).readonly(),
|
|
9237
|
+
coverage: EventMediaCoverageSchema,
|
|
9238
|
+
/** The rendition actually cut from — what the default or the fallback chose. */
|
|
9239
|
+
profile: CamProfileSchema,
|
|
9240
|
+
/** `copy` = the camera's own H.264, untouched. `encode` = re-encoded (H.265
|
|
9241
|
+
* source, a downscale, or a playback rate other than 1). */
|
|
9242
|
+
video: _enum(["copy", "encode"])
|
|
9243
|
+
});
|
|
9648
9244
|
var RtspRestreamEntrySchema = object({
|
|
9649
9245
|
brokerId: string(),
|
|
9650
9246
|
url: string(),
|
|
@@ -10044,6 +9640,34 @@ method(object({
|
|
|
10044
9640
|
}), {
|
|
10045
9641
|
kind: "mutation",
|
|
10046
9642
|
auth: "admin"
|
|
9643
|
+
}), method(object({
|
|
9644
|
+
deviceId: number(),
|
|
9645
|
+
/** Absent = the largest H.264 rendition at or below 1080p, which is
|
|
9646
|
+
* also the one that can be copied. Falls back to whatever the ring
|
|
9647
|
+
* actually retained, and the answer says which. */
|
|
9648
|
+
profile: CamProfileSchema.optional(),
|
|
9649
|
+
aroundMs: number(),
|
|
9650
|
+
preSeconds: number().min(0).max(20).default(4),
|
|
9651
|
+
postSeconds: number().min(0).max(20).default(6),
|
|
9652
|
+
kinds: array(EventMediaKindSchema).min(1).default(["mp4"]),
|
|
9653
|
+
/** GIF geometry. The video keeps the source's own. */
|
|
9654
|
+
gifMaxWidth: number().int().min(120).max(1280).default(640),
|
|
9655
|
+
gifFps: number().int().min(1).max(15).default(8),
|
|
9656
|
+
/**
|
|
9657
|
+
* Playback rate, applied to EVERY container so they stay one clip.
|
|
9658
|
+
* `1` is real time and is what allows the copy branch.
|
|
9659
|
+
*/
|
|
9660
|
+
speed: number().min(1).max(8).default(1)
|
|
9661
|
+
}), EventMediaProductionSchema, {
|
|
9662
|
+
kind: "mutation",
|
|
9663
|
+
auth: "admin"
|
|
9664
|
+
}), method(object({ handle: string() }), object({
|
|
9665
|
+
base64: string(),
|
|
9666
|
+
mime: string(),
|
|
9667
|
+
bytes: number().int()
|
|
9668
|
+
}).nullable(), {
|
|
9669
|
+
kind: "mutation",
|
|
9670
|
+
auth: "admin"
|
|
10047
9671
|
}), method(_void(), array(CameraStreamSchema).readonly()), method(_void(), array(ProfileSlotSchema).readonly()), method(object({ brokerId: string() }), BrokerStatsSchema), method(object({ brokerId: string() }), object({
|
|
10048
9672
|
probed: boolean(),
|
|
10049
9673
|
summary: string()
|
|
@@ -32608,6 +32232,12 @@ Object.freeze({
|
|
|
32608
32232
|
addonId: null,
|
|
32609
32233
|
access: "create"
|
|
32610
32234
|
},
|
|
32235
|
+
"streamBroker.fetchEventMedia": {
|
|
32236
|
+
capName: "stream-broker",
|
|
32237
|
+
capScope: "system",
|
|
32238
|
+
addonId: null,
|
|
32239
|
+
access: "create"
|
|
32240
|
+
},
|
|
32611
32241
|
"streamBroker.getAllRtspEntries": {
|
|
32612
32242
|
capName: "stream-broker",
|
|
32613
32243
|
capScope: "system",
|
|
@@ -32692,6 +32322,12 @@ Object.freeze({
|
|
|
32692
32322
|
addonId: null,
|
|
32693
32323
|
access: "create"
|
|
32694
32324
|
},
|
|
32325
|
+
"streamBroker.produceEventMedia": {
|
|
32326
|
+
capName: "stream-broker",
|
|
32327
|
+
capScope: "system",
|
|
32328
|
+
addonId: null,
|
|
32329
|
+
access: "create"
|
|
32330
|
+
},
|
|
32695
32331
|
"streamBroker.publishCameraStream": {
|
|
32696
32332
|
capName: "stream-broker",
|
|
32697
32333
|
capScope: "system",
|
|
@@ -33743,12 +33379,6 @@ Object.defineProperty(exports, "EventCategory", {
|
|
|
33743
33379
|
return EventCategory;
|
|
33744
33380
|
}
|
|
33745
33381
|
});
|
|
33746
|
-
Object.defineProperty(exports, "Fmp4BoxSplitter", {
|
|
33747
|
-
enumerable: true,
|
|
33748
|
-
get: function() {
|
|
33749
|
-
return Fmp4BoxSplitter;
|
|
33750
|
-
}
|
|
33751
|
-
});
|
|
33752
33382
|
Object.defineProperty(exports, "LabelAttributionSchema", {
|
|
33753
33383
|
enumerable: true,
|
|
33754
33384
|
get: function() {
|
|
@@ -33899,12 +33529,6 @@ Object.defineProperty(exports, "buildEventKindDescriptor", {
|
|
|
33899
33529
|
return buildEventKindDescriptor;
|
|
33900
33530
|
}
|
|
33901
33531
|
});
|
|
33902
|
-
Object.defineProperty(exports, "buildFfmpegArgs", {
|
|
33903
|
-
enumerable: true,
|
|
33904
|
-
get: function() {
|
|
33905
|
-
return buildFfmpegArgs;
|
|
33906
|
-
}
|
|
33907
|
-
});
|
|
33908
33532
|
Object.defineProperty(exports, "cosineSimilarity", {
|
|
33909
33533
|
enumerable: true,
|
|
33910
33534
|
get: function() {
|
|
@@ -33977,12 +33601,6 @@ Object.defineProperty(exports, "isScheduleActive", {
|
|
|
33977
33601
|
return isScheduleActive;
|
|
33978
33602
|
}
|
|
33979
33603
|
});
|
|
33980
|
-
Object.defineProperty(exports, "isSoftwareDecode", {
|
|
33981
|
-
enumerable: true,
|
|
33982
|
-
get: function() {
|
|
33983
|
-
return isSoftwareDecode;
|
|
33984
|
-
}
|
|
33985
|
-
});
|
|
33986
33604
|
Object.defineProperty(exports, "kebabToCamel", {
|
|
33987
33605
|
enumerable: true,
|
|
33988
33606
|
get: function() {
|
|
@@ -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-k21GqaWS.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
let node_fs$1 = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
node_fs = require_dist.__toESM(node_fs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as hfModelUrl,
|
|
1
|
+
import { M as hfModelUrl, W as BaseAddon, k as embeddingEncoderCapability } from "../dist-CDLLzMJw.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import "node:fs";
|