diff --git a/doc/12_cameraConfig_roadmap.md b/doc/12_cameraConfig_roadmap.md index 40e3a0c..b5babd8 100644 --- a/doc/12_cameraConfig_roadmap.md +++ b/doc/12_cameraConfig_roadmap.md @@ -76,10 +76,12 @@ Separate Admin-Seite (kein Viewer-Umbau nötig). Pro Kamera eine Zeile mit Combo `Aus`, `160×120`, `320×240`, `640×360`, `640×480`, `800×600`, `1280×720` - `Aus` → `stream: false` = **Snapshot-Modus**: kein Video. Der Viewer zeigt stattdessen - alle 5 s ein Einzelbild (`GET /api/snapshot/`) mit grossem Banner „Single Picture - no Video". Server-seitig öffnet jeder Snapshot das Gerät kurz (one-shot via - `grabSnapshot()`) und schliesst es wieder → Gerät meist geschlossen, minimale Mobil- - Bandbreite (1 JPEG/5 s statt 30/s). + alle 15 s ein **HD-Einzelbild** (`GET /api/snapshot//hires`, HD-Auflösung pro Kamera + aus cameras.json) mit grossem Banner „Single Picture no Video". Server-seitig öffnet jeder + Grab das Gerät kurz und schliesst es wieder → Gerät meist geschlossen, minimale Mobil- + Bandbreite (1 HD-JPEG / 15 s statt 30 Frames/s). Der generische Endpunkt + `GET /api/snapshot/` (z. B. fürs Homing) bleibt unverändert (Live-Frame bzw. one-shot + via `grabSnapshot()` an `liveSize`). - Auflösung → `stream: true` + `liveSize: "x"` (kontinuierlicher MJPEG-Stream) - Bei cam2 (C920) ein dezenter ⚠-Hinweis-Tooltip: „C920 braucht bei kleinen 4:3-Auflösungen überdurchschnittlich Bandbreite – siehe Doku." diff --git a/public/config.html b/public/config.html index ed1749f..19c41bc 100644 --- a/public/config.html +++ b/public/config.html @@ -29,7 +29,7 @@

Auflösungs-Änderung ist sofort aktiv (laufende Streams frieren kurz ein).
- „Aus" schaltet in den Snapshot-Modus: kein Video, alle 5 s ein Einzelbild im Viewer.
+ „Aus" schaltet in den Snapshot-Modus: kein Video, alle 15 s ein HD-Einzelbild im Viewer.
⚠ C920 (cam2) braucht bei kleinen 4:3-Auflösungen überdurchschnittlich Bandbreite (siehe doc/12).

diff --git a/public/viewer.js b/public/viewer.js index 34079e3..95019bd 100644 --- a/public/viewer.js +++ b/public/viewer.js @@ -38,24 +38,27 @@ function stopStream(cam) { log(cam.id, 'Live aus'); } -// ── Snapshot-Modus (stream:false): alle 5 s ein Einzelbild ────────────────── -// Kein Video – der Server öffnet das Gerät pro Snapshot kurz (one-shot). Fehler -// (z. B. Gerät gerade durch HD-Grab belegt) werden still übersprungen, das letzte -// gute Bild bleibt stehen. -const SNAPSHOT_INTERVAL_MS = 5000; +// ── Snapshot-Modus (stream:false): alle 15 s ein HD-Einzelbild ────────────── +// Kein Video – pro Snapshot holt der Viewer ein Bild in HD-Auflösung (/hires, +// pro Kamera in cameras.json konfiguriert). Da nur 1 Bild / 15 s übertragen wird, +// ist HD hier bandbreiten-unkritisch und liefert das beste Standbild. Der Server +// öffnet das Gerät pro Grab kurz und schliesst es wieder. Fehler (z. B. Gerät +// gerade belegt) werden still übersprungen, das letzte gute Bild bleibt stehen. +const SNAPSHOT_INTERVAL_MS = 15000; async function fetchSnapshot(cam) { if (!cam.snapshotActive) return; try { - const r = await fetch(`/api/snapshot/${encodeURIComponent(cam.id)}?t=${Date.now()}`, - { signal: AbortSignal.timeout(8000) }); + const r = await fetch(`/api/snapshot/${encodeURIComponent(cam.id)}/hires?t=${Date.now()}`, + { signal: AbortSignal.timeout(20000) }); if (!r.ok) { setInfo(cam, `Snapshot-Fehler (HTTP ${r.status})`, 'warn'); return; } const blob = await r.blob(); + const w = r.headers.get('X-Frame-Width') || '?'; const url = URL.createObjectURL(blob); cam.img.src = url; if (cam.lastBlobUrl) URL.revokeObjectURL(cam.lastBlobUrl); cam.lastBlobUrl = url; - setInfo(cam, `Einzelbild · ${new Date().toLocaleTimeString()}`, 'ok'); + setInfo(cam, `Einzelbild ${w}px · ${new Date().toLocaleTimeString()}`, 'ok'); } catch (_e) { setInfo(cam, 'Snapshot-Timeout', 'warn'); }