Linux下C语言通过v4l2对USB摄像头数据的获取
目录
usb摄像头
USB摄像头是一种通过USB接口连接到计算机的摄像头设备,用于捕捉图像和视频。它们通常具有内置的镜头和传感器,可以通过计算机上的软件应用程序进行控制和操作。USB摄像头广泛应用于视频通话、视频会议、网络直播、监控、视频录制等领域。
以下是一些USB摄像头的特点和优势:
- 易于安装和使用:USB摄像头通常只需插入计算机的USB端口即可自动识别和安装驱动程序,无需额外的电源供应。
- 高清图像和视频:许多USB摄像头支持高清图像和视频捕捉,可以提供清晰的视频质量。
- 灵活性:USB摄像头可以通过调整角度或位置来灵活拍摄不同的场景,适用于各种应用场景。
- 多功能性:USB摄像头通常具有多种功能,例如自动对焦、自动曝光、麦克风等,可以满足不同需求。
- 兼容性:USB接口是一种通用接口,几乎所有的计算机和设备都支持USB连接,因此USB摄像头具有较好的兼容性。
- 便携性:USB摄像头通常比传统的摄像头更小巧轻便,便于携带和移动。
在选择USB摄像头时,您可以根据自己的需求和预算考虑一些因素,例如摄像头的分辨率、帧率、对焦方式、兼容性等。市场上有许多知名品牌生产的USB摄像头可供选择,您可以根据自己的需求选择适合的产品。
像我们笔记本电脑的摄像头就很多都是通过usb摄像头来实现的,就是通过芯片去采集sensor的数据,进过isp处理,然后再转换为标准usb uvc数据输出,这样电脑端就可以直接免驱(现在基本都预装在里面了不需要驱动)使用了,通过自带的照相机或者opencv等就可以直接打开使用拉。像海思、星辰的芯片都支持这样子处理,很多usb摄像头也都是用他们的方案。
v4l2
Video4Linux2(简称V4L2)是Linux操作系统下用于支持视频设备的内核驱动程序框架。它允许应用程序访问和控制各种视频设备,如摄像头、视频采集卡等。V4L2提供了一组API,使开发人员能够编写应用程序来捕获、处理和显示视频流。
以下是一些V4L2的特点和功能:
- 标准化接口:V4L2提供了标准化的接口,使开发人员能够编写与各种视频设备兼容的应用程序,而无需了解特定设备的细节。
- 支持多种视频设备:V4L2支持各种视频设备,包括USB摄像头、网络摄像头、视频采集卡等,使其成为Linux系统下广泛应用的视频设备驱动框架。
- 功能丰富:V4L2提供了丰富的功能和控制选项,如捕获图像、设置视频格式、调整曝光、对焦、白平衡等。
- 性能优化:V4L2旨在提供高性能的视频捕获和处理能力,可以满足各种实时视频应用的需求。
- 开源和社区支持:V4L2是开源项目,得到了广泛的社区支持和贡献,持续改进和更新。
使用V4L2的应用程序可以通过调用相应的API来与视频设备进行交互,从而实现视频捕获、处理和显示等功能。开发人员可以利用V4L2来开发各种视频应用程序,如视频监控、视频会议、视频流媒体等。
有时候项目对摄像头的要求不是那么高,同时为了加快进度,就会选择直接拿别人开发好的usb摄像头来使用,这样我们就不需要做isp部分的功能,只需要通过v4l2直接采集数据即可了,非常的方便快捷,而且有些厂家的mipi驱动也会直接帮你接上v4l2驱动,整套移植完后就可以直接用v4l2来操作而不需要去用厂家的sdk,比如jetson、瑞芯微。
程序实现
默认情况下Linux内核是已经打开相关驱动了,如果没有的话就需要自己去内核源码里面打开uvc相关再重新编译烧写。
正常情况下,接入usb video设备后在/dev目录下会有对应的video设备,我们程序先通过open去打开,再对摄像头的格式进行配置,具体摄像头支持什么配置可以看你买的摄像头的资料,读取一下配置看看是否配置正确,再mmap申请缓冲区把内核空间映射到用户空间,然后再开始摄像头采集,后面再循环读取队列里面的数据即可,队列里面的数据即是摄像头输出的数据,但要转为显示的话就要看你摄像头输出的是什么,然后你的显示器支持显示什么,转换一下格式再显示即可。
//
#include "v4l2.h"
struct VideoFrame MFrame[COUNT];//定义保存映射空间的首地址和长度
int video_linux_init(const char *device, unsigned short width, unsigned short height)
{
printf("%s %d %d \n", device, width, height);
//1.打开摄像头设备文件
int vfd = open(device,O_RDWR);
if(vfd < 0)
{
perror("Video device open!");
return -1;
}
//2.设置摄像头格式
struct v4l2_format sfmt;
sfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
sfmt.fmt.pix.width = width;
sfmt.fmt.pix.height = height;
sfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//(设置视频输出格式,但是要摄像头支持)
int sret = ioctl(vfd, VIDIOC_S_FMT, &sfmt);
if(sret < 0)
{
perror("set fmt fail");
close(vfd);
return -1;
}
//3.获取摄像头当前的采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int ret = ioctl(vfd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("Get FMT fail!");
exit(1);
}
char fmtstr[8] = {0};
memcpy(fmtstr,&vfmt.fmt.pix.pixelformat,4);
if(strcmp(fmtstr,"YUYV") != 0)
{
close(vfd);
return -1;
}
printf("width:%d,height:%d,format:%s\n",vfmt.fmt.pix.width,vfmt.fmt.pix.height,(char *)fmtstr);
return vfd;
}
uint8_t video_linux_mmap(int vfd)
{
memset(MFrame, 0, sizeof(MFrame));
//向内核申请视频缓存
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.count = COUNT;//申请缓存区个数
reqbuf.memory = V4L2_MEMORY_MMAP;
int ret = ioctl(vfd, VIDIOC_REQBUFS, &reqbuf);
if(ret < 0)
{
perror("request fail");
return 0;
}
//把内核空间映射到用户空间
struct v4l2_buffer kbuf;
memset(&kbuf, 0, sizeof(kbuf));
int i = 0;
for(i=0; i<(int)reqbuf.count; i++)
{
//把申请的缓存区空间一个一个取出来与用户空间映射
kbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
kbuf.index = i;
kbuf.memory = V4L2_MEMORY_MMAP;
if(ioctl(vfd, VIDIOC_QUERYBUF, &kbuf)<0)
{
perror("query buf fail");
return 0;
}
//映射
MFrame[i].length = kbuf.length;
MFrame[i].start = (char *)mmap(NULL, kbuf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vfd, kbuf.m.offset);
if(MFrame[i].start == MAP_FAILED)
{
perror("mmap fail");
return 0;
}
//缓存入队
if(ioctl(vfd, VIDIOC_QBUF, &kbuf) < 0)
{
perror("qbuf fail");
return 0;
}
}
return 1;
}
uint8_t video_linux_start(int vfd)
{
//启动摄像头数据采集
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(vfd, VIDIOC_STREAMON, &type)<0)
{
perror("start fail");
return 0;
}
return 1;
}
uint8_t video_linux_get_frame(int vfd, FrameBuffer *frame)
{
//出队
struct v4l2_buffer readbuf;
readbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
readbuf.memory = V4L2_MEMORY_MMAP;
if(ioctl(vfd, VIDIOC_DQBUF, &readbuf) < 0)//取一帧数据
{
perror("dqbuf fail");
return 0;
}
memcpy(frame->buf, MFrame[readbuf.index].start, MFrame[readbuf.index].length);
frame->length = readbuf.length;
//入队
if(ioctl(vfd, VIDIOC_QBUF, &readbuf) < 0)
{
perror("qbuf fail");
return 0;
}
return 1;
}
uint8_t video_linux_stop(int vfd)
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//采集结束
if(ioctl(vfd, VIDIOC_STREAMOFF, &type)<0)
{
perror("stop fail");
return 0;
}
//释放映射空间
int i=0;
for(i=0; i<COUNT; i++)
{
munmap(MFrame[i].start, MFrame[i].length);
}
return 1;
}
void video_linux_destroy(int vfd)
{
close(vfd);
}
- 分享
- 举报
-
浏览量:1622次2023-12-28 14:40:29
-
浏览量:1787次2023-10-26 13:56:58
-
浏览量:1025次2023-10-26 13:41:10
-
浏览量:1461次2023-10-25 14:27:44
-
浏览量:846次2023-10-30 14:53:50
-
浏览量:2856次2018-12-27 13:35:53
-
浏览量:887次2023-11-23 13:38:54
-
浏览量:1022次2024-02-22 15:39:36
-
浏览量:999次2024-01-18 15:33:23
-
浏览量:9114次2020-07-17 12:21:58
-
浏览量:1188次2023-12-08 16:23:35
-
浏览量:2495次2020-09-04 13:47:44
-
浏览量:1080次2023-12-26 18:03:31
-
浏览量:2217次2022-11-23 16:19:38
-
浏览量:1445次2023-06-02 17:41:13
-
浏览量:888次2023-06-03 16:08:12
-
浏览量:2833次2024-02-04 10:54:00
-
浏览量:1180次2023-12-08 16:48:29
-
浏览量:2681次2023-06-28 17:22:40
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
毛巾卷
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明