【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:

img

如果要进行视频数据采集,大致步骤如下:

1.打开设备文件/dev/videoX
2.根据打开的设备,查询设备能力集
3.设置视频数据的格式、参数等
4.分配buffer
5.开始视频流采集工作
6.将Buffer dequeue到v4l2框架
7.进程使用buffer块后,重新Buffer enqueue入队到v4l2框架

常见的硬件拓扑结构:

img

通常一个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等。

img

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可以内嵌在其他结构体中,以便提供与用户层交互的功能

流程分析

内部注册及调用流程如下:

img

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接口来完成,流程如下:

img

用户层的ioctl回调到__video_do_ioctl,该函数会对系统提供的strauct v4l2_ioctl_info v4l2_ioctls[]表进行查询,找到对应的项后进行调用;驱动做的工作就是填空题,实现对应的回调,在合适的时候被调用;













参考文章:

https://www.cnblogs.com/LoyenWang/p/15456230.html

posted @ 2023-04-01 20:24  Emma1111  阅读(2508)  评论(0编辑  收藏  举报