videobuf是应用程序和v4l2驱动程序的一个中间层,用它来进行视频数据缓冲区的分配和管理。
它根据应用程序的需求(缓冲区的数量的大小),分配相应的视频缓冲区,这个缓冲区是在内核空间分配的,并通过mmap方法映射到用户空间,在内核空间形成一个缓冲区队列,在应用程序中有相应的缓冲区数组对应,它们指向的内存地址是一样的。在驱动程序中,根据配置的硬件参数(FIFO阈值),将vip硬件图像存储器中的数据放到缓冲区队列中的 每个缓冲区,然后等待应用程序来读取该缓冲区的数据。videobuf主要由一些特殊的数据结构和ioctl调用组成,下边对其做整体分析:
一、 初始化
初始化缓冲区队列:
1、驱动层的调用
在v4l2_vip.c文件中:
static int vip_open(struct file *file)
{
struct vip_dev *dev = video_drvdata(file);
struct vip_fh *fh = NULL;
int retval = 0;
、、、、、、、、、
/*allocate and initialize per filehandle data*/
fh = kzalloc(sizeof(*fh), GFP_KERNEL);//此时已经把vb_vidq成员的空间分配好
if(NULL == fh){
dev->users--;
retval = -ENOMEM;
}
、、、、、、、、、、
fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fh->fmt = &formats[0];
、、、、、、、、
/*初始化videobuf缓冲区队列 vip_video_qops 是驱动层的videobuf回掉函数*/
videobuf_queue_vmalloc_init(&fh->vb_vidq, &vip_video_qops, NULL,
&dev->slock, fh->type, V4L2_FIELD_INTERLACED,
sizeof(struct vip_buffer), fh);
return 0;
}
2、videobuf层的操作
在videobuf-vmalloc.c中:
videobuf_queue_vmalloc_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv
)
{
videobuf_queue_core_init(q, ops, dev, irlock, type, field, msize, priv, &qops);
}
//上边函数多了一个qops参数,它是 videobuf层的数据操作函数集
void videobuf_queue_core_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv,
struct videobuf_qtype_ops *int_ops)
{
BUG_ON(!q);
memset(q, 0, sizeof(*q));
//初始化队列的一些成员
q->irqlock = irqlock;
q->dev = dev;
q->type = type;
q->field = field;
q->msize = msize;
q->ops = ops;
q->priv_data = priv;
q->int_ops = int_ops;
//检测必要的成员数据是否不为空
/* All buffer operations are mandatory */
BUG_ON(!q->ops->buf_setup);
BUG_ON(!q->ops->buf_prepare);
BUG_ON(!q->ops->buf_queue);
BUG_ON(!q->ops->buf_release);
/* Lock is mandatory for queue_cancel to work */
BUG_ON(!irqlock);
/* Having implementations for abstract methods are mandatory */
BUG_ON(!q->int_ops);
mutex_init(&q->vb_lock);//初始化自旋锁
init_waitqueue_head(&q->wait);//初始化videobuf队列queue中的等待队列
INIT_LIST_HEAD(&q->stream);//初始化链表
}
二、 videobuf的相关ioctl()操作
这些操作有三部分组成,1是应用程序通过ioctl函数调用v4l2驱动中实现的接口函数,2这些接口函数在调用videobuf层的响应函数来做出处理,3如果有的处理参数需要v4l2层来决定的话,就调用v4l2层的videobuf设置函数。
1、ioctl接口函数列表
static const struct v4l2_ioctl_ops vip_capture_ioctl_fops = {
、、、、、、
.vidioc_reqbufs = vip_reqbufs,
.vidioc_querybuf = vip_querybuf,
.vidioc_qbuf = vip_qbuf,
.vidioc_dqbuf = vip_dqbuf,
.vidioc_streamon = vip_streamon,
.vidioc_streamoff = vip_streamoff,
、、、、、、
};
2、v4l2层设置回掉函数列表
Static struct videobuf_queue_ops vip_video_qops = {
.buf_setup = buffer_setbuf,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
}
三、函数调用关系分析
1、请求视频流缓冲区
/*Request video streaming buffer memory VIDIOC_REABUFS*/
v4l2_vip.c
A、vip_reqbufs
static int vip_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct vip_fh *fh = priv;
return (videobuf_reqbufs(&fh->vb_vidq, p));
}
Videobuf-core.c
请求缓冲区,主要是给
B、videobuf_reqbufs
int videobuf_reqbufs(struct videobuf_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int size, count;
int retval;
?略过参数检测?
count = req->count;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
size = 0;
调用v4l2驱动实现的参数这只函数buffer大小参数
q->ops->buf_setup(q, &count, &size);
size = PAGE_ALIGN(size);//和下一页对其
dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
count, size, (count*size)>>PAGE_SHIFT);
//分配缓冲区并返回实际分配的缓冲区个数
retval = __videobuf_mmap_setup(q, count, size, req->memory);
if (retval < 0) {
dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
goto done;
}
req->count = retval;
retval = 0;
、、、、、、
}
C、__videobuf_mmap_setup
/* Locking: Caller holds q->vb_lock */
int __videobuf_mmap_setup(struct videobuf_queue *q,
unsigned int bcount, unsigned int bsize, enum v4l2_memory memory)
{
unsigned int i;
int err;
MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
//清空之前用的videobuf缓冲区
err = __videobuf_mmap_free(q);
if (0 != err)
return err;
/* Allocate and initialize buffers */
for (i = 0; i < bcount; i++) {
q->bufs[i] = videobuf_alloc(q);//分配bcount个struct videobuf_buffer
if (q->bufs[i] == NULL)
break;
//初始化基本的参数
q->bufs[i]->i = i;
q->bufs[i]->input = UNSET;
q->bufs[i]->memory = memory;
q->bufs[i]->bsize = bsize;
switch (memory) {
case V4L2_MEMORY_MMAP:
q->bufs[i]->boff = bsize * i;
break;
case V4L2_MEMORY_USERPTR:
case V4L2_MEMORY_OVERLAY:
/* nothing */
break;
}
}
if (!i)
return -ENOMEM;
dprintk(1, "mmap setup: %d buffers, %d bytes each\n",
i, bsize);
return i;
}
D、videobuf_alloc
void *videobuf_alloc(struct videobuf_queue *q)
{
struct videobuf_buffer *vb;
BUG_ON(q->msize < sizeof(*vb));
if (!q->int_ops || !q->int_ops->alloc) {
printk(KERN_ERR "No specific ops defined!\n");
BUG();
}
vb = q->int_ops->alloc(q->msize);
if (NULL != vb) {
init_waitqueue_head(&vb->done);//初始化videobuf缓冲区中的等待队列
vb->magic = MAGIC_BUFFER;
}
return vb;
}
在videobuf-vmalloc.c中
/* ---------------------------------------------------------------------
* vmalloc handlers for the generic methods
/* Allocated area consists on 3 parts:
struct video_buffer
struct _buffer (cx88_buffer, saa7134_buf, ...)
struct videobuf_dma_sg_memory
*/
E、__videobuf_alloc
分配缓冲区的内存空间
static void *__videobuf_alloc(size_t size)
{
struct videobuf_vmalloc_memory *mem;
struct videobuf_buffer *vb;
//给上边两个数据结构分配空间,size是在open()函数中初始化videobuf时传进来的最顶层的包含videobuf_buffer的设备数据结构体的大小,所以该设备结构体的第一个成员就要是videobuf_buffer。
vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);
//注意vb->priv是硬件视频数据到用户空间的中转缓冲区
mem = vb->priv = ((char *)vb)+size;
mem->magic=MAGIC_VMAL_MEM;
dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
__func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), mem,(long)sizeof(*mem));
//返回分配的struct videobuf_buffer结构体指针
return vb;
}
2、获取指定缓冲区的属性
应用程序通过VIDIOC_QUERYBUF ioctl()命令获取上边分配的缓冲区的一些属性,一边将其mmap到用户空间。
A、vip_querybuf
static int vip_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vip_fh *fh = priv;
return (videobuf_querybuf(&fh->vb_vidq, p));
}
B、videobuf_querybuf
Int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
int ret = -EINVAL;
mutex_lock(&q->vb_lock);
//参数检测
、、、、、
//获取指定videobuf_buffer的一些信息,b->index是应用程序传过来的
videobuf_status(q, b, q->bufs[b->index], q->type);
ret = 0;
done:
mutex_unlock(&q->vb_lock);
return ret;
}
C、videobuf_status
把videobuf_buffer数据成员拷贝到v4l2_buffer的数据中
/* Locking: Caller holds q->vb_lock */
static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
struct videobuf_buffer *vb, enum v4l2_buf_type type)
{
MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
b->index = vb->i;
b->type = type;
b->memory = vb->memory;
switch (b->memory) {
case V4L2_MEMORY_MMAP:
b->m.offset = vb->boff;
b->length = vb->bsize;
break;
case V4L2_MEMORY_USERPTR:
b->m.userptr = vb->baddr;
b->length = vb->bsize;
break;
case V4L2_MEMORY_OVERLAY:
b->m.offset = vb->boff;
break;
}
b->flags = 0;
if (vb->map)
b->flags |= V4L2_BUF_FLAG_MAPPED;
switch (vb->state) {
case VIDEOBUF_PREPARED:
case VIDEOBUF_QUEUED:
case VIDEOBUF_ACTIVE:
b->flags |= V4L2_BUF_FLAG_QUEUED;
break;
case VIDEOBUF_DONE:
case VIDEOBUF_ERROR:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
case VIDEOBUF_NEEDS_INIT:
case VIDEOBUF_IDLE:
/* nothing */
break;
}
if (vb->input != UNSET) {
b->flags |= V4L2_BUF_FLAG_INPUT;
b->input = vb->input;
}
b->field = vb->field;
b->timestamp = vb->ts;
b->bytesused = vb->size;
b->sequence = vb->field_count >> 1;
}
上边函数是应用程序调用获取之前分配video buffer的长度和偏移量,用来应用程序映射该buffer到用户空间
3、将申请的缓冲区放到视频采集输入/输出队列
A、vip_qbuf
/* Queue video memory buffer VIDIOC_QBUF*/
static int vip_qbuf(struct file *file, viod *priv, struct v4l2_buffer *p)
{
struct vip_fh *fh = priv;
return (videobuf_qbuf(&fh->vb_vidq, p));
}
B、videobuf_qbuf
//每一个分配的缓冲区都要执行这个函数
int videobuf_qbuf(struct videobuf_queue *q,
struct v4l2_buffer *b)
{
struct videobuf_buffer *buf;
enum v4l2_field field;
unsigned long flags = 0;
int retval;
MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
//略过加锁,参数以及运行状态检测
、、、、、、、、、、、、、、、、、
buf = q->bufs[b->index];//index由应用程序指定
switch (b->memory) {
case V4L2_MEMORY_MMAP:
if (0 == buf->baddr) {
dprintk(1, "qbuf: mmap requested "
"but buffer addr is zero!\n");
goto done;
}
break;
case V4L2_MEMORY_USERPTR:
if (b->length < buf->bsize) {
dprintk(1, "qbuf: buffer length is not enough\n");
goto done;
}
if (VIDEOBUF_NEEDS_INIT != buf->state &&
buf->baddr != b->m.userptr)
q->ops->buf_release(q, buf);
buf->baddr = b->m.userptr;
break;
case V4L2_MEMORY_OVERLAY:
buf->boff = b->m.offset;
break;
default:
dprintk(1, "qbuf: wrong memory type\n");
goto done;
}
dprintk(1, "qbuf: requesting next field\n");
field = videobuf_next_field(q);//根据当前的场,得到下一个场域
retval = q->ops->buf_prepare(q, buf, field);//设置缓冲区的参数
if (0 != retval) {
dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
goto done;
}
list_add_tail(&buf->stream, &q->stream);//将该缓冲区添加到输出队列中
if (q->streaming) {
spin_lock_irqsave(q->irqlock, flags);
q->ops->buf_queue(q, buf);
spin_unlock_irqrestore(q->irqlock, flags);
}
dprintk(1, "qbuf: succeded\n");
retval = 0;
wake_up_interruptible_sync(&q->wait);
done:
mutex_unlock(&q->vb_lock);
if (b->memory == V4L2_MEMORY_MMAP)
up_read(¤t->mm->mmap_sem);
return retval;
}
4、从输出队列取出已含有采集数据的帧缓冲区
A、vip_dqbuf
/*Transfer buffers beween incoming and outgoing queue VIDIOC_DQBUF*/
static int vip_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vip_fh *fh = priv;
return (videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK));
}
B、videobuf_dqbuf
int videobuf_dqbuf(struct videobuf_queue *q,
struct v4l2_buffer *b, int nonblocking)
{
struct videobuf_buffer *buf = NULL;
int retval;
MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
mutex_lock(&q->vb_lock);
//获取下一个已近填充视频数据的缓冲区
retval = stream_next_buffer(q, &buf, nonblocking);
if (retval < 0) {
dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
goto done;
}
//略过buf->state处理
、、、、、、
}
list_del(&buf->stream);//将该缓冲区从链表中删除,使其处于用户状态
memset(b, 0, sizeof(*b));
videobuf_status(q, b, buf, q->type);
done:
mutex_unlock(&q->vb_lock);
return retval;
}
C、stream_next_buffer
如果输出队列中有视频数据的缓冲区,并且缓冲区的状态正常,那么就返回该缓冲区
/* Locking: Caller holds q->vb_lock */
static int stream_next_buffer(struct videobuf_queue *q,
struct videobuf_buffer **vb, int nonblocking)
{
int retval;
struct videobuf_buffer *buf = NULL;
retval = stream_next_buffer_check_queue(q, nonblocking);
if (retval)
goto done;
//得到输出队列中第一个有视频数据的缓冲区
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
retval = videobuf_waiton(buf, nonblocking, 1);
if (retval < 0)
goto done;
*vb = buf;//正常返回该缓冲区
done:
return retval;
}
D、stream_next_buffer_check_queue
这个函数的功能就是检测是否开始队列中视频数据流的传输(q->streaming),如果没有,就返回负的错误吗。如果开始传输,再判断输出队列中是否有缓冲区,有的话正常返回0,否则根据阻塞标志看是返回错误码还是阻塞等待输出队列缓冲区。
/* Locking: Caller holds q->vb_lock */
static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
{
int retval;
checks:
if (!q->streaming) {//在stream on中置位1,意味着开始流数据传输
dprintk(1, "next_buffer: Not streaming\n");
retval = -EINVAL;
goto done;
}
if (list_empty(&q->stream)) {//如果输出队列为空,则根据传入的阻塞标志看是否要等待
if (noblock) {//如果是非阻塞的流处理,则直接返回
retval = -EAGAIN;
dprintk(2, "next_buffer: no buffers to dequeue\n");
goto done;
} else {//如果是阻塞流处理,则是当前读取数据进程进入等待状态,当输出队列有缓冲区时唤醒该进程。
dprintk(2, "next_buffer: waiting on buffer\n");
/* Drop lock to avoid deadlock with qbuf */
mutex_unlock(&q->vb_lock);
/* Checking list_empty and streaming is safe without locks because we goto checks to validate while holding locks before proceeding */
retval=
wait_event_interruptible(q->wait,!list_empty(&q->stream)|| !q->streaming);
mutex_lock(&q->vb_lock);
if (retval)
goto done;
goto checks;
}
}
retval = 0;
done:
return retval;
}
该函数用来判断视频缓冲区的状态是否在队列和激活,否则等待
E、videobuf_waiton
#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED)
int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
{
MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
if (non_blocking) {//如果是非阻塞流操作操作
if (WAITON_CONDITION)//并且等待条件满足
return 0;//则正常返回
else
return -EAGAIN;//否则返回错误码
}
//如果是阻塞流操作,那么就会使该进程进入等待队列
if (intr)
return wait_event_interruptible(vb->done, WAITON_CONDITION);
else
wait_event(vb->done, WAITON_CONDITION);
return 0;
}
四、V4L2 层缓冲区参数设置:
1、buffer_setup
static int buffer_setup(struct video_queue *vp, unsigned *count,
unsigned int *size)
{
struct vip_fh *fh = vq->priv_data;
struct vip_dev *dev = fh->dev;
*size = fh->width*fh->height*2;//两场odd/even
if(0 == *count)
*count = 32;
while(*size * *count > vid_limit * 1024 *1024);//最大缓冲区限制
(*count)--;
dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, *count, *size);
return 0;
}
2、buffer_prepare
static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
rnum v4l2_field field)
{
struct vip_fh *fh = vq->priv_data;/*open set*/
struct vip_dev *dev = fh->dev;
struct vip_buffer *buf = container_of(vb, struct vip_buffer, vb);
int rc;
dprintk(dev, 1, "%s, field=%d\n", __func__, field);
BUG_ON(NULL == fh->fmt);
if(fh->width < NORM_MINW || fh->width > NORM_MAXW ||
fh->height < NORM_MINH || fh->height > NORM_MAXH)
return -EINVAL;
buf->vb.size = fh->width * fh->height * 2;
if(0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
//设置缓冲区的像素参数
buf->fmt = fh->fmt;
buf->vb.width = fh->width;
buf->vb.heignt = fh->heignt;
buf->vb.filed = fh->filed;
//precalculate_bars(fh);
if(VIDEOBUF_NEEDS_INIT == buf->vb.state){
rc = videobuf_iolock(vq, &buf->vb, NULL);
if(rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(vq, buf);
return rc;
}
3、buffer_queue
static void buffer_queue (struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct vip_buffer *buf = container(vb, struct vip_buffer, vb);
struct vip_fh *fh = vq->priv_data;
struct vip_dev *dev = fh->dev;
struct vip_damqueue *vdq = &dev->vidq;
dprintk(dev, 1, "%s\n, __func__");
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vdq->active);//添加缓冲区到DMA链表
}
enum videobuf_state{
VIDEOBUF_DEDDS_INIT = 0 realse_buffer()
VIDEOBUF_PREPARED = 1 buffer_prepare()
VIDEOBUF_QUEUE = 2 buffer_queue()
VIDEOBUF_ACTIVE = 3
VIDEOBUF_DONE = 4 fillbuff()->wake_up(&buf->vb.done)
VIDEOBUF_ERROR = 5
VIDEOBUF_IDLE = 6
}
应用程序的调用过程:
1、 reqbufs() -> buf_setup()
2、 querybuf()->videobuf_status()
3、 qbuf()->buf_prepare()
4、 streamon()->buf_queue()
wake_up_interruptible_sysc(&q->wait)
5、 dqbuf()->stream_next_buffer()->stream_next_buffer_check_queue()
6、 qbuf()->buf_prepare()->buf_queue()