V4L2简介

article/2025/9/29 17:14:07

http://work-blog.readthedocs.org/en/latest/v4l2%20intro.html

第一章 V4L2简介

1.1、什么是v4l2

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

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在Linux系统中的结构图如下:

_images/V4L2框图.png

V4L2简单框图

1.2、从应用层看V4L2

从V4L2简单框图可以看出,V4L2是一个字符设备,而V4L2的大部分功能都是通过设备文件的ioctl导出的。

**可以将这些ioctl分类如下**:

  1. Query Capability:查询设备支持的功能,只有VIDIOC_QUERY_CAP一个。
  2. 优先级相关:包括VIDIOC_G_PRIORITY,VIDIOC_S_PRIORITY,设置优先级。
  3. capture相关:视频捕获相关Ioctl。
capture ioctl list
ID 描述
VIDIOC_ENUM_FMT 枚举设备所支持的所有数据格式
VIDIOC_S_FMT 设置数据格式
VIDIOC_G_FMT 获取数据格式
VIDIOC_TRY_FMT 与VIDIOC_S_FMT一样,但不会改变设备的状态
VIDIOC_REQBUFS 向设备请求视频缓冲区,即初始化视频缓冲区
VIDIOC_QUERYBUF 查询缓冲区的状态
VIDIOC_QBUF 从设备获取一帧视频数据
VIDIOC_DQBUF 将视频缓冲区归回给设备,
VIDIOC_OVERLAY 开始或者停止overlay
VIDIOC_G_FBUF 获取video overlay设备或OSD设备的framebuffer参数
VIDIOC_S_FBUF 设置framebuffer参数
VIDIOC_STREAMON 开始流I/O操作,capture or output device
VIDIOC_STREAMOFF 关闭流I/O操作
  1. TV视频标准:
TV Standard
ID 描述
VIDIOC_ENUMSTD 枚举设备支持的所有标准
VIDIOC_G_STD 获取当前正在使用的标准
VIDIOC_S_STD 设置视频标准
VIDIOC_QUERYSTD 有的设备支持自动侦测输入源的视频标准,此时使用此ioctl查询侦测到的视频标准
  1. input/output:
Input / Output
ID 描述
VIDIOC_ENUMINPUT 枚举所有input端口
VIDIOC_G_INPUT 获取当前正在使用的input端口
VIDIOC_S_INPUT 设置将要使用的input端口
VIDIOC_ENUMOUTPUT 枚举所有output端口
VIDIOC_G_OUTPUT 获取当前正在使用的output端口
VIDIOC_S_OUTPUT 设置将要使用的output端口
VIDIOC_ENUMAUDIO 枚举所有audio input端口
VIDIOC_G_AUDIO 获取当前正在使用的audio input端口
VIDIOC_S_AUDIO 设置将要使用的audio input端口
VIDIOC_ENUMAUDOUT 枚举所有audio output端口
VIDIOC_G_AUDOUT 获取当前正在使用的audio output端口
VIDIOC_S_AUDOUT 设置将要使用的audio output端口
  1. controls:设备特定的控制,例如设置对比度,亮度
controls
ID 描述
VIDIOC_QUERYCTRL 查询指定control的详细信息
VIDIOC_G_CTRL 获取指定control的值
VIDIOC_S_CTRL 设置指定control的值
VIDIOC_G_EXT_CTRLS 获取多个control的值
VIDIOC_S_EXT_CTRLS 设置多个control的值
VIDIOC_TRY_EXT_CTRLS 与VIDIOC_S_EXT_CTRLS相同,但是不改变设备状态
VIDIOC_QUERYMENU 查询menu
  1. 其他杂项:
