ubuntuusers.de

StdErr von FFmpeg von sed in Datei pipen

Status: Ungelöst | Ubuntu-Version: Nicht spezifiziert
Antworten |

theMatthias

Anmeldungsdatum:
20. März 2010

Beiträge: Zähle...

Servus, ich würde gerne die StdErr-Ausgabe von FFmpeg mit sed editieren und anschließend in eine Datei pipen. Soweit zur Theorie 😉

Die Konsolenausgabe (das was später in die Datei soll) bekomme ich schon soweit hin:

ffmpeg -i 0102_berserk.flv -y -f flv 0102_berserk.2.flv 2>&1 | sed "/^frame=/d"

Allerdings lässt sich das nicht wie folgt in eine Datei pipen!

ffmpeg -i 0102_berserk.flv -y -f flv 0102_berserk.2.flv 2>&1 | sed "/^frame=/d" >> output.txt

Irgendeine Ahnung warum das Pipen so nicht funktioniert? Danke vorab!

Grüße Matthias

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2505

Willkommen an Bo(a)rd. 😉

Kannste das irgendwie konkretisieren, was da nicht geht? Bei mir klappt das so und ich sähe jetzt auch auf Anhieb keinen Fehler dran.

theMatthias

(Themenstarter)

Anmeldungsdatum:
20. März 2010

Beiträge: 6

Sorry, vergaß ich zu erwähnen.. ☺ sed schreib erst nach dem Terminieren von ffmpeg in die angegebene Datei. Ich brauch die Info aber schon früher. (während ffmpeg noch läuft)..

Gruß Matthias

theMatthias

(Themenstarter)

Anmeldungsdatum:
20. März 2010

Beiträge: 6

Bisher habe ich den kompletten StdErr (von ffmpeg) fortlaufend in eine Datei geschrieben.

ffmpeg -i 0102_berserk.flv -y -f flv 0102_berserk.2.flv 2> output.txt

Diese Datei habe ich dann durch einen anderen (parallelen) Task (grep oä.) ausgelesen bzw. geparsed. Allerdings habe ich alle ungewollten "frame=" Informationen und Steuerzeichen ^M mit in der Datei. Die wird dann recht groß.. 😉

Gruß Matthias

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2505

Achso.

Ein Blick in man sed offenbart:

       -u, --unbuffered

              load minimal amounts of data from the input files and flush
              the output buffers more often

Versuch's also mal mit dem Parameter -u für sed. ☺

Übrigens, wenn dich das interessiert oder du auf weitere Probleme dieser Art stoßen solltest, kannst du dir die Abfolge der read()'s und write()'s anschauen:

ffmpeg -i bla.flv -y -f flv bla.2.flv 2>&1 | strace -tt -o /tmp/sed.log sed -u "/^frame=/d" >> bla.log

strace schreibt dir dann in /tmp/sed.log genau auf, was wann gelesen und geschrieben wird. Diese Logfile kannst du dann auch schön mit tail -f verfolgen.

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Hi Matthias,

Willkommen !

Geht es eigentlich ohne das "sed" zur Echtzeit ?
Dann könntest Du ja rein auf Verdacht mal "awk" probieren, vielleicht hat das ein anderes Zeitverhalten ?

Also:

... | awk '{if($0 !~ "^frame=")print;}' >> output.txt

Ich bin gespannt.

track

theMatthias

(Themenstarter)

Anmeldungsdatum:
20. März 2010

Beiträge: 6

Hi, danke für die lösungvorschläge! Ich bin dann bei sed geblieben. ☺

[...] sed -u  "/^frame=/d" >> irgendwo.txt

hat funktioniert.

strace -tt -o /tmp/sed.log sed -u "/^frame=/d"

strace kannte ich auch noch nicht! sehr interessant.. danke!

Beste Grüße Matthias

theMatthias

(Themenstarter)

Anmeldungsdatum:
20. März 2010

Beiträge: 6

hi, ich bin mittlerweile mit sed in der nächsten runde angekommen. 😉 nun bin ich dabei den output von ffmpeg auszulesen / zu parsen. problematisch ist allerdings, dass der output von ffmpeg durch CR (^M) getrennt ist. Um später die log files mittels tail besser auslesen zu können, will ich mit sed nun alle CR durch LF ersetzten.

