V4L2深入理解

               

1. VIDIOC_REQBUFS: 请求Kernel分配Video Buffer

其申请流程如下图所示:

1.1  Kernel态相关数据结构

struct uvc_fh { struct uvc_video_chain *chain; struct uvc_streaming *stream;  //Stream---> enum uvc_handle_state state;};struct uvc_streaming { struct list_head list; struct uvc_device *dev; struct video_device *vdev; struct uvc_video_chain *chain; atomic_t active; struct usb_interface *intf; int intfnum; __u16 maxpsize; struct uvc_streaming_header header; enum v4l2_buf_type type; unsigned int nformats; struct uvc_format *format; struct uvc_streaming_control ctrl; struct uvc_format *cur_format; struct uvc_frame *cur_frame; /* Protect access to ctrl, cur_format, cur_frame and hardware video  * probe control.  */ struct mutex mutex; unsigned int frozen : 1struct uvc_video_queue queue; // UVC Video Queue---> void (*decode) (struct urb *urb, struct uvc_streaming *video,   struct uvc_buffer *buf); /* Context data used by the bulk completion handler. */ struct {  __u8 header[256];  unsigned int header_size;  int skip_payload;  __u32 payload_size;  __u32 max_payload_size; } bulk; struct urb *urb[UVC_URBS]; char *urb_buffer[UVC_URBS]; dma_addr_t urb_dma[UVC_URBS]; unsigned int urb_size; __u32 sequence; __u8 last_fid;    struct tasklet_struct *tasklet[UVC_URBS];     /* ddl@rock-chips.com */};struct uvc_video_queue { enum v4l2_buf_type type; void *mem;             // 已经分配的连续虚拟内存的首地址 unsigned int flags; unsigned int count;    // 已分配的buffer个数 unsigned int buf_size; // 每个buffer的大小 unsigned int buf_used; struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];  // UVC buffer---> struct mutex mutex; /* protects buffers and mainqueue */ spinlock_t irqlock; /* protects irqqueue */    wait_queue_head_t wait; /* wait if mainqueue is empty */ struct list_head mainqueue; struct list_head irqqueue;    };struct uvc_buffer { unsigned long vma_use_count; struct list_head stream; /* Touched by interrupt handler. */ struct v4l2_buffer buf;     // v4l2_buffer ---> struct list_head queue; wait_queue_head_t wait;     // 初始化等待队列 enum uvc_buffer_state state; unsigned int error;};struct v4l2_buffer { __u32   index;    //buffer索引 enum v4l2_buf_type      type;     //如V4L2_BUF_TYPE_VIDEO_CAPTURE __u32   bytesused; __u32   flags; enum v4l2_field  field;    // V4L2_FIELD_NONE struct timeval  timestamp;  struct v4l2_timecode timecode; __u32   sequence; /* memory location */ enum v4l2_memory        memory;   // V4L2_MEMORY_MMAP union {  __u32           offset;   //在已经分配的大块内存中的偏移量,                                          //其首地址保存在uvc_video_queue->mem中  unsigned long   userptr;  struct v4l2_plane *planes; } m; __u32   length;   //申请的内存大小 __u32   input; __u32   reserved;};


1.2 uvc_alloc_buffers实现代码

/* * Allocate the video buffers. * * Pages are reserved to make sure they will not be swapped, as they will be * filled in the URB completion handler. * * Buffers will be individually mapped, so they must all be page aligned. */int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,  unsigned int buflength)unsigned int bufsize = PAGE_ALIGN(buflength); unsigned int i; void *mem = NULLint ret; if (nbuffers > UVC_MAX_VIDEO_BUFFERS)  nbuffers = UVC_MAX_VIDEO_BUFFERS; mutex_lock(&queue->mutex); if ((ret = __uvc_free_buffers(queue)) < 0)  goto done; /* Bail out if no buffers should be allocated. */ if (nbuffers == 0)  goto done; /* Decrement the number of buffers until allocation succeeds. */ for (; nbuffers > 0; --nbuffers) {  mem = vmalloc_32(nbuffers * bufsize);  if (mem != NULL)   break; } if (mem == NULL) {  ret = -ENOMEM;  goto done; } for (i = 0; i < nbuffers; ++i) {  memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);  queue->buffer[i].buf.index = i;  queue->buffer[i].buf.m.offset = i * bufsize;  queue->buffer[i].buf.length = buflength;  queue->buffer[i].buf.type = queue->type;  queue->buffer[i].buf.field = V4L2_FIELD_NONE;  queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;  queue->buffer[i].buf.flags = 0;  init_waitqueue_head(&queue->buffer[i].wait); } queue->mem = mem; queue->count = nbuffers; queue->buf_size = bufsize; ret = nbuffers;done: mutex_unlock(&queue->mutex); return ret;}


 2. VIDIOC_QUERYBUF: 把Kernel分配的内存映射到用户空间

 

 

 3. VIDIOC_QBUF: 把uvc_buffer放入队列中

 

 

