V4L2 Camera 开发

news2024/11/25 11:33:29

一、什么是V4L2

vl42是video for Linux 2的缩写,是一套Linux内核视频设备的驱动框架,该驱动框架为应用层提供一套统一的操作接口(一系列的ioctl)

假如要进行视频数据采集,大体的步骤如图左侧所示:

  1. 打开设备文件/dev/videoX;
  2. 根据打开的设备,查询设备能力集;
  3. 设置视频数据的格式、参数等;
  4. 分配buffer,这个buffer可以是用户态分配的,也可以是从内核中获取的;
  5. 开始视频流采集工作;
  6. 将buffer enqueue到v4l2框架,底层负责将视频数据填充后,应用层再将buffer dequeue以便获取数据,然后再将buffer enqueue,如此循环往复;

1. 打开设备

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
	//1.打开设备
	int fd = open("/dev/video0", O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return -1;
	}
	//9.关闭设备
	close(fd);
	return 0;
}

2. 获取支持格式

0

获取摄像头格式VIDIOC_ENUM_FMT--对应存储格式的结构体struct v4l2_fmtdesc

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
	//1.打开设备
	int fd = open("/dev/video0", O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return -1;
	}
	//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
	struct v4l2_fmtdesc v4fmt;
	v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	int i=0;
	while(1)
	{
		v4fmt.index = i++;  
		int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
		if(ret < 0)
		{
			perror("获取失败");
			break;
		}
		printf("index=%d\n", v4fmt.index);
		printf("flags=%d\n", v4fmt.flags);
		printf("description=%s\n", v4fmt.description);
		unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
		printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
		printf("reserved=%d\n", v4fmt.reserved[0]);
	}
	//9.关闭设备
	close(fd);
	return 0;
}

3.配置摄像头采集格式

0

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>

int main(void)
{
	//1.打开设备
	int fd = open("/dev/video0", O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return -1;
	}

	//3.设置采集格式
	struct v4l2_format vfmt;
	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
	vfmt.fmt.pix.width = 640;//设置宽(不能任意)
	vfmt.fmt.pix.height = 480;//设置高
	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
	int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
	if(ret < 0)
	{
		perror("设置格式失败");
	}

	memset(&vfmt, 0, sizeof(vfmt));
	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret  = ioctl(fd, VIDIOC_G_FMT, &vfmt);
	if(ret < 0)
	{
		perror("获取格式失败");
	}

	if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 && 
	vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
	{
		printf("设置成功\n");
	}else
	{
		printf("设置失败\n");
	}
	
	//9.关闭设备
	close(fd);
	return 0;
}

4.申请内核缓冲区队列

0

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>

int main(void)
{
	//1.打开设备
	int fd = open("/dev/video0", O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return -1;
	}
	//3.设置采集格式
	struct v4l2_format vfmt;
	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
	vfmt.fmt.pix.width = 640;//设置宽(不能任意)
	vfmt.fmt.pix.height = 480;//设置高
	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
	int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
	if(ret < 0)
	{
		perror("设置格式失败");
	}
	//4.申请内核空间
	struct v4l2_requestbuffers reqbuffer;
	reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuffer.count = 4; //申请4个缓冲区
	reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
	ret  = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
	if(ret < 0)
	{
		perror("申请队列空间失败");
	}
	//9.关闭设备
	close(fd);
	return 0;
}

5.把内核的缓冲区队列映射到用户空间

0

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>

int main(void)
{
	//1.打开设备
	int fd = open("/dev/video0", O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return -1;
	}

	//3.设置采集格式
	struct v4l2_format vfmt;
	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
	vfmt.fmt.pix.width = 640;//设置宽(不能任意)
	vfmt.fmt.pix.height = 480;//设置高
	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
	int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
	if(ret < 0)
	{
		perror("设置格式失败");
	}

	//4.申请内核空间
	struct v4l2_requestbuffers reqbuffer;
	reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuffer.count = 4; //申请4个缓冲区
	reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
	ret  = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
	if(ret < 0)
	{
		perror("申请队列空间失败");
	}

	//5.映射
	unsigned char *mptr[4];//保存映射后用户空间的首地址
    unsigned int  size[4];
	struct v4l2_buffer mapbuffer;
	//初始化type, index
	mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	for(int i=0; i<4; i++)
	{
		mapbuffer.index = i;
		ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
		if(ret < 0)
		{
			perror("查询内核空间队列失败");
		}
		mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, 
                                            MAP_SHARED, fd, mapbuffer.m.offset);
            size[i]=mapbuffer.length;

		//通知使用完毕--‘放回去’
		ret  = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
		if(ret < 0)
		{
			perror("放回失败");
		}
	}
	//9.关闭设备
	close(fd);
	return 0;
}

6.开始采集

VIDIOC_STREAMON(开始采集写数据到队列中)

VIDIOC_DQBUF(告诉内核我要某一个数据,内核不可以修改)

VIDIOC_QBUF(告诉内核我已经使用完毕)

VIDIOC_STREAMOFF(停止采集-不在向队列中写数据)

0

//6.开始采集
	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_STREAMON, &type);
	if(ret < 0)
	{
		perror("开启失败");
	}

7.采集数据,

//从队列中提取一帧数据
	struct v4l2_buffer  readbuffer;
	readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
	if(ret < 0)
	{
		perror("提取数据失败");
	}

	FILE *file=fopen("my.jpg", "w+");
	//mptr[readbuffer.index]
	fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);
	fclose(file);
	
	//通知内核已经使用完毕
	ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
	if(ret < 0)
	{
		perror("放回队列失败");
	}

8. 停止采集

ret = ioctl(fd, VIDIOC_STREAMOFF, &type);

9.释放映射

for(int i=0; i<4; i++) munmap(mptr[i], size[i]);

10.关闭设备

close(fd);

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

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

相关文章

从零学算法(LCR 178)

教学过程中&#xff0c;教练示范一次&#xff0c;学员跟做三次。该过程被混乱剪辑后&#xff0c;记录于数组 actions&#xff0c;其中 actions[i] 表示做出该动作的人员编号。请返回教练的编号。 示例 1&#xff1a; 输入&#xff1a;actions [5, 7, 5, 5] 输出&#xff1a;7 …

java中使用redis2个库并支持Redis哈希表

一个redis实例&#xff0c;默认包含16个库&#xff0c;序号从0到15。在redis命令行中&#xff0c;可以用select 序号来切换。我最近在做的一个项目中&#xff0c;需要使用redis的2个库。一个是由其他子系统写入&#xff0c;web后端&#xff08;java&#xff09;只读取&#xff…

瑞芯微RK3568|SDK开发之Kernel编译

1. Kernel手动编译 1.1 kernel查询帮助 使用./build.sh -h kernel查看kernel的详细编译命令如下所示。 图1.1编译内核 上图表示&#xff0c;单独编译kernel固件分为三步&#xff0c;进入kernel目录&#xff0c;选择默认配置文件&#xff0c;编译镜像。 1.2 kernel…

多家快递公司物流信息批量查询方法及操作说明

在网购时代&#xff0c;快递单号的查询和管理成了一个重要的问题。尤其是对于需要处理大量快递单号的人来说&#xff0c;一个高效、便捷的查询工具至关重要。本文将介绍如何使用“固乔快递查询助手”软件&#xff0c;快速、准确地查询多家不同快递公司的快递单号&#xff0c;并…

最新白皮书:软件定义的硬件打开通往高性能数据加速的大门

在众多行业的数字化转型过程中&#xff0c;基于硬件的数据处理加速是构建高性能、高效率智能系统的关键之处&#xff0c;因而市场上出现了诸如FPGA、GPU和xPU等许多通用或者面向特定应用&#xff08;如NPU&#xff09;的硬件加速器。尽管它们的性能和效率都高于通用处理器&…

基于R的linkET包qcorrplot可视化Mantel test相关性网络热图分析correlation heatmap

写在前面 需求是对瘤胃宏基因组结果鉴定到的差异菌株与表观指标、瘤胃代谢组、血清代谢组、牛奶代谢组中有差异的部分进行关联分析&#xff0c;效果图如下&#xff1a; 数据准备 逗号分隔的csv格式文件&#xff0c;两个表格&#xff0c;一个是每个样本对应的表观指标数据&…

工业交换机常见的故障有哪些?

通常情况下&#xff0c;工业交换机出现故障可以分为两类&#xff1a;软件性能故障和硬件物理故障。软性能故障通常指工业交换机在研发设计阶段出现的问题。 物理层故障主要指交换机本身的硬件故障以及连接交换机的物理线路故障。安防专用工业交换机的交换是根据通信双方传输信…

构建智能客服知识库,优化客户体验不是难题!

在当今快节奏的商业环境中&#xff0c;客户都希望得到及时个性化的支持&#xff0c;拥有一个智能客服知识库对于现代企业至关重要。智能客服知识库是一个集中存储、组织和访问与客户服务互动相关的信息的综合性知识库。它为企业提供了全面的知识来源&#xff0c;使他们能够为客…

PHP循环获取Excel表头字母A-Z,当超过时输出AA,AB,AC,AD······

PHP循环获取Excel表头字母A-Z&#xff0c;当超过时输出AA,AB,AC,AD PHP循环生成Excel的列字母表 $count_num 26 * 27; $letter A; $arr []; while($count_num--){$arr[] $letter;$letter; }结果如下&#xff1a; 转为JSON更为直观&#xff1a; ["A","B&…

