问题出现背景
在近期的开发中,提出了需要使用RV1106G2板卡接入USB摄像头采集图像并转推RTSP视频流的需求,针对摄像头打开的实现,我第一时间想到的就是利用FFMPEG来打开摄像头,DEMO程序很快就写好了,在PC端电脑运行的非常好,但是一移植到RV1106平台上,就发现一但程序执行到打开摄像头的环节时,会报Out of memory导致程序的崩溃甚至连带内核一起报错死机。
运行平台及软件版本
平台:Rockchip RV1106G2 SoloLinker-A V2
FFMPEG: ffmpeg4.3 ZLmediakit修改支持HEVC RTMP的版本GITHUB地址
解决过程
针对该问题,首先我使用了v4l2-ctl --list-formats-ext命令来查看USB摄像头支持的分辨率格式,然后换用了更小的分辨率来打开USB摄像头,发现了如果采用小的分辨率来打开(如640x480,800x600),程序并没有任何问题,一切正常,但是如果打开的分辨率提高到1000x***这一档往上,程序立刻当场崩溃死机。
面对这情况,我了解到了FFMPEG底层实际上是调用V4L2来打开UVC设备的,于是我采用了更加底层的V4L2来实现采集摄像头程序,然后发现了如果采用V4L2来打开摄像头,打开4K分辨率的画面采集都没有问题,但是使用V4L2来编程需要自己对图像的缓存进行分配,由此我也了解了V4L2对内存的申请,故转过头来我怀疑是FFMPEG源码里对V4L2内存申请部分出了问题。
解决问题
通过对FFMPEG V4L2源码的解读,最终定位到了问题的出现点。
在FFMPEG的v4l2.c中
FFMPEG使用v4l2_ioctl(s->fd, VIDIOC_REQBUFS, &req)申请内核帧缓存区时,申请的默认大小是
static const int desired_video_buffers = 256;
一次性在内核申请了256帧的缓存,而在RV1106G2这样的小运存平台上,如果采用的分辨率是720P,那么它会一次性在内核申请了256张MJPEG 720P的缓存区,这大小超过了RV1106G2的运存,就会导致程序连带着内核一起崩溃!
解决的方法也很简单,在FFMPEG的v4l2.c中,把第42行的
static const int desired_video_buffers = 256;
改小点就行了,我这边改成了
static const int desired_video_buffers = 6;
修改之后,重新编译FFMPEG就可以正常打开高分辨率的UVC摄像头采集图像了。
FFMPEG打开UVC摄像头
AVDictionary *options = NULL;
av_dict_set(&options,"video_size","1280x720",0); //800x600 //640x480
av_dict_set(&options, "input_format", "mjpeg", 0);
av_dict_set(&options,"framerate","30",0);
if (avformat_open_input (&fmtCtx, configSetting->srcFileUri, inputFmt, &options) < 0){
printf("can not open_input_file\n");
return;
}