controls
ID 描述
VIDIOC_G_MODULATOR  
VIDIOC_S_MODULATOR  
VIDIOC_G_CROP  
VIDIOC_S_CROP  
VIDIOC_G_SELECTION  
VIDIOC_S_SELECTION  
VIDIOC_CROPCAP  
VIDIOC_G_ENC_INDEX  
VIDIOC_ENCODER_CMD  
VIDIOC_TRY_ENCODER_CMD  
VIDIOC_DECODER_CMD  
VIDIOC_TRY_DECODER_CMD  
VIDIOC_G_PARM  
VIDIOC_S_PARM  
VIDIOC_G_TUNER  
VIDIOC_S_TUNER  
VIDIOC_G_FREQUENCY  
VIDIOC_S_FREQUENCY  
VIDIOC_G_SLICED_VBI_CAP  
VIDIOC_LOG_STATUS  
VIDIOC_DBG_G_CHIP_IDENT  
VIDIOC_S_HW_FREQ_SEEK  
VIDIOC_ENUM_FRAMESIZES  
VIDIOC_ENUM_FRAMEINTERVALS  
VIDIOC_ENUM_DV_PRESETS  
VIDIOC_S_DV_PRESET  
VIDIOC_G_DV_PRESET  
VIDIOC_QUERY_DV_PRESET  
VIDIOC_S_DV_TIMINGS  
VIDIOC_G_DV_TIMINGS  
VIDIOC_DQEVENT  
VIDIOC_SUBSCRIBE_EVENT  
VIDIOC_UNSUBSCRIBE_EVENT  
VIDIOC_CREATE_BUFS  
VIDIOC_PREPARE_BUF  

v4l2设备的基本操作流程如下:

  1. **打开设备**,例如 fd = open("/dev/video0",0)
  2. 查询设备能力. 例如:
struct capability cap;
ioctl(fd,VIDIOC_QUERYCAP,&cap)
  1. 设置优先级(可选).
  2. **配置设备**。包括:
  • 视频输入源的视频标准,VIDIOC_*_STD
  • 视频数据的格式 , VIDIOC_*_FMT
  • 视频输入端口, VIDIOC_*_INPUT
  • 视频输出端口,VIDIOC_*_OUTPUT
  1. **启动设备开始I/O操作**。V4L2支持一下三种I/O方式:

    • **Read/Write**:通过调用设备节点文件的Read/Write函数,与设备交互数据。打开设备后,默认使用的是此方法。
    • **Stream I/O**:流操作,只传递数据缓冲区指针,不拷贝数据。使用此方法,需要调用VIDIOC_REQBUFS ioctl来通知设备。流操作I/O有两种方式memory map和user buffer。(具体区别后面章节介绍)
    • overlay : 也可以理解为memory to memory 传输。将数据从内存拷贝到显存中。overlay设备独有的。

    对于Capture device可以以如下方式启动设备:

    • 调用VIDIOC_REQBUFS ioctl来分配缓冲区队列;
    • 调用VIDIOC_STREAMON ioctl通知设备开始stream IO
    • 调用VIDIOC_QBUF ioctl从设备获取一帧视频数据;
    • 使用完数据后,调用VIDIOC_DQBUF将缓冲区还给设备,以便设备填充下一帧数据。
  2. 释放资源并关闭设备。

1.3、从驱动层看V4L2

在驱动层,V4L2为驱动编写者做了很多工作。只需要实现硬件相关的代码,并且注册相关设备即可。

硬件相关代码的编写,除了编写具体硬件的控制代码外,最主要的就是将代码与V4L2框架绑定。绑定主要分为以下两个部分:

  • 关系绑定:也就是要将我们自己的结构体,与V4L2框架中相关连的结构体绑定在一起。
  • iocontrol等函数绑定:将V4L2所定义的空的函数指针,与自己的函数绑定在一起。

3.1 关系绑定

提到关系绑定,就必须介绍下V4L2几个重要结构体。

  • struct video_device:主要的任务就是负责向内核注册字符设备
  • struct v4l2_device:一个硬件设备可能包含多个子设备,比如一个电视卡除了有capture设备,可能还有VBI设备或者FM tunner。而v4l2_device就是所有这些设备的根节点,负责管理所有的子设备。
  • struct v4l2_subdev:子设备,负责实现具体的功能。

v4l2_device,v4l2_subdev可以看作所有设备和子设备的基类。我们在编写自己的驱动时,往往需要继承这些设备基类,添加一些自己的数据成员。例如第三章要讲到的soc_camera_host结构体,就是继承v4l2_device,并添加了互斥锁、子设备列表等成员变量。

_images/v4l2-intro.png

v4l2 framework 简略版

