如何在Python中通过ffmpeg更快向stdin写入字节生成帧处理后新视频
核心问题分析
- 异步实现无效的原因:你使用的ffmpeg-python的
run_async返回的是标准同步子进程对象,stdout.read、stdin.write都是阻塞IO操作,套async壳子不会实现非阻塞调度,反而额外增加协程调度开销,所以性能更差。 - 写入耗时远高于读取的核心原因:你没有给输出的FFmpeg进程指定编码参数,默认使用的编码预设优先级偏向压缩率而非速度,编码耗时占了绝大多数时间。你观测到的
process2.stdin.write耗时高,本质是FFmpeg编码速度慢导致管道缓冲区被占满,write操作被阻塞等待缓冲区释放,不是Python侧写入本身的性能问题。
优化方案
调整FFmpeg编码参数
给输出的FFmpeg进程指定H.264编码器、极速编码预设,合理设置画质参数,这是最有效的优化手段:
process2 = ( ffmpeg .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height)) .output('new2.mp4', vcodec='libx264', pix_fmt='yuv420p', preset='ultrafast', # 编码速度最快的预设,压缩率最低 crf=23) # 画质参数,范围0-51,数值越小画质越好、文件越大 .overwrite_output() .run_async(pipe_stdin=True) )
如果对压缩率没有要求,可以进一步降低crf数值提升编码速度。
移除冗余的numpy操作
你翻转得到的out_frame本身就是uint8类型,不需要额外调用astype(np.uint8)。另外翻转后的numpy数组是非连续内存,直接转bytes会有额外拷贝,先转成连续内存再输出:
# 替换原来的循环内处理逻辑 out_frame = np.ascontiguousarray(in_frame[:, ::-1], dtype=np.uint8) process2.stdin.write(out_frame.tobytes())
合并音频处理流程
不需要单独抽取音频再后续混流,可以直接在输出进程中指定输入原视频的音频流,减少一次文件IO开销:
input_video = ffmpeg.input(video_path) audio_stream = input_video.audio process2 = ( ffmpeg .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height)) .output(audio_stream, 'new2.mp4', vcodec='libx264', pix_fmt='yuv420p', preset='ultrafast', crf=23, acodec='aac') .overwrite_output() .run_async(pipe_stdin=True) )
异步实现的正确姿势
如果要使用异步优化,必须使用asyncio原生的create_subprocess_exec异步子进程接口,替换同步的子进程读写操作,避免阻塞事件循环。原异步实现只是给同步操作套了协程壳,不会带来任何性能收益。
内容的提问来源于stack exchange,提问作者A.K.M. Adib
相关产品推荐
相关产品推荐

