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

     一个简单的应用程序,来实现在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,由于没有键盘输入,该部分无法验证。
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值