FHEM > Codeschnipsel

Videostreams von IP-Kamera als E-Mail und Live (NVR, h.264, IPC, HLS, FFmpeg)

(1/4) > >>

Torxgewinde:
Hallo,
Vor langer Zeit hatte ich mal MJPG-Streamer entwickelt und einen MJPEG-stream ohne Plugin an den Browser senden können. Damit war es auch einfach Snapshots zu machen und diese als E-Mail oder Push Nachricht zu versenden wenn ein Event in FHEM ausgelöst wurde. Die Zeit geht weiter und MJPEG ist mittlerweile nicht mehr die erste Wahl, da es zu wenige Framerate liefert und zu bandbreitenintensiv ist - Heute nimmt mal lieber h.264 oder h.265.

Etabliert, aber leider nicht direkt im Browser darstellbar ist RTSP als Streaming-Protokoll. Im Browser kann man allerdings HLS, DASH oder WebRTC streamen. DASH hat Probleme auf iOS und WebRTC hat wunderbar wenig Latenz, stellt aber an den Server ein paar mehr Anforderungen als HLS. HLS ist ist sehr kompatibel, hat allerdings auch eine heftige Latenz mit der man aber arbeiten kann. Hat man einen HLS Stream kann man diesen mit einem Webserver ausliefern und die meisten Browser können diesen Stream nativ oder mit Javascript darstellen.

Ein HLS-Stream besteht im Grunde aus kurzen Video-Segmenten (*.ts Dateien) und einer M3U8-Playliste. Man kann auch mehrere Auflösungen des Streams anbieten und der Client schaltet dann jeweils auf den Stream um, der ohne zu stocken übertragen werden kann.

Für FHEM ist ein HLS Stream aus einem weiterem Grund interessant: Man kann darin vorwärts und rückwärts gehen und so auch Video von vor einigen Sekunden abrufen. Das ist nützlich, wenn ein Bewegungsmelder in FHEM triggert und auch das Video vor diesem Trigger-Zeitpunkt z.B. per E-Mail verschickt werden soll.

Wenn die IP-Kamera jetzt auch noch direkt Streams per RTSP im h.264 Format anbietet, braucht man nichteinmal das Video transkodieren, sondern es genügt das Video zu remuxen. Das kann auch ein embedded Gerät mit wenig RAM und wenig CPU - in meinem Fall ist es ein WLAN-Router der mit OpenWRT bespielt wurde.

IP-Kamera konfigurieren
Die IP Kamera sollte einen, oder zwei RTSP Streams ausliefern und man sollte die URL kennen. Bei einer Hikvision Kamera ist der erste Stream zum Beispiel "rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/101/?transportmode=unicast" und der zweite Stream hat die URL "rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast". In den Stream kann man mit VLC oder ffplay reinschauen:

--- Code: ---ffplay -rtsp_transport tcp -i rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast
--- Ende Code ---
Hinweis #1: -rtsp_transport tcp korrigiert kleinere Fehler im Netzwerk, da der Stream über TCP läuft. Standard ist UDP und da kann es manchmal zu kleinen Fehlern in der Übertragung kommen.
Hinweis #2: Hat die IP-Kamera mehrere Streams, sollte einer mit geringer Bandbreite (kleine Auflösung und/oder kleine Framerate) und der andere hochauflösend sein. In meinem Fall habe ich h.264 als Codec eingestellt und h.264+ oder auch h.265/h.265+ zugunsten der Kompatibilität zu Browsern vermieden.

RTSP-Streams in HLS-Dateien umwandeln
Da ein HLS-Stream aus Dateien besteht muss man diese irgendwo speichern. Der Speicherort sollte von einem HTTP-Server aus erreichbar sein. Auf OpenWRT ist dies zum Beispiel der Ordner /www. Natürlich kann man auch NGinx oder Apache als Webserver installieren. Eigentlich müsste sogar FHEMs eigener Webserver zum ausliefern der HLS-Dateien geeignet sein. Dann kann man mit folgendem Skript die notwendigen Dateien in den Ordner /opt/fhem/www/video speichern und später im Browser abrufen.

--- Code: ---#!/bin/bash

#Put HLS output into $WEBFOLDER
WEBFOLDER="/opt/fhem/www/video"

#High resolution RTSP stream
STREAMHIGH="rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/101/?transportmode=unicast"

#Low resolution RTSP stream
STREAMLOW="rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast"

cd

#download most recent hls.js if not present
if [ ! -f "$WEBFOLDER/hls.js" ]; then
wget "https://cdn.jsdelivr.net/npm/hls.js@latest" -O "$WEBFOLDER/hls.js" || exit 1
fi

#this is a simple webpage with the HLS video.
#Safari plays it without Javascript, Firefox needs hls.js
cat << 'EOM' > "$WEBFOLDER/index.html"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="hls.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
video {
object-fit: fill;
width: 100%;
height: auto;
}
</style>
</head>
<body>
<video id="video" controls autoplay>
<source src="stream.m3u8" type="application/x-mpegURL">
</video>
<script>
if (Hls.isSupported()) {
var video = document.getElementById('video');

var config = {
autoStartLoad: true,
startPosition: 30,
backBufferLength: 60
};
var hls = new Hls(config);

hls.loadSource('stream.m3u8');
hls.attachMedia(video);

//autoplay, many browsers are configured to only allow autoplay when muted
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.muted = true;
video.volume = 0;
video.play();
});

//when new fragment loaded, seek to 5 seconds before the current end of stream, total delay is ~10-12s this way
hls.on(Hls.Events.FRAG_LOADED, function() {
video.currentTime = video.duration - 5;
});
}
</script>
</body>
</html>
EOM

# https://ffmpeg.org/ffmpeg.html#Advanced-options
# https://ffmpeg.org/ffmpeg-formats.html#hls-2
# -hide_banner: Hide the infos at startup
# -rtsp_transport tcp: Default is to use UDP, which reveals skipped packets in my network
# -i $STREAMHIGH: the input at high resolution
# -i $STREAMLOW: the input at low resultion
# -codec copy: do not transcode, just remux - this is essential for embedded devices!
# -b:v:0 1024k Describe the bandwidth of video of the first input (index starts at 0)
# -b:v:1 256k  For the low resolution stream the bandwidth is about 256kBit/s
# -map 0:v include the videostream of the first input
# -map 1:v include the videostream of second input as well
# -map 0:a for audio of first input (not used here)
# -copytb 1 Use the demuxer timebase
# -copyts copy timestamps from input to output
# -f hls output is HLS
# -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' select first video stream, name it "hd", assign a group + select second video stream, name it "sd", assign it to group
# -hls_time 5 each *.ts file has a length of ~5 seconds
# -hls_allow_cache 0 client must not cache the file
# -hls_list_size 10 limit the number of files to this value
# -hls_flags delete_segments+append_list+program_date_time : delete unreferenced (old) *.ts files, append new ts files to playlist, insert recorded date+time
# -hls_segment_filename '$WEBFOLDER/%v_file%d.ts' pattern for the *.ts filename
# -master_pl_name stream.m3u8 pattern for the the HLS master playlist
# -master_pl_publish_rate 10 publish a current master playlist after creating N *.ts segments
# '$WEBFOLDER/%v_stream.m3u8' the output for HLS playlists

#run ffmpeg
ffmpeg -hide_banner \
  -rtsp_transport tcp -i $STREAMHIGH \
  -rtsp_transport tcp -i $STREAMLOW \
  -codec copy \
  -b:v:0 2M -b:v:1 256k \
  -map 0:v -map 1:v \
  -copytb 1 -copyts \
  -f hls \
  -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' \
  -hls_time 5 \
  -hls_allow_cache 0 \
  -hls_list_size 10 \
  -hls_flags delete_segments+append_list+program_date_time \
  -hls_segment_filename "$WEBFOLDER/%v_file%d.ts" \
  -master_pl_name stream.m3u8 \
  -master_pl_publish_rate 10 \
  "$WEBFOLDER/%v_stream.m3u8"

exit 0
--- Ende Code ---
Das Skript kopiert die notwendigen Dateien in den $WEBFOLDER und startet ffmpeg mit den passenden Parametern. Ich habe die Parameter kommentiert. Der HD-Stream von der Kamera hat in meinem Fall ca. 2MBit/s und der SD-Stream ca. 256kBit/s. Diese Zahl sollte man an die eigene Kamera anpassen.

Nachdem das Skript läuft, sollte relativ bald die ersten M3U8 Playlisten und TS-Dateien im $WEBFOLDER auftauchen und man sollte mit dem Browser/VLC/ffplay das Livebild streamen können. Im Firefox kann man begrenzte Bandbreiten simulieren und damit prüfen ob auch wirklich zwischen HD und SD Stream umgeschaltet wird (Tastendruck F12 und dann bei den Handy-ansichten kann man das umschalten).

E-Mail bei Bewegung versenden, Video als MOV Datei im Anhang
Ein weiteres Skript nimmt einen Videoschnipsel auf. Dabei wird zwei Segmente in die Zeit vor dem Event zurückgespult und für 30 Sekunden aufgenommen. Da curl auch SMTP zum E-Mail versenden kennt und mit Attachments umgehen kann nutze ich dies:

--- Code: ---#!/bin/bash

#generate message with timestamp before running the recording
MESSAGE="Es wurde eine Bewegung erkannt ($(date))"

echo "$(date): recording video..."
#assuming 5 seconds per HLS segment:
#seek backwards two segments = -live_start_index -2 = -10 seconds from current end-of-stream
#record for a duration of 35 seconds               
ffmpeg -y \
-hide_banner \
-loglevel fatal \
-live_start_index -2 \
-i /www/video/sd_stream.m3u8 \
-c copy -map 0 \
-t 35 \
-f mov "/opt/fhem/motion.mov" || exit 1

echo "$(date): sending email now..."
#https://everything.curl.dev/usingcurl/smtp
#https://curl.se/docs/manpage.html
#https://superuser.com/questions/1628906/using-curl-to-send-multipart-alternative-email-with-encoder-quoted-printable-ho
curl --ssl-reqd smtps://mail.server.de:465 \
    --login-options AUTH=PLAIN \
    --user blubb@example.de:password \
    --mail-from blubb@example.de \
    --mail-rcpt bla@example.com \
    --mail-rcpt-allowfails \
    -H "Subject: Bewegung erkannt" \
    -F '=(;type=multipart/alternative' \
    -F "=$MESSAGE" \
    -F "= <body>$MESSAGE</body>;type=text/html" \
    -F '=)' \
    -F "=@/opt/fhem/motion.mov;encoder=base64" || exit 1

rm -f /opt/fhem/motion.mov || exit 1
echo "$(date): done"

exit 0
--- Ende Code ---

Triggern kann dieses Skipt ein DOIF:

--- Code: ---defmod PIRHaustuer.Action DOIF ( [PIRHaustuer.device:"^motion$"] )\
("bash /opt/fhem/motion_email.sh")
attr PIRHaustuer.Action do always
--- Ende Code ---

Zum Darstellen des HLS Streams in FHEMWEB:

--- Code: ---defmod Camera.stream weblink htmlCode \
<a href="/fhem?detail=Camera.stream">edit</a><br />\
\
<iframe src="https://meinServer.lan/video/" height="600" width="800"></iframe>

--- Ende Code ---

HTH

Edit: Fehler im Skript (siehe Unterhaltung unten) gefixt.

Torxgewinde:
Mit FFmpeg kann man auch eine effiziente Szenenanalyse laufen lassen und damit Bewegungen nur aus dem Videostrom erkennen.

Inspiriert ist das Vorgehen von einem Modul für HomeAssitant. Allerdings würde ich nicht md5, sondern CRC32 als Prüfsumme favorisieren (spart nochmal RAM und CPU im Vergleich zu MD5).

