基于V4L2框架的摄像头从上层到底层开发

news2025/1/31 2:54:46

文章目录

      • 一、V4L2应用开发
        • 1、识别摄像头
        • 2、查看摄像头设备的能力
        • 3、查看支持视频格式
        • 4、设置视频格式
        • 5、申请帧缓冲
        • 6、启动采集
        • 7、出队取一帧图像
        • 8、入队归还帧缓冲
        • 9、停止视频采集
        • 10、退出释放资源
      • 二、V4L2框架源码分析
        • 1、struct video_device
        • 2、struct v4l2_device *v4l2_dev
        • 3、struct v4l2_subdev
        • 4、V4L2框架
      • 三、基于NVIDIA平台的CSI摄像头驱动源码分析
        • 1、tegracam_device平台设备
        • 2、定义imx219摄像头设备类
        • 3、设备树匹配检测
        • 4、探测函数probe初始化
        • 5、基于英伟达平台摄像头的设备框架图

一、V4L2应用开发

1、识别摄像头

(1)开发过程中的第一步要先确定硬件是否正常工作,方便进行后续开发。
插入USB摄像头,查看摄像头是否生成,注意会生成2个设备,其中一个可以捕获图像。至于为什么会有2个,自行百度了解。

ls -l /dev/video*

请添加图片描述
(2)查看USB摄像头的设备ID

lsusb

请添加图片描述
(3)通过ID号 查看设备厂商等信息

sudo cat /sys/kernel/debug/usb/devices |grep 1908 -A 5

请添加图片描述
(4)下载v4l2-utils工具,v4l2-utils 是一个包含一系列与 Video4Linux2 (V4L2) 框架相关的实用程序和库的集合。它们的作用是帮助开发者、系统管理员和用户进行 V4L2 设备的管理、测试、配置和诊断。

sudo apt install v4l2-utils

主要用到的是v4l2-ctl指令,可以查看指令帮助集

v4l2-ctl -h

请添加图片描述
通过v4l2工具,查看摄像头 参数

v4l2-ctl -d  /dev/video0 --all

(5)安装guvcview工具,进行视频显示,实现件监控效果

sudo apt-get install guvcview

开始视频显示,设备节点是video0 还是video1需要都尝试下,错误的节点会直接报弹窗报错

guvcview -d /dev/video1 

弹窗报错图如下,需要重新切换正确节点
请添加图片描述
视频采集出现如下错误,解决方法

V4L2_CORE: Could not grab image (select timeout): Resource temporarily unavailable

虚拟机–>设置–>usb控制器—>usb兼容性,选择3.0以上即可

显示结果如下请添加图片描述
视频显示工具2,可自行安装测试

sudo apt-get install cheese
cheese -d /dev/video1  
2、查看摄像头设备的能力

现给出部分源码,后续会给出整个源码
结构体:struct v4l2_capability
内核源码路径include\uapi\linux\videodev2.h
请添加图片描述

/* Values for 'capabilities' field */
#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
/*查看 摄像头设备的能力*/	
int get_capability(int fd){
	int ret=0;
	struct v4l2_capability cap;	
	
	memset(&cap, 0, sizeof(struct v4l2_capability)); 
	
	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);  /*查看设备能力信息*/
	if (ret < 0) {
	    printf("VIDIOC_QUERYCAP failed (%d)\n", ret);
	    return ret;
	}
	printf("Driver Info: \n  Driver Name:%s \n  Card Name:%s \n  Bus info:%s \n",cap.driver,cap.card,cap.bus_info);
	printf("Device capabilities: \n"); 	
	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { /*支持视频捕获(截取一帧图像保存)*/
	  printf("  support video capture \n");
	}

	if (cap.capabilities & V4L2_CAP_STREAMING) { /*支持视频流操作(mmap映射到同一缓冲区队列后的入队出队 即流入流出)*/
	  printf("  support streaming i/o\n");
	}

	if(cap.capabilities & V4L2_CAP_READWRITE) { /*支持读写(需内核到应用空间拷贝 慢)*/
	  printf("  support read i/o\n");
	}
	return ret;
}

编译后输出,如果需要放到开发板上,需使用交叉编译工具链
请添加图片描述

3、查看支持视频格式

结构体:struct v4l2_fmtdesc
请添加图片描述

