Ich habe eine Python -GUI für einen Media -Player geschrieben (eine gemeinsame Bibliothek, die ich von FFPlay in FFMPEG erstellt habe). Es verwendet die Wiederholungsverstärkung, um die Lautstärke so anzupassen, dass die meisten Musik gleichermaßen laut klingen. Ich konnte keine gemeinsame Bibliothek finden, die die Wiederholungsverstärkung berechnet, sodass ich FFMPEG als Subprozess ausführe, um die Replay -Verstärkung über ein Rohr über die Stderr -Ausgabe zurückzugeben. Ich verwende Threads, weil das Ausführen der Subprozessbefehle aus der Haupt -GUI in der Wiedergabe von Dateien kurze Lücken oder Schluckaufe verursacht (der Player wird auch in einem Thread ausgeführt). Ich habe festgestellt, dass die Python -GUI, wenn mehr als einer der Fäden für FFMPEG gleichzeitig ausgeführt wird, die Python -GUI nicht sauber herunterfahren wird. Ich benutze Geany für meine Ide; Es wird das Programm in einem Terminalfenster ausgeführt. Wenn alles so endet, wie es sollte, werden Nachrichten im Fenster angezeigt "(Programm mit Code: 0) Drücken Sie die Rückgabe, um fortzusetzen." Durch das Drücken der Eingabetaste wird das Fenster sofort geschlossen. Ich muss es entweder schließen, indem ich in der oberen linken Ecke auf das X klicke oder Strg+c. Auf meinem System werden die meisten Fäden in etwa 2 Sekunden vervollständigt. Indem das Timer -Intervall auf 3 Sekunden oder mehr festgelegt wird, wird alles richtig geschlossen. Wenn ich das Intervall auf etwa 2 Sekunden oder weniger verkürze, tritt das oben beschriebene Problem auf. Ich habe derzeit den Timer auf 100 ms eingestellt. Aus einer Vielzahl von Gründen kann ich nicht länger ein Intervall verwenden. Dieses Beispiel hat nicht den Spieler. Es ist nur ein Widget, das eine Folge von Threads startet, die jeder FFMPEG in einem Subprozess ausführen. < /P>
Der Beispielcode ist unten angezeigt. Ich leite Python 3.12, Pyqt 5.15 auf Linux Mint 22.1. Nur für den Fall, dass es wichtig ist, verwende ich FFMPEG 7.1. < /P>
import sys, subprocess, queue, threading
from PyQt5 import QtWidgets, QtGui, QtCore
class AudioPlayerDialog(QtWidgets.QDialog):
def __init__(self, mediaList):
super().__init__()
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(QtWidgets.QLabel('WIDGET'))
self.setLayout(vbox)
self.calcGainTimer = None
self.gdCalcThreads = None
self.finished.connect(self.closeEvent)
self.mediaList = mediaList
self.mediaListLen = len(mediaList)
self.normGainList = [-1. for x in mediaList]
self.buildNormGainList()
def closeEvent(self, event):
print('**** close event')
if self.calcGainTimer is not None:
print('**** stopping calcGainTimer')
self.calcGainTimer.stop()
if self.gdCalcThreads is not None:
print('**** checking gain calc threads')
for idx, t in self.gdCalcThreads.copy().items():
if t.is_alive():
print('**** waiting for thread {} to finish'.format(idx))
t.join()
print('**** closing ...')
def buildNormGainList(self):
self.calcGainQueue = queue.LifoQueue(maxsize=self.mediaListLen + 10)
for i in range(self.mediaListLen-1, -1, -1):
self.calcGainQueue.put_nowait(i)
# setup gain/dur calculation thread dictionary
self.gdCalcThreads = dict()
# For each expiration of the following timer, a thread will be
# started to calculate the replay gain for a particular file
# and a check will determine if all files have been processed.
print('@@@@ starting updt timer')
self.calcGainTimer = QtCore.QTimer()
self.calcGainTimer.timeout.connect(self.stopGainDurCalcWhenDone)
self.calcGainTimer.setInterval(100) # interval in msec.
self.calcGainTimer.start()
def stopGainDurCalcWhenDone(self):
# this is the target of the updt timer
try:
# start a thread if the queue is not empty
idx = self.calcGainQueue.get_nowait()
if idx not in self.gdCalcThreads:
print('@@@@ creating thread for', idx)
thread = threading.Thread(None, self.requestGain, args=(idx,))
self.gdCalcThreads[idx] = thread
thread.start()
except queue.Empty:
pass
except Exception as err:
print('**** ERROR: get request from calcGainQueue failed.\n\t'\
'{}'.format(err))
# check if any thread finished but updates not yet finished
for idx,t in self.gdCalcThreads.items():
if not t.is_alive() and self.normGainList[idx] < 0.:
# Thread finished; update not yet completed. Invert the
# sign of the gain, which indicates update completed.
self.normGainList[idx] = -self.normGainList[idx]
print('@@@@ update finished for', idx)
# check if all files have been processed; stop updt timer if so
done = True # assume all calc done
for i in range(self.mediaListLen):
if self.normGainList < 0.:
done = False
break
if done:
print('@@@@ all calc are done -----------------------')
# all files have been processed
print('@@@@ stopping updt timer')
self.calcGainTimer.stop()
self.calcGainTimer = None
self.gdCalcThreads = None
TEST = False # set True to pass dummy filename of ffmpeg
def requestGain(self, idx):
print('@@@@ start subprocess for', idx)
if self.TEST:
fileName = 'XXXX'
else:
fileName = self.mediaList[idx]
cmd = ['ffmpeg', '-i', '{}'.format(fileName), '-hide_banner',
'-af', 'replaygain', '-f', 'null', '-']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
_,err = proc.communicate(timeout=30)
text = err.decode('utf8')
except subprocess.TimeoutExpired:
print('**** Wait for report from subprocess expired')
proc.kill()
text = ''
except Exception as err:
print('**** Unable to get report from subprocess: {}'.format(err))
proc.kill()
text = ''
# actual program parses gain from text
gain = 1.
self.normGainList[idx] = -gain # save negative gain
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
# put full-qualified filenames of media in list
mediaList = [
]
apd = AudioPlayerDialog(mediaList)
apd.exec()
print('\nFINI')
sys.exit()
< /code>
Ich habe versucht, den Subprozess ohne Rohr auszuführen. Das machte keinen Unterschied. Indem ein Dummy -Dateiname an ffmpeg übergeben wird. Dies führt dazu, dass das Programm schnell endet. Nichts Nützliches wird getan, aber es verhindert, dass zwei Threads gleichzeitig ausgeführt werden und zeigt, dass das Programm sauber heruntergefahren wird. Der Player -Thread sowie ein einzelner Thread für den FFMPEG -Subprozess verursachen kein Problem. Zwei FFMPEG -Subprozess -Threads allein (ohne Spieler) verursachen das Problem. < /P>
Ich muss etwas falsch machen, aber ich kann nicht sehen, was es ist. Vielleicht können Sie es sehen? < /P>
Bearbeiten: Ich habe mehr Tests durchgeführt. Ich öffnete zwei Terminalfenster und lief in einem Fenster, um Prozesse zu überwachen, und leitete das Python -GUI -Programm im anderen Fenster. Auch durch diese Threads endeten. Schließlich endeten alle FFMPEG -Prozesse und nur der Python -Prozess und sein Bash -Fenster blieben. Ich habe das Python -Programm beendet und seine Prozesse endeten und verschwanden. Wenn Sie in der oberen rechten Ecke auf das X klicken, schloss das Fenster und dieser Bash-Prozess verschwand oben. Wie kann ich feststellen, was nicht richtig endet? Diese Änderungen haben das Problem nicht gelöst. Alle Vorschläge, um mehr darüber zu erfahren, werden geschätzt. Ich kann das Beispiel angeben, wenn jemand interessiert ist. < /P>
Diese Änderung hat nicht geholfen. Gleiche Erfahrung. Das Programm scheint OK herunterzufahren, aber wie ich oben beschreibe, bleibt etwas hängen. Mehrere Threads liefen gleichzeitig, aber das Programm wurde sauber geschlossen. Der Subprozessaufruf hat etwas, das nicht vollständig endet. Wie ich oben erwähnt habe, verschwinden diese Prozesse aus dem, was Top zeigt, aber es gibt etwas, das nicht sichtbar ist, das Top verweilt. Ich werde den Titel dieses Beitrags bearbeiten, um Subprozess zu erwähnen. Siehe meine Antwort unten.
Warum endet mein Python -Programm nicht erfolgreich, wenn mehrere Instanzen von Subprozess.Popen ffmpeg (und nur ffmpeg) ⇐ Python
-
- Similar Topics
- Replies
- Views
- Last post