/* * Queue a video buffer. Attempting to queue a buffer that has already been * queued will return -EINVAL. */int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf)struct uvc_buffer *buf; unsigned long flags; int ret = 0; uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); if (v4l2_buf->type != queue->type ||     v4l2_buf->memory != V4L2_MEMORY_MMAP) {  uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "   "and/or memory (%u).\n", v4l2_buf->type,   v4l2_buf->memory);  return -EINVAL; } mutex_lock(&queue->mutex); if (v4l2_buf->index >= queue->count) {  uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");  ret = -EINVAL;  goto done; } buf = &queue->buffer[v4l2_buf->index]; if (buf->state != UVC_BUF_STATE_IDLE) {  uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "   "(%u).\n", buf->state);  ret = -EINVAL;  goto done; } if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&     v4l2_buf->bytesused > buf->buf.length) {  uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");  ret = -EINVAL;  goto done; } spin_lock_irqsave(&queue->irqlock, flags); if (queue->flags & UVC_QUEUE_DISCONNECTED) {  spin_unlock_irqrestore(&queue->irqlock, flags);  ret = -ENODEV;  goto done; } buf->state = UVC_BUF_STATE_QUEUED; if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)  buf->buf.bytesused = 0else  buf->buf.bytesused = v4l2_buf->bytesused; list_add_tail(&buf->stream, &queue->mainqueue); list_add_tail(&buf->queue, &queue->irqqueue); spin_unlock_irqrestore(&queue->irqlock, flags);done: mutex_unlock(&queue->mutex); return ret;}


 4. VIDIOC_STREAMON: 做好准备工作并提交URB请求

 

/* * Initialize bulk URBs and allocate transfer buffers. The packet size is * given by the endpoint. */static int uvc_init_video_bulk(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags)struct urb *urb; unsigned int npackets, pipe, i; u16 psize; u32 size;            // 获取每个包的大小 psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;  // 一次可传输的最大负荷传输大小 size = stream->ctrl.dwMaxPayloadTransferSize; stream->bulk.max_payload_size = size;     /*    分配置urb传输buffer,返回包个数(size/psize),它指每个urb    包含多少个包; 为每个stream->urb_buffer分配DMA buffer,如下:    for (i = 0; i < UVC_URBS; ++i) {        stream->urb_size = psize * npackets; // urb_buffer的大小        //分配DMA内存,并把地址保存在urb_buffer[i]和urb_dma[i]中,        //同一块内存,不同的表示方法        //1) stream->urb_buffer[i] = offset + page->vaddr;        //2) stream->urb_dma[i] = offset + page->dma;        stream->urb_buffer[i] = usb_alloc_coherent(                     stream->dev->udev, stream->urb_size,                     gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);        if (!stream->urb_buffer[i]) {            uvc_free_urb_buffers(stream);            break;        }    }    */ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0)  return -ENOMEM; size = npackets * psize; if (usb_endpoint_dir_in(&ep->desc))  pipe = usb_rcvbulkpipe(stream->dev->udev,           ep->desc.bEndpointAddress); else  pipe = usb_sndbulkpipe(stream->dev->udev,           ep->desc.bEndpointAddress); if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)  size = 0for (i = 0; i < UVC_URBS; ++i) {        // 创建一个 urb  urb = usb_alloc_urb(0, gfp_flags);  if (urb == NULL) {   uvc_uninit_video(stream, 1);   return -ENOMEM;  }        /* 填充urb参数  struct urb {   void *transfer_buffer; // (in) associated data buffer   dma_addr_t transfer_dma;// (in) dma addr for transfer_buffer   usb_complete_t complete;// (in) completion routine   struct usb_iso_packet_descriptor iso_frame_desc[0];               // (in) ISO ONLY  }    */  usb_fill_bulk_urb(urb, stream->dev->udev, pipe,                          stream->urb_buffer[i],  //传输buffer                          size,        //传输buffer的大小                          uvc_video_complete, //URB请求完成之后的callback                          stream);  urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  urb->transfer_dma = stream->urb_dma[i]; //给urb->transfer_dma赋值  // 把此urb保存到stream->urb[i]中  stream->urb[i] = urb; } return 0;}


