linux应用项目(二)摄像头(2)从零写一个V4L2虚拟摄像头驱动之详细分析
2017-06-22 20:45
621 查看
虚拟摄像头驱动的过程理解透彻了,那么真实摄像头驱动的程序将会十分容易,
本文将总结虚拟摄像头驱动实现的详细细节。相信弄透后字符设备驱动将会十分清晰。
1、简单字符设备
![](http://img.blog.csdn.net/20170623103433731?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2、复杂设备驱动
![](http://img.blog.csdn.net/20170623103507731?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20170622202730566?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
我们要做的是写个硬件相关驱动,其中用到了核心层V4l2-dev提供的API函数。比如内核 中的vivi.c,是一个虚拟视频驱动+虚拟摄像头的例子。实际中我们需要检测到摄像头设备,然后在调用注册函数,产生/dev/video节点。比如USB摄像头,检测到有USB摄像头插入后,调用注册函数,产生设备节点。现在我们写一个虚拟摄像头驱动,加载驱动后就产生设备节点。
(1)仿真vivi.c拷贝头文件。
(2)写入口函数、出口函数
(3)GPL协议
(4)一贯思路:分配,设置,注册
(5)video_register_device中使用了release ,fops 所以也设置一下。
代码如下:
video_register_device(struct video_device *vdev,
int type, int nr)函数解析
内核中说明
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
参数1:video_device结构体
参数2:type:设备类型?
参数3:设备节点后缀 -1 分配一个最小。或者自己固定
(7)video_device结构体
上面分配,设置了半天的结构体到底有什么成员呢?我们需要设置结构体的中的那些重要成员呢,请看下面
重点设置:fops、ioctl_ops、release
(8)测试
首先在虚拟上直接加载驱动的时候,会提示无法找到函数,这里我们通过加载vivi的环境,来创造我们虚拟驱动的环境。
![](http://img.blog.csdn.net/20170622205543415?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
测试结果如下:xawtv并不能识别出我们的/dev/video0,因为没有提供相关函数,如myvivi_vidioc_querycap,应用程序无法从中获得是否是视频捕获设备。
![](http://img.blog.csdn.net/20170622205644177?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
(2)注意增加的是v4l2_ioctl_ops 类型里面的函数,即 myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
(3)提供第一个ioctl函数 vidioc_querycap
v4l2_ioctl_ops(V4l2-ioctl.h (linux-3.4.2\include\media))
参考vivi.c中:
![](http://img.blog.csdn.net/20170622210845677?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
应用程序调用vidioc_querycap分析,用来判断它是否是一个视频捕获设备。
(4)v4l2_file_operations结构体
应用程序要调用ioctl ,所以这里提供。
static const struct v4l2_file_operations myvivi_fops = {
.owner
= THIS_MODULE,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
};
(5)file_operations结构体(核心层,应用程序系统调用首先会调用这里的)和 v4l2_file_operations结构体(硬件层,核心层会调用到这里)
都在V4l2-dev.h里面定义。 (linux-3.4.2\include\media)
(6)调用过程:
应用程序ioctl --》核心层file_operations v4l2_fops里面的.unlocked_ioctl = v4l2_ioctl被调用。v4l2_ioctl里面调用:vdev->fops->ioctl(filp, cmd, arg);设备fops里面的ioctl:v4l2_file_operations
myvivi_fops里面.ioctl = video_ioctl2被调用。video_ioctl2--》video_usercopy(__video_do_ioctl)--》v4l2_ioctl_ops myvivi_ioctl_ops里面的ioctl被调用。
用户层调用ioctl(),经过v4l2_ioctl —->video_ioctl2——>__video_do_ioctl()。
(7)调用框图
![](http://img.blog.csdn.net/20170622221537914?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20170622221612928?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3l1d3V6dTA1MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
vivi.c中这么做:支持很多格式,定义了vivi_fmt。
我们只支持一种V4L2_PIX_FMT_YUYV:改为如下即可
vivi里这么做:构造了v4l2_format结构体给用户空间
(内核驱动空间返回给用户空间v4l2_format结构体)
用户空间与内核驱动空间比较v4l2_format结构体
4、增加.vidioc_s_fmt_vid_cap(设置当前驱动的视频捕获格式)
用户空间来设置内核驱动空间v4l2_format结构体
vivi.c这么做
本文将总结虚拟摄像头驱动实现的详细细节。相信弄透后字符设备驱动将会十分清晰。
零、字符设备编写思路
简单字符设备常规的方法是单层,实现入口、出口修饰一下即可,而复杂一点的字符设备驱动则采用分层的架构,内核为我们提供核心层及API,然后我们实现硬件部分的驱动,摄像头驱动便是如此,应重点把握里面的几个重要结构体及系统调用过程。1、简单字符设备
2、复杂设备驱动
一、框架搭建
内核在V4l2-dev.c (linux-3.4.2\drivers\media\video)中提供了V4L2的核心函数。我们再来看一下整体框架:我们要做的是写个硬件相关驱动,其中用到了核心层V4l2-dev提供的API函数。比如内核 中的vivi.c,是一个虚拟视频驱动+虚拟摄像头的例子。实际中我们需要检测到摄像头设备,然后在调用注册函数,产生/dev/video节点。比如USB摄像头,检测到有USB摄像头插入后,调用注册函数,产生设备节点。现在我们写一个虚拟摄像头驱动,加载驱动后就产生设备节点。
(1)仿真vivi.c拷贝头文件。
(2)写入口函数、出口函数
(3)GPL协议
(4)一贯思路:分配,设置,注册
(5)video_register_device中使用了release ,fops 所以也设置一下。
代码如下:
/* 仿照vivi.c */ #include <linux/module.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/pci.h> #include <linux/random.h> #include <linux/version.h> #include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/freezer.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE, }; static struct video_device *myvivi_device; static void myvivi_release(struct video_device *vdev) { } static int myvivi_init(void) { int error; /* 1. 分配一个video_device结构体 */ myvivi_device = video_device_alloc(); /* 2. 设置 */ myvivi_device->release = myvivi_release; myvivi_device->fops = &myvivi_fops; /* 3. 注册 */ error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1); return error; } static void myvivi_exit(void) { video_unregister_device(myvivi_device); video_device_release(myvivi_device); } module_init(myvivi_init); module_exit(myvivi_exit); MODULE_LICENSE("GPL");(6)static inline int __must_check
video_register_device(struct video_device *vdev,
int type, int nr)函数解析
内核中说明
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
参数1:video_device结构体
参数2:type:设备类型?
参数3:设备节点后缀 -1 分配一个最小。或者自己固定
(7)video_device结构体
上面分配,设置了半天的结构体到底有什么成员呢?我们需要设置结构体的中的那些重要成员呢,请看下面
重点设置:fops、ioctl_ops、release
struct video_device { #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity; #endif /* device ops */ const struct v4l2_file_operations *fops; /* sysfs */ struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ /* Set either parent or v4l2_dev if your driver uses v4l2_device */ struct device *parent; /* device parent */ struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; /* Priority state. If NULL, then v4l2_dev->prio will be used. */ struct v4l2_prio_state *prio; /* device info */ char name[32]; int vfl_type; /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; /* use bitops to set/clear/test flags */ unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ int index; /* V4L2 file handles */ spinlock_t fh_lock; /* Lock for all v4l2_fhs */ struct list_head fh_list; /* List of struct v4l2_fh */ int debug; /* Activates debug level*/ /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ void (*release)(struct video_device *vdev); /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; /* serialization lock */ struct mutex *lock; };
(8)测试
首先在虚拟上直接加载驱动的时候,会提示无法找到函数,这里我们通过加载vivi的环境,来创造我们虚拟驱动的环境。
测试结果如下:xawtv并不能识别出我们的/dev/video0,因为没有提供相关函数,如myvivi_vidioc_querycap,应用程序无法从中获得是否是视频捕获设备。
二、加入v4l2_ioctl_ops中的必要ioctl函数。并实现vidioc_querycap
(1)代码如下static int myvivi_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { strcpy(cap->driver, "myvivi"); strcpy(cap->card, "myvivi"); cap->version = 0x0001; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; return 0; } static const struct v4l2_ioctl_ops myvivi_ioctl_ops = { // 表示它是一个摄像头设备 .vidioc_querycap = myvivi_vidioc_querycap, #if 0 /* 用于列举、获得、测试、设置摄像头的数据的格式 */ .vidioc_enum_fmt_vid_cap = myvivi_vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = myvivi_vidioc_s_fmt_vid_cap, /* 缓冲区操作: 申请/查询/放入队列/取出队列 */ .vidioc_reqbufs = myvivi_vidioc_reqbufs, .vidioc_querybuf = myvivi_vidioc_querybuf, .vidioc_qbuf = myvivi_vidioc_qbuf, .vidioc_dqbuf = myvivi_vidioc_dqbuf, // 启动/停止 .vidioc_streamon = myvivi_vidioc_streamon, .vidioc_streamoff = myvivi_vidioc_streamoff, #endif }; static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ }; static struct video_device *myvivi_device; static void myvivi_release(struct video_device *vdev) { } static int myvivi_init(void) { int error; /* 1. 分配一个video_device结构体 */ myvivi_device = video_device_alloc(); /* 2. 设置 */ myvivi_device->release = myvivi_release; myvivi_device->fops = &myvivi_fops; myvivi_device->ioctl_ops = &myvivi_ioctl_ops; /* 3. 注册 */ error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1); return error; }
(2)注意增加的是v4l2_ioctl_ops 类型里面的函数,即 myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
(3)提供第一个ioctl函数 vidioc_querycap
static const struct v4l2_ioctl_ops myvivi_ioctl_ops = { // 表示它是一个摄像头设备 .vidioc_querycap = myvivi_vidioc_querycap, #if 0 /* 用于列举、获得、测试、设置摄像头的数据的格式 */ .vidioc_enum_fmt_vid_cap = myvivi_vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = myvivi_vidioc_s_fmt_vid_cap, /* 缓冲区操作: 申请/查询/放入队列/取出队列 */ .vidioc_reqbufs = myvivi_vidioc_reqbufs, .vidioc_querybuf = myvivi_vidioc_querybuf, .vidioc_qbuf = myvivi_vidioc_qbuf, .vidioc_dqbuf = myvivi_vidioc_dqbuf, // 启动/停止 .vidioc_streamon = myvivi_vidioc_streamon, .vidioc_streamoff = myvivi_vidioc_streamoff, #endif };v4l2_ioctl_ops结构体很大,还好不是所有都是必须的,下面列出了一些必须的ioctl函数。后面会一一详细说明功能。
v4l2_ioctl_ops(V4l2-ioctl.h (linux-3.4.2\include\media))
struct v4l2_ioctl_ops{ /* ioctl callbacks */ /* VIDIOC_QUERYCAP handler */ int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);判断它是否是一个视频捕获设备 /* VIDIOC_ENUM_FMT handlers */ int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_fmtdesc *f); 获取当前驱动支持的视频格式 /* VIDIOC_G_FMT handlers */ int (*vidioc_g_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); 读取当前驱动的频捕获格式 /* VIDIOC_S_FMT handlers */ int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); 设置当前驱动的频捕获格式 /* VIDIOC_TRY_FMT handlers */ int (*vidioc_try_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); 验证当前驱动的显示格式 /* Buffer handlers */ int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b); 分配内存 int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b); 把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址 int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b); 把数据从缓存中读取出来 int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b); 把数据放回缓存队列 /* Stream on/off */ int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i); 开始视频显示函数 int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i); 结束视频显示函数 }
参考vivi.c中:
static int myvivi_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { strcpy(cap->driver, "myvivi"); strcpy(cap->card, "myvivi"); cap->version = 0x0001; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; return 0; }vidioc_querycap作用:
应用程序调用vidioc_querycap分析,用来判断它是否是一个视频捕获设备。
static int vidioc_querycap(struct file * file,void *priv,struct v4l2_capability *cap) { /** 先来看看这个v4l2_capability struct v4l2_capability { __u8 driver[16]; //驱动名字 __u8 card[32]; //设备名字 __u8 bus_info[32]; //设备在系统中的位置 Location of the device in the system __u32 version; //驱动的版本号 __u32 capabilities; //设备能力集 __u32 reserved[4]; //保留字段,驱动必须要将这个数组设置为0 }; */ struct vivi_fh *fh = priv; //从vivi_fh中获取vivi_dev struct vivi_dev *dev = fh->dev; strcpy(cap->driver,"vivi"); //驱动的名字 strcpy(cap->card,"vivi"); //设备的名字 //设备在系统中的位置 strlcpy(cap->bus_info,dev->v4l2_dev.name,sizeof(cap->bus_info)); cap->version = VIVI_VERSION; //版本号 //能力集合 //V4L2_CAP_VIDEO_CAPTURE : 是一个视频捕捉设备 //V4L2_CAP_STREAMING :支持ioctl系统调用来获取数据 //V4L2_CAP_READWRITE :支持read/write系统调用来获取数据 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; return 0; }这里我们知道了这个设备是一个视频捕获设备,且支持系统调用来获取数据。
(4)v4l2_file_operations结构体
应用程序要调用ioctl ,所以这里提供。
static const struct v4l2_file_operations myvivi_fops = {
.owner
= THIS_MODULE,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
};
(5)file_operations结构体(核心层,应用程序系统调用首先会调用这里的)和 v4l2_file_operations结构体(硬件层,核心层会调用到这里)
都在V4l2-dev.h里面定义。 (linux-3.4.2\include\media)
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, #ifdef CONFIG_COMPAT .compat_ioctl = v4l2_compat_ioctl32, #endif .release = v4l2_release, .poll = v4l2_poll, .llseek = no_llseek, };
struct v4l2_file_operations { struct module *owner; ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*ioctl) (struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); #ifdef CONFIG_COMPAT long (*compat_ioctl32) (struct file *, unsigned int, unsigned long); #endif unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct file *); int (*release) (struct file *); };
(6)调用过程:
应用程序ioctl --》核心层file_operations v4l2_fops里面的.unlocked_ioctl = v4l2_ioctl被调用。v4l2_ioctl里面调用:vdev->fops->ioctl(filp, cmd, arg);设备fops里面的ioctl:v4l2_file_operations
myvivi_fops里面.ioctl = video_ioctl2被调用。video_ioctl2--》video_usercopy(__video_do_ioctl)--》v4l2_ioctl_ops myvivi_ioctl_ops里面的ioctl被调用。
用户层调用ioctl(),经过v4l2_ioctl —->video_ioctl2——>__video_do_ioctl()。
(7)调用框图
三、增加列举、获得、测试、设置摄像头的数据格式的ioctl
1、增加 .vidioc_enum_fmt_vid_cap(获取支持的视频格式)vivi.c中这么做:支持很多格式,定义了vivi_fmt。
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct vivi_fmt *fmt; if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; fmt = &formats[f->index]; strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; }v4l2_fmtdesc是要返回给用户空间的,用户空间调用这个ioctl来获取支持的视频格式。
我们只支持一种V4L2_PIX_FMT_YUYV:改为如下即可
static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index >= 1) return -EINVAL; strcpy(f->description, "4:2:2, packed, YUYV"); f->pixelformat = V4L2_PIX_FMT_YUYV; return 0; }2、增加.vidioc_g_fmt_vid_cap(返回当前视频捕获格式)
vivi里这么做:构造了v4l2_format结构体给用户空间
(内核驱动空间返回给用户空间v4l2_format结构体)
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); f->fmt.pix.width = dev->width; 宽度 f->fmt.pix.height = dev->height; 高度 f->fmt.pix.field = dev->field; f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = 每一行长度 (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV || 整个图像的大小 dev->fmt->fourcc == V4L2_PIX_FMT_UYVY) f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; else f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; return 0; }我们直接定义一个v4l2_format结构体。
static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { memcpy(f, &myvivi_format, sizeof(myvivi_format)); return (0); }3、增加.vidioc_try_fmt_vid_cap(测试驱动程序是否支持某种格式)
用户空间与内核驱动空间比较v4l2_format结构体
/* 测试驱动程序是否支持某种格式 */ static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { unsigned int maxw, maxh; enum v4l2_field field; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) //不支持这个 就返回错误 return -EINVAL; field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) { field = V4L2_FIELD_INTERLACED; } else if (V4L2_FIELD_INTERLACED != field) { return -EINVAL; } maxw = 1024; //设备最大支持的宽度、高度 maxh = 768; /* 调整format的width, height, * 计算bytesperline, sizeimage */ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3; //16颜色深度 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; }
4、增加.vidioc_s_fmt_vid_cap(设置当前驱动的视频捕获格式)
用户空间来设置内核驱动空间v4l2_format结构体
vivi.c这么做
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; int ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; if (vb2_is_streaming(q)) { dprintk(dev, 1, "%s device busy\n", __func__); return -EBUSY; } dev->fmt = get_format(f); dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; dev->field = f->fmt.pix.field; return 0; }我们直接拷贝到结构体之中去,实际存在硬件的时候,是要操作给硬件摄像头的。现在存到结构体中。
static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f); if (ret < 0) return ret; memcpy(&myvivi_format, f, sizeof(myvivi_format)); return ret; }
四、增加缓冲区操作的ioctl函数
未完待续。。。。。相关文章推荐
- linux应用项目(二)摄像头(1)V4L2框架分析
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- linux gspca usb摄像头驱动添加对新型号的详细移植步骤
- Linux 驱动学习笔记(file_operations结构体详细分析)
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- 和菜鸟一起学linux之V4L2摄像头应用流程
- 一个基于v4l2框架的输出驱动分析
- DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析
- DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析
- linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(2)
- ov9650摄像头驱动之——linux内核v4l2架构分析1
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析 推荐
- linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1)
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- V4L2视频驱动和应用分析
- V4L2视频驱动和应用分析