Pi-OS-Lite-basiertes Image für Raspberry Pi 4, das nach erstem Boot von alleine einen WebRTC-Stream der angeschlossenen Logitech-C920-Kamera serviert. Image wird via GitHub Actions in der Cloud gebaut.
# 1. Raspberry Pi OS Lite 64-bit mit Raspberry Pi Imager flashen
# Hostname: chaoscrew, SSH aktivieren, WLAN setzen
# 2. Pi booten, per SSH verbinden
ssh streamer@chaoscrew.local
# 3. Setup-Repo klonen und Installer starten
git clone https://github.com/cccdemon/RDOC-RaspiCam.git
cd RDOC-RaspiCam
sudo scripts/install-on-pi.sh
# 4. Browser
# http://chaoscrew.localDer Installer macht den bisherigen Image-Build zur Laufzeit auf dem Pi: Docker installieren, cccdemon/homecam-docker nach /opt/server-tech klonen, Pi-Overrides kopieren, MediaMTX+ffmpeg-Container bauen, systemd/udev/avahi/watchdog konfigurieren und den Stream starten.
Optional:
SERVER_TECH_REPO=cccdemon/homecam-docker SERVER_TECH_REF=main sudo -E scripts/install-on-pi.shPublic TLS via Cloudflare DNS-Challenge:
sudo nano /opt/server-tech/.env
# set:
# PUBLIC_HOSTNAME=stream.example.org
# PUBLIC_EMAIL=admin@example.org
# CLOUDFLARE_API_TOKEN=<token with Zone.DNS:Edit>
sudo systemctl restart chaoscrew-streamingThe installer builds a local Caddy image with the Cloudflare DNS plugin. DNS-01 does not require port 80 for certificate issuance; expose/forward 443 for browser access.
# 1. Image bauen (auf GitHub Actions, ~25 min)
make image
# 2. Flash-Anleitung (SD-Karte ist auf Laufwerk G:)
make flash-prep
# 3. Pi 4 booten, mit C920 verbunden
ping chaoscrew.local
# Browser: http://chaoscrew.local- Build: GitHub-Account +
ghCLI eingerichtet (oder WSL2 für lokale Fallback-Builds) - Hardware: Raspberry Pi 4 (4 oder 8 GB), aktiver Lüfter empfohlen, Logitech C920, mind. 16 GB SD-Karte (Class A1 oder besser)
- Tooling auf dem Build-Host:
gh,make, optionalwsl
Ein bootbares .img.xz-File (~ 800 MB komprimiert, ~ 3 GB entpackt) das:
- Pi OS Lite Bookworm 64-bit als Basis
- Hostname
chaoscrew, SSH aktiviert (Konfig kommt vom RPi-Imager-Preconfig-Step) - avahi/mDNS broadcastet
chaoscrew.local - Docker + Docker-Compose vorinstalliert
- Server-Tech als echtes Git-Working-Tree in
/opt/server-tech(geclont während Build auscccdemon/homecam-docker, Default-Refmain) — Updates viacd /opt/server-tech && git pull && sudo systemctl restart chaoscrew-streaming - Pi-spezifischer ffmpeg-Pfad (Hardware-Encoder
h264_v4l2m2m, 1920x1080@30, ~8 Mbps) inmediamtx.pi.template.yml - Audio-Capture vom USB-Kamera-Mikro (C920 Stereo) als Opus 96k — Default an, toggle via
AUDIO_ENABLEDin.env - Docker-Images vorgepullt im Image — kein 5-Min-Wait beim Erstboot
- systemd-Service
chaoscrew-streaming.serviceläuft beim Boot automatisch hoch - udev-Regel: bei C920-(Wieder)-Anstecken neustart des Streams
- tmpfs für
/var/log(via fstab), ext4 commit=600 — schont die SD - Hardware-Watchdog (BCM2835) aktiviert, Reboot bei Hang nach 15 s
- Optional: Caddy-Container mit Auto-TLS via
.envPUBLIC_HOSTNAME=
make image:
- Triggert das Workflow
.github/workflows/build-image.ymlaufmain. - Workflow läuft auf
ubuntu-24.04, klont pi-gen, kopiert unserestage-streaming/rein, baut. - Im Build-Stage 20 macht der pi-gen-chroot ein
git clone --depth=1 --branch=$SERVER_TECH_REFvoncccdemon/homecam-dockerdirekt nach/opt/server-tech. Standard-Ref:main. Über die Workflow-Inputsserver_tech_repo/server_tech_refkannst du beim manuellen Trigger auf einen Tag oder SHA pinnen. - Output:
chaoscrew-streaming-YYYYMMDD.img.xz+.sha256als Artifact. gh run downloadzieht das Artifact ins lokaledeploy/.
Build-Zeit auf GH-Actions-ubuntu-24.04: ~ 20–25 min (das meiste ist apt und docker pull).
Update-Workflow ohne Image-Reflash: Da /opt/server-tech ein echtes Git-Working-Tree ist, kannst du auf dem Pi:
ssh streamer@chaoscrew.local
cd /opt/server-tech && git pull
sudo systemctl restart chaoscrew-streamingBei Konflikten mit unseren Pi-Overrides (compose.override.yml, mediamtx.pi.template.yml, Caddyfile, .env, start-streaming.sh) ggf. git stash davor — die Pi-Files sind nicht im Server-tech-Repo getrackt, dürfen also nebeneinander leben.
make image-local SERVER_TECH_REPO=raumdock/Server-tech SERVER_TECH_REF=main:
- Klont pi-gen ins lokale
pi-gen/. - Pre-pullt die ARM64-Docker-Images via
tools/prepull-images.sh. - Ruft
pi-gen/build-docker.shaus WSL2 mitSERVER_TECH_REPO/SERVER_TECH_REFals Env. - Output landet in
pi-gen/deploy/, wird nachdeploy/kopiert.
Funktioniert oft, bricht manchmal (binfmt-Flakiness, chroot, CRLF). Wenn der WSL-Build streikt, GH Actions nutzen.
make flash-prep druckt die Schritt-für-Schritt-Anleitung. Kurzform:
- Raspberry Pi Imager öffnen (nicht balena-Etcher!) — der Imager kann WiFi/SSH/User direkt im Boot-FAT-Volume hinterlegen.
- CHOOSE OS → "Use custom" → das
.img.xzausdeploy/. - CHOOSE STORAGE → die SD auf Laufwerk G:.
- Strg+Shift+X für Erweitert: Hostname, SSH, WiFi, Locale.
- WRITE.
- SD in Pi 4, USB-C-Power, C920 angeschlossen, ggf. Ethernet.
- Grüne LED blinkt initial, geht nach 60–90 s in Dauer-grün — Boot komplett.
- Aus Windows:
ping chaoscrew.localantwortet. - Browser:
http://chaoscrew.local→ Stream-UI lädt, WebRTC verbindet binnen ~3 s.
ssh streamer@chaoscrew.local
systemctl status chaoscrew-streaming
docker compose -f /opt/server-tech/docker-compose.yml -f /opt/server-tech/compose.override.yml ps
v4l2-ctl --list-devicesErwartet: Service active (running), beide Container healthy, C920 in der V4L2-Liste.
/opt/server-tech/.env editieren:
PUBLIC_HOSTNAME=stream.example.org
PUBLIC_EMAIL=admin@example.org
DNS-A-Record auf eure öffentliche IP, Port 80/443 forwardet auf den Pi, dann:
sudo systemctl restart chaoscrew-streamingCaddy holt Let's-Encrypt-Cert automatisch (HTTP-01-Challenge), Stream ist von außen via https://stream.example.org erreichbar.
Pi 4 hat einen Hardware-H.264-Encoder (BCM2711 V4L2 M2M). Default-Config nutzt VIDEO_MODE=auto: Wenn die C920 einen nativen H.264-Modus anbietet, wird dieser per -c:v copy direkt durchgereicht. Sonst nutzt ffmpeg den Pi-4-Hardware-Encoder für 1920x1080@30 bei ~8 Mbps:
- CPU: ~15-25 % auf einem Core. Bleibt unter Throttle-Schwelle, aber aktive Kühlung ist trotzdem dringend empfohlen für stundenlanges Streaming.
- Profil: nicht erzwungen. Manche
h264_v4l2m2m-Builds lehnen Profilnamen wiebaselineoderconstrained_baselineab; der Encoder-Default ist für WebRTC hier stabiler. - Keine B-Frames (
-bf 0): Encoder unterstützt sie nicht. - CBR statt CRF: v4l2m2m kann nur Bitrate-basiert, kein CRF.
-b:v 8M -maxrate 8M.
Performance-Hinweis: Der Pi-Encoder beschleunigt nur das H.264-Encoding. MJPEG-Decode und Pixel-Format-Konvertierung bleiben CPU-Arbeit. Für die niedrigste CPU-Last ist VIDEO_MODE=camera_h264 ideal, weil die C920 dann H.264 selbst liefert und ffmpeg nur noch remuxt.
- Gelegentliches Banding auf flächigen Farben (graue Wand, blauer Himmel)
- Sporadische GOP-Glitches bei abrupten Bewegungen
- Pi-Foundation hat den Encoder als "deprecated" markiert — wird aber im Pi-OS-64-Kernel 6.x weiter ausgeliefert.
Für LAN-Live-Streaming akzeptabel. Für Studio-Recording → drop zu Software-libx264 (siehe Fallback unten).
Default: an, Codec Opus 96k Stereo, gecaptured von der ALSA-Karte der Kamera.
Warum Opus statt AAC: WebRTC kann nativ Opus → kein Transcode-Hop in MediaMTX (was sonst CPU kosten würde). Opus läuft auch in HLS-fmp4 und allen modernen Browsern.
Detection in start-streaming.sh prepare:
arecord -l | grep "HD Pro Webcam C920" # exact match
arecord -l | grep "USB" # fallback: any USB captureFindet's nichts trotz AUDIO_ENABLED=true → Warning im Log, Stream läuft Video-only weiter.
Toggle:
ssh streamer@chaoscrew.local
sudo sed -i 's/^AUDIO_ENABLED=.*/AUDIO_ENABLED=false/' /opt/server-tech/.env
sudo systemctl restart chaoscrew-streamingLatenz: Opus-Encoding fügt ~5-20 ms zur Audio-Pipeline. WebRTC-Total-Latenz bleibt unter 1 s.
Container-Anforderung: /dev/snd ist im compose.override.yml als Device-Passthrough deklariert. Wenn du den Stack händisch ohne Override startest, fehlt Audio.
/opt/server-tech/mediamtx.pi.template.yml editieren — am Ende ist der libx264-ultrafast-Block auskommentiert (720p@30, ~4 Mbps, ~50 % CPU). Den oberen v4l2m2m-Block auskommentieren, den unteren aktivieren, dann:
sudo systemctl restart chaoscrew-streamingOptional kannst du auch die /dev/video10..13 Passthroughs in compose.override.yml rauskommentieren — sie sind im Software-Modus nicht nötig.
| Symptom | Schau hier nach |
|---|---|
chaoscrew.local nicht erreichbar |
Pi mit Ethernet-Kabel an Router stecken; mDNS auf manchen Switches deaktiviert. arp -a → Pi-IP finden. |
chaoscrew-streaming startet nicht |
journalctl -u chaoscrew-streaming -f |
| Kamera nicht erkannt | lsusb | grep 046d:082d, v4l2-ctl --list-devices. C920 USB-Kabel direkt am Pi (nicht über Hub). |
| Stream stottert / Encoder-Fehler im Log | vcgencmd measure_temp — sollte unter 70 °C bleiben. Active-Cooling-Lüfter dazu. Falls trotzdem Glitches: auf Software-libx264-Fallback wechseln (siehe unten). |
| ffmpeg meldet "Cannot open V4L2 M2M codec" | Kernel-Modul bcm2835-codec fehlt oder ist disabled. lsmod | grep bcm2835_codec. Ist ab Bookworm Default aktiv. Wenn weg, im RaspiImage neu bauen. |
| Encoder-Banding auf einfarbigen Flächen | v4l2m2m-Eigenheit. Bitrate auf 12 Mbps anheben (mediamtx.pi.yml -b:v 12M) oder Software-Fallback nutzen. |
| Kein Audio im Browser, obwohl AUDIO_ENABLED=true | journalctl -u chaoscrew-streaming | grep Audio. Wahrscheinlich keine ALSA-Karte gefunden — arecord -l ssh'en und checken. C920 muss vor Service-Start eingesteckt sein, sonst greift die udev-Rebind-Regel. |
| Audio asynchron zum Video | mediamtx.pi.yml runOnInit: editieren, -itsoffset 0.2 (Audio 200 ms verzögert) bzw. -itsoffset -0.2 (Video verzögern). USB-Bus-Sync-Drift ist Hardware-abhängig. |
| Watchdog rebootet ständig | journalctl -k | grep -i watchdog. Normalerweise heißt das: Service hängt. systemctl disable streaming.watchdog als Notfall. |
| Caddy bekommt kein Cert | Port 80 erreichbar von außen? curl -vI http://<public-ip> aus dem Internet. ACME-Email gesetzt? |
RaspiImage/
├── Makefile # Build + Flash-Helper
├── README.md # diese Datei
├── pi-gen.config # pi-gen top-level config + SERVER_TECH_REPO/REF
├── .github/workflows/
│ └── build-image.yml # GH Actions pi-gen build
├── tools/
│ └── prepull-images.sh # docker pull ARM64 images → save to images.tar
├── stage-streaming/ # custom pi-gen stage on top of stage2 Lite
│ ├── prerun.sh
│ ├── EXPORT_IMAGE # marks this stage as image-producing
│ ├── 00-install-system/ # apt packages (incl. git, alsa-utils), avahi, systemd, udev, motd
│ ├── 10-install-docker/ # Docker + Compose plugin
│ ├── 20-install-streaming/ # git-clone Server-tech in chroot + Pi-Overrides
│ └── 30-finalize/ # commit=600, watchdog, swap-off, BT/UART aus
└── deploy/ # Build-Output (gitignored)
AGPL-3.0-or-later. Server-Tech-Snapshot folgt der Server-Tech-Lizenz.