最近自己所在小组做了一个智能家居系统,本人主要负责摄像头图像采集部分,需要完成的功能是实现摄像头数据采集,而且图片需要在LCD上显示,需要经过网络远程发送,自己小白一个,做之前什么都不懂,经历各种查资料请教过后总算出效果了,感触颇深。这期间CSDN上各位大神的各种博客对自己帮助很大,在此一并谢过!!!!!同时也发现很多博客都只包含一个小部分,感觉如果有一个篇完整的介绍可能对新手会有帮助,因此在此简单介绍摄像头采集整个流程。第一次发博客,恳请各位大神多多指教,如有不妥之处,还请见谅。
废话少说,直接进正题。首先要说明的是我们的摄像头是在ARM Cortex-A8要下运行的,出来的图像结果与PC机上会有一定的不同,请各位注意。
1、驱动支持
在那位法国牙医的无私奉献下,Linux内核几乎支持所有的USB摄像头,不过要想自己的Linux内核支持USB免驱摄像头,还需要先配置内核,
Device Drivers --->
<*> Multimedia support --->
<*> Video For Linux
[ ] Enable Video For Linux API 1 (DEPRECATED)
[*] Video capture adapters --->
[*] V4L USB devices --->
<*> USB Video Class (UVC)
[*] UVC input events device support
这样在板子上插入摄像头后终端就会有显示:
[root@farsight /]# usb 1-1.1: new full speed USB device using s3c2410-ohci and a ddress 4
uvcvideo: Found UVC 1.00 device Webcam C110 (046d:0829)
input: Webcam C110 as /class/input/input2
同时输入命令:lsusb 也会有相应信息,在此不就不详细展开了,网上有很多资料。最主要的是此时进入/dev 目录下,ls 会新增加一个设备,我的是video0,不同情况下需自己确认,这个设备名很重要。至此,Linux内核对摄像头的驱动支持就没问题了。
2、开始操作摄像头
经典操作v4l2的方法一共也就那么步,大致为:打开设备->查看设备功能->设置图片格式->申请帧缓冲->内存映射->帧缓冲入列->开始采集->读数据(包括处理数据)->帧缓冲重新入列->关闭设备。看着名字挺霸气的,其实每一步都是调用内核驱动提供的出来的接口就可以了。
2.1 打开设备
fd = open(dev_name, O_RDWR, 0 );//打开设备文件,阻塞模式
if (fd < 0){
perror("open /dev/video0 fialed! ");
return -1;
}
打开一个open就OK了,注意此处用的是阻塞模式,如果是非阻塞模式(O_NONBLOCK)的话,即使摄像头尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序,感觉这样有点不合理,也懂内核为何要这样设计。
2.2 查看设备功能
<span style="font-size:14px;">struct v4l2_capability cap;
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);//查看设备功能
if (ret < 0){
perror("requre VIDIOC_QUERYCAP fialed! \n");
return -1;
}
printf("driver:\t\t%s\n",cap.driver);
printf("card:\t\t%s\n",cap.card);
printf("bus_info:\t%s\n",cap.bus_info);
printf("version:\t%d\n",cap.version);
printf("capabilities:\t%x\n",cap.capabilities);
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE){
printf("Device %s: supports capture.\n",dev_name);
}
if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING){
printf("Device %s: supports streaming.\n",dev_name);
}</span>
查看设备功能也没什么好说的,看代码就OK啦。
2.3 设置图片格式
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = WIDTH;
fmt.fmt.pix.height = HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt)){//设置图片格式
perror("set format failed!");
return -1;
}
if(-1 == ioctl(fd, VIDIOC_G_FMT, &fmt)){//得到图片格式
perror("set format failed!");
return -1;
}
printf("fmt.type:\t\t%d\n",fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", \
fmt.fmt.pix.pixelformat & 0xFF,\
(fmt.fmt.pix.pixelformat >> 8) & 0xFF, \
(fmt.fmt.pix.pixelformat >> 16) & 0xFF,\
(fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
也是一个命令就完成:VIDIOC_S_FMT,其中WIDTH,HEGHT 是定义的宏,后面很多地方都要用这两个参数,定义成宏比传参方便。V4L2_PIX_FMT_YUYV 指定输出格式为YUYV,关于YUYV,RGB等等什么什么格式,网上也有很详细的介绍,比如这篇