【genius_platform软件平台开发】第五十八讲:Linux系统之V4L2视频驱动-VIDIOC_REQBUFS向驱动申请帧缓冲代码详解
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_requestbuffers
;v4l2_requestbuffers
结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
一般不超过5
个,CAP_BUF_NUM = 4
返回值说明:
执行成功时,函数返回值为0
;V4L2
驱动层分配好了视频缓冲区;
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向驱动申请帧缓冲代码详解相关推荐
- 【genius_platform软件平台开发】第九十八讲:嵌入式网络接口(MAC、PHY)
1. 嵌入式网络简介 1.1 嵌入式下的网络硬件接口 提起网络,我们一般想到的硬件就是"网卡",现在网卡已经是通过一个芯片来完成了,嵌入式网络硬件分为两部分:MAC和PHY,大家都 ...
- 【genius_platform软件平台开发】第二十八讲:NEON指令集优化(附实例)
当在ARM芯片上进行一些例如图像处理等计算的时候,常常会因为计算量太大造成计算帧率较低的情况.因而,需要选择一种更加简单快捷的计算方式以获得处理速度上的提升.ARM NEON就是一个不错的选择. ※ ...
- 【genius_platform软件平台开发】第九十二讲:vs开发环境中sln、vcxproj、vcxproj.filters、sdf、ilk、pdb、pch、pbd等文件含义
拿libusb开源库的vs工程中为例: 1. 创建工程生产 1.1 *.sln sln是解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需的信息 microsoft官方说明:Soluti ...
- 【genius_platform软件平台开发】第九十四讲:int64_t的格式化问题(lld和PRId64)
问题起因是在进行上位机软件优化的工作安排时,同事对unsigned long long 类型的时间戳进行了格式化输出优化,从%ull优化为了% PRIu64,我进行代码合并请求处理的时候突然感觉这个可 ...
- 王佩丰excel学习笔记(五):第十五——十八讲
目录 第十五讲 第十六讲 第十七讲 第十八讲 第十五讲 根据某些条件突出显示单元格:开始-条件格式-突出显示单元格规则 制作数据范围趋势:开始-条件格式-数据条 用于分组统计:插入-切片器 多重条件格 ...
- 【genius_platform软件平台开发】第五十二讲:Linux系统之V4L2视频驱动详解
V4L2视频驱动详解 刚建的微信群欢迎加入一起学习.讨论: 1. 简介 1.1 视频输入输出设备(video capture device,video output device) 1.2 VBI设备 ...
- 跨平台应用开发进阶(五十八):短链基本工作原理与实现方案
文章目录 一.短链是什么 二.为什么要使用短链 三.短链跳转基本原理 四.短链实现步骤 五.使用哈希算法生成短链 5.1 其他方法生成短链 5.2 实现方案 六.短链生成工具 6.1 网站工具:ft1 ...
- 【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. ...
- 【genius_platform软件平台开发】第三十七讲:网卡混杂模式和原始套接字
Linux下网卡的混杂模式 混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址.普通模式下网卡只接收发给本机的包(包括广播包)传递给上层程序,其它的包一律丢弃.一般来说,混 ...
最新文章
- java soap附件_java - 附件在SoapUI中工作,但在Java中不能使用SAAJ API吗? - 堆栈内存溢出...
- 【SeeMusic】创建 SeeMusic 工程并编辑相关内容 ( 创建工程 | 导入 MIDI 文件 | 导入音频 | 导入视频 )
- 汇编--条件跳转指令
- 傅里叶变换性质证明卷积_积分变换(3)——傅里叶变换的性质
- 关于kafka生产者相关监控指标的理解(未解决)
- python3程序设计基础刘德山答案_Python 3 程序设计学习指导与习题解答
- 在上司面前硬不起来?教你如何快速将字符串转换为可执行代码
- 奇点汽车黄浴:SLAM的动态地图和语义问题
- Linux基本命令之pidof和kill/killall
- React Native常用组件之ListView组件
- 膝盖中了一箭之康复篇-第九个月暨3月份目标总结
- php生成一个500错误_Hyperf 发布 v2.0.1 版本 | 企业级的 PHP 微服务云原生协程框架...
- c语言王者荣耀程序代码,王者荣耀:张大仙直言C语言上手并不难,玩家:张工程师成了?...
- c语言怎么设置命令行字体大小,C语言入门教程-命令行参数
- 大数据架构与技术——(一)大数据概述
- JavaScript的三座大山--(2)--作用域和闭包
- 微信小程序接收二进制流文件(图片预览,文件打开)
- 怎么给其他账号的服务器备案
- ubuntu和windows双系统默认启动顺序
- win10如何设置有线和无线同时上内网和外网?