TQ2440 基于V4L2编程框架的 LCD实时显示(上)

前言

本次实验为基于V4L2框架,使用USB摄像头进行图像采集,用4.3寸LCD屏进行实时显示。由于,知识缺乏未能完整的进行下去,失败在图像实时显示上面,因为要放一段时间,所以在此做次记录,希望能帮助到一些小伙伴,站在前人的路上把该问题解决掉。

一、情况说明

1、使用TQ2440开发板、4.3寸TFT LCD屏,分辨率为480*272  16bpp

2、普通USB摄像头,采集的图像格式为YUV422,分辨率为320*240

二、实验进展

1、USB摄像头可以进行图片采集,但在lcd显示时不能全屏显示,只显示lcd屏上面一半,且出现三个窗口暂时不知怎么解决

2、摄像头采集的图片为YUV422格式要在LCD屏显示需要转成RGB565格式,这个已经完成

3、由于二十多块买的摄像头其分辨率320*240,需要将其放大到480*272,这个代码已经完成

三、所遇问题

现在不知如何解决图像正确显示问题。。。。

TQ2440 基于V4L2编程框架的 LCD实时显示(上)_第1张图片

下面是显示代码:

#include "v4l2_device.h"

static int width=320;
static int height=240;
struct buffer *buffers=NULL;
unsigned char* newBuf;
char *dev_name="/dev/video0";
short oldpic[240][320];
short delpic[272][480];

struct v4l2_src
{
    int cam_fd;
}app;


/********************************
*函数功能:打开摄像头
*********************************/
int open_cam_dev(void)
{
    if((app.cam_fd=open(dev_name,O_RDWR|O_NONBLOCK,0))<0)
    {
        printf("device:%s open failed!!\n",dev_name);
        exit(-1);
    }
    return 0;
}


