Claude: Phase 1 ScreenShot OK
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
# AppRobotWebcam – Hi-Res-Snapshot via Consumer-Umhängen
|
# AppRobotWebcam – Hi-Res-Snapshot via Consumer-Umhängen
|
||||||
|
|
||||||
> Status: **Phase 1 implementiert** (Code steht, Messung an der Live-Instanz steht
|
> Status: **Phase 1 abgeschlossen** (2026-06-04):
|
||||||
> noch aus). Phase 2 weiterhin Konzept.
|
> **Linchpin beantwortet: `freed: true`.** go2rtc gibt das Gerät frei, sobald der letzte
|
||||||
|
> Consumer weg ist. Zwei Folgeprobleme blockieren Phase 2: Stream erholt sich nicht
|
||||||
|
> (bleibt schwarz), und go2rtc läuft nach dem Test auf 106 % (erfordert Container-Recreate).
|
||||||
> Vorgeschichte & gescheiterte Ansätze: siehe `04_Delay_roadmap.md` (Abschnitt
|
> Vorgeschichte & gescheiterte Ansätze: siehe `04_Delay_roadmap.md` (Abschnitt
|
||||||
> „KONSOLIDIERT"). Diese Datei beschreibt den Ansatz, der die dort dokumentierten
|
> „KONSOLIDIERT"). Diese Datei beschreibt den Ansatz, der die dort dokumentierten
|
||||||
> Fehler **strukturell** umgeht.
|
> Fehler **strukturell** umgeht.
|
||||||
@@ -139,9 +141,12 @@ im schlimmsten Fall ist es ein Reconnect von cam0.
|
|||||||
|
|
||||||
### Was wir daraus lernen
|
### Was wir daraus lernen
|
||||||
- `msUntilFree` → der reale Pausenwert für Schritt 3/5 (statt der geratenen 4 s).
|
- `msUntilFree` → der reale Pausenwert für Schritt 3/5 (statt der geratenen 4 s).
|
||||||
- Wird der Producer **nicht** gestoppt (`freed: false`): go2rtc hält das Gerät warm →
|
- **Gemessen (2026-06-04):** `freed: true`, `msUntilFree: 0`, `zeroConsumerAt: 4850 ms`.
|
||||||
Ansatz so nicht tragfähig → prüfen, ob ein go2rtc-Setting das Verhalten ändert,
|
go2rtc hält das Gerät **nicht** warm — es stoppt den Producer sofort wenn der letzte
|
||||||
sonst zurück zu Weg A (separate Kamera, siehe `04_*`).
|
Consumer weg ist. Der Ansatz ist bestätigt.
|
||||||
|
- Würde der Producer nicht gestoppt (`freed: false`): go2rtc hält das Gerät warm →
|
||||||
|
Ansatz so nicht tragfähig → zurück zu Weg A (separate Kamera, siehe `04_*`). Trat
|
||||||
|
**nicht** ein.
|
||||||
|
|
||||||
> ⚠ Die genaue JSON-Form von `/api/streams` (Felder `producers`/`consumers`/`state`)
|
> ⚠ Die genaue JSON-Form von `/api/streams` (Felder `producers`/`consumers`/`state`)
|
||||||
> vor dem Bauen kurz an der echten Instanz ansehen (`curl -s localhost:1984/api/streams`)
|
> vor dem Bauen kurz an der echten Instanz ansehen (`curl -s localhost:1984/api/streams`)
|
||||||
@@ -149,9 +154,82 @@ im schlimmsten Fall ist es ein Reconnect von cam0.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Erster Live-Test (2026-06-04) — INCONCLUSIVE (nicht widerlegt, ersetzt durch zweiten)
|
||||||
|
|
||||||
|
Antwort: `{ freed: false, zeroConsumerAt: null, producerStoppedAt: null }`.
|
||||||
|
|
||||||
|
**Richtig gelesen:** `zeroConsumerAt: null` = cam0 hatte während der vollen 10 s Messung
|
||||||
|
**nie 0 Consumer**. Damit wurde die eigentliche Linchpin-Frage (*stoppt der Producer bei
|
||||||
|
0 Consumern?*) **gar nicht getestet** — der Vorzustand „0 Consumer" wurde nie erreicht.
|
||||||
|
Der Test ist **ergebnislos, nicht widerlegend.** (Frühere Notiz „Producer warm gehalten →
|
||||||
|
Ansatz tot" war voreilig und ist zurückgezogen.)
|
||||||
|
|
||||||
|
**CPU-Spike 106 % war transient.** Direkt nach Disconnect→Reconnect kurz 106 %, danach
|
||||||
|
von selbst auf **23 %** zurück (`docker stats AppRobotGo2RTC`). `curl /api/streams` direkt
|
||||||
|
danach zeigte einen **sauberen Zustand**: je **1** mjpeg-Producer (`-c:v mjpeg`, 640×480)
|
||||||
|
+ **1** Consumer pro Kamera — **kein** doppelter Producer, **kein** H.264. Der Test hat
|
||||||
|
**keinen kaputten State hinterlassen**; cam0/cam1 unangetastet. Read-only-Pfad bestätigt.
|
||||||
|
|
||||||
|
**Warum nie 0 Consumer? — vor dem Weiterbauen klären:**
|
||||||
|
- **Verdacht A — zweiter Consumer:** weiterer Browser-Tab, oder die go2rtc-Debug-UI auf
|
||||||
|
`:1984` zeigte cam0 (= persistenter Consumer). Frühes Monitor-Log zeigte `consumers → 2`.
|
||||||
|
- **Verdacht B — Abmelde-Lag:** `el.remove()` schließt die WS (Browser-Log zeigt
|
||||||
|
`stream.onclose`), go2rtc meldet den Consumer aber nicht (rechtzeitig) als entfernt.
|
||||||
|
- **Datenquelle:** das `samples`-Array der Antwort (Consumer-Zahl alle 200 ms) zeigt es
|
||||||
|
exakt — beim nächsten Lauf auswerten (F12-Console: geloggtes `release-test JSON` aufklappen).
|
||||||
|
|
||||||
|
**Nächster Schritt (tragfähig):** sicherstellen, dass **nur ein** Consumer total existiert
|
||||||
|
(alle anderen Tabs + go2rtc-UI schließen) und der Feed auf Klick **vollständig** beendet
|
||||||
|
wird, dann neu messen. Erst wenn die Consumer-Zahl nachweislich auf 0 fällt (`samples`
|
||||||
|
zeigt `consumers: 0`), ist die Linchpin-Frage beantwortbar. Fällt sie dann auf 0 und der
|
||||||
|
Producer stoppt → `freed: true` → Phase 2. Stoppt der Producer trotz 0 Consumern nicht →
|
||||||
|
*dann erst* ist der Ansatz widerlegt → Weg A (separate Kamera, `04_*`).
|
||||||
|
|
||||||
|
## Zweiter Live-Test (2026-06-04) — ✅ Phase 1 abgeschlossen
|
||||||
|
|
||||||
|
Kamera: cam1. Alle anderen Consumer geschlossen (offener Tab an unerwartetem Ort gefunden
|
||||||
|
und geschlossen). Test mit einem einzigen Browser-Tab durchgeführt.
|
||||||
|
|
||||||
|
**Ergebnis:**
|
||||||
|
```json
|
||||||
|
{ "id": "cam1", "freed": true, "msUntilFree": 0,
|
||||||
|
"zeroConsumerAt": 4850, "producerStoppedAt": 4850 }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Was das bedeutet:**
|
||||||
|
- **`freed: true` → Linchpin hält.** go2rtc gibt das Gerät frei, sobald der letzte
|
||||||
|
Consumer weg ist. Der Grundansatz ist tragfähig.
|
||||||
|
- **`msUntilFree: 0`** — Producer stoppt **gleichzeitig** mit dem letzten Consumer-Abgang
|
||||||
|
(kein „warm halten"). Die Pause für Phase 2 kann sehr kurz gewählt werden.
|
||||||
|
- **`zeroConsumerAt: 4850ms`** — es dauert ~5 s, bis go2rtc den WS-Consumer nach
|
||||||
|
`el.remove()` als entfernt registriert. Das ist der Wert, der in Schritt 3 als Pause
|
||||||
|
gebraucht wird: **Browser muss cam0 loslassen und dann ~5 s warten**, bevor der
|
||||||
|
Hi-Res-Grab starten kann. (Nicht 4 s wie geraten — real ~5 s, vermutlich ein go2rtc
|
||||||
|
internen Timeout.)
|
||||||
|
|
||||||
|
**Bug 1 — Stream blieb schwarz (behoben, Ursache: zweiter Tab):**
|
||||||
|
`startStream()` im `finally` feuerte `stream.onopen`, danach sofort `ondisconnect →
|
||||||
|
Video-Fehler: MEDIA_ELEMENT_ERROR: Empty src attribute → onclose`. Ursache war ein
|
||||||
|
**zweiter offener Browser-Tab** an unerwartetem Ort. Mit genau einem Tab **kommt der
|
||||||
|
Stream korrekt in der 640er-Auflösung zurück.** Kein Code-Fix nötig.
|
||||||
|
|
||||||
|
**Bug 2 — CPU 106 % nach Test (behoben, Ursache: zweiter Tab):**
|
||||||
|
Direkt nach dem ersten Test 106 %, erforderte Container-Recreate. Nach Schließen des
|
||||||
|
zweiten Tabs und erneutem Test: CPU geht danach auf ~40 % zurück, stabil. Ursache war
|
||||||
|
der zweite Consumer, der go2rtc in einen unklaren Zustand beim Reconnect trieb.
|
||||||
|
**Mit einem Tab: kein CPU-Problem. Kein Code-Fix nötig für Single-Operator-Betrieb.**
|
||||||
|
|
||||||
|
> ⚠ Einschränkung bleibt: **Immer nur ein Tab/Client pro Kamera**, wenn der HD-Test
|
||||||
|
> läuft — sonst fällt der Consumer-Count nie auf 0 und der Test schlägt fehl. Für
|
||||||
|
> Single-Operator-Betrieb (Button auf Anforderung) ist das akzeptiert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## PHASE 2 — Den Hi-Res-Grab ergänzen (Schritt 4 + 5)
|
## PHASE 2 — Den Hi-Res-Grab ergänzen (Schritt 4 + 5)
|
||||||
|
|
||||||
Nur starten, **wenn Phase 1 `freed: true` geliefert hat.**
|
Phase 1 hat `freed: true` geliefert. **Phase 2 kann gestartet werden.**
|
||||||
|
Realer Pausenwert aus der Messung: `zeroConsumerAt: 4850 ms` → Schritt 3/5 mit **5 s**
|
||||||
|
planen (statt der geratenen 4 s).
|
||||||
|
|
||||||
### Vorbereitung (Config, per Redeploy – nicht zur Laufzeit)
|
### Vorbereitung (Config, per Redeploy – nicht zur Laufzeit)
|
||||||
`docker-compose.yaml`, go2rtc-`streams` ergänzen:
|
`docker-compose.yaml`, go2rtc-`streams` ergänzen:
|
||||||
|
|||||||
Reference in New Issue
Block a user