video4linux2(V4L2)是Linux内核中关于视频设备的内核驱动,它为Linux中视频设备访问提供了通用接口,在Linux系统中,V4L2驱动的Video设备节点路径通常/dev/video/中的videoX
V4L2驱动对用户空间提供字符设备,主设备号为81,对于视频设备,其次设备号为0-63。除此之外,次设备号为64-127的Radio设备,次设备号为192-223的是Teletext设备,次设备号为224-255的是VBI设备
V4L2驱动的Video设备在用户空间通过各种ioctl调用进行控制,并且可以使用mmap进行内存映射
命令值如下所示:
[csharp] view plaincopy
---------------------------------------------------------------------
[csharp] view plaincopy
#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) /*查询能力*/
#define VIDIO_G_FMT _IOWR('V', 4, struct v4l2_format) /*获得格式*/
#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) /*设置格式*/
#define VIDIOC_REQBUFS _IOWR('V', 8, strut v4l2_requestbuffers) /*申请内存*/
#define VIDIOC_G_FBUF _IOW('V', 10, struct v4l2_framebuffer) /*获得Framebuffer*/
#define VIDIOC_S_BUF _IOW('V', 11, struct v4l2_framebuffer) /*设置Framebuffer*/
#define VIDIOC_OVERLAY _IOW('V', 14, int) /*设置Overlay*/
#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) /*将内存加入队列*/
#define VIDIOC_DQBUF _IOWR('V', 17, strut v4l2_buffer) /*从队列取出内存*/
#define VIDIOC_STREAMON _IOW('V', 18, int) /*开始流*/
#define VIDIOC_STREAMOFF _IOW('V', 19, int) /*停止流*/
#define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) /*得到控制*/
#define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) /*设置控制*/
---------------------------------------------------------------------
头文件 include/linux/videodev2.h
include/media/v4l2-dev.h
V4L2驱动核心实现文件:driver/media/video/v4l2-dev.c
v4l2-dev.h中定义的video_device是V4L2驱动程序的核心数据结构
[csharp] view plaincopy
struct video_device
{
const struct v4l2_file_operations *fops;
struct cdev *cdev;//字符设备
struct device *parent;//父设备
struct v4l2_device *v4l2_dev;//父v4l2_device
char name[32];//名称
int vfl_type;//类型
int minor;//次设备号
/*释放回调*/
void (*release)(struct video_device *vdev);
/*ioctl回调*/
const struct v4l2_ioctl_ops *ioctl_ops;
}
常用的结构
参见/include/linux/videodev2.h
1)设备能力结构
struct v4l2_capability
{
__u8 driver[16];//驱动名
__u8 card[32];//例如Hauppauge winTV
__u8 bus_info[32];//PCI总线信息
__u32 version;//内核版本
__u32 capabilities;//设备能力
__u32 reserved[4];
};
2)数据格式结构
struct v4l2_format
{
enum v4l2_buf_type type;//本结构的数据类型
};
3)像素格式结构
struct v4l2_pix_format
{
__u32 width;//宽度
__u32 height;//高度
}
4)请求缓冲
struct v4l2_requestbuffers
{
__u32 count;//缓存数量
enum v4l2_buf_type type;//数据流类型
}
5)数据流类型包括V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR
enum v4l2_memory{
};
video4linux2驱动程序的注册drivers/media/video
video_register_device函数用来注册一个v4l驱动程序
[csharp] view plaincopy
int video_register_device(struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 1);
}
其中参数type支持的类型如下
#define VFL_TYPE_GRABBER 0//视频
#define VFL_TYPE_VBI 1//从视频消隐的时间取得信息的设备
#define VFL_TYPE_RADIO 2 //广播
#define VFL_TYPE_VTX 3//视传设备
#define VFL_TYPE_MAX 4//最大值
----------------->返回调用 __video_register_device()
__video_register_device 函数先检查设备类型,接下来
寻找一个可用的子设备号,最后注册相应的字符设备
static int __video_register_device(struct video_device *vdev, int type, int nr, int warn_if_nr_in_use)
{
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VTX:
minor_offset = 192;
minor_cnt = 32;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
}
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
vdev->cdev->ops = &v4l2_fops;
//注册字符设备
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
ret = device_register(&vdev->dev);
//注册完毕设备信息存储在video_device数组中
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
}
v4l2_fops为video4linux2设备提供了统一的应用层接口,v4l2_fops定义如下
[csharp] view plaincopy
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
v4l2_fops中的成员函数最终要调用struct video_device->fops中相应的成员
struct video_device->fops是具体video4linux2摄像头驱动程序必须实现的接口
static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off)
{
return vdev->fops->read(filp, buf, sz, off);
}
驱动探测函数s3c_fimc_probe定义
[csharp] view plaincopy
static int s3c_fimc_probe(struct platform_device *dev)
{
ctrl = s3c_fimc_register_controller(pdev);
clk_enable(ctrl->clock);//使能时钟
//注册V4L2驱动
ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
}
s3c_fimc_register_contoller函数主要用来分配资源与申请中断
static struct s3c_fimc_control *s3c_fimc_register_controller(struct platform_device *pdev)
{
ctrl->vd = &s3c_fimc_video_device[id];
//申请中断
ctrl->irq = platform_get_irq(pdev, 0);
if(request_irq(ctrl->irq, s3c_fimc_irq, IRQF_DISABLED, ctrl->name, ctrl))
};
struct video_device s3c_fimc_video_device[S3C_FIMC_MAX_CTRLS] = {
[0] = {
.vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
.fops = &s3c_fimc_fops,
.ioctl_ops = &s3c_fimc_v4l2_ops,
.release = s3c_fimc_vdev_release,
.name = "sc3_video0",
},
}
s3c_fimc_v4l2_ops,是在drivers/media/video/samsung/fimc中实现的v4l2_ioctl_ops,在用户空间进行ioctl等调用时,要调用到具体实现的各个函数指针
[csharp] view plaincopy
static int s3c_fimc_open(struct file *filp)
{
struct s3c_fimc_control *ctrl;
int id, ret;
id =0;
ctrl = &s3c_fimc.ctrl[id];
mutex_lock(&ctrl->lock);
if (atomic_read(&ctrl->in_use)) {
ret = -EBUSY;
goto resource_busy;
} else {
atomic_inc(&ctrl->in_use);
s3c_fimc_reset(ctrl);
filp->private_data = ctrl;
}
mutex_unlock(&ctrl->lock);
return 0;
resource_busy:
mutex_unlock(&ctrl->lock);
return ret;
}
用户空间
打开设备文件
fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
1)结构体
[csharp] view plaincopy
struct v4l2_capability cap;
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
/include/linux/videodev2.h
struct v4l2_capability {
__u8 driver[16]; /* i.e. "bttv" */
__u8 card[32]; /* i.e. "Hauppauge WinTV" */
__u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
__u32 version; /* should use KERNEL_VERSION() */
__u32 capabilities; /* Device capabilities */
__u32 reserved[4];
};
驱动实现
static int s3c_fimc_v4l2_querycap(struct file *filp, void *fh,
struct v4l2_capability *cap)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
strcpy(cap->driver, "Samsung FIMC Driver");
strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card));
sprintf(cap->bus_info, "FIMC AHB-bus");
cap->version = 0;
cap->capabilities = (V4L2_CAP_VIDEO_OVERLAY | \
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING);
return 0;
}
应用层调用
static int video_capability(int fd)
{
int ret = 0;
/***********get the device capability********/
struct v4l2_capability cap;
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret < 0) {
perror("VIDIOC_QUERYCAP failed ");
return ret;
}
printf("\n****Capability informations****\n");
printf("driver: %s\n", cap.driver);
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
printf("Capture capability is supported\n");
if (cap.capabilities & V4L2_CAP_STREAMING)
printf("Streaming capability is supported\n");
if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
printf("Overlay capability is supported\n");
return 0;
}
[csharp] view plaincopy
结构体
struct v4l2_input input;
int index;
得到INPUT
ret = ioctl(fd, VIDIOC_G_INPUT, &index);
input.index = index;
列举INPUT
ret = ioctl(fd, VIDIOC_ENUMINPUT, &input);
设置INPUT
ret = ioctl(fd, VIDIOC_S_INPUT, &index);
struct v4l2_input {
__u32 index; /* Which input */
__u8 name[32]; /* Label */
__u32 type; /* Type of input */
__u32 audioset; /* Associated audios (bitfield) */
__u32 tuner; /* Associated tuner */
v4l2_std_id std;
__u32 status;
__u32 capabilities;
__u32 reserved[3];
};
Ioctl: VIDIOC_S_INPUT This IOCTL takes pointer to integer containing index of the input which has to be set. Application will provide the index number as an argument.
0 - Composite input,
1 - S-Video input.
驱动
static int s3c_fimc_v4l2_s_input(struct file *filp, void *fh,
unsigned int i)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
if (i >= S3C_FIMC_MAX_INPUT_TYPES)
return -EINVAL;
ctrl->v4l2.input = &s3c_fimc_input_types[i];
if (s3c_fimc_input_types[i].type == V4L2_INPUT_TYPE_CAMERA)
ctrl->in_type = PATH_IN_ITU_CAMERA;
else
ctrl->in_type = PATH_IN_DMA;
return 0;
}
static struct v4l2_input s3c_fimc_input_types[] = {
{
.index = 0,
.name = "External Camera Input",
.type = V4L2_INPUT_TYPE_CAMERA,
.audioset = 1,
.tuner = 0,
.std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M,
.status = 0,
},
{
.index = 1,
.name = "Memory Input",
.type = V4L2_INPUT_TYPE_MEMORY,
.audioset = 2,
.tuner = 0,
.std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M,
.status = 0,
}
};
static int s3c_fimc_v4l2_enum_input(struct file *filp, void *fh,
struct v4l2_input *i)
{
if (i->index >= S3C_FIMC_MAX_INPUT_TYPES)
return -EINVAL;
memcpy(i, &s3c_fimc_input_types[i->index], sizeof(struct v4l2_input));
return 0;
}
应用
static int video_input(int fd)
{
/***********get and set the VIDIO INPUT********/
int ret = 0;
struct v4l2_input input;//视频输入信息,对应命令VIDIOC_ENUMINPUT
int index;
index = 0; //0 - Composite input, 1 - S-Video input.
ret = ioctl (fd, VIDIOC_S_INPUT, &index);
if (ret < 0) {
perror ("VIDIOC_S_INPUT");
return ret;
}
input.index = index;
ret = ioctl (fd, VIDIOC_ENUMINPUT, &input);
if (ret < 0){
perror ("VIDIOC_ENUMINPUT");
return ret;
}
printf("\n****input informations****\n");
printf("name of the input = %s\n", input.name);
return 0;
}