从ffmpeg获取实时输出以用于进度条(PyQt4,stdout)
我看了很多问题,但还是搞不明白。我在用PyQt,想要运行 ffmpeg -i file.mp4 file.avi
这个命令,并且希望能实时看到输出,这样我就可以做一个进度条。
我查看了这些问题:
ffmpeg能显示进度条吗? 如何实时捕获子进程的输出?我能看到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
时,我就收不到任何输出。我猜这可能和输出缓冲有关,但我不知道怎么读取像这样的行:
frame= 51 fps= 27 q=31.0 Lsize= 769kB time=2.04 bitrate=3092.8kbits/s
我可以用它来计算进度。
有没有人能给我一个例子,教我怎么把ffmpeg的信息获取到python里,使用或不使用PyQt都可以(如果可能的话)
编辑: 最后我选择了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
太好了!
9 个回答
- 从命令行调用通常是不必要的。
- 我知道根据经验,ffmpeg的输出有一部分是通过
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())
在这个特定的情况下,我想捕获ffmpeg的状态输出(这个输出会发送到错误输出流),这个StackOverflow上的问题帮我解决了这个问题:FFMPEG和Python的subprocess
关键是要在调用subprocess.Popen()
时加上universal_newlines=True
,因为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)
另外要注意,在这个代码示例中,错误输出流的状态信息是直接重定向到subprocess.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 的实时输出。