This commit is contained in:
chk
2026-06-07 17:00:43 +02:00
parent 39fa6d07f5
commit d9cfa7e974
13 changed files with 744 additions and 62 deletions

View File

@@ -7,16 +7,35 @@ const path = require('path');
const { CameraSwitch } = require('./src/cameraSwitch');
const { createSnapshotRouter, createStreamRouter, createCamerasRouter } = require('./src/snapshotService');
const { createConfigRouter } = require('./src/configService');
const { resolveHwenc, mseCodecString } = require('./src/hwencode');
const PORT = parseInt(process.env.PORT ?? '8444', 10);
const LIVE_SIZE = process.env.LIVE_SIZE ?? '640x480';
const LIVE_FPS = parseInt(process.env.LIVE_FPS ?? '30', 10);
const HIRES_SIZE = process.env.HIRES_SIZE ?? '1280x960';
const HIRES_FPS = parseInt(process.env.HIRES_FPS ?? '15', 10);
const ENCODE_MODE = process.env.ENCODE_MODE ?? 'copybsf'; // 'copybsf' (niedrige CPU) | 'mjpeg' (Re-Encode-Fallback)
const ENCODE_MODE = process.env.ENCODE_MODE ?? 'copybsf'; // 'copybsf' (niedrige CPU) | 'mjpeg' (Re-Encode) | 'h264' (GPU)
const ON_DEMAND = (process.env.ON_DEMAND ?? 'true') !== 'false'; // Live nur bei Verbrauchern (spart idle-CPU)
const IDLE_GRACE_MS = parseInt(process.env.IDLE_GRACE_MS ?? '15000', 10);
// ── Hardware-Encoding (nur relevant für Kameras mit encode='h264') ────────────
// GPU=intel|amd|auto|none Maschinen-GPU (intel/amd → VAAPI, none → libx264)
// HWENC=vaapi|qsv|libx264 Encoder erzwingen (überschreibt GPU)
// HWENC_DEVICE=/dev/dri/renderD128 VAAPI/QSV-Renderknoten
const HWENC = resolveHwenc({
vendor: process.env.GPU ?? 'auto',
encoder: process.env.HWENC,
device: process.env.HWENC_DEVICE,
});
const H264 = {
bitrate: process.env.H264_BITRATE ?? '3M',
gop: process.env.H264_GOP ? parseInt(process.env.H264_GOP, 10) : undefined, // default in hwencode: ~2×fps
profile: process.env.H264_PROFILE ?? 'main',
fragMs: parseInt(process.env.H264_FRAG_MS ?? '200', 10),
jpegFps: parseInt(process.env.H264_JPEG_FPS ?? '2', 10), // Snapshot-Nebenausgang
};
const MSE_CODEC = process.env.H264_MSE_CODEC ?? mseCodecString(H264.profile, process.env.H264_LEVEL ?? '1F');
// ── cameras.json → CameraSwitch-Instanzen ─────────────────────────────────────
const CAMERAS_PATH = path.join(__dirname, 'cameras.json');
let camerasJson;
@@ -43,6 +62,7 @@ for (const cam of camsConfig) {
if (!cam.id || !cam.device) {
console.error(`cameras.json: Eintrag ohne id/device: ${JSON.stringify(cam)}`); process.exit(1);
}
const camEncode = cam.encode ?? ENCODE_MODE;
// Per-Kamera-Felder in cameras.json überschreiben die globalen Env-Werte
switches[cam.id] = new CameraSwitch({
id: cam.id, device: cam.device,
@@ -50,10 +70,11 @@ for (const cam of camsConfig) {
liveFps: cam.liveFps ?? LIVE_FPS,
hiresSize: cam.hiresSize ?? HIRES_SIZE,
hiresFps: cam.hiresFps ?? HIRES_FPS,
encode: cam.encode ?? ENCODE_MODE,
encode: camEncode,
hiresEncode: cam.hiresEncode, // undefined → fällt im Konstruktor auf encode zurück
stream: cam.stream !== false, // UI "Aus" → Kamera startet nicht live
onDemand: ON_DEMAND, idleGraceMs: IDLE_GRACE_MS,
hwenc: HWENC, h264: H264, mseCodec: MSE_CODEC, // nur für encode='h264' relevant
});
camsMeta.push({
id: cam.id,
@@ -62,6 +83,8 @@ for (const cam of camsConfig) {
position: cam.position ?? '',
stream: cam.stream !== false,
hires: cam.hires !== false,
encode: camEncode,
mseCodec: camEncode === 'h264' ? MSE_CODEC : null,
note: cam.note ?? '',
});
}
@@ -78,6 +101,7 @@ app.use('/api/config', createConfigRouter({
getCamerasJson: () => camerasJson,
setCamerasJson: (v) => { camerasJson = v; },
persist: persistCameras,
mseCodec: MSE_CODEC,
}));
app.get('/health', (_req, res) => {
@@ -107,6 +131,7 @@ server.listen(PORT, '0.0.0.0', () => {
console.log(`AppRobotWebcam http://0.0.0.0:${PORT}`);
console.log(` Kameras: ${camsMeta.map((c) => `${c.id}=${c.device} "${c.name}" stream=${c.stream}`).join(', ')}`);
console.log(` Live: ${LIVE_SIZE}@${LIVE_FPS} · HD-Grab: ${HIRES_SIZE}@${HIRES_FPS} · Encode: ${ENCODE_MODE} · On-Demand: ${ON_DEMAND}`);
console.log(` H.264-GPU: ${HWENC.encoder} @ ${HWENC.device} · ${H264.bitrate}, profile=${H264.profile}, MSE=${MSE_CODEC} (nur für encode=h264)`);
console.log(` Viewer: http://0.0.0.0:${PORT}/`);
// Live-Producer starten (Dauerbetrieb)