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

相关文章

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左

SpringBoot中4种数据水平分片策略

《SpringBoot中4种数据水平分片策略》数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题,下面小编就来和大家分享4种数据分片策略吧... 目录一、前言二、哈希分片2.1 原理2.2 SpringBoot实现2.3 优缺点分析2.4 适用场景三、范围分片

Redis分片集群、数据读写规则问题小结

《Redis分片集群、数据读写规则问题小结》本文介绍了Redis分片集群的原理,通过数据分片和哈希槽机制解决单机内存限制与写瓶颈问题,实现分布式存储和高并发处理,但存在通信开销大、维护复杂及对事务支持... 目录一、分片集群解android决的问题二、分片集群图解 分片集群特征如何解决的上述问题?(与哨兵模

浅析如何保证MySQL与Redis数据一致性

《浅析如何保证MySQL与Redis数据一致性》在互联网应用中,MySQL作为持久化存储引擎,Redis作为高性能缓存层,两者的组合能有效提升系统性能,下面我们来看看如何保证两者的数据一致性吧... 目录一、数据不一致性的根源1.1 典型不一致场景1.2 关键矛盾点二、一致性保障策略2.1 基础策略:更新数

Oracle 数据库数据操作如何精通 INSERT, UPDATE, DELETE

《Oracle数据库数据操作如何精通INSERT,UPDATE,DELETE》在Oracle数据库中,对表内数据进行增加、修改和删除操作是通过数据操作语言来完成的,下面给大家介绍Oracle数... 目录思维导图一、插入数据 (INSERT)1.1 插入单行数据,指定所有列的值语法:1.2 插入单行数据,指

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名