参考资料:韦东山第三期 v4l2应用程序开发分为两个部分,数据采集流程和控制流程两个部分 数据采集流程: 分为空闲链表和完成链表 驱动程序周而复始地做如下事情:
- 从硬件采集到数据
- 把"空闲链表"取出buffer,把数据存入buffer
- 把含有数据的buffer放入"完成链表"
- 监测"完成链表",等待它含有buffer
- 从"完成链表"中取出buffer
- 处理数据
- 把buffer放入"空闲链表"
- 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,但是驱动程序不一定能申请到,requery申请
- 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、数据格式
枚举格式的时候,只是返回支持的pixelformat和描述struct v4l2_fmtdesc fmtdesc; fmtdesc.index = 0; // 比如从0开始 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type为"捕获" ioctl(vd->fd, VIDIOC_ENUM_FMT, &fmtdesc); // uapi/linux/videodev2.h 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]; }; enum v4l2_buf_type { V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, // 摄像头一般就为捕获设备 V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, V4L2_BUF_TYPE_VBI_CAPTURE = 4, V4L2_BUF_TYPE_VBI_OUTPUT = 5, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, #if 1 /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, #endif V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, V4L2_BUF_TYPE_SDR_CAPTURE = 11, /* Deprecated, do not use */ V4L2_BUF_TYPE_PRIVATE = 0x80, };
2、获取当前摄像头使用的格式
获取当前格式更加详细的信息,就需要使用VIDIOC_G_FRTstruct v4l2_format currentFormat; memset(¤tFormat, 0, sizeof(struct v4l2_format)); currentFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(vd->fd, VIDIOC_G_FMT, ¤tFormat); #if 0 struct v4l2_format { __u32 type; // 表示捕获设备 union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ struct v4l2_sdr_format sdr; /* V4L2_BUF_TYPE_SDR_CAPTURE */ __u8 raw_data[200]; /* user-defined */ } fmt; }; /* * V I D E O I M A G E F O R M A T */ struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; __u32 field; /* enum v4l2_field */ __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ __u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */ __u32 ycbcr_enc; /* enum v4l2_ycbcr_encoding */ __u32 quantization; /* enum v4l2_quantization */ __u32 xfer_func; /* enum v4l2_xfer_func */ }; #endif
3、设置当前格式
使用VIDIOC_S_FMTstruct v4l2_format fmt; memset(&fmt, 0, sizeof(struct v4l2_format)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 1024; fmt.fmt.pix.height = 768; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; fmt.fmt.pix.field = V4L2_FIELD_ANY; int ret = ioctl(vd->fd, VIDIOC_S_FMT, &fmt);
选择输入源:
int value; ioctl(h->fd,VIDIOC_G_INPUT,&value); // 读到的value从0开始, 0表示第1个input源 int value = 0; // 0表示第1个input源 ioctl(h->fd,VIDIOC_S_INPUT,&value)
其他参数:
如果每一参数都提供一系列的ioctl cmd,那使用起来很不方便。 对于这些参数,APP使用对应ID来选中它,然后使用VIDIOC_QUERYCTRL、VIDIOC_G_CTRL、VIDIOC_S_CTRL来操作它。 不同参数的ID值不同。以亮度Brightness为例,有如下调用方法:1、查询
struct v4l2_queryctrl qctrl; memset(&qctrl, 0, sizeof(qctrl)); qctrl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0; ioctl(fd, VIDIOC_QUERYCTRL, &qctrl); /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { __u32 id; __u32 type; /* enum v4l2_ctrl_type */ __u8 name[32]; /* Whatever */ __s32 minimum; /* Note signedness */ __s32 maximum; __s32 step; __s32 default_value; __u32 flags; __u32 reserved[2]; };
2、获得当前值
struct v4l2_control c; c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0; ioctl(h->fd, VIDIOC_G_CTRL, &c); /* * C O N T R O L S */ struct v4l2_control { __u32 id; __s32 value; };
3、设置
struct v4l2_control c; c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0; c.value = 99; ioctl(h->fd, VIDIOC_S_CTRL, &c);
理解接口:
SU:select unit,选择输入源 PU:processiong unit,用于调整亮度、对比度、色度等 EU:encoding unit,对采集到的数据进行个性化处理的功能 ID会传递给PU app->驱动->(id, val)->硬件操作方法:
使用ioctl操作设备节点/dev/video0时,不同的ioctl操作可能是video control接口,或者video streaming接口 跟视频流相关的操作,比如VIDIOC_ENUM_FMT,VIDIOC_G_FMT,VIDIOC_S_FMT,VIDIOC_STREAMON,VIDIOC_STREAMOFF,是操作video streaming的接口 其他的接口,大多是video control接口 从驱动和硬件角度来看,要操作video control接口,需要指明: 1、entity:要操作的是那个terminal或者unit,比如PU 2、control selector:要操作entity里面的那个控制项,比如亮度 3、控制项里面的哪些位:比如camera terminal里的CT_PANTILT_RELATIVE_CONTROL控制项对应的32位数据,其中前16位对应PAN控制(左右转动),后16位对应TILE控制(上下转动) 但是应用程序不关心这些,使用一个ID来指定entity、control selector、哪些位:struct v4l2_control c; c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0; c.value = 99; ioctl(h->fd, VIDIOC_S_CTRL, &c); /* * C O N T R O L S */ struct v4l2_control { __u32 id; __s32 value; };
支持V4L2_CAP_STREAMING就可以用mmap函数,不支持的话就只能用V4L2_CAP_READWRITE了
标签:__,V4L2,程序开发,ioctl,VIDIOC,u32,应用,v4l2 From: https://www.cnblogs.com/lethe1203/p/18097134