int get_suppurt_video_format(int fd){
	int ret=0;
	struct v4l2_fmtdesc fmtdesc;
	
	printf("List device support video format:  \n");
	
	memset(&fmtdesc, 0, sizeof(fmtdesc));
	fmtdesc.index = 0;
	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) 	/*枚举出支持的视频格式*/
	{
		fmtdesc.index++;
		printf("  { pixelformat = ''%c%c%c%c'', description = ''%s'' }\n",
		          fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF, 
		          (fmtdesc.pixelformat >> 24) & 0xFF, fmtdesc.description);
	}		
    return ret;
}

请添加图片描述

4、设置视频格式

结构体:v4l2_formatv4l2_pix_format
请添加图片描述
请添加图片描述

#define VIDEO_WIDTH  320  //采集图像的宽度
#define VIDEO_HEIGHT 240  //采集图像的高度	

nt set_video_format(int fd){	
	int ret = 0;
	struct v4l2_format fmt;
	
	memset(&fmt, 0, sizeof(fmt));
	fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width       = VIDEO_WIDTH; 
	fmt.fmt.pix.height      = VIDEO_HEIGHT;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; /*支持mipg的摄像头最好*/
                                                 /*普通摄像头会默认设置V4L2_PIX_FMT_YUYV格式 要用到jpeg库*/
	fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;		/*视频帧的扫描方式*/

	/*设置视频格式	*/
	ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
	if (ret < 0) {
	    printf("VIDIOC_S_FMT failed (%d)\n", ret);
	    return ret;
	}

	/*获取视频格式*/ 
	ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
	if (ret < 0) {
	    printf("VIDIOC_G_FMT failed (%d)\n", ret);
	    return ret;
	}
	
	printf("Stream Format Informations:\n");
	printf(" type: %d\n", fmt.type);
	printf(" width: %d\n", fmt.fmt.pix.width);
	printf(" height: %d\n", fmt.fmt.pix.height);
	char fmtstr[8];
	memset(fmtstr, 0, 8);
	memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);
	printf(" pixelformat: %s\n", fmtstr);
	printf(" field: %d\n", fmt.fmt.pix.field);
	printf(" bytesperline: %d\n", fmt.fmt.pix.bytesperline);
	printf(" sizeimage: %d\n", fmt.fmt.pix.sizeimage);
	
	return ret;
}

请添加图片描述

5、申请帧缓冲

结构体:v4l2_requestbuffers
请添加图片描述
请添加图片描述
请添加图片描述

#define	REQBUFS_COUNT	4	 /*缓存区个数*/
struct v4l2_requestbuffers reqbufs; 	/*定义缓冲区*/
struct cam_buf {
	void *start;
	size_t length;
};
struct cam_buf bufs[REQBUFS_COUNT]; 	/*映射后指向的同一片帧缓冲区*/

int request_buf(int fd){
	int ret=0;
	int i;
	struct v4l2_buffer vbuf;
	
	memset(&reqbufs, 0, sizeof(struct v4l2_requestbuffers));
	reqbufs.count	= REQBUFS_COUNT;					/*缓存区个数*/
	reqbufs.type	= V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbufs.memory	= V4L2_MEMORY_MMAP;					/*设置操作申请缓存的方式:映射 MMAP*/
	ret = ioctl(fd, VIDIOC_REQBUFS, &reqbufs); 	/*向驱动申请缓存	*/
	if (ret == -1) {	
		printf("VIDIOC_REQBUFS fail  %s %d\n",__FUNCTION__,__LINE__);
		return ret;
	}
	/*循环映射并入队 -> 让内核 和 应用的虚拟地址空间 指向同一片物理内存*/
	for (i = 0; i < reqbufs.count; i++){
		/*真正获取缓存的地址大小  注:你申请的多少个不一定返回那么多,原理需知内核底层代码,后续会有讲解*/
		memset(&vbuf, 0, sizeof(struct v4l2_buffer));
		vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuf.memory = V4L2_MEMORY_MMAP;
		vbuf.index = i;

		/*获取视频缓冲区的详细信息*/
		ret = ioctl(fd, VIDIOC_QUERYBUF, &vbuf);
		if (ret == -1) {
		  printf("VIDIOC_QUERYBUF fail  %s %d\n",__FUNCTION__,__LINE__);
			return ret;
		}
		/*映射缓存到用户空间,通过mmap讲内核的缓存地址映射到用户空间,并且和文件描述符fd相关联*/
		bufs[i].length = vbuf.length;
		bufs[i].start = mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset);
		if (bufs[i].start == MAP_FAILED) {
			printf("mmap fail  %s %d\n",__FUNCTION__,__LINE__);
			return ret;
		}
		/*每次映射都会入队,放入缓冲队列*/
		vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuf.memory = V4L2_MEMORY_MMAP;
		ret = ioctl(fd, VIDIOC_QBUF, &vbuf);
		if (ret == -1) {
			printf("VIDIOC_QBUF err %s %d\n",__FUNCTION__,__LINE__);
			return ret;
		}
	}
	return ret;
}
6、启动采集
int start_camera(int fd)
{
	int ret;
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_STREAMON, &type); 	/*ioctl控制摄像头开始采集*/
	if (ret == -1) {
		perror("start_camera");
		return -1;
	}
	fprintf(stdout, "camera->start: start capture\n");
	return 0;
}
7、出队取一帧图像
int camera_dqbuf(int fd, void **buf, unsigned int *size, unsigned int *index){
	int ret=0;
	struct v4l2_buffer vbuf;
	vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vbuf.memory = V4L2_MEMORY_MMAP;
	ret = ioctl(fd, VIDIOC_DQBUF, &vbuf);	/*出队,也就是从设备中取出图片*/
	if (ret == -1) {
		perror("camera dqbuf ");
		return -1;
	}	
	*buf = bufs[vbuf.index].start;
	*size = vbuf.bytesused;
	*index = vbuf.index;	
	return ret;
}
8、入队归还帧缓冲
int camera_eqbuf(int fd, unsigned int index)
{
	int ret;
	struct v4l2_buffer vbuf;

	vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vbuf.memory = V4L2_MEMORY_MMAP;
	vbuf.index = index;
	ret = ioctl(fd, VIDIOC_QBUF, &vbuf);		/*入队*/
	if (ret == -1) {
		perror("camera->eqbuf");
		return -1;
	}

	return 0;
}
9、停止视频采集
int camera_stop(int fd)
{
	int ret;
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
	if (ret == -1) {
		perror("camera->stop");
		return -1;
	}
	fprintf(stdout, "camera->stop: stop capture\n");

	return 0;
}
10、退出释放资源
int camera_exit(int fd)
{
	int i;
	int ret=0;
	struct v4l2_buffer vbuf;
	vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vbuf.memory = V4L2_MEMORY_MMAP;
	
	/*出队所有帧缓冲*/
	for (i = 0; i < reqbufs.count; i++) {
		ret = ioctl(fd, VIDIOC_DQBUF, &vbuf);
		if (ret == -1)
			break;
	}
	
	/*取消所有帧缓冲映射*/
	for (i = 0; i < reqbufs.count; i++)
		munmap(bufs[i].start, bufs[i].length);
	fprintf(stdout, "camera->exit: camera exit\n");
	return ret;
}

后续会详解ioctl()函数如何调用底层,完成上述一系列操作。

二、V4L2框架源码分析

作用:管理V4L2设备,向应用暴露控制接口。
内核源码:include/media/v4l2-dev.h

1、struct video_device

主要需要知道的是这几个结构体。

const struct v4l2_file_operations *fops;
const struct v4l2_ioctl_ops *ioctl_ops;	
struct v4l2_device *v4l2_dev;
struct vb2_queue *queue;

请添加图片描述

在这里插入图片描述
请添加图片描述

2、struct v4l2_device *v4l2_dev

请添加图片描述
总之,v4l2_device 表示整个 V4L2 子系统的顶层对象,用于管理和操作 V4L2 子系统,而 video_device 表示单个视频设备节点,用于表示和操作具体的视频设备实例。

3、struct v4l2_subdev

请添加图片描述
请添加图片描述
请添加图片描述
V4l2的子设备提供函数,可由底层驱动进行注册勾连实现。这是Linux驱动内核源码经常有这种。
**请添加图片描述
**某一底层驱动实现:
请添加图片描述请添加图片描述请添加图片描述

4、V4L2框架

请添加图片描述

三、基于NVIDIA平台的CSI摄像头驱动源码分析