ist es möglich mit sed alle CR (^M) durch LF (^J) zu ersetzten und trotzdem mit der option -u einen ungebufferten output in eine datei zu pipen ?

wenn ich den outout von ffmpeg in eine datei umleite:

ffmpeg -i bla.mpeg -y -vpre libx264-default bla.flv 2> test.log

und anschließend diese datei mit folgenden befhel auslese bekomme ich den gewünschten ouput

cat test.log| sed 's/'`echo -e "\015"`'/\n/g'

bzw:

[...]
frame=  971 fps= 31 q=18.0 size=     963kB time=31.09 bitrate= 253.8kbits/s dup=0 drop=29
frame= 1001 fps= 31 q=18.0 size=     999kB time=32.09 bitrate= 255.0kbits/s dup=0 drop=29
frame= 1028 fps= 30 q=18.0 size=    1032kB time=32.99 bitrate= 256.2kbits/s dup=0 drop=29
frame= 1058 fps= 31 q=18.0 size=    1101kB time=33.99 bitrate= 265.4kbits/s dup=0 drop=30
frame= 1077 fps= 31 q=18.0 size=    1126kB time=34.63 bitrate= 266.4kbits/s dup=0 drop=30
[...]

In folgendem scenario wird der output von sed erst nach beenden von ffmpeg geschrieben:

ffmpeg -i bla.mpeg -y -vpre libx264-default bla.flv2>&1 | sed -u 's/'`echo -e "\015"`'/\n/g' >> test.log

durch strace bekomme ich folgenden output: [...]

08:35:37.984883 read(0, "frame=  412 fps= 30 q=19.0 size="..., 1024) = 94
08:35:39.693520 read(0, "frame=  447 fps= 30 q=19.0 size="..., 1024) = 94
08:35:40.496080 read(0, "frame=  492 fps= 31 q=19.0 size="..., 1024) = 94
08:35:42.013259 read(0, "frame=  533 fps= 32 q=19.0 size="..., 1024) = 94
08:35:42.042531 read(0, "frame=  571 fps= 32 q=19.0 size="..., 1024) = 94
08:35:43.541971 read(0, "frame=  603 fps= 31 q=19.0 size="..., 1024) = 94
08:35:44.792048 read(0, "frame=  608 fps= 30 q=18.0 size="..., 1024) = 94
08:35:45.479216 read(0, "frame=  643 fps= 30 q=18.0 size="..., 1024) = 94
08:35:46.620965 read(0, "frame=  673 fps= 31 q=18.0 size="..., 1024) = 94
08:35:47.396273 read(0, "frame=  685 fps= 30 q=18.0 size="..., 1024) = 188
08:35:49.058300 read(0, "frame=  720 fps= 30 q=18.0 size="..., 1024) = 94

[...]

sed liest die einzelnen sequenzen nur, schreibt diese aber nicht. komich, komich.. 😉

gruß matthias

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2505

Ok, nun wird's langsam interessant. 😀 😉

Eigentlich täte ich sagen, nimm tr, wenn du \r nach \n umwandeln willst. Problematisch dabei ist, dass tr wieder "buffered" ist und du ja den Output live beobachten willst.

Mit sed gibt es das Problem, dass es zeilenweise arbeitet und eine Zeile ist für sed erst bei einem Newline zuende – und der kommt in deinem Output erst ganz am Schluss vor, nachdem der ganze Progress-Kram ausgegeben wurde. Ich wüsste jetzt auch nicht, wie man sed beibringen könnte, dass es ein anderes Zeichen als Zeilenabschluss ansieht.

Die Struktur der Ausgabe ist also:

[großer Header]
\n
[1. Progress-Zeile]
\r[2. Progress-Zeile]
\r[3. Progress-Zeile]
...
\r[x. Progress-Zeile]
\n
[Rest]
\n