Die Parameter:

* -hide_banner --> ffmpeg schreibt üblichweise Infos aus stdout beim Starten, das brauchen wir hier nicht
* -rtsp_transport --> Empfange den kleineren RTSP stream von der Kamera mit TCP statt UDP, bügelt kleinere Netzwerkprobleme aus
* -i --> die URL der Kamera
* -an --> verwerfe Tonspuren
* -vf --> VideoFilter
* select --> wähle nur Bilder bei denen die Suche zutrifft
* isnan(prev_selected_t)+gte(t-prev_selected_t\,0.5) --> selektiere nur Bilder die einen zeitlichen Abstand von 0.5 Sekunden haben
* scale --> weiterer Filter, skaliere das Bild runter auf ein Viertel (1/64 wäre noch effizienter, ist aber dann sehr grob)
* select=gt(scene\,0.05) --> wähle nur Bilder aus dem Videostream bei denen sich er Inhalt mehr als 0.05 geändert hat (Wert kann 0 bis 1.0 sein, 0 lässt alles durch)
* -f framehash --> Output soll in das Modul 'framehash' gegeben werden
* -hash crc32 --> Parameter für das Modul 'framehash' um die CRC32 zu berechnen
* - --> Das Framehash-Modul soll auf STDOUT schreiben, könnte auch in eine Datei geschrieben werden
--- Code: ---ffmpeg -hide_banner -loglevel fatal \
-rtsp_transport tcp -i rtsp://username:passwort@camera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast \
-an \
-vf "select=isnan(prev_selected_t)+gte(t-prev_selected_t\,0.5), scale=iw/4:-2:flags=neighbor, select=gt(scene\,0.05)" \
-f framehash -hash crc32 -

--- Ende Code ---

Der Output ist dann sowas:

--- Code: ---#format: frame checksums
#version: 2
#hash: CRC32
#software: Lavf58.76.100
#tb 0: 1/25
#media_type 0: video
#codec_id 0: rawvideo
#dimensions 0: 480x270
#sar 0: 1/1
#stream#, dts,        pts, duration,     size, hash
0,        473,        473,        1,   194400, ba72f638
0,        686,        686,        1,   194400, 0004a3b1
0,        806,        806,        1,   194400, fc61cca3
--- Ende Code ---

Immer wenn sich das Bild deutlich genug ändert (Szenenwechsel), wird eine weitere Zeile mit ausgegeben.

Zum Testen kann man auch eine Webcam direkt am PC nehmen, da kann man dann auch ein Bild im SDL Fenster zeigen:

--- Code: ---ffmpeg  -hide_banner -loglevel fatal -f v4l2 -framerate 25 -video_size 640x480 -i /dev/video0 -an -vf "select=isnan(prev_selected_t)+gte(t-prev_selected_t\,0.5), scale=iw/4:ih/4:flags=neighbor, select=gt(scene\,0.05)" -f framehash -hash crc32 - -c:v rawvideo -pix_fmt yuv420p -window_size qcif -f sdl "bla"

--- Ende Code ---

Torxgewinde:
Man kann den Stream recht einfach als Videodatei speichern. Ein einfaches DOIF ermöglicht mitschneiden des Streams als z.B. *.mov oder *.mkv. Da auch hier nicht transkodiert wird, ist die CPU Last kleiner 5% auf einem einfachen Hardware. Falls man keinen Bedarf an dem HLS Stream hat, kann man alternativ auch hier den RTSP stream mitschneiden. Beim HLS Stream kann man zeitlich in die Vergangenheit springen und das Vergangene aufzeichnen, bei Mitschnitt von RTSP fehlen ab dem Starten der Aufnahme einige Sekunden in der Aufzeichnung.

Da system() zwar ermöglicht einen Prozess in den Hintergrund zu forken, aber leider immer das Log zumüllt mit einem Fehler "-1", habe ich qx() verwendet. Damit qx() nicht den FHEM Prozess pausiert, wird eine mit screen geforkte Session gestartet. Der immense Vorteil dabei ist, dass man auch nachträglich mit screen -dRR NVR nochmal schauen kann was FFmpeg als Ausgabe schreibt.


