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