V4L2视频采集与H264编码2—v4l2采集YUV数据

2024-06-08 00:08

本文主要是介绍V4L2视频采集与H264编码2—v4l2采集YUV数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  在上一篇中因为是在PC机上使用的USB摄像头只能支持GPEG image格式,但是H264编码需要使用YUV数据,所以我找了个ARM开发板来做测试。本以为代码从PC机移植到开发板是很简单的一个事,谁知因为平台或是V4L2底层驱动的不同,最终也是花了九牛二虎之力才把问题给解了。话不多说,直接上代码:

[objc]  view plain  copy
  1. /*============================================================================= 
  2. #     FileName: v4l2.c 
  3. #         Desc: this program aim to get image from USB camera, 
  4. #               used the V4L2 interface. 
  5. #       Author: Licaibiao 
  6. #      Version:  
  7. #   LastChange: 2016-12-10  
  8. #      History: 
  9.  
  10. =============================================================================*/  
  11. #include <unistd.h>  
  12. #include <sys/types.h>  
  13. #include <sys/stat.h>  
  14. #include <fcntl.h>  
  15. #include <stdio.h>  
  16. #include <sys/ioctl.h>  
  17. #include <stdlib.h>  
  18. #include <linux/types.h>  
  19. #include <linux/videodev2.h>  
  20. #include <malloc.h>  
  21. #include <math.h>  
  22. #include <string.h>  
  23. #include <sys/mman.h>  
  24. #include <errno.h>  
  25. #include <assert.h>  
  26.   
  27. #define FILE_VIDEO  "/dev/video0"  
  28. #define JPG "./out/image%d"  
  29.   
  30. typedef struct{  
  31.     voidvoid *start;  
  32.     int length;  
  33. }BUFTYPE;  
  34.   
  35. BUFTYPE *usr_buf;  
  36. static unsigned int n_buffer = 0;  
  37.   
  38.   
  39. /*set video capture ways(mmap)*/  
  40. int init_mmap(int fd)  
  41. {  
  42.     /*to request frame cache, contain requested counts*/  
  43.     struct v4l2_requestbuffers reqbufs;  
  44.   
  45.     memset(&reqbufs, 0sizeof(reqbufs));  
  46.     reqbufs.count = 1;                              /*the number of buffer*/  
  47.     reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;      
  48.     reqbufs.memory = V4L2_MEMORY_MMAP;                
  49.   
  50.     if(-1 == ioctl(fd,VIDIOC_REQBUFS,&reqbufs))  
  51.     {  
  52.         perror("Fail to ioctl 'VIDIOC_REQBUFS'");  
  53.         exit(EXIT_FAILURE);  
  54.     }  
  55.       
  56.     n_buffer = reqbufs.count;  
  57.     printf("n_buffer = %d\n", n_buffer);  
  58.     //usr_buf = calloc(reqbufs.count, sizeof(usr_buf));  
  59.     usr_buf = calloc(reqbufs.countsizeof(BUFTYPE));  
  60.     if(usr_buf == NULL)  
  61.     {  
  62.         printf("Out of memory\n");  
  63.         exit(-1);  
  64.     }  
  65.   
  66.     /*map kernel cache to user process*/  
  67.     for(n_buffer = 0; n_buffer < reqbufs.count; ++n_buffer)  
  68.     {  
  69.         //stand for a frame  
  70.         struct v4l2_buffer buf;  
  71.         memset(&buf, 0sizeof(buf));  
  72.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  73.         buf.memory = V4L2_MEMORY_MMAP;  
  74.         buf.index = n_buffer;  
  75.           
  76.         /*check the information of the kernel cache requested*/  
  77.         if(-1 == ioctl(fd,VIDIOC_QUERYBUF,&buf))  
  78.         {  
  79.             perror("Fail to ioctl : VIDIOC_QUERYBUF");  
  80.             exit(EXIT_FAILURE);  
  81.         }  
  82.   
  83.         usr_buf[n_buffer].length = buf.length;  
  84.         usr_buf[n_buffer].start = (charchar *)mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED, fd,buf.m.offset);  
  85.   
  86.         if(MAP_FAILED == usr_buf[n_buffer].start)  
  87.         {  
  88.             perror("Fail to mmap");  
  89.             exit(EXIT_FAILURE);  
  90.         }  
  91.   
  92.     }  
  93.   
  94. }  
  95.   
  96. int open_camera(void)  
  97. {  
  98.     int fd;  
  99.     struct v4l2_input inp;  
  100.   
  101.     fd = open(FILE_VIDEO, O_RDWR | O_NONBLOCK,0);  
  102.     if(fd < 0)  
  103.     {     
  104.         fprintf(stderr, "%s open err \n", FILE_VIDEO);  
  105.         exit(EXIT_FAILURE);  
  106.     };  
  107.   
  108.     inp.index = 0;  
  109.     if (-1 == ioctl (fd, VIDIOC_S_INPUT, &inp))  
  110.     {  
  111.         fprintf(stderr, "VIDIOC_S_INPUT \n");  
  112.     }  
  113.   
  114.     return fd;  
  115. }  
  116.   
  117. int init_camera(int fd)  
  118. {  
  119.     struct v4l2_capability  cap;    /* decive fuction, such as video input */  
  120.     struct v4l2_format      tv_fmt; /* frame format */    
  121.     struct v4l2_fmtdesc     fmtdesc;    /* detail control value */  
  122.     struct v4l2_control     ctrl;  
  123.     int ret;  
  124.       
  125.             /*show all the support format*/  
  126.     memset(&fmtdesc, 0sizeof(fmtdesc));  
  127.     fmtdesc.index = 0 ;                 /* the number to check */  
  128.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  129.   
  130.     /* check video decive driver capability */  
  131.     if(ret=ioctl(fd, VIDIOC_QUERYCAP, &cap)<0)  
  132.     {  
  133.         fprintf(stderr, "fail to ioctl VIDEO_QUERYCAP \n");  
  134.         exit(EXIT_FAILURE);  
  135.     }  
  136.       
  137.     /*judge wherher or not to be a video-get device*/  
  138.     if(!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE))  
  139.     {  
  140.         fprintf(stderr, "The Current device is not a video capture device \n");  
  141.         exit(EXIT_FAILURE);  
  142.     }  
  143.   
  144.     /*judge whether or not to supply the form of video stream*/  
  145.     if(!(cap.capabilities & V4L2_CAP_STREAMING))  
  146.     {  
  147.         printf("The Current device does not support streaming i/o\n");  
  148.         exit(EXIT_FAILURE);  
  149.     }  
  150.       
  151.     printf("\ncamera driver name is : %s\n",cap.driver);  
  152.     printf("camera device name is : %s\n",cap.card);  
  153.     printf("camera bus information: %s\n",cap.bus_info);  
  154.   
  155.     /*display the format device support*/  
  156.     printf("\n");  
  157.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)  
  158.     {     
  159.         printf("support device %d.%s\n",fmtdesc.index+1,fmtdesc.description);  
  160.         fmtdesc.index++;  
  161.     }  
  162.     printf("\n");  
  163.   
  164.     /*set the form of camera capture data*/  
  165.     tv_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;      /*v4l2_buf_typea,camera must use V4L2_BUF_TYPE_VIDEO_CAPTURE*/  
  166.     tv_fmt.fmt.pix.width = 680;  
  167.     tv_fmt.fmt.pix.height = 480;  
  168.     tv_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;   /*V4L2_PIX_FMT_YYUV*/  
  169.     tv_fmt.fmt.pix.field = V4L2_FIELD_NONE;         /*V4L2_FIELD_NONE*/  
  170.     if (ioctl(fd, VIDIOC_S_FMT, &tv_fmt)< 0)   
  171.     {  
  172.         fprintf(stderr,"VIDIOC_S_FMT set err\n");  
  173.         exit(-1);  
  174.         close(fd);  
  175.     }  
  176.   
  177.     init_mmap(fd);  
  178. }  
  179.   
  180. int start_capture(int fd)  
  181. {  
  182.     unsigned int i;  
  183.     enum v4l2_buf_type type;  
  184.       
  185.     /*place the kernel cache to a queue*/  
  186.     for(i = 0; i < n_buffer; i++)  
  187.     {  
  188.         struct v4l2_buffer buf;  
  189.         memset(&buf, 0sizeof(buf));  
  190.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  191.         buf.memory = V4L2_MEMORY_MMAP;  
  192.         buf.index = i;  
  193.   
  194.         if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))  
  195.         {  
  196.             perror("Fail to ioctl 'VIDIOC_QBUF'");  
  197.             exit(EXIT_FAILURE);  
  198.         }  
  199.     }  
  200.   
  201.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  202.     if(-1 == ioctl(fd, VIDIOC_STREAMON, &type))  
  203.     {  
  204.         printf("i=%d.\n", i);  
  205.         perror("VIDIOC_STREAMON");  
  206.         close(fd);  
  207.         exit(EXIT_FAILURE);  
  208.     }  
  209.   
  210.     return 0;  
  211. }  
  212.   
  213.   
  214. int process_image(voidvoid *addr, int length)  
  215. {  
  216.     FILEFILE *fp;  
  217.   
  218.     static int num = 0;  
  219.   
  220.     char image_name[20];  
  221.     sprintf(image_name, JPG, num++);  
  222.     if((fp = fopen(image_name, "w")) == NULL)  
  223.     {  
  224.         perror("Fail to fopen");  
  225.         exit(EXIT_FAILURE);  
  226.     }  
  227.     fwrite(addr, length, 1, fp);  
  228.     usleep(500);  
  229.     fclose(fp);  
  230.     return 0;  
  231. }  
  232.   
  233. int read_frame(int fd)  
  234. {  
  235.     struct v4l2_buffer buf;  
  236.     unsigned int i;  
  237.     memset(&buf, 0sizeof(buf));  
  238.     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  239.     buf.memory = V4L2_MEMORY_MMAP;  
  240.     //put cache from queue  
  241.     if(-1 == ioctl(fd, VIDIOC_DQBUF,&buf))  
  242.     {  
  243.         perror("Fail to ioctl 'VIDIOC_DQBUF'");  
  244.         exit(EXIT_FAILURE);  
  245.     }  
  246.     assert(buf.index < n_buffer);  
  247.   
  248.     //read process space's data to a file  
  249.     process_image(usr_buf[buf.index].start, usr_buf[buf.index].length);  
  250.     if(-1 == ioctl(fd, VIDIOC_QBUF,&buf))  
  251.     {  
  252.         perror("Fail to ioctl 'VIDIOC_QBUF'");  
  253.         exit(EXIT_FAILURE);  
  254.     }  
  255.     return 1;  
  256. }  
  257.   
  258.   
  259. int mainloop(int fd)  
  260. {  
  261.     int count = 10;  
  262.     while(count-- > 0)  
  263.     {  
  264.         for(;;)  
  265.         {  
  266.             fd_set fds;  
  267.             struct timeval tv;  
  268.             int r;  
  269.   
  270.             FD_ZERO(&fds);  
  271.             FD_SET(fd,&fds);  
  272.   
  273.             /*Timeout*/  
  274.             tv.tv_sec = 2;  
  275.             tv.tv_usec = 0;  
  276.             r = select(fd + 1,&fds,NULL,NULL,&tv);  
  277.               
  278.             if(-1 == r)  
  279.             {  
  280.                  if(EINTR == errno)  
  281.                     continue;  
  282.                 perror("Fail to select");  
  283.                 exit(EXIT_FAILURE);  
  284.             }  
  285.             if(0 == r)  
  286.             {  
  287.                 fprintf(stderr,"select Timeout\n");  
  288.                 exit(-1);  
  289.             }  
  290.   
  291.             if(read_frame(fd))  
  292.             break;  
  293.         }  
  294.     }  
  295.     return 0;  
  296. }  
  297.   
  298. void stop_capture(int fd)  
  299. {  
  300.     enum v4l2_buf_type type;  
  301.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  302.     if(-1 == ioctl(fd,VIDIOC_STREAMOFF,&type))  
  303.     {  
  304.         perror("Fail to ioctl 'VIDIOC_STREAMOFF'");  
  305.         exit(EXIT_FAILURE);  
  306.     }  
  307. }  
  308.   
  309. void close_camera_device(int fd)  
  310. {  
  311.     unsigned int i;  
  312.     for(i = 0;i < n_buffer; i++)  
  313.     {  
  314.         if(-1 == munmap(usr_buf[i].start,usr_buf[i].length))  
  315.         {  
  316.             exit(-1);  
  317.         }  
  318.     }  
  319.   
  320.     free(usr_buf);  
  321.   
  322.     if(-1 == close(fd))  
  323.     {  
  324.         perror("Fail to close fd");  
  325.         exit(EXIT_FAILURE);  
  326.     }  
  327. }  
  328.   
  329.   
  330. void main(void)  
  331. {  
  332.     int fd;  
  333.     fd = open_camera();  
  334.     init_camera(fd);  
  335.     start_capture(fd);  
  336.     mainloop(fd);  
  337.     stop_capture(fd);  
  338.     close_camera_device(fd);  
  339. }  
    该代码由上一章的代码移植过来,主要是针对我的板子移植,主要修改了:

