【v4l2】Linux多媒体框架 - v4l2 core详解
简介
V4L2是Video for linux2的简称,是Linux中关于视频设备的内核驱动框架。
常用结构体:
定义在include/linux/videodev2.h
struct v4l2_requestbuffers // 申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability // 视频设备的功能,对应命令VIDIOC_QUERYCAP
struct v4l2_input // 视频输入信息,对应命令VIDIOC_ENUMSTD
struct v4l2_standard // 视频的制式,如PAL,NTSC,对应命令VIDIOC_ENUMSTD
struct v4l2_buffer // 驱动中的一帧图像的缓存,对应命令VIDIC_QUERYBUF
struct v4l2_format // 帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_crop // 视频信号矩形边框
接口:
V4L2规范中不仅定义了通用API元素(Common API Elements),图像的格式(Image Formats),输入/输出方法(Input/Output),还定义了Linux内核驱动处理视频信息的一系列接口:
Video Capture Interface : 视频采集接口,应用于摄像头
Video Output Interface:视频输出接口,将静止图像或图像序列编码为模拟视频接口
Video overlay Interface : 视频覆盖/预览接口,可以将采集到的视频数据直接传输到显示设备,无需cpu参与,这种方式显示图像的效率比其他方式更高
Video Output Overlay Interface :视频输出覆盖接口
Codec Interface : 编解码接口
数据采集:
V4L2支持内存映射方式(mmap)和直接读取方式(read)来采集数据,欠着用于连续视频数据的采集,后者常用于静态图像数据的采集:
- 帧传输:使用read和write方法,每一帧都要通过I/O操作在用户和内核空间之间拷贝数据
- 流传输:用户与内核空间之间交换缓冲区指针,这些缓冲区将被映射到应用的地址空间
v4l2-core
V4L2是Linux内核中的关于视频设备驱动的框架,对上向应用层提供统一的接口,对下支持各类复杂硬件的灵活扩展;v4l2框架,主要包括v4l2-core、media framework,videobuf2等模块。
应用角度
从应用角度使用v4l2:
如果要进行视频数据采集,大致步骤如下:
1.打开设备文件/dev/videoX
2.根据打开的设备,查询设备能力集
3.设置视频数据的格式、参数等
4.分配buffer
5.开始视频流采集工作
6.将Buffer dequeue到v4l2框架
7.进程使用buffer块后,重新Buffer enqueue入队到v4l2框架
常见的硬件拓扑结构:
通常一个Camera的模组就是这样,包括镜头,感光元件,MIPI(CSI)接口,Soc的Mipi接口对应Camera模组传输视频数据,通过I2C/SPI传输控制数据配置Camera模组,Camera模组一般也会包含ISP(视频处理单元)对图像进行处理,有的Soc还会集成其他处理模块对camera的raw数据进行格式转换及缩放等图像处理。
数据结构
v4l2框架以v4l2_device和v4l2_subdev进行抽象,以v4l2_device代表整个输入设备,以v4l2_subdev代表子模块,比如CSI、Sensor等。
1.v4l2_device:对视频设备的整体进行抽象,可以看成是一个纽带,将各个子设备联系在一起,通常它会嵌入在其他结构体中以提供v4l2框架的功能;
struct v4l2_device {
struct device *dev; // 设备基类:指向设备结构的指针
struct media_device *mdev; // 媒体设备基类:指向结构media_device的指针可能为NULL。
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); // 释放在ref计数变为0时调用的函数
};
2.v4l2_subdev:对子设备进行抽象,结构体中包含的struct v4l2_subdev_ops是一个完备的操作函数集,用于对接各种不同的子设备,比如video,audio,sensor等,同时还有一个核心的函数集struct v4l2_subdev_core_ops,提供更通用的功能。子设备驱动根据设备特点实现该函数集的某些函数即可
struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
#endif
struct list_head list; // v4l2_subdev设备列表
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; // 子设备控制接口
char name[V4L2_SUBDEV_NAME_SIZE]; // 子设备名
u32 grp_id;
void *dev_priv;
void *host_priv;
struct video_device *devnode; // 子设备节点
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;
};
3.v4l2_subdev_ops:子设备的操作接口
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core; // 视频设备通用操作:初始化,加载FW,上电和Reset等
const struct v4l2_subdev_tuner_ops *tuner; // 用于广播相关设置的回调
const struct v4l2_subdev_audio_ops *audio; // 用于音频相关设置的回调
const struct v4l2_subdev_video_ops *video; // 视频设备的特有操作,设置帧率,裁剪图像,开关视频流等
const struct v4l2_subdev_vbi_ops *vbi; // 当v4l设备通过vbi设备节点以视频模式打开时使用的回调。
const struct v4l2_subdev_ir_ops *ir; // IR子设备的操作
const struct v4l2_subdev_sensor_ops *sensor; // v4l2子设备sensor子操作
const struct v4l2_subdev_pad_ops *pad;
};
4.video_device:用于向系统注册字符设备节点,以便用户空间可以进行交互,各类设置以及数据Buffer的获取等,在该结构体中能看到struct v4l2_ioctl_ops和struct vb2_queue结构体字段,这些与上文中的应用层代码相关。video_device可以内嵌在其他结构体中,以便提供与用户层交互的功能
流程分析
内部注册及调用流程如下:
1.在驱动的实现中,驱动结构体中内嵌struct video_device,同时实现struct v4l2_file_operations结构体中的函数,最终通过video_register_device向应用层提供注册
2.v4l2_register_device函数通过cdev_add向系统注册字符设备,并指定了file_operation,用户空间调用open/read/write/ioctl等接口,便可回调到驱动实现中;
3.v4l2_register_device函数中,通过device_register向系统注册设备,会在/sys文件系统下创建节点;
完成注册后,用户空间可通过文件描述符进行访问,从应用层看,大部分都是通过ioctl接口来完成,流程如下:
用户层的ioctl回调到__video_do_ioctl,该函数会对系统提供的strauct v4l2_ioctl_info v4l2_ioctls[]表进行查询,找到对应的项后进行调用;驱动做的工作就是填空题,实现对应的回调,在合适的时候被调用;
参考文章: