Last active
December 12, 2025 09:36
-
-
Save commshare/526e98328b826ba96770 to your computer and use it in GitHub Desktop.
v4l2抓图,有注释
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* refer to | |
| https://github.com/YYuemei/Camera-Driver/blob/master/123.c | |
| 摄像头抓图程序,修改之后,实现了亮度计算 | |
| */ | |
| #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 CLEAR(x) memset (&(x), 0, sizeof (x)) | |
| struct buffer { | |
| void * start; | |
| size_t length; | |
| }; | |
| static char * dev_name = "/dev/video0";//摄像头设备名 | |
| 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; | |
| ////////////////////////////////////////////////////// | |
| //获取一帧数据 | |
| ////////////////////////////////////////////////////// | |
| 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; | |
| /*从驱动中拿到数据填充到app的buf里头,和驱动用的是同一个数据类型struct v4l2_buffer*/ | |
| ioctl (fd, VIDIOC_DQBUF, &buf); //出列采集的帧缓冲 | |
| /*这是一种验证*/ | |
| assert (buf.index < n_buffers); | |
| printf ("buf.index dq is %d\n",buf.index); | |
| /*数据虽然是在驱动里头产生,但是app已经通过buffers数组用mmap做了映射,所以通过buffers数组和buf得到的index,就可以操作驱动已经产生的数据了*/ | |
| fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, file_fd); //将其写入文件中 | |
| /*用完之后,app告诉驱动,可以重新使用这个内存了*/ | |
| 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("tt.jpg", "w");//图片文件名 | |
| fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//打开设备 | |
| ioctl (fd, VIDIOC_QUERYCAP, &cap);//获取摄像头参数 | |
| CLEAR (fmt); | |
| fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| fmt.fmt.pix.width = 640; | |
| fmt.fmt.pix.height = 480; | |
| fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; | |
| fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; | |
| ioctl (fd, VIDIOC_S_FMT, &fmt); //设置图像格式 | |
| sleep(3); | |
| 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"); | |
| /*app维护一个视频帧类型为元素的数组buffers,每个元素是一个struct buffers大小,buffers指向数组首地址*/ | |
| buffers = calloc (req.count, sizeof (*buffers));//内存中建立对应空间 | |
| for (n_buffers = 0; n_buffers < req.count; ++n_buffers) | |
| { | |
| /*这个内存看起来是在app里头分配的,但看起来会被驱动修改???*/ | |
| struct v4l2_buffer buf; //驱动中的一帧 | |
| CLEAR (buf); | |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
| buf.memory = V4L2_MEMORY_MMAP; | |
| buf.index = n_buffers; | |
| /*这里好像就是app的buf变量让驱动给修改了*/ | |
| if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) //映射用户空间 | |
| printf ("VIDIOC_QUERYBUF error\n"); | |
| /*app还要通过一个buffers数组来记录这些一帧帧内存的信息,内存地址居然是app映射到驱动里头去的*/ | |
| buffers[n_buffers].length = buf.length; | |
| buffers[n_buffers].start = /*看起来app和驱动映射了内存才能让app直接操作啊*/ | |
| 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; | |
| } | |
| unmap: | |
| 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; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment