【V4L2】V4L2框架浅析

概述

V4L2(Video4Linux的缩写)是Linux下关于视频采集相关设备的驱动框架,为驱动和应用程序提供了一套统一的接口规范。

V4L2支持的设备十分广泛,但是其中只有很少一部分在本质上是真正的视频设备。

按照v4l2的标准,它会将一个数据流设备抽象成一个videoX节点,从属主设备都对应着各自的v4l2_subdev实现,并且按照media controller进行统一管理。

V4L2框架下面就是相机驱动部分,驱动部分控制着上下电逻辑以及寄存器读取时序并按照I2C协议与硬件进行通信,和根据MIPI CSI协议传递数据,从而达到控制各个硬件设备,并且获取图像数据的目的

V4L2的主要数据结构

v4l2_device:嵌入到video_device中,表示一个v4l2设备的实例,内部通过一个链表管理着整个从属的所有子设备。(/include/media)

struct v4l2_device {
    struct device *dev;
#if defined(CONFIG_MEDIA_CONTROLLER)
    struct media_device *mdev;
#endif
    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_subdev:依附在v4l2_device之下,表示一个v4l2设备的子设备,一个v4l2_device下可以有多个sub_device

subdevice的设计目的是为了多路复用,用一个v4l2_device可以服务多个v4l2_subdev,而有些驱动没有v4l2_subdev,只有video_device。

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;  // subdev的操作方法
    const struct v4l2_subdev_internal_ops *internal_ops;
    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;

    struct v4l2_subdev_state *state;
};

video_device:是一个字符设备,video_device包含一个cdev,v4l2_device是一个v4l2实例,嵌入到video_device中,v4l2维护着一张链表管理v4l2_subdevice,v4l2_subdev表示摄像头的I2C控制模块

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;   // v4l2设备操作接口
    u32 device_caps;
    /* sysfs */
    struct device dev;
    struct cdev *cdev;                  // 字符设备
    struct v4l2_device *v4l2_dev;       // v4l2设备
    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;  // v4l2d的ioctl操作集
    DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

    struct mutex *lock;
};

v4l2驱动框架:

【V4L2】V4L2框架浅析

v4l2主要注册接口

  • v4l2_device
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
  • v4l2_subdev
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,struct v4l2_subdev *sd);
void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
  • video_device
int video_register(struct video_device *vdev,int type,int nr);
void video_unregister_device(struct video_device *vdev);

V4L2执行流程

【V4L2】V4L2框架浅析

1.打开video设备

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

int vfd;

if((vfd = open("/dev/video0",O_RDWR)) < 0)
{
    perror("DEVICE open error");
    return -1;
}

2.查看并设置设备

在打开设备获取其文件句柄后,就需要查询设备的属性,主要通过ioctl传入VIDIOC_QUERYCAP参数来完成,设备属性通过v4l2_capability结构体来表达,另外还可以通过传入VIDIOC_ENUM_FMT来枚举支持的数据格式,通过传入VIDIOC_G_FMT/VIDIOC_S_FMT来分别获取和设置数据格式,通过传入VIDIOC_G_PARM/VIDIOC_S_PARM来分别获取和设置参数。

// init device
struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width  = 1280;   // 设置相机宽度
fmt.fmt.pix.height = 720;    // 设置相机高度
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;   // 像素格式:YUYV
fmt.fmt.pix.field  = V4L2_FIELD_NONE;

if(-1 == xioctl(vfd , VIDIOC_S_FMT, &fmt)) {
    perror("VIDIOC_S_FMT");
    return errno;
}

3.向驱动申请帧缓冲区

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

struct v4l2_requestbuffers *request = &config->request;
request->type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
request->memory = V4L2_MEMORY_MMAP;
request->count  = 4;

if(-1 == ioctl(config->vfd, VIDIOC_REQBUFS, request)){
    perror("VIDIOC_REQBUFS");
    return errno;
}

if(request->count < 2) {
    fprintf(stderr, "Not enough buffer memory.\n");
    return EXIT_FAILURE;
}

4.将帧缓冲区入队

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

struct v4l2_buffer buffer;

// config->buffer_count为4,VIDIOC_REQBUFS时已指定count
for(i = 0; i < config->buffer_count; i++) {
    memset(&buffer, 0, sizeof(buffer));
    buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buffer.memory = V4L2_MEMORY_MMAP;
    buffer.index  = i;

    if(-1 == xioctl(config->vfd, VIDIOC_QBUF, &buffer)) {
        perror("VIDIOC_QBUF");
        return errno;
    }
}

5.开启数据流

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

enum v4l2_buf_type type;

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == xioctl(config->vfd, VIDIOC_STREAMON, &type)) {
    perror("VIDIOC_STREAMON");
    return errno;
}

6.将帧缓冲区出队

一旦数据流开始进行流转,就可以通过调用ioctk下发VIDIOC_DQBUF命令来获取帧缓冲区,并且将缓冲区的图像数据取出,进行预览、拍照或者录像的处理。(此处一般配合epoll/poll/select使用)

struct v4l2_buffer buffer;
{
    memset(&buffer, 0, sizeof(buffer));
    buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buffer.memory = V4L2_MEMORY_MMAP;

    if(-1 == xioctl(config->vfd, VIDIOC_DQBUF, &buffer)) {
        switch(errno) {
        case EAGAIN:
            return 0;
        case EIO:
        default:
            perror("VIDIOC_DQBUF");
            return errno;
        }
    }
    // swap buffer pointer.
    v4l2_capture_t *capture = (v4l2_capture_t *)config->refs;
    capture->data = config->buffers[buffer.index].data;
    capture->len  = config->buffers[buffer.index].len;
}

7.将缓冲重新入列

通过调用ioctk下发VIDIOC_QBUF命令将缓冲区重新入列,这样可以实现循环采集。

// &#x5C06;&#x5DF2;&#x7ECF;&#x53D6;&#x5B8C;&#x7684;&#x5E27;&#x7F13;&#x51B2;&#x533A;&#xFF0C;&#x6E05;&#x96F6;
memset(&buffer, 0, sizeof(buffer));

// &#x91CD;&#x65B0;&#x538B;&#x5165;v4l2&#x6846;&#x67B6;&#x7684;&#x961F;&#x5217;&#x4E2D;
if(-1 == xioctl(config->vfd, VIDIOC_QBUF, &buffer)){
     perror("VIDIOC_QBUF");
     return errno;
}

8.停止视频的采集

enum v4l2_buf_type type;

if(-1 == xioctl(config->vfd, VIDIOC_STREAMOFF, &type)) {
    perror("VIDIOC_STREAMOFF");
    return errno;
}

9.关闭视频设备

close(vfd);

调用流程:

【V4L2】V4L2框架浅析

V4L2常用API及命令标识符

1.常用数据结构体在内核目录include/linux/videodev2.h中定义

struct v4l2_requestbuffers reqbufs;   // &#x5411;&#x9A71;&#x52A8;&#x7533;&#x8BF7;&#x5E27;&#x7F13;&#x51B2;&#x7684;&#x60C5;&#x6C42;&#xFF0C;&#x91CC;&#x9762;&#x5305;&#x542B;&#x7533;&#x8BF7;&#x7684;&#x4E2A;&#x6570;
struct v4l2_capability cap;       // &#x8BBE;&#x5907;&#x7684;&#x529F;&#x80FD;&#xFF0C;&#x6BD4;&#x5982;&#x662F;&#x5426;&#x662F;&#x89C6;&#x9891;&#x8F93;&#x5165;&#x8BBE;&#x5907;
struct v4l2_input input;          // &#x89C6;&#x9891;&#x8F93;&#x5165;
struct v4l2_standard std;         // &#x89C6;&#x9891;&#x7684;&#x5236;&#x5F0F;&#xFF0C;&#x6BD4;&#x5982;PAL&#xFF0C;NTSC&#x5236;
struct v4l2_fromat fmt;           // &#x5E27;&#x7684;&#x683C;&#x5F0F;&#xFF0C;&#x6BD4;&#x5982;&#x5BBD;&#x5EA6;&#xFF0C;&#x9AD8;&#x5EA6;&#x7B49;
struct v4l2_buffer buf;           // &#x4EE3;&#x8868;&#x9A71;&#x52A8;&#x4E2D;&#x7684;&#x5E27;&#x7F13;&#x5B58;

