从ffmpeg获取实时输出以在进度条中使用(PyQt4,stdout)

Jas*_*eil 22 python subprocess ffmpeg stdout pyqt

我看了很多问题,但仍然无法弄清楚这一点.我正在使用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()
Run Code Online (Sandbox Code Playgroud)

但是当我更改命令时,ffmpeg -i file.mp4 file.avi我没有收到任何输出.我猜这与stdout/output缓冲有关,但是我不知道如何读取看起来像的行

frame=   51 fps= 27 q=31.0 Lsize=     769kB time=2.04 bitrate=3092.8kbits/s
Run Code Online (Sandbox Code Playgroud)

我可以用来计算进度.

有人可以告诉我一个如何从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
Run Code Online (Sandbox Code Playgroud)

这给出了这个输出:

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
Run Code Online (Sandbox Code Playgroud)

完善!

小智 15

在捕获ffmpeg状态输出(转到STDERR)的特定情况下,这个SO问题为我解决了:FFMPEG和Pythons子进程

诀窍是添加universal_newlines=Truesubprocess.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)
Run Code Online (Sandbox Code Playgroud)

另请注意,在此代码示例中,STDERR状态输出将直接重定向到 subprocess.STDOUT


jlp*_*jlp 14

我发现从子进程获得动态反馈/输出的唯一方法是使用像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()
Run Code Online (Sandbox Code Playgroud)

被调用的子进程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
Run Code Online (Sandbox Code Playgroud)

你需要使用一些与你从ffmpeg获得的输出相匹配的正则表达式,并对它进行某种计算以显示进度条,但这至少可以获得ffmpeg的无缓冲输出.