Повреждение ffmpeg при передаче данных из stdin - PullRequest
3 голосов
/ 22 апреля 2019

У меня есть программа, которая генерирует изображения и создает из них видео. В настоящее время работает создание всех изображений одновременно, затем запуск 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 теперь искажается. Я уверен, что это как-то связано с тем фактом, что у меня теперь есть некоторое время обработки между записями в 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 каким-то образом опрашивает пустой канал, а затем портится из-за этого? Я не понимаю, как связь подпроцесса вообще работает ...). Любая помощь будет оценена.

1 Ответ

0 голосов
/ 22 апреля 2019

Не уверен, почему это работает, но изменение вызова sp.Popen для запуска FFmpeg с sp.Popen(cmd, stdin=sp.PIPE, stderr=sp.PIPE, stdout=DEVNULL) на sp.Popen(cmd, stdin=sp.PIPE, stderr=DEVNULL, stdout=DEVNULL) сработало.Я предполагаю, что это как-то связано с этим ТАКИМ вопросом о проблемах с конвейером stderr .

...