V4L2应用层分析

本文详细介绍了Linux下的Video for Linux 2(V4L2)驱动,它是现代视频设备的主要接口。V4L2是一个内核驱动,通过ioctl接口与应用层交互,用于管理底层硬件。应用层通过特定的ioctl命令和参数来操作设备,例如查询设备支持的功能、设置图像格式等。以USB摄像头为例,展示了V4L2应用层的程序流程,包括打开设备、查询驱动支持、设置图像格式、申请缓冲区、映射缓冲区、开始捕获、从输出队列读取数据等步骤。V4L2不包含视频编码功能,若需输出视频文件,需借助如ffmpeg等编码库。
摘要由CSDN通过智能技术生成

V4L2应用层分析

一、总述

V4L2,即Video for Linux 2,是第二代为Linux打造的音频、视频驱动。相比第一代V4L,V4L2支持更多的设备,同时更加稳定。现今的视频设备,如USB摄像头,基本都支持V4L2,故现今无需再学习第一代的V4L。

首先要说明的是,V4L2是一个驱动,而不是链接库,故不要想着找什么动态、静态链接库文件(.so文件和.a文件)。驱动是存在于内核层的代码,用于管理底层硬件。应用层无法直接使用驱动中的变量和函数,只能通过一系列标准调用接口来使用驱动(实现原理为系统调用syscall),常用的接口函数有open、write、read、ioctl、close,他们的声明原型如下:

// 应用层接口
// fcntl.h
extern int open (const char *__file, int __oflag, ...) __nonnull ((1));

// unistd.h
extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;
extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;
extern int close (int __fd);

// sys/ioctl.h
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

由于 V4L2中只使用了ioctl这一个控制接口函数,故这里只介绍ioctl函数:

参数:
	int __fd:文件描述符,是open函数的返回值。
	unsigned long int __request:可以为简单的012等数字,也可以为规范的编码值,用于告知驱动控制命令的类型。
	...:三个点表示编译器在编译时不要检查此参数的类型。一般为整型变量或指针。
	
返回值:当ioctl操作成功时返回 0,否则返回负数。

对于一个复杂的驱动来说,应用层的write和read函数由于传入参数单一,已经无法满足复杂驱动的需要。在此种情况下,ioctl凭借他传入参数的多样性成为最常用的接口函数,其传入参数的多样性体现如下:

1.ioctl的第二个参数unsigned long int __request,通常代表传入的控制命令。不同值的__request,代表着不同含义的控制命令,例如:V4L2中VIDIOC_QUERYCAP代表查询设备的驱动支持情况,VIDIOC_S_FMT代表设置设备属性。

__request可以由驱动开发者自定义使用规则,然后应用层开发者按照此自定义规则传入__request(如简单的0代表读取属性,1代表写入属性)。

驱动开发者最好遵从Linux内核的规范,按照内核提供好的格式对命令进行编码,详见头文件asm-generic/ioctl.h,此头文件在内核层和应用层都有提供,可随时查阅。对于应用层开发者来说,只需要按照驱动开发者提供的头文件中的说明,使用相应的命令即可。

2.ioctl的第三个参数 ...,通常代表需要传入的控制数据。...意味着编译器不会检查此处变量类型,这就允许第三个参数可以为不同类型的变量,实现c++重载的效果,再结合它对应的内核层的声明(见下段代码),第三个参数可以是简单的整型数值,也可以是指针(地址),当传入的是指针时,驱动层函数得到的即为传入参数的地址。此参数也会参与到第二个参数__request的编码中。

long (*unlocked_ioctl) (struct file *fl, unsigned int cmd, unsigned long arg);

综上所述,V4L2通过ioctl,通过传入不同的控制命令__request和不同的控制数据(V4L2中为结构体的地址或指向结构体的指针),来实现对底层硬件的操作。这些控制命令及相应注释见头文件linux/videodev2.h

二、例子

V4L2应用层的程序流程如下(以USB摄像头为例):

1.打开USB摄像头的设备节点:int cameraFd = open("/dev/video*", O_RDWR | O_NONBLOCK),*的取值根据自己USB摄像头实际的节点号确定,打开方式中,阻塞和非阻塞根据自己需要确定。

2.查询当前USB摄像头的驱动支持情况。由于摄像头驱动开发者可能并没有按照V4L2驱动标准实现全部的驱动功能,而只实现了部分功能,所以需要查询当前摄像头的驱动支持情况,以确定后续通过怎样的方式驱动摄像头。

struct v4l2_capability cap;
ioctl(cameraFd, VIDIOC_QUERYCAP, &cap);

执行完上述ioctl函数后,驱动会根据功能的实现情况填充cap结构体变量的成员。注意,这是一个一般规律,即执行完某个ioctl函数后,驱动会为传入的结构体的成员赋值。

接下来,我们可以把所有成员的值都打印出来,以查看当前摄像头的驱动支持情况。

printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nKernel Version:%u.%u.%u\nCapability is %#x\nDevice capability is %#x\n",
            cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF,cap.capabilities,
             cap.device_caps);

v4l2_capability 结构体定义和成员变量意义说明见头文件linux/videodev2.h,如下图所示:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值