2.常用ioctl接口命令include/linux/videodev2.h定义

VIDIOC_REQBUFS&#xFF1A;&#x8BF7;&#x6C42;&#x7F13;&#x5B58;&#x7684;&#x6570;&#x91CF;&#xFF0C;&#x9A71;&#x52A8;&#x4F1A;&#x636E;&#x6B64;&#x7533;&#x8BF7;&#x5BF9;&#x5E94;&#x6570;&#x91CF;&#x7684;&#x89C6;&#x9891;&#x7F13;&#x5B58;&#x3002;
VIDIOC_QUERYBUF&#xFF1A;&#x628A;VIDIOC_REQBUFS&#x4E2D;&#x5206;&#x914D;&#x7684;&#x6570;&#x636E;&#x7F13;&#x5B58;&#x8F6C;&#x6362;&#x6210;&#x7269;&#x7406;&#x5730;&#x5740;
VIDIOC_QUERYCAP&#xFF1A;&#x901A;&#x8FC7; struct v4l2_buffer &#x7ED3;&#x6784;&#x4F53;&#x7684; index&#xFF0C;&#x8BBF;&#x95EE;&#x5BF9;&#x5E94;&#x5E8F;&#x53F7;&#x7684; buffer&#xFF0C;&#x83B7;&#x53D6;&#x5230;&#x5BF9;&#x5E94; buffer &#x7684;&#x7F13;&#x5B58;&#x4FE1;&#x606F;&#x3002;
VIDIOC_ENUM_FMT&#xFF1A;&#x83B7;&#x53D6;&#x5F53;&#x524D;&#x9A71;&#x52A8;&#x652F;&#x6301;&#x7684;&#x89C6;&#x9891;&#x683C;&#x5F0F;
VIDIOC_S_FMT&#xFF1A;&#x8BBE;&#x7F6E;&#x5F53;&#x524D;&#x9A71;&#x52A8;&#x7684;&#x89C6;&#x9891;&#x6355;&#x83B7;&#x683C;&#x5F0F;&#xFF0C;&#x8BBE;&#x7F6E;&#x4E4B;&#x524D;&#x6700;&#x597D;&#x8C03;&#x7528;VIDIOC_TRY_FMT&#x5224;&#x65AD;&#x662F;&#x5426;&#x652F;&#x6301;
VIDIOC_G_FMT&#xFF1A;&#x8BFB;&#x53D6;&#x5F53;&#x524D;&#x9A71;&#x52A8;&#x7684;&#x89C6;&#x9891;&#x6355;&#x83B7;&#x683C;&#x5F0F;&#xFF0C;&#x6BD4;&#x5982;&#xFF1A;width,height,sizeimage&#x7B49;&#x53C2;&#x6570;
VIDIOC_TRY_FMT&#xFF1A;&#x9A8C;&#x8BC1;&#x5F53;&#x524D;&#x9A71;&#x52A8;&#x7684;&#x683C;&#x5F0F;&#x662F;&#x5426;&#x88AB;&#x9A71;&#x52A8;&#x652F;&#x6301;&#xFF0C;&#x4E0D;&#x4F1A;&#x6539;&#x53D8;&#x4EFB;&#x4F55;&#x786C;&#x4EF6;&#x8BBE;&#x7F6E;&#x786C;&#x4EF6;&#x8BBE;&#x7F6E;
VIDIOC_CROPCAP&#xFF1A;&#x67E5;&#x8BE2;&#x9A71;&#x52A8;&#x7684;&#x4FEE;&#x526A;&#x80FD;&#x529B;
VIDIOC_S_CROP&#xFF1A;&#x8BBE;&#x7F6E;&#x89C6;&#x9891;&#x4FE1;&#x53F7;&#x7684;&#x8FB9;&#x6846;
VIDIOC_G_CROP&#xFF1A;&#x8BFB;&#x53D6;&#x89C6;&#x9891;&#x4FE1;&#x53F7;&#x7684;&#x8FB9;&#x6846;
VIDIOC_QBUF&#xFF1A;&#x5C06;User&#x7A7A;&#x95F4;&#x5DF2;&#x7ECF;&#x5904;&#x7406;&#x8FC7;&#x7684;buffer&#xFF0C;&#x91CD;&#x65B0;&#x5165;&#x961F;&#xFF0C;&#x79FB;&#x4EA4;&#x7ED9; driver&#xFF0C;&#x7B49;&#x5F85;&#x586B;&#x5145;&#x6570;&#x636E;&#x3002;
VIDIOC_DQBUF&#xFF1A;&#x5C06;driver&#x5DF2;&#x7ECF;&#x586B;&#x5145;&#x597D;&#x6570;&#x636E;&#x7684; buffer &#x51FA;&#x5217;&#xFF0C;&#x4F9B;&#x5E94;&#x7528;&#x4F7F;&#x7528;&#x3002;
VIDIOC_STREAMON&#xFF1A;&#x5F00;&#x59CB;&#x89C6;&#x9891;&#x663E;&#x793A;&#x51FD;&#x6570;
VIDIOC_STREAMOFF&#xFF1A;&#x7ED3;&#x675F;&#x89C6;&#x9891;&#x663E;&#x793A;&#x51FD;&#x6570;
VIDIOC_QUERYSTD&#xFF1A;&#x68C0;&#x67E5;&#x5F53;&#x524D;&#x89C6;&#x9891;&#x8BBE;&#x5907;&#x652F;&#x6301;&#x7684;&#x6807;&#x51C6;&#xFF0C;&#x4F8B;&#x5982;PAL&#x6216;NTSC&#x3002;
VIDIOC_OVERLAY&#xFF1A;&#x8BBE;&#x7F6E;&#x4F7F;&#x80FD;osd&#x53C2;&#x6570;&#xFF0C;&#x4F20;&#x5165;1&#x8868;&#x793A;&#x4F7F;&#x80FD;&#xFF0C;0&#x8868;&#x793A;&#x5173;&#x95ED;

