http://blog.csdn.net/zhandoushi1982/article/details/6456440
一个简单的应用程序,来实现在LCD上显示当前camera的图像数据,也可以根据键盘输入保存摄像头数据到BMP图片中。
(1) 如下:
文件头和全局变量:
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/mman.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <asm/types.h>
- #include <linux/videodev2.h>
- #include <linux/fb.h>
- static unsigned int capframe = 0; //保存的图片计数
- static char filename[30]; //保存的图片文件名
- FILE *bmpFile; //保存图片时创建的文件指针
- unsigned char bmp_head_t[] = { //66字节的BMP位图头
- 0x42,0x4d,0x42,0x58,0x02,0x00,0x00,0x00,0x00,0x00,
- 0x42,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0xf0,0x00,
- 0x00,0x00,0x40,0x01,0x00,0x00,0x01,0x00,0x10,0x00,
- 0x03,0x00,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0xe0,0x07,
- 0x00,0x00,0x1f,0x00,0x00,0x00
- };
- static char *vf_buff; //从摄像头读取数据保存的内存指针
- static int fb_bpp; //帧缓冲设备的每像素位数
- unsigned char bmp_head[54]; //54字节的BMP位图头
- static int cam_fp = -1; //打开的摄像头句柄
- static int fb_fp = -1; //打开的帧缓冲句柄
- static char *fb_addr = NULL; //帧缓冲mmap的映射返回地址
- static char *cam_name = "/dev/video1"; //摄像头设备路径和名字
- int width=0; //帧缓冲的resolution width
- int height=0; //帧缓冲的resolution height
主函数如下:
- int main(int argc, char *argv[])
- {
- struct timeval tv1; //监控键盘输入的select时间参数
- unsigned long size; //一帧画面的字节数
- int index=0; //V4L2 input索引号
- struct v4l2_capability cap; //V4L2设备功能结构体变量
- struct v4l2_input i; //V4L2设备输入信息
- struct v4l2_framebuffer fb; //V4L2的一帧设备缓存
- int on=1; //控制V4L2设备overlay的参数
- int tmp;
- fd_set fds1; //监控键盘输入的select fd_set变量
- int fd; //监控键盘输入的select句柄
- char cmd[256]; //存储从键盘读入的字符串
- cam_fp = cam_init(); //获得摄像头句柄
- fb_fp = fb_init(); //获得帧缓冲句柄
- size=width*height*fb_bpp/8;
- if((tmp=ioctl(cam_fp, VIDIOC_QUERYCAP, &cap))<0) { //查询驱动功能
- printf("VIDIOC_QUERYCAP error, ret=0x%x\n",tmp);
- goto err;
- }
- printf("Driver:%s, Card:%s, cap=0x%x\n",cap.driver,cap.card,cap.capabilities);
- memset(&i, 0, sizeof(i));
- i.index=index;
- if(ioctl(cam_fp, VIDIOC_ENUMINPUT, &i)<0) //枚举输入源
- goto err;
- printf("input name:%s\n",i.name);
- index=0;
- if(ioctl(cam_fp, VIDIOC_S_INPUT, &index)<0) //设置输入源
- goto err;
- if(ioctl(cam_fp, VIDIOC_S_OUTPUT, &index)<0) //设置输出源
- goto err;
- if(ioctl(cam_fp, VIDIOC_G_FBUF, &fb)<0) //获取V4L2设备FB属性参数
- goto err;
- printf("g_fbuf:capabilities=0x%x,flags=0x%x,width=%d,height=%d\npixelformat=0x%x,bytesperline=%d,colorspace=%d,base=0x%x\n",
- fb.capability,fb.flags,fb.fmt.width,fb.fmt.height,fb.fmt.pixelformat,
- fb.fmt.bytesperline,fb.fmt.colorspace,fb.base);
- fb.capability = cap.capabilities; //V4L2设备功能赋值给V4L2的FB功能属性
- fb.fmt.width =width; //LCD的FB宽度赋值给V4L2的FB宽度
- fb.fmt.height = height; //LCD的FB高度赋值给V4L2的FB高度
- fb.fmt.pixelformat = (fb_bpp==32)?V4L2_PIX_FMT_BGR32:V4L2_PIX_FMT_RGB565; //赋值V4L2的FB像素位数
- if(ioctl(cam_fp, VIDIOC_S_FBUF, &fb)<0) //设置新的FB属性给摄像头
- goto err;
- on = 1;
- if(ioctl(cam_fp, VIDIOC_OVERLAY, &on)<0) //使能摄像头的overlay
- goto err;
- vf_buff = (char*)malloc(size);
- if(vf_buff==NULL)
- goto err;
- if(fb_bpp==16){ //16位BMP
- *((unsigned int*)(bmp_head_t+18)) = width;
- *((unsigned int*)(bmp_head_t+22)) = height;
- *((unsigned short*)(bmp_head_t+28)) = 16;
- }else{
- bmp_head[0] = 'B';
- bmp_head[1] = 'M';
- *((unsigned int*)(bmp_head+2)) = (width*fb_bpp/8)*height+54; //整个位图大小
- *((unsigned int*)(bmp_head+10)) = 54; //从54字节开始存图像
- *((unsigned int*)(bmp_head+14)) = 40; //图像描述信息块的大小
- *((unsigned int*)(bmp_head+18)) = width;
- *((unsigned int*)(bmp_head+22)) = height;
- *((unsigned short*)(bmp_head+26)) = 1; //图像的plane总数
- *((unsigned short*)(bmp_head+28)) = fb_bpp;
- *((unsigned short*)(bmp_head+34)) = (width*fb_bpp/8)*height; //图像数据区大小
- }
- while(1)
- {
- if (!read_data(cam_fp, vf_buff, width, height, fb_bpp)) //读摄像头数据到vf_buff
- {
- printf("read error\n");
- }
- memcpy(fb_addr,vf_buff,width*height*fb_bpp/8); //将读到的图像数据从内存拷贝到帧缓冲地址
- fd=0; //键盘句柄
- tv1.tv_sec=0;
- tv1.tv_usec=0; //无限等待
- FD_ZERO(&fds1);
- FD_SET(fd,&fds1); //绑定句柄跟监控对象
- select(fd+1,&fds1,NULL,NULL,&tv1); //监控键盘输入
- if(FD_ISSET(fd,&fds1)) //如果键盘有输入
- {
- memset(cmd,0,sizeof(cmd));
- read(fd,cmd,256); //读取键盘输入的字符
- if(strncmp(cmd,"quit",4)==0){ //如果键盘输入quit
- printf("-->quit\n");
- on=0;
- if(ioctl(cam_fp, VIDIOC_OVERLAY, &on)<0) //关掉V4L2设备的overlay
- goto err;
- close(fb_fp);
- close(cam_fp); //释放FB跟摄像头的句柄
- return 0;
- }else if(strncmp(cmd,"capt",4)==0){ //如果键盘输入capt
- printf("-->capture\n");
- printf("write to img --> ");
- writeImageToFile(size); //把FB数据保存到位图中
- printf("OK\n");
- }
- }
- }
- err:
- if (cam_fp)
- close(cam_fp);
- if (fb_fp)
- close(fb_fp);
- return 1;
- }
(2)子函数叙述如下。FB和摄像头的初始化如下:
- static inline int fb_init(void)
- {
- int dev_fp = -1;
- int fb_size;
- struct fb_var_screeninfo vinfo;
- dev_fp = open("/dev/graphics/fb0", O_RDWR);
- if (dev_fp < 0) {
- perror("/dev/fb0");
- return -1;
- }
- if (ioctl(dev_fp, FBIOGET_VSCREENINFO, &vinfo)) { //获取FB基本信息
- printf("Error reading variable information.\n");
- exit(1);
- }
- width=vinfo.xres;
- height=vinfo.yres;
- fb_bpp=vinfo.bits_per_pixel; //将FB值赋值给全局变量
- if(fb_bpp==24) fb_bpp=32;
- fb_size=width*height*fb_bpp/8; //一帧FB的字节大小
- if ((fb_addr = (char*)mmap(0, fb_size,
- PROT_READ | PROT_WRITE, MAP_SHARED, dev_fp, 0)) < 0) { //从文件的0位置开始在内存中自动映射fb_size大小空间
- perror("mmap()");
- return -1;
- }
- printf("%dx%d bpp:%d mmaped 0x%08x\n",width,height,fb_bpp,fb_addr);
- return dev_fp;
- }
- static inline int cam_init(void)
- {
- int dev_fp = -1;
- dev_fp = open(cam_name, O_RDWR);
- if (dev_fp < 0) {
- printf("error open %s\n",cam_name);
- return -1;
- }
- return dev_fp;
- }
从摄像头读数据到内存中:
- static inline int read_data(int fp, char *buf, int width, int height, int bpp)
- {
- int ret=0;
- if ((ret = read(fp, buf, (width*height*bpp/8))) != (width*height*bpp/8)) {
- printf("read %d--0x%x\n",ret,ret);
- return 0;
- }
- return ret;
- }
把内存中图像存储成BMP
- void writeImageToFile(unsigned int size)
- {
- capframe++; //图像序号计数
- sprintf(filename,"/tmp/0%d.bmp",capframe); //位图的文件名
- bmpFile=fopen(filename, "w+"); //可写可追加内容的方式创建文件
- if(fb_bpp == 16)
- fwrite(bmp_head_t,1,66,bmpFile);
- else
- fwrite(bmp_head,1,54,bmpFile); //往文件中写入BMP位图头
- fwrite(vf_buff,1,size,bmpFile); //在继续往文件中追加位图数据
- fclose(bmpFile);
- }
(3)使用的makefile如下:
- CROSS_COMPILE = /home/zhangcheng/gcc/arm-2008q3/bin/arm-linux-
- CC = $(CROSS_COMPILE)gcc
- cam2fb: cam2fb.c
- $(CC) -o cam2fb cam2fb.c -static
执行make编译后,看到属性是不可执行属性,所以先将其属性改成可执行。通过adb将这个可执行文件拷贝入/data内,先修改其属性后,再用./执行即可。过一会儿,即可在LCD上显示camera捕获的动态图像,同时adb显示如下:
(4)该程序还可以实现输入键盘字符quit退出,输入键盘字符capt保存当前图片到某个目前下成一个BMP,由于没有键盘输入,该部分无法验证。