VIDIOC_REQBUFS向驱动申请帧缓冲代码详解

  • 1. 概述
  • 2. 应用层
  • 3. 内核驱动
  • 3.1 vb2_ioctl_reqbufs函数
  • 3.2 vb2_core_reqbufs函数
  • 3.3 __vb2_queue_alloc函数
  • 3.4 __setup_offsets函数
  • 3.5 __vb2_buf_mem_alloc函数

1. 概述

  • VIDIOC_REQBUFS 请求在内核空间分配视频缓冲区

  • 功能
    请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存)
    V4L2是视频设备的驱动层,位于内核空间,所以通过 VIDIOC_REQBUFS控制命令申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数,把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。

  • 参数说明
    参数类型为 V4L2的申请缓冲区数据结构体类型 struct v4l2_requestbuffersv4l2_requestbuffers 结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
    一般不超过5个,CAP_BUF_NUM = 4

  • 返回值说明
    执行成功时,函数返回值为 0V4L2驱动层分配好了视频缓冲区;

2. 应用层

struct v4l2_requestbuffers {  __u32           count;  __u32           type;       /* enum v4l2_buf_type */  __u32           memory;     /* enum v4l2_memory */  __u32           reserved[2];
};  struct v4l2_requestbuffers req;
/* 申请设备的缓存区 */
memset(&req, 0, sizeof(req));
req.count = CAP_BUF_NUM;  //申请一个拥有四个缓冲帧的缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;if (ioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0)
{if (EINVAL == errno){printf(stderr, "%s does not support ""memory mapping\n", dev->dev);return TFAIL;}else{printf(stderr, "%s does not support ""memory mapping, unknow error\n", dev->dev);return TFAIL;}
}
if (req.count < 2)
{printf(stderr, "Insufficient buffer memory on %s\n",dev->dev);return TFAIL;
}

3. 内核驱动

3.1 vb2_ioctl_reqbufs函数

/* vb2 ioctl helpers */int vb2_ioctl_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
{// 获取devstruct video_device *vdev = video_devdata(file);// 检查队列memory类型是否一致,不一致则直接退出int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type);if (res)return res;if (vb2_queue_is_busy(vdev, file))return -EBUSY;res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count);/** 注意*     1. 当count=0的时候,释放所有buffer*//* If count == 0, then the owner has released all buffers and heis no longer owner of the queue. Otherwise we have a new owner. */if (res == 0)vdev->queue->owner = p->count ? file->private_data : NULL;return res;
}
EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);

3.2 vb2_core_reqbufs函数