摄像头框架最难的在于每个公司的平台不一样,华为海思有自己独立编写的摄像头框架平台,瑞芯微也有自己独立的摄像头框架平台,每个产家都不同,只是编写出来的摄像头框架平台最后都会勾连到上层内核V4L2框架提供方案给底层驱动实现具体的函数。

1、tegracam_device平台设备

(1)定义的基于英伟达摄像头设备的类
请添加图片描述
(2)对应的传感器操作函数
请添加图片描述
(3)对应的控制函数
请添加图片描述
(4)寄存器配置函数
请添加图片描述
总结:而imx219.c的驱动程序会基于tegracam_device框架进行选配实现一系列的操作函数
请添加图片描述

2、定义imx219摄像头设备类

以imx219.c源码为例
请添加图片描述

3、设备树匹配检测

请添加图片描述

4、探测函数probe初始化

请添加图片描述

5、基于英伟达平台摄像头的设备框架图

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

unity ui 同屏

一共有三个摄像机&#xff0c;上屏&#xff0c;下屏 和 类似照相机的ccamera 类似照相机的ccamera的设置&#xff1a; 下屏摄像机设置&#xff1a; 下屏交互的Canvas设置&#xff1a; 新建一个canvas&#xff0c;下面放上rawimage&#xff1a; 如果下屏不想显示的内容&#xf…

【前端】实现快速改变内容大小选择框

简言 简单实现选择框改变内容大小和位置。 内容 这里实现选择框改变内容大小是让内容宽高等于选择框的百分之百&#xff0c;当选择框大小改变时&#xff0c;内容也会响应的改变。 位置则是根据定位实现的。 选择框 选择框就是一个div&#xff0c;然后定位上下左右四条边和…

Stable Diffusion【古风模型】:喜欢古风的看过来,超写实汉服兼顾现代风格大模型汉服国风桃夭

这次来介绍【Stable Diffusion【古风模型】&#xff1a;喜欢古风的看过来&#xff0c;超写实汉服兼顾现代风格大模型汉服国风桃夭】&#xff0c;对于汉服国风桃妖大模型&#xff0c;不仅在古装国风写实上表现出色&#xff0c;同时该模型也兼容现代风格&#xff0c;并且出图效果…

测试人员在面试时的注意事项

一、技术方面面试 在某种程度上来说&#xff0c;技术面试重要到能够决定你是否被聘用。在技术岗位方面&#xff0c;在个人品德没有问题的前提下&#xff0c;招聘公司对技术是最关心的。 我现在并不能给你分析具体的面试题&#xff0c;因为与笔试题相比&#xff0c;面试题千变万…

信创基础硬件之芯片

信创基础硬件之芯片 文章目录 信创基础硬件之芯片服务器服务器的定义服务器的功能服务器的构成服务器的性能 处理器&#xff08;CPU&#xff09;CPUGPUDPU CPU的分类按CPU指令集架构分类按CPU体系架构分类 CPU产业链六大国产CPU公司详解海光飞腾鲲鹏兆芯龙芯申威 国产CPU对比从…

自动群发国际短信脚本的详情介绍!

在当今全球化的商业环境中&#xff0c;信息的及时传递显得尤为重要&#xff0c;国际短信作为一种高效、低成本的沟通方式&#xff0c;被广泛应用于企业营销、客户服务、产品推广等领域。 为了满足企业对于群发国际短信的需求&#xff0c;市场上涌现出了许多自动群发国际短信脚…

Golang编译优化——稀疏条件常量传播

文章目录 一、概述二、稀疏条件常量传播2.1 初始化worklist2.2 构建def-use链2.3 更新值的lattice2.4 传播constant值2.5 替换no-constant值 一、概述 常量传播&#xff08;constant propagation&#xff09;是一种转换&#xff0c;对于给定的关于某个变量 x x x和一个常量 c …

初探MFC程序混合使用QT

一、背景 随着操作系统国产化替代的趋势越发明显&#xff0c;软件支持国际化、跨平台&#xff0c;已然是必须做的一件事情。原有的软件UI层用的是MFC&#xff0c;将其换成QT&#xff0c;想必是一种较好的方案。对于大型软件&#xff0c;特别是已发布&#xff0c;但还处于不断迭…

43.乐理基础-拍号-常见的拍号与强弱关系

