我有一个程序可以生成图像并根据它们创建视频。当前有效的方法是一次创建所有图像,然后在子进程中运行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是否以某种方式轮询一个空管道,然后从中被破坏了?我什至不知道子流程通信是如何工作的……)。任何帮助将不胜感激。
答案 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有关。