int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count)
{unsigned int num_buffers, allocated_buffers, num_planes = 0;// #define VB2_MAX_PLANES  (8)unsigned plane_sizes[VB2_MAX_PLANES] = { };int ret;// 正在取流中if (q->streaming) {dprintk(1, "streaming active\n");return -EBUSY;}/** 注意* 1。 这里进行count=0判断,memory类型判断,删除所有buffer,所以为什么应用层会进行0 buffer的申请操作。*/if (*count == 0 || q->num_buffers != 0 || q->memory != memory) {/** We already have buffers allocated, so first check if they* are not in use and can be freed.*/mutex_lock(&q->mmap_lock);if (q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) {mutex_unlock(&q->mmap_lock);dprintk(1, "memory in use, cannot free\n");return -EBUSY;}/** Call queue_cancel to clean up any buffers in the PREPARED or* QUEUED state which is possible if buffers were prepared or* queued without ever calling STREAMON.*/__vb2_queue_cancel(q);ret = __vb2_queue_free(q, q->num_buffers);mutex_unlock(&q->mmap_lock);if (ret)return ret;/** In case of REQBUFS(0) return immediately without calling* driver's queue_setup() callback and allocating resources.*/if (*count == 0)return 0;}/** Make sure the requested values and current defaults are sane.*/// #define VB2_MAX_FRAME   (32)num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);// min_buffers_needed = 2一般为2num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);memset(q->alloc_devs, 0, sizeof(q->alloc_devs));q->memory = memory;// 从驱动层获取到可以支持多少buffers和planes per buffer/** Ask the driver how many buffers and planes per buffer it requires.* Driver also sets the size and allocator context for each plane.*/ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, plane_sizes, q->alloc_devs);if (ret)return ret;// 最后分配buffer/* Finally, allocate buffers and video memory */allocated_buffers =__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);if (allocated_buffers == 0) {dprintk(1, "memory allocation failed\n");return -ENOMEM;}// 检查是否大于最小需求buffer/** There is no point in continuing if we can't allocate the minimum* number of buffers needed by this vb2_queue.*/if (allocated_buffers < q->min_buffers_needed)ret = -ENOMEM;/** Check if driver can handle the allocated number of buffers.*/if (!ret && allocated_buffers < num_buffers) {num_buffers = allocated_buffers;/** num_planes is set by the previous queue_setup(), but since it* signals to queue_setup() whether it is called from create_bufs()* vs reqbufs() we zero it here to signal that queue_setup() is* called for the reqbufs() case.*/num_planes = 0;ret = call_qop(q, queue_setup, q, &num_buffers,&num_planes, plane_sizes, q->alloc_devs);if (!ret && allocated_buffers < num_buffers)ret = -ENOMEM;/** Either the driver has accepted a smaller number of buffers,* or .queue_setup() returned an error*/}mutex_lock(&q->mmap_lock);q->num_buffers = allocated_buffers;if (ret < 0) {/** Note: __vb2_queue_free() will subtract 'allocated_buffers'* from q->num_buffers.*/__vb2_queue_free(q, allocated_buffers);mutex_unlock(&q->mmap_lock);return ret;}mutex_unlock(&q->mmap_lock);/** 注意*   1.这里对应用层传递过来的count进行了,已经分配buffer数量的更新操作。*//** Return the number of successfully allocated buffers* to the userspace.*/*count = allocated_buffers;q->waiting_for_buffers = !q->is_output;return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_reqbufs);

3.3 __vb2_queue_alloc函数

/*** __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)* video buffer memory for all buffers/planes on the queue and initializes the* queue** Returns the number of buffers successfully allocated.*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,unsigned int num_buffers, unsigned int num_planes,const unsigned plane_sizes[VB2_MAX_PLANES])
{unsigned int buffer, plane;struct vb2_buffer *vb;int ret;/* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */num_buffers = min_t(unsigned int, num_buffers,VB2_MAX_FRAME - q->num_buffers);// 分配 vidoe buffer了for (buffer = 0; buffer < num_buffers; ++buffer) {/* Allocate videobuf buffer structures */vb = kzalloc(q->buf_struct_size, GFP_KERNEL);if (!vb) {dprintk(1, "memory alloc for buffer struct failed\n");break;}// 设置video buffer的数据成员vb->state = VB2_BUF_STATE_DEQUEUED;vb->vb2_queue = q;vb->num_planes = num_planes;vb->index = q->num_buffers + buffer;vb->type = q->type;vb->memory = memory;for (plane = 0; plane < num_planes; ++plane) {vb->planes[plane].length = plane_sizes[plane];vb->planes[plane].min_length = plane_sizes[plane];}/** 注意*  1. 这里使用的idx索引*/q->bufs[vb->index] = vb;/* Allocate video buffer memory for the MMAP type */if (memory == VB2_MEMORY_MMAP){ret = __vb2_buf_mem_alloc(vb);if (ret) {dprintk(1, "failed allocating memory for buffer %d\n",buffer);q->bufs[vb->index] = NULL;kfree(vb);break;}__setup_offsets(vb);/** Call the driver-provided buffer initialization* callback, if given. An error in initialization* results in queue setup failure.*/ret = call_vb_qop(vb, buf_init, vb);if (ret) {dprintk(1, "buffer %d %p initialization failed\n",buffer, vb);__vb2_buf_mem_free(vb);q->bufs[vb->index] = NULL;kfree(vb);break;}}}dprintk(1, "allocated %d buffers, %d plane(s) each\n",buffer, num_planes);return buffer;
}

3.4 __setup_offsets函数

