1. 问题背景
最近在用瑞芯微3588开发板做一个视频处理的项目,前两天拷机发生了闪退,弹出的问题是“打开文件过多”,经过初步排查定位到是MPP硬解码部分出的问题。
我的MPP解码部分主要用来读取网络相机rtsp流,主要参考了一个github项目GitHub - MUZLATAN/ffmpeg_rtsp_mpp: ffmpeg 拉取rtsp h264流, 使用mpp解码, 目前在firefly 板子上跑通了
我将它封装成C++类的形式,方便使用。最开始使用是没有问题的,但是在我加了一个相机异常处理机制的时候,就出现了“打开文件过多”的问题。这个相机异常处理机制主要是解决相机未连接、断电重连的时候,软件依然正常运行,而且在相机重连之后可以重新读取画面,而软件不需要重启。
我的解决办法是,如果检测到无法从相机获取数据了,就调用析构函数释放掉解码器(我封装的那个类),然后重新实例化解码类尝试连接相机。那个项目里,获取相机数据采用的是ffmpeg的av_read_frame,然后用mpp调用硬件进行解码。
if (av_read_frame(pFormatCtx, av_packet) < 0) //从相机获取数据
{
printf("av_read_frame get packet failed!");
return -1;
}
在我尝试多次插拔相机之后,软件就出现了文章开头的问题:打开文件过多。
2. 问题现象及分析
问题现象主要分为两种,一种是相机一直连接或者一直不连接,这样软件不会出现问题;第二种是相机时而连接,时而不连接,就会导致“文件打开过多”。
经过查阅,得知rk3588开发板的每个进程最多能打开1024个文件
利用lsof指令实时监控软件打开的文件数
lsof -p 进程号 | wc -l
发现打开的文件数量确实在一直上涨,而且也确实发生在插拔相机的时候。但是现在并不清楚到底泄露了什么资源,可以进一步查看进程具体打开了哪些文件
查看软件打开的文件:
ls -l /proc/进程id/fd
可以看出来,每次插拔相机都导致一个mpp_service和若干个dmabuf增加,也就是泄露。
所以,原因应该也很明显了,就是解码器释放的时候没有完全释放掉资源,下面就需要排查代码了。
3. 问题解决
启动人眼搜索大法,一行行排查代码,看到疑似申请了资源的代码都详细查看一下
最后定位到两个函数:
第一个函数mpp_create会申请mpp services,是导致mpp_services泄露的根源;
第二个函数mpp_buffer_group_get会申请缓冲区暂存解码出来的图像。
在两个函数头文件声明的地方,也都发现了相应的释放函数:
添加到析构函数中,问题解决~
4. 总结
这么想来,RAII真是福音,修改后的代码已放到这儿了