5. urb数据解析 (uvc_video_complete)

       当URB请求(usb_submit_urb)完成之后,它将调用其回调函数(uvc_video_complete),下面分析此回调函数到底做了些什么。即如何把transfer_buffer或transfer_dma中数据转换为应用程序需要的v4l2_buffer中的数据。

 5.1 uvc_video_complete

static void uvc_video_complete(struct urb *urb){     struct uvc_streaming *stream = urb->context; struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *buf = NULL; unsigned long flags; int ret; switch (urb->status) { case 0:  breakdefault:  uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "   "completion handler.\n", urb->status); case -ENOENT:  /* usb_kill_urb() called. */  if (stream->frozen)   returncase -ECONNRESET: /* usb_unlink_urb() called. */ case -ESHUTDOWN: /* The endpoint is being disabled. */  uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);  return; }  spin_lock_irqsave(&queue->irqlock, flags); if (!list_empty(&queue->irqqueue))  buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,           queue);//从queue->irqqueue中取出一个空的uvc_buffer spin_unlock_irqrestore(&queue->irqlock, flags);        //把urb中的数据转换为uvc_buffer中的数据,并设置对应的状态 stream->decode(urb, stream, buf); if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {  uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",   ret); }}

5.2  stream->decode是什么?

        现在关键是stream->decode到底做了些什么?它也是一个回调函数,首先要搞明白它是一个什么函数,其注册过程如下图所示:

5.3 uvc_video_decode_bulk

 

 

5.4 uvc_video_decode_isoc

static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf){ u8 *mem; int ret, i; for (i = 0; i < urb->number_of_packets; ++i) {  if (urb->iso_frame_desc[i].status < 0) {   uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "    "lost (%d).\n", urb->iso_frame_desc[i].status);     /* Mark the buffer as faulty. */   if (buf != NULL)    buf->error = 1;   continue;  }  /* Decode the payload header. */  mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;  do {   ret = uvc_video_decode_start(stream, buf, mem,    urb->iso_frame_desc[i].actual_length);   if (ret == -EAGAIN)    buf = uvc_queue_next_buffer(&stream->queue,           buf);  } while (ret == -EAGAIN);  if (ret < 0)   continue;  /* Decode the payload data. */  uvc_video_decode_data(stream, buf, mem + ret,   urb->iso_frame_desc[i].actual_length - ret);  /* Process the header again. */  uvc_video_decode_end(stream, buf, mem,   urb->iso_frame_desc[i].actual_length);  if (buf->state == UVC_BUF_STATE_READY) {   if (buf->buf.length != buf->buf.bytesused &&       !(stream->cur_format->flags &         UVC_FMT_FLAG_COMPRESSED))    buf->error = 1;   buf = uvc_queue_next_buffer(&stream->queue, buf);  } }}


 

6. VIDIOC_DQBUF: 获取视频数据

 

  7. CameraHAL工作流程

  

 

 

 

 

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
Android V4L2代理是一种用于在Android设备上访问Linux V4L2驱动的机制。V4L2代理为Android应用程序提供了一种简单的方式来控制和管理相机硬件。它充分利用了V4L2驱动的功能,使得开发人员可以更加灵活地操作相机。 Android V4L2代理工作流程如下:首先,应用程序通过调用Android相机API来请求访问相机硬件。然后,V4L2代理将这些请求翻译成V4L2驱动可以理解的指令,并将其发送到Linux内核中的V4L2驱动。驱动完成请求后,将结果返回给V4L2代理,再由代理传递给应用程序。 通过使用Android V4L2代理,开发人员可以直接访问V4L2驱动的高级功能,如手动调整相机参数、设置相机模式、捕获图像和视频等。这使得开发人员可以更好地控制相机硬件,并实现各种复杂的图像处理操作。 与直接访问V4L2驱动相比,使用V4L2代理具有一定的优势。首先,它提供了一个统一的接口,简化了对各种不同相机硬件的管理。其次,它可以屏蔽底层驱动的复杂性,使开发人员能够更专注于应用程序的逻辑。此外,V4L2代理还提供了一套高级API,使得开发人员可以更快速地开发相机相关的功能。 综上所述,Android V4L2代理是一种为了简化相机硬件访问的机制,在Android设备上使用V4L2驱动的高级功能。它使开发人员能够更好地控制相机硬件,并提供了一种简化和加速应用程序开发的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值