Standbild HiRes
This commit is contained in:
@@ -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/<id>`) 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/<id>/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/<id>` (z. B. fürs Homing) bleibt unverändert (Live-Frame bzw. one-shot
|
||||
via `grabSnapshot()` an `liveSize`).
|
||||
- Auflösung → `stream: true` + `liveSize: "<W>x<H>"` (kontinuierlicher MJPEG-Stream)
|
||||
- Bei cam2 (C920) ein dezenter ⚠-Hinweis-Tooltip: „C920 braucht bei kleinen 4:3-Auflösungen
|
||||
überdurchschnittlich Bandbreite – siehe Doku."
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<p class="hint">
|
||||
Auflösungs-Änderung ist sofort aktiv (laufende Streams frieren kurz ein).<br>
|
||||
„Aus" schaltet in den Snapshot-Modus: kein Video, alle 5 s ein Einzelbild im Viewer.<br>
|
||||
„Aus" schaltet in den Snapshot-Modus: kein Video, alle 15 s ein HD-Einzelbild im Viewer.<br>
|
||||
⚠ C920 (cam2) braucht bei kleinen 4:3-Auflösungen überdurchschnittlich Bandbreite (siehe doc/12).
|
||||
</p>
|
||||
</main>
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user