由于毕设中用到了摄像头拍照功能,然后网上查了一下,最后自己稍微修改,以当前时间保存为.jpeg格式的图片,最后稍微添加了自己的一些小理解,与大家分享一下。
-----------------------------------------------------------------------------------------------------------------------------------
用的摄像头是ZC301P,已经加载进内核,输出格式是JPEG,大小是640*480,所以后面的图片大小计算的时候是640*480*3。
cam.c:
#include <fcntl.h> #include <stdlib.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <time.h> #include <stdio.h> #include <string.h> int main(){ //////add by liuzj time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char buffer1[30]; char path[30]; /////end int fd = open("/dev/video0",O_RDWR); printf("lzj------->>>fd is %d\n",fd); ////// struct v4l2_capability cap; ioctl(fd,VIDIOC_QUERYCAP,&cap); printf("lzj---------->>>>>Driver Name:%s\nCard Name:%s\nBus info:%s\n",cap.driver,cap.card,cap.bus_info); ////// struct v4l2_fmtdesc fmtdesc; fmtdesc.index = 0; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) != -1){ printf("lzj-------->>>>>fmtdesc.description is %s\n",fmtdesc.description); fmtdesc.index ++; } ////// struct v4l2_format fmt; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd,VIDIOC_G_FMT,&fmt); printf("lzj----------->>>>>fmt.fmt.width is %d\nfmt.fmt.pix.height is %d\nfmt.fmt.pix.colorspace is %d\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.colorspace); ////// struct v4l2_requestbuffers req; req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl(fd,VIDIOC_REQBUFS,&req); struct buffer { void *start; unsigned int length; }*buffers; buffers = (struct buffer*)calloc (req.count, sizeof(*buffers)); unsigned int n_buffers = 0; for(n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf; memset(&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; if(ioctl(fd,VIDIOC_QUERYBUF,&buf) == -1) { printf("lzj---------_>>>>>>error\n"); close(fd); exit(-1); } buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset); if(MAP_FAILED == buffers[n_buffers].start) { printf("lzj--------__>>>>>error 2\n"); close(fd); exit(-1); } } //// unsigned int i; enum v4l2_buf_type type; for(i = 0; i < 4; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl(fd,VIDIOC_QBUF,&buf); } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd,VIDIOC_STREAMON,&type); //// struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl(fd,VIDIOC_DQBUF,&buf); //add by liuzj strftime(buffer1, sizeof(buffer1), "%Y-%m-%d_%H-%M.jpeg", tmp); snprintf(path,sizeof(path),"/data/%s",buffer1); //modify by liuzj int fdyuyv = open(path,O_WRONLY|O_CREAT,00700); printf("\nlzj--------->>>>fdyuyv is %d\n",fdyuyv); int resultyuyv = write(fdyuyv,buffers[buf.index].start,640*480*3); printf("lzj--------->>>resultyuyv is %d\n",resultyuyv); close(fdyuyv); //// close(fd); return 0; }
交叉编译好了之后,可以移到开发板上直接运行。
/apps >: pic
lzj------->>>fd is 3
lzj---------->>>>>Driver Name:zc3xx
Card Name:PC Camera
Bus info:usb-s3c24xx-1.1
lzj-------->>>>>fmtdesc.description is JPEG
lzj----------->>>>>fmt.fmt.width is 640
fmt.fmt.pix.height is 480
fmt.fmt.pix.colorspace is 7
lzj--------->>>>fdyuyv is 4
lzj--------->>>resultyuyv is 118784
会打印出我摄像头支持的格式和采集的相关参数,然后进入我存放的目录下去寻找我的图片,再利用tftp命令来将其传到电脑上查看。
/apps >: cd /data/
/data >: ls
2016-05-09 12-47.jpeg
/date >: tftp -pr 2016-05-09 12-47.jpeg 10.228.26.4
最后贴上我的一点小注释 1.打开摄像头 int fd = open("/dev/video0",O_RDWR); 2.捕获摄像头能力及属性 ioctl(fd,VIDIOC_QUERYCAP,&cap); 1.查看当前摄像头支持的视频格式 ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) 2.查看视频帧格式 ioctl(fd,VIDIOC_G_FMT,&fmt);//读取当前驱动的捕捉格式 5.向驱动申请缓冲帧 ioctl(fd,VIDIOC_REQBUFS,&req);//向驱动申请缓冲帧 req.count = 4;//申请4个缓冲区 req.memory = V4L2_MEMORY_MMAP;//设置使用mmap来访问内存 6.申请物理内存并进行内存映射 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。将申请到的帧缓冲全部入队列,以便存放采集到的数据,利用mmap进行映射。 struct buffer { void *start; unsigned int length; }*buffers; buffers = (struct buffer*)calloc (req.count, sizeof(*buffers)); for(n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf; memset(&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; if(ioctl(fd,VIDIOC_QUERYBUF,&buf) == -1) { printf("lzj---------_>>>>>>error\n"); close(fd); exit(-1); } buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset); if(MAP_FAILED == buffers[n_buffers].start) { printf("lzj--------__>>>>>error 2\n"); 7.开始视频传输 for(i = 0; i < 4; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl(fd,VIDIOC_QBUF,&buf); }//将4个缓冲区放入输入队列中去,VIDIOC_QBUF为其控制命令。 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd,VIDIOC_STREAMON,&type);//开始进行视频采集 8.采集图像 从视频缓冲区的输出队列中取得一个已经保存有一帧视频数据的视频缓冲区。 struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl(fd,VIDIOC_DQBUF,&buf); //从视频的缓冲区中取得一个缓冲区数据到buf中去,VIDIOC_DQBUF为其控制命令。 9.将采集到的图片以当前时间保存下来 strftime(buffer1, sizeof(buffer1), "%Y-%m-%d_%H-%M.jpeg", tmp); snprintf(path,sizeof(path),"/data/%s",buffer1); int fdyuyv = open(path,O_WRONLY|O_CREAT,00700); int resultyuyv = write(fdyuyv,buffers[buf.index].start,640*480*3); close(fdyuyv); close(fd);