Technik

Webcam

Die Webcam ist auf dem Dach des Clubheimes montiert. Es handelt sich um einen Raspberry (Modell B) inklusive Kameramodul. Montiert ist das ganze wetterfest in eine Überwachungskamera-Attrappe. Die Kamera streamt mit 720p (1280×720) Auflösung durch ein VPN-Verbindung zum Webserver. Als Datenrate hat sich 1Mbit als vernünftiger Kompromiss ergeben. Für den reibungslosen Ablauf sorgt eine Reihe von shell und php Scripten.

/opt/vc/bin/raspivid -o - -t 0 -w 1280 -h 720 -fps 20 -b 1000000 -md 2 -g 100|nc 10.0.8.1 2222

Auf dem Server wartet vlc auf den Stream:

nc -l -s 10.0.8.1 -p 2222|vlc -I "dummy" stream:///dev/stdin :sout="#std{access=livehttp{seglen=10,delsegs=true,numsegs=5, index=/var/www/vhosts/sgecam.de/httpdocs/stream/stream.m3u8, index-url=https://sgecam.de/stream/stream-########.ts}, mux=ts{use-key-frames}, dst=/var/www/vhosts/sgecam.de/httpdocs/stream/stream-########.ts}" :demux=h264 vlc://quit

Der vlc schreibt den stream häppchenweise in einen Speicher. Dazu legt er eine dynamische Playlist an. Der Zugriff auf diese Dateien erfolgt dann über den Apache Webserver. Damit wird das Problem umgangen das der VLC zwar auch Webserver spielen kann, aber kein vernünftiges Logging etc. hat. Außerdem läuft dann nach außen alles durch einen Server. Die Streamadresse lautet:  https://www.sgecam.de/stream/stream.m3u8

Okay, das ganze ist etwas komplizierter. Zum einen müssen Verbindungsabbrüche erkannt und gemanagt werden. Dann haben wir noch das Problem mit der Helligkeit. Es macht einfach keinen Sinn einen pechschwarzen Stream zu senden, und am Dechsi ist es nachts wirklich dunkel. Dann zeigt die Webseite einfach das letzte vernünftige Bild an.

Es hat sich jedoch herausgestellt, das bei Standbildern auch bei wesentlich längerer Belichtungszeit die Kamera vernünftige Fotos macht. Wenn es zu dunkel für „normale“ Bilder ist wird an den Kameraparametern gedreht, um über eine längere Belichtungszeit doch noch ein vernünftiges Foto hinzubekommen. Wenn die Bilder ohne Tricks hell genug sind wird der Stream gestartet.

Raspivid wird übrigens automatisch beendet wenn die Netcat-Verbindung zum Server zusammenbricht. Dann geht alles von vorne los.

#!/bin/bash
SAVEDIR=/ramdisc
cd /home/pi
while [ true ]; do
echo Start
# Ein Bild machen...
/opt/vc/bin/raspistill -n -o $SAVEDIR/out.jpg -w 2592 -h 1458
time /usr/bin/php5 webcam.php
CODE=$?

rm $SAVEDIR/out.jpg
echo $CODE
if [ $CODE -ge 1 ]
then
 echo Dunkel $CODE
 # Bild war zu dunkel...
 C=$(($CODE*100000))
 echo $C
 # Neues Bild mit längerer Belichtung...
 time /opt/vc/bin/raspistill -n -ss $C -ISO 100 -o $SAVEDIR/out.jpg -w 25 92 -h 1458
 /usr/bin/php5 webcam.php
 rm $SAVEDIR/out.jpg
else
 # Bild war hell genug
 # Jetzt wird versucht den Stream zu starten...
 echo Stream start
 sleep 0.1
 /opt/vc/bin/raspivid -o - -t 0 -w 1280 -h 720 -fps 20 -b 1000000 -md 2 - g 100|nc 10.0.8.1 2222
 echo "Stream stopped (keine Verbindung oder Server meint zu dunkel)"
fi

done;#!/bin/bash
SAVEDIR=/ramdisc
cd /home/pi
while [ true ]; do
echo Start
# Ein Bild machen...
/opt/vc/bin/raspistill -n -o $SAVEDIR/out.jpg -w 2592 -h 1458
time /usr/bin/php5 webcam.php
CODE=$?

rm $SAVEDIR/out.jpg
echo $CODE
if [ $CODE -ge 1 ]
then
 echo Dunkel $CODE
 # Bild war zu dunkel...
 C=$(($CODE*100000))
 echo $C
 # Neues Bild mit längerer Belichtung...
 time /opt/vc/bin/raspistill -n -ss $C -ISO 100 -o $SAVEDIR/out.jpg -w 25 92 -h 1458
 /usr/bin/php5 webcam.php
 rm $SAVEDIR/out.jpg
else
 # Bild war hell genug
 # Jetzt wird versucht den Stream zu starten...
 echo Stream start
 sleep 0.1
 /opt/vc/bin/raspivid -o - -t 0 -w 1280 -h 720 -fps 20 -b 1000000 -md 2 - g 100|nc 10.0.8.1 2222
 echo "Stream stopped (keine Verbindung oder Server meint zu dunkel)"
fi

done;

Da beim Raspberry hierbei die h.264 Kompression in der GPU gerechnet wird ist die Belastung der CPU bei etwa 1% durch den Stream. Die OpenVPN-Verbindung zum Server braucht etwa 13% CPU

Auf der anderen Seite wartet vlc auf den eingehenden Stream:

#!/bin/bash
rm /var/www/vhosts/sgecam.de/httpdocs/stream/stream-*.ts
while true; do
 nc -l -s 10.0.8.1 -p 2222|vlc -I "dummy" stream:///dev/stdin :sout="#std{access=livehttp{seglen=10,delsegs=true,numsegs=5, index=/var/www/vhosts/sgecam.de/httpdocs/stream/stream.m3u8, index-url=https://sgecam.de/stream/stream-########.ts}, mux=ts{use-key-frames}, dst=/var/www/vhosts/sgecam.de/httpdocs/stream/stream-########.ts}" :demux=h264 vlc://quit
 sleep 2
 # Alte streams löschen damit Dunkelheitserkennung richtige Datei aussucht
 rm /var/www/vhosts/sgecam.de/httpdocs/stream/stream-*.ts
done

Das Zielverzeichnis liegt übrigends in einer Ramdisk, die 5MB Ringpuffer lohnen sich nicht auf die Festplatte zu schreiben.

Auf dem Server wird jede Minute per cronjob ein Bild gemacht:

#!/bin/bash
cd /var/www/vhosts/sgecam.de/httpdocs/webcam
 rm webcam.jpg
 file=`ls /var/www/vhosts/sgecam.de/httpdocs/stream/*.ts | tail -2 | head -1`
 ffmpeg -i $file -vframes 1 webcam.jpg
 php5-cgi webcam.php $file

Die webcam.php fügt Logo und Datum in das Bild ein, macht die Helligkeitserkennung und beendet bei zu dunklen Bildern den Stream:

 exec('pkill -f "nc -l -s 10.0.8.1 -p 2222"');

Als vernünftiger Wert für die Helligkeitserkennung hat sich 50 herausgestellt. Gerechnet wird wie folgt:

 function helligkeit()
 {
 $px=0;
 $r=0;
 $g=0;
 $b=0;
 for($i=0; $i < $this->h_gross; $i+=12) {
 for($j=0; $j < $this->w_gross; $j+=12) {
 $rgb = imagecolorat($this->image_gross, $j, $i);
 $r+= ($rgb >> 16) & 0xFF;
 $g+= ($rgb >> 8) & 0xFF;
 $b+= $rgb & 0xFF;
 $px++;
 }

 }
 $r=$r/$px;
 $b=$b/$px;
 $g=$g/$px;
 $h=($r*0.299)+($b*0.114)+($g*0.587);
 $this->log_action("Hell H:$h R:$r G:$g B: $b ");
 $this->hell=$h;
 return $h;
 }

 $exitcode=intval(1/$this->hell*900*$this->exif["ISOSpeedRatings"]/640);
 if ($exitcode>60) $exitcode=60; # Belichtungszeit in S für nächstes Bild
 exit($exitcode);

Ein paar abschließenden Bemerkungen:

  • Mehr Auflösung: kein Problem der Raspberry streamt auch locker mit Full HD (1920×1080) bei 30 Bildern pro Sekunde. Aber für die Anzeige braucht das eigentlich keiner und die Datenrate steigt dann auf mindestens 3 Mbit.
  • Datenrate: Durch die fest installierte Kamera ist der Bereich des Bildes, der sich verändert, vergleichsweise gering. Würde die Kamera schwenken müsste an diesem Parameter sicher gedreht werden.
  • Der Rechenaufwand zum transcodieren ist immens. Den Stream in 2 Auflösungen anzubieten verbraucht unheimliche Ressourcen. Auf dem Server (vserver) steht keine GPU zur Verfügung. Vielleicht klappt das ja mit dem neuen 3er PI zusätzlich einen Handy-tauglichen Stream anzubieten.
  • Die gewählte Lösung ist nicht die schnellste, aber mir kommt es auf Stabilität und nicht auf Echtzeitübertragung an. Die minimale Verzögerung dieser Lösung ist 10,3 Sekunden (300ms Empfangspuffer im vlc und 10s Segmente auf dem Server. Je nachdem wie der Client sich verhält kann die Verzögerung bis zu 60 Sekunden betragen, so lange wird das Video im Speicher gehalten. Wenn ihr mit eurem Handy vorm Tor steht und auf eurer Bild wartet: Setzt euch auf die Bank 🙂
  • Welche Streamserver und Dienste verwenden? Ich habe mit allem möglichen experimentiert:
    • ffmpeg/avconv hat Probleme den Stream der Kamera richtig zu verarbeiten…
    • uv4l hat nach der Installation meine raspbian Betriebssystem geschrottet. Der Minirechner ist dann unspezifisch nach etwa 30 Minuten abgestürzt.
    • 4vl2 Treiber läuft nicht stabil. Bei jedem Versuch Kernel-Modul neu laden…
      Damit macht schon das experimentieren keinen Spaß weil man nie weiß ob es die Parameter sind oder ob es mal wieder der Treiber verbockt.

An dieser Stelle möchte ich mich nochmal ganz herzlich bei allen bedanken, die den PI und die ganze Software dazu entwickelt haben.

Wenn jemand auch seine Raspberry-Webcam live streamen möchte und Unterstützung braucht… einfach bei mir melden.

Udo Rathje