• V4L2 Camera 开发


    一、什么是V4L2

    vl42是video for Linux 2的缩写,是一套Linux内核视频设备的驱动框架,该驱动框架为应用层提供一套统一的操作接口(一系列的ioctl)

    假如要进行视频数据采集,大体的步骤如图左侧所示:

    1. 打开设备文件/dev/videoX;
    2. 根据打开的设备,查询设备能力集;
    3. 设置视频数据的格式、参数等;
    4. 分配buffer,这个buffer可以是用户态分配的,也可以是从内核中获取的;
    5. 开始视频流采集工作;
    6. 将buffer enqueue到v4l2框架,底层负责将视频数据填充后,应用层再将buffer dequeue以便获取数据,然后再将buffer enqueue,如此循环往复;

    1. 打开设备

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. int main(void)
    8. {
    9. //1.打开设备
    10. int fd = open("/dev/video0", O_RDWR);
    11. if(fd < 0)
    12. {
    13. perror("打开设备失败");
    14. return -1;
    15. }
    16. //9.关闭设备
    17. close(fd);
    18. return 0;
    19. }

    2. 获取支持格式

    0

    获取摄像头格式VIDIOC_ENUM_FMT--对应存储格式的结构体struct v4l2_fmtdesc

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. #include <sys/ioctl.h>
    8. #include <linux/videodev2.h>
    9. int main(void)
    10. {
    11. //1.打开设备
    12. int fd = open("/dev/video0", O_RDWR);
    13. if(fd < 0)
    14. {
    15. perror("打开设备失败");
    16. return -1;
    17. }
    18. //2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
    19. struct v4l2_fmtdesc v4fmt;
    20. v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    21. int i=0;
    22. while(1)
    23. {
    24. v4fmt.index = i++;
    25. int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
    26. if(ret < 0)
    27. {
    28. perror("获取失败");
    29. break;
    30. }
    31. printf("index=%d\n", v4fmt.index);
    32. printf("flags=%d\n", v4fmt.flags);
    33. printf("description=%s\n", v4fmt.description);
    34. unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
    35. printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
    36. printf("reserved=%d\n", v4fmt.reserved[0]);
    37. }
    38. //9.关闭设备
    39. close(fd);
    40. return 0;
    41. }

    3.配置摄像头采集格式

    0

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. #include <sys/ioctl.h>
    8. #include <linux/videodev2.h>
    9. #include <string.h>
    10. int main(void)
    11. {
    12. //1.打开设备
    13. int fd = open("/dev/video0", O_RDWR);
    14. if(fd < 0)
    15. {
    16. perror("打开设备失败");
    17. return -1;
    18. }
    19. //3.设置采集格式
    20. struct v4l2_format vfmt;
    21. vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
    22. vfmt.fmt.pix.width = 640;//设置宽(不能任意)
    23. vfmt.fmt.pix.height = 480;//设置高
    24. vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
    25. int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    26. if(ret < 0)
    27. {
    28. perror("设置格式失败");
    29. }
    30. memset(&vfmt, 0, sizeof(vfmt));
    31. vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    32. ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
    33. if(ret < 0)
    34. {
    35. perror("获取格式失败");
    36. }
    37. if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 &&
    38. vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
    39. {
    40. printf("设置成功\n");
    41. }else
    42. {
    43. printf("设置失败\n");
    44. }
    45. //9.关闭设备
    46. close(fd);
    47. return 0;
    48. }

    4.申请内核缓冲区队列

    0

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. #include <sys/ioctl.h>
    8. #include <linux/videodev2.h>
    9. #include <string.h>
    10. int main(void)
    11. {
    12. //1.打开设备
    13. int fd = open("/dev/video0", O_RDWR);
    14. if(fd < 0)
    15. {
    16. perror("打开设备失败");
    17. return -1;
    18. }
    19. //3.设置采集格式
    20. struct v4l2_format vfmt;
    21. vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
    22. vfmt.fmt.pix.width = 640;//设置宽(不能任意)
    23. vfmt.fmt.pix.height = 480;//设置高
    24. vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
    25. int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    26. if(ret < 0)
    27. {
    28. perror("设置格式失败");
    29. }
    30. //4.申请内核空间
    31. struct v4l2_requestbuffers reqbuffer;
    32. reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    33. reqbuffer.count = 4; //申请4个缓冲区
    34. reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
    35. ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
    36. if(ret < 0)
    37. {
    38. perror("申请队列空间失败");
    39. }
    40. //9.关闭设备
    41. close(fd);
    42. return 0;
    43. }

    5.把内核的缓冲区队列映射到用户空间

    0

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. #include <sys/ioctl.h>
    8. #include <linux/videodev2.h>
    9. #include <string.h>
    10. #include <sys/mman.h>
    11. int main(void)
    12. {
    13. //1.打开设备
    14. int fd = open("/dev/video0", O_RDWR);
    15. if(fd < 0)
    16. {
    17. perror("打开设备失败");
    18. return -1;
    19. }
    20. //3.设置采集格式
    21. struct v4l2_format vfmt;
    22. vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
    23. vfmt.fmt.pix.width = 640;//设置宽(不能任意)
    24. vfmt.fmt.pix.height = 480;//设置高
    25. vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
    26. int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    27. if(ret < 0)
    28. {
    29. perror("设置格式失败");
    30. }
    31. //4.申请内核空间
    32. struct v4l2_requestbuffers reqbuffer;
    33. reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    34. reqbuffer.count = 4; //申请4个缓冲区
    35. reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
    36. ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
    37. if(ret < 0)
    38. {
    39. perror("申请队列空间失败");
    40. }
    41. //5.映射
    42. unsigned char *mptr[4];//保存映射后用户空间的首地址
    43. unsigned int size[4];
    44. struct v4l2_buffer mapbuffer;
    45. //初始化type, index
    46. mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    47. for(int i=0; i<4; i++)
    48. {
    49. mapbuffer.index = i;
    50. ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
    51. if(ret < 0)
    52. {
    53. perror("查询内核空间队列失败");
    54. }
    55. mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE,
    56. MAP_SHARED, fd, mapbuffer.m.offset);
    57. size[i]=mapbuffer.length;
    58. //通知使用完毕--‘放回去’
    59. ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
    60. if(ret < 0)
    61. {
    62. perror("放回失败");
    63. }
    64. }
    65. //9.关闭设备
    66. close(fd);
    67. return 0;
    68. }

    6.开始采集

    VIDIOC_STREAMON(开始采集写数据到队列中)

    VIDIOC_DQBUF(告诉内核我要某一个数据,内核不可以修改)

    VIDIOC_QBUF(告诉内核我已经使用完毕)

    VIDIOC_STREAMOFF(停止采集-不在向队列中写数据)

    0

    1. //6.开始采集
    2. int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    3. ret = ioctl(fd, VIDIOC_STREAMON, &type);
    4. if(ret < 0)
    5. {
    6. perror("开启失败");
    7. }

    7.采集数据,

    1. //从队列中提取一帧数据
    2. struct v4l2_buffer readbuffer;
    3. readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    4. ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
    5. if(ret < 0)
    6. {
    7. perror("提取数据失败");
    8. }
    9. FILE *file=fopen("my.jpg", "w+");
    10. //mptr[readbuffer.index]
    11. fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);
    12. fclose(file);
    13. //通知内核已经使用完毕
    14. ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
    15. if(ret < 0)
    16. {
    17. perror("放回队列失败");
    18. }

    8. 停止采集

    ret = ioctl(fd, VIDIOC_STREAMOFF, &type);

    9.释放映射

    for(int i=0; i<4; i++) munmap(mptr[i], size[i]);

    10.关闭设备

    close(fd);
  • 相关阅读:
    Springboot毕设项目基于javaweb电费管理系统v39ge(java+VUE+Mybatis+Maven+Mysql)
    【Unity3D】自动构建Android包时指定aab或apk以及设置sdk版本
    神秘的Java集合与UML
    Linux系统运行时参数命令
    1-十四烷基-3-甲基咪唑六氟磷酸盐([C14MIm][PF6])修饰纳米SiO2二氧化硅(mg级瓶装)
    uni-app 微信小程序movable-area遮盖 遮挡住 点击事件
    揭晓:一条SQL语句的执行过程是怎么样的?
    《爵士乐史》乔德.泰亚 笔记
    .NET Emit 入门教程:第五部分:动态生成方法(MethodBuilder 与 DynamicMethod)
    05-HTTPS 秘钥库与证书(Java)
  • 原文地址:https://blog.csdn.net/qq_53676406/article/details/132853055