使用FFmpeg作为Python子进程读取视频帧到Numpy数组的内存泄漏排查
内存泄漏问题分析与修复
核心泄漏点
- 未处理ffmpeg的stderr输出:
pipe_thread中仅调用pipe.stderr.flush()但未实际读取stderr内容,ffmpeg会持续向stderr输出日志,这些数据会在缓冲区中不断累积,导致内存占用逐步上升。subprocess的管道缓冲区若不主动读取,数据会一直被Python进程持有,无法被垃圾回收。 - numpy数组重复创建:每次循环里
self.image = np.frombuffer(...)都会生成新的numpy数组,旧数组虽会被GC回收,但频繁创建新对象可能导致回收延迟,加剧内存占用问题。
修复方案
1. 读取并丢弃stderr内容
替换原代码中pipe.stderr.flush()的逻辑,主动读取stderr的所有可用内容并丢弃,避免缓冲区堆积:
# 替换原pipe.stderr.flush() # 读取stderr内容并丢弃 while pipe.stderr.peek(): pipe.stderr.readline()
2. 复用numpy数组,减少对象创建
初始化时创建的self.image可以复用,无需每次赋值新数组,直接修改已有数组的内容:
# 替换原self.image = np.frombuffer(...) data = pipe.stdout.read(self.H * self.W * self.P) if len(data) == self.H * self.W * self.P: self.image[:] = np.frombuffer(data, dtype='uint8').reshape((self.H, self.W, self.P)) self.flag = True
3. 完善子进程清理逻辑
终止子进程后调用wait(),确保子进程完全退出,避免残留系统资源:
if len(data) < self.H * self.W * self.P: # 视频结束,重新加载 pipe.terminate() pipe.wait() # 等待子进程彻底退出 pipe = None
修复后的pipe_thread完整代码
def pipe_thread(self): pipe = None while not self.kill_thread: st_tm = time.time() if pipe is None: pipe = subprocess.Popen(self.command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=-1) data = pipe.stdout.read(self.H * self.W * self.P) pipe.stdout.flush() # 读取并丢弃stderr内容 while pipe.stderr.peek(): pipe.stderr.readline() if len(data) < self.H * self.W * self.P: # 视频结束,重新加载 pipe.terminate() pipe.wait() pipe = None else: self.image[:] = np.frombuffer(data, dtype='uint8').reshape((self.H, self.W, self.P)) self.flag = True step = time.time() - st_tm time.sleep(max(0.04 - step, 0.0)) if pipe is not None: pipe.terminate() pipe.wait() pipe = None
内容的提问来源于stack exchange,提问作者paddyg
相关产品推荐
相关产品推荐

