在移植android中camrea中间件时候,v4l2是必须要掌握的,实际上很多人在移植过程中对v4l2的结构都是似懂非懂,所以在此对v4l2中所涉及到的一些结构体做个详细讲解。
v4l2的api在其官方网站上文档,大家可以下载,大部分的接口都是通过系统调用ioctl来完成,第一个参数是通过open打开的设备节点,一般都是/dev/videox,x为一个数字,比如/dev/video0, 第二参数为cmd,我们也叫命令,第三个参数就是各种结构体,我们着重讲的就是cmd和各个结构体
==================================================================
所有的结构体的定义都在#include <linux/videodev2.h>中定义:
struct v4l2_input input;原型:
struct v4l2_input
{
__u32 index; // 视频输入设备的索引
__u8 name[32]; // 视频输入设备的名字
__u32 type;
__u32 audioset;
__u32 tuner;
v4l2_std_id std;
__u32 status;
__u32 reserved[4];
};
用于获取当前接入的video输入设备的属性和设备的编号, 一般对应命令VIDIOC_ENUMINPUT
该结构体对应的命令为:
VIDIOC_G_INPUT: 获取当前设备在系统中的索引编号,通过index返回
VIDIOC_ENUMINPUT: 根据index,查询获取video设备的信息,一般通过struct v4l2_input的内容
VIDIOC_S_INPUT::设置当前的video输入设备的index编号
例子:
struct v4l2_input input;
int index;
if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index)) {
perror ("VIDIOC_G_INPUT");
exit (EXIT_FAILURE);
}
memset (&input, 0, sizeof (input));
input.index = index;
if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) {
//在根据index,通过查询获取struct v4l2_input的内容
perror ("VIDIOC_ENUMINPUT");exit (EXIT_FAILURE);
}
printf ("Current input: %s\n", input.name);
当然也可以将当前的video输入设备的index编号进行更改为第一个:
//Switching to the first video input
int index;
index = 0;
if (-1 == ioctl (fd, VIDIOC_S_INPUT, &index)) {
perror ("VIDIOC_S_INPUT");
exit (EXIT_FAILURE);
}
===========================================================
struct v4l2_format my_format;
原型:
struct v4l2_format
{
enum v4l2_buf_type type; // 对于摄像头,一定是V4L2_BUF_TYPE_VIDEO_CAPTURE
union
{
struct v4l2_pix_format pix;
// 对于摄像头输入输出,我们只关注这个就可以啦
struct v4l2_window win; //用于overlaystruct v4l2_vbi_format vbi; //原始VBI摄像输入和输出参数
struct v4l2_sliced_vbi_format sliced;
__u8 raw_data[200];
} fmt;
};
用于协商驱动和应用程序之间的video输出帧的格式,比如:宽,高,格式,帧图像大小,宽,高的选择需要问驱动工程师,一般支持哪种大小(一般称作为分辨率),或者有数据手册,也可以产看camera的数据手册,在编程中非常重要
该结构体一般用于以下命令:
VIDIOC_G_FMT, // 获取帧图像格式
VIDIOC_S_FMT, //设置帧图像格式
例子:
struct v4l2_format my_format;
bzero(&my_format, sizeof(my_format));
my_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//设定struct v4l2_pix_format
my_format.fmt.pix.height = 640; // 图像输出高
my_format.fmt.pix.width = 480; // 图像输出宽
my_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
// 图像输出格式,现在的cmos摄像头一般都是会有几种选择:
//yuv420===>V4L2_PIX_FMT_YUYV,或者是V4L2_PIX_FMT_YUV420,
//可以跟驱动工程师获取到
//yuv422==》V4L2_PIX_FMT_YUV422P//jpeg ==>V4L2_PIX_FMT_JPEG/V4L2_PIX_FMT_MJPEG
ret = ioctl(dev_fd, VIDIOC_S_FMT, &my_format);
当然你也可以去获取图像的默认参数或者设置后的参数:
实例代码:
struct v4l2_format my_fmt;
my_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(dev_fd, VIDIOC_G_FMT, &my_fmt);
LOGD(" Width = %d\n", my_fmt.fmt.pix.width);
LOGD(" Height = %d \n", my_fmt.fmt.pix.height);
LOGD(" Image size = %d\n", my_fmt.fmt.pix.sizeimage);
==============================================================================
struct v4l2_buffer buf;
原型:
struct v4l2_buffer
{
__u32 index; //需要使用的buffer个数,非常重要,一般都是3-4个,需要咨询驱动工程师
enum v4l2_buf_type type;
// buffer的类型:如果是video输入输出,
//就和上面的struct v4l2_format保持一致,
//为V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 bytesused; // buffer中已经被图像填充的数据大小:bytes单位__u32 flags;
enum v4l2_field field; // 在video输入输出中一般不用到
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
enum v4l2_memory memory; // video的I/O方式:通常是选择:
//V4L2_MEMORY_MMAP:通过mmap的方式将数据送给用户层
union {
__u32 offset;
unsigned long userptr;
} m; // 用于指示buffer的起始位置,当用mmap的时候,我们会用offset成员
__u32 length; // buffer的大小
__u32 input;
__u32 reserved;
};
用于控制驱动,向dma申请,查询,调度内存buffer
一般都会对应如下几个命令:
VIDIOC_REQBUFS : 向dma申请内存buffer
VIDIOC_QUERYBUF: 查询已经申请好的内存buffer信息,比如buffer的起始位置,大小
VIDIOC_QBUF: 调度空buffer进队列,用于buffer准备填充图像数据
VIDIOC_DQBUF: 调度填充完图像数据buffer出队列,可以交给应用程序使用啦
实例:
1,申请buffer:
struct v4l2_requestbuffers
{
__u32 count;
enum v4l2_buf_type type;
//内存的类型,一般为V4L2_BUF_TYPE_VIDEO_CAPTURE;
enum v4l2_memory memory;
//一般都是V4L2_MEMORY_MMAP
__u32 reserved[2];};
struct v4l2_requestbuffers reqbuf;
bzero(&reqbuf, sizeof(reqbuf));
reqbuf.count = 3; // 申请3个buffer
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;// must be this
ret = ioctl(dev_fd, VIDIOC_REQBUFS, &reqbuf)
//to allocate buffer with dma in kernel space
注意啦,在向内核申请buffer的时候,我们用的
结构体是struct v4l2_requestbuffers
2,查询buffer信息:struct v4l2_buffer buf
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.index = 2; //查询第二个buffer的信息
ret = ioctl(dev_fd, VIDIOC_QUERYBUF, &buf);
LOGD("buf.m.offset = 0x%x\n", buf.m.offset);
LOGD("buf.length; = 0x%x\n", buf.length);
3,将buffer放入工作队列中,准备调度:
struct v4l2_buffer buf;
memset(&buf, 0 ,sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 2; //将第二个buffer放入队列
ret = ioctl(dev_fd, VIDIOC_QBUF, &buf);
4,将已经填充图形数据的buffer出队列:
struct v4l2_buffer dq_buf;
memset(&dq_buf, 0 ,sizeof(struct v4l2_buffer));
dq_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
dq_buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(dev_fd, VIDIOC_DQBUF, &dq_buf);
return dq_buf.index ;
ioctl返回时dq_buf中,内核应经告知到底是哪个buffer出了队列
===================================================================================
开始抓取图片:
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //为1
ioctl(dev_fd, VIDIOC_STREAMOFF, &type);
第三个采参数时就是给一个整型指针
停止获取图像
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(dev_fd, VIDIOC_STREAMON, &type);
================================================================================
到此基本上需要用到的结构体都在这里啦,当然不同cpu的camera hal中v4l2的编程会有些许不同,但是以上几个接口肯定是会用到的