Videobuf 流程
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層設置回掉函數清單
[cpp] view plaincopy
- 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 <driver>_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,否則根據阻塞標誌看是返回錯誤碼還是阻塞等待輸出佇列緩衝區。
[cpp] view plaincopy
- /* 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()
來自:http://blog.csdn.net/panda19881/article/details/8748934