USB摄像头驱动配置及V4L2编程
来源:互联网 发布:淘宝天猫京东入驻条件 编辑:程序博客网 时间:2024/05/28 05:18
1、摄像头驱动开发
1.1 摄像头软件系统架构
摄像头系统架构分为四层:摄像头、支持V4L2的摄像头驱动、V4L2核心、应用程序。
V4L2核心是Linux系统自带的组件,它可以屏蔽摄像头驱动层的差异,不管底层的摄像头有什么差异,上层应用统一调用V4L2来实现对摄像头的操作。因此驱动程序和应用程序都需要遵循V4L2规范
1.2 摄像头驱动使能
#make menuconfig ARCH=arm找到Device Drivers->Multimedia support,把Video for Linux和Video capture adaptes选上
进入Video capture adaptes,选中并进入V4L USB devices,选并中进入GSPCA based webcams,选中对应的摄像头驱动,如果是免驱的USB摄像头则勾选UVC即可
编译内核
#make uImage ARCH=arm CROSS_COMPILE=arm-linux-
拷贝uImage到/tftpboot/目录下
下载并启动linux内核:
往开发板上插入摄像头后,会弹出相应提示,同时使用ls /dev/可以查看是否有vedio的驱动文件,如果有则说明驱动开发成功。
2、V4L2编程
这部分转自http://blog.csdn.net/eastmoon502136/article/details/8190262
2.1 介绍
应用程序通过V4L2进行视频采集的原理
V4L2支持内存映射方式(mmap)和直接读取方式(read)来采集数据,前者一般用于连续视频数据的采集,后者常用于静态图片数据的采集,本文重点讨论内存映射方式的视频采集。
应用程序通过V4L2接口采集视频数据分为五个步骤:
首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;
其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;
第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;
第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;
第五,停止视频采集。
具体的程序实现流程可以参考下面的流程图:
其实其他的都比较简单,就是通过ioctl这个接口去设置一些参数。最主要的就是buf管理。他有一个或者多个输入队列和输出队列。
启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序接下来采集下一帧数据,放入第二个帧缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。
应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。
最后,应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集,如图所示。
每一个帧缓冲区都有一个对应的状态标志变量,其中每一个比特代表一个状态
V4L2_BUF_FLAG_UNMAPPED 0B0000
V4L2_BUF_FLAG_MAPPED 0B0001
V4L2_BUF_FLAG_ENQUEUED 0B0010
V4L2_BUF_FLAG_DONE 0B0100
缓冲区的状态转化如图所示。
下面的程序注释的很好,就拿来参考下:
2.2函数介绍
1. 定义
V4L2(Video ForLinux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。
2. 工作流程:
打开设备-> 检查和设置设备属性->设置帧格式-> 设置一种输入输出方法(缓冲区管理)-> 循环获取数据-> 关闭设备。
3. 设备的打开和关闭:
#include<fcntl.h>
int open(constchar *device_name, int flags);
#include <unistd.h>
int close(intfd);
例:
注意:V4L2 的相关定义包含在头文件<linux/videodev2.h>中.
4. 查询设备属性: VIDIOC_QUERYCAP
相关函数:
相关结构体:
例:显示设备信息
5. 帧格式:
例:显示所有支持的格式
// 查看或设置当前格式
VIDIOC_G_FMT,VIDIOC_S_FMT
// 检查是否支持某种格式
例:显示当前帧的相关信息
例:检查是否支持某种帧格式
6. 图像的缩放
// 设置缩放
7. 申请和管理缓冲区,应用程序和设备有三种交换数据的方法,直接read/write ,内存映射(memorymapping) ,用户指针。这里只讨论 memorymapping.
// 向设备申请缓冲区
例:申请一个拥有四个缓冲帧的缓冲区
获取缓冲帧的地址,长度:
VIDIOC_QUERYBUF
int ioctl(intfd, int request, struct v4l2_buffer *argp);
MMAP ,定义一个结构体来映射每个缓冲帧。
#include<sys/mman.h>
void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);
//addr 映射起始地址,一般为NULL ,让内核自动选择
//length 被映射内存块的长度
//prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE
//flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE
//fd,offset, 确定被映射的内存地址
返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);
int munmap(void*addr, size_t length);// 断开映射
//addr 为映射后的地址,length 为映射后的内存长度
例:将四个已申请到的缓冲帧映射到应用程序,用buffers 指针记录。
// 映射
8. 缓冲区处理好之后,就可以开始获取数据了
例:把四个缓冲帧放入队列,并启动数据流
例:获取一帧并处理
至于驱动的实现,可以参考内核中,我是用usb摄像头的,所以,其实现都是好的。主要就是应用程序的实现了。驱动都哦在uvc目录下面,这个待理解。
3、实例代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <getopt.h> #include <fcntl.h> #include <unistd.h>#include <errno.h>#include <malloc.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/time.h>#include <sys/mman.h>#include <sys/ioctl.h> #include <asm/types.h> #include <linux/videodev2.h> struct buffer { void * start; size_t length;}; struct buffer *buffers;unsigned long n_buffers;unsigned long file_length;int file_fd;char *dev_name = "/dev/video0";int fd;static int read_frame (void){ struct v4l2_buffer buf; /*帧出列*/ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl (fd, VIDIOC_DQBUF, &buf); write(file_fd,buffers[buf.index].start,buffers[buf.index].length); /*buf入列*/ ioctl(fd, VIDIOC_QBUF, &buf); return 1;} int main (int argc,char ** argv){ struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_requestbuffers req; struct v4l2_buffer buf; unsigned int i; enum v4l2_buf_type type; file_fd = open("test.jpg", O_RDWR | O_CREAT, 0777); fd = open (dev_name, O_RDWR | O_NONBLOCK, 0); /*获取驱动信息*/ ioctl (fd, VIDIOC_QUERYCAP, &cap); printf("Driver Name:%s\n Card Name:%s\n Bus info:%s\n\n",cap.driver,cap.card,cap.bus_info); /*设置图像格式*/ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 320; fmt.fmt.pix.height = 240; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; ioctl (fd, VIDIOC_S_FMT, &fmt) ; /*申请图像缓冲区*/ req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl (fd, VIDIOC_REQBUFS, &req); buffers = calloc (req.count, sizeof (*buffers)); for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { /*获取图像缓冲区的信息*/ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; ioctl (fd, VIDIOC_QUERYBUF, &buf); buffers[n_buffers].length = buf.length; // 把内核空间中的图像缓冲区映射到用户空间 buffers[n_buffers].start = mmap (NULL , //通过mmap建立映射关系 buf.length, PROT_READ | PROT_WRITE , MAP_SHARED , fd, buf.m.offset); } /*图像缓冲入队*/ for (i = 0; i < n_buffers; ++i) { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl (fd, VIDIOC_QBUF, &buf); } //开始捕捉图像数据 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl (fd, VIDIOC_STREAMON, &type); fd_set fds; FD_ZERO (&fds); FD_SET (fd, &fds); select(fd + 1, &fds, NULL, NULL, NULL); /*读取一幅图像*/ read_frame(); for (i = 0; i < n_buffers; ++i) munmap (buffers[i].start, buffers[i].length); close (fd); close (file_fd); printf("Camera Done.\n"); return 0;}
- USB摄像头驱动配置及V4L2编程
- linux下通过V4L2驱动USB摄像头
- 嵌入式Linux下Camera编程--V4L2 (V4L2内核驱动配置、USB转串配置)
- V4L2 usb 摄像头 测试
- V4L2 usb 摄像头 测试
- Android NDK基于V4L2驱动使用USB摄像头
- V4L2编程使用USB摄像头生成一张图片
- v4l2摄像头驱动
- V4L2摄像头驱动移植
- 【摄像头】V4L2编程
- V4L2 摄像头应用程序编程
- Linux摄像头V4L2编程
- LINUX USB 摄像头驱动
- Android驱动USB摄像头
- android驱动USB摄像头
- ok6410驱动usb摄像头
- USB摄像头驱动
- USB摄像头驱动2
- srs流媒体服务器 安装配置
- 什么是数据分析的关键指标?
- python入门系列4―——语句
- RocketMQ实战4
- 如何查看一个class文件,是jdk什么版本的编译器编译的?
- USB摄像头驱动配置及V4L2编程
- LCM UDP数据通信
- 分布式使用心得
- 应不应该收取房屋中介费用?房屋中介费用收取合理吗
- RocketMQ实战(三):分布式事务
- java获取对象属性类型、属性名称、属性值
- 矩阵的之字型遍历
- 极大似然法估计
- ios自动布局