首先拍号的定义&#xff1a;39.认识音符、40.什么是一拍、41.小节、小节线、终止线、42.看懂拍号的意义 通过 39.认识音符、40.什么是一拍、41.小节、小节线、终止线、42.看懂拍号的意义 应该可以知道 Y的取值只能是2、4、8、16、32、64。。。。因为Y指的是Y分音符&#xff0c;…

数据库数据恢复—Sql Server数据库文件丢失丢失怎么恢复数据?

数据库数据恢复环境&#xff1a; 5块硬盘组建一组RAID5阵列&#xff0c;划分LUN供windows系统服务器使用。windows系统服务器内运行了Sql Server数据库&#xff0c;存储空间在操作系统层面划分了三个逻辑分区。 数据库故障&#xff1a; 数据库文件丢失&#xff0c;主要涉及3个…

Adobe系列软件安装

双击解压 先运行Creative_Cloud_Set_Up.exe。 完毕后&#xff0c;运行AdobeGenP.exe 先Path&#xff0c;选路径&#xff0c;如 C:\Program Files\Adobe 后Search 最后Patch。 关闭软件&#xff0c;修图&#xff01;

【LeetCode刷题记录】124. 二叉树中的最大路径和

124 二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的…

记录我的程序猿副业首笔创收

在这个充满机遇的数字时代&#xff0c;我&#xff0c;一个普通的程序猿&#xff0c;编程爱好者&#xff0c;终于在云端源想这个平台上收获了属于我的第一桶金。这是一个关于兼职、学习与成长的故事&#xff0c;希望能激发同在编程路上的你&#xff0c;勇敢迈出那一步。 先晒晒…

深度学习论文: SuperPoint: Self-Supervised Interest Point Detection and Description

深度学习论文: SuperPoint: Self-Supervised Interest Point Detection and Description SuperPoint: Self-Supervised Interest Point Detection and Description PDF: https://arxiv.org/pdf/1712.07629 PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代…

RuntimeError: Tensor must have a last dimension with stride 1

我在使用torch.view_as_complex将weight转化为复数时&#xff0c;遇到了这样一个错误&#xff1a;由于我在对weight使用view_as_complex之前使用了F.interpolate函数进行了分辨率调整&#xff0c;因此只需对张量weight添加.contiguous()即可。

【商业】SD NAND(贴片式TF卡)性能体验及应用

SD NAND【商业】   外观   NAND与TF卡的区别   雷龙CS SD NAND(贴片式TF卡)性能体验及应用   最后 SD NAND 外观正反示意图 NAND与TF卡的区别 什么是SD NAND&#xff1f;它俗称贴片式T卡&#xff0c;贴片式TF卡&#xff0c;贴片式SD卡&#xff0c;贴片式内存卡&am…

2023年谷歌拒了228万应用,禁了33.3万账号,开发者们应如何应对2024的挑战?

谷歌在上周一公布了去年如何应对恶意应用和恶意行为。 报告指出&#xff0c;去年谷歌在Google Play平台上&#xff0c;通过不断升级安全系统、更新政策规定、运用先进的机器学习技术&#xff0c;以及严格把关应用审核流程&#xff0c;成功阻止了高达228万个不合规的应用程序上架…

家用洗地机应该怎么选?哪个牌子好?市场上主流洗地机品牌推荐

洗地机的出现&#xff0c;让越来越多的家庭享受清洁的过程&#xff0c;给人们腾出来更多的时间陪伴家人和休息。但是在选购一台洗地机前&#xff0c;大家多多少少肯定有些疑问&#xff0c;洗地机到底实不实用&#xff1f;好不好用&#xff1f;能扫干净吗&#xff1f;还有哪些好…

网盘应用:桌面端界面欣赏,这个赛道容不下小玩家。

网盘&#xff08;Cloud Storage&#xff09;是一种云存储服务&#xff0c;允许用户在互联网上存储、管理和共享文件。它提供了一个在线的虚拟硬盘&#xff0c;用户可以通过网络将文件上传到云端&#xff0c;并随时随地访问和管理这些文件。 阿里云盘

Rumor Containment by Blocking Nodes in Social Networks

Abstract 谣言在社交网络中快速传播&#xff0c;可能严重损害我们的社会。在本文中&#xff0c;我们提出了一种基于整数线性规划&#xff08;ILP&#xff09;的数学规划公式&#xff0c;通过阻止建模为线性阈值模型的复杂社交网络中的节点子集&#xff08;称为阻止者&#xff…