【发布时间】:2019-09-11 05:17:01
【问题描述】:
我有一个程序可以生成图像并从中创建视频。目前可行的是一次创建所有图像,然后在子进程中运行 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):
pipe.stdin.write(frame)
但是,当我有数千张图片无法全部放入内存时,这将变得不可行,因为子进程 fork 调用请求了太多内存并失败了。我的解决方案是在程序开始时分叉(避免内存错误),然后在创建帧时将它们通过管道传输到标准输入:
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 是否以某种方式轮询一个空管道然后被它损坏?我不明白子进程通信是如何工作的......)。任何帮助将不胜感激。
【问题讨论】:
-
ffmpeg是否足够聪明,不会在 stdin 读取数据时从标准输入读取键盘命令?如果没有,-nostdin可能会有所帮助 -
试过
-nostdin,没有运气:/ -
什么是 OUTPUT_VIDEO_PATH?扩展将确定容器类型,这可能会影响事物。
-
最后一帧后,需要关闭管道,等待ffmpeg完成。
-
_call_subprocess是一个上下文管理器,它关闭流并在其 finally 块中的子进程上调用wait。
标签: python linux ffmpeg subprocess pipe