从stdin管道输入时ffmpeg损坏

问题描述 投票:3回答:1

我有一个程序可以生成图像并从中创建视频。目前可行的是一次创建所有图像,然后在子进程中运行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)

但是,当我有数千个不适合内存的图像时,这变得不可行,因为子进程fork调用请求太多内存并失败。我的解决方案是在程序开始时fork(避免内存错误),然后在创建时将帧传递给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的输出现在已损坏。我很确定这与我在渲染帧时在写入stdin之间有一些处理时间这一事实有关 - 我注意到如果我使用第一个解决方案,但只是在写入之间添加一些休眠时间到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):
        time.sleep(1) # <------------------- This sleep ruins everything!!
        pipe.stdin.write(frame)

但是我不确定是什么导致这个或如何修复它(FFmpeg以某种方式轮询空管然后被破坏了吗?我不明白子进程通信如何工作......)。任何帮助,将不胜感激。

python linux ffmpeg subprocess pipe
1个回答
0
投票

不知道为什么会这样,但改变sp.Popen调用从sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL)运行FFmpeg到sp.Popen(cmd, stdin=sp.PIPE, stderr=DEVNULL, stdout=DEVNULL)工作。我猜这与this SO question about piping issues with stderr有关。

© www.soinside.com 2019 - 2024. All rights reserved.