绑定的基本流程

  • 根据需要”重载”v4l2_device或v4l2_subdev结构体,添加需要的结构体成员。例如 :

    • linux/include/media/soc_camera.h文件中soc_camera_host重载了v4l2_device:

      struct soc_camera_host {
      struct v4l2_device v4l2_dev;
      struct list_head list;
      struct mutex host_lock;         /* Protect during probing */
      unsigned char nr;               /* Host number */
      void *priv;
      const char *drv_name;
      struct soc_camera_host_ops *ops;
      };
      
    • linux/drivers/media/video/Ml86v7667.c中ml86v7667_priv结构体”重载”了v4l2_subdev:

        struct ml86v7667_priv {
            struct v4l2_subdev                sd;
            struct v4l2_ctrl_handler  hdl;v4l2_std_id                       std;};
      
  • v4l2_device与V4L2框架的绑定:通过调用v4l2_device_register函数实现。例如,上面提到的soc_camera_host的绑定:

    int soc_camera_host_register(struct soc_camera_host *ici)
    {struct soc_camera_host *ix;int ret;if (!ici || !ici->ops ||!ici->ops->try_fmt ||!ici->ops->set_fmt ||!ici->ops->set_bus_param ||!ici->ops->querycap ||((!ici->ops->init_videobuf ||!ici->ops->reqbufs) &&!ici->ops->init_videobuf2) ||!ici->ops->add ||!ici->ops->remove ||!ici->ops->poll ||!ici->v4l2_dev.dev)return -EINVAL;if (!ici->ops->set_crop)ici->ops->set_crop = default_s_crop;if (!ici->ops->get_crop)ici->ops->get_crop = default_g_crop;if (!ici->ops->cropcap)ici->ops->cropcap = default_cropcap;if (!ici->ops->set_parm)ici->ops->set_parm = default_s_parm;if (!ici->ops->get_parm)ici->ops->get_parm = default_g_parm;if (!ici->ops->enum_fsizes)ici->ops->enum_fsizes = default_enum_fsizes;mutex_lock(&list_lock);list_for_each_entry(ix, &hosts, list) {if (ix->nr == ici->nr) {ret = -EBUSY;goto edevreg;}}      ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
          if (ret < 0)goto edevreg;list_add_tail(&ici->list, &hosts);mutex_unlock(&list_lock);mutex_init(&ici->host_lock);scan_add_host(ici);return 0;edevreg:mutex_unlock(&list_lock);return ret;}
    
  • v4l2_subdev与v4l2_device的绑定:通过v4l2_device_register_subdev函数,将subdev注册到根节点上。例如:

    static int soc_camera_platform_probe(struct platform_device *pdev)
    {struct soc_camera_host *ici;struct soc_camera_platform_priv *priv;struct soc_camera_platform_info *p = pdev->dev.platform_data;struct soc_camera_device *icd;int ret;if (!p)return -EINVAL;if (!p->icd) {dev_err(&pdev->dev,"Platform has not set soc_camera_device pointer!\n");return -EINVAL;}priv = kzalloc(sizeof(*priv), GFP_KERNEL);if (!priv)return -ENOMEM;icd = p->icd;/* soc-camera convention: control's drvdata points to the subdev */platform_set_drvdata(pdev, &priv->subdev);/* Set the control device reference */icd->control = &pdev->dev;ici = to_soc_camera_host(icd->parent);v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);v4l2_set_subdevdata(&priv->subdev, p);strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);if (ret)goto evdrs;return ret;evdrs:platform_set_drvdata(pdev, NULL);kfree(priv);return ret;
    }
    
  • video_device与v4l2_device的绑定:将v4l2_device的地址赋值给video_device的v4l2_dev即可。

此步不一定必要。只要有办法通过文件节点file(struct file)找到v4l2_device即可。

3.2 函数绑定

在v4l2 framework 简略版图中,绿色的方框都是需要我们绑定并实现的。

其中v4l2_file_operations和v4l2_ioctl_ops是必须实现的。而v4l2_subdev_ops下的八类ops中,v4l2_subdev_core_ops是必须实现的,其余需要根据设备类型选择实现的。比如video capture类设备需要实现v4l2_subdev_core_ops, v4l2_subdev_video_ops。

  • v4l2_file_operations:实现文件类操作,比如open,close,read,write,mmap等。但是ioctl是不需要实现的,一般都是用video_ioctl2代替。例如linux/drivers/media/video/soc_camera.c文件中soc_camera_fops的实现:
static struct v4l2_file_operations soc_camera_fops = {.owner          = THIS_MODULE,.open           = soc_camera_open,.release        = soc_camera_close,
    .unlocked_ioctl = video_ioctl2,
    .read           = soc_camera_read,.mmap           = soc_camera_mmap,.poll           = soc_camera_poll,
};
  • v4l2_ioctl_ops:V4L2导出给应用层使用的所有ioctl都是在这个地方实现的。但不必全部实现,只实现自己相关的ioctl即可。例如linux/drivers/media/video/soc_camera.c中soc_camera_ioctl_ops的实现:
static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {.vidioc_querycap         = soc_camera_querycap,.vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,.vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,.vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,.vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,.vidioc_enum_input       = soc_camera_enum_input,.vidioc_g_input          = soc_camera_g_input,.vidioc_s_input          = soc_camera_s_input,.vidioc_s_std            = soc_camera_s_std,.vidioc_g_std            = soc_camera_g_std,.vidioc_enum_framesizes  = soc_camera_enum_fsizes,.vidioc_reqbufs          = soc_camera_reqbufs,.vidioc_querybuf         = soc_camera_querybuf,.vidioc_qbuf             = soc_camera_qbuf,.vidioc_dqbuf            = soc_camera_dqbuf,.vidioc_create_bufs      = soc_camera_create_bufs,.vidioc_prepare_buf      = soc_camera_prepare_buf,.vidioc_streamon         = soc_camera_streamon,.vidioc_streamoff        = soc_camera_streamoff,.vidioc_cropcap          = soc_camera_cropcap,.vidioc_g_crop           = soc_camera_g_crop,.vidioc_s_crop           = soc_camera_s_crop,.vidioc_g_parm           = soc_camera_g_parm,.vidioc_s_parm           = soc_camera_s_parm,.vidioc_g_chip_ident     = soc_camera_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG.vidioc_g_register       = soc_camera_g_register,.vidioc_s_register       = soc_camera_s_register,
#endif
};
  • v4l2_subdev_ops:v4l2_subdev有可能需要实现的ops的总合。分为8类,core,audio,video,vbi,tuner......等。例如,

    linuxdriversmediavideosoc_camera_platform.c中platform_subdev_ops的实现

static struct v4l2_subdev_video_ops platform_subdev_video_ops = {.s_stream       = soc_camera_platform_s_stream,.enum_mbus_fmt  = soc_camera_platform_enum_fmt,.cropcap        = soc_camera_platform_cropcap,.g_crop         = soc_camera_platform_g_crop,.try_mbus_fmt   = soc_camera_platform_fill_fmt,.g_mbus_fmt     = soc_camera_platform_fill_fmt,.s_mbus_fmt     = soc_camera_platform_fill_fmt,.g_mbus_config  = soc_camera_platform_g_mbus_config,
};static struct v4l2_subdev_ops platform_subdev_ops = {.core   = &platform_subdev_core_ops,.video  = &platform_subdev_video_ops,
};

函数绑定只是将驱动所实现的函数赋值给相关的变量即可。


http://chatgpt.dhexx.cn/article/dOuM8n6y.shtml

相关文章

我们一起学linux之V4L2摄像头应用流程

一、概述 Video for Linuxtwo(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频…

深入学习Linux摄像头(一)v4l2应用编程

深入学习Linux摄像头&#xff08;一&#xff09;v4l2应用编程 一、什么是v4l2 二、v4l2 API介绍 2.1 Querying Capabilities 2.2 Application Priority 2.3 Device Inputs and Outputs 2.4 Video Standards 2.5 Camera Control Reference 2.6 Image Format 2.7 Cropping, compo…

V4L2驱动框架详解

1. V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 2 2. video_device 2.1图像处理模块 2.2 video注册流程 3. videobuf2 3.1 与video device的关系: 3.2 buffer类型 3.3 vb2_ops回调函数 3.4 mmap 流程 4. Subdev 4.1 概念 4.2 subdev注册流程 5. media fra…

html背景自动适应,css背景图片如何自适应?

css可以使用background-size属性设置背景图片自适应&#xff0c;为背景图片设置background-size:cover;样式即可使背景图片自适应。 css可以使用background-size属性设置背景图片自适应。background-size属性规定背景图像的尺寸。 background-size属性&#xff1a; 语法&#x…

css实现图片自适应容器的几种方式

css实现图片自适应容器 经常有这样一个场景,需要让图片自适应容器的大小。 1、img标签的方式 我们马上就能想到,把width、height 设置为100%啊。来看一哈效果。 <div class=div1><img src="./peiqi.png" alt=""> </div>.div1 {widt…

css 背景图片自适应属性整理

本篇博客主要记录一些使用 css 对背景图片自适应的操作整合 背景图片取消重复 background-image: url(image.jpg); background-repeat:no-repeat;修改前 修改后 背景图片固定&#xff08;不会随着内容滚动而改变位置&#xff09; background-image: url(image.jpg); back…

CSS实现图片自适应布局

CSS实现图片自适应布局 最轻松的写法 <div class"container">// 这里图片尺寸为440X440像素&#xff0c;<img src"./images/medium.jpg" alt"test"> </div><style>.container {width: 600px;height: 600px;border: 1p…

CSS图片自适应框架

CSS图片自适应框架 使用img来设置 <!DOCTYPE html> <html><style>body{margin: 0;padding: 10px;}#a_1{display: block;width: 100px;height: 50px;overflow: hidden;padding: 10px;border: dashed;margin: 10px;}#a_1img{width: 100%;height: 100%;}</s…

css之实现图片自适应

文章目录 一、position实现图片自适应二、padding补偿法三、图片以正方形显示 原图如下&#xff1a; 在开发中&#xff0c;想在固定的高度和宽度中显示不一样的图片时&#xff0c;就会出现压缩&#xff0c;导致图片最后显示不好看&#xff0c;以下提供几种方法进行调整&#xf…

css实现图片自适应缩放的两种方法

想要实现图片根据给定宽高来自适应缩放的时候&#xff0c;会想到什么属性&#xff1f;是object-fit: cover;吗&#xff1f; 来试一试&#xff01;为了展示效果&#xff0c;我特地选了对称的图片。下面两个img元素都有一个div包裹&#xff0c;div设置固定宽高&#xff0c;第一个…

3种CSS实现背景图片全屏铺满自适应的方式

来源 | https://www.fly63.com/ 一张清晰漂亮的背景图片能给网页加分不少&#xff0c;设计师也经常会给页面的背景使用大图&#xff0c;我们既不想图片因为不同分辨率图片变形&#xff0c;也不希望当在大屏的情况下&#xff0c;背景有一块露白&#xff0c;简而言之&#xff0c;…

css怎么设置背景图片自适应大小

在css中&#xff0c;可以利用“background-size”属性设置背景图片自适应大小&#xff0c;该属性用于设置背景图片的大小&#xff0c;只需要给背景图片元素添加“background-size:cover;”样式&#xff0c;即可使背景图片的大小自适应。 本教程操作环境&#xff1a;windows10系…

CSS——图片自适应宽高

宽度拉伸&#xff1a;把width设置成100%&#xff0c;height设置auto 高度拉伸&#xff1a;把height设置成100%&#xff0c;width设置auto ​ <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv&…

使用CSS进行图片自适应的两个方法

1.object-fit 属性 设置好图片的宽高&#xff0c;然后设置object-fit属性为contain就是常见的图片自适应效果。 img {width: 400px;height: 400px;object-fit: contain;} object-fit: fill|contain|cover|scale-down|none|initial|inherit; 2.background 我们把图片作为背景…

css实现一个图片自适应,图片不会变形。

第一种&#xff0c;图片填充满容器 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" conten…

常见分布的概率分布及期望方差

文章转载&#xff1a;https://www.jianshu.com/p/c05bafb52877f 参考链接&#xff1a;https://blog.csdn.net/sodacoco/article/details/89041910

卡方分布(Chi-Square Distribution):

定义&#xff1a;如果我们的随机变量是标准正态分布&#xff08;详见以前博客的高斯分布&#xff09;&#xff0c;那么多个随机变量的平方和服从的分布即为卡方分布。 XY12Y22⋯Yn2 其中&#xff0c;Y1,Y2,⋯,Yn均为服从标准正态分布的随机变量&#xff0c;那么XX服从卡方分布&…

自由度为n的卡方分布χ²(n)的期望等于n、方差等于2n的证明

自由度为n的卡方分布χ&#xff08;n&#xff09;的期望等于n、方差等于2n的证明 出自&#xff1a;http://blog.sina.com.cn/s/blog_4cb6ee6c0102xh17.html posted on 2019-06-09 20:31 蔡军帅_ACM 阅读( ...) 评论( ...) 编辑 收藏

常见分布的期望与方差

离散型随机变量的期望&#xff1a;连续型随机变量的期望&#xff1a;方差公式&#xff1a;DX E(X) - (EX)

线性回归和卡方分布与方差分析

**1、线性回归 例子 import numpy as np import pylab def compute_error(b,m,data):totalError 0#Two ways to implement this#first way# for i in range(0,len(data)):# x data[i,0]# y data[i,1]## totalError (y-(m*xb))**2#second wayx data[:,0]y da…