@camstack/system 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/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/platform-probe/index.d.ts +20 -9
- package/dist/builtins/platform-probe/index.js +81 -305
- package/dist/builtins/platform-probe/index.mjs +83 -305
- package/dist/builtins/system-config/system-config.addon.js +51 -118
- package/dist/builtins/system-config/system-config.addon.mjs +51 -118
- package/dist/index.js +1 -2
- package/dist/index.mjs +2 -2
- package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +9 -0
- package/dist/kernel/index.d.ts +0 -2
- package/dist/kernel/inference/inference-resolver.d.ts +16 -0
- package/dist/kernel/transport/parent-unowned-call.d.ts +20 -0
- package/dist/{manifest-python-deps-XWJwKYDx.js → manifest-python-deps-CCmnJmNf.js} +27 -37
- package/dist/{manifest-python-deps-CPJXzrZt.mjs → manifest-python-deps-DPA7N9UP.mjs} +31 -35
- package/package.json +1 -1
- package/dist/builtins/platform-probe/hardware-decode-accel-probe.d.ts +0 -37
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +0 -13
- package/dist/kernel/hwaccel/hwaccel-service.d.ts +0 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import { BaseAddon, EventCategory, errMsg, platformProbeCapability, scoreRuntimes } from "@camstack/types";
|
|
2
|
+
import { BaseAddon, EventCategory, emitReadiness, errMsg, platformProbeCapability, scoreRuntimes } from "@camstack/types";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import * as os from "node:os";
|
|
@@ -11,7 +11,7 @@ import * as os from "node:os";
|
|
|
11
11
|
* entire Node process (WS handshakes, tRPC subscriptions, metrics — all
|
|
12
12
|
* stalled). Keeping it async turns the probe into a true background task.
|
|
13
13
|
*/
|
|
14
|
-
var execFileAsync
|
|
14
|
+
var execFileAsync = promisify(execFile);
|
|
15
15
|
/** Minimal no-op logger for default parameter */
|
|
16
16
|
var noopLogger = {
|
|
17
17
|
debug() {},
|
|
@@ -39,7 +39,7 @@ async function getAvailableRAM_MB() {
|
|
|
39
39
|
const platform = os.platform();
|
|
40
40
|
try {
|
|
41
41
|
if (platform === "darwin") {
|
|
42
|
-
const { stdout: output } = await execFileAsync
|
|
42
|
+
const { stdout: output } = await execFileAsync("vm_stat", [], {
|
|
43
43
|
encoding: "utf8",
|
|
44
44
|
timeout: 3e3
|
|
45
45
|
});
|
|
@@ -164,7 +164,7 @@ var PlatformScorer = class {
|
|
|
164
164
|
}
|
|
165
165
|
if (platform === "linux") {
|
|
166
166
|
try {
|
|
167
|
-
const { stdout } = await execFileAsync
|
|
167
|
+
const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
|
|
168
168
|
encoding: "utf8",
|
|
169
169
|
timeout: 5e3
|
|
170
170
|
});
|
|
@@ -230,7 +230,7 @@ var PlatformScorer = class {
|
|
|
230
230
|
"python"
|
|
231
231
|
];
|
|
232
232
|
for (const cmd of candidates) try {
|
|
233
|
-
await execFileAsync
|
|
233
|
+
await execFileAsync(cmd, ["--version"], { timeout: 5e3 });
|
|
234
234
|
return cmd;
|
|
235
235
|
} catch (err) {
|
|
236
236
|
this.logger.debug(`Python command "${cmd}" not found`, { meta: { error: errMsg(err) } });
|
|
@@ -328,296 +328,91 @@ var InferenceConfigResolver = class {
|
|
|
328
328
|
}
|
|
329
329
|
};
|
|
330
330
|
//#endregion
|
|
331
|
-
//#region src/builtins/platform-probe/
|
|
332
|
-
var execFileAsync$1 = promisify(execFile);
|
|
333
|
-
function buildCandidates(hardware) {
|
|
334
|
-
const out = [];
|
|
335
|
-
if (hardware.platform === "darwin") {
|
|
336
|
-
out.push({
|
|
337
|
-
encoder: "h264_videotoolbox",
|
|
338
|
-
codec: "H264",
|
|
339
|
-
family: "videotoolbox"
|
|
340
|
-
});
|
|
341
|
-
out.push({
|
|
342
|
-
encoder: "hevc_videotoolbox",
|
|
343
|
-
codec: "H265",
|
|
344
|
-
family: "videotoolbox"
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
if (hardware.platform === "linux") {
|
|
348
|
-
if (hardware.gpu?.type === "nvidia") {
|
|
349
|
-
out.push({
|
|
350
|
-
encoder: "h264_nvenc",
|
|
351
|
-
codec: "H264",
|
|
352
|
-
family: "nvenc"
|
|
353
|
-
});
|
|
354
|
-
out.push({
|
|
355
|
-
encoder: "hevc_nvenc",
|
|
356
|
-
codec: "H265",
|
|
357
|
-
family: "nvenc"
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
if (hardware.gpu?.type === "intel" || hardware.gpu?.type === "amd" || !hardware.gpu) {
|
|
361
|
-
out.push({
|
|
362
|
-
encoder: "h264_vaapi",
|
|
363
|
-
codec: "H264",
|
|
364
|
-
family: "vaapi"
|
|
365
|
-
});
|
|
366
|
-
out.push({
|
|
367
|
-
encoder: "hevc_vaapi",
|
|
368
|
-
codec: "H265",
|
|
369
|
-
family: "vaapi"
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return out;
|
|
374
|
-
}
|
|
375
|
-
async function runTestEncode(candidate, opts) {
|
|
376
|
-
const ffmpeg = opts.ffmpegPath ?? "ffmpeg";
|
|
377
|
-
const timeout = opts.timeoutMs ?? 8e3;
|
|
378
|
-
const nullSink = os.platform() === "win32" ? "NUL" : "/dev/null";
|
|
379
|
-
const baseArgs = [
|
|
380
|
-
"-hide_banner",
|
|
381
|
-
"-loglevel",
|
|
382
|
-
"error"
|
|
383
|
-
];
|
|
384
|
-
const inArgs = [
|
|
385
|
-
"-f",
|
|
386
|
-
"lavfi",
|
|
387
|
-
"-i",
|
|
388
|
-
"testsrc=duration=0.04:size=16x16:rate=25"
|
|
389
|
-
];
|
|
390
|
-
let outArgs;
|
|
391
|
-
if (candidate.family === "vaapi") outArgs = [
|
|
392
|
-
"-vaapi_device",
|
|
393
|
-
"/dev/dri/renderD128",
|
|
394
|
-
"-vf",
|
|
395
|
-
"format=nv12,hwupload",
|
|
396
|
-
"-c:v",
|
|
397
|
-
candidate.encoder,
|
|
398
|
-
"-frames:v",
|
|
399
|
-
"1",
|
|
400
|
-
"-f",
|
|
401
|
-
"null",
|
|
402
|
-
nullSink
|
|
403
|
-
];
|
|
404
|
-
else outArgs = [
|
|
405
|
-
"-c:v",
|
|
406
|
-
candidate.encoder,
|
|
407
|
-
"-frames:v",
|
|
408
|
-
"1",
|
|
409
|
-
"-f",
|
|
410
|
-
"null",
|
|
411
|
-
nullSink
|
|
412
|
-
];
|
|
413
|
-
try {
|
|
414
|
-
await execFileAsync$1(ffmpeg, [
|
|
415
|
-
...baseArgs,
|
|
416
|
-
...inArgs,
|
|
417
|
-
...outArgs
|
|
418
|
-
], {
|
|
419
|
-
timeout,
|
|
420
|
-
encoding: "utf8"
|
|
421
|
-
});
|
|
422
|
-
return {
|
|
423
|
-
encoder: candidate.encoder,
|
|
424
|
-
codec: candidate.codec,
|
|
425
|
-
family: candidate.family,
|
|
426
|
-
available: true
|
|
427
|
-
};
|
|
428
|
-
} catch (err) {
|
|
429
|
-
return {
|
|
430
|
-
encoder: candidate.encoder,
|
|
431
|
-
codec: candidate.codec,
|
|
432
|
-
family: candidate.family,
|
|
433
|
-
available: false,
|
|
434
|
-
reason: errMsg(err)
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
var HardwareEncoderProber = class {
|
|
439
|
-
cached = null;
|
|
440
|
-
inflight = null;
|
|
441
|
-
logger;
|
|
442
|
-
ffmpegPath;
|
|
443
|
-
constructor(logger, ffmpegPath = "ffmpeg") {
|
|
444
|
-
this.logger = logger;
|
|
445
|
-
this.ffmpegPath = ffmpegPath;
|
|
446
|
-
}
|
|
447
|
-
getCached() {
|
|
448
|
-
return this.cached;
|
|
449
|
-
}
|
|
450
|
-
async probe(hardware, options = {}) {
|
|
451
|
-
if (!options.force && this.cached) return this.cached;
|
|
452
|
-
if (this.inflight) return this.inflight;
|
|
453
|
-
this.inflight = this.runProbe(hardware).then((res) => {
|
|
454
|
-
this.cached = res;
|
|
455
|
-
this.inflight = null;
|
|
456
|
-
return res;
|
|
457
|
-
}).catch((err) => {
|
|
458
|
-
this.inflight = null;
|
|
459
|
-
throw err;
|
|
460
|
-
});
|
|
461
|
-
return this.inflight;
|
|
462
|
-
}
|
|
463
|
-
async runProbe(hardware) {
|
|
464
|
-
const candidates = buildCandidates(hardware);
|
|
465
|
-
const start = Date.now();
|
|
466
|
-
this.logger.info("Probing hardware encoders", { meta: {
|
|
467
|
-
platform: hardware.platform,
|
|
468
|
-
arch: hardware.arch,
|
|
469
|
-
candidates: candidates.length
|
|
470
|
-
} });
|
|
471
|
-
const probes = await Promise.all(candidates.map((c) => runTestEncode(c, { ffmpegPath: this.ffmpegPath })));
|
|
472
|
-
probes.push({
|
|
473
|
-
encoder: "libx264",
|
|
474
|
-
codec: "H264",
|
|
475
|
-
family: "software",
|
|
476
|
-
available: true
|
|
477
|
-
});
|
|
478
|
-
probes.push({
|
|
479
|
-
encoder: "libx265",
|
|
480
|
-
codec: "H265",
|
|
481
|
-
family: "software",
|
|
482
|
-
available: true
|
|
483
|
-
});
|
|
484
|
-
const pickDefault = (codec) => {
|
|
485
|
-
const hw = probes.find((p) => p.codec === codec && p.available && p.family !== "software");
|
|
486
|
-
if (hw) return hw.encoder;
|
|
487
|
-
return codec === "H264" ? "libx264" : "libx265";
|
|
488
|
-
};
|
|
489
|
-
const result = {
|
|
490
|
-
encoders: probes,
|
|
491
|
-
defaultH264: pickDefault("H264"),
|
|
492
|
-
defaultH265: pickDefault("H265"),
|
|
493
|
-
probedAt: Date.now()
|
|
494
|
-
};
|
|
495
|
-
const elapsed = Date.now() - start;
|
|
496
|
-
this.logger.info("Hardware encoder probe complete", { meta: {
|
|
497
|
-
elapsedMs: elapsed,
|
|
498
|
-
defaultH264: result.defaultH264,
|
|
499
|
-
defaultH265: result.defaultH265,
|
|
500
|
-
availableHw: probes.filter((p) => p.available && p.family !== "software").map((p) => p.encoder)
|
|
501
|
-
} });
|
|
502
|
-
return result;
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
//#endregion
|
|
506
|
-
//#region src/builtins/platform-probe/hardware-decode-accel-probe.ts
|
|
507
|
-
var execFileAsync = promisify(execFile);
|
|
508
|
-
var defaultExecFn = async (ffmpegPath, args) => {
|
|
509
|
-
const { stdout } = await execFileAsync(ffmpegPath, [...args], {
|
|
510
|
-
timeout: 8e3,
|
|
511
|
-
encoding: "utf8"
|
|
512
|
-
});
|
|
513
|
-
return { stdout };
|
|
514
|
-
};
|
|
331
|
+
//#region src/builtins/platform-probe/index.ts
|
|
515
332
|
/**
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
* method names, lowercased and de-duplicated (order preserved).
|
|
333
|
+
* The decode-hwaccel backends `ctx.kernel.hwaccel.resolve` accepts. The cap
|
|
334
|
+
* input enum is WIDER (it also carries EP-only names — coreml/openvino/… —
|
|
335
|
+
* shared with other probe surfaces), so the provider param must stay
|
|
336
|
+
* `string`-typed to satisfy the `InferProvider` contract. We narrow it here
|
|
337
|
+
* with a type guard instead of a cast: any value that is not a known decode
|
|
338
|
+
* backend (or the `'none'` sentinel) resolves to `null` → auto-probe.
|
|
523
339
|
*/
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
340
|
+
var HWACCEL_DECODE_BACKENDS = [
|
|
341
|
+
"videotoolbox",
|
|
342
|
+
"cuda",
|
|
343
|
+
"nvdec",
|
|
344
|
+
"vaapi",
|
|
345
|
+
"qsv",
|
|
346
|
+
"d3d11va",
|
|
347
|
+
"dxva2",
|
|
348
|
+
"amf",
|
|
349
|
+
"vdpau",
|
|
350
|
+
"drm"
|
|
351
|
+
];
|
|
352
|
+
function isHwAccelBackend(value) {
|
|
353
|
+
return HWACCEL_DECODE_BACKENDS.includes(value);
|
|
354
|
+
}
|
|
355
|
+
function narrowHwAccelPrefer(value) {
|
|
356
|
+
if (value === "none") return "none";
|
|
357
|
+
if (typeof value === "string" && isHwAccelBackend(value)) return value;
|
|
358
|
+
return null;
|
|
536
359
|
}
|
|
537
|
-
/**
|
|
538
|
-
* Probes which `-hwaccel` decode backends the configured ffmpeg binary
|
|
539
|
-
* supports. Mirrors {@link HardwareEncoderProber} (configured binary, cached,
|
|
540
|
-
* single-flight, forceable). Never throws: a probe failure yields an empty
|
|
541
|
-
* method list so callers fall back to software decode.
|
|
542
|
-
*/
|
|
543
|
-
var HardwareDecodeAccelProber = class {
|
|
544
|
-
cached = null;
|
|
545
|
-
inflight = null;
|
|
546
|
-
logger;
|
|
547
|
-
ffmpegPath;
|
|
548
|
-
execFn;
|
|
549
|
-
constructor(logger, ffmpegPath = "ffmpeg", execFn = defaultExecFn) {
|
|
550
|
-
this.logger = logger;
|
|
551
|
-
this.ffmpegPath = ffmpegPath;
|
|
552
|
-
this.execFn = execFn;
|
|
553
|
-
}
|
|
554
|
-
getCached() {
|
|
555
|
-
return this.cached;
|
|
556
|
-
}
|
|
557
|
-
async probe(options = {}) {
|
|
558
|
-
if (!options.force && this.cached) return this.cached;
|
|
559
|
-
if (this.inflight) return this.inflight;
|
|
560
|
-
this.inflight = this.runProbe().then((res) => {
|
|
561
|
-
this.cached = res;
|
|
562
|
-
this.inflight = null;
|
|
563
|
-
return res;
|
|
564
|
-
}).catch((err) => {
|
|
565
|
-
this.inflight = null;
|
|
566
|
-
throw err;
|
|
567
|
-
});
|
|
568
|
-
return this.inflight;
|
|
569
|
-
}
|
|
570
|
-
async runProbe() {
|
|
571
|
-
try {
|
|
572
|
-
const { stdout } = await this.execFn(this.ffmpegPath, ["-hide_banner", "-hwaccels"]);
|
|
573
|
-
const methods = parseHwAccelsOutput(stdout);
|
|
574
|
-
this.logger.info("Hardware decode-accel probe complete", { meta: { methods } });
|
|
575
|
-
return {
|
|
576
|
-
methods,
|
|
577
|
-
probedAt: Date.now()
|
|
578
|
-
};
|
|
579
|
-
} catch (err) {
|
|
580
|
-
this.logger.warn("Hardware decode-accel probe failed — assuming none available", { meta: { error: errMsg(err) } });
|
|
581
|
-
return {
|
|
582
|
-
methods: [],
|
|
583
|
-
probedAt: Date.now()
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
//#endregion
|
|
589
|
-
//#region src/builtins/platform-probe/index.ts
|
|
590
360
|
var PlatformProbeNativeAddon = class extends BaseAddon {
|
|
591
361
|
scorer = null;
|
|
592
|
-
encoderProber = null;
|
|
593
|
-
decodeAccelProber = null;
|
|
594
362
|
cachedCaps = null;
|
|
363
|
+
/**
|
|
364
|
+
* Per-boot generation stamp for the manual readiness emissions below.
|
|
365
|
+
* Constant for the lifetime of this addon instance (== one process boot);
|
|
366
|
+
* consumer-side registries derive a monotonic epoch from generation
|
|
367
|
+
* transitions. Mirrors `BaseAddon._readinessGeneration` (private there).
|
|
368
|
+
*/
|
|
369
|
+
readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
595
370
|
constructor() {
|
|
596
371
|
super({});
|
|
597
372
|
}
|
|
598
373
|
/**
|
|
599
|
-
*
|
|
600
|
-
* `
|
|
601
|
-
*
|
|
602
|
-
*
|
|
374
|
+
* Manual readiness protocol. The provider registers synchronously from
|
|
375
|
+
* `onInitialize`, but the REAL hardware + EP scoring is the async
|
|
376
|
+
* `probePromise` (embedded-Python install included). The BaseAddon
|
|
377
|
+
* auto-emit would flip `ready` at registration time — BEFORE any
|
|
378
|
+
* accelerator is visible — so probe-gated consumers (detection-pipeline
|
|
379
|
+
* engine auto-pick) would read accelerator-blind results and stick on
|
|
380
|
+
* onnx-CPU. Instead: `starting` at init, the single authoritative
|
|
381
|
+
* `ready` once the probe resolves, `down` on shutdown.
|
|
603
382
|
*/
|
|
604
|
-
|
|
383
|
+
get autoEmitReadiness() {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
/** Bare cluster node id — readiness is scoped `{type:'node', nodeId}`. */
|
|
387
|
+
bareLocalNodeId() {
|
|
388
|
+
const raw = this.ctx.kernel?.localNodeId ?? "hub";
|
|
389
|
+
return raw.includes("/") ? raw.split("/")[0] : raw;
|
|
390
|
+
}
|
|
391
|
+
/** Emit a `system.ready-state` transition for the platform-probe cap. */
|
|
392
|
+
emitProbeReadiness(state) {
|
|
393
|
+
const ctx = this.ctxIfReady;
|
|
394
|
+
if (!ctx) return;
|
|
395
|
+
const nodeId = this.bareLocalNodeId();
|
|
605
396
|
try {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
397
|
+
emitReadiness(ctx.eventBus, {
|
|
398
|
+
capName: platformProbeCapability.name,
|
|
399
|
+
scope: {
|
|
400
|
+
type: "node",
|
|
401
|
+
nodeId
|
|
402
|
+
},
|
|
403
|
+
state,
|
|
404
|
+
generation: this.readinessGeneration,
|
|
405
|
+
sourceNodeId: nodeId
|
|
406
|
+
});
|
|
407
|
+
} catch {}
|
|
611
408
|
}
|
|
612
409
|
async onInitialize() {
|
|
410
|
+
this.emitProbeReadiness("starting");
|
|
613
411
|
const embeddedPython = await this.ctx.deps.ensurePython().catch((err) => {
|
|
614
412
|
this.ctx.logger.debug("ensurePython unavailable for platform probe", { meta: { error: errMsg(err) } });
|
|
615
413
|
return null;
|
|
616
414
|
});
|
|
617
415
|
this.scorer = new PlatformScorer(this.ctx.logger, embeddedPython);
|
|
618
|
-
const ffmpegPath = await this.resolveFfmpegBinaryPath();
|
|
619
|
-
this.encoderProber = new HardwareEncoderProber(this.ctx.logger, ffmpegPath);
|
|
620
|
-
this.decodeAccelProber = new HardwareDecodeAccelProber(this.ctx.logger, ffmpegPath);
|
|
621
416
|
const emitPhase = (phase, payload) => {
|
|
622
417
|
this.ctx.eventBus?.emit({
|
|
623
418
|
id: `platform-probe-${phase}-${Date.now()}`,
|
|
@@ -644,6 +439,7 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
|
|
|
644
439
|
bestReason: caps.bestScore.reason,
|
|
645
440
|
bestScore: caps.bestScore.score
|
|
646
441
|
} });
|
|
442
|
+
this.emitProbeReadiness("ready");
|
|
647
443
|
return caps;
|
|
648
444
|
}).catch((err) => {
|
|
649
445
|
const msg = errMsg(err);
|
|
@@ -669,42 +465,24 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
|
|
|
669
465
|
},
|
|
670
466
|
resolveHwAccel: async (input) => {
|
|
671
467
|
const hwaccel = this.ctx.kernel.hwaccel;
|
|
672
|
-
if (!hwaccel) return {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return prober.probe(caps.hardware);
|
|
682
|
-
},
|
|
683
|
-
refreshHardwareEncoders: async () => {
|
|
684
|
-
const prober = this.encoderProber;
|
|
685
|
-
if (!prober) throw new Error("Hardware encoder prober not initialized");
|
|
686
|
-
const caps = await getCaps();
|
|
687
|
-
return prober.probe(caps.hardware, { force: true });
|
|
688
|
-
},
|
|
689
|
-
getHardwareDecodeAccels: async () => {
|
|
690
|
-
const prober = this.decodeAccelProber;
|
|
691
|
-
if (!prober) throw new Error("Hardware decode-accel prober not initialized");
|
|
692
|
-
return prober.probe();
|
|
693
|
-
},
|
|
694
|
-
refreshHardwareDecodeAccels: async () => {
|
|
695
|
-
const prober = this.decodeAccelProber;
|
|
696
|
-
if (!prober) throw new Error("Hardware decode-accel prober not initialized");
|
|
697
|
-
return prober.probe({ force: true });
|
|
468
|
+
if (!hwaccel) return {
|
|
469
|
+
preferred: [],
|
|
470
|
+
rationale: "kernel hwaccel unavailable"
|
|
471
|
+
};
|
|
472
|
+
const res = await hwaccel.resolve(narrowHwAccelPrefer(input.prefer));
|
|
473
|
+
return {
|
|
474
|
+
preferred: res.preferred,
|
|
475
|
+
rationale: res.rationale
|
|
476
|
+
};
|
|
698
477
|
}
|
|
699
478
|
}
|
|
700
479
|
}];
|
|
701
480
|
}
|
|
702
481
|
async onShutdown() {
|
|
482
|
+
this.emitProbeReadiness("down");
|
|
703
483
|
this.scorer = null;
|
|
704
|
-
this.encoderProber = null;
|
|
705
|
-
this.decodeAccelProber = null;
|
|
706
484
|
this.cachedCaps = null;
|
|
707
485
|
}
|
|
708
486
|
};
|
|
709
487
|
//#endregion
|
|
710
|
-
export {
|
|
488
|
+
export { InferenceConfigResolver, PlatformProbeNativeAddon, PlatformProbeNativeAddon as default, PlatformScorer };
|
|
@@ -44,8 +44,7 @@ let _camstack_types = require("@camstack/types");
|
|
|
44
44
|
/** Section key → user-facing section title. */
|
|
45
45
|
var SECTION_TITLES = {
|
|
46
46
|
server: "Server",
|
|
47
|
-
auth: "Authentication"
|
|
48
|
-
ffmpeg: "FFmpeg"
|
|
47
|
+
auth: "Authentication"
|
|
49
48
|
};
|
|
50
49
|
/** Keys that map 1:1 to a given yml section — used by `updateGlobalSettings`
|
|
51
50
|
* to figure out which section owns each key in the incoming patch. */
|
|
@@ -53,10 +52,7 @@ var KEY_TO_SECTION = {
|
|
|
53
52
|
port: "server",
|
|
54
53
|
host: "server",
|
|
55
54
|
dataPath: "server",
|
|
56
|
-
tokenExpiry: "auth"
|
|
57
|
-
binaryPath: "ffmpeg",
|
|
58
|
-
hwAccel: "ffmpeg",
|
|
59
|
-
threadCount: "ffmpeg"
|
|
55
|
+
tokenExpiry: "auth"
|
|
60
56
|
};
|
|
61
57
|
var SystemConfigAddon = class extends _camstack_types.BaseAddon {
|
|
62
58
|
id = "system-config";
|
|
@@ -67,119 +63,56 @@ var SystemConfigAddon = class extends _camstack_types.BaseAddon {
|
|
|
67
63
|
this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
|
|
68
64
|
}
|
|
69
65
|
buildGlobalSchema() {
|
|
70
|
-
return { sections: [
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
type: "text",
|
|
86
|
-
key: "port",
|
|
87
|
-
label: "Port",
|
|
88
|
-
description: "Listening port",
|
|
89
|
-
disabled: true
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: "text",
|
|
93
|
-
key: "host",
|
|
94
|
-
label: "Host",
|
|
95
|
-
description: "Bind address",
|
|
96
|
-
disabled: true
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
type: "text",
|
|
100
|
-
key: "dataPath",
|
|
101
|
-
label: "Data Path",
|
|
102
|
-
description: "Root data directory",
|
|
103
|
-
disabled: true,
|
|
104
|
-
span: 2
|
|
105
|
-
}
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: "system-config-auth",
|
|
110
|
-
title: SECTION_TITLES["auth"],
|
|
111
|
-
description: "Token and session settings.",
|
|
112
|
-
columns: 1,
|
|
113
|
-
fields: [{
|
|
66
|
+
return { sections: [{
|
|
67
|
+
id: "system-config-server",
|
|
68
|
+
title: SECTION_TITLES["server"],
|
|
69
|
+
description: "Core server connection settings — read-only, loaded from config.yaml at bootstrap.",
|
|
70
|
+
columns: 2,
|
|
71
|
+
fields: [
|
|
72
|
+
{
|
|
73
|
+
type: "info",
|
|
74
|
+
key: "server-restart-note",
|
|
75
|
+
label: "Restart required",
|
|
76
|
+
content: "Server settings are read-only. Change them in config.yaml and restart.",
|
|
77
|
+
variant: "warning"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
114
80
|
type: "text",
|
|
115
|
-
key: "
|
|
116
|
-
label: "
|
|
117
|
-
description: "
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
label: "None (CPU only)"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
value: "videotoolbox",
|
|
154
|
-
label: "VideoToolbox (macOS)"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
value: "vaapi",
|
|
158
|
-
label: "VA-API (Linux Intel/AMD)"
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
value: "qsv",
|
|
162
|
-
label: "QSV (Intel Quick Sync)"
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
value: "cuda",
|
|
166
|
-
label: "CUDA (NVIDIA)"
|
|
167
|
-
}
|
|
168
|
-
]
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
type: "number",
|
|
172
|
-
key: "threadCount",
|
|
173
|
-
label: "Thread Count",
|
|
174
|
-
description: "0 = auto (let FFmpeg decide)",
|
|
175
|
-
min: 0,
|
|
176
|
-
max: 16,
|
|
177
|
-
step: 1,
|
|
178
|
-
default: 0
|
|
179
|
-
}
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
] };
|
|
81
|
+
key: "port",
|
|
82
|
+
label: "Port",
|
|
83
|
+
description: "Listening port",
|
|
84
|
+
disabled: true
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "text",
|
|
88
|
+
key: "host",
|
|
89
|
+
label: "Host",
|
|
90
|
+
description: "Bind address",
|
|
91
|
+
disabled: true
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: "text",
|
|
95
|
+
key: "dataPath",
|
|
96
|
+
label: "Data Path",
|
|
97
|
+
description: "Root data directory",
|
|
98
|
+
disabled: true,
|
|
99
|
+
span: 2
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}, {
|
|
103
|
+
id: "system-config-auth",
|
|
104
|
+
title: SECTION_TITLES["auth"],
|
|
105
|
+
description: "Token and session settings.",
|
|
106
|
+
columns: 1,
|
|
107
|
+
fields: [{
|
|
108
|
+
type: "text",
|
|
109
|
+
key: "tokenExpiry",
|
|
110
|
+
label: "Token Expiry",
|
|
111
|
+
description: "JWT token lifetime (e.g. 24h, 7d, 1h)",
|
|
112
|
+
placeholder: "24h",
|
|
113
|
+
default: "24h"
|
|
114
|
+
}]
|
|
115
|
+
}] };
|
|
183
116
|
}
|
|
184
117
|
async getGlobalSettings() {
|
|
185
118
|
const schema = this.buildGlobalSchema();
|