// 其实主要就是内存模块的对齐 page
/*** __setup_offsets() - setup unique offsets ("cookies") for every plane in* the buffer.*/
static void __setup_offsets(struct vb2_buffer *vb)
{struct vb2_queue *q = vb->vb2_queue;unsigned int plane;unsigned long off = 0;if (vb->index) {struct vb2_buffer *prev = q->bufs[vb->index - 1];struct vb2_plane *p = &prev->planes[prev->num_planes - 1];off = PAGE_ALIGN(p->m.offset + p->length);}for (plane = 0; plane < vb->num_planes; ++plane) {vb->planes[plane].m.offset = off;dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",vb->index, plane, off);off += vb->planes[plane].length;off = PAGE_ALIGN(off);}
}

3.5 __vb2_buf_mem_alloc函数

/*** __vb2_buf_mem_alloc() - allocate video memory for the given buffer*/
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{struct vb2_queue *q = vb->vb2_queue;void *mem_priv;int plane;int ret = -ENOMEM;/** Allocate memory for all planes in this buffer* NOTE: mmapped areas should be page aligned*/for (plane = 0; plane < vb->num_planes; ++plane) {// page对齐unsigned long size = PAGE_ALIGN(vb->planes[plane].length);// 内核mmapmem_priv = call_ptr_memop(vb, alloc,q->alloc_devs[plane] ? : q->dev,q->dma_attrs, size, q->dma_dir, q->gfp_flags);if (IS_ERR_OR_NULL(mem_priv)) {if (mem_priv)ret = PTR_ERR(mem_priv);goto free;}/* Associate allocator private data with this plane */vb->planes[plane].mem_priv = mem_priv;}return 0;
free:/* Free already allocated memory if one of the allocations failed */for (; plane > 0; --plane) {call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);vb->planes[plane - 1].mem_priv = NULL;}return ret;
}

【genius_platform软件平台开发】第五十八讲:Linux系统之V4L2视频驱动-VIDIOC_REQBUFS向驱动申请帧缓冲代码详解相关推荐

  1. 【genius_platform软件平台开发】第九十八讲:嵌入式网络接口(MAC、PHY)

    1. 嵌入式网络简介 1.1 嵌入式下的网络硬件接口 提起网络,我们一般想到的硬件就是"网卡",现在网卡已经是通过一个芯片来完成了,嵌入式网络硬件分为两部分:MAC和PHY,大家都 ...

  2. 【genius_platform软件平台开发】第二十八讲:NEON指令集优化(附实例)

    当在ARM芯片上进行一些例如图像处理等计算的时候,常常会因为计算量太大造成计算帧率较低的情况.因而,需要选择一种更加简单快捷的计算方式以获得处理速度上的提升.ARM NEON就是一个不错的选择. ※ ...

  3. 【genius_platform软件平台开发】第九十二讲:vs开发环境中sln、vcxproj、vcxproj.filters、sdf、ilk、pdb、pch、pbd等文件含义

    拿libusb开源库的vs工程中为例: 1. 创建工程生产 1.1 *.sln sln是解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需的信息 microsoft官方说明:Soluti ...

  4. 【genius_platform软件平台开发】第九十四讲:int64_t的格式化问题(lld和PRId64)

    问题起因是在进行上位机软件优化的工作安排时,同事对unsigned long long 类型的时间戳进行了格式化输出优化,从%ull优化为了% PRIu64,我进行代码合并请求处理的时候突然感觉这个可 ...

  5. 王佩丰excel学习笔记(五):第十五——十八讲

    目录 第十五讲 第十六讲 第十七讲 第十八讲 第十五讲 根据某些条件突出显示单元格:开始-条件格式-突出显示单元格规则 制作数据范围趋势:开始-条件格式-数据条 用于分组统计:插入-切片器 多重条件格 ...

  6. 【genius_platform软件平台开发】第五十二讲:Linux系统之V4L2视频驱动详解

    V4L2视频驱动详解 刚建的微信群欢迎加入一起学习.讨论: 1. 简介 1.1 视频输入输出设备(video capture device,video output device) 1.2 VBI设备 ...

  7. 跨平台应用开发进阶(五十八):短链基本工作原理与实现方案

    文章目录 一.短链是什么 二.为什么要使用短链 三.短链跳转基本原理 四.短链实现步骤 五.使用哈希算法生成短链 5.1 其他方法生成短链 5.2 实现方案 六.短链生成工具 6.1 网站工具:ft1 ...

  8. 【genius_platform软件平台开发】第九十三讲:串口通信(485通信)

    485通信 1. 485通信 1.1 termios结构 1.2 头文件 1.3 函数讲解 1.3.1 tcgetattr 1.3.2 tcsetattr 1.4 示例工程 1.5 参考文献 1.5. ...

  9. 【genius_platform软件平台开发】第三十七讲:网卡混杂模式和原始套接字

    Linux下网卡的混杂模式 混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址.普通模式下网卡只接收发给本机的包(包括广播包)传递给上层程序,其它的包一律丢弃.一般来说,混 ...

最新文章

  1. java soap附件_java - 附件在SoapUI中工作,但在Java中不能使用SAAJ API吗? - 堆栈内存溢出...
  2. 【SeeMusic】创建 SeeMusic 工程并编辑相关内容 ( 创建工程 | 导入 MIDI 文件 | 导入音频 | 导入视频 )
  3. 汇编--条件跳转指令
  4. 傅里叶变换性质证明卷积_积分变换(3)——傅里叶变换的性质
  5. 关于kafka生产者相关监控指标的理解(未解决)
  6. python3程序设计基础刘德山答案_Python 3 程序设计学习指导与习题解答
  7. 在上司面前硬不起来?教你如何快速将字符串转换为可执行代码
  8. 奇点汽车黄浴:SLAM的动态地图和语义问题
  9. Linux基本命令之pidof和kill/killall
  10. React Native常用组件之ListView组件
  11. 膝盖中了一箭之康复篇-第九个月暨3月份目标总结
  12. php生成一个500错误_Hyperf 发布 v2.0.1 版本 | 企业级的 PHP 微服务云原生协程框架...
  13. c语言王者荣耀程序代码,王者荣耀:张大仙直言C语言上手并不难,玩家:张工程师成了?...
  14. c语言怎么设置命令行字体大小,C语言入门教程-命令行参数
  15. 大数据架构与技术——(一)大数据概述
  16. JavaScript的三座大山--(2)--作用域和闭包
  17. 微信小程序接收二进制流文件(图片预览,文件打开)
  18. 怎么给其他账号的服务器备案
  19. ubuntu和windows双系统默认启动顺序
  20. win10如何设置有线和无线同时上内网和外网?

热门文章

  1. RNN-bptt简单推导
  2. 2023年中职组“网络空间安全”赛项XX市竞赛任务书
  3. 英语六级真题高频词汇之听力
  4. IOT | 令人心动的小米生态链
  5. 嵌入式工程师值得一看的网站及工具总结(2023)
  6. 彻底删除linux的python和pip,linux下安装python和pip
  7. VBA 批量打印多工作簿的指定工作表
  8. 剑指offer——javascript编程
  9. 6-1 图书列表 (25 分)
  10. 一个Java小白面试得力集团的收获