赞
踩
本篇文章主要介绍Linux内核中的V4L2框架,本篇文章所用内核版本:linux-4.19
v4L2 (Video for Linux 2),是linux的一套视频框架,共主体位于内核,可以理解为是整个linux系统上面的视频源捕获驱动框架。其广泛应用在嵌入式设备、移动端以及个人电脑设备上面,市而上使用视频图像采集的设备如:手机、IPC、行车记录仪都会用到这个框架来进行视频采集。
v4L2允许 应用程序 控制图像传感器以及传输格式,应用程序 借此完成拍照、预览、视频记录等图像传感器数据应用。
之前,Linux还存在第一版的V4L2,该版本在内核2.6.38版中放弃支持。
linux采用多层次的驱动架构来对接口进行统一与抽象,最低层次的驱动总是直接面向硬件的,而最高层次的驱动被划分为字符、块、网络设备三大类,前两类驱动在文件系统中形成类似文件的“虚拟文件”,又称为“节点node",这些节点拥有不同的名称代表不同的设备,在目录/dev下进行统一管理,系统调用函数如open、close、read等也与普通文件的操作有相似之处,这种接口的一致性是由VFS(虚拟文件系统层)抽象完成的。
V4L2是关于视频设备的中间驱动层,向上 为 应用程序 访问 视频设备提供了通用接口,向下为设备驱动程序开发提供了统一的V4L2框架。其视频设备节点路径通常为/dev中的videoX。V4L2驱动对用户空间提供“字符设备”的形式,主设备号为81,在用户空间通过各种ioctl调用进行控制,并且可以使用mmap进行内存映射。
V4L2支持多种设备,有以下接口:
√视频采集接口(video capture interface)
√视频输出接口(video output interface)
√直接传输视频接口(video overlay interface)
√视频间隔消隐信号接口(VBI interface)
√收音机接口(radio interface).
应用通过open、ioctl等系统调用操作video设备。在内核空间,Video设备的具体操作
方法由驱动中的struct video_device提供。
驱动使用video_register_device函数将struct video_device注册到V4L2的核心
层,然后V4L2的核心层再向上注册一个字符设备。这样应用就可以使用系统调用访问虚拟
文件系统中Video设备提供的方法,然后进一步访问V4L2核心层提供的v4l2_fops方法集
合,最后通过struct video_device结构休中的fops和ioctl_ops方法集合访问Video
主设备。
Video主设备通过V4L2_subdev_call方法访问Video从设备,同时Video从设备可以
通过notify回调方法通知主设备发生了事件。
v4L2的驱动源码在kernel/drivers/media/v4l2-core目录下,主要代码文件有:
(1)v4l2-dev.c //视频设备硬件的操作,包含video_device的注册、释放等,主要包括 //以下函数: videodev_init register_chrdev_region class_register videodev_exit class_unregister unregister_chrdev_region __video_register_device video_unregister_device (2)v4l2-common.c //一些通用操作,V4l2的子设备一般是摄像头和摄像头控制器, //它们和主机的控制操作是通过i2c总线完成的。V4l2驱动框架中跟i2c相关的代码在 //v4l2_common.c中 v4l2_ctrl_query_fill v4l2_i2c_subdev_init v4l2_i2c_new_subdev_board v4l2_i2c_new_subdev v4l2_i2c_subdev_addr v4l2_i2c_tuner_addrs v4l2_spi_subdev_init v4l2_spi_new_subdev clamp_align v4l_bound_align_image __v4l2_find_nearest_size v4l2_get_timestamp v4l2_g_parm_cap v4l2_s_parm_cap (3)v4l2-device.c //V4L2的设备支持,主要是注册v4I2_device,包括以下函数: v4l2_device_register v4l2_device_unregister v4l2_device_put v4l2_device_release v4l2_device_register_subdev v4l2_device_unregister_subdev v4l2_device_register_subdev_nodes v4l2_device_release_subdev_node (4)v4l2-ioctl.c //处理V4L2的ioctl命令的一个通用的框架。 (5)v412-subdev.c //v4l2子设备 (6)v4l2-mem2mem.c //使用videobuf缓冲区的设备辅助函数。
V4L2缓冲区管理
V4L2维护着两个缓冲队列:一个用于驱动(INPUT队列),另一个用于用户程序
(OUTPUT队列)。
缓冲区(由VIDIOC_REQBUFS命令申请)被用户空间的应用程序放入驱动的队
列中(通过VIDIOC_QBUF ioctl命令)以便填充数据。驱动按顺序填充缓冲区后,
缓冲区由INPUT队列放入OUTPUT队列。
当用户程序调用VIDIOC_DQBUF命令后,驱动会在OUTPUT队列中寻找可用的
缓冲,如果可用则推送到用户程序,不可用则等待直到有可用缓冲后再推送给用
户程序。缓冲区使用完后,必须调用VIDIOC_QBUF将缓冲区重新加入INPUT队列
以便再次填充数据。
注意驱动程序会独立自主的填充INPUT队列中的缓冲区,如果用户程序对缓
冲数据使用不及时,INPUT队列被填满,驱动暂停等待,会产生丢帧。
以字符设备驱动为例,请阅读我之前所写的一篇文章:字符设备驱动的三种实现方法
struct v4l2_device:一个硬件设备可能包含多个子设备,比如一个电视除了有capture设备,可能还有VBI设备或者FM tunner。而v4l2_device就是所有这些设备的根节点,负责管理所有的子设备。
/ * * *struct v4l2_device -用于V4L2设备驱动程序的主结构 * * @dev:指向设备结构体的指针。 * @mdev:指向结构体media_device的指针,可以为NULL。 * @subdevs:用于跟踪已注册的子设备 * @lock:锁定这个结构体;如果该结构嵌入到一个更大的结构中,驱动程序也可以使用 * 该结构。 * @name:唯一的设备名称,默认为驱动器名称+总线ID * @notify:通知进行了某个操作(某些子设备被调用) * @ctrl_handler:控制处理程序。可能是NULL。 * @prio:设备的优先级状态 * @ref:跟踪对这个结构体的引用。 * @release:当ref计数变为0时调用的释放函数。 * * V4L2设备的每个实例都应该创建v4l2_device结构体,无论是独立的还是嵌入到 * 更大的结构体中。 * *它允许轻松访问子设备(参见V4L2 -subdev.h),并提供基本的V4L2设备级支持。 * * . .注意:: * * #) @dev->driver_data指向该结构体。 * #)如果没有父设备,@dev可能是%NULL * / struct v4l2_device { struct device *dev; struct media_device *mdev; struct list_head subdevs; spinlock_t lock; char name[V4L2_DEVICE_NAME_SIZE]; void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg); struct v4l2_ctrl_handler *ctrl_handler; struct v4l2_prio_state prio; struct kref ref; void (*release)(struct v4l2_device *v4l2_dev); };
struct video_device:这个结构体的主要作用时提供/dev/videoX或/dev/v4l-subdevx设备节点,同时对捕获接口进行抽象,用来描述一个出帧的设备。另外,Video子设备也是继承自该结构体。该结构体包含指向v4l2_file_operations、v4l2_ioctl_ops等的操作对象指针。
/ * * * struct video_device——用于创建和管理V4L2设备节点的结构。 * * @entity: &struct media_entity * @intf_devnode:指向&struct media_intf_devnode的指针 * @pipe: &struct media_pipeline * @fops:指向视频设备的&struct v4l2_file_operations的指针 * @device_caps: v4l2_capabilities中使用的设备能力 * @dev: &struct设备用于视频设备 * @cdev:字符设备 * @v4l2_dev:指向struct v4l2_device父设备的指针 * @dev_parent:指向&结构设备父设备的指针 * @ctrl_handler:与该设备节点关联的控制处理程序。可能为NULL。 * @queue: &struct vb2_queue与该设备节点相关联。可能为NULL。 * @prio:指向带有设备优先级状态的struct v4l2_prio_state的指针。 * 如果为NULL,则使用v4l2_dev->prio。 * @name:视频设备名称 * @vfl_type: V4L设备类型,由&enum vfl_devnode_type定义 * @vfl_dir: V4L接收器、发射器或m2m * @minor:设备节点“minor”。如果注册失败,则设置为-1 * @num:视频设备节点编号 * @flags:视频设备标志。使用bitops来设置/清除/测试标志。 * 包含一组&enum v4l2_video_device_flags。 * @index:属性用于区分一个物理设备上的多个索引 * @fh_lock:对所有v4l2_fhs进行锁 * @fh_list: struct v4l2_fh的列表 * @dev_debug:内部设备调试标志,驱动程序不使用 * @tvnorm:支持的电视规范 * * @release:视频设备release()回调函数 * @ioctl_ops:带有ioctl回调函数的指向&struct v4l2_ioctl_ops的指针 * * @valid_ioctls:该设备有效的ioctl的位图 * @lock:指向& &struct mutex 序列化锁的指针 * * . .注意:: *只有在无法从@v4l2_dev推导出@dev_parent时才设置它。 * / * struct video_device { #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity; struct media_intf_devnode *intf_devnode; struct media_pipeline pipe; #endif const struct v4l2_file_operations *fops; u32 device_caps; /* sysfs */ struct device dev; struct cdev *cdev; struct v4l2_device *v4l2_dev; struct device *dev_parent; struct v4l2_ctrl_handler *ctrl_handler; struct vb2_queue *queue; struct v4l2_prio_state *prio; /* device info */ char name[32]; enum vfl_devnode_type vfl_type; enum vfl_devnode_direction vfl_dir; int minor; u16 num; unsigned long flags; int index; /* V4L2 file handles */ spinlock_t fh_lock; struct list_head fh_list; int dev_debug; v4l2_std_id tvnorms; /* callbacks */ void (*release)(struct video_device *vdev); const struct v4l2_ioctl_ops *ioctl_ops; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); struct mutex *lock; };
(1)分配/设置/注册v4l2_device(调用函数注册v4l2_device_register),有辅助作用,提供自旋锁以及引用计数
(2)分配video_device:video_device_alloc()或kzalloc();
(3)设置video_device:.fops、.ioctl_ops、dev;
(4)注册video_device: video_register_device()
参考阅读:https://blog.csdn.net/seiyaaa/article/details/120199720
Video设备注册时的执行流程可总结如下: 1.设置设备注销时资源释放回调和v4l2_device结构体。 2.检查设备类型并确定设备节点基本名称。 3.设置设备类型、次设备号及设备节点数量。 4.将video_device结构体指针保存到全局video_device数组中。 5.根据设备类型验证哪那些ioctl函数可以使用。 6.分配字符设备结构体。 7.设置字符设备的操作函数集合为v4l2_fops。 8.将video设备注册为字符设备,并注册设备; 9.设置设备引用计数为0时的回调函数,该函数主要的工作是删除注册的字符设备, 回调v4l2_device中的release函数(通常是video_device_release函数)释放 video_device结构体内存,最后减少v4l2_device的引用计数; 10.增加video_device所属v4l2_device的引用计数。 11.设置已注册标志V4L2_FL_REGISTERED。
Video设备访问流程
(1)首先通过系统调用访问/dev/videox用户空间设备节点。
(2)进入到内核空间,访问字符设备struct file_operations中的方法。对于Video
设备,该操作集合被v4L2子系统初始化为v4l2_fops集合。
(3)通过v4L2子系统提供的v4l2_fops集合,可直接调用底层驱动实现的Video主设备
struct v4l2_file_operations方法,对于ioctl方法,则需要借助中间函数
__video_do_ioctl调用底层驱动实现的struct v4l2_ioctl_ops中的ioctl功能。
struct v4l2_file_operations方法和struct v4l2_ioctl_ops方法属于主设备
方法,需要主设备的驱动实现。
( 4) struct v4l2_file_operations和struct v4l2_ioctl_ops中的函数都可以
通过v4l2_subdev_call调用video从设备struct v4l2_subdev_core_ops、struct
v4l2_subdev_video_ops、struct v4l2_subdev_pad_ops等方法,这些方法都要在
从设备驱动中实现。
为便于开发,常见的Linux发行版会附带一个v4l2-ctl的命令行工具,可以用来测试摄像头子系统。该工具可以列出系统内的设备列表,查询设备能力,调整设备属性以及设置像素格式、分辨率、帧率等,同时也可以执行捕捉图像等动作。
参考阅读:v4l2-ctl基本使用方法
V4L2调试
由丁video系统的配置较复杂,为便于调试,V4L2提供了简单但庞大的用户空间调试手段,用于跟踪框架或用户空间API的信息。
椎架的调试信息可通过下述命令开启(通过dmesg查看):
# echo 0x3 > /sys/module/videobuf2_v4l2/parameters/debugt
# echo 0x3 > /sys/module/videobuf2_common/parameters/debug
V4L2的用户空问API跟踪通过下述命令开启:
# echo 0x3 >/sys/class/video4linux/video0/dev_debug
Video设备要工作正常,驱动兼容性是一个重要的方面,v4l2-compliance 工具可以通过测试V4L2设备的各个方面来判断其驱动兼容性。
使用方法请阅读:v4l-utils之v4l2-compliance
初步了解V4L2的驱动框架后,下一篇将以《虚拟摄像头驱动:drivers\media\platform\vivid》进行详细分析解读,进一步深入理解,敬请期待.
本文内容主要来自百度百科以及韦东山老师的课程笔记,如有侵权,联系删除!欢迎各位在评论区指导交流!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。