近期在一个项目中,遇到摄像头连接线不稳定,导致获取数据卡死问题。这是硬件与底层问题所导致的,我们要在软件层进行一些处理;
初步方案是在获取数据错误时,重新初始化以达到恢复正常状态
部分初始化代码如下:
fd = open(m_device_name, O_RDWR);
if(fd == -1)
{
printf("Error opening V4L2 interface\n");
return -1;
}
// Determine if fd is a V4L2 Device
if (0 != wrap_ioctl(fd, VIDIOC_QUERYCAP, &cap)) {
printf("Not v4l2 compatible\n");
goto FAIL_EXIT;
}
printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n",cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) {
printf("Capture not supported\n");
goto FAIL_EXIT;
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
printf("Streaming IO Not Supported\n");
goto FAIL_EXIT;
}
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
fmtdesc.index++;
}
vfmt = (struct v4l2_format) {0};
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
vfmt.fmt.pix.width = m_camera_w;
vfmt.fmt.pix.height = m_camera_h;
vfmt.fmt.pix.pixelformat = m_pixel_fmt;
if (!vfmt.fmt.pix.pixelformat)
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
type = (v4l2_buf_type)vfmt.type;
m_type = type;
if (-1 == wrap_ioctl(fd, VIDIOC_S_FMT, &vfmt)) {
printf("VIDIOC_S_FMT\n");
goto FAIL_EXIT;
}
部分反初始化代码如下:
ioctl(fd, VIDIOC_STREAMOFF, &type);
for (i = 0 ; i < m_bufcnt; i++) {
buf = (struct v4l2_buffer) {0};
buf.type = type;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
wrap_ioctl(fd, VIDIOC_QUERYBUF, &buf);
munmap(m_video_buffer[i].start, buf.length);
}
在通过调用获取数据接口无法获取数据时 进行反初始化,并重新初始化
wrap_ioctl(fd, VIDIOC_DQBUF, &buf);
程序段错误崩溃,代码在这里崩溃。
wrap_ioctl(fd, VIDIOC_S_FMT, &vfmt)
此时内核报错:
rkcif_mipi_lvds: rkcif_s_fmt_vid_cap_mplane queue busy
跟踪内核代码,
static inline bool vb2_is_busy(struct vb2_queue *q)
{
return (q->num_buffers > 0);
}
发现此时报错提示信息指向 queue 不为空,此时开始回到反初始化代码处,查看释放问题
需要注意一点的是:
VIDIOC_STREAMON 和 VIDIOC_STREAMOFF 两个 ioctl 用来开始和停止 capturing 或者 output ,而且VIDIOC_STREAMOFF 会删除输入和输出队列中的所有 buffer 。
重点在于 VIDIOC_STREAMOFF 会删除输入和输出队列中的所有 buffer 。
此时大概知道问题在哪里了,释放处我在VIDIOC_STREAMOFF之后,又调用了VIDIOC_QUERYBUF。
这时我加了打印,发现获取到的buf.length为0,导致munmap失败。也就是queue没有真正的释放。
修改代码如下:
ioctl(fd, VIDIOC_STREAMOFF, &type);
for (i = 0 ; i < m_bufcnt; i++) {
munmap(m_video_buffer[i].start, m_video_buffer[i].length);
}
CLEAR(req);
req.count = 0;
req.type = type;
req.memory = V4L2_MEMORY_MMAP;
wrap_ioctl(fd, VIDIOC_REQBUFS, &req);
此时问题得到解决。
这就是本次调试的过程,个人理解或有不对之处,请指正。