android的camera学习(4) 您所在的位置:网站首页 安卓代码框架怎么用的 android的camera学习(4)

android的camera学习(4)

2024-07-17 15:59| 来源: 网络整理| 查看: 265

文章目录 一、什么是V4L2二、V4l2的基本流程三、v4l2框架整理四、V4l2的buf管理五、V4L2的整体流程 参考笔记:

https://blog.csdn.net/yhg20090519/article/details/78366179 https://blog.csdn.net/yhg20090519/article/details/78496936 https://www.cnblogs.com/vedic/p/10763838.html https://blog.csdn.net/kuangzuxiaoN/article/details/77048509 一、什么是V4L2

相机驱动层位于HAL Moudle与硬件层之间,借助linux内核驱动框架,以文件节点的方式暴露接口给用户空间,让HAL Module通过标准的文件访问接口,从而能够将请求顺利地下发到内核中,而在内核中,为了更好的支持视频流的操作,早先提出了v4l视频处理框架,但是由于操作复杂,并且代码无法进行较好的重构,难以维护等原因,之后便衍生出了v4l2框架。

按照v4l2标准,它将一个数据流设备抽象成一个videoX节点,从属的子设备都对应着各自的v4l2_subdev实现,并且通过media controller进行统一管理,整个流程复杂但高效,同时代码的扩展性也较高。

所以我们可以看到对应的相机节点再dev目录下

mvk_8qxp:/ # ls /dev/video* /dev/vidie0 /dev/video1 /dev/video3 /dev/video4

这也就是针对我们调试相机的时候可以看到的一些基础信息

二、V4l2的基本流程

在这里插入图片描述

1. 打开video设备 在需要进行视频数据流的操作之前,首先要通过标准的字符设备操作接口open方法来打开一个video设备,并且将返回的字符句柄存在本地,之后的一系列操作都是基于该句柄,而在打开的过程中,会去给每一个子设备的上电,并完成各自的一系列初始化操作。

2. 查看并设置设备 在打开设备获取其文件句柄之后,就需要查询设备的属性,该动作主要通过ioctl传入VIDIOC_QUERYCAP参数来完成,其中该系列属性通过v4l2_capability结构体来表达,除此之外,还可以通过传入VIDIOC_ENUM_FMT来枚举支持的数据格式,通过传入VIDIOC_G_FMT/VIDIOC_S_FMT来分别获取和获取当前的数据格式,通过传入VIDIOC_G_PARM/VIDIOC_S_PARM来分别获取和设置参数。

3. 申请帧缓冲区 完成设备的配置之后,便可以开始向设备申请多个用于盛装图像数据的帧缓冲区,该动作通过调用ioctl并且传入VIDIOC_REQBUFS命令来完成,最后将缓冲区通过mmap方式映射到用户空间

4. 将帧缓冲区入队 申请好帧缓冲区之后,通过调用ioctl方法传入VIDIOC_QBUF命令来将帧缓冲区加入到v4l2 框架中的缓冲区队列中,静等硬件模块将图像数据填充到缓冲区中

5. 开启数据流 将所有的缓冲区都加入队列中之后便可以调用ioctl并且传入VIDIOC_STREAMON命令,来通知整个框架开始进行数据传输,其中大致包括了通知各个子设备开始进行工作,最终将数据填充到V4L2框架中的缓冲区队列中。

6. 将帧缓冲区出队 一旦数据流开始进行流转了,我们就可以通过调用ioctl下发VIDIOC_DQBUF命令来获取帧缓冲区,并且将缓冲区的图像数据取出,进行预览、拍照或者录像的处理,处理完成之后,需要将此次缓冲区再次放入V4L2框架中的队列中等待下次的图像数据的填充。 整个采集图像数据的流程现在看来还是比较简单的,接口的控制逻辑很清晰,主要原因是为了提供给用户的接口简单而且抽象,这样方便用户进行集成开发,其中的大部分复杂的业务处理都被V4L2很好的封装了,接下来我们来详细了解下V4L2框架内部是如何表达以及如何运转的。

