Python subprocess调用ffmpeg流式输出时异常处理与进程终止问题问询
问题根因
你的代码存在两个核心问题导致异常场景下卡死、CPU升高:
- 未消费
stderr管道:ffmpeg报错时会向stderr写入大量日志,管道缓冲区满后ffmpeg会被阻塞挂起,既不退出也不输出stdout,导致你的循环永久等待。 stdout读取终止条件错误:subprocess的stdout是二进制模式,readline读完返回的是空字节b"",而非空字符串"",你原来的终止条件永远无法触发,ffmpeg退出后循环会一直空转,CPU占用持续升高。
communicate不适用的原因是它会等待子进程完全退出后再一次性返回所有输出,无法满足流式返回的需求。
解决方案
通过后台daemon线程单独消费stderr避免阻塞,同时修改读取逻辑,主动检测进程状态,异常时及时清理资源,修改后代码如下:
import subprocess import threading def stream(link): ffmpegcmd = [ "ffmpeg", # "-loglevel", # "panic", "-i", link, "-vcodec", "copy", "-f", "mp4", "-movflags", "frag_keyframe+empty_moov", "pipe:", ] ffmpeg_sb = None def drain_stderr(): """后台线程消费stderr,避免缓冲区满阻塞进程""" while ffmpeg_sb and ffmpeg_sb.poll() is None: # 不需要stderr日志可直接丢弃,也可写入日志文件用于排查问题 ffmpeg_sb.stderr.read(1024) try: ffmpeg_sb = subprocess.Popen( ffmpegcmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # 启动daemon线程,主进程退出时自动销毁 threading.Thread(target=drain_stderr, daemon=True).start() # 修复终止条件,用空字节作为结束标识 for stdout_line in iter(ffmpeg_sb.stdout.readline, b""): # 每次输出前检查进程是否异常退出 if ffmpeg_sb.poll() is not None and ffmpeg_sb.returncode != 0: raise RuntimeError(f"ffmpeg异常退出,返回码:{ffmpeg_sb.returncode}") yield stdout_line except Exception as e: print("Exception:", str(e)) if ffmpeg_sb and ffmpeg_sb.poll() is None: ffmpeg_sb.kill() finally: # 回收子进程资源,避免僵尸进程 if ffmpeg_sb: ffmpeg_sb.wait() ffmpeg_sb.stdout.close() ffmpeg_sb.stderr.close() print("Finished")
优化说明
- 后台daemon线程会持续消费stderr的内容,不会让缓冲区满导致ffmpeg阻塞
- 修复了readline的终止条件,ffmpeg正常退出后循环会自动结束
- 每次yield前都检查进程状态,一旦ffmpeg异常退出就主动抛异常,走清理逻辑
- 用finally块保证不管是正常结束还是异常退出,都会回收子进程资源,不会残留僵尸进程导致CPU升高
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

