Python:管道子进程的大型stdout时出现奇怪的挂起行为

时间:2017-09-06 01:33:16

标签: python ffmpeg subprocess pipe stdout

我正在调用ffmpeg从视频文件中提取二进制数据流,然后将该二进制数据放入列表中。此数据流中有大量数据,大约4,000 kb。这是代码

# write and call ffmpeg command, piping stdout
cmd = "ffmpeg -i video.mpg -map 0:1 -c copy -f data -"
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

# read from stdout, byte by byte
li = []
for char in iter(lambda: proc.stdout.read(1), ""):
    li.append(char)

这很好用。但是,如果我从stdout中取出我正在阅读的部分,它就会开始工作但是会挂起:

cmd = "ffmpeg -i video.mpg -map 0:1 -c copy -f data -"
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
time.sleep(10)

我必须在最后添加time.sleep(10),否则流程将在subprocess之前结束,从而导致此错误:

av_interleaved_write_frame(): Invalid argument
Error writing trailer of pipe:: Invalid argument
size=       0kB time=00:00:00.00 bitrate=N/A speed=N/A
video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing ove
rhead: 0.000000%
Conversion failed!

调用subprocess.call(cmd, stdout=subprocess.PIPE)subprocess.call(cmd)也会导致挂起(后者只会在控制台中显示stdout而前者不显示。

是否有一些关于从stdout读取的内容可以防止这种情况发生(就像缓冲区被清除一样),或者我是否在其他地方不知不觉地引入了一个错误?我担心这么小的变化导致程序破裂;它并没有激发人们的信心。

此代码的另一个问题是我需要从另一个线程的列表中读取。这可能意味着我需要使用Queue。但是当我执行下面的代码时,它需要11秒而不是等同于列表的3秒:

cmd = "ffmpeg -i video.mpg -loglevel panic -hide_banner -map 0:1 -c copy -f data -"
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

q = Queue()

for char in iter(lambda: proc.stdout.read(1), ""):
    q.put(char)

我应该使用其他数据结构吗?

1 个答案:

答案 0 :(得分:1)

  1. 一次一个字节地从管道读取数据效率非常低。你应该阅读更大的块。

  2. 正如您所注意到的那样,执行子进程然后终止父进程而不等待子进程完成将导致管道错误并且子进程将失败。

  3. 如果操作系统缓冲区被填满(例如,如果你没有像你的情况那样从管道中读取),调用subprocess.call(cmd, stdout=subprocess.PIPE)将阻止/停止编写器。

  4. 只要您一次不读取一个字节,
  5. Queue就可以了