对应的函数流程可以用这个图来表示: 在这里插入图片描述 其实可以看出,V4L2的整个流程都是用ioctl完成的,关于ioctl,我们后面说。

三、v4l2框架整理

在这里插入图片描述 v4l2_device 充当父类,通过链表把所有注册到其下的子设备管理起来,这些设备可以是GRABBER也可以是RADIO或者VBI的。 这就是里面一些属性,有一些没有看明白名,所以这里就没有详解。

// kernel_imx/include/media/v4l2-device.h 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); //释放v4l2设备函数指针 };

v4l2_subdev结构体包含了对设备操作的ops和ctrls,这部分代码和硬件相关,需要驱动工程师根据硬件实现控制上下电、读取ID、饱和度、对比度和视频数据流打开关闭等接口函数。 这个结构体代表每一个子设备在初始化的时候都要挂载在v4l2_device上,将其统一管理。

// kernel_imx/include/media/v4l2-subdev.h struct v4l2_subdev { #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity; #endif struct list_head list; struct module *owner; //模块拥有者 bool owner_v4l2_dev; u32 flags; struct v4l2_device *v4l2_dev; //指向父设备 const struct v4l2_subdev_ops *ops; //提供一些控制v4l2设备的接口 const struct v4l2_subdev_internal_ops *internal_ops; //向V4L2框架提供的接口函数 struct v4l2_ctrl_handler *ctrl_handler; //subdev控制接口 char name[V4L2_SUBDEV_NAME_SIZE]; //子设备名 u32 grp_id; void *dev_priv; void *host_priv; struct video_device *devnode; //video_device 设备节点 struct device *dev; struct fwnode_handle *fwnode; struct list_head async_list; struct v4l2_async_subdev *asd; struct v4l2_async_notifier *notifier; struct v4l2_async_notifier *subdev_notifier; struct v4l2_subdev_platform_data *pdata; };

下面两个结构体是

const struct v4l2_subdev_ops *ops; //提供一些控制v4l2设备的接口 const struct v4l2_subdev_internal_ops *internal_ops; //向V4L2框架提供的接口函数

这两个属性的结构体:

struct v4l2_subdev_ops { const struct v4l2_subdev_core_ops *core; //视频设备通用的操作:初始化、加载FW、上电和RESET等 const struct v4l2_subdev_tuner_ops *tuner; //tuner特有的操作 const struct v4l2_subdev_audio_ops *audio; //audio特有的操作 const struct v4l2_subdev_video_ops *video; //视频设备的特有操作:裁剪图像、开关视频流等 const struct v4l2_subdev_vbi_ops *vbi; const struct v4l2_subdev_ir_ops *ir; const struct v4l2_subdev_sensor_ops *sensor; const struct v4l2_subdev_pad_ops *pad; }; /** * struct v4l2_subdev_internal_ops - V4L2 subdev internal ops * * @registered: called when this subdev is registered. When called the v4l2_dev * field is set to the correct v4l2_device. * * @unregistered: called when this subdev is unregistered. When called the * v4l2_dev field is still set to the correct v4l2_device. * * @open: called when the subdev device node is opened by an application. * * @close: called when the subdev device node is closed. Please note that * it is possible for @close to be called after @unregistered! * * @release: called when the last user of the subdev device is gone. This * happens after the @unregistered callback and when the last open * filehandle to the v4l-subdevX device node was closed. If no device * node was created for this sub-device, then the @release callback * is called right after the @unregistered callback. * The @release callback is typically used to free the memory containing * the v4l2_subdev structure. It is almost certainly required for any * sub-device that sets the V4L2_SUBDEV_FL_HAS_DEVNODE flag. * * .. note:: * Never call this from drivers, only the v4l2 framework can call * these ops. */ struct v4l2_subdev_internal_ops { /* 当subdev注册时被调用,读取IC的ID来进行识别 */ int (*registered)(struct v4l2_subdev *sd); void (*unregistered)(struct v4l2_subdev *sd); /* 当设备节点被打开时调用,通常会给设备上电和设置视频捕捉FMT */ int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); /* 释放对应的subdev */ void (*release)(struct v4l2_subdev *sd); };

调用 video_device 结构体进行设备的注册

// kernel_imx/include/media/v4l2-dev.h 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; // v4l 设备 struct cdev *cdev; // 字符设备 struct v4l2_device *v4l2_dev; // v4l2_device 父设备 struct device *dev_parent; // 父设备 struct v4l2_ctrl_handler *ctrl_handler; // 设备节点的控制处理结构,可能为NULL 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 */ /* ioctl回调函数集,提供file_operations中的ioctl调用 */ void (*release)(struct video_device *vdev); // 释放函数 const struct v4l2_ioctl_ops *ioctl_ops; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); struct mutex *lock; ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); };

v4l2_fh是用来保存子设备的特有操作方法,也就是下面要分析到的v4l2_ctrl_handler,内核提供一组v4l2_fh的操作方法,通常在打开设备节点时进行v4l2_fh注册。

// kernel_imx/include/media/v4l2-fh.h /** * struct v4l2_fh - Describes a V4L2 file handler * * @list: list of file handlers * @vdev: pointer to &struct video_device * @ctrl_handler: pointer to &struct v4l2_ctrl_handler * @prio: priority of the file handler, as defined by &enum v4l2_priority * * @wait: event' s wait queue * @subscribe_lock: serialise changes to the subscribed list; guarantee that * the add and del event callbacks are orderly called * @subscribed: list of subscribed events * @available: list of events waiting to be dequeued * @navailable: number of available events at @available list * @sequence: event sequence number * * @m2m_ctx: pointer to &struct v4l2_m2m_ctx */ struct v4l2_fh { struct list_head list; struct video_device *vdev; struct v4l2_ctrl_handler *ctrl_handler; enum v4l2_priority prio; /* Events */ wait_queue_head_t wait; struct mutex subscribe_lock; struct list_head subscribed; struct list_head available; unsigned int navailable; u32 sequence; struct v4l2_m2m_ctx *m2m_ctx; }; /*这个头文件中还包含了如何去初始化子设备等、添加子设备、删除子设备*/ void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev); /** * v4l2_fh_add - Add the fh to the list of file handles on a video_device. * * @fh: pointer to &struct v4l2_fh * * .. note:: * The @fh file handle must be initialised first. */ void v4l2_fh_add(struct v4l2_fh *fh); /** * v4l2_fh_open - Ancillary routine that can be used as the open\(\) op * of v4l2_file_operations. * * @filp: pointer to struct file * * It allocates a v4l2_fh and inits and adds it to the &struct video_device * associated with the file pointer. */ int v4l2_fh_open(struct file *filp); /** * v4l2_fh_del - Remove file handle from the list of file handles. * * @fh: pointer to &struct v4l2_fh * * On error filp->private_data will be %NULL, otherwise it will point to * the &struct v4l2_fh. * * .. note::

v4l2_ctrl_handler是用于保存子设备控制方法集的结构体,对于视频设备这些ctrls包括设置亮度、饱和度、对比度和清晰度等,用链表的方式来保存ctrls,可以通过v4l2_ctrl_new_std函数向链表添加ctrls。 其实下面的注释已经有对应的说明了

// kernel_imx/include/media/v4l2-ctrls.h struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s64 min, s64 max, u64 step, s64 def); /** * v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 * menu control. * * @hdl: The control handler. 已经初始化的v4l2_ctrl_handler的结构体 * @ops: The control ops. 已经设置好的v4l2_ctrl_ops结构体 * @id: The control ID. 对应的ID通过;ioctl传递的参数,也就是序列号 * @max: The control's maximum value. 操作范围的最大值和最小值 * @mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for * menus with


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有