应用层编程:将缓存放入队列中
struct v4l2_buffer v4l2_buffer;
int number_buffers = 5;
for(i = 0; i < number_buffers; i++)
{
memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
v4l2_buffer.index = i; //想要放入队列的缓存index
v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buffer);
......
}
vidioc_qbuf->vb2_ioctl_qbuf
int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct video_device *vdev = video_devdata(file);
/*判断vdev->queue->owner && vdev->queue->owner != file->private_data
* 在前面qbuf的时候也用到
*/
if (vb2_queue_is_busy(vdev, file))
return -EBUSY;
return vb2_qbuf(vdev->queue, p);
}
vidioc_qbuf->vb2_ioctl_qbuf->vb2_qbuf
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
/*判断q->fileio的值,默认为0*/
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
return vb2_internal_qbuf(q, b);
}
vidioc_qbuf->vb2_ioctl_qbuf->vb2_qbuf->vb2_internal_qbuf
static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
/*
* 查询buffer的type,memory类型是否匹配,后面分析
*/
int ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
struct vb2_buffer *vb;
if (ret)
return ret;
/*取出要放入队列的缓存*/
vb = q->bufs[b->index];
switch (vb->state) {
/*在reqbuf的时候设置为VB2_BUF_STATE_DEQUEUED*/
case VB2_BUF_STATE_DEQUEUED:
ret = __buf_prepare(vb, b);
if (ret)
return ret;
break;
case VB2_BUF_STATE_PREPARED:
break;
case VB2_BUF_STATE_PREPARING:
dprintk(1, "buffer still being prepared\n");
return -EINVAL;
default:
dprintk(1, "invalid buffer state %d\n", vb->state);
return -EINVAL;
}
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
*/
/*将要放入队列缓存的queued_entry放入vb2_queue的queued_list双向链表中*/
list_add_tail(&vb->queued_entry, &q->queued_list);
/*放入队列的buffer引用计数+1*/
q->queued_count++;
q->waiting_for_buffers = false;
/*设置vb->state的状态已放入队列*/
vb->state = VB2_BUF_STATE_QUEUED;
/*不成立*/
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
/*
* For output buffers copy the timestamp if needed,
* and the timecode field and flag if needed.
*/
if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
V4L2_BUF_FLAG_TIMESTAMP_COPY)
vb->v4l2_buf.timestamp = b->timestamp;
vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
if (b->flags & V4L2_BUF_FLAG_TIMECODE)
vb->v4l2_buf.timecode = b->timecode;
}
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
//如果已经streaming,不成立
if (q->start_streaming_called)
__enqueue_in_driver(vb);
/* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
/*
* If streamon has been called, and we haven't yet called
* start_streaming() since not enough buffers were queued, and
* we now have reached the minimum number of queued buffers,
* then we can finally call start_streaming().
*/
//如果正在streaming,但是streaming启动完毕,并且入队列的buffer
//大于等于vb2_queue需要的最少buffer个数,不成立
if (q->streaming && !q->start_streaming_called &&
q->queued_count >= q->min_buffers_needed) {
ret = vb2_start_streaming(q);
if (ret)
return ret;
}
dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
return 0;
}
__fill_v4l2_buffer
static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
struct vb2_queue *q = vb->vb2_queue;
/* Copy back data such as timestamp, flags, etc. */
/*拷贝vb->v4l2_buf中m变量对应地址偏移大小的内容给v4l2_buffer*/
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
b->reserved2 = vb->v4l2_buf.reserved2;
b->reserved = vb->v4l2_buf.reserved;
/*如果是多平面视频方式,不成立*/
if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
/*
* Fill in plane-related data if userspace provided an array
* for it. The caller has already verified memory and size.
*/
/*直接将vb->num_planes赋值给传下来的b->length*/
b->length = vb->num_planes;
/*将vb->v4l2_planes中的内容拷贝给b->m.planes*/
memcpy(b->m.planes, vb->v4l2_planes,
b->length * sizeof(struct v4l2_plane));
} else {/*如果是单平面视频方式*/
/*
* We use length and offset in v4l2_planes array even for
* single-planar buffers, but userspace does not.
*/
/*将平面的大小赋值给b->length*/
b->length = vb->v4l2_planes[0].length;
/*帧数据的大小,这里为0,后面的分析中可以看到更新
* 注意应用层需要使用bytesused表示帧数据大小
*/
b->bytesused = vb->v4l2_planes[0].bytesused;
/*使用mmap方式,获取request_buffer的mem_offset
* 对于user_ptr获取userptr,dma_buffer获取fd
*/
if (q->memory == V4L2_MEMORY_MMAP)
b->m.offset = vb->v4l2_planes[0].m.mem_offset;
else if (q->memory == V4L2_MEMORY_USERPTR)
b->m.userptr = vb->v4l2_planes[0].m.userptr;
else if (q->memory == V4L2_MEMORY_DMABUF)
b->m.fd = vb->v4l2_planes[0].m.fd;
}
/*
* Clear any buffer state related flags.
*/
/*
* q->timestamp_flags 初始值 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC| V4L2_BUF_FLAG_TSTAMP_SRC_EOF
* 在xilinx-dma.c文件中初始化
* 表示为递增类型,它在内核中的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;
if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) !=
V4L2_BUF_FLAG_TIMESTAMP_COPY) {
/*
* 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;
}
/*在reqbuf将vb->state设置为VB2_BUF_STATE_DEQUEUED*/
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:
/* nothing */
break;
}
if (__buffer_in_use(q, vb))
b->flags |= V4L2_BUF_FLAG_MAPPED;
}
接着分析__buffer_in_use
static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
{
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane) {
/*获取reqbuf时设置的mem_priv*/
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.
*/
/*判断mem_priv是否被设置并且判断num_users回调函数的返回值是否大于1
表示之前已经调用reqbuf
*/
if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
return true;
}
return false;
}
call_memop(vb, num_users, mem_priv)会调用vb2_dma_contig_memops->vb2_dc_num_users
static unsigned int vb2_dc_num_users(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
/*读取buf->refcount的值,在reqbuf时设置为1*/
return atomic_read(&buf->refcount);
}
大概就是将需要放入队列的buffer放入vb2_queue队列中,并填充v4l2_buffer的信息返回给应用层