直白解读linux下V4L2拍照
来源:互联网 发布:linux informix 目录 编辑:程序博客网 时间:2024/06/05 20:57
声明:
环境:linux或ubunt下
编辑器:vim
编译工具:gcc
设备:USB接口的摄像头
难点理解:
1.对于缓冲帧的解释:假设我们申请5个缓冲帧,那么这5个缓冲帧就相当于5个盘子,操作系统在内存中开辟5个缓存区队列来存放这5个盘子,当相机拍照后将数据放入盘子中,我们通过VIDIOC_DQBUF取数据可以理解为将盘子取出来,然后对数据操作,操作完成之后,要通过VIDIOC_QBUF将盘子放回去,以保证可以循环接收数据。
2.对于映射的解释:对于映射,可以理解为cpu在处理时会给你分配几个空间,然后把这几个空间的首地址返回给你,如果你有数据,那么他就会把数据放到这个空间内。
程序源码:(包含详细注释)
/* @sun : 2017-8-18 email: 13045892482@163.com csdn : Mr_sunp abstract : under linux,Get an image with v4l2. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <linux/videodev2.h>#include <sys/ioctl.h>#include <sys/mman.h>#define NB_BUFFER 5#define DBG_DIR "jpg/"#define VIDEO_L_MASK 0x01#define VIDEO_R_MASK 0x02#if 1/* 头文件 :<linux/videodev2.h> 函数 :int ioctl(intfd, int request, struct v4l2_capability *argp); struct v4l2_capability 设备的功能 { __u8 driver[16]; // 驱动名字 __u8 card[32]; // 设备名字 __u8 bus_info[32]; // 设备在系统中的位置 __u32 version; // 驱动版本号 __u32 capabilities;// 设备支持的操作 __u32 reserved[4]; // 保留字段 }; VIDIOC_QUERYCAP //查询驱动功能 *//* fd:文件描述符 */int showCapability(int fd){ printf("-----------------------设备信息---------------------\n");struct v4l2_capability cap; if(ioctl(fd,VIDIOC_QUERYCAP,&cap) < 0) { perror("error ioctl VIDIOC_QUERYCAP !"); return -1; } printf("DriverName :%s\nCard Name :%s\nBus info :%s\nDriverVersion:%u.%u.%u\n" ,cap.driver ,cap.card ,cap.bus_info ,(cap.version>>16)&0xff ,(cap.version>>8)&0xff ,cap.version&0xff); return 0;}#endif#if 1/* 函数 :int ioctl(intfd, int request, struct v4l2_fmtdesc *argp); struct v4l2_fmtdesc { __u32 index; // 要查询的格式序号,应用程序设置 enum v4l2_buf_type type; // 帧类型,应用程序设置 __u32 flags; // 是否为压缩格式 __u8 description[32]; // 格式名称 __u32 pixelformat; // 格式 __u32 reserved[4]; // 保留 }; VIDIOC_ENUM_FMT //指令含义:获取当前驱动支持的视频格式 *//* fd:文件描述符 */int showFmtdesc(int fd){ printf("-----------------------支持格式---------------------\n"); struct v4l2_fmtdesc dis_fmtdesc; dis_fmtdesc.index=0; dis_fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("Supportformat:\n"); while(ioctl(fd,VIDIOC_ENUM_FMT,&dis_fmtdesc)!=-1) { printf("\t%d.%s\n",dis_fmtdesc.index+1,dis_fmtdesc.description); dis_fmtdesc.index++; } return 0;}#endif#if 1/* 函数 :int ioctl(intfd, int request, struct v4l2_format *argp); struct v4l2_format 帧的格式 { enum v4l2_buf_type type; //帧类型,应用程序设置 union fmt { structv4l2_pix_format pix; //视频设备使用 structv4l2_window win; structv4l2_vbi_format vbi; structv4l2_sliced_vbi_format sliced; __u8raw_data[200]; }; }; VIDIOC_G_FMT: //指令含义:读取当前驱动的捕获格式 VIDIOC_S_FMT: //指令含义:设置当前驱动的捕获格式 VIDIOC_ENUM_FMT: //指令含义:获取当前驱动支持的视频格式 *//* fd:文件描述符 */int showFormat(int fd){ printf("------------------------帧信息----------------------\n"); struct v4l2_format dis_fmt; dis_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd,VIDIOC_G_FMT,&dis_fmt); printf("Currentdata format information:\n\twidth:%d\n\theight:%d\n" ,dis_fmt.fmt.pix.width ,dis_fmt.fmt.pix.height); struct v4l2_fmtdesc fmtdesc; fmtdesc.index=0; fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1) { if(fmtdesc.pixelformat & dis_fmt.fmt.pix.pixelformat) { printf("\tformat:%s\n",fmtdesc.description); break; } fmtdesc.index++; } return 0;}#endif#if 1/* V4L2_FIELD_ANY //指令含义: V4L2_PIX_FMT_MJPEG //指令含义:MJPEG格式 V4L2_PIX_FMT_UYVY //指令含义:YUYV格式 VIDIOC_S_PARM //指令格式:检查格式和设置 *//* fd : 文件描述符 w :宽 h:高 */int setFormat(int fd ,int w,int h){ printf("--------------------设置当前格式--------------------\n"); struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .fmt = { .pix = { .width = w, //帧宽 .height = h, //帧高 .pixelformat = V4L2_PIX_FMT_MJPEG, //帧格式 .field = V4L2_FIELD_ANY, } }, }; //检查格式 和 设置是否成功 if(ioctl(fd,VIDIOC_S_PARM,&fmt) < 0){ perror("error ioctl VIDIOC_S_PARM !"); return -1; }else{ printf("set image size:\n\twidth = %d\n\theight = %d\n",w,h); } return 0;}#endif#if 1/* fd :文件描述符 fps:帧率 numerator:这里固定为1 */int setFps(int fd ,int fps){ printf("--------------------设置当前帧率--------------------\n"); struct v4l2_streamparm setfps = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .parm = { .capture = { .timeperframe = { .numerator=1, .denominator = fps, //帧率时间长度为 = numerator/denominator } } }, };if (ioctl(fd, VIDIOC_S_PARM, &setfps) < 0) { perror("error SETFPS failed !"); return -1; }else{ printf("\tset fps : %d\n",fps); } return 0;}#endif#if 1/* fd :文件描述符 val:曝光值 */int setExposure(int fd,int val){ printf("--------------------设置曝光模式--------------------\n"); struct v4l2_control con_setup = { //设置手动曝光 .id = V4L2_CID_EXPOSURE_AUTO, .value = V4L2_EXPOSURE_MANUAL, }; if(ioctl(fd,VIDIOC_S_CTRL,&con_setup)<0){ perror("set exposure failed !"); } struct v4l2_control con_set = { //设置曝光绝对值 .id = V4L2_CID_EXPOSURE_ABSOLUTE, .value = val, }; if(ioctl(fd,VIDIOC_S_CTRL,&con_set)<0){ perror("set exposure failed !"); }else{ printf("set exposure\n\tmode : 手动\n\tval : %d\n",val); } return 0;}#endif#if 1/* fd :文件描述符 val:焦距 */int setFocus(int fd ,int val){ printf("--------------------设置对焦模式--------------------\n"); struct v4l2_control con_setup = { //关闭自动对焦 .id = V4L2_CID_FOCUS_AUTO, .value = 0, }; if(ioctl(fd,VIDIOC_S_CTRL,&con_setup) < 0){ perror("focus set failed !"); }else{ printf("focus off;\n"); } struct v4l2_control con_set = { //设置焦点值 .id = V4L2_CID_FOCUS_ABSOLUTE, .value = val, }; if(ioctl(fd,VIDIOC_S_CTRL,&con_set) < 0){ perror("focus set failed !"); }else{ printf("focus on;\n\tval : %d\n",val); } return 0;}#endif#if 1/* 申请和管理缓冲区,应用程序和设备有三种交换数据的方法,直接read/write ,内存映射(memorymapping) ,用户指针。这里只讨论 memorymapping. 函数 :int ioctl(intfd, int request, struct v4l2_requestbuffers *argp); struct v4l2_requestbuffers 申请帧缓冲 { __u32 count; // 缓冲区内缓冲帧的数目 enum v4l2_buf_type type;// 缓冲帧数据格式 enum v4l2_memorymemory; // 区别是内存映射还是用户指针方式 __u32 reserved[2]; }; VIDIOC_REQBUFS //指令含义:分配内存 *//* fd :文件描述符 */int requestBuffers(int fd){ printf("---------------------申请缓冲区---------------------\n"); struct v4l2_requestbuffers rb = { .count = NB_BUFFER, //缓冲帧个数 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,//数据类型:流类型,永远都是 .memory = V4L2_MEMORY_MMAP, //存储类型:V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR }; if (ioctl(fd, VIDIOC_REQBUFS, &rb) < 0){ perror("error VIDIOC_REQBUFS !"); return -1; } printf("count : %d\n",NB_BUFFER); return 0;}#endif#if 1/* 函数 :int ioctl(intfd, int request, struct v4l2_buffer *argp); struct v4l2_buffer { __u32 index; //buffer 序号 enum v4l2_buf_type type; //buffer 类型 __u32 byteused; //buffer 中已使用的字节数 __u32 flags; //区分是MMAP 还是USERPTR enum v4l2_fieldfield; struct timeval timestamp; //获取第一个字节时的系统时间 struct v4l2_timecode timecode; __u32 sequence; //队列中的序号 enum v4l2_memory memory; //IO 方式,被应用程序设置 union m { __u32 offset; //缓冲帧地址,只对MMAP 有效 unsigned longuserptr; }; __u32 length; //缓冲帧长度 __u32 input; __u32 reserved; }; VIDIOC_QUERYBUF //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址 *//* MMAP:定义一个结构体来映射每个缓冲帧 */struct buffer{ void *start; unsigned int length;} *buffers;/* #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 为映射后的内存长度 *//* fd :文件描述符 addr:二级指针,用来存放开辟的buffers地址 */int getBufLenAndAddr(int fd,struct buffer **addr){ printf("-----------------获取缓冲区地址,长度---------------\n"); buffers = (struct buffer*)calloc(NB_BUFFER,sizeof(*buffers)); *addr = buffers; //记录开辟的空间首地址,便于free if(!buffers){ //将n_buffers个以申请到的缓冲帧映射到引用程序,用buffers指针记录 perror("error calloc failed !"); return -1; } unsigned int n_buffers; for(n_buffers=0;n_buffers<NB_BUFFER;++n_buffers){ //循环映射 struct v4l2_buffer buf; memset(&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; // 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小 if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){ perror("error VIDIOC_QUERYBUF failed !"); return -1; } buffers[n_buffers].length = buf.length; //映射内存 buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ,MAP_SHARED,fd,buf.m.offset); if (MAP_FAILED == buffers[n_buffers].start){ //MAP_FAILED判断返回是否错误 perror("error mmap failed !"); return -1; } } return 0;}#endif#if 1/* VIDIOC_STREAMON 指令含义:开始采集 VIDIOC_STREAMOFF 指令含义:结束采集 *//* fd :文件描述符 */int putQueue(int fd){ printf("-----------------将申请到的缓冲帧放入队-------------\n"); unsigned int i; for(i=0;i<NB_BUFFER;i++){ struct v4l2_buffer buf; buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory =V4L2_MEMORY_MMAP; buf.index = i; if(ioctl(fd,VIDIOC_QBUF, &buf) < 0) { perror("error VIDIOC_QBUF failed !"); return -1; }; } return 0;}#endif#if 1/* fd :文件描述符 */int videoOn(int fd){ printf("--------------------开始采集数据-------------------\n"); enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int temp = ioctl(fd,VIDIOC_STREAMON,&type); if(temp < 0){ perror("video on failed !\n"); } return 0;}#endif#if 1/* fd :文件描述符 */int handleDate(int fd){ printf("--------------------获取一帧并处里------------------\n"); struct v4l2_buffer buf; memset(&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl (fd,VIDIOC_DQBUF, &buf); // 从缓冲区取出一个缓冲帧char jpgName[32]; sprintf(jpgName,DBG_DIR "%03d.jpg",buf.index); FILE *fSource = fopen(jpgName,"wb"); fwrite(buffers[buf.index].start,1,(size_t)buf.bytesused,fSource); fclose(fSource); ioctl (fd, VIDIOC_QBUF,&buf); // 将取出的缓冲帧放回缓冲区 }#endif#if 1/* fd :文件描述符 */int videoOff(int fd){ printf("----------------------结束采集数据------------------\n"); int type_off = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(ioctl(fd,VIDIOC_STREAMOFF,&type_off)){ perror("error STREAMOFF failed"); return -1; } return 0;}#endifint main(int argc, char *argv[]){ int fd; struct buffer *ptr; int i; if((fd = open(argv[1], O_RDWR)) == -1) { perror("error open !"); return -1; } mkdir(DBG_DIR,0777); showCapability(fd); showFmtdesc(fd); showFormat(fd); setFormat(fd,800,600); setFps(fd,60); setExposure(fd,12);// setFocus(fd,12); 焦距的设置因摄像头而定,如果不支持这去掉这个为默认状态 requestBuffers(fd); getBufLenAndAddr(fd,&ptr); putQueue(fd); videoOn(fd); handleDate(fd); videoOff(fd); free(ptr); close(fd); return 0;}
阅读全文
1 0
- 直白解读linux下V4L2拍照
- 直白解读linux下pthread的简单操作
- Linux下V4L2拍照测试用例
- Linux下V4L2拍照测试用例
- Linux下V4L2拍照测试用例
- Linux下V4L2一个调试问题方法(拍照偏绿)
- Linux下使用V4L2读取获取拍照(获取静态图像)
- Linux下V4L2一个调试问题方法(拍照偏绿)
- Linux下的V4L2编程
- Linux下V4L2编程小结
- Linux下V4L2编程小结
- linux 下录音机驱动(v4l2)
- linux下V4L2开发流程
- Linux下Camera编程--V4L2
- linux 下的v4l2运行
- V4L2拍照程序
- 直白
- 解读linux下/proc
- 梯度下降法
- 关于Android5.0 onActivityResult 的data为null 的处理方案
- sql server 2008中 代理找不到
- 一个大型MySQL分布式系统诞生
- 数据库gbk乱码问题
- 直白解读linux下V4L2拍照
- Piwik
- Mysql----Group By的使用
- http基础(一)
- python中的socket模块
- jconsole或jvisualvm监控远程weblogic应用JVM内存情况
- 开源飞控OpenPilot的扩展卡尔曼滤波(EKF)学习笔记
- 企业级 Office工具包Spire.Office V2.16.12发布 | 附下载
- PHP优化杂烩