/********************************
*函数功能:初始化摄像头+显示摄像头信息
*********************************/
void init_device(void)
{
    struct v4l2_capability cap;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_format fmt;
    unsigned int min=0;
    
    //1、查询摄像头信息并打印
    if (ioctl(app.cam_fd, VIDIOC_QUERYCAP, &cap) == -1) 
    {
        printf("Error opening device %s: unable to query device.\n",dev_name);
        exit(-1);
    }
    else
    {
    /*打印信息*/
    printf("*************Camera-params***************\n");
    printf("driver:\t\t%s\n",cap.driver);
        printf("card:\t\t%s\n",cap.card);
        printf("bus_info:\t%s\n",cap.bus_info);
        printf("version:\t%d\n",cap.version);
        printf("capabilities:\t%x\n",cap.capabilities);
        printf("*****************end*********************\n");
         
        /*检测摄像头是否支持图像抓取功能*/
        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) 
        {
            printf("Device %s: supports capture.\n",dev_name);
        }
/*检测摄像头是否支持视频流*/
        if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
        {
            printf("Device %s: supports streaming.\n",dev_name);
        }
    }


   
    //2、显示所有支持格式
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("Support format:\n");
    while(ioctl(app.cam_fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    {
        printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
        fmtdesc.index++;
    }
    
    //3、设置摄像头格式
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.height = height;
    fmt.fmt.pix.width = width;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    
    if (-1 == ioctl(app.cam_fd, VIDIOC_S_FMT, &fmt))  
    { 
    printf("VIDIOC_S_FMT error!\n");
        exit(-1);   
    }
        
    if(ioctl(app.cam_fd, VIDIOC_G_FMT, &fmt) == -1)
    {
        printf("Unable to get format\n");
        exit(-1); 
    } 
    {
         printf("fmt.type:\t\t%d\n",fmt.type);
         printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF,     (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
         printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
         printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
         printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
    }
    /* Note VIDIOC_S_FMT may change width and height. */   

    /* Buggy driver paranoia. 
    min = fmt.fmt.pix.width * 2;   
    if (fmt.fmt.pix.bytesperline < min)   
fmt.fmt.pix.bytesperline = min;   
    min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;   
    if (fmt.fmt.pix.sizeimage < min)   
fmt.fmt.pix.sizeimage = min; 
*/ 
   
}

/********************************
*函数功能:映射摄像头帧缓冲
*********************************/
void init_mmap(void)
{
    unsigned int n_buffers;
    struct v4l2_requestbuffers req;
    
    //1、请求四个缓冲帧
    req.count = 4;
    req.memory = V4L2_MEMORY_MMAP;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    if(ioctl(app.cam_fd,VIDIOC_REQBUFS,&req)==-1)
    {
        printf("request for buffers error!\n");
    }
    if (req.count < 2)
    {
    printf("Insufficient buffer memory on %s\n",dev_name); 
        exit(-1);
    }
    
    //2、分配图像缓冲结构 (mmap for buffers) 
    buffers = calloc(req.count, sizeof (*buffers));  
    if (!buffers) 
    {
        printf ("Out of memory: error!\n");
        exit(-1);
    }
    
    //3、把内核空间中的图像缓冲区映射到用户空间 
    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) 
    {   
    struct v4l2_buffer buf;   

CLEAR(buf);   

buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
buf.memory      = V4L2_MEMORY_MMAP;   
buf.index       = n_buffers;   
/*获取要映射屏幕大小*/
if (-1 == ioctl(app.cam_fd, VIDIOC_QUERYBUF, &buf))   
{
printf ("VIDIOC_QUERYBUF error!\n");
exit(-1);   
} 

/*这里的输出是为了检查有关lcd参数的大小*/
buffers[n_buffers].length = buf.length;   
printf("buf.length=%d\nbuf.m.offset=%d\n",buf.length,buf.m.offset);

buffers[n_buffers].start =   
mmap(NULL /* start anywhere */,   
buf.length,   
PROT_READ | PROT_WRITE /* required */,   
MAP_SHARED /* recommended */,   
app.cam_fd, buf.m.offset);   
/*不成功则退出*/
if (MAP_FAILED == buffers[n_buffers].start)   
{
printf ("mmap error!\n");
exit(-1);   
}  
    }   
}


/********************************
*函数功能:启动捕获图像
*********************************/
void start_capture(void)
{
    unsigned int i;
    enum v4l2_buf_type type;//帧类型,应用程序设置  
    
    /*将缓冲帧放入队列*/
    for (i = 0; i < 4; ++i)
    {   
struct v4l2_buffer buf;   
CLEAR(buf);   

buf.index       = i;   
buf.memory      = V4L2_MEMORY_MMAP;   
buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

if (-1 == ioctl(app.cam_fd, VIDIOC_QBUF, &buf))   
{
printf ("VIDIOC_QBUF error!\n");
exit(-1);   
}  
    }   

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
    /*启动数据流*/
    if (-1 == ioctl(app.cam_fd, VIDIOC_STREAMON, &type))  
    {
printf ("VIDIOC_STREAMON error!\n");
exit(-1);   
    }    
    printf ("Start to capture!\n");
}


/********************************
*函数功能:主循环播放视频流
*********************************/
void mainloop(short *fb_addr)
{
    int ret;
    fd_set fds;
    struct timeval tv; 
    
    while (1)
    {   
    /*每次循环都要清空*/
FD_ZERO(&fds);   
/*添加描述符*/
FD_SET(app.cam_fd, &fds);   

/* Timeout. */   
tv.tv_sec  = 2;   
tv.tv_usec = 0;   
ret = select (app.cam_fd + 2, &fds, NULL, NULL, NULL);   
 
/*select错误,退出*/
if (-1 == ret) 
{   
printf("Select() error!\n");
//exit(-1);   
break;
}   
if (0 == ret) {   
printf("select timeout\n");   
continue;   
}   
if(FD_ISSET(app.cam_fd, &fds))
{
display_pic(fb_addr);
}
    }
}


void display_pic(short *fb_addr)
{
    struct v4l2_buffer buf;   
    unsigned char* ptr;
    unsigned long i,j;
    
    CLEAR (buf);   
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
    buf.memory = V4L2_MEMORY_MMAP;   


    /*取出一缓冲帧*/
    if (-1 == ioctl(app.cam_fd, VIDIOC_DQBUF, &buf)) 
    {   
printf(" VIDIOC_DQBUF error!\n");
exit(-1);
    }   
    /*检查缓冲帧是否有效*/
    assert(buf.index < 4);   
    
    ptr = (unsigned char*)buffers[buf.index].start;
    /*图片格式转换*/
    yuv422_2_rgb(ptr);
    
    //memcpy(fb_addr,oldpic,buffers[buf.index].length);
    /*图片放大*/
    fangsuo(fb_addr);
    /*将摄像头获取的图像写入LCD缓冲帧进行显示
    *但在这里因为失败了,原因暂时未知
    *时间:2017.08.10
    */
    for(i=0;i<272;i++)
  for(j=0;j<480;j++)
  {
    *(fb_addr+i*480+j) = delpic[i][j];
      }
 
    //printf("the camera_buf size:%d\n",buffers[buf.index].length);
    
    /*缓冲帧入队列*/
    if (-1 == ioctl(app.cam_fd, VIDIOC_QBUF, &buf))   
    {
    printf("VIDIOC_QBUF error!\n");
exit(-1); 
    }
}
/************************************************
*对320*240图片采用最近邻插值法放大到480*272分辨率
***********************************************/
void fangsuo(short *fb_addr)
{
unsigned int i,j;
unsigned int srcx=0,srcy=0;
float tmpX,tmpY;

tmpY = 240/272;
tmpX = 320/480;

for(i=0;i<272;i++)
  for(j=0;j<480;j++)
  {
  srcx = (int)(j*tmpX +0.5);
  srcy = (int)(i*tmpY +0.5);
 
  delpic[i][j] = oldpic[srcy][srcx];
  //delpic[i][j] = i+j;
  } 
}


/*********************************************
*摄像头采集的为:YUV422(16bpp)转换为RGB565(16bpp)
*********************************************/
void yuv422_2_rgb(unsigned char* inbuf)
{
       unsigned char *YUVdata;
       short *RGBdata;
       unsigned long i,size=width*height;
       unsigned char y,u,v;
       unsigned int r,g,b;
       unsigned short rgb;
       
       YUVdata= inbuf; 
       RGBdata = (short*)oldpic;
       
       for(i=0;i>4)&0x0f;
             v=(*(YUVdata+i*2+1)>>4)&0xf0;
   
             //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);    
             //rgb888转rgb565
    r >>= 3; //RGB565格式中red占5位,green占6位,blue占5位,故需进行移位
    g >>= 2;
    b >>= 3;
    
    rgb = (r<<11)|(g<<5)|(b);//生成一个2个字节长度的像素点,即rgb565
      //赋给lcd映射的buffer
             *RGBdata++ = (short)rgb;   
       }
}

void uninit_device(int lcd_fd)
{
    unsigned int i;
    for (i = 0; i < 4; ++i)
      munmap (buffers[i].start, buffers[i].length);   
    
    free(buffers);  
    
    close(app.cam_fd);
    close(lcd_fd);
    printf("quit!!\n");
}

 

 

你可能感兴趣的:(linux学习)