zoukankan      html  css  js  c++  java
  • V4L2框架下采集JPEG图像内存溢出

    问题描述:
    采集格式 JPEG,1280*720,申请BUFFER的数量:4
    向设备申请缓存区时,内核报错,显示剩余内存不足

    [<c0011725>] (unwind_backtrace) from [<c000fdd3>] (show_stack+0xb/0xc)
    [<c000fdd3>] (show_stack) from [<c00815c9>] (dump_header+0x45/0x120)
    [<c00815c9>] (dump_header) from [<c00604b1>] (oom_kill_process+0x4d/0x2e0)
    [<c00604b1>] (oom_kill_process) from [<c00609e1>] (out_of_memory+0x1b1/0x294)
    [<c00609e1>] (out_of_memory) from [<c0062fb7>] (__alloc_pages_nodemask+0x617/0x68c)
    [<c0062fb7>] (__alloc_pages_nodemask) from [<c007bd87>] (__vmalloc_node_range+0xb7/0x120)
    [<c007bd87>] (__vmalloc_node_range) from [<c007be17>] (__vmalloc_node+0x27/0x34)
    [<c007be17>] (__vmalloc_node) from [<c007becf>] (vmalloc_user+0x1d/0x3a)
    [<c007becf>] (vmalloc_user) from [<bf9813fb>] (vb2_vmalloc_alloc+0x2e/0x74 [videobuf2_vmalloc])
    [<bf9813fb>] (vb2_vmalloc_alloc [videobuf2_vmalloc]) from [<bf96f515>] (vb2_queue_error+0xec/0x3b4 [videobuf2_core])
    [<bf96f515>] (vb2_queue_error [videobuf2_core]) from [<bf96fd89>] (vb2_core_reqbufs+0xe8/0x1b4 [videobuf2_core])
    [<bf96fd89>] (vb2_core_reqbufs [videobuf2_core]) from [<bf97b7df>] (vb2_reqbufs+0x1a/0x1c [videobuf2_v4l2])
    [<bf97b7df>] (vb2_reqbufs [videobuf2_v4l2]) from [<bf986c35>] (uvc_request_buffers+0x18/0x2a [uvcvideo])
    [<bf986c35>] (uvc_request_buffers [uvcvideo]) from [<bf987c09>] (uvc_ioctl_reqbufs+0x26/0x21a [uvcvideo])
    [<bf987c09>] (uvc_ioctl_reqbufs [uvcvideo]) from [<c014cdf1>] (__video_do_ioctl+0xf9/0x1ac)
    [<c014cdf1>] (__video_do_ioctl) from [<c014cb0d>] (video_usercopy+0x14d/0x32c)
    [<c014cb0d>] (video_usercopy) from [<c01487f9>] (v4l2_ioctl+0x4d/0x6c)
    [<c01487f9>] (v4l2_ioctl) from [<c008da25>] (vfs_ioctl+0x11/0x1c)
    [<c008da25>] (vfs_ioctl) from [<c008e087>] (do_vfs_ioctl+0x577/0x66c)
    [<c008e087>] (do_vfs_ioctl) from [<c008e19f>] (SyS_ioctl+0x23/0x40)
    [<c008e19f>] (SyS_ioctl) from [<c000d2e1>] (ret_fast_syscall+0x1/0x54)
    Mem-Info:
    active_anon:141 inactive_anon:0 isolated_anon:0
     active_file:17 inactive_file:25 isolated_file:0
     unevictable:0 dirty:0 writeback:0 unstable:0
     slab_reclaimable:81 slab_unreclaimable:952
     mapped:42 shmem:0 pagetables:15 bounce:0
     free:479 free_pcp:0 free_cma:338
    Node 0 active_anon:564kB inactive_anon:0kB active_file:68kB inactive_file:100kB unevictable:0kB isolated(anon):0kB isolated(file):0kB mapped:168kB dirty:0kB writeback:0kB shmem:0kB writeback_tmp:0kB unstable:0kB pages_scanned:0 all_unreclaimable? yes
    Normal free:1916kB min:644kB low:804kB high:964kB active_anon:564kB inactive_anon:0kB active_file:68kB inactive_file:100kB unevictable:0kB writepending:0kB present:65408kB managed:28048kB mlocked:0kB slab_reclaimable:324kB slab_unreclaimable:3808kB kernel_stack:392kB pagetables:60kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:1352kB
    lowmem_reserve[]: 0 0
    Normal: 17*4kB (MC) 21*8kB (MEC) 19*16kB (UMC) 7*32kB (UMC) 4*64kB (MC) 1*128kB (C) 1*256kB (C) 1*512kB (C) 0*1024kB 0*2048kB = 1916kB
    42 total pagecache pages
    16352 pages RAM
    0 pages HighMem/MovableOnly
    9340 pages reserved
    512 pages cma reserved
    报错信息

    定位到该段代码:

     1 /*set video capture ways(mmap)*/
     2 int Init_mmap(int fd) {
     3     /*to request frame cache, contain requested counts*/
     4     struct v4l2_requestbuffers reqbufs;
     5 
     6     memset(&reqbufs, 0, sizeof(reqbufs));
     7     reqbufs.count = BUFFER_NUM; /*the number of buffer*/
     8     reqbufs.type = V4L2_BUF_TYPE;
     9     reqbufs.memory = V4L2_MEMORY_MMAP;
    10 
    11     if (-1 == ioctl(fd, VIDIOC_REQBUFS, &reqbufs)) {
    12         perror("Fail to ioctl 'VIDIOC_REQBUFS'");
    13         return -1;
    14     }
    15     sleep(1);    
    16 
    17     n_buffer = reqbufs.count;
    18     printf("n_buffer = %d\n", n_buffer);
    19     usr_buf = (BUFTYPE *)calloc(reqbufs.count, sizeof(BUFTYPE));
    20     if (usr_buf == NULL) {
    21         printf("Out of memory\n");
    22         return -1;
    23     }
    24 
    25     /*map kernel cache to user process*/
    26     for (n_buffer = 0; n_buffer < reqbufs.count; n_buffer++) {
    27         // stand for a frame
    28         struct v4l2_buffer buf;
    29         memset(&buf, 0, sizeof(buf));
    30         buf.type = V4L2_BUF_TYPE;
    31         buf.memory = V4L2_MEMORY_MMAP;
    32         buf.index = n_buffer;
    33 
    34         /*check the information of the kernel cache requested*/
    35         if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf)) {
    36             perror("Fail to ioctl : VIDIOC_QUERYBUF");
    37             return -1;
    38         }
    39       
    40         usr_buf[n_buffer].length = buf.length;
    41         usr_buf[n_buffer].start =
    42             (char *)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
    43                          fd, buf.m.offset);
    44 
    45         if (MAP_FAILED == usr_buf[n_buffer].start) {
    46             perror("Fail to mmap");
    47             return -1;
    48         }
    49     }
    50 
    51     return 0;
    52 }

    在第11行,向设备申请缓存空间时报错。将分辨率改小或者将buffer数量减少,就不会报错。

    关于视频采集方式

    操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2属于内核的驱动层,所以捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

    一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。

    • read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。
    • 内存映射方式:把设备里的内存映射到应用程序中的内存空间,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。
    • 用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。
    分析一下代码
    第4行 :struct v4l2_requestbuffers reqbufs;

    这个reqbufs,在驱动里用来装图像数据(缓冲帧),驱动处数据存完会才可以出队。如果只分配1个buf,当出队后并映射到用户空间后,驱动就没有buf可以装图像数据了。
    而且这个buf是用连续的物理内存的,不可以使用太多,至少要保证>=2, 方便用户程序和驱动使用。
    定义该结构体后,初始化结构体,用type来区分流或者缓冲区,count是所需buffer数量,memory设置为v4l2_MEMORY_MMAP。

    第11行 :ioctl(fd, VIDIOC_REQBUFS, &reqbufs)

    调用IOCTL,其中有三个参数,fd,告诉驱动控制的是哪个设备,此处就是/dev/video0,第二个控制命令VIDIOC_REQBUFS,告诉驱动要为该设备在内核空间申请一片连续的物理内存来存放缓存帧,那第三个参数就说明了需要申请多少个缓冲区,视频捕获类型,以及通过什么方式使用该内存区。这只是申请,也许实际申请到的buffer个数并不等于count。

     第26行:mmap

    申请之后通过 VIDIOC_QUERYBUF 查询BUFFER的状态信息,将获取到的缓存帧地址和长度等信息填充到 v4l2_buffer buf 中,再通过mmap将内核地址空间映射到用户空间,数据就可以被应用程序使用了。

    (buffer分配好后,应用程序和驱动如何进行数据的存取呢?详见V4L2 API详解 Buffer的准备和数据读取

    那么问题来了,该段程序并没有传给驱动每个buffer的大小,那么分配buffer的大小是根据什么来的呢?分配buffer的数量又如何确定?

    猜想:
    在YUYV模式下,每帧的大小是固定的,实际上使用的buffer的大小可以是定值。但是工作在MJPEG模式下,每帧的大小会根据图像的复杂度不同而变化,如果分配固定值,很可能会溢出,为了避免该情况,只能先分配一个足够大的缓存,那么这个最大值应该是驱动定义或者有摄像头硬件芯片返回得到。在Init_mmap之前,我们会设置
    v4l2_format ,在其中初始化分辨率与工作模式,驱动应该是由此来决定buffer的大小。

    那么问题又来了?为什么当buffer数量为4,分辨率为720P的时候,会显示剩余内存不足?申请了多少,还差多少?

    v4l2的学习建议和流程解析

     V4L2视频采集的基本流程

    Linux下V4l2接口摄像头图像数据采集问题

    ioctl VIDIOC_REQBUFS

    V4L2 API详解 Buffer的准备和数据读取

  • 相关阅读:
    一个编译器的实现0
    《穿越计算机的迷雾》笔记
    C#WinForm应用程序实现自动填充网页上的用户名和密码并点击登录按钮
    一个编译器的实现2——从文法到LL(1)分析表的概念和算法
    使用百度地图API的例子
    过桥问题 Bridge and torch problem
    (译)跟媳妇解释面向对象设计
    批量照片缩小器展示多线程控件BackgroundWorker后台工作使用方法
    图解:邮件(消息)的加密解密和数字签名
    一个编译器的实现1——开篇
  • 原文地址:https://www.cnblogs.com/y4247464/p/15724746.html
Copyright © 2011-2022 走看看