V4L2 简介
Video for Linux two(Video4Linux2)简称 V4L2,是 V4L 的改进版。V4L2 是 linux操作系统下用于视频和音频数据采集设备的驱动框架,为驱动和应用程序提供了一套统一的接口规范。
在 Linux 下,所有外设都被看成一种特殊的文件,成为“设备文件”,可以象访问普通文件一样对其进行读写。采用 V4L2 驱动的摄像头设备文是/dev/videoX以及一些子设备/dev/v4l-subdevX。V4L2 支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。应用层通过 open/ioctl/close 等函数来操作V4L2 设备文件的方式控制 camera 设备。
V4L2 支持的设备:
- Video capture device : 从摄像头等设备上获取视频数据。对很多人来讲,video capture 是 V4L2 的基本应用。设备名称为/dev/video,主设备号 81,子设备号 0~63
- Video output device : 将视频数据编码为模拟信号输出。与 video capture 设备名相同。
- Video overlay device : 将同步锁相视频数据(如 TV)转换为 VGA 信号,或者将抓取的视频数据直接存放到视频卡的显存中。
- Video output overlay device :也被称为 OSD(On-Screen Display)
- VBI device : 提供对 VBI(Vertical Blanking Interval)数据的控制,发送 VBI 数据或抓取 VBI 数据。设备名/dev/vbi0~vbi31,主设备号 81,子设备号 224~255
- Radio device : FM/AM 发送和接收设备。设备名/dev/radio0~radio63,主设备号81,子设备号 64~127
V4L2 架构总览
总体架构图
CRM 根设备
CRM 即 camera request manager,创建了 V4L2 根设备/dev/video0,供所有子设备注册。
V4L2 子设备
每个子设备实现特定功能,如音视频混合,编解码等。对于 camera,有如下子设备:
SENSOR, IFE, ICP, LRME, JPEG, FD, CPAS, CSIPHY, ACTUATOR, CCI, FLASH,EEPROM, OIS 等。
通过设备文件/dev/v4l-subdevX 供应用层(CSL)调用其功能。
V4L2 驱动层实现
V4L2 驱动架构
几个重要的结构体
- video_device:保存 V4L2 的 device node 数据。
- v4l2_device:表示一个 v4l2 设备,各个子设备都挂在这个结构体里面。
- v4l2_subdev:每个子设备都有一个实例。
Kernel V4l2 的文档:
https://www.kernel.org/doc/html/v4.9/media/kapi/v4l2-core.html
驱动主要实现的功能
- 具体硬件的控制代码
- 实现 V4L2 架构相关结构体与函数
- 将代码(结构体与函数)与 V4L2 框架绑定
要实现的主要结构体
struct video_device
主要的任务就是负责向内核注册字符设备。
此结构体既可以动态分配,也可以嵌入到一个更大的结构体中。
动态分配方法如下:
struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
return -ENOMEM;
vdev->release = video_device_release;
也可嵌入到一个大结构体中,此时要实现 release()回调:
struct video_device *vdev = &my_vdev->vdev;
vdev->release = my_vdev_release;
设置结构体主要域:
- v4l2_dev: 设置为 v4l2_device 父设备。
- name: 设置为唯一的描述性设备名。
- fops: 设置为已有的 v4l2_file_operations 结构体。实现文件操作。设置 .unlocked_ioctl 指向 video_ioctl2。请勿使用 .ioctl!它已被废弃。
- ioctl_ops: 一般设为 v4l2_ioctl_ops 来简化 ioctl 的维护。
- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后释放。详见下一节。
- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己实现的 v4l2_prio_state 结构体。
注册视频设备:
err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (err) {
video_device_release(vdev); /* or kfree(my_vdev); */
return err;
}
这个操作会创建一个字符设备
注销设备:
video_unregister_device(vdev);
这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。
struct v4l2_device
struct v4l2_device:一个硬件设备可能包含多个子设备,比如 camera 包含 sensor 设备,actuator 设备,flash 设备等。而 v4l2_device 就是所有这些设备的根节点,负责管理所有的子设备。
简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体嵌入到一个更大的结构体中。
注册这个设备实例:
v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
注销 v4l2_device 使用如下函数:
v4l2_device_unregister(struct v4l2_device *v4l2_dev);
struct v4l2_subdev
struct v4l2_subdev:子设备,负责实现具体的功能。
子设备驱动可使用如下函数初始化 v4l2_subdev 结构体:
v4l2_subdev_init(sd, &ops);
向 v4l2_device 注册 v4l2_subdev:
int err = v4l2_device_register_subdev(v4l2_dev, sd);
注销子设备则可用如下函数:
v4l2_device_unregister_subdev(sd);
此后,子设备模块就可卸载,且 sd->dev == NULL。
要实现的主要函数
video_device->v4l2_file_operations
实现文件类操作,比如 open,close,read,write,mmap 等。但是 ioctl 是不需要实现的,一般都是用 video_ioctl2 代替。
.unlocked_ioctl = video_ioctl2,
video_device->v4l2_ioctl_ops
V4L2 导出给应用层使用的所有 ioctl。只要实现需要用的,如:
v4l2_subdev->v4l2_subdev_ops 及其子函数:
必须实现的:
v4l2_subdev_core_ops
其它可按需选择,如:
v4l2_subdev_video_ops
V4L2 应用层实现
V4L2 是一个字符设备,通过设备文件向应用层提供各种功能。应用层可以像操作普通文件一样 open/close V4L2 设备文件,而 V4L2 的大部分功能都是通过设备文件的 ioctl 导出的。
IOCTL 命令分类
功能查询相关
优先级相关
视频捕获相关
TV 视频相关
输入输出相关
控制相关
杂项
V4l2 设备的基本操作流程
- 打开并获取设备属性
- 设置帧格式
- 设置帧缓冲
- 开始进行视频流采集
- 停止采集关闭设备
高通平台 V4L2 架构实现
驱动层实现
驱动对应用层提供的设备节点,V4L2 相关设备文件:
其中部分子设备类型如下:
//未完待续...