MySQL 8 0 填坑历险之安装与基本用户权限

MySQL 8.0 填坑历险之安装 MySQL 是当下流行的关系型数据库&#xff0c;搜掠了各种文章&#xff0c;多少有些偏差&#xff0c;为此来分享下我的经历&#xff1b; 安装环境 环境版本备注Ubuntu 18.0418.04.6 LTS (Bionic Beaver)LinuxKernel5.4.0-42-genericMySQL 8.0.23mysq…

上海亚商投顾:沪指震荡调整 减肥药、华为概念股持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日震荡调整&#xff0c;上证50午后一度跌近1%&#xff0c;券商、保险等权重板块走势低迷。 华为概…

大咖共探AGI时代机遇,腾讯云助力大模型规模化应用提速

引言 2023 年&#xff0c;科技圈的“顶流”莫过于大模型。自 ChatGPT 的问世拉开大模型与生成式 AI 产业的发展序幕后&#xff0c;国内大模型快速跟进&#xff0c;已完成从技术到产品、再到商业的阶段跨越&#xff0c;并深入垂直行业领域。 新技术的爆发&#xff0c;催生新的应…

唤醒手腕 2023年 B 站课程 Golang 语言详细教程笔记(更新中)

0001、1000集GO语言Flag毒誓 唤醒手腕UP猪Pig目标花费1000集进行讲解Go语言视频学习教程&#xff08;有趣的灵魂&#xff0c;适合小白&#xff0c;不适合巨佬&#xff09;&#xff0c;从2023年3月19日开始&#xff0c;将会一直每天更新&#xff0c;准备在2024年5月1日之前更新…

(主)9.26锁存器状态机方法、题解大综合(加码加码加码)

触发器 D D锁存器就是在有时钟信号时&#xff0c;D信号是啥&#xff0c;Q就是啥&#xff0c;并且在时钟信号消失时&#xff0c;锁存住消失前一刻的信号状态 主从D锁存器中&#xff0c;D信号是主锁存器的输入信号&#xff0c;主锁存器的信号是从锁存器的输入信号 用D实现的电…

苹果电脑专业的条形码工具iBarcoder:助力高效条形码生成,提升工作效率

iBarcoder for mac是Mac os平台上的一款优秀条码生成软件。它可以帮助用户建立不同类型的条形码&#xff0c;并允许用户设计并打印自己的条码标签&#xff0c;创建条码图形的零售包装、书籍、贴纸等。 在iBarcoder中&#xff0c;用户可以方便地创建专业的条形码标签&#xff0…

Linux服务器安装Anaconda 配置远程jupyter lab使用虚拟环境

参考的博客&#xff1a; Linux服务器安装Anaconda 并配置远程jupyter lab anaconda配置远程访问jupyter&#xff0c;并创建虚拟环境 理解和创建&#xff1a;Anaconda、Jupyterlab、虚拟环境、Kernel 下边是正文了。 https://www.anaconda.com/download是官网网址&#xff0c;可…

C/C++进程线程超详细详解

目录 前言 一、进程基础 1.进程概念 2.进程特征 3.进程状态&#xff08;如图清晰可见&#xff09; 4&#xff0c;进程的标识 实例代码如下&#xff1a; 5.进程的种类 实例shell脚本程序如下: 二、进程API 1.创建子进程 实例代码如下&#xff1a; 2.exec函数族 函数…

ptmalloc源码分析 - free()函数的实现(10)

目录 一、步骤1-标记MMAP分配的使用munmap_chunk释放 二、步骤2-释放的是小块内存&#xff0c;则chunk放入fastbin 三、步骤3-如果不是MMAP分配&#xff0c;则释放到unsorted bins 四、步骤4-如果nextchunk 就是Top chunk&#xff0c;则直接扩容Top chunk 五、步骤5-如果释…

分享从零开始学习网络设备配置--任务4.1 IPv6地址的基本配置

任务描述 某公司构建了互联互通的办公网&#xff0c;需要不断扩大网络规模。网络管理员小赵决定采用IPv6的地址&#xff0c;满足公司网络规模的未来发展。 由于IPv4地址耗尽及IPv4地址区域分配不均衡&#xff0c;成为运营商必须面临的一个问题。另外随着互联网的商业化&#…

【python】将python脚本打包成可执行的.exe文件 推荐使用auto-py-to-exe

将python脚本打包成可执行的exe文件 1.不推荐使用网上流行的打包程序&#xff0c;主要是有以下几点&#xff0c;一、cx_freeze与Pyinstaller都是命令行的形 式&#xff0c;且对于Pyinstaller的打包常常需要打包完成后&#xff0c;再修改打包后的原路径&#xff0c;二、非可视…