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`, `160×120`, `320×240`, `640×360`, `640×480`, `800×600`, `1280×720`
|
||||||
|
|
||||||
- `Aus` → `stream: false` = **Snapshot-Modus**: kein Video. Der Viewer zeigt stattdessen
|
- `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
|
alle 15 s ein **HD-Einzelbild** (`GET /api/snapshot/<id>/hires`, HD-Auflösung pro Kamera
|
||||||
no Video". Server-seitig öffnet jeder Snapshot das Gerät kurz (one-shot via
|
aus cameras.json) mit grossem Banner „Single Picture no Video". Server-seitig öffnet jeder
|
||||||
`grabSnapshot()`) und schliesst es wieder → Gerät meist geschlossen, minimale Mobil-
|
Grab das Gerät kurz und schliesst es wieder → Gerät meist geschlossen, minimale Mobil-
|
||||||
Bandbreite (1 JPEG/5 s statt 30/s).
|
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)
|
- 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
|
- Bei cam2 (C920) ein dezenter ⚠-Hinweis-Tooltip: „C920 braucht bei kleinen 4:3-Auflösungen
|
||||||
überdurchschnittlich Bandbreite – siehe Doku."
|
überdurchschnittlich Bandbreite – siehe Doku."
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<p class="hint">
|
<p class="hint">
|
||||||
Auflösungs-Änderung ist sofort aktiv (laufende Streams frieren kurz ein).<br>
|
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).
|
⚠ C920 (cam2) braucht bei kleinen 4:3-Auflösungen überdurchschnittlich Bandbreite (siehe doc/12).
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -38,24 +38,27 @@ function stopStream(cam) {
|
|||||||
log(cam.id, 'Live aus');
|
log(cam.id, 'Live aus');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Snapshot-Modus (stream:false): alle 5 s ein Einzelbild ──────────────────
|
// ── Snapshot-Modus (stream:false): alle 15 s ein HD-Einzelbild ──────────────
|
||||||
// Kein Video – der Server öffnet das Gerät pro Snapshot kurz (one-shot). Fehler
|
// Kein Video – pro Snapshot holt der Viewer ein Bild in HD-Auflösung (/hires,
|
||||||
// (z. B. Gerät gerade durch HD-Grab belegt) werden still übersprungen, das letzte
|
// pro Kamera in cameras.json konfiguriert). Da nur 1 Bild / 15 s übertragen wird,
|
||||||
// gute Bild bleibt stehen.
|
// ist HD hier bandbreiten-unkritisch und liefert das beste Standbild. Der Server
|
||||||
const SNAPSHOT_INTERVAL_MS = 5000;
|
// ö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) {
|
async function fetchSnapshot(cam) {
|
||||||
if (!cam.snapshotActive) return;
|
if (!cam.snapshotActive) return;
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`/api/snapshot/${encodeURIComponent(cam.id)}?t=${Date.now()}`,
|
const r = await fetch(`/api/snapshot/${encodeURIComponent(cam.id)}/hires?t=${Date.now()}`,
|
||||||
{ signal: AbortSignal.timeout(8000) });
|
{ signal: AbortSignal.timeout(20000) });
|
||||||
if (!r.ok) { setInfo(cam, `Snapshot-Fehler (HTTP ${r.status})`, 'warn'); return; }
|
if (!r.ok) { setInfo(cam, `Snapshot-Fehler (HTTP ${r.status})`, 'warn'); return; }
|
||||||
const blob = await r.blob();
|
const blob = await r.blob();
|
||||||
|
const w = r.headers.get('X-Frame-Width') || '?';
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
cam.img.src = url;
|
cam.img.src = url;
|
||||||
if (cam.lastBlobUrl) URL.revokeObjectURL(cam.lastBlobUrl);
|
if (cam.lastBlobUrl) URL.revokeObjectURL(cam.lastBlobUrl);
|
||||||
cam.lastBlobUrl = url;
|
cam.lastBlobUrl = url;
|
||||||
setInfo(cam, `Einzelbild · ${new Date().toLocaleTimeString()}`, 'ok');
|
setInfo(cam, `Einzelbild ${w}px · ${new Date().toLocaleTimeString()}`, 'ok');
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
setInfo(cam, 'Snapshot-Timeout', 'warn');
|
setInfo(cam, 'Snapshot-Timeout', 'warn');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user