Was mir da jetzt einfiele, wäre awk. Dem kannst du sagen, was er als "Newline" (hier: Record Separator) betrachten soll. Außerdem kannst du ihm mit fflush() sagen, dass die Ausgabe genau jetzt erfolgen soll. Sowas zum Beispiel:

ffmpeg -i bla.flv -y -f flv bla.2.flv 2>&1 | awk 'BEGIN {RS="\r"} {print; fflush();}'

awk sieht also jetzt "[großer Header]\n[1. Progress-Zeile]" als den ersten Record an und gibt ihn sofort aus. Die nächste Progress-Zeile ist dann der nächste Record und so weiter. Als Zeilentrenner für die Ausgabe nimmt awk hier immernoch ein Newline, da wir nur RS verändert haben, aber nicht ORS.

Die Ausgabe hiervon kannst du nun wieder nach sed -u pipen. Keine Ahnung, was du dann tun willst, aber das hier würde zum Beispiel immer den aktuellen Zeitpunkt im Video ausgeben:

ffmpeg -i bla.flv -y -f flv bla.2.flv 2>&1 | awk 'BEGIN {RS="\r"} {print; fflush();}' | sed -u -E '/^frame/ s/.*time=([^ ]*) .*/ZEIT: \1/'

Wenn das alles irgendwann doch zu komplex wird, hast du natürlich immernoch die Möglichkeit, die Ausgabe von ffmpeg in ein eigenes Skript zu pipen. Da kannst du dann machen, was du willst, und musst dich nicht mit den Buffern rumschlagen (zumindest nicht in dieser Art und Weise).

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Also, hier noch ein sed dran zu pipen, das würde ich nicht tun !

Das awk hat doch so ungefähr 1002 Möglichkeiten, da kannst Du den Rest der Manipulationen
auch gleich da mit machen ...

Um z.B. die Zeit mit zu drucken reicht die Funktion strftime():

ffmpeg -i bla.flv -y -f flv bla.2.flv 2>&1 | awk 'BEGIN {RS="\r"} {print  strftime() ": " $0; fflush();}'


track

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2505

Jau, klar. ☺ Wie gesagt, keine Ahnung, was er damit am Ende machen will – und eigentlich soll das ja auch erstmal in eine Datei, also wird er da hintendran sowieso nix mehr machen wollen. Denke ich.

Das war auch mehr als Beispiel gedacht, wie es dann mit ("normalen") Zeilen weitergehen könnte. Erschien mir so am übersichtlichsten in dem Moment. 😉

(Die Zeit bei mir ist übrigens der Zeitstempel im Video, an dem sich ffmpeg gerade befindet und nicht die aktuelle Uhrzeit. Hätte man mit awk aber auch machen können, klar.)

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Ach so, Du setzt nur "time=" in "ZEIT: " um, und sortierst sie dann um ... ?

Na ja, das sind in awk auch nur drei Nebensätze: sub("time=","ZEIT: "); f9=$9; $9=""; print $f9,$0;


track

theMatthias

(Themenstarter)

Anmeldungsdatum:
20. März 2010

Beiträge: 6

Hi Vaim, Hi Track, ich habe mich für eine einfachere Variante entschieden. 😉

Zunächst pipe ich die komplette Konsolenausgabe von ffmpeg in eine Datei (inklusive CR (^M)). Diese Datei lese ich dann innerhalb eines anderen Prozesses aus. Hier bestand das Problem, dass ich die letzte Zeile nicht durch tail oä auslesen konnte (wegen dem CR).

Zwischenzeitlich habe ich dann die komplette Log-Datei mit tr bearbeitet und alle CR durch NL ersetzt, was sehr zeitaufwendig würde da die Größe der Logdatei auf einige MB ansteigt.

Aus diesem Grund lesen ich nun mittels "tail" nur die letzten 300 Zeilen aus (tail -c) und bearbeite das anschließend mit "tr" um dann wieder mit tail die letzte Zeile auszulesen.

also:

tail -c 300 logfile.log | tr "\r" "\n" | tail -n 1 | (weiteres auslesen mittels awk etc..)

anders formuliert: "von hinten durch die Brust ins Auge" 😉

Vielen Dank für Eure Hilfe! Matthias

Antworten |