Linux的帧缓冲(Frame Buffer)之三:LCD上显示摄像头

本文介绍了一个简单的应用程序,用于实现在LCD上显示当前camera的图像数据,并可根据键盘输入保存摄像头数据到BMP图片中。该程序使用了V4L2和FB设备进行数据交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个简单的应用程序,来实现在LCD上显示当前camera的图像数据,也可以根据键盘输入保存摄像头数据到BMP图片中。

(1) 如下:

         文件头和全局变量:

  1. #include <stdio.h> 
  2. #include <string.h> 
  3. #include <stdlib.h> 
  4. #include <fcntl.h> 
  5. #include <unistd.h> 
  6. #include <ctype.h> 
  7. #include <errno.h> 
  8. #include <sys/mman.h> 
  9. #include <sys/time.h> 
  10. #include <sys/ioctl.h> 
  11. #include <asm/types.h>  
  12. #include <linux/videodev2.h> 
  13. #include <linux/fb.h> 
  14.  
  15. static unsigned int capframe = 0;       //保存的图片计数 
  16. static char filename[30];       //保存的图片文件名 
  17. FILE *bmpFile;                  //保存图片时创建的文件指针 
  18.  
  19. unsigned char bmp_head_t[] = {          //66字节的BMP位图头 
  20.          0x42,0x4d,0x42,0x58,0x02,0x00,0x00,0x00,0x00,0x00, 
  21.         0x42,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0xf0,0x00, 
  22.         0x00,0x00,0x40,0x01,0x00,0x00,0x01,0x00,0x10,0x00, 
  23.         0x03,0x00,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x00, 
  24.         0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
  25.         0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0xe0,0x07, 
  26.         0x00,0x00,0x1f,0x00,0x00,0x00 
  27. }; 
  28. static char *vf_buff;       //从摄像头读取数据保存的内存指针 
  29. static int fb_bpp;              //帧缓冲设备的每像素位数 
  30. unsigned char bmp_head[54];         //54字节的BMP位图头 
  31.  
  32. static int cam_fp = -1;         //打开的摄像头句柄 
  33. static int fb_fp = -1;          //打开的帧缓冲句柄 
  34. static char *fb_addr = NULL;        //帧缓冲mmap的映射返回地址 
  35. static char *cam_name = "/dev/video1"//摄像头设备路径和名字 
  36. int width=0;                //帧缓冲的resolution width 
  37. int height=0;               //帧缓冲的resolution height 
#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

          主函数如下:

  1. int main(int argc, char *argv[]) 
  2.     struct timeval tv1;     //监控键盘输入的select时间参数 
  3.     unsigned long size;     //一帧画面的字节数 
  4.     int index=0;            //V4L2 input索引号 
  5.     struct v4l2_capability cap;     //V4L2设备功能结构体变量 
  6.     struct v4l2_input i;        //V4L2设备输入信息 
  7.     struct v4l2_framebuffer fb;     //V4L2的一帧设备缓存 
  8.     int on=1;               //控制V4L2设备overlay的参数 
  9.     int tmp; 
  10.     fd_set fds1;            //监控键盘输入的select fd_set变量 
  11.     int fd;             //监控键盘输入的select句柄 
  12.     char cmd[256];          //存储从键盘读入的字符串 
  13.      
  14.     cam_fp = cam_init();        //获得摄像头句柄 
  15.     fb_fp = fb_init();          //获得帧缓冲句柄 
  16.      
  17.     size=width*height*fb_bpp/8; 
  18.  
  19.     if((tmp=ioctl(cam_fp, VIDIOC_QUERYCAP, &cap))<0) {       //查询驱动功能 
  20.         printf("VIDIOC_QUERYCAP error, ret=0x%x\n",tmp); 
  21.         goto err; 
  22.     } 
  23.     printf("Driver:%s, Card:%s, cap=0x%x\n",cap.driver,cap.card,cap.capabilities); 
  24.      
  25.     memset(&i, 0, sizeof(i)); 
  26.     i.index=index; 
  27.     if(ioctl(cam_fp, VIDIOC_ENUMINPUT, &i)<0)        //枚举输入源 
  28.         goto err; 
  29.     printf("input name:%s\n",i.name);    
  30.      
  31.     index=0; 
  32.     if(ioctl(cam_fp, VIDIOC_S_INPUT, &index)<0)      //设置输入源 
  33.         goto err; 
  34.  
  35.     if(ioctl(cam_fp, VIDIOC_S_OUTPUT, &index)<0)     //设置输出源 
  36.             goto err; 
  37.  
  38.     if(ioctl(cam_fp, VIDIOC_G_FBUF, &fb)<0)      //获取V4L2设备FB属性参数 
  39.         goto err; 
  40.     printf("g_fbuf:capabilities=0x%x,flags=0x%x,width=%d,height=%d\npixelformat=0x%x,bytesperline=%d,colorspace=%d,base=0x%x\n"
  41.         fb.capability,fb.flags,fb.fmt.width,fb.fmt.height,fb.fmt.pixelformat, 
  42.         fb.fmt.bytesperline,fb.fmt.colorspace,fb.base); 
  43.     fb.capability = cap.capabilities;           //V4L2设备功能赋值给V4L2的FB功能属性 
  44.     fb.fmt.width =width;                //LCD的FB宽度赋值给V4L2的FB宽度 
  45.     fb.fmt.height = height;             //LCD的FB高度赋值给V4L2的FB高度 
  46.     fb.fmt.pixelformat = (fb_bpp==32)?V4L2_PIX_FMT_BGR32:V4L2_PIX_FMT_RGB565;       //赋值V4L2的FB像素位数 
  47.  
  48.     if(ioctl(cam_fp, VIDIOC_S_FBUF, &fb)<0)      //设置新的FB属性给摄像头 
  49.         goto err; 
  50.     on = 1; 
  51.     if(ioctl(cam_fp, VIDIOC_OVERLAY, &on)<0)     //使能摄像头的overlay 
  52.         goto err; 
  53.      
  54.     vf_buff = (char*)malloc(size); 
  55.     if(vf_buff==NULL) 
  56.         goto err; 
  57.  
  58.     if(fb_bpp==16){         //16位BMP 
  59.         *((unsigned int*)(bmp_head_t+18)) = width; 
  60.         *((unsigned int*)(bmp_head_t+22)) = height; 
  61.         *((unsigned short*)(bmp_head_t+28)) = 16; 
  62.     }else
  63.     bmp_head[0] = 'B'
  64.     bmp_head[1] = 'M'
  65.     *((unsigned int*)(bmp_head+2)) = (width*fb_bpp/8)*height+54;        //整个位图大小 
  66.     *((unsigned int*)(bmp_head+10)) = 54;               //从54字节开始存图像 
  67.     *((unsigned int*)(bmp_head+14)) = 40;               //图像描述信息块的大小 
  68.     *((unsigned int*)(bmp_head+18)) = width; 
  69.     *((unsigned int*)(bmp_head+22)) = height; 
  70.     *((unsigned short*)(bmp_head+26)) = 1;              //图像的plane总数 
  71.     *((unsigned short*)(bmp_head+28)) = fb_bpp; 
  72.     *((unsigned short*)(bmp_head+34)) = (width*fb_bpp/8)*height;        //图像数据区大小 
  73.     } 
  74.      
  75.     while(1) 
  76.     { 
  77.         if (!read_data(cam_fp, vf_buff, width, height, fb_bpp))     //读摄像头数据到vf_buff 
  78.         { 
  79.         printf("read error\n"); 
  80.         } 
  81.         memcpy(fb_addr,vf_buff,width*height*fb_bpp/8);      //将读到的图像数据从内存拷贝到帧缓冲地址 
  82.         fd=0;                           //键盘句柄 
  83.         tv1.tv_sec=0; 
  84.         tv1.tv_usec=0;                      //无限等待 
  85.         FD_ZERO(&fds1); 
  86.         FD_SET(fd,&fds1);                   //绑定句柄跟监控对象 
  87.         select(fd+1,&fds1,NULL,NULL,&tv1);          //监控键盘输入 
  88.         if(FD_ISSET(fd,&fds1))                  //如果键盘有输入 
  89.         { 
  90.         memset(cmd,0,sizeof(cmd)); 
  91.         read(fd,cmd,256);                   //读取键盘输入的字符 
  92.         if(strncmp(cmd,"quit",4)==0){           //如果键盘输入quit 
  93.             printf("-->quit\n"); 
  94.             on=0; 
  95.             if(ioctl(cam_fp, VIDIOC_OVERLAY, &on)<0)     //关掉V4L2设备的overlay 
  96.             goto err; 
  97.             close(fb_fp); 
  98.             close(cam_fp);                  //释放FB跟摄像头的句柄 
  99.             return 0; 
  100.         }else if(strncmp(cmd,"capt",4)==0){         //如果键盘输入capt 
  101.             printf("-->capture\n"); 
  102.             printf("write to img --> "); 
  103.             writeImageToFile(size);             //把FB数据保存到位图中 
  104.             printf("OK\n"); 
  105.         } 
  106.         } 
  107.     } 
  108.  
  109. err: 
  110.     if (cam_fp) 
  111.         close(cam_fp); 
  112.     if (fb_fp) 
  113.         close(fb_fp); 
  114.     return 1; 
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和摄像头的初始化如下:

  1. static inline int fb_init(void
  2.     int dev_fp = -1; 
  3.     int fb_size; 
  4.     struct fb_var_screeninfo vinfo; 
  5.  
  6.     dev_fp = open("/dev/graphics/fb0", O_RDWR); 
  7.     if (dev_fp < 0) { 
  8.         perror("/dev/fb0"); 
  9.         return -1; 
  10.     } 
  11.     if (ioctl(dev_fp, FBIOGET_VSCREENINFO, &vinfo)) {   //获取FB基本信息 
  12.             printf("Error reading variable information.\n"); 
  13.         exit(1); 
  14.     } 
  15.     width=vinfo.xres; 
  16.     height=vinfo.yres; 
  17.     fb_bpp=vinfo.bits_per_pixel;    //将FB值赋值给全局变量 
  18.     if(fb_bpp==24) fb_bpp=32; 
  19.     fb_size=width*height*fb_bpp/8;  //一帧FB的字节大小 
  20.     if ((fb_addr = (char*)mmap(0, fb_size,   
  21.             PROT_READ | PROT_WRITE, MAP_SHARED, dev_fp, 0)) < 0) {   //从文件的0位置开始在内存中自动映射fb_size大小空间 
  22.         perror("mmap()"); 
  23.         return -1; 
  24.     } 
  25.     printf("%dx%d bpp:%d mmaped 0x%08x\n",width,height,fb_bpp,fb_addr); 
  26.  
  27.     return dev_fp; 
  28. static inline int cam_init(void
  29.     int dev_fp = -1; 
  30.  
  31.     dev_fp = open(cam_name, O_RDWR); 
  32.     if (dev_fp < 0) { 
  33.         printf("error open %s\n",cam_name); 
  34.         return -1; 
  35.     } 
  36.  
  37.     return dev_fp; 
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;
}

       从摄像头读数据到内存中:

  1. static inline int read_data(int fp, char *buf, int width, int height, int bpp) 
  2.     int ret=0; 
  3.     if ((ret = read(fp, buf, (width*height*bpp/8))) != (width*height*bpp/8)) { 
  4.         printf("read %d--0x%x\n",ret,ret); 
  5.         return 0; 
  6.     } 
  7.     return ret; 
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

  1. void writeImageToFile(unsigned int size) 
  2.     capframe++;       //图像序号计数 
  3.     sprintf(filename,"/tmp/0%d.bmp",capframe);   //位图的文件名 
  4.     bmpFile=fopen(filename, "w+");      //可写可追加内容的方式创建文件 
  5.     if(fb_bpp == 16) 
  6.         fwrite(bmp_head_t,1,66,bmpFile); 
  7.     else 
  8.         fwrite(bmp_head,1,54,bmpFile);      //往文件中写入BMP位图头 
  9.     fwrite(vf_buff,1,size,bmpFile);         //在继续往文件中追加位图数据 
  10.     fclose(bmpFile); 
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如下:

  1. CROSS_COMPILE  = /home/zhangcheng/gcc/arm-2008q3/bin/arm-linux- 
  2. CC    = $(CROSS_COMPILE)gcc 
  3.  
  4. cam2fb: cam2fb.c 
  5.     $(CC)  -o cam2fb cam2fb.c -static 
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,由于没有键盘输入,该部分无法验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值