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

文章目录

      • 一、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.mzph.cn/bicheng/8361.shtml

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

相关文章

C#核心之面向对象-继承

面向对象-继承 文章目录 1、继承的基本规则1、基本概念2、基本语法3、示例4、访问修饰符的影响5、子类和父类的同名成员 2、里氏替换原则1、基本概念2、is和as3、基本实现 3、继承中的构造函数1、基本概念2、父类的无参构造函数3、通过base调用指定父类构造 4、万物之父和装箱拆…

关于ESP32下载的几个小问题

文章目录 一、没有收到串口数据二、vscode使用jtag烧录失败 在使用esp32的时候&#xff0c;下载遇到了这么几个小问题&#xff0c;写一下解决方法。 一、没有收到串口数据 报错如下&#xff1a; 这是在使用arduino下载的时候出现的错误&#xff1a;A fatal error occurred: …

c++多线程2小时速成

简介 c多线程基础需要掌握这三个标准库的使用&#xff1a;std::thread,std::mutex, andstd::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::th…

Web Component fancy-components

css-doodle 组件库 fancy-components 组件库使用 yarn add fancy-components使用&#xff1a; import { FcBubbles } from fancy-components new FcBubbles() //要用哪个就new哪个 new 这里可能会报错eslink,eslintrc.js中处理报错 module.exports {rules: {no-new: off} …

日志审计系统在提高网络安全方面具有哪些重要的作用

随着信息技术的飞速发展&#xff0c;我们正处于一个高度互联、数据驱动的网络时代。在这个时代&#xff0c;日志审计系统作为网络安全和信息管理的重要工具&#xff0c;发挥着至关重要的作用。下面德迅云安全就详细介绍下关于日志审计系统在当今网络时代的重要性。 一、什么是日…

四、Redis五种常用数据类型-List

List是Redis中的列表&#xff0c;按照插入顺序保存数据&#xff0c;插入顺序是什么样的&#xff0c;数据就怎么保存。可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含232-1个元素(4294967295&#xff0c;每个列表超过40亿个元素)。是一种双向列表结构…

Redis单机安装

1.编译 cd redis安装目录 makemake install2.修改配置文件redis.conf #端口修改 port 6379 #后台进程启动 yes daemonize yes # daemonize no #注释掉 为了可以远程连接 #bind 127.0.0.1 #设置密码 requirepass pwd3.启动 ./redis-server ../redis.conf查看进程 [rootlocal…

使用Maven对Java独立应用程序进行编译打包

一、 安装Maven 1.解压&#xff0c;移动安装包 sudo tar -zxf ~/apache-maven-3.9.6-bin.tar.gz -C /usr/local/ cd /usr/local/ sudo mv apache-maven-3.9.6/ ./maven-3.9.6 sudo chown -R qiangzi ./maven-3.9.6 二、Java应用程序代码 1.版本信息&#xff1a; Spark-2.1…

设计软件有哪些?渲染软件篇(1),渲染100邀请码1a12

做设计要用到很多软件&#xff0c;今天我给大家介绍一些渲染方面的&#xff0c;仅供参考。 1、渲染100(http://www.xuanran100.com/?ycode1a12) 渲染100是网渲平台&#xff0c;为设计师提供高性能的渲染服务。通过它设计师可以把本地渲染移到云端进行&#xff0c;速度快价格便…

APP广告变现:自刷的秘密与规则

在移动互联网时代&#xff0c;广告已成为众多APP盈利的主要方式之一。对于开发者和运营者而言&#xff0c;如何通过广告变现提高收益是他们必须关注的问题。然而&#xff0c;在众多的变现方法中&#xff0c;“自刷广告”这一概念可能让一些人感到迷惑。实际上&#xff0c;只要在…

巨资回流,量子投资热潮再起

一股新的信心和资金浪潮正在席卷量子计算产业。 2023年4月30日&#xff0c;澳大利亚联邦政府和昆士兰州政府宣布共同出资9.4亿澳元&#xff08;约合6.2亿美元&#xff09;&#xff0c;支持美国初创企业PsiQuantum在布里斯班附近建设一台大型量子计算机。这项投资是最新的迹象之…

字符串的赋值

因为字符串本质上就是数组&#xff0c;因此它的赋值也具有数组的特点&#xff1a;只能在初始化时用等号。 1&#xff0e;字符串的初始化 方法一&#xff1a;指定大小 char s[6]"Hello"; //指定字符串最大占用6个的char型内存单元 虽然看上去是将整个字符串赋给了…

RT-IoT2022 数据集-扩展数据(自制方法)

数据集官网Discover datasets around the world!https://archive.ics.uci.edu/dataset/942/rt-iot2022RT-IoT2022 是源自实时物联网基础设施的专有数据集&#xff0c;作为集成了各种物联网设备和复杂网络攻击方法的综合资源而引入。该数据集包含正常和对抗性网络行为&#xff0…

基于ConvNeXt网络的图像识别

1、前言 ConvNeXt 网络基于传统的卷积神经网络&#xff0c;与当下 transformer当道而言简直是一股清流 ConvNeXt并没有特别复杂或者创新的结构 ConvNeXt 网络有五种大小&#xff0c;可以参考下面 2、项目实现 完整的项目如下&#xff1a; 这里参考了网上的ConvNeXt 模型&…

Springboot+Vue项目-基于Java+MySQL的个人云盘管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

神经网络的基础:从感知机到复杂网络的进化

文章目录 一、神经网络的例子二、理解神经信号的传递2.1 感知机的结构和功能2.2 优化函数2.3 明确表示偏置 三、理解感知机和激活函数的作用3.1 基础感知机模型3.2 激活函数的引入 一、神经网络的例子 理解感知机 在探索神经网络之前&#xff0c;我们首先回顾了感知机的基本概…

iOS抓包工具——Stream

[清风穆云 ](https://cloud.tencent.com/developer/user/4224041) iOS抓包工具——Stream 关注作者 前往小程序&#xff0c;Get_更优_阅读体验&#xff01; 立即前往 腾讯云 开发者社区 文档建议反馈控制台 首页 学习 活动 专区 工具 TVP [最新优惠活动 ](http…

可视化实验三 Matplotlib库绘图及时变数据可视化

1.1 任务一 1.1.1 恢复默认配置 #绘图风格&#xff0c;恢复默认配置 plt.rcParams.update(plt.rcParamsDefault)#恢复默认配置 或者 plt.rcdefaults() 1.1.2 汉字和负号的设置 import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"]"SimH…

Vue3 路由入门

先安装路由 npm i vue-router //创建路由器 import { createRouter, createWebHashHistory } from vue-router//1.导入组件 import Home from /components/Home.vue import News from /components/News.vue//2.配置路由映射规则 const routes [{name: home,path: /home,compo…

学习方法的重要性

原贴&#xff1a;https://www.cnblogs.com/feily/p/13999204.html 原贴&#xff1a;https://36kr.com/p/1236733055209095 1、 “一万小时定律”的正确和误区 正确&#xff1a; 天才和大师的非凡&#xff0c;不是真的天资超人一等&#xff0c;而是付出了持续不断的努力&…