Linux v4l2架构学习总链接
step 5 : 设置缓存
2,查询并映射缓存
memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
v4l2_buffer.index = i; //想要查询的缓存
v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;
/* 查询缓存信息 */
ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buffer);
调用vidioc_querybuf
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
return vb2_querybuf(&dev->vb_vidq, p);
}
int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
int ret;
/*
* 查询的类型和申请的buffer的类型要匹配
*/
if (b->type != q->type) {
dprintk(1, "wrong buffer type\n");
return -EINVAL;
}
/*
* buffer个数有限
*/
if (b->index >= q->num_buffers) {
dprintk(1, "buffer index out of range\n");
return -EINVAL;
}
/*
* 取出对应编号的vb2_buffer
*/
vb = q->bufs[b->index];
/*
* 检测是不是多平面视频格式
*/
ret = __verify_planes_array(vb, b);
if (!ret)
/*
* 这里分析非多平面视频格式的情况
*/
vb2_core_querybuf(q, b->index, b);
return ret;
}
对应q->buf_ops->fill_user_buffer
buf_ops是在vb2_queue_init中注册的,对应v4l2_buf_ops
调用__fill_v4l2_buffer
static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
{
struct v4l2_buffer *b = pb;
/*
* 对于vb2_v4l2_buffer好像是第一次见到,这里分析一下来源
* 先说一下这个函数的2个参数
* vb是来源于bufs[index],申请buffer的时候填充的
* pb则是来自于用户空间的地址
* vbuf = to_vb2_v4l2_buffer(vb)
* 说明和vb有关系
* __vb2_queue_alloc中vb创建如下
* vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
* 而q->buf_struct_size值在
* vb2_queue_init
* if (q->buf_struct_size == 0)
* q->buf_struct_size = sizeof(struct vb2_v4l2_buffer);
* 也就是不为0的时候,这里会赋值
* 但是vivi驱动中已经对其赋值了
* q->buf_struct_size = sizeof(struct vivi_buffer);
* 这样就找到了,对应的vb2_v4l2_buffer
* 实际看vivi_buffer,
* struct vivi_buffer {
* struct vb2_buffer vb;
* struct list_head list;
* struct vivi_fmt *fmt;
* }
* 没看到vb2_v4l2_buffer相关的东西
* 于是去看最新的vivi驱动代码,如下
* struct vivid_buffer {
* struct vb2_v4l2_buffer vb;
* struct list_head list;
* };
* 这里就可以看到vb2_v4l2_buffer的来源了
* 其实主要原因还是vivi驱动太老,linux内核太新不匹配造成的
*/
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
/*
* 以下就是填充用户空间需要的信息
*/
/* Copy back data such as timestamp, flags, etc. */
b->index = vb->index;
b->type = vb->type;
b->memory = vb->memory;
b->bytesused = 0;
b->flags = vbuf->flags;
b->field = vbuf->field;
b->timestamp = ns_to_timeval(vb->timestamp);
b->timecode = vbuf->timecode;
/*
* 这里的sequence值应该是用户表示帧数据编号
*/
b->sequence = vbuf->sequence;
b->reserved2 = 0;
b->reserved = 0;
/* 针对多平面视频格式,这里暂时不分析 */
if (q->is_multiplanar) {
/*
* Fill in plane-related data if userspace provided an array
* for it. The caller has already verified memory and size.
*/
b->length = vb->num_planes;
for (plane = 0; plane < vb->num_planes; ++plane) {
struct v4l2_plane *pdst = &b->m.planes[plane];
struct vb2_plane *psrc = &vb->planes[plane];
pdst->bytesused = psrc->bytesused;
pdst->length = psrc->length;
if (q->memory == VB2_MEMORY_MMAP)
pdst->m.mem_offset = psrc->m.offset;
else if (q->memory == VB2_MEMORY_USERPTR)
pdst->m.userptr = psrc->m.userptr;
else if (q->memory == VB2_MEMORY_DMABUF)
pdst->m.fd = psrc->m.fd;
pdst->data_offset = psrc->data_offset;
memset(pdst->reserved, 0, sizeof(pdst->reserved));
}
} else {
/*
* We use length and offset in v4l2_planes array even for
* single-planar buffers, but userspace does not.
*/
/*
* length: 平面的大小
* byteused: 帧数据的大小,这里为0,后面的分析中可以看到更新
* 应用代码中有些会用length代表帧数据的大小,这个值有可能是不准确的
* 比如帧大小是4812Byte,那么驱动中有可能为了方便,将length设置为5000
* 所以最好使用byteused
*/
b->length = vb->planes[0].length;
b->bytesused = vb->planes[0].bytesused;
/*
* 对于MMAP类型
* 获取之前计算的offset
* 也许这里还没有看明白offset的作用
* 等后面mmap的时候就可以明白了
*/
if (q->memory == VB2_MEMORY_MMAP)
b->m.offset = vb->planes[0].m.offset;
else if (q->memory == VB2_MEMORY_USERPTR)
b->m.userptr = vb->planes[0].m.userptr;
else if (q->memory == VB2_MEMORY_DMABUF)
b->m.fd = vb->planes[0].m.fd;
}
/*
* Clear any buffer state related flags.
*/
/*
* q->timestamp_flags 初始值 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
* 表示使用内核的 monotonic 时钟生成的递增的时间戳
* V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC & V4L2_BUF_FLAG_TIMESTAMP_MASK
* 值 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
*/
b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
/*
* copy_timestamp值为0,if满足
*/
if (!q->copy_timestamp) {
/*
* For non-COPY timestamps, drop timestamp source bits
* and obtain the timestamp source from the queue.
*/
b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
}
/*
* 对于vb->state的值
* 在使用reqbuf的时候VIDIOC_REQBUFS,看上一篇
* vb->state = VB2_BUF_STATE_DEQUEUED
* 表示该buffer在用户空间控制,直接break
*/
switch (vb->state) {
case VB2_BUF_STATE_QUEUED:
case VB2_BUF_STATE_ACTIVE:
b->flags |= V4L2_BUF_FLAG_QUEUED;
break;
case VB2_BUF_STATE_ERROR:
b->flags |= V4L2_BUF_FLAG_ERROR;
/* fall through */
case VB2_BUF_STATE_DONE:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
case VB2_BUF_STATE_PREPARED:
b->flags |= V4L2_BUF_FLAG_PREPARED;
break;
case VB2_BUF_STATE_PREPARING:
case VB2_BUF_STATE_DEQUEUED:
case VB2_BUF_STATE_REQUEUEING:
/* nothing */
break;
}
/*
* 对于 vb2_buffer_in_use后面分析
*/
if (vb2_buffer_in_use(q, vb))
b->flags |= V4L2_BUF_FLAG_MAPPED;
/*
* 说实话这里我看了大半个小时没有看明白,为什么要加这个
* 对分析vivi来说,这都是新申请的内存,为什么会有这么多的flags
* 这里解释一下下面的flags
* V4L2_BUF_FLAG_DONE
* 设置此标志后,缓冲区当前位于传出队列中,准备从驱动程序中出队
* 调用VIDIOC_QUERYBUF ioctl时,驱动程序会设置或清除此标志
* 调用VIDIOC_QBUF或VIDIOC_DQBUF之后,始终将其清除。
* 当然,缓冲区不能同时在两个队列上,V4L2_BUF_FLAG_QUEUED和
* V4L2_BUF_FLAG_DONE标志是互斥的。
*
* V4L2_BUF_FLAG_LAST
* 硬件产生的最后一个缓冲区。 当调用ioctl VIDIOC_QUERYBUF或
* VIDIOC_DQBUF ioctl时,mem2mem编解码器驱动程序在捕获队列上
* 为最后一个缓冲区设置此标志。 由于硬件限制,最后一个缓冲区可能为空。
* 在这种情况下,驱动程序会将字节使用的字段设置为0,而不考虑格式。
* 任何后续对VIDIOC_DQBUF ioctl的调用将不再阻塞,而是返回EPIPE错误代码。
*
* last_buffer_dequeued
* 如果最后一个已解码的缓冲区已经出队,
* 则可在poll()和DQBUF中使用以立即返回。
* 最后一个缓冲区出列是根据上面的V4L2_BUF_FLAG_LAST判断的
*
* 总的来说,如果last_buffer_dequeued设置了,那么poll()和DQBUF中
* 不需要等待,可以直接返回
*
* 现在相关的标志解释完了,解释一下为什么会出现在这里,毕竟追到现在还没什么帧数据
* 产生。
*
* 个人理解是这个是用在DQBUF中的,DQBUF主要是获取数据,而且也会用到这个函数
*/
if (!q->is_output &&
b->flags & V4L2_BUF_FLAG_DONE &&
b->flags & V4L2_BUF_FLAG_LAST)
q->last_buffer_dequeued = true;
分析一下上面使用到的last_buffer_dequeued
bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
{
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane) {
void *mem_priv = vb->planes[plane].mem_priv;
/*
* If num_users() has not been provided, call_memop
* will return 0, apparently nobody cares about this
* case anyway. If num_users() returns more than 1,
* we are not the only user of the plane's memory.
*/
/*
* 分析每一个plane的buffer使用情况
* 主意这里判断是要大于1
*/
if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
return true;
}
return false;
}
对应vb->vb2_queue->mem_ops->num_users
vb2_vmalloc_memops.num_users,,就是vb2_vmalloc_num_users
static unsigned int vb2_vmalloc_num_users(void *buf_priv)
{
struct vb2_vmalloc_buf *buf = buf_priv;
return refcount_read(&buf->refcount);
}
可以看到就是读取refcount的值,这个变量我们之前分析过,VIDIOC_REQBUFS的时候,
vb2_vmalloc_alloc将这个值设置为1。
老路子,继续写应用程序测试
struct v4l2_buffer buf;
for(i = 0; i < req.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {
printf("Querybuf fail\n");
goto err;
}
printf("buffer[%d]: length = %d, offset = %d\n", i, buf.length, buf.m.offset);
}
输出结果
buffer[0]: length = 4147200, offset = 0
buffer[1]: length = 4147200, offset = 4149248
buffer[2]: length = 4147200, offset = 8298496
buffer[3]: length = 4147200, offset = 12447744
分析一下这个结果,帧数据大小为1920*1080*2 = 4147200
而且是但平面视频格式
offset是length经过也对齐后的,4147200页对齐后大小为4149248
所以就出现了上面的打印结果