C语言高级应用---操作linux下V4L2摄像头应用程序
来源:互联网 发布:和平网络电视手机破解 编辑:程序博客网 时间:2024/06/05 15:22
我们都知道,想要驱动linux下的摄像头,其实很简单,照着V4L2的手册一步步来写,很快就可以写出来,但是在写之前我们要注意改变系统的一些配置,使系统支持framebuffer,在dev下产生fb0这样的节点,这样我们才能在linux系统上操作Camera摄像头,framebuffer在之前的博文已经有说过了,这里就不再提了。
有需要了解framebuffer的那么请点击:http://baike.baidu.com/view/3351639.htm
最重要的,我们需要改一个脚本,在/dev/grub.conf,我们来看看怎么改:
# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to this file# NOTICE: You have a /boot partition. This means that# all kernel and initrd paths are relative to /boot/, eg.# root (hd0,0)# kernel /vmlinuz-version ro root=/dev/sdb2# initrd /initrd-[generic-]version.img#boot=/dev/sdbdefault=0timeout=5splashimage=(hd0,0)/grub/splash.xpm.gzhiddenmenutitle CentOS (2.6.32-431.el6.i686)root (hd0,0)kernel /vmlinuz-2.6.32-431.el6.i686 ro root=UUID=2bc12537-d6c1-4e67-b4e5-e9c466205554 nomodeset rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=auto LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet vga=0x318initrd /initramfs-2.6.32-431.el6.i686.img
通常情况下,要让framebuffer生效,要加一句vga=???(这里是参数),简单介绍一下:
我写vga=0x318就是默认就设置为1024x768x24bpp模式。当然还有其它的模式:如下图,根据自己的系统来配置。
色彩640x400640x480800x6001024x7681280x10241600x12004bits??0x302???8bits0x3000x3010x3030x3050x3070x31C15bits?0x3100x3130x3160x3190x31D16bits?0x3110x3140x3170x31A0x31E24bits?0x3120x3150x3180x31B0x31F32bits??????配置完成以后,我们先来了解一下V4L2的主要功能。
V4L2就使程序有发现设备和操作设备的能力.它主要是用一系列的回调函数来实现这些功能。像设置摄像头的频率、帧频、视频压缩格式和图像参数等等。当然也可以用于其他多媒体的开发,如音频等。
但是此框架只能运行在Linux操作系统之上。v4L2是针对uvc免驱usb设备的编程框架 ,主要用于采集usb摄像头等,编程模式如下:
采集方式
打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
extern int ioctl (int __fd, unsigned long int __request, …) __THROW;__fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;__request:具体的命令标志符。在进行V4L2开发中,一般会用到以下的命令标志符:VIDIOC_REQBUFS:分配内存VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址VIDIOC_QUERYCAP:查询驱动功能VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式VIDIOC_S_FMT:设置当前驱动的频捕获格式VIDIOC_G_FMT:读取当前驱动的频捕获格式VIDIOC_TRY_FMT:验证当前驱动的显示格式VIDIOC_CROPCAP:查询驱动的修剪能力VIDIOC_S_CROP:设置视频信号的边框VIDIOC_G_CROP:读取视频信号的边框VIDIOC_QBUF:把数据放回缓存队列VIDIOC_DQBUF:把数据从缓存中读取出来VIDIOC_STREAMON:开始视频显示函数VIDIOC_STREAMOFF:结束视频显示函数VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。这些IO调用,有些是必须的,有些是可选择的。
V4L2操作流程:点击这个网址,说得很详细了,这里不多说。
http://baike.baidu.com/view/5494174.htm
接下来我们来看看实战部分,下面是我自己写的程序接口,可以实现视频采集:
1、project.c
#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include "j-yuv.h"#include "CameralOpt.h"#include "FrameBufferOpt.h"#define WIDTH 640#define HIGHT 480int main(void){char yuyv[WIDTH*HIGHT*2];char bmp[WIDTH*HIGHT*3];//set_bmp_header((struct bmp_header_t *)bmp, WIDTH, HIGHT);//初始化摄像头Init_Cameral(WIDTH , HIGHT );//初始化framebufferInit_FrameBuffer(WIDTH , HIGHT ); //开启摄像头Start_Cameral();//采集一张图片int count = 0 ; while(1){Get_Picture(yuyv);yuyv2rgb24(yuyv, bmp, WIDTH, HIGHT);Write_FrameBuffer(bmp);//printf("count:%d \n" , count++);}//关闭摄像头Stop_Cameral();//关闭FramebufferExit_Framebuffer();//退出Exit_Cameral();return 0;}2、juv.h
#ifndef __JYUV_H#define __JYUV_Htypedef unsigned char u8;typedef unsigned short u16;typedef unsigned int u32;#pragma pack(1)//定义bmp头struct bmp_header_t{ u16 magic; u32 file_size; u32 RESERVED1; u32 offset; //54 bytes 表示54个偏移量 u32 head_num; //40 u32 width; u32 height; u16 color_planes; //1 u16 bit_count; u32 bit_compression; //0 u32 image_size; //except the size of header u32 h_resolution; u32 v_resolution; u32 color_num; u32 important_colors;};#pragma pack()void set_bmp_header(struct bmp_header_t * header, u32 width, u32 height);int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height);#endif /* __JYUV_H */3、juv.c
#include "j-yuv.h"#define BIT_COUNT 24void set_bmp_header(struct bmp_header_t *header, u32 width, u32 height){ header->magic = 0x4d42; header->image_size = width * height * BIT_COUNT/8; header->file_size = header->image_size + 54; header->RESERVED1 = 0; header->offset = 54; header->head_num = 40; header->width = width; header->height = height; header->color_planes = 1; header->bit_count = BIT_COUNT; header->bit_compression = 0; header->h_resolution = 0; header->v_resolution = 0; header->color_num = 0; header->important_colors = 0;}//yuyv转rgb24的算法实现int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height){ u32 i, in, rgb_index = 0; u8 y0, u0, y1, v1; int r, g, b; u32 out = 0, x, y; for(in = 0; in < width * height * 2; in += 4) {y0 = yuyv[in+0];u0 = yuyv[in+1];y1 = yuyv[in+2];v1 = yuyv[in+3];for (i = 0; i < 2; i++){if (i)y = y1;elsey = y0;r = y + (140 * (v1-128))/100; //rg = y - (34 * (u0-128))/100 - (71 * (v1-128))/100; //g b = y + (177 * (u0-128))/100; //bif(r > 255) r = 255; if(g > 255) g = 255; if(b > 255) b = 255; if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0;y = height - rgb_index/width -1;x = rgb_index%width;rgb[(y*width+x)*3+0] = b;rgb[(y*width+x)*3+1] = g;rgb[(y*width+x)*3+2] = r;rgb_index++;} } return 0;}4、FrameBufferOpt.c
#include "FrameBufferOpt.h"static int Frame_fd ; static int *FrameBuffer = NULL ; static int W , H ;//初始化framebufferint Init_FrameBuffer(int Width , int Higth){W = Width ; H = Higth ; Frame_fd = open("/dev/fb" , O_RDWR);if(-1 == Frame_fd){perror("open frame buffer fail");return -1 ; }//根本就不用CPU搬运 用DMA做为搬运工FrameBuffer = mmap(0, 1280*1024*4 , PROT_READ | PROT_WRITE , MAP_SHARED , Frame_fd ,0 );if(FrameBuffer == (void *)-1){perror("memory map fail");return -2 ;}return 0 ; }//写入framebufferint Write_FrameBuffer(const char *buffer){int row , col ; char *p = NULL ; for(row = 0 ; row <1024 ; row++){for(col = 0 ; col < 1280 ; col++){if((row < H) && (col < W)){p = (char *)(buffer + (row * W+ col ) * 3); FrameBuffer[row*1280+col] = RGB((unsigned char)(*(p+2)),(unsigned char)(*(p+1)),(unsigned char )(*p));}}}return 0 ; }//退出framebufferint Exit_Framebuffer(void){munmap(FrameBuffer , W*H*4);close(Frame_fd);return 0 ; }5、FrameBufferOpt.h
#ifndef _FRAMEBUFFEROPT_H#define _FRAMEBUFFEROPT_H#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h>#define RGB(r,g,b)((r<<16)|(g<<8)|b)//初始化ramebufferint Init_FrameBuffer(int Width , int Higth);//写数据到framebufferint Write_FrameBuffer(const char *buffer);//退出framebufferint Exit_Framebuffer(void);#endif //_FRAMEBUFFEROPT_H6、CameralOpt.h
#ifndef _CAMERALOPT_H#define _CAMERALOPT_H#include <stdio.h>#include <linux/videodev2.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <errno.h>#include <sys/mman.h>#define COUNT 3//初始化摄像头int Init_Cameral(int Width , int Hight);int Exit_Cameral(void); //退出摄像头//摄像头开始采集int Start_Cameral(void);int Stop_Cameral(void);//停止摄像头//获取摄像头的数据int Get_Picture(char *buffer);#endif //_CAMERALOPT_H7、CameralOpt.c
#include "CameralOpt.h"int video_fd ; int length ; char *yuv[COUNT] ; struct v4l2_buffer enqueue , dequeue ; //定义出入队的操作结构体成员int Init_Cameral(int Width , int Hight){//参数检查char *videodevname = NULL ; videodevname = "/dev/video0" ; //打开设备video_fd = open(videodevname , O_RDWR);if(-1 == video_fd ){perror("open video device fail");return -1 ; }int i ; int ret ; struct v4l2_format format ; format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; format.fmt.pix.width = Width; format.fmt.pix.height = Hight; format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV ; //我支持的格式是这个ret = ioctl(video_fd , VIDIOC_S_FMT , &format);if(ret != 0){perror("set video format fail");return -2 ; }//申请buffer,切割成几个部分//3struct v4l2_requestbuffers requestbuffer ; requestbuffer.count = COUNT ; requestbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; requestbuffer.memory = V4L2_MEMORY_MMAP ; ret = ioctl(video_fd , VIDIOC_REQBUFS , &requestbuffer);if(ret != 0){perror("request buffer fail ");return -3 ;}//querybufferstruct v4l2_buffer querybuffer ; querybuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; querybuffer.memory = V4L2_MEMORY_MMAP ; for(i = 0 ; i < COUNT ; i++){querybuffer.index = i ; ret = ioctl(video_fd , VIDIOC_QUERYBUF , &querybuffer);if(ret != 0){perror("query buffer fail");return -4 ; }//printf("index:%d length:%d offset:%d \n" , //querybuffer.index , querybuffer.length , querybuffer.m.offset);length = querybuffer.length ; //将摄像头内存印射到进程的内存地址yuv[i] = mmap(0,querybuffer.length , PROT_READ | PROT_WRITE , MAP_SHARED , video_fd , querybuffer.m.offset );//列队struct v4l2_buffer queuebuffer ; queuebuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; queuebuffer.memory = V4L2_MEMORY_MMAP ; queuebuffer.index = i ; ret = ioctl(video_fd , VIDIOC_QBUF , &queuebuffer);if(ret != 0){perror("queuebuffer fail");return -5 ; }}//初始化入队出队enqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; enqueue.memory = V4L2_MEMORY_MMAP ; dequeue.memory = V4L2_MEMORY_MMAP ; return 0 ; }int Exit_Cameral(void){int i ; for(i = 0 ; i < COUNT ; i++)munmap(yuv+i , length);close(video_fd);return 0 ; }int Start_Cameral(void){//开启摄像头int ret ; int on = 1 ; ret = ioctl(video_fd , VIDIOC_STREAMON , &on);if(ret != 0){perror("start Cameral fail");return -1 ; }return 0 ; }int Stop_Cameral(void){//停止摄像头int ret ; int off= 1 ; ret = ioctl(video_fd , VIDIOC_STREAMOFF, &off);if(ret != 0){perror("stop Cameral fail");return -1 ; }return 0 ;}int Get_Picture(char *buffer){int ret ; //出队ret = ioctl(video_fd , VIDIOC_DQBUF , &dequeue);if(ret != 0){perror("dequeue fail");return -1 ; }//获取图片数据 YUV yuv[dequeue.index]memcpy(buffer , yuv[dequeue.index] , dequeue.length);//write(yuyv_fd , yuv[dequeue.index] , dequeue.length);enqueue.index = dequeue.index ; ret = ioctl(video_fd , VIDIOC_QBUF , &enqueue);if(ret != 0){perror("enqueue fail");return -2 ; }return 0 ; }
运行结果:楼主本人,长得丑别喷。
画面其实是一直在动的,只是我拍了一张图片而已。
4 0
- C语言高级应用---操作linux下V4L2摄像头应用程序
- linux之V4L2摄像头应用流程
- linux学习之 V4L2的摄像头应用
- linux之V4L2摄像头应用流程
- linux之V4L2摄像头应用流程
- linux之V4L2摄像头应用流程
- linux之V4L2摄像头应用流程
- V4L2 摄像头应用程序编程
- LINUX下 V4L2 摄像头设备初始化问题
- Linux下 摄像头 V4L2曝光时间控制
- linux下通过V4L2驱动USB摄像头
- 全志R16下通过V4L2直接获取摄像头的灰度图的C语言程序
- V4L2摄像头应用流程
- V4L2摄像头应用流程
- Linux V4L2摄像头采集
- Linux摄像头V4L2编程
- 和菜鸟一起学linux之V4L2摄像头应用流程
- 和菜鸟一起学linux之V4L2摄像头应用流程
- 第五周项目3——(2)时间类
- 快捷键的使用!!!
- APK反编译
- 数据库设计Step by Step (10)——范式化
- max * max
- C语言高级应用---操作linux下V4L2摄像头应用程序
- 树莓派 vnc设置
- 对象与对象
- 总结安装VMware和centOS6.5的相关问题。
- 数据库设计Step by Step (11)——通用设计模式(系列完结篇)
- mybatis----关系篇
- 关于Cursor的moveToFirst和moveToNext的意义
- 关于String类型常亮进行“==” 和equal()比较的不同
- 算法实现:字符串的所有排列情况