zoukankan      html  css  js  c++  java
  • android摄像头(camera)之buffer管理

    一,V4L2驱动申请buffer

    视频应用可以通过两种方式从V4L2驱动申请buffer

    1. V4L2_MEMORY_USERPTR方式, 顾名思义是用户空间指针的意思,应用层负责分配需要的内存空间,然后以指针的形式传递给V4L2驱动层,V4L2驱动会把capture的内容保存到指针所指的空间

    一般来说,应用层需要确保这个内存空间物理上是连续的(IPU处理单元的需求),在android系统可以通过PMEM驱动来分配大块的连续物理内存。应用层在不需要的时候要负责释放申请的PMEM内存。

    2. V4L2_MEMORY_MMAP方式,内存映射模式,应用调用VIDIOC_REQBUFS ioctl分配设备buffers,参数标识需要的数目和类型。这个ioctl也可以用来改变buffers的数据以及释放分配的内存,当然

    这个内存空间一般也是连续的。在应用空间能够访问这些物理地址之前,必须调用mmap函数把这些物理空间映射为用户虚拟地址空间。

      虚拟地址空间是通过munmap函数释放的; 而物理内存的释放是通过VIDIOC_REQBUFS来实现的(设置参数buf count为(0)),物理内存的释放是实现特定的,mx51 v4l2是在关闭设备时进行释放

    的。所以二者都是申请连续的物理内存,只是申请和释放的方式不同

    2.V4L2_MEMORY_USERPTRqualcommandroid 4.1后使用ion方式分配内存,原来是使用pmem分配内存,先从hal层申请内存空间,然后给camera驱动用的。

    二,camera的测试代码流程

    1,打开设备

    static int open_device(char *dev_name)

    {

    assert(dev_name);

    int fd = -1;

    fd = open(dev_name , O_RDWR);

    if( -1 == fd ) {

    MYLOGD("open %s fail: %s ", dev_name, strerror (errno));

    exit(EXIT_FAILURE);

    }

    MYLOGD("the fd of %s is %d ", dev_name, fd);

    return fd;

    }



    2,初始化camera,设置camera输出图像的格式

    static int init_cam_device(int dev_fd)

    {

    int ret = -1;

    int input_index;


    //ret = fimc_v4l2_querycap(dev_fd);

    //assert(ret == 0);

    //获取到输入源通道

    input_index = cam_v4l2_enuminput(dev_fd);

    assert(input_index == 0);

     

    ret = cam_v4l2_s_input(dev_fd, input_index);

    assert(ret == 0);


    MYLOGD("VIDIOC_S_FMT start... dev_fd = %d ", dev_fd);


    ret = cam_v4l2_s_format(dev_fd, IMAGE_HEIGHT, IMAGE_WIDTH,

    V4L2_PIX_FMT_YUYV);

    assert(ret == 0);


    ret = cam_v4l2_g_fmt(dev_fd);

    assert(ret == 0);

     

    init_cam_mmap(dev_fd);

     

    return 1;

     

    }


    三,向内核申请buffer,并将buffer映射mmap到引用空间

    1)数据结构

      struct buffer{

         void *start; //mmap后的地址;
         size_t length//大小;
      }user_buffers[4]; //用于记录将内核buffer映射mmap到用户空间的地址和大小

     

    2)申请4buffer

      camera_v4l2_reqbuf(dev_fd, 4);


    3)查询申请到的buffer信息,比如每个buffer的其实位置和大小

      camera_v4l2_querybuf(dev_fd, &buf, buf_index);

     

    4)将buffer映射到用户空间

      user_buffers[buf_index].length = buf.length;

      user_buffers[buf_index].start = mmap(NULLbuf.length,PROT_READ | PROT_WRITE /* required */ ,MAP_SHARED /* recommended */ ,dev_fd, buf.m.offset);

      注释: NULL/* start anywhere */

      buf.lengthbuffer在内核的地址

      buf.m.offset:内核中的buffer大小

    5),将所有的buffer全部放到循环工作队列中,集中管理

      for (i = 0; i < max_buffers; ++i)
      {
        camera_v4l2_qbuf(dev_fd, i);
      }

    6),开始获取图像:

      ioctl(dev_fd, VIDIOC_STREAMON, &type);

    7),通过select来监控camera数据是否准备好

      FD_ZERO (&rd_set);
      FD_SET (dev_fd, &rd_set);

      ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL);

      if(FD_ISSET(dev_fd, &rd_set))//如果camera准备好,就可以去读数据了

        read_frame(dev_fd)

    8)read_frame(dev_fd)的实现:

      //select仅仅是知道有数据可以读了,但是在多个buffer中,

      //不知道是哪个buffer准备好了,所以将准备好的buffer出队列,从而知道buffer的编号

      int index = camera_v4l2_dqbuf(dev_fd);

      //buffer中的yuv420数据转换成rgb565

      //因为lcd是没办法显示yuv的,所以需要将yuv转换成rgb565

      yuyv_to_rgb(src_address, data_buf);

      //将转换好的rgb565数据方到framebuffer中去显示

      show_rgb565_img(data_buf, LCD_WIDTH, LCD_HEIGHT);


    四,高通buffer管理使用代码流程

    一,HAL->kernel 分配buffer
    mm_stream_request_buf->//Mm_camera_stream.c (8953_dual_camerasrchardwareqcomcameraqcamera2stackmm-camera-interfacesrc)    
        ioctl(my_obj->fd, VIDIOC_REQBUFS, &bufreq);//向kernel发送IOCTL命令,请求分配buffer
    {=================kernel===================<
            v4l_reqbufs->//V4l2-ioctl.c (8953_dual_camerasrckernelmsm-3.18driversmediav4l2-core)    
                                        //ops->vidioc_reqbufs(file, fh, p)
                camera_v4l2_reqbufs->//Camera.c (camera)    
                    vb2_reqbufs->//Videobuf2-core.c (8953_dual_camerasrckernelmsm-3.18driversmediav4l2-core)    
                        __reqbufs->//
                            __vb2_queue_alloc->
                                __vb2_buf_mem_alloc->//分配video buffer
                                                                         //最后通过mmap函数将buffer映射到用户空间
     =================kernel===================>
    }
    二,HAL->kernel 读取一帧数据
    mm_stream_read_msm_frame->//Mm_camera_stream.c (8953_dual_camerasrchardwareqcomcameraqcamera2stackmm-camera-interfacesrc)    
        ioctl(my_obj->fd, VIDIOC_DQBUF, &vb);//向kernel发送IOCTL命令,向kernel读取一帧数据
                                                                                 //kernel流程同【一】
     
    三,HAL->kernel 向kernel buffer释放一个buffer,使kernel可以填充新的视频数据
    mm_stream_qbuf->//Mm_camera_stream.c (8953_dual_camerasrchardwareqcomcameraqcamera2stackmm-camera-interfacesrc)    
         ioctl(my_obj->fd, VIDIOC_QBUF, &buffer);//向kernel发送IOCTL命令,向kernel释放buffer,让kernel可以填充新的视频数据
                                                                                         //kernel流程同【一】
     


     

  • 相关阅读:
    【小梅哥SOPC学习笔记】Altera SOPC嵌入式系统设计教程
    modelsim使用常见问题及解决办法集锦③
    modelsim使用常见问题及解决办法集锦 ②
    KeepAlived双主模式高可用集群
    充分利用nginx的reload功能平滑的上架和更新业务
    nginx日志配置指令详解
    MongoDB 副本集
    MongoDB 备份还原
    MongoDB的搭建、参数
    mongoDB整个文件夹拷贝备份还原的坑
  • 原文地址:https://www.cnblogs.com/shelley-AL/p/10683234.html
Copyright © 2011-2022 走看看