--- Code: ---defmod NVR DOIF ([$SELF:command] eq "start")\
{\
my $WEBFOLDER="/opt/fhem/www/video";;\
my $INPUT="-live_start_index -2 -i /www/video/stream.m3u8";; ##input is HLS stream, seek back two segments, ~10s\
\
my $result = qx(screen -d -S NVR -m bash -c "ffmpeg -y -hide_banner -loglevel info $INPUT -c copy \"$WEBFOLDER/camera.mp4\"" && echo "start OK" || echo "start NOK" );;\
Log(0, "$SELF: $result");;\
}\
DOELSEIF ([$SELF:command] eq "stop")\
{\
##send the keypress "q" to the screen-session named "NVR", this gracefully ends FFmpeg\
my $result = qx( screen -S NVR -p 0 -X stuff q && echo "stop OK" || echo "stop NOK" );;\
Log(0, "$SELF: $result");;\
}\
DOELSEIF ([$SELF:command] eq "prepare")\
{\
##prepare directory for video in FHEMWEB, download hls.js if needed\
my $WEBFOLDER="/opt/fhem/www/video";;\
\
my $result = qx( mkdir -p $WEBFOLDER;; [ ! -f $WEBFOLDER/hls.js ] && wget "https://cdn.jsdelivr.net/npm/hls.js\@latest" -O "$WEBFOLDER/hls.js" );;\
Log(0, "$SELF: $result");;\
}\
DOELSE\
##
attr NVR cmdState Recording|Stopped|prepared|unknown
attr NVR readingList command
attr NVR setList command:start,stop
attr NVR webCmd command
attr NVR widgetOverride command:uzsuSelectRadio,stop,start

--- Ende Code ---

Zum Darstellen der MP4 Datei:

--- Code: ---defmod CameraView weblink htmlCode \
<a href="/fhem?detail=CameraView">edit</a><br />\
<video id="video" width="640" controls muted autoplay src="/fhem/video/camera.mp4">\
Your browser does not support the video tag.\
</video>

--- Ende Code ---

cotecmania:
Hi,

ich habe auch das Problem, dass meine alten Kameras (BNC) MJPEG lieferten, was ich in FTUI ohne Probleme anzeigen kann.
Die neueren Kameras bieten aber nur noch RTSP.
Könnte man das "Umleiten" relativ einfach auf dem FHEM Raspy zusätzlich laufen lassen und den FHEM Webserver verwenden ?
Ich möchte so wenig wie möglich zusätzliche Dinge installieren bzw. zusätzliche Hardware verwenden.
Wenn ja könntest Du das genau erklären, da ich in Linux nicht so fit bin.

Gruss
Joe

Torxgewinde:
Hallo @cotecmania,
Klar, das geht. h.264/RTSP ist wirklich ein Segen im Vergleich zu MJPEG (=JPEG/HTTP). Der Stream ist damit deutlich flüssiger und belegt weniger Bandbreite. HLS hat prinzipbedingt eine spürbare Verzögerung von 30 bis 5 Sekunden, das wird man nur los wenn man auf WebRTC wechseln würde, was aber einen Server wie z.B. "Janus" erfordert! Soviel zur Motivation.

Deinem Post entnehme ich, dass FHEM auf einem RPi läuft. Ich VERMUTE dort läuft das Raspberry-Pi-OS drauf, was praktisch Debian+Extras entspricht. Dort hast du vermutlich FHEM in /opt installiert. Ein Raspberry PI sollte keine Probleme mit der Wandlung von RTSP nach HLS haben (RAM und CPU sollte reichen). Ist der h.264/RTSP-Stream einmal im h.264/HLS-Stream-Format, kann man das gut in FHEM in der WebUI einbinden und anzeigen lassen.

