Zeitgleiche Aufnahmen am FritzRepeater
Hallo,
wie ich bereits in dem Issue https://github.com/Pavion/tvstreamrecord/issues/22 lesen konnte, hat tvstreamrecord keine Begrenzung für zeitgleiche Aufnahmen. Die DVB-C fähigen Geräte von AVM hingegen streamen an ein Gerät aber immer nur einmal.
Ich habe tvstreamrecord auf einer DS415+ laufen. Diese hat zwei Ethernet-Interfaces, welche beide an meiner FritzBox hängen. Zusätzlich habe ich im Netz den FritzRepeater mit DVB-C. Das gleichzeitige Aufnehmen wäre also theoretisch möglich, da ich über zwei verschiedene IPs auf den Repeater zugreifen kann. Funktionieren tut das allerdings mit tvstreamrecord noch nicht. Wahrscheinlich deshalb, weil beide ffmpeg-Prozesse ihre Verbindung über das selbe Interface aufbauen wollen.
Gibt es hier eine Möglichkeit das Problem mit ffmpeg selbst zu lösen oder wäre da eine Lösung über tvstreamrecord möglich? Ein Beispiel: Wenn bereits eine Aufnahme läuft, dann sorgt tvstreamrecord dafür, dass der zweite ffmpeg-Prozess über das zweite Interface arbeitet.
Ich stelle mich auch gern als Versuchskaninchen zur Verfügung.
Beste Grüße Daniel
Hallo Daniel,
es ist an sich eine interessante Idee, allerdings ist es eher eine Linux-/Netzwerk-Frage und da kenne ich mich nicht so gut aus. Zudem sind mehrere LANs eher für Link Aggregation gedacht, das ist wieder eine andere Geschichte... Was ich aber gerade gelesen habe, sieht in etwa so aus:
Die Regel, welches Netzwerkadapter verwendet wird (route), kann nur in Linux direkt eingestellt werden; https://superuser.com/questions/1355077/ffmpeg-choose-outbound-ip-eth0-or-eth1 Das sehe dann in etwa so aus:
root@DiskStation:~# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
IP1 0.0.0.0 255.255.255.0 U 0 0 0 eth0
IP2 0.0.0.0 255.255.255.0 U 0 0 0 eth1
Diese Route muss aber eigentlich an die Empfänger-Adresse (IP1/IP2) geknüpft werden. Ob man dem Repeater zwei Adressen beibringen kann, wage ich jedoch zu bezweifeln.
Weitere wage Ideen:
- Proxy-artiges Tool, welches auf DS über eth1 läuft
- zweites TSR im Docker-Container laufen lassen und den Docker auf eth1 abrichten
Also nach einer Stunde Recherche fand ich leider nichts, was ich umsetzen oder gar testen könnte. Wenn Du weiterführende Ideen hast, melde Dich gern wieder.
Gruß Pav
Hallo Pavion,
danke für deine Ideen, die Docker-Variante wäre mein letzter Ausweg.
Ich habe bereits zwei Möglichkeit erfolgreich testen können. Allerdings gelingt mir in beiden Fällen immer nur der direkte Aufruf von ffmpeg über die Kommandozeile.
Variante 1
https://superuser.com/questions/241178/how-to-use-different-network-interfaces-for-different-processes
Über netns einen Interface-Namespace erzeugen, in welchem dann ein ffmpeg-Prozess läuft.
sudo ip netns add eth1_ns
sudo ip link set eth1 netns eth1_ns
sudo ip netns exec eth1_ns sudo ifconfig eth1 10.1.1.10/24 up
sudo ip netns exec eth1_ns sudo ifconfig lo 127.0.0.1/8 up
sudo ip netns exec eth1_ns sudo route add default gw 10.1.1.1
sudo ip netns exec eth1_ns sudo dhclient eth1
sudo ip netns exec eth1_ns sudo /volume1/@appstore/VideoStation/bin/ffmpeg XXXXXXXXXX
Funktioniert, wenn komplett über die Kommandozeile ausgeführt. Anschließend: Alle Schritte bis zum dhclient habe ich direkt über die Kommandozeile gemacht. Den letzten Punkt wollte ich dann über tvstreamrecord starten. Egal ob in der tvstreamrecord.py oder über die Config (ffmpeg-Pfad), ich bekomme immer Meldungen:
2019-02-09 15:15:44.754 | FFMPEG could not be started. Error: [Errno 2] No such file or directory
2019-02-09 15:15:44.745 | [u'sudo ip netns exec eth1_ns sudo /volume1/@appstore/VideoStation/bin/ffmpeg', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=346&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,1400,1401,1402,1403,1404,1406,1405,1470,1476,2171', u'-y', u'-t', u'675', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190209151544 - dasbloghaus_tv___Grafitti_for_Love.ts']
Variante 2
https://daniel-lange.com/archives/53-Binding-applications-to-a-specific-IP.html
IP-Adresse des anderen Interfaces angeben und über das bind.so-Skript ausführen.
BIND_ADDR="192.168.170.45" LD_PRELOAD=/usr/lib/bind.so sudo /volume1/@appstore/VideoStation/bin/ffmpeg XXXXXXXXXXXXX
Auch das funktioniert problemlos über Kommandozeile. Doch dann die selben Probleme wie oben. Über tvstreamrecord geht nix...
2019-02-09 18:18:47.514 | FFMPEG could not be started. Error: [Errno 2] No such file or directory
2019-02-09 18:18:47.506 | [u'BIND_ADDR="192.168.170.45" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=314&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,200,210,220,221,225,222,230,231,250', u'-y', u'-t', u'2592', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190209181847 - Bl_tentr_ume.ts']
Es scheint sich um irgendein Rechte-Problem zu handeln. Kannst du vielleicht weiterhelfen?
VG Daniel
Nein. Nur die Leerzeichen im Befehl werden falsch interpretiert. Wenn Du ihn in eine .sh packst, sollte es funktionieren. Schaue ich mir morgen genauer an.
Die Idee hatte ich auch schon...
Variante 3
Ein Skript welches ich in der Config als Pfad zu ffmpeg angebe.
Kompletter Pfad für ffmpeg sh /volume1/@appstore/VideoStation/bin/ffmpegbinder.sh
Das Skript wiederum startet ffmpeg in der bind-Variante:
#!/bin/sh
str="'$*'"
BIND_ADDR="192.168.170.45" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg "$str"
Und was soll ich sagen, das Resultat bleibt leider das selbe:
2019-02-09 18:43:14.081 | FFMPEG could not be started. Error: [Errno 2] No such file or directory
2019-02-09 18:43:14.073 | [u'sh /volume1/homes/admin/ffmpegbinder.sh', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=314&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,200,210,220,221,225,222,230,231,250', u'-y', u'-t', u'1125', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190209184314 - Bl_tentr_ume.ts']
Welche Leerzeichen meinst du?
Leerzeichen zwischen sh und /volume1 Vielleicht ./xxx.sh?
Leerzeichen war ein gutes Stichwort...
Folgendes hab ich an der tvstreamrecord.py angepasst:
attr = [config.cfg_ffmpeg_path,"-i", ('"'+self.url+'"'), '-y', '-t', deltasec] + ffargs + [('"'+fn+'"')]
attr = " ".join(attr)
und
self.process = subprocess.Popen(attr, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
In der Config als ffmpeg-Pfad:
BIND_ADDR="192.168.170.45" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg
Uns siehe da...
2019-02-10 00:04:26.290 | Record: Stopflag for 'Sportschau' received
2019-02-10 00:03:18.580 | FFMPEG (rtsp) record 'Sportschau' called with:
2019-02-10 00:03:18.580 | BIND_ADDR="192.168.170.45" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg -i "rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171" -y -t 821 -loglevel fatal -acodec copy -vcodec copy "/volume2/Aufnahme/20190210000318 - Sportschau.ts"
Und in der Fritz steht auch tatsächlich die 192.168.170.45 (mein eth1) als zugreifende IP da... Ich werde das morgen noch ein wenig erweitern und dynamisch machen. Immerhin will ich diesen Modus ja nur für eine zeitgleiche Aufnahme.
Theoretisch könnte man diese Methode auch für virtuelle Ethernet-Interfaces nutzen. Man bräuchte also nicht zwingend zusätzliche echte Interfaces... Gut für FritzBoxen mit 4 Tunern. Mein Repeater hat leider nur 2 ;-)
So, neue Erkenntnisse und Probleme...
Änderungen an der tvstreamrecord.py wie in meinem letzten Kommentar.
In der Config als ffmpeg-Pfad:
sh /volume1/@appstore/VideoStation/bin/ffmpegbinder.sh
Mein ffmpegbinder.sh-Skript sieht so aus:
#!/bin/sh
for i in "192.168.170.21" "192.168.170.45"
do
runningfile="/volume1/@appstore/tvstreamrecord/running_$i.run"
if [ -f $runningfile ]; then
echo "file $runningfile exists"
else
echo "file $runningfile not exists"
echo "running now">$runningfile
echo "using bind adress $i"
BIND_ADDR="$i" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg "$@"
rm -- "$runningfile"
break
fi
done
Zeitgleiche Aufnahmen funktionieren einwandfrei. Aber das Beenden von Aufnahmen klappt nicht... :-(
Anscheinend wird das Skript wegen shell=True nicht als Child-Prozess, sondern als eigener Prozess ausgeführt. Somit greifen terminate() oder kill() nicht.
So, jetzt bin ich wach und wieder am PC :)
Ich sehe, Du hast schon sehr viel Arbeit erledigt und sogar die richtige Stelle in meinem Code gefunden! Leider kann ich nicht wirklich viel davon testen, da ich nicht mal einen Tuner mehr habe, daher bleibe ich erstmal bei Theorie und freue mich auf eine Diskussion.
Durch die Verwendung von shell=True wird die Anwendung noch unsicherer, da damit absolut alle möglichen Befehle mit maximalen Admin-Rechten auf DS ausgeführt werden könnten... Dabei gibt es sogar Leute, die das Tool ohne Passwort im Internet freigeben oO
Eigentlich würde ich, wenn, den Pfad zuerst splitten und auf shell=True verzichten:
attr = shlex.split(config.cfg_ffmpeg_path) + ["-i", self.url, '-y', '-t', deltasec] + ffargs + [fn]
Bin mir aber nicht sicher, ob es mit Deinem Befehl funktioniert. Vielleicht könntest Du das testen? Würde es funktionieren, wenn Dein Befehl in einer .sh stünde?
Was die Implementierung der Schaltung angeht, fallen mir zwei Wege ein:
1. Über eine .sh
Eine Umschaltung in einer .sh realisieren (sorry für mein Linux, ist nicht meine Muttersprache ^^):
if [ -f "flag1" ]
then
touch flag2
BIND2
rm flag2
else
touch flag1
BIND1
rm flag1
fi
Wenn Du Dich darin auskennst, könnte ich mir vorstellen, dass so etwas auf mehr Interfaces erweitert und parametrisiert werden könnte. Ggf. könnte man die vorher erforderliche Einrichtung ebenfalls in die gleiche Datei packen und irgendwie nur bei Bedarf (oder erstem Start) ausführen. Das Ziel wäre eine Art Mega-Script, den ein Bediener auf DS downloaden, [ggf. geringfügig anpassen], seinen Pfad in TSR eintragen und sofort nutzen kann. Oder ist es zu viel gehofft?!
2. über TSR
Einführung und Verarbeitung eines neuen Parameters analog zu "Alternate URL": https://github.com/Pavion/tvstreamrecord/blob/f5d7d1bb0973b2d06986f16bd0f3a3a9caf279cc/tvstreamrecord.py#L1122 Vorteil: bequem anzupassen Nachteil: dadurch, dass dennoch einiges auf Linux-Ebene gemacht wird, ist der Vorteil geringer, dafür aber mehr Code und Optionen. Nicht dass ich die Arbeit von mir schieben würde ^^
Ich habe schon seit einer Stunde an meiner Nachricht geschrieben und wurde gerade fertig, als Du Deine abgeschickt hast...
Ich sehe, wir kamen auf ähnliche Ideen... Du hast schon den Script und ich habe vielleicht die Lösung für Dein Shell-Problem 🙂
Sehr gut, fast gleichzeitig geantwortet :D
Ich würde deine split-Methode mal testen und shell=True weglassen.
Also split-Methode ohne shell=True und als ffmpeg-Pfad sh /volume1/@appstore/VideoStation/bin/ffmpegbinder.sh funktioniert:
2019-02-10 10:39:05.105 | Record: Stopflag for 'NZZ Standpunkte - Die Hydra hebt ihr Haupt - der neue Judenhass' received
2019-02-10 10:38:45.247 | Record: Stopflag for 'Pauline am Strand' received
2019-02-10 10:37:36.328 | FFMPEG (rtsp) record 'Pauline am Strand' called with:
2019-02-10 10:37:36.328 | ['sh', '/volume1/@appstore/VideoStation/bin/ffmpegbinder.sh', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=362&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,400,401,402,403,407,408,404,470,1276,2171', u'-y', u'-t', u'2063', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190210103736 - Pauline_am_Strand.ts']
2019-02-10 10:37:36.326 | total records: 2 // running records: 2
2019-02-10 10:36:49.971 | FFMPEG (rtsp) record 'NZZ Standpunkte - Die Hydra hebt ihr Haupt - der neue Judenhass' called with:
2019-02-10 10:36:49.971 | ['sh', '/volume1/@appstore/VideoStation/bin/ffmpegbinder.sh', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=314&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,200,210,220,221,225,222,230,231,250', u'-y', u'-t', u'1510', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190210103649 - NZZ_Standpunkte___Die_Hydra_hebt_ihr_Haupt___der_neue_Judenhass.ts']
2019-02-10 10:36:49.969 | total records: 1 // running records: 1
Beenden funktioniert allerdings trotzdem nicht...
Hm. Ich nutze ja terminate(), da sollte nix mehr laufen. Kann es sein, dass Dein Script durch sh im separaten Thread gestartet wird? Geht es nicht mit ./ffmpegbinder.sh?
Ich vermute auch das mein Skript nicht als Kind-Prozess läuft. Mal gucken warum das so ist...
Also, das Skript wird als Kind ausgeführt...
$ sudo pstree -p
├─python(26950)─┬─sh(12510)───ffmpeg(12512)
│ ├─{python}(26960)
│ ├─{python}(26961)
│ ├─{python}(26962)
│ ├─{python}(26963)
│ ├─{python}(26964)
│ ├─{python}(26965)
│ ├─{python}(26966)
│ ├─{python}(26967)
│ ├─{python}(26968)
│ ├─{python}(26969)
│ ├─{python}(1998)
│ ├─{python}(12509)
│ └─{python}(12511)
$ sudo ps -ef | grep ffmpeg && sudo ps -ef | grep python
root 26950 1 0 10:35 ? 00:00:24 python tvstreamrecord.py
root 12510 26950 0 13:14 ? 00:00:00 sh /volume1/@appstore/VideoStation/bin/ffmpegbinder.sh -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 18169 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210131410 - Sportschau.ts
root 12512 12510 2 13:14 ? 00:00:09 /volume1/@appstore/VideoStation/bin/ffmpeg -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 18169 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210131410 - Sportschau.ts
Nach dem Beenden der Aufnahmen über tvstreamrecord ist nur noch der ffmpeg-Prozess da. Somit wird das Bash-Skript anscheinend korrekt beendet.
root 20015 1 3 13:33 ? 00:00:01 /volume1/@appstore/VideoStation/bin/ffmpeg -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 17011 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210133328 - Sportschau.ts
aber muss man wirklich sh vorschreiben und extra shell erzeugen?
Mein .terminate() sendet SIGTERM nur an parent: https://unix.stackexchange.com/questions/146756/forward-sigterm-to-child-in-bash Wenn es ohne .sh nicht funktioniert, könnte das die Lösung sein.
So, funktioniert auch ohne sh:
2019-02-10 13:59:13.147 | ['/volume1/@appstore/VideoStation/bin/ffmpegbinder.sh', u'-i', 'rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171', u'-y', u'-t', u'15466', u'-loglevel', u'fatal', u'-acodec', u'copy', u'-vcodec', u'copy', u'/volume2/Aufnahme/20190210135913 - Sportschau.ts']
2019-02-10 13:59:13.146 | FFMPEG (rtsp) record 'Sportschau' called with:
$ sudo ps -ef | grep ffmpeg
root 19994 1 0 13:33 ? 00:00:08 python tvstreamrecord.py
root 30193 19994 0 13:59 ? 00:00:00 /bin/sh /volume1/@appstore/VideoStation/bin/ffmpegbinder.sh -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 15466 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210135913 - Sportschau.ts
root 30195 30193 3 13:59 ? 00:00:00 /volume1/@appstore/VideoStation/bin/ffmpeg -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 15466 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210135913 - Sportschau.ts
Aber nach dem Beenden:
$ sudo ps -ef | grep ffmpeg
root 30193 19994 0 13:59 ? 00:00:00 [ffmpegbinder.sh] <defunct>
root 30195 1 2 13:59 ? 00:00:04 /volume1/@appstore/VideoStation/bin/ffmpeg -i rtsp://192.168.170.43:554/?avm=1&freq=306&bw=8&msys=dvbc&mtype=64qam&sr=6900&specinv=1&pids=0,16,17,18,20,100,101,102,103,104,106,84,105,1176,2070,2171 -y -t 15466 -loglevel fatal -acodec copy -vcodec copy /volume2/Aufnahme/20190210135913 - Sportschau.ts
Der ffmpeg-Prozess will ums Verrecken nicht mit sterben...
Da ich selbst mit Linux, bash und Co. nicht so fit bin, werde ich mich mal hier einlesen: https://riccomini.name/kill-subprocesses-linux-bash
Mein .terminate() sendet SIGTERM nur an parent: https://unix.stackexchange.com/questions/146756/forward-sigterm-to-child-in-bash Wenn es ohne .sh nicht funktioniert, könnte das die Lösung sein.
Wird probiert ;-)
So... Änderungen am Skript:
#!/bin/sh
_term() {
echo "caught SIGTERM signal"
echo "$(date)"
pkill -9 -P "$$" 2>/dev/null
}
trap _term SIGTERM
for i in "192.168.170.21" "192.168.170.45"
do
runningfile="/volume1/@appstore/tvstreamrecord/running_$i.run"
if [ -f $runningfile ]; then
echo "file $runningfile exists"
else
echo "file $runningfile not exists"
echo "running now" >> $runningfile
echo "using bind adress $i"
BIND_ADDR="$i" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg "$@"
rm -- "$runningfile"
break
fi
done
Beim Beenden über tvstreamrecord passiert nix. Alles läuft weiter. _term() wird anscheinend gar nicht aufgerufen.
Rufe ich direkt in der Kommandozeile sudo pkill -9 -P $PID auf, dann wird die _term()-Funktion aufgerufen (erkenne ich an der dem Zeitstempel den mir die Funktion ausgibt).
Wird Dein Script wieder sh aufgerufen? Damit wäre noch ein Shell drüber, wenn ich es richtig verstehe. Ansonsten bin ich mit meinem Latein am Ende...
Egal ob mit oder ohne sh, das Verhalten bleibt gleich. Anscheinend wird der SIGTERM-Befehl nicht korrekt gesendet oder empfangen...
So, ein erster Erfolg. Wie weiter oben angekündigt wollte ich mich hier https://riccomini.name/kill-subprocesses-linux-bash mal einlesen. Gesagt, getan und die dort beschriebene kill_child_processes() mittels trap angesprochen und es funktioniert.
#!/bin/sh
kill_child_processes() {
isTopmost=$1
curPid=$2
childPids=`ps -o pid --no-headers --ppid ${curPid}`
for childPid in $childPids
do
echo "try to kill child $childPid"
kill_child_processes 0 $childPid
done
if [ $isTopmost -eq 0 ]; then
echo "try to kill parent $curPid"
kill -9 $curPid 2> /dev/null
fi
if [ -f $runningfile ]; then
echo "try to delete $runningfile"
rm -- "$runningfile"
fi
}
trap "kill_child_processes 1 $$; exit 0" 0
for i in "192.168.170.21" "192.168.170.45"
do
runningfile="/volume1/@appstore/tvstreamrecord/running_$i.run"
if [ -f $runningfile ]; then
echo "file $runningfile exists"
else
echo "file $runningfile not exists"
echo "running now" >> $runningfile
echo "using bind adress $i"
BIND_ADDR="$i" LD_PRELOAD=/usr/lib/bind.so /volume1/@appstore/VideoStation/bin/ffmpeg "$@"
rm -- "$runningfile"
# exit
break
fi
done
Die kill_child_processes()-Funktion killt erst alle Kinder, dann den Hauptprozess und in meinem Fall wird noch die Running-Datei gelöscht.
Sonderlich schön ist das Skript aktuell nicht, aber es funktioniert. Ich kann zwei gleichzeitge Aufnahmen über tvstreamrecord starten und beenden.
Gleichzeitig sorgt das Skript dafür, dass die angegebenen IPs nur einmal für diesem Zweck verwendet werden können. Normalerweise würden sich mehrere ffmpeg-Prozesse über die gleiche IP zum FritzGerät nämlich in die Quere kommen und immer gegenseitig zum Abbruch führen.
Meine nächster Idee ist jetzt, mein eth1 um ein virtuelles Interface mit eigener IP zu erweitern und dann diese beiden Interfaces für die Aufnahmen zu nutzen. Somit wird eth0 nicht mit den Aufnahmen belastet und steht komplett für die anderen Aufgaben des NAS zur Verfügung.
Ich will nicht behaupten, alles verstanden zu haben, aber das klingt doch cool! :)
- Hast Du schon überlegt, ob Du Dein Script mit einer kurzen Anleitung bei GitHub einstellst, damit auch andere davon profitieren könnten?
- Was passiert eigentlich, wenn eine dritte Aufnahme gestartet wird? Verstehe ich es richtig, dass sie dann gar nicht erst gestartet würde? Eigentlich müsste dann die 1. Aufnahme abgebrochen werden....
- Was passiert, wenn die Flags nicht gelöscht werden (z.B. weil DS während einer Aufnahme heruntergefahren wurde)?
- Ja klar, wenn ich komplett fertig bin. 😉
- Die dritte Aufnahme wird über tvstreamrecord zwar theoretisch gestartet, aber es startet nie ein
ffmpeg, da mein Skript aufgrund der vorhanden Tickets normal zu Ende läuft. Danach greift aktuell deine Retry-Logik. Wenn es x-mal erfolglos probiert wurde, hört tvstreamrecord ja dann auf es zu probieren. Hier bin ich aktuell aber noch am Testen.
2019-02-11 12:55:57.017 | Something went wrong with 'Punkt 12 - Das RTL-Mittagsjournal', retry 1/20 in 10 seconds
2019-02-11 12:55:57.016 | FFMPEG record 'Punkt 12 - Das RTL-Mittagsjournal' ended
- Theoretisch sollte auch hier die
kill_child_processes()getriggert und die Tickets gelöscht werden. Kann ich aber bei Gelegenheit mal testen.
Also auch mit virtuellen Interfaces (über ein reales Interface) klappt das wunderbar.
Einfach Interfaces erstellen:
sudo ifconfig eth1:1 192.168.170.46
sudo ifconfig eth1:2 192.168.170.47
In meinem Skript verwende ich dann die beiden neuen IPs und das klappt wunderbar. Der gesamte Traffic läuft nun ausschließlich über eth1. 😄
Anderes Thema: Kannst du mit dieser Meldung etwas anfangen?
FFMPEG could not be started. Error: 'ascii' codec can't decode byte 0xe2 in position 275: ordinal not in range(128)
Die wird ab und zu mal geworfen und dann kommt ein erfolgreicher Retry.
coole Sache! ^^
Ich bin gespannt, wenn das mal den Endstand erreicht hat.
Die Fehlermeldung kommt mir schon bekannt vor und sollte eig. nur kommen, wenn der Titel unzulässige Zeichen enthält, und nur unter Python 2.x
So, da will ich mich auch mal wieder zu Wort melden... Mein Skript ist gewachsen, und ich erklär natürlich auch warum. 😉
Probleme
Es gibt einige Themen die sich schwierig gestaltet haben:
- Man müsste nach jedem Boot die virtuellen Interfaces per
ifconfigneu erzeugen. - Mit
ifconfigwären die IPs vorgegeben und man muss vorher prüfen ob diese auch noch im Netz frei sind. - Bisher musste man die IPs fest in meinem Skript vorgeben.
- Gleichzeitige Aufnahmen auf dem selben Sender belegen beide IPs, aber ein Tuner ist in meinem Fall noch frei und könnte über eine dritte IP genutzt werden (passiert wenn man 2 Sendungen hintereinander aufnimmt und diese überschneiden sich z.B. 4 Minuten).
Insgesamt alles "verbesserungswürdig"...
Was tut mein neues Skript?
-
Exit-Händler definieren, falls Aufnahmen vorzeitig beendet werden soll.
- Alle Kind-Prozesse killen (z.B.
ffmpeg) -
running-Ticket für IP löschen - Den Hauptprozess killen
- Alle Kind-Prozesse killen (z.B.
-
Prüfen ob meine virtuellen Interfaces vorhanden sind.
- Interfaces nicht vorhanden
- Es erzeugt die vier virtuellen Interfaces
vrt0bisvrt3, welche allesamt über das reale Interfaceeth1laufen. Alle vier haben unterschiedliche Dummy-MAC-Adressen (eigene MAC-Adressen sind notwendig, damit der Router per DCHP eigenständige IPs vergibt). - Es startet für jedes Interface einen
dhclientum per DHCP eine IP vom DHCP-Server zu bekommen.
- Es erzeugt die vier virtuellen Interfaces
- Interfaces vorhanden
- IPs auslesen.
- Interfaces nicht vorhanden
-
Freie IP (aus den ausgelesen vom zweiten Schritt) für Aufnahme finden anhand
running-Ticket.-
running-Ticket für IP vorhanden, dann nächste IP prüfen. - Kein
running-Ticket vorhanden...- Das
running-Ticket schreiben - Aufnahme starten
- Wenn Aufnahme normal beendet wird, dann wird das Ticket wieder gelöscht und der Prozess beendet.
- Das
-
Resultat
Ich kann also nun 4 Aufnahmen auf 2 unterschiedlich Sendern gleichzeitig durchführen. Sollte eine der 4 Aufnahme einen dritten Sender aufnahmen wollen, schlägt dieser ffmpeg-Prozess fehl und die Sendung wird nicht aufgenommen. Die anderen Prozesse laufen normal weiter. Es gilt: Wer zuerst kommt... 😄
#!/bin/sh
# exit or kill handler
kill_child_processes() {
isTopmost=$1
curPid=$2
childPids=`ps -o pid --no-headers --ppid ${curPid}`
for childPid in $childPids
do
echo "kill child $childPid"
kill_child_processes 0 $childPid
done
if [ $isTopmost -eq 0 ]; then
echo "kill parent $curPid"
kill -9 $curPid 2> /dev/null
fi
if [ ! -z "$deletefile" ] && [ -f $deletefile ]; then
echo "delete $deletefile"
rm -- "$deletefile"
fi
}
trap "kill_child_processes 1 $$; exit 0" 0
# create virtual interfaces
vrtip=$(ifconfig | awk -F"[: ]+" '/vrt/{getline; {print $4}}')
if [ -z "$vrtip" ]; then
echo "no vrt interfaces found"
for i in "0" "1" "2" "3"; do
echo "create vrt$i"
ip link add link eth1 address 00:11:22:33:44:"$i$i" vrt"$i" type macvlan
/usr/sbin/dhclient -4 -nw -lf /tmp/dhcpv4.leases.vrt"$i" -pf /tmp/dhcpcd-vrt"$i".pid -sf /var/run/dhclient-script vrt"$i"
done
sleep 10
vrtip=$(ifconfig | awk -F"[: ]+" '/vrt/{getline; {print $4}}')
fi
echo "vrtip: $vrtip"
# find free ip and try to record
for i in $vrtip; do
runningfile="running_$i.run"
if [ -f $runningfile ]; then
echo "$runningfile exists"
else
echo "$runningfile not exists"
echo "running now" >> $runningfile
echo "using bind adress $i"
deletefile="$runningfile"
BIND_ADDR="$i" LD_PRELOAD=/usr/lib/bind.so /volume1/\@appstore/ffmpeg/bin/ffmpeg "$@"
rm -- "$runningfile"
unset deletefile
break
fi
done
Schlusswort
Das ist aber noch nicht die Endfassung des Skriptes. Aktuell benutze ich ein ffmpeg über eine Community-Quelle und das ist fest im Skript angegeben. Dieses Setting könnte auch als Argument an das Skript gegeben werden. Außerdem könnte man auch die Anzahl der zu erzeugenden virtuellen Interfaces als Argument verarbeiten. Es ist also immer noch Luft noch oben.
Ich hoffe man blickt einigermaßen durch.
VG Daniel