Files
webcam-server/README.md
T

147 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Webcam
![Webcam Server – Python MJPEG-Relay für ESP32-CAM und Raspberry-Pi-Kameras](picture/webcam-server.png)
## Das Problem – und die Lösung
Günstige Netzwerkkameras wie ESP32-CAM oder Raspberry-Pi-Kameras liefern ihren Stream nur per **unverschlüsseltem HTTP** und hängen im **privaten Netz** (z. B. `192.168.x.x`). Direkt ins Internet gehören sie nicht: kein HTTPS, keine Anmeldung, und schon wenige Zuschauer bringen einen ESP32 an seine Grenzen. Außerdem blockiert der Browser HTTP-Streams auf HTTPS-Seiten (Mixed Content).
Dieses Projekt löst das mit einem kleinen Relay:
```
Kamera (HTTP, privates Netz) → relay.py → Zoraxy (HTTPS, Login) → Browser im Internet
```
- Die Kameras bleiben im Heimnetz und sind von außen nie direkt erreichbar. Nach außen sichtbar ist nur der Relay.
- **HTTPS** übernimmt der Reverse Proxy [Zoraxy](https://github.com/tobychui/zoraxy) (Zertifikat, Domain). Der Relay selbst spricht nur HTTP.
- **Zugriffsschutz** per Basic-Auth (`AUTH_USER` / `AUTH_PASS`) oder in Zoraxy.
- Jede Kamera wird nur **einmal** abgeholt, egal wie viele Leute zuschauen.
- Das Web-Frontend zeigt alle Kameras mit Status, Foto, Vollbild und Start/Stop. Die Kamera-Einstellung (z. B. Auflösung) lässt sich dort ebenfalls auslösen, ohne dass der Browser die Kamera direkt ansprechen muss.
> Ohne `AUTH_USER` / `AUTH_PASS` (oder einen Login in Zoraxy) kann jeder, der die Adresse kennt, die Kameras sehen. Für den Betrieb im Internet also immer einen Login einrichten.
## Bestandteile
Zwei kleine Python-Tools für MJPEG-Kameras:
- **`relay.py`** – holt den MJPEG-Stream jeder Kamera nur **einmal** ab und stellt ihn für beliebig viele Zuschauer per HTTP bereit (gedacht für den Betrieb hinter einem Reverse Proxy wie Zoraxy).
- **`webcam.py`** – Kommandozeilenwerkzeug, um einen MJPEG-Stream zu testen, Einzelbilder/Frames zu speichern oder live anzuzeigen.
## Voraussetzungen
- Python 3
- [`requests`](https://pypi.org/project/requests/)
- Optional für `webcam.py --show`: `opencv-python` und `numpy`
```bash
pip install requests
pip install opencv-python numpy # nur für --show
```
## Relay (`relay.py`)
### Konfiguration
Die Konfiguration erfolgt über eine `.env`-Datei neben `relay.py` oder über Umgebungsvariablen (echte Umgebungsvariablen haben Vorrang).
```bash
cp .env.example .env
```
| Variable | Beschreibung | Standard |
|---|---|---|
| `CAMERA_<name>` | Stream-URL einer Kamera, eine Zeile pro Kamera. Der Name wird Teil der URL (`/cam1/`). | – (mindestens eine nötig) |
| `SETUP_<name>` | Optional: Konfigurations-URL der Kamera (z. B. `http://IP/control?var=framesize&val=11`). Das Relay ruft sie auf, wenn im Frontend „Setup“ geklickt wird. | – |
| `PORT` | Port, auf dem der Relay lauscht | `8000` |
| `AUTH_USER` / `AUTH_PASS` | Optional: Basic-Auth. Nur sinnvoll mit HTTPS (z. B. in Zoraxy). | aus |
Beispiel:
```ini
CAMERA_cam1=http://192.168.1.8:8080/stream.mjpg
CAMERA_cam2=http://192.168.1.9:8080/stream.mjpg
PORT=8000
#AUTH_USER=benutzername
#AUTH_PASS=geheim
```
> `.env` enthält ggf. Zugangsdaten – nicht ins Repository einchecken.
### Starten
```bash
python3 relay.py
```
### Als systemd-Dienst
`webcam-relay.service` liegt im Repository. Pfade und Benutzer (`/home/joachim/git/webcam`, `joachim`) bei Bedarf in der Datei anpassen.
Der Dienst nutzt das System-Python (`/usr/bin/python3`). Dort muss `requests` installiert sein, auch wenn es z. B. in Conda oder einem venv schon vorhanden ist:
```bash
sudo apt install python3-requests
```
```bash
sudo cp webcam-relay.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now webcam-relay
```
Status und Logs:
```bash
systemctl status webcam-relay
journalctl -u webcam-relay -f
```
### Endpunkte
| Pfad | Beschreibung |
|---|---|
| `/` | Web-Frontend: Übersicht aller Kameras |
| `/<name>/` | Web-Frontend: große Ansicht einer Kamera, die anderen als Vorschau-Leiste |
| `/status.json` | Online-Status aller Kameras (wird vom Frontend alle 5 s abgefragt) |
| `/<name>/stream.mjpg` | Livestream (`multipart/x-mixed-replace`) |
| `/<name>/snapshot.jpg` | Aktuelles Einzelbild |
| `/stream.mjpg`, `/snapshot.jpg` | Alias für die erste Kamera (Abwärtskompatibilität) |
| `/health` | Status aller Kameras (`200` wenn alle liefern, sonst `503`); ohne Authentifizierung erreichbar |
### Web-Frontend
Das Frontend liegt in `frontend.html` (reines HTML/CSS/JS, keine Abhängigkeiten) und wird vom Relay ausgeliefert. Änderungen an der Datei wirken sofort, ohne Neustart.
- Dunkles Design, zwei Kameras nebeneinander (auf dem Handy untereinander); Klick auf den Namen öffnet die große Ansicht
- Statuspunkt und Hinweis „Kamera offline“ pro Kamera, automatisches Neuverbinden, Zähler „X / Y online“
- Buttons pro Kamera: Start, Stop, Reload, Foto (Download), Vollbild
- Toolbar: Alle starten / stoppen / neu laden
- „Setup“ pro Kamera und „Setup alle“, sofern `SETUP_<name>` konfiguriert ist; das Relay ruft die URL auf, der Browser muss die Kamera nicht erreichen
- Streams werden getrennt, solange der Tab im Hintergrund ist
- Alle Links sind relativ, daher funktioniert es auch unter einem Proxy-Prefix wie `/webcam/`
### Funktionsweise
- Pro Kamera läuft ein Hintergrund-Thread, der den Stream liest und die JPEG-Frames anhand der Marker `FFD8`/`FFD9` ausschneidet.
- Bei Verbindungsfehlern wird nach 3 Sekunden automatisch neu verbunden.
- Alle Zuschauer bekommen jeweils das neueste Frame – die Kamera wird unabhängig von der Zuschauerzahl nur einmal belastet.
- Der Header `X-Accel-Buffering: no` verhindert Pufferung durch den Proxy.
## Stream-Tool (`webcam.py`)
```bash
python3 webcam.py # Verbindung testen, Frame-Infos ausgeben
python3 webcam.py --snapshot bild.jpg # ein Einzelbild speichern
python3 webcam.py --save frames/ -n 50 # 50 Frames als JPEG speichern
python3 webcam.py --show # Live-Fenster (q oder Esc beendet)
python3 webcam.py --url http://IP:PORT/stream.mjpg
```
| Option | Beschreibung |
|---|---|
| `--url` | Stream-URL (Standard ist in `webcam.py` hinterlegt) |
| `--snapshot DATEI` | Ein Frame speichern und beenden |
| `--save ORDNER` | Frames im Ordner speichern |
| `-n N` | Nach N Frames beenden (`0` = endlos) |
| `--show` | Live-Fenster anzeigen (OpenCV) |