Der einzige Punkt, den ich nicht hunderpro sagen kann, ist ob der der FHEM eigene Webserver wirklich in der Lage ist die Dateien für den HLS Stream auszuliefern. Wenn man den FHEM eigenen Webserver dazu bewegen möchte, Dateien für einen auszuliefern:

* legt man einen Ordner im Verzeichnis /opt/fhem/www/ an. Zum Beispiel: mkdir /opt/fhem/www/video. Der Nutzer "fhem" sollte Zugriff auf diesen Ordner haben, also die Rechte prüfen.
* Jede Datei, die in diesem Ordner liegt, kann man dann im Browser abrufen unter der Adresse: "https://<IP>/fhem/video/". Dort kann man also direkt die Datei "hls.js" hinkopieren, die Firefox hilft den HLS-Stream anzuzeigen. Nativ (also ohne dieses ECMA/Javascript) können das nur iOS Geräte und ich glaube auch Browser wie Chrome. Die hls.js kann man von https://cdn.jsdelivr.net/npm/hls.js@latest runterladen. Wieder auf die Zugriffsrechte achten und ausprobieren, dass der FHEM Webserver nun die Datei unter "https://<IP>/fhem/video/hls.js" auch wirklich an einen Browser ausliefert.
Dann musst du in dem RPi die Programme installieren, vor allem ist dies "ffmpeg". Das sollte mit dem Paketmanager "apt" gelingen: sudo apt -y install ffmpeg screen wget.

In den Kameras sollte man die Einstellungen prüfen, damit auch wirklich ein h.264/RTSP Stream erzeugt wird. Der Codec ist also "h.264", ohne irgendwelche Extras. Hikvision bietet zum Beispiel auch "h.264+" an, das komprimiert zwar besser ist aber zu speziell für einige Browser. Auch "h.265" oder "h.265+" sind zwar toll in sich, aber nicht so kompatibel. Deswegen schau' erstmal ob "h.264" einstellbar ist und stelle das auch ein. Kann die Kamera mehrere Streams anbieten, dann sollte man einen in hoher Auflösung und einen mit geringerer Auflösung einstellen, allerdings auch hier beide auf "h.264" als Codec. Damit man die Kamera von RPi aus auslesen kann, muss die URI bekannt sein, das heißt nicht nur die IP, sondern auch der Pfad und ggf. Parameter. Die URI kann testen mit dem Programm "VLC" oder auch "ffplay". Beide Programme sind sogar für Windows-OS verfügbar.

Wenn die URI bekannt ist, dann kann man das in dem Skript aus dem ersten Post eintragen. Das Skript kann man in den FHEM Ordner kopieren, zum Beispiel nach /opt/fhem/scripts/RTSP-to-HLS.sh. Ich habe hier mal die Pfade angepasst, wie sie VERMUTLICH sein werden:

--- Code: ---#!/bin/bash

#Put HLS output into $WEBFOLDER
WEBFOLDER="/opt/fhem/www/video"

#High resolution RTSP stream
STREAMHIGH="rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/101/?transportmode=unicast"

#Low resolution RTSP stream
STREAMLOW="rtsp://username:password@kamera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast"

cd

#download most recent hls.js if not present
if [ ! -f "$WEBFOLDER/hls.js" ]; then
wget "https://cdn.jsdelivr.net/npm/hls.js@latest" -O "$WEBFOLDER/hls.js" || exit 1
fi

#this is a simple webpage with the HLS video.
#Safari plays it without Javascript, Firefox needs hls.js
cat << 'EOM' > "$WEBFOLDER/index.html"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="hls.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
video {
object-fit: fill;
width: 100%;
height: auto;
}
</style>
</head>
<body>
<video id="video" controls autoplay>
<source src="stream.m3u8" type="application/x-mpegURL">
</video>
<script>
if (Hls.isSupported()) {
var video = document.getElementById('video');

var config = {
autoStartLoad: true,
startPosition: 30,
backBufferLength: 60
};
var hls = new Hls(config);

hls.loadSource('stream.m3u8');
hls.attachMedia(video);

//autoplay, many browsers are configured to only allow autoplay when muted
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.muted = true;
video.volume = 0;
video.play();
});

