本次实验为基于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,这个代码已经完成
现在不知如何解决图像正确显示问题。。。。
#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");
}