最近买了一块飞淩的6410 arm11开发板,看到用户手册里有一个摄像头的例子,拿来一试,给版子接上了很久以前买来的十几块的破摄像头,结果发现没戏,上网一查,那个例子是专门写给飞淩卖的CMOS摄像头的........
去论坛上看了看,发现还是有人成功的,原来在裁剪linux内核的时候要把对USB摄像头的支持选上才行:
Device Drivers -->
<*>Multimedia support --->
<*>Video For Linux
<*>Video capture adapters --->
<*>V4L USB devices --->
<*>USB Video Class
<*>UVC input events device support
<*>GSPCA based webcams -->
<*>ZC3XX USB Camera Driver
Device Drivers -->
<*>USB Support --->
<*>Support for Host_side USB
<*>USB announce new devices然后就OK了,编译,重新烧写,OH,对了,上面这些选项一定要选*,不要选M哦,然后UCV设备不能用V4L只能用V4L2,我在这上面可是走了弯路的......ZC3XX是摄像头的驱动型号。
等linux整好之后,插上摄像头,发现/dev目录下多了video2,恩,不错,识别了(不过我试用网上流行的cat命令一直是无法读取)
下面就开始编程了......我决定要先在电脑上试好程序再往板子上移植,电脑上还自带一个看起来比较高级的摄像头哈,可是实验两个摄像头,排除一些硬件上的问题.....
去网上搜了几个V4L2的程序.....都不能用.....泪奔......于是只好自己动手改改了.....
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <getopt.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <malloc.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <asm/types.h> #include <linux/videodev2.h> #define WIDTH 640 #define HEIGHT 480 /*定义位图文件头*/ struct BIT_MAP_FILE_HEADER { short bfType; long bfSize; short bfReserved1; short bfReserved2; long bfOffBits; }__attribute__((packed)); /*定义信息头*/ struct BIT_MAP_INFO_HEADER { long biSize; long biWidth; long biHeight; short biPlanes; short biBitCount; long biCompression; long biSizeImage; long biXPelsPerMeter; long biYPelsPerMeter; long biClrUsed; long biClrImportant; }__attribute__((packed)); typedef struct BIT_MAP_FILE_HEADER BIT_MAP_FILE_HEADER; typedef struct BIT_MAP_INFO_HEADER BIT_MAP_INFO_HEADER; BIT_MAP_FILE_HEADER bmp_map_file; BIT_MAP_INFO_HEADER bmp_map_info; #define CLEAR(x) memset (&(x), 0, sizeof (x)) struct buffer { void * start; size_t length; }; static char * dev_name = "/dev/video2";//摄像头设备名 static int fd = -1; struct buffer * buffers = NULL; static unsigned int n_buffers = 0; FILE *file_fd; static unsigned long file_length; static unsigned char *file_name; long U[256], V[256], Y1[256], Y2[256]; void MakeConversionTable() { long i; for (i=0; i<256; i++) { V[i] = 15938*i-2221300; U[i] = 20238*i-2771300; Y1[i] = 11644*i; Y2[i] = 19837*i-311710; } } int max(int a,int b) { if(a > b) return a; else return b; } int min(int a,int b) { if(a<b) return a; else return b; } void yuv2bmp(unsigned char *pRGBBuf, const unsigned char *pYUVBuf, int m_nWidth, int m_nHeight) { unsigned char *pY, *pU, *pV, *pUbase, *pVbase; long i, j; unsigned char *pR, *pG, *pB; long RGB_SIZE = m_nWidth * m_nHeight * 3; MakeConversionTable(); // check if width and height is both in unit of 16 pixels if ((m_nWidth % 16) || (m_nHeight % 16)) return; pY = pYUVBuf; pU = pUbase = pYUVBuf + m_nWidth * m_nHeight; pV = pVbase = pUbase + m_nWidth*m_nHeight/2; for (i=0; i< m_nHeight; i++) { pB = pRGBBuf+RGB_SIZE-3*m_nWidth*(i+1); pG = pRGBBuf+RGB_SIZE-3*m_nWidth*(i+1)+1; pR = pRGBBuf+RGB_SIZE-3*m_nWidth*(i+1)+2; for (j=0; j < m_nWidth; j+=2) { *pR = max (0, min (255, (V[*pV] + Y1[*pY])/10000) ); //R value *pB = max (0, min (255, (U[*pU] + Y1[*pY])/10000) ); //B value *pG = max (0, min (255, (Y2[*pY] - 5094*(*pR) - 1942*(*pB))/10000) ); //G value pR += 3; pB += 3; pG += 3; pY++; *pR = max (0, min (255, (V[*pV] + Y1[*pY])/10000) ); //R value *pB = max (0, min (255, (U[*pU] + Y1[*pY])/10000) ); //B value *pG = max (0, min (255, (Y2[*pY] - 5094*(*pR) - 1942*(*pB))/10000) ); //G value pR += 3; pB += 3; pG += 3; pY++; pU++; pV++; } } return; } static void fillbmpheader(void) { int width = WIDTH; int height = HEIGHT; bmp_map_file.bfType =/* 'MB'*/0x4d42; bmp_map_file.bfSize = /*sizeof(BIT_MAP_FILE_HEADER) +sizeof(BIT_MAP_INFO_HEADER)+*/14 + 40+ width*height*3; bmp_map_file.bfReserved1 = 0; bmp_map_file.bfReserved2 = 0; bmp_map_file.bfOffBits = /*sizeof(BIT_MAP_FILE_HEADER) + sizeof(BIT_MAP_INFO_HEADER);*/0x36; bmp_map_info.biSize = sizeof(BIT_MAP_INFO_HEADER); bmp_map_info.biPlanes = 1; bmp_map_info.biHeight = height; bmp_map_info.biWidth = width; bmp_map_info.biBitCount = 24; bmp_map_info.biClrImportant = 0; bmp_map_info.biClrUsed = 0; bmp_map_info.biSizeImage = width*height*3; bmp_map_info.biCompression = 0; bmp_map_info.biXPelsPerMeter = 0; bmp_map_info.biYPelsPerMeter = 0; } // //获取一帧数据 // static int read_frame (void) { struct v4l2_buffer buf; unsigned int i; CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl (fd, VIDIOC_DQBUF, &buf); //出列采集的帧缓冲 assert (buf.index < n_buffers); printf ("buf.index dq is %d,/n",buf.index); fillbmpheader(); unsigned char *bmpchar = malloc(WIDTH*HEIGHT*3); yuv2bmp(bmpchar,buffers[buf.index].start,WIDTH,HEIGHT); //printf("length: %d\n",buffers[buf.index].length); //fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, file_fd); //将其写入文件中 fwrite(&bmp_map_file, sizeof(BIT_MAP_FILE_HEADER), 1, file_fd); fwrite(&bmp_map_info, sizeof(BIT_MAP_INFO_HEADER), 1, file_fd); fwrite(bmpchar,WIDTH*HEIGHT*3,1,file_fd); ioctl (fd, VIDIOC_QBUF, &buf); //再将其入列 return 1; } int main (int argc,char ** argv) { struct v4l2_capability cap; struct v4l2_format fmt; unsigned int i; enum v4l2_buf_type type; file_fd = fopen("test-mmapxy.bmp", "w");//图片文件名 fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//打开设备 ioctl (fd, VIDIOC_QUERYCAP, &cap);//获取摄像头参数 struct v4l2_fmtdesc fmt1; int ret; memset(&fmt1,0,sizeof(fmt1)); fmt1.index = 0; fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt1)) == 0) { fmt1.index++; printf("{pixelformat = %c%c%c%c, description = %s}\n",fmt1.pixelformat & 0xFF,(fmt1.pixelformat >> 8)& 0xFF,(fmt1.pixelformat >> 16)&0xFF,(fmt1.pixelformat >> 24)&0xFF,fmt1.description); } CLEAR (fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = WIDTH; fmt.fmt.pix.height = HEIGHT; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; ioctl (fd, VIDIOC_S_FMT, &fmt); //设置图像格式 file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; //计算图片大小 struct v4l2_requestbuffers req; CLEAR (req); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl (fd, VIDIOC_REQBUFS, &req); //申请缓冲,count是申请的数量 if (req.count < 2) printf("Insufficient buffer memory/n"); buffers = calloc (req.count, sizeof (*buffers));//内存中建立对应空间 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 (fd, VIDIOC_QUERYBUF, &buf)) //映射用户空间 printf ("VIDIOC_QUERYBUF error/n"); buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap (NULL /* start anywhere */, //通过mmap建立映射关系 buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, buf.m.offset); if (MAP_FAILED == buffers[n_buffers].start) printf ("mmap failed/n"); } for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (-1 == ioctl (fd, VIDIOC_QBUF, &buf))//申请到的缓冲进入列队 printf ("VIDIOC_QBUF failed/n"); } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == ioctl (fd, VIDIOC_STREAMON, &type)) //开始捕捉图像数据 printf ("VIDIOC_STREAMON failed/n"); for (;;) //这一段涉及到异步IO { fd_set fds; struct timeval tv; int r; FD_ZERO (&fds);//将指定的文件描述符集清空 FD_SET (fd, &fds);//在文件描述符集合中增加一个新的文件描述符 /* Timeout. */ tv.tv_sec = 2; tv.tv_usec = 0; r = select (fd + 1, &fds, NULL, NULL, &tv);//判断是否可读(即摄像头是否准备好),tv是定时 if (-1 == r) { if (EINTR == errno) continue; printf ("select err/n"); } if (0 == r) { // fprintf (stderr, "select timeout/n"); // exit (EXIT_FAILURE); } if (read_frame ())//如果可读,执行read_frame ()函数,并跳出循环 break; } for (i = 0; i < n_buffers; ++i) if (-1 == munmap (buffers[i].start, buffers[i].length)) printf ("munmap error"); close (fd); fclose (file_fd); exit (EXIT_SUCCESS); return 0; }
以上是代码......代码太粗糙了,大家不要笑话哈,下次发个整理版上来,大家注意几个地方:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
这一句是要选摄像头输出的格式,我的摄像头买太久了,我实在不知道它输出什么格式,于是使用下面的代码来输出可以使用的格式:struct v4l2_fmtdesc fmt1; int ret; memset(&fmt1,0,sizeof(fmt1)); fmt1.index = 0; fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt1)) == 0) { fmt1.index++; printf("{pixelformat = %c%c%c%c, description = %s}\n",fmt1.pixelformat & 0xFF,(fmt1.pixelformat >> 8)& 0xFF,(fmt1.pixelformat >> 16)&0xFF,(fmt1.pixelformat >> 24)&0xFF,fmt1.description); }
输出后发现我的电脑自带的摄像头支持YUYV和MJPEG格式的, 而我买来的摄像头只支持YUYV格式的,
MJPEG格式的就爽了,因为可以直接输出到jpg格式的文件里,而YUYV格式的只要把它转为bmp再输出,除了要实现YUYV转RGB的算法之外,还得添加bmp文件头之类的.....麻烦啊......
最后的结果......电脑上实验:电脑自带的摄像头,使用JPG格式输出,图像清晰,使用YUYV转BMP格式输出....图像不太清晰哦......有色块,这也是为什么我说这是个半成品案例的原因.......
移植到板子上实验:使用我买来的破摄像头:YUYV转bmp格式的,图像不太清晰.....有色块.....还需要继续改进....哎......