Получение результата в реальном времени из ffmpeg для использования в индикаторе выполнения (PyQt4, stdout)

Я рассмотрел ряд вопросов, но до сих пор не могу понять это. Я использую PyQt, и я надеюсь запустить ffmpeg -i file.mp4 file.avi и получить вывод по мере его потока, чтобы я мог создать индикатор выполнения.

Я рассмотрел следующие вопросы: Может ли ffmpeg показать индикатор выполнения? ловить stdout в реальном времени из подпроцесса

Я могу видеть вывод команды rsync, используя этот код:

 import subprocess, time, os, sys cmd = "rsync -vaz -P source/ dest/" p, line = True, 'start' p = subprocess.Popen(cmd, shell=True, bufsize=64, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) for line in p.stdout: print("OUTPUT>>> " + str(line.rstrip())) p.stdout.flush() 

Но когда я меняю команду на ffmpeg -i file.mp4 file.avi я не получаю никакого вывода. Я предполагаю, что это имеет какое-то отношение к буферизации stdout / output, но я зациклился на том, как читать строку, которая выглядит как

 frame= 51 fps= 27 q=31.0 Lsize= 769kB time=2.04 bitrate=3092.8kbits/s 

Что я мог бы использовать, чтобы понять прогресс.

Может ли кто-нибудь показать мне пример того, как получить эту информацию из ffmpeg в python, с использованием или без использования PyQt (если возможно)


EDIT: я закончил работу с решением jlp, мой код выглядел так:

 #!/usr/bin/python import pexpect cmd = 'ffmpeg -i file.MTS file.avi' thread = pexpect.spawn(cmd) print "started %s" % cmd cpl = thread.compile_pattern_list([ pexpect.EOF, "frame= *\d+", '(.+)' ]) while True: i = thread.expect_list(cpl, timeout=None) if i == 0: # EOF print "the sub process exited" break elif i == 1: frame_number = thread.match.group(0) print frame_number thread.close elif i == 2: #unknown_line = thread.match.group(0) #print unknown_line pass 

Что дает этот результат:

 started ffmpeg -i file.MTS file.avi frame= 13 frame= 31 frame= 48 frame= 64 frame= 80 frame= 97 frame= 115 frame= 133 frame= 152 frame= 170 frame= 188 frame= 205 frame= 220 frame= 226 the sub process exited 

Отлично!

6 Solutions collect form web for “Получение результата в реальном времени из ffmpeg для использования в индикаторе выполнения (PyQt4, stdout)”

Единственный способ найти динамическую обратную связь / выход из дочернего процесса – использовать что-то вроде pexpect:

 #! /usr/bin/python import pexpect cmd = "foo.sh" thread = pexpect.spawn(cmd) print "started %s" % cmd cpl = thread.compile_pattern_list([pexpect.EOF, 'waited (\d+)']) while True: i = thread.expect_list(cpl, timeout=None) if i == 0: # EOF print "the sub process exited" break elif i == 1: waited_time = thread.match.group(1) print "the sub process waited %d seconds" % int(waited_time) thread.close() 

вызываемый подпроцесс foo.sh просто ждет случайного промежутка времени между 10 и 20 секундами, вот код для него:

 #! /bin/sh n=5 while [ $n -gt 0 ]; do ns=`date +%N` p=`expr $ns % 10 + 10` sleep $p echo waited $p n=`expr $n - 1` done 

Вы хотите использовать какое-то регулярное выражение, которое соответствует выходу, получаемому из ffmpeg, и делает какие-то вычисления на нем, чтобы показать индикатор выполнения, но это, по крайней мере, даст вам небуферизованный вывод из ffmpeg.

В этом конкретном случае для захвата вывода статуса ffmpeg (который идет в STDERR), этот вопрос SO решил это для меня: подпроцесс FFMPEG и Pythons

Хитрость заключается в том, чтобы добавить universal_newlines=True в вызов subprocess.Popen() , потому что вывод ffmpeg фактически небуферизирован, но поставляется с символами новой строки.

 cmd = "ffmpeg -i in.mp4 -y out.avi" process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,universal_newlines=True) for line in process.stdout: print(line) 

Также обратите внимание, что в этом примере кода вывод состояния STDERR напрямую перенаправляется на subprocess.STDOUT

  1. Вызов из оболочки обычно не требуется.
  2. Я знаю из опыта, что часть выхода ffmpeg поступает на stderr , а не на stdout .

Если все, что вы хотите сделать, это распечатать выходную строку, как в приведенном выше примере, просто выполните следующее:

 import subprocess cmd = 'ffmpeg -i file.mp4 file.avi' args = cmd.split() p = subprocess.Popen(args) 

Обратите внимание, что строка чата ffmpeg завершается с помощью \r , поэтому она будет перезаписана в одной строке! Я думаю, это означает, что вы не можете перебирать строки в p.stderr , как это p.stderr с вашим примером rsync. Чтобы создать свой собственный индикатор выполнения, вам, возможно, потребуется обработать чтение самостоятельно, это должно начать:

 p = subprocess.Popen(args, stderr=subprocess.PIPE) while True: chatter = p.stderr.read(1024) print("OUTPUT>>> " + chatter.rstrip()) 