hls.on(Hls.Events.FRAG_LOADED, function() {
video.currentTime = video.duration - 5;
});

}
</script>
</body>
</html>
EOM

# https://ffmpeg.org/ffmpeg.html#Advanced-options
# https://ffmpeg.org/ffmpeg-formats.html#hls-2
# -hide_banner: Hide the infos at startup
# -rtsp_transport tcp: Default is to use UDP, which reveals skipped packets in my network
# -i $STREAMHIGH: the input at high resolution
# -i $STREAMLOW: the input at low resultion
# -codec copy: do not transcode, just remux - this is essential for embedded devices!
# -b:v:0 1024k Describe the bandwidth of video of the first input (index starts at 0)
# -b:v:1 256k  For the low resolution stream the bandwidth is about 256kBit/s
# -map 0:v include the videostream of the first input
# -map 1:v include the videostream of second input as well
# -map 0:a for audio of first input (not used here)
# -copytb 1 Use the demuxer timebase
# -copyts copy timestamps from input to output
# -f hls output is HLS
# -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' select first video stream, name it "hd", assign a group + select second video stream, name it "sd", assign it to group
# -hls_time 5 each *.ts file has a length of ~5 seconds
# -hls_allow_cache 0 client must not cache the file
# -hls_list_size 10 limit the number of files to this value
# -hls_flags delete_segments+append_list+program_date_time : delete unreferenced (old) *.ts files, append new ts files to playlist, insert recorded date+time
# -hls_segment_filename '$WEBFOLDER/%v_file%d.ts' pattern for the *.ts filename
# -master_pl_name stream.m3u8 pattern for the the HLS master playlist
# -master_pl_publish_rate 10 publish a current master playlist after creating N *.ts segments
# '$WEBFOLDER/%v_stream.m3u8' the output for HLS playlists

#run ffmpeg
ffmpeg -hide_banner \
  -rtsp_transport tcp -i $STREAMHIGH \
  -rtsp_transport tcp -i $STREAMLOW \
  -codec copy \
  -b:v:0 2M -b:v:1 256k \
  -map 0:v -map 1:v \
  -copytb 1 -copyts \
  -f hls \
  -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' \
  -hls_time 5 \
  -hls_allow_cache 0 \
  -hls_list_size 10 \
  -hls_flags delete_segments+append_list+program_date_time \
  -hls_segment_filename '$WEBFOLDER/%v_file%d.ts' \
  -master_pl_name stream.m3u8 \
  -master_pl_publish_rate 10 \
  '$WEBFOLDER/%v_stream.m3u8'"

exit 0
--- Ende Code ---

Wenn du nun nach "https://<IP>/fhem/video/index.html" surfst, sollte der HLS Stream im Browser angezeigt werden. Will man das in FHEM anzeigen, kann man sich das Leben einfach machen und ein "<iframe>" nutzen:

--- Code: ---defmod myCamera.stream weblink htmlCode \
<a href="/fhem?detail=myCamera.stream">edit</a><br />\
\
<iframe src="/fhem/video/index.html" height="455" width="800"></iframe>
--- Ende Code ---

Das Skript /opt/fhem/scripts/RTSP-to-HLS.sh anfangs von Hand ausführen, am besten als Nutzer "fhem". Wenn das klappt, kann man es aus FHEM heraus mitstarten und es einfach immer mitlaufen lassen. Da FFMPEG in diesem Fall nicht den Codec ändern muss, muss sich der RPi auch nicht sehr anstrengen und das Skript belegt weder viel CPU noch RAM. Zum Starten aus FHEM heraus:

--- Code: ---defmod di_Startup DOIF (0) ## Startup Befehle\
({Log(0, "di_Startup startet")})\
("screen -d -S HLS -m bash -c /opt/fhem/scripts/RTSP-to-HLS.sh")\
({Log(0, "di_Startup beendet")})
attr di_Startup do always
attr di_Startup startup set $SELF cmd_1
--- Ende Code ---

HTH!

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln