RK3566应用mipi摄像头ov8858(一)
v4l2设备编程流程
- open:打开设备节点/dev/videoX
- ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力,比如
- 确认它是否是"捕获设备",因为有些节点是输出设备
- 确认它是否支持mmap操作,还是仅支持read/write操作
- ioctl VIDIOC_ENUM_FMT:枚举它支持的格式
- ioctl VIDIOC_S_FMT:在上面枚举出来的格式里,选择一个来设置格式
- ioctl VIDIOC_REQBUFS:申请buffer,APP可以申请很多个buffer,但是驱动程序不一定能申请到
- ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射
- 如果申请到了N个buffer,这个ioctl就应该执行N次
- 执行mmap后,APP就可以直接读写这些buffer
- ioctl VIDIOC_QBUF:把buffer放入"空闲链表"
- 如果申请到了N个buffer,这个ioctl就应该执行N次
- ioctl VIDIOC_STREAMON:启动摄像头
- 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"
- poll/select
- ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer
- 处理:前面使用mmap映射了每个buffer的地址,处理时就可以直接使用地址来访问buffer
- ioclt VIDIOC_QBUF:把buffer放入"空闲链表"
- ioctl VIDIOC_STREAMOFF:停止摄像头
获取摄像头类型以及摄像头所支持的格式
1.打开设备树
我所使用的是野火鲁班猫1开发板,通过命令sudo vi /boot/uEnv/uEnv.txt
修改配置文件中对应型号的设备树插件,如下图:
我的设备为ov8858,只要将对应名称的设备树插件前面的#去掉就可以开启摄像头。
记得修改完配置文件后需要reboot一下
2.通过v4l2-ctl命令来获取摄像头列表
列出摄像头设备
v4l2-ctl --list-devices
其中 /dev/video0
就是刚才打开的ov8858摄像头
3.编写应用代码获取摄像头类型以及摄像头所支持的格式
首先文件描述符和定义结构体
int fd;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_capability cap;
接下来是打开摄像头设备
fd = open("/dev/video0",O_RDWR);
if(fd<0)
{
printf("can not open video0\n");
return -1;
}
获取摄像头类型
使用到VIDIOC_QUERYCAP
命令来获得v4l2_capability结构体的描述
v4l2_capability结构体内容如下:
结构体原型
struct v4l2_capability {
__u8 driver[16]; //name of the driver module
__u8 card[32]; //name of the card
__u8 bus_info[32];//name of the bus
__u32 version; //kernel version
__u32 capabilities;//capabilities of the physical device as a whole
__u32 device_caps; //capabilities aeccessed via this particular device
__u32 reserved[3];
};
capabilities字段的值包括
#define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT 0x00000002 /* Is a video output device */
#define V4L2_CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay */
#define V4L2_CAP_VBI_CAPTURE 0x00000010 /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT 0x00000020 /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */
#define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */
/* Is a video capture device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000
/* Is a video output device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000
/* Is a video mem-to-mem device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_M2M_MPLANE 0x00004000
/* Is a video mem-to-mem device */
#define V4L2_CAP_VIDEO_M2M 0x00008000
#define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
#define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
#define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
#define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */
#define V4L2_CAP_SDR_CAPTURE 0x00100000 /* Is a SDR capture device */
#define V4L2_CAP_EXT_PIX_FORMAT 0x00200000 /* Supports the extended pixel format */
#define V4L2_CAP_SDR_OUTPUT 0x00400000 /* Is a SDR output device */
#define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */
#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
#define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */
#define V4L2_CAP_TOUCH 0x10000000 /* Is a touch device */
#define V4L2_CAP_DEVICE_CAPS 0x80000000 /* sets device capabilities field */
可以看到上面有许多类型,若要一个一个判断设备类型,那可太麻烦了,所以这里有一个办法,通过v4l2-ctl命令来查询摄像头类型:v4l2-ctl --list-formats-ext -d /dev/video0
可以看到我的摄像头类型是video capture multiplanar
接着回到代码中,验证是否正确
通过ioctl函数控制使用VIDIOC_QUERYCAP命令来获取摄像头类型
if(ioctl(fd,VIDIOC_QUERYCAP,&cap) !=0)
{
perror("FAIL to ioctl QIDIOC_QUERYCAP\n");
}
if(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
printf("v4l2 dev support CAPTURE_MPLANE\n");
获取摄像头所支持的格式
所使用到的命令是VIDIOC_ENUM_FMT
作用是枚举设备支持的图像格式
通过该命令来获取struct v4l2_fmtdesc结构体的描述
struct v4l2_fmtdesc {
__u32 index; /* Format number */
__u32 type; /* enum v4l2_buf_type */
__u32 flags;
__u8 description[32]; /* Description string */
__u32 pixelformat; /* Format fourcc */
__u32 reserved[4];
};
获取当前格式
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; //需初始化该结构体变量否则会报错,在前面已经知道了摄像头的类型
fmtdesc.index = 0;
//从v4l2_fmtdesc结构体内容可知description是一个数组,通过遍历索引来获取格式内容
while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
{
printf("[%d]fmt:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
完整代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/types.h>
#include <linux/videodev2.h>
int main(int argc,char **argv)
{
int fd;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_capability cap;
if(argc != 2)
{
printf("Usage: %s </dev/videoX>,print format detail for video device\n",argv[0]);
return -1;
}
/*open*/
fd = open(argv[1],O_RDWR);
if(fd<0)
{
printf("can not open %s\n",argv[1]);
return -1;
}
if(ioctl(fd,VIDIOC_QUERYCAP,&cap) !=0)
{
perror("FAIL to ioctl QIDIOC_QUERYCAP\n");
}
if(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
printf("v4l2 dev support CAPTURE_MPLANE\n");
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; //需初始化该结构体变量否则会报错,在前面已经知道了摄像头的类型
fmtdesc.index = 0;
//从v4l2_fmtdesc结构体内容可知description是一个数组,通过遍历索引来获取格式内容
while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
{
printf("[%d]fmt:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
return 0;
}
将代码文件上传至开发板,进行交叉编译后执行,执行结果如下: