#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <errno.h>
int lcdfd =0;
unsigned short *lcdptr = NULL;
int lcd_w,lcd_h,i,j;
static int convert_yuyv_to_rgb(unsigned char *inBuf, unsigned char *outBuf,int imgWidth,int imgHeight)
{
int rows,cols;
int y,u,v,r,g,b;
unsigned char *YUVdata,*RGBdata;
int Ypos,Upos,Vpos;//in the inBuf's postion
YUVdata= inBuf;
RGBdata = outBuf;
Ypos=0;
Upos=Ypos+1;
Vpos=Upos+2;
for(rows=0;rows<imgHeight;rows++)
{
for(cols=0;cols<imgWidth;cols++)
{
y=YUVdata[Ypos];
u=YUVdata[Upos]-128;
v=YUVdata[Vpos]-128;
//r= y+1.4075*v;
//g= y-0.3455*u-0.7169*v;
//b= y+1.779*u;
r=y+ v+ (v*103>>8);
g=y- (u*88>>8) - (v*183>>8);
b=y+ u + (u*198>>8);
r=r>255?255:(r<0?0:r);
g=g>255?255:(g<0?0:g);
b=b>255?255:(b<0?0:b);
*(RGBdata++)=( ((g & 0x1C) << 3) | ( r >> 3) );
*(RGBdata++)=( (b & 0xF8) | ( g >> 5) );
Ypos+=2;
if(!(cols& 0x01))//if cols%2!=0
{
Upos=Ypos+1;
Vpos=Upos+2;
}
}
}
return 0;
}
void lcd_show_rgb(unsigned char *rgbdata,int w,int h)
{
unsigned short *ptr = lcdptr;
for(i = 0;i < h; i++)
{
for(j = 0; j < w; j++)
{
memcpy(ptr+j,rgbdata+j*2,2);
}
ptr += lcd_w;
rgbdata += w*2;
}
}
int main(void)
{
lcdfd = open("/dev/fb0",O_RDWR);
//获取lcd信息
struct fb_var_screeninfo info; //inux/fb.h中定义
int lret = ioctl(lcdfd,FBIOGET_VSCREENINFO,&info); //获取lcd屏幕信息,linux/fb.h中定义
//虚拟机ubuntu
lcd_w = info.xres_virtual;
lcd_h = info.yres_virtual;
/*开发板实际屏幕
lcd_w = info.xres;
lcd_h = info.yres; */
lcdptr = (unsigned short *)mmap(NULL,lcd_w*lcd_h*2,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
//1、打开设备/
int fd = open("/dev/video1",O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
// 2、获取支持格式 struct v4l2_format
int ret = ioctl(fd,VIDIOC_ENUM_FMT,&v4fmt);
if(ret < 0){
perror("获取失败");
break;
}
printf("index=%d\n",v4fmt.index);
printf("flags=%d\n",v4fmt.flags);
printf("description=%s\n",v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n",p[0],p[1],p[2],p[3]);
}
*/
//3、设置采集格式
struct v4l2_format v4fmt;
struct v4l2_streamparm streamparm = {0};
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; //不初始化这个会出错
v4fmt.fmt.pix.width = 640; //宽
v4fmt.fmt.pix.height = 480; //高
v4fmt.fmt.pix.pixelformat =V4L2_PIX_FMT_YUYV; //图片格式
int ret =ioctl(fd,VIDIOC_S_FMT,&v4fmt); //设置图片格式命令
if(ret <0)
{
perror("设置失败");
}
/* 获取streamparm */
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_G_PARM, &streamparm);
/** 判断是否支持帧率设置 **/
if (V4L2_CAP_TIMEPERFRAME & streamparm.parm.capture.capability) {
streamparm.parm.capture.timeperframe.numerator = 1;
streamparm.parm.capture.timeperframe.denominator = 30;//30fps
if (0 > ioctl(fd, VIDIOC_S_PARM, &streamparm)) {
fprintf(stderr, "ioctl error: VIDIOC_S_PARM: %s\n", strerror(errno));
return -1;
}
}
//4、申请内核空间
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP; //内存映射方式
ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuffer);
if(ret < 0)
{
perror("申请缓冲区失败");
}
//5、映射内核空间到用户空间去 查询内核空间VIDIOC_QUERYBUF 放回VIDIOC_QBUF
unsigned char *mptr[4]; //保存后用户空间的首地址
unsigned int size[4];
struct v4l2_buffer mapbuffer;
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
for(i=0;i<4;i++) //循环查询4个内核空间
{
mapbuffer.index = i;
ret = ioctl(fd,VIDIOC_QUERYBUF,&mapbuffer);//从内核空间查询一个做映射
if(ret <0)
{
perror("查询空间队列失败");
}
mptr[i] = (unsigned char *)mmap(NULL,mapbuffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,mapbuffer.m.offset);
size[i] = mapbuffer.length;
//通知使用完毕,放回去
ret = ioctl(fd,VIDIOC_QBUF,&mapbuffer);
if(ret < 0)
{
perror("放回失败");
}
}
//6、开始采集数据
int type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd,VIDIOC_STREAMON,&type);
if(ret < 0)
{
perror("开始失败");
}
unsigned char rgbdata[640*480*2]; //一帧数据的空
printf("lcd_w%d\n",lcd_w);
while(1)
{
//从队列中提取一帧数据
struct v4l2_buffer readbuffer;
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
ret = ioctl(fd,VIDIOC_DQBUF,&readbuffer);
if(ret < 0)
{
perror("提取失败");
}
//yuyv转rgb在LCD上显示
convert_yuyv_to_rgb(mptr[readbuffer.index],rgbdata,640,480);
lcd_show_rgb(rgbdata,640,480);
//通知内核使用完毕
ret = ioctl(fd,VIDIOC_QBUF,&readbuffer);
if(ret < 0)
{
perror("放回队列失败");
}
}
//7、停止采集/
ret = ioctl(fd,VIDIOC_STREAMOFF,&type);
//8、释放映射/
for(i=0;i<4;i++)
{
munmap(mptr[i],size[i]);
}
//9、关闭设备/
close(fd);
close(lcdfd);
return 0;
}