私はいくつかの質問を見てきましたが、それでもこれを完全に理解することはできません。私は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 /出力バッファリングと関係があると思いますが、次のような行を読み取る方法については行き詰まっています
frame= 51 fps= 27 q=31.0 Lsize= 769kB time=2.04 bitrate=3092.8kbits/s
進捗状況を把握するために使用できます。
PyQtを使用して、または使用せずに、この情報をffmpegからpythonに取得する方法の例を誰かに教えてもらえますか(可能な場合)
編集:最終的に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
完璧!
子プロセスから動的なフィードバック/出力を取得するために私が見つけた唯一の方法は、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サブプロセス
Ffmpegの出力は実際にはバッファリングされていませんが、改行文字が含まれているため、トリックは_universal_newlines=True
_をsubprocess.Popen()
呼び出しに追加することです。
_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
_にリダイレクトされることに注意してください。
stderr
ではなくstdout
に来ることを知っています。上記の例のように、出力行を印刷するだけの場合は、次のようにします。
import subprocess
cmd = 'ffmpeg -i file.mp4 file.avi'
args = cmd.split()
p = subprocess.Popen(args)
Ffmpegチャットの行は\r
で終了しているため、同じ行で上書きされることに注意してください。これは、rsyncの例のように、p.stderr
の行を繰り返すことができないことを意味すると思います。独自のプログレスバーを作成するには、自分で読み取りを処理する必要がある場合があります。これで開始できます。
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)
これがお役に立てば幸いです。
また、QProcessからQTextEditなどにスロットを接続することにより、PyQt4のQProcess(元の質問で尋ねられた)を使用して非常に明確に行うことができます。私はまだ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()
期間(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)