Python Subprocess, Queue und Speichermanagement

jb_alvarado

Lieutenant
Registriert
Sep. 2015
Beiträge
596
Hallo Allerseits,
seit ein paar Jahren entwickle ich eine Playout-Software basierend auf Python. Das Programm spielt Video-Playlisten im 24/7 Betrieb ab und das fast Zeitgenau. Da ich mit diesem Programm angefangen habe Python zu lernen, ist der Code stellenweise sicher verbesserungsfähig, aber darum soll es jetzt nicht gehen.

Was immer wieder mal gewünscht wird, ist die Möglichkeit einen Livestream mit einzubinden. Also die Playlist soll normal abspielen, aber zusätzlich soll die Möglichkeit bestehen z.B. per rtmp an das Programm zu streamen und dieses spielt dann diesen Stream weiter. Ist der Livestream vorbei, soll die Playliste weiterspielen.

Ich habe das mal ein bisschen getestet und grundsätzlich scheint es mit diesem Code zu funktionieren:

Python:
#!/usr/bin/env python3

from subprocess import PIPE, Popen
from threading import Thread
from time import sleep
from types import SimpleNamespace
from queue import Queue

buffer_size = 65424
streaming_queue = Queue(maxsize=0)

pre_settings = [
    '-pix_fmt', 'yuv420p', '-s', '1024x576', '-r', '25', '-c:v', 'mpeg2video',
    '-g', '1', '-b:v', '50000k', '-minrate', '50000k', '-maxrate', '50000k',
    '-bufsize', '25000k', '-c:a', 'mp2', '-b:a', '384k', '-ar', '48000',
    '-ac', '2', '-f', 'mpegts', '-']

ff_proc = SimpleNamespace(live=None, decoder=None, encoder=None)

dec_cmd = [
    'ffmpeg', '-v', f'level+error',
    '-hide_banner', '-nostats', '-f', 'lavfi',
    '-i', 'testsrc=duration=3600:size=1024x576:rate=25',
    '-f', 'lavfi', '-i', 'anoisesrc=d=3600:c=pink:r=48000:a=0.2'
    ] + pre_settings

enc_cmd = [
    'ffplay', '-hide_banner', '-nostats', '-v', 'level+error', '-i', 'pipe:0']

ff_proc.encoder = Popen(enc_cmd, stderr=None, stdin=PIPE, stdout=None)


def rtmp_server(que):
    server_cmd = [
    'ffmpeg', '-hide_banner', '-nostats', '-v', 'level+error',
    '-f', 'live_flv', '-listen', '1',
    '-i', 'rtmp://localhost:1935/live/stream'] + pre_settings

    while True:
        with Popen(server_cmd, stderr=None, stdout=PIPE) as ff_proc.live:
            while True:
                buffer = ff_proc.live.stdout.read(buffer_size)
                if not buffer:
                    break
                que.put(buffer)
        sleep(.33)


def terminate_processes():
    """
    kill orphaned processes
    """
    if ff_proc.decoder and ff_proc.decoder.poll() is None:
        ff_proc.decoder.terminate()
    if ff_proc.live and ff_proc.live.poll() is None:
        ff_proc.live.terminate()
    if ff_proc.encoder and ff_proc.encoder.poll() is None:
        ff_proc.encoder.kill()


rtmp_server_thread = Thread(target=rtmp_server, args=(streaming_queue,))
rtmp_server_thread.daemon = True
rtmp_server_thread.start()

try:
    with Popen(dec_cmd, stdout=PIPE, stderr=None) as ff_proc.decoder:
        while True:
            buf_dec = ff_proc.decoder.stdout.read(buffer_size)
            if not streaming_queue.empty():
                buf_live = streaming_queue.get()
                ff_proc.encoder.stdin.write(buf_live)
                del buf_dec
            elif buf_dec:
                ff_proc.encoder.stdin.write(buf_dec)
            else:
                break
except (KeyboardInterrupt, BrokenPipeError):
    terminate_processes()

War nur schnell zusammengeschustert...

Bei meinem Programm ist mir Stabilität wichtiger, als Funktionsumfang. Da ich schon mal eine Meldung bekommen habe, wegen Speicherüberlauf, diesen selbst aber noch nicht reproduzieren konnte, mache ich mir jetzt natürlich Gedanke ob es bei dieser Konstruktion hier langfristig Probleme geben kann. Vielleicht erst nach Woche, aber das wäre schlimm genug, weil man das schwierig debuggen kann.


Wenn ihr das so überfliegt, fallen euch dabei problematische Stellen auf? Abgesehen von Schönheitsfehlern und der Gleichen.
 
Habs nur überflogen und hab null Tau von Medienwiedergaber via Python, aber:
gibts eine Prüfung zwischendurch, dass der Livestream noch OK ist?
sprich: Livestream wird korrekt gestartet, bleibt aber hängen oder endet unerwarteterweise, ohne dass das durch die while True-Schleife erkannt wird?
 
Für den Livestream an sich ist keine Prüfung vorgesehen. Der Gedanke ist, dass im Hintergrund die Playlist immer abspielt und nur wenn der Livestream etwas rein bekommt, wird das ausgegeben.

Die while true Schleife in der rtmp_server Funktion ist deshalb da, weil ffmpeg stoppt wenn es keinen Input mehr bekommt.
 
mhm dann sind wir meinem Gedanken schön näher und ich formuliere meine Frage um:
was passiert, wenn ffmpeg glaubt einen input zu haben der nimmer da ist? gibt es einen API call an ffmpeg im sinne von: ist die Wiedergabe noch ok - gültiger input hin oder her?

oder ist man hier ffmpeg "ausgeliefert"?
 
Im Grunde ist man hier ffmpeg ausgeliefert, aber das finde ich auch gut so. Für ffmpeg gilt nur nur: es gibt einen Input, oder es gibt Keinen. Sobald ffmpeg keinen Input mehr bekommt, bricht es ab, was in meinem Fall auch gut so ist, weil das Python Programm dann sofort zur Playliste umschwenken soll. Da darf es nämlich keine Lücke geben.

Man könnte dem ffmpeg in der Server Funktion auch einen Timeout mitgeben, aber das wäre kontraproduktiv.
 
  • Gefällt mir
Reaktionen: mercury
Zurück
Oben