Эти ответы не сработали для меня: / Вот как я это сделал.

Это из моего проекта KoalaBeatzHunter .

Наслаждайтесь!

 def convertMp4ToMp3(mp4f, mp3f, odir, kbps, callback=None, efsize=None): """ mp4f: mp4 file mp3f: mp3 file odir: output directory kbps: quality in kbps, ex: 320000 callback: callback() to recieve progress efsize: estimated file size, if there is will callback() with % Important: communicate() blocks until the child process returns, so the rest of the lines in your loop will only get executed after the child process has finished running. Reading from stderr will block too, unless you read character by character like here. """ cmdf = "ffmpeg -i "+ odir+mp4f +" -f mp3 -ab "+ str(kbps) +" -vn "+ odir+mp3f lineAfterCarriage = '' print deleteFile(odir + mp3f) child = subprocess.Popen(cmdf, shell=True, stderr=subprocess.PIPE) while True: char = child.stderr.read(1) if char == '' and child.poll() != None: break if char != '': # simple print to console # sys.stdout.write(char) # sys.stdout.flush() lineAfterCarriage += char if char == '\r': if callback: size = int(extractFFmpegFileSize(lineAfterCarriage)[0]) # kb to bytes size *= 1024 if efsize: callback(size, efsize) lineAfterCarriage = '' 

Затем вам нужно еще 3 функции для его реализации.

 def executeShellCommand(cmd): p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE) out, err = p.communicate() return out.rstrip(), err.rstrip(), p.returncode def getFFmpegFileDurationInSeconds(filename): cmd = "ffmpeg -i "+ filename +" 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//" time = executeShellCommand(cmd)[0] h = int(time[0:2]) m = int(time[3:5]) s = int(time[6:8]) ms = int(time[9:11]) ts = (h * 60 * 60) + (m * 60) + s + (ms/60) return ts def estimateFFmpegMp4toMp3NewFileSizeInBytes(duration, kbps): """ * Very close but not exact. duration: current file duration in seconds kbps: quality in kbps, ex: 320000 Ex: estim.: 12,200,000 real: 12,215,118 """ return ((kbps * duration) / 8) 

И, наконец, вы делаете:

 # get new mp3 estimated size secs = utls.getFFmpegFileDurationInSeconds(filename) efsize = utls.estimateFFmpegMp4toMp3NewFileSizeInBytes(secs, 320000) print efsize utls.convertMp4ToMp3("AwesomeKoalaBeat.mp4", "AwesomeKoalaBeat.mp3", "../../tmp/", 320000, utls.callbackPrint, efsize) 

Надеюсь, это поможет!

Если у вас есть продолжительность (которую вы также можете получить с выхода FFMPEG), вы можете рассчитать прогресс, прочитав время, прошедшее с момента окончания (время) при кодировании.

Простой пример:

  pipe = subprocess.Popen( cmd, stderr=subprocess.PIPE, close_fds=True ) fcntl.fcntl( pipe.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(pipe.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK, ) while True: readx = select.select([pipe.stderr.fileno()], [], [])[0] if readx: chunk = pipe.stderr.read() if not chunk: break result = re.search(r'\stime=(?P<time>\S+) ', chunk) elapsed_time = float(result.groupdict()['time']) # Assuming you have the duration in seconds progress = (elapsed_time / duration) * 100 # Do something with progress here callback(progress) time.sleep(10) 

Вы также можете сделать это довольно четко с помощью QProcess PyQt4 (как задано в исходном вопросе), подключив слот из QProcess к QTextEdit или тому подобное. Я все еще довольно новичок в python и pyqt, но вот как мне это удалось:

 import sys from PyQt4 import QtCore, QtGui class ffmpegBatch(QtGui.QWidget): def __init__(self): super(ffmpegBatch, self).__init__() self.initUI() def initUI(self): layout = QtGui.QVBoxLayout() self.edit = QtGui.QTextEdit() self.edit.setGeometry(300, 300, 300, 300) run = QtGui.QPushButton("Run process") layout.addWidget(self.edit) layout.addWidget(run) self.setLayout(layout) run.clicked.connect(self.run) def run(self): # your commandline whatnot here, I just used this for demonstration cmd = "systeminfo" proc = QtCore.QProcess(self) proc.setProcessChannelMode(proc.MergedChannels) proc.start(cmd) proc.readyReadStandardOutput.connect(lambda: self.readStdOutput(proc)) def readStdOutput(self, proc): self.edit.append(QtCore.QString(proc.readAllStandardOutput())) def main(): app = QtGui.QApplication(sys.argv) ex = ffmpegBatch() ex.show() sys.exit(app.exec_()) if __name__ == '__main__': main() 
Python - лучший язык программирования в мире.