(1)camera 打开由阻塞打开改为了非阻塞方式打开

(2)mmap 由原来的MAP_PRIVATE 模式改为MAP_SHARED方式

(3)imag 格式由原来的V4L2_PIX_FMT_JPEG 改为V4L2_PIX_FMT_YUV420

(4)在我的开发板中,必须设定camera 设备的索引号,也就是上面代码中的ioctl (fd, VIDIOC_S_INPUT, &inp) ,值为inp.index = 0; 如果不设置,select的时候会出现select   timeout 的问题。

    将上面代码交叉编译后放到开发板上去运行,结果如下:

[objc]  view plain  copy
  1. /tmp # ./test   
  2.   
  3. camera driver name is : sunxi-vfe  
  4. camera device name is : sunxi-vfe  
  5. camera bus information: sunxi_vfe vfe.2  
  6.   
  7. support device 1.planar YUV 422  
  8. support device 2.planar YUV 420  
  9. support device 3.planar YVU 420  
  10. support device 4.planar YUV 422 UV combined  
  11. support device 5.planar YUV 420 UV combined  
  12. support device 6.planar YUV 422 VU combined  
  13. support device 7.planar YUV 420 VU combined  
  14. support device 8.MB YUV420  
  15. support device 9.YUV422 YUYV  
  16. support device 10.YUV422 YVYU  
  17. support device 11.YUV422 UYVY  
  18. support device 12.YUV422 VYUY  
  19. support device 13.RAW Bayer BGGR 8bit  
  20. support device 14.RAW Bayer GBRG 8bit  
  21. support device 15.RAW Bayer GRBG 8bit  
  22. support device 16.RAW Bayer RGGB 8bit  
  23. support device 17.RAW Bayer BGGR 10bit  
  24. support device 18.RAW Bayer GBRG 10bit  
  25. support device 19.RAW Bayer GRBG 10bit  
  26. support device 20.RAW Bayer RGGB 10bit  
  27. support device 21.RAW Bayer BGGR 12bit  
  28. support device 22.RAW Bayer GBRG 12bit  
  29. support device 23.RAW Bayer GRBG 12bit  
  30. support device 24.RAW Bayer RGGB 12bit  
  31.   
  32. n_buffer = 3  
  33. /tmp # ls  
  34. hostapd   messages  out       test      utmp  
  35. /tmp # cd out  
  36. /tmp/out # ls  
  37. image0  image2  image4  image6  image8  
  38. image1  image3  image5  image7  image9  
  39. /tmp/out # ls -l  
  40. total 4520  
  41. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image0  
  42. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image1  
  43. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image2  
  44. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image3  
  45. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image4  
  46. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image5  
  47. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image6  
  48. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image7  
  49. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image8  
  50. -rw-r--r--    1 root     root        460800 Jan  1 00:01 image9  
  51. /tmp/out #   
    从我们输出的信息可以看出,在我开发板上可以支持24种image格式,在这了我们后面H264的编码,这里选用的是V4L2_PIX_FMT_YUV420 格式。我开发板上装的是GC0308 VGA 摄像头,从生成image的大小可以判断出是正确的(YUV420数据大小 = 长 * 宽 * 1.5 = 640 * 480 * 1.5 = 460800 = 450k)

可以将image文件拷出来,使用pYUV 软件查看YUV图片。这里需要注意,使用pYUV 查看YUV图片的时候,需要正确设置图片格式,按我上面代码采集的数据格式,其设置如下图:


正常打开显示的图片为:


到这里采集YUV数据就结束了,下一章介绍使用X264库将YUV数据编码成H264视频文件。


这篇关于V4L2视频采集与H264编码2—v4l2采集YUV数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040690

相关文章

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

MySQL中查询和展示LONGBLOB类型数据的技巧总结

《MySQL中查询和展示LONGBLOB类型数据的技巧总结》在MySQL中LONGBLOB是一种二进制大对象(BLOB)数据类型,用于存储大量的二进制数据,:本文主要介绍MySQL中查询和展示LO... 目录前言1. 查询 LONGBLOB 数据的大小2. 查询并展示 LONGBLOB 数据2.1 转换为十

使用SpringBoot+InfluxDB实现高效数据存储与查询

《使用SpringBoot+InfluxDB实现高效数据存储与查询》InfluxDB是一个开源的时间序列数据库,特别适合处理带有时间戳的监控数据、指标数据等,下面详细介绍如何在SpringBoot项目... 目录1、项目介绍2、 InfluxDB 介绍3、Spring Boot 配置 InfluxDB4、I