FFMPEG 和 Pythons 子进程
Posted
技术标签:
【中文标题】FFMPEG 和 Pythons 子进程【英文标题】:FFMPEG and Pythons subprocess 【发布时间】:2010-11-30 03:27:01 【问题描述】:我正在尝试为FFMPEG
编写一个 gui。我正在使用 pythons 子进程为我想要的每个转换创建一个 ffmpeg 进程。这工作正常,但我也想要一种方法来获取转换的进度,无论它是否失败等等。我想我可以通过访问进程的标准输出来做到这一点,如下所示:
致电subprocess.Popen()
# Convert - Calls FFMPEG with current settings. (in a seperate
# thread.)
def convert(self):
# Check if options are valid
if self.input == "" or self.output == "":
return False
# Make the command string
ffmpegString = self.makeString()
# Try to open with these settings
try:
self.ffmpeg = subprocess.Popen(ffmpegString, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except OSError:
self.error.append("OSError: ")
except ValueError:
self.error.append("ValueError: Couldn't call FFMPEG with these parameters")
# Convert process should be running now.
阅读stdout
:
convert = Convert()
convert.input = "test.ogv"
convert.output = "test.mp4"
convert.output_size = (0, 0)
convert.convert()
while 1:
print convert.ffmpeg.stdout.readline()
这可行,但是 ffmpeg 的状态不显示。我假设它与 ffmpeg 刷新它的方式有关。有没有办法访问它?
【问题讨论】:
【参考方案1】:由于难以解决的缓冲问题,我经常注意到使用子进程读取标准输出(甚至是标准错误!)的问题。当我确实需要从子进程中读取此类 stdout/stderr 时,我最喜欢的解决方案是切换到使用,而不是 subprocess
、pexpect
(或者,在 Windows 上,wexpect
)。
【讨论】:
你的两个链接都失效了,请修复它们。【参考方案2】:只需将 ,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)
现在你的循环如下:
frame= 1900 fps=453 q=18.6 Lsize= 3473kB time=00:01:16.08 bitrate= 373.9kbits/s
使用 time= 值来确定百分比进度。
【讨论】:
有趣的解决方案,但添加了一些 Popen 的缓冲问题。如果您正在寻找实时编码监控,则无法直接使用。【参考方案3】:我认为你不能使用 readline 因为 ffmpeg 从不打印一行,通过写入 \r (回车)然后再次写入该行来更新状态。
size= 68kB time=0.39 bitrate=1412.1kbits/s \rsize= 2786kB time=16.17 bitrate=1411.2kbits/s \rsize= 5472kB time=31.76 bitrate=1411.2kbits/s \r\n
如果您检查上面的行,您会注意到只有一个 \n 并且在文件完成转换时打印出来。
【讨论】:
你可以做 readline,但你只有在完成后才能得到该行的最终版本......【参考方案4】:由于 ffmpeg 将未刷新的数据写入 stderr,因此您必须使用 fcntl 将 stderr 文件描述符设置为非阻塞。
fcntl.fcntl( pipe.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(pipe.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK, )然后循环使用select读取数据
而真: readx = select.select([pipe.stderr.fileno()], [], [])[0] 如果读取: 块 = pipe.stderr.read()如需完整示例,请转到 here。
【讨论】:
我不确定这是否仍然正确...我在 py2.7 上的 ffmpeg 的当前版本中仅在 stderr 上执行 readline 即可获得渐进式输出:for line in proc.stderr: print line proc.stderr.flush()
啊不,嗯,有点。我在编码期间没有获得frame= xxx
状态输出的渐进式输出(因为这是一个反复更新的行),但我看到元数据行然后它阻塞直到编码完成,只显示最后的状态更新,然后它显示剩余的摘要行。【参考方案5】:
FFMPEG:
FFMPEG 在 stderr 界面上输出所有状态文本(您在命令行上手动运行时看到的内容)。为了从 ffmpeg 捕获输出,您需要查看 stderr 接口 - 或像示例一样重定向它。
检查 stderr 上的输出:
这是另一种尝试从 stderr 读取的方法,而不是在调用 Popen 时重定向它
Python 中的Popen class 有一个名为stderr 的文件对象,您可以像访问stdout 一样访问它。我想你的循环看起来像这样:
while 1:
print convert.ffmpeg.stdout.readline()
print convert.ffmpeg.stderr.readline()
免责声明:我没有在 Python 中对此进行过测试,但我使用 Java 制作了一个类似的应用程序。
【讨论】:
Gorgapor,你确定他是吗? 是的,stderr在代码片段中重定向到subprocess.Popen这一行——当然如果不使用代码片段下的滚动条也可以截掉... 【参考方案6】:ffmpegCommand='''
ffmpeg
-f lavfi
-i anullsrc=channel_layout=1c:sample_rate=11025
-rtsp_transport tcp
-rtsp_transport udp
-rtsp_transport http
-thread_queue_size 32000
-i rtsp://xxx.xxx.xxx.xxx:554/user=admin&password=xxx&channel=1&stream=1.sdp?real_stream
-reconnect 1
-reconnect_at_eof 1
-reconnect_streamed 1
-reconnect_delay_max 4294
-tune zerolatency
-c:v copy
-c:a aac
-bufsize 6000k
-f flv rtmp://a.rtmp.youtube.com/live2/xxx-xxx-xxx-xxx'''
cmd=ffmpegCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:
print(p.stderr.readline().rstrip('\r\n'))
【讨论】:
以上是关于FFMPEG 和 Pythons 子进程的主要内容,如果未能解决你的问题,请参考以下文章