*.stdout.readline von Binarystream?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
jb_alvarado
User
Beiträge: 55
Registriert: Mittwoch 11. Juli 2018, 11:11

Hallo Allerseits,

ich habe zwei subprocesse die ich gerne über stdout und stdin Verbinden möchte. Der Quell Prozess läuft in einem Thread und verarbeitet Videodateien nacheinander. Der Zielprozess läuft im Mainthread und soll, im Gegensatz zum Quellprozess konstant bestehen bleiben.

Verbinden tue ich die beiden mit einer Queue. Das ganze schaut dann in etwa so aus (ist noch ziemlich WIP):

Code: Alles auswählen

# independent thread for clip preparation
def play_clips(buffer, GetSourceIter):
    get_source = GetSourceIter()

    for src_cmd in get_source.next():
        try:
            decoder = Popen([
                'ffmpeg', '-hide_banner', '-nostats'
                ] + src_cmd + ['-pix_fmt', 'yuv420p', '-r', '25',
                '-c:v', 'mpeg2video', '-intra', '-q:v', '2',
                '-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2',
                '-threads', '2', '-f', 'mpegts', '-'], stdout=PIPE)

            for data in iter(decoder.stdout.readline, ''):
                if not data:
                    break

                buffer.put(data)

        finally:
            decoder.wait()

    else:
        buffer.put(None)


def main():
    buffer = Queue(maxsize=120)
    try:
        playout = Popen([
            'ffplay',
            '-hide_banner', '-nostats', '-i', 'pipe:0'],
            stderr=None,
            stdin=PIPE,
            stdout=None
            )

        play_thread = Thread(
            name='play_clips', target=play_clips, args=(
                buffer, GetSourceIter,)
            )
        play_thread.daemon = True
        play_thread.start()

        while True:
            data = buffer.get()
            if not data:
                playout.terminate()
                break

            playout.stdin.write(data)

    finally:
        playout.wait()
Im Grunde funktioniert das so auch schon, allerdings frage ich mich, ob hier decoder.stdout.readline wirklich das richtige ist? Gibt es hier noch eine andere Möglichkeit? Einen Binarystream Zeile für Zeile zu lesen scheint mir etwas seltsam zu sein.

Dazu kommt auch, dass beim Quellprozess einen MPEG-TS Stream erzeugt wird und dieser sollte exakt 188 Byte große Pakete schicken, mir scheint aber dass die Pakete die in der Queue landen unterschiedlich groß sind. Ist jetzt nur eine Vermutung, aber wenn ich die Startzeit mit der Länge der einzelnen Videoclips aufrechnen und mir die Differenzen ausgeben lassen, bekomme ich nicht exakte Ergebnisse, die sich von Clip zu Clip unterscheiden.
Zuletzt geändert von jb_alvarado am Dienstag 28. Mai 2019, 08:34, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dann nimm doch read & lies Blöcke ein. Gerne auch 188 Bit lange. Wobei das nicht durch 8 teilbar ist.
jb_alvarado
User
Beiträge: 55
Registriert: Mittwoch 11. Juli 2018, 11:11

188 Bit war falsch, es sind 188 Byte, also wäre das kein Problem. Ich versuche das mal mit read().
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jb_alvarado: Wie stellst Du denn fest das die Pakete andere grössen als 188 Bytes haben? Das sind ja Binärdaten und die MPEG-TS-Pakete sind ja auch nicht mit einem Zeilenende-Zeichen abgeschlossen.

Machst Du da sonst noch irgend etwas? Weil so wie es da jetzt steht, sieht das ziemlich unnötig umständlich aus. Warum der zusätzliche Thread? Warum selbst Code schreiben der Daten von einem Prozess zum anderen schaufelt, wenn man die doch einfach direkt über die Pipe verbinden könnte?
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
jb_alvarado
User
Beiträge: 55
Registriert: Mittwoch 11. Juli 2018, 11:11

@__blackjack__, das mit der Paketgröße war nur eine Vermutung, weil ich einen Teststream über mehre Stunden laufen ließ. Dabei arbeitet mein Script eine Playliste ab, die einen festgelegten Startzeitpunkt hat, und jeder Clip hat eine festgelegte Länge. Zur Messung habe ich mir nach jedem Clip die Zeit ausrechnen lassen, ob mein Stream noch zum Playlistenstart synchron ist. Dabei variieren die Zeiten zwischen 0.5 und 2 Sekunden, einmal gab es auch eine Spitze von über 20 Sekunden. Kann das aber nicht genau belegen, ob es am readline liegt. Einmal hatte ich den Fehler drin, dass mein Quellprozess keine konstante Bitrate erzeugt und zum anderen weiß ich nicht was mit readline wirklich ausgelesen wird. Da ist das ganze mit *.read(188), wie __deets__ vorgeschlagen hat, schon genauer, vermute ich mal.

Das Konstrukt und der Thread kommen zustande weil ich in dem Quellprozess auch GetSourceIter() aufrufe und dort werden Playlisten aufgerufen und verarbeitet. Ursprünglich war das Script noch anders aufgebaut, da hatte ich noch einen dritten subprocess, wo ein buffer drin lief und ich alle Prozesse überwachen musste, da musste ich mit einen Thread arbeiten, vielleicht würde es jetzt auch ohne gehen. Ich brauche beim Quellprozess halt so geringe Reaktionszeiten wie möglich.

Direkt pipen geht nicht, weil der Quellprozess bei jeder Videodatei einen neuen subprocess startet, der Zielprozess soll aber konstant durchlaufen muss.
jb_alvarado
User
Beiträge: 55
Registriert: Mittwoch 11. Juli 2018, 11:11

Noch mal kurz einen Nachtrag: die 188 in .read() ist zu klein, dafür ist python wohl zu langsam :). Habe den Wert jetzt auf 65424 gesetzt und damit scheint es sehr gut zu laufen. 65536 ist wohl die Linux pipe Buffer Größe und davon habe ich die nächst-kleinere Zahl genommen die durch 188 teilbar ist (keine Ahnung ob das jetzt groß einen Unterschied macht...).
Antworten