总结

在linux端开发视频设备大部分使用场景是通过ioctl发送命令符到V4L2框架中,实现获取设备配置信息,设置设备数据格式,拉取设备视频流,获取视频帧进行处理等过程。

此篇仅为v4l2框架简介,还有event,mem2mem,videobuf2等部分,详情请看Linux源码,V4L2部分完全开源。

参考文章:

https://blog.csdn.net/weixin_42462202/article/details/99680969
https://www.cnblogs.com/unreal/articles/1820295.html
https://blog.csdn.net/thisway_diy/article/details/128459836

Original: https://www.cnblogs.com/Wangzx000/p/16652601.html
Author: _Wangzx
Title: 【V4L2】V4L2框架浅析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607525/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 总结

    门诊:11张 张张是主表,主表之王是患者信息住院:12张 张张由入院登记开始,外挂处方点评一张电子病历:12张 病历概要打头来,紧接门(急)病历,急诊留观放尾中,住院病历放最后检验…

    Linux 2023年6月13日
    097
  • 缓冲区溢出二:从缓冲区溢出到获取反弹shell实例

    一、说明 之前写过一篇”缓冲区溢出一:函数调用过程中的堆栈变化及缓冲区溢出利用原理“,道理讲得还可以,但现在看还是需要一个示例来讲解从攻击角度如何实现返回地…

    Linux 2023年5月28日
    0106
  • 国产银河麒麟Kylin V10操作系统-如何把常用文件夹加入左侧侧边栏(类似windows文件资源管理器中的收藏夹)

    国产银河麒麟Kylin V10操作系统-如何把常用文件夹加入左侧侧边栏(类似windows文件资源管理器中的收藏夹) 第一步:确保侧边栏正确显示。 打开”我的电脑&#8…

    Linux 2023年6月14日
    0149
  • 七种方案!探讨Redis分布式锁的正确使用姿势

    日常开发中,秒杀下单、抢红包等等业务场景,都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的…

    Linux 2023年5月28日
    084
  • Linux 服务器巡检脚本

    bash;gutter:true;</p> <h1>!/bin/bash</h1> <p>cat < $RESULTFILE …

    Linux 2023年6月13日
    091
  • OrchardCore Headless建站拾遗

    书接上回,OrchardCore的基本设置写了,但是有一说一,这个东西还是挺复杂的,如果需要构建一个简单的企业网站,还需要干点别的活。 本文考虑在尽量少编程的基础上,完成一个Hea…

    Linux 2023年6月6日
    091
  • Docker镜像构建之docker commit

    我们可以通过公共仓库拉取形象,但有时公共仓库拉取的形象不符合我们的需求。虽然我们已经从繁琐的部署工作中解脱出来,但在实际开发中,我们可能希望镜像包含整个项目的完整环境,将打包的完整…

    Linux 2023年5月27日
    087
  • mac使用brew安装postgresql

    在您的命令行中运行以下命令: brew doctorbrew update安装在您的命令行中运行以下命令:brew install postgres 你也可以指定版本: brew …

    Linux 2023年6月14日
    0117
  • MySQL Performance Schema详解

    MySQL的performance schema 用于监控MySQL server在一个较低级别的运行过程中的资源消耗、资源等待等情况。 1 performance schema特…

    Linux 2023年6月13日
    093
  • Redis 持久化

    Redis 提供了一系列不同的持久性选项: RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。 AOF(Append Only File):AOF…

    Linux 2023年5月28日
    075
  • Mysql 安全加固经验总结

    本文为博主原创,转载请注明出处: 1.内网部署Mysql mysql 数据库在使用过程中,需要给服务提供连接和访问的权限,而不需要进行公网连接和访问,所以在安全环境和现网环境部署m…

    Linux 2023年6月14日
    087
  • Linux Ubuntu 下载&安装 MySQL

    1. 下载安装 下载&安装:一句搞定 sudo apt update sudo apt install mysql-server 查看版本信息 mysql –versio…

    Linux 2023年6月14日
    095
  • SQLI-LABS(Less-7)

    Less-7(GET-Dump into outfile-String) 打开 Less-7页面,可以看到页面中间有一句 Please input the ID as parame…

    Linux 2023年6月6日
    072
  • How to Operate SharePoint User Alerts with PowerShell

    When you migrate list or site, the user alerts in the site will not be migrated together w…

    Linux 2023年5月28日
    0136
  • 【Docker搭建】1. 在Docker中安装MySQL5.7

    docker pull mysql:5.7 将全部的配置文件和关联的文件夹统一放到 /opt/docker/mysql 中 创建 MySQL 配置文件文件夹 mkdir -p /o…

    Linux 2023年6月13日
    078
  • spring boot @Validated注解不生效[转]

    问题: 接口上添加了@Validated注解,但是bindingResult.hasErrors()的结果却是false,即一直不能返回验证信息,@Validated不生效,排查了…

    Linux 2023年6月8日
    094
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球