从标准输入进行管道输入时,ffmpeg损坏

时间:2019-04-21 22:06:32

标签: python linux ffmpeg subprocess pipe

我有一个程序可以生成图像并根据它们创建视频。当前有效的方法是一次创建所有图像,然后在子进程中运行FFmpeg并将图像通过stdin传递到视频中:

cmd = ['ffmpeg', '-y',
       '-s', '{}x{}'.format(OUTPUT_WIDTH, OUTPUT_HEIGHT),
       '-r', str(OUTPUT_VIDEO_FPS),
       '-an',
       '-pix_fmt', colour,
       '-c:v', 'rawvideo', '-f', 'rawvideo',
       '-i', '-',
       '-vcodec', 'libx264',
       '-pix_fmt', 'yuv420p',
       '-preset', 'medium', OUTPUT_VIDEO_PATH]

out_frames = []
for i in range(num_frames):
    out_frame = render_frame(...)
    out_frames.append(out_frame)

with _call_subprocess(sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL)) as pipe:
    for frame_no, frame in enumerate(out_frames):
        pipe.stdin.write(frame)

但是,当我有成千上万个图像都无法容纳在内存中时,这将变得不可行,因为子进程派生调用会占用过多的内存并失败。我的解决方案是在程序的开头进行分叉(避免内存错误),然后在创建帧时将其通过管道传递给stdin:

cmd = ['ffmpeg', '-y',
       '-s', '{}x{}'.format(OUTPUT_WIDTH, OUTPUT_HEIGHT),
       '-r', str(OUTPUT_VIDEO_FPS),
       '-an',
       '-pix_fmt', colour,
       '-c:v', 'rawvideo', '-f', 'rawvideo',
       '-i', '-',
       '-vcodec', 'libx264',
       '-pix_fmt', 'yuv420p',
       '-preset', 'medium', OUTPUT_VIDEO_PATH]

with _call_subprocess(sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL)) as pipe:
    for i in range(num_frames):
        out_frame = render_frame(...)
        pipe.stdin.write(out_frame)

但是,ffmpeg的输出现在已损坏。我很确定这与以下事实有关:在渲染帧时,我现在在两次写入标准输入之间有一些处理时间-我注意到,如果使用第一种解决方案,只需在写入之间增加一些睡眠时间到标准输入,输出也损坏了!

cmd = ['ffmpeg', '-y',
       '-s', '{}x{}'.format(OUTPUT_WIDTH, OUTPUT_HEIGHT),
       '-r', str(OUTPUT_VIDEO_FPS),
       '-an',
       '-pix_fmt', colour,
       '-c:v', 'rawvideo', '-f', 'rawvideo',
       '-i', '-',
       '-vcodec', 'libx264',
       '-pix_fmt', 'yuv420p',
       '-preset', 'medium', OUTPUT_VIDEO_PATH]

out_frames = []
for i in range(num_frames):
    out_frame = render_frame(...)
    out_frames.append(out_frame)

with _call_subprocess(sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL)) as pipe:
    for frame_no, frame in enumerate(out_frames):
        time.sleep(1) # <------------------- This sleep ruins everything!!
        pipe.stdin.write(frame)

但是,我不确定是什么原因甚至导致了该问题或如何解决(FFmpeg是否以某种方式轮询一个空管道,然后从中被破坏了?我什至不知道子流程通信是如何工作的……)。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

不确定为什么这样做,但将sp.Popen调用更改为将FFmpeg从sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL)运行到sp.Popen(cmd, stdin=sp.PIPE, stderr=DEVNULL, stdout=DEVNULL)是可行的。我猜想这与this SO question about piping issues with stderr有关。