13.uvc視頻初始化 13.1 uvc數據流控制 [cpp struct uvc_streaming_control { __u16 bmHint; __u8 bFormatIndex; //視頻格式索引 __u8 bFrameIndex; //視頻幀索引 __u32 dwFrameInterval; //視頻幀間隔 __u16 wKeyFrameRate; // __u16 wPFrameRate; __u16 wCompQuality; __u16 wCompWindowSize; __u16 wDelay; //延時 __u32 dwMaxVideoFrameSize; //最大視頻幀大小 __u32 dwMaxPayloadTransferSize; __u32 dwClockFrequency; //時鐘頻率 __u8 bmFramingInfo; __u8 bPreferedVersion; __u8 bMinVersion; //版本 __u8 bMaxVersion; //版本 } __attribute__((__packed__)); 13.2 uvc_video_init [cpp] int uvc_video_init(struct uvc_streaming *stream) { struct uvc_streaming_control *probe = &stream->ctrl; //獲取uvc數據流的uvs數據流控制對象 struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; int ret; if (stream->nformats == 0) { uvc_printk(KERN_INFO, "No supported video formats found.\n"); return -EINVAL; } atomic_set(&stream->active, 0); uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); //初始化視頻緩沖區隊列 usb_set_interface(stream->dev->udev, stream->intfnum, 0); //選擇Alt.Setting 0 if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) //VS_PROBE_CONTROL(GET_DEF) uvc_set_video_ctrl(stream, probe, 1); //VS_PROBE_CONTROL(SET_DEF) ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); //VS_PROBE_CONTROL(GET_CUR) if (ret < 0) return ret; for (i = stream->nformats; i > 0; --i) { //獲取對應的uvc格式 format = &stream->format[i-1]; if (format->index == probe->bFormatIndex) //匹配uvc格式索引值 break; } if (format->nframes == 0) { uvc_printk(KERN_INFO, "No frame descriptor found for the default format.\n"); return -EINVAL; } for (i = format->nframes; i > 0; --i) { frame = &format->frame[i-1]; //獲取對應的uvc幀 if (frame->bFrameIndex == probe->bFrameIndex) //匹配uvc幀索引值 break; } probe->bFormatIndex = format->index; //設置uvc視頻流控制的格式索引為uvc格式的索引 probe->bFrameIndex = frame->bFrameIndex; //設置uvc視頻流控制的幀索引為uvc幀的索引 stream->cur_format = format; //設置uvc格式為uvc數據流的cur_format成員 stream->cur_frame = frame; //設置uvc幀未uvc數據流的cur_frame成員 /* Select the video decoding function 選擇視頻解碼函數*/ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { //視頻采集 if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) stream->decode = uvc_video_decode_isight; else if (stream->intf->num_altsetting > 1) stream->decode = uvc_video_decode_isoc; //同步方式 else stream->decode = uvc_video_decode_bulk; //bluk方式 } else { //視頻播放 if (stream->intf->num_altsetting == 1) stream->decode = uvc_video_encode_bulk; else { uvc_printk(KERN_INFO, "Isochronous endpoints are not supported for video output devices.\n"); return -EINVAL; } } return 0; } 13.2.1 初始化uvc隊列 [cpp] void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,int drop_corrupted) { mutex_init(&queue->mutex); spin_lock_init(&queue->irqlock); INIT_LIST_HEAD(&queue->mainqueue); //初始化uvc視頻隊列mainqueue鏈表 INIT_LIST_HEAD(&queue->irqqueue); //初始化uvc視頻隊列irqqueue鏈表 queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; queue->type = type; } 14.uvc V4L2設備 14.1 V4L2操作函數集 [cpp] const struct v4l2_file_operations uvc_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, //打開方法 .release = uvc_v4l2_release, //釋放方法 .unlocked_ioctl = uvc_v4l2_ioctl, //控制方法 .read = uvc_v4l2_read, //讀方法 .mmap = uvc_v4l2_mmap, //映射方法 .poll = uvc_v4l2_poll, //輪詢方法 }; 14.2 打開方法 14.2.1 相關結構體 [cpp] struct uvc_fh {//uvc句柄 struct uvc_video_chain *chain; //uvc視頻鏈 struct uvc_streaming *stream; //uvc視頻流 enum uvc_handle_state state; }; 14.2.2 open [cpp] static int uvc_v4l2_open(struct file *file) { struct uvc_streaming *stream; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); stream = video_drvdata(file); //獲取uvc視頻流 if (stream->dev->state & UVC_DEV_DISCONNECTED) //設備沒連接 return -ENODEV; ret = usb_autopm_get_interface(stream->dev->intf); //喚醒設備 if (ret < 0) return ret; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); //創建uvc句柄 if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); return -ENOMEM; } if (atomic_inc_return(&stream->dev->users) == 1) { ret = uvc_status_start(stream->dev); //uvc狀態開始 if (ret < 0) { usb_autopm_put_interface(stream->dev->intf); atomic_dec(&stream->dev->users); kfree(handle); return ret; } } handle->chain = stream->chain; //捆綁uvc句柄和uvc視頻鏈 handle->stream = stream; //捆綁uvc句柄和uvc視頻流 handle->state = UVC_HANDLE_PASSIVE; //設置uvc狀態為未激活 file->private_data = handle; //將uvc句柄作為文件的私有數據 return 0; } 14.2.2.1 uvc_status_start啟動狀態 [cpp] int uvc_status_start(struct uvc_device *dev) { if (dev->int_urb == NULL) return 0; return usb_submit_urb(dev->int_urb, GFP_KERNEL); //提交urb } 參看 12.uvc狀態初始化 14.3 控制方法 14.3.1 V4L2的控制方式可以參考下面的資料 linux媒體接口API 常用的命令 [cpp] VIDIOC_REQBUFS:分配內存 VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數據緩存轉換成物理地址 VIDIOC_QUERYCAP:查詢驅動功能 VIDIOC_ENUM_FMT:獲取當前驅動支持的視頻格式 VIDIOC_S_FMT:設置當前驅動的頻捕獲格式 VIDIOC_G_FMT:讀取當前驅動的頻捕獲格式 VIDIOC_TRY_FMT:驗證當前驅動的顯示格式 VIDIOC_CROPCAP:查詢驅動的修剪能力 VIDIOC_S_CROP:設置視頻信號的邊框 VIDIOC_G_CROP:讀取視頻信號的邊框 VIDIOC_QBUF:把數據從緩存中讀取出來 VIDIOC_DQBUF:把數據放回緩存隊列 VIDIOC_STREAMON:開始視頻顯示函數 VIDIOC_STREAMOFF:結束視頻顯示函數 VIDIOC_QUERYSTD:檢查當前視頻設備支持的標准,例如PAL或NTSC。 14.3.2 uvc設備V4L2控制方法uvc_v4l2_do_ioctl [cpp] static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file);//獲取V4L2設備 struct uvc_fh *handle = file->private_data;//獲取uvc句柄 struct uvc_video_chain *chain = handle->chain;//獲取uvc視頻鏈 struct uvc_streaming *stream = handle->stream;//獲取uvc視頻流 long ret = 0; switch (cmd) { ... case ...: { ... break; } return ret; } a.VIDIOC_STREAMON 開始視頻顯示函數 [cpp] <pre class="cpp" name="code"> case VIDIOC_STREAMON: { int *type = arg; if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; mutex_lock(&stream->mutex); ret = uvc_video_enable(stream, 1); //uvc視頻流使能 mutex_unlock(&stream->mutex); if (ret < 0) return ret; break; }</pre>a.1 uvc視頻流使能<br> <pre class="cpp" name="code">int uvc_video_enable(struct uvc_streaming *stream, int enable) { int ret; if (!enable) { uvc_uninit_video(stream, 1);//逆初始化視頻 usb_set_interface(stream->dev->udev, stream->intfnum, 0); uvc_queue_enable(&stream->queue, 0);//uvc禁用隊列 return 0; } ret = uvc_queue_enable(&stream->queue, 1); //uvc使能隊列 if (ret < 0) return ret; /* Commit the streaming parameters. */ ret = uvc_commit_video(stream, &stream->ctrl); //uvc提交視頻參數 if (ret < 0) return ret; return uvc_init_video(stream, GFP_KERNEL); //uvc初始化視頻 }</pre>a.1.1 uvc使能隊列 <pre></pre> <pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) { unsigned int i; int ret = 0; mutex_lock(&queue->mutex); if (enable) { //使能uvc隊列 if (uvc_queue_streaming(queue)) { //判斷隊列標志是否為UVC_QUEUE_STREAMING ret = -EBUSY; goto done; } queue->sequence = 0; queue->flags |= UVC_QUEUE_STREAMING; //設置隊列標志 queue->buf_used = 0; //設置緩沖區使用標志 } else { uvc_queue_cancel(queue, 0); //取消uvc隊列 INIT_LIST_HEAD(&queue->mainqueue); //重新初始化uvc隊列mainqueue隊列頭 for (i = 0; i < queue->count; ++i) queue->buffer[i].state = UVC_BUF_STATE_IDLE; //設置緩沖區狀態為閒置態 queue->flags &= ~UVC_QUEUE_STREAMING; //設置隊列標志 } done: mutex_unlock(&queue->mutex); return ret; }</pre>a.1.2 uvc提交視頻參數 <pre></pre> <pre class="cpp" name="code"><pre class="cpp" name="code">int uvc_commit_video(struct uvc_streaming *stream,struct uvc_streaming_control *probe) { return uvc_set_video_ctrl(stream, probe, 0); //uvc設置視頻控制 }</pre><span style="font-family:Arial,Helvetica,sans-serif">a.1.3 uvc初始化視頻</span> <pre></pre> <pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) { struct usb_interface *intf = stream->intf; struct usb_host_endpoint *ep; unsigned int i; int ret; stream->sequence = -1; stream->last_fid = -1; stream->bulk.header_size = 0; stream->bulk.skip_payload = 0; stream->bulk.payload_size = 0; if (intf->num_altsetting > 1) { //同步方式 struct usb_host_endpoint *best_ep = NULL; unsigned int best_psize = 3 * 1024; unsigned int bandwidth; unsigned int uninitialized_var(altsetting); int intfnum = stream->intfnum; /* Isochronous endpoint, select the alternate setting. */ bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { uvc_trace(UVC_TRACE_VIDEO, "Device requested null bandwidth, defaulting to lowest.\n"); bandwidth = 1; } else { uvc_trace(UVC_TRACE_VIDEO, "Device requested %u B/frame bandwidth.\n", bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_interface *alts; unsigned int psize; alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts,stream->header.bEndpointAddress); if (ep == NULL) continue; /* Check if the bandwidth is high enough. */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); if (psize >= bandwidth && psize <= best_psize) { altsetting = i; best_psize = psize; best_ep = ep; } } if (best_ep == NULL) { uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting for requested bandwidth.\n"); return -EIO; } uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u (%u B/frame bandwidth).\n", altsetting, best_psize); ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); if (ret < 0) return ret; ret = uvc_init_video_isoc(stream, best_ep, gfp_flags); //uvc初始化視頻(同步方法) } else { //Bulk方式 /* Bulk endpoint, proceed to URB initialization. */ ep = uvc_find_endpoint(&intf->altsetting[0],stream->header.bEndpointAddress); if (ep == NULL) return -EIO; ret = uvc_init_video_bulk(stream, ep, gfp_flags); //uvc初始化視頻(bulk方法) } if (ret < 0) return ret; /* Submit the URBs. */ for (i = 0; i < UVC_URBS; ++i) { ret = usb_submit_urb(stream->urb[i], gfp_flags); //提交urb if (ret < 0) { uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n", i, ret); uvc_uninit_video(stream, 1); return ret; } } return 0; }</pre>a.1.3.1 同步方式 <pre></pre> <pre class="cpp" name="code"><pre class="cpp" name="code">static int uvc_init_video_isoc(struct uvc_streaming *stream,struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; unsigned int npackets, i, j; u16 psize; u32 size; psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); size = stream->ctrl.dwMaxVideoFrameSize; npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); //分配urb緩沖區 if (npackets == 0) return -ENOMEM; size = npackets * psize; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(npackets, gfp_flags); //分配urb if (urb == NULL) { uvc_uninit_video(stream, 1); return -ENOMEM; } urb->dev = stream->dev->udev; //設置urb urb->context = stream; urb->pipe = usb_rcvisocpipe(stream->dev->udev,ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; urb->transfer_buffer = stream->urb_buffer[i]; urb->transfer_dma = stream->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; for (j = 0; j < npackets; ++j) { urb->iso_frame_desc[j].offset = j * psize; urb->iso_frame_desc[j].length = psize; } stream->urb[i] = urb; } return 0; }</pre><span style="font-family:Arial,Helvetica,sans-serif">a.1.3.2 Bluk方式</span> <pre></pre> <pre class="cpp" name="code"><pre class="cpp" name="code">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; npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); //分配urb緩沖區 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 = 0; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(0, gfp_flags); //分配urb if (urb == NULL) { uvc_uninit_video(stream, 1); return -ENOMEM; } usb_fill_bulk_urb(urb, stream->dev->udev, pipe,stream->urb_buffer[i], size, uvc_video_complete,stream); //設置urb urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = stream->urb_dma[i]; stream->urb[i] = urb; } return 0; }</pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"></span><pre class="cpp" name="code">a.1.3.1 同步方式和<span style="font-family:Arial, Helvetica, sans-serif;">a.1.3.2 Bluk方式 兩種方式初始化uvc視頻主要是分配設置urb,然後在</span><span style="font-family:Arial, Helvetica, sans-serif;">uvc_init_video函數中又通過</span><span style="font-family:Arial, Helvetica, sans-serif;">usb_submit_urb提交了urb,</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;">兩種方法的urb回調函數都是uvc_video_complete</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;">a.2 urb回調函數uvc_video_complete</span></pre><pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"></span><pre class="cpp" name="code">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: break; default: uvc_printk(KERN_WARNING, "Non-zero status (%d) in video completion handler.\n", urb->status); case -ENOENT: /* usb_kill_urb() called. */ if (stream->frozen) return; case -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); spin_unlock_irqrestore(&queue->irqlock, flags); stream->decode(urb, stream, buf); //調用uvc視頻流的decode方法 if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { //再次提交urb uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",ret); } }</pre>對於同步和bilk方式的decode方法分別是 <pre></pre> <pre class="cpp" name="code"><span style="font-family:Arial, Helvetica, sans-serif;"><span style="WHITE-SPACE: pre"> </span>stream->decode = uvc_video_decode_isoc <span style="WHITE-SPACE: pre"> </span>stream->decode = uvc_video_encode_bulk; 這個在前面uvc_video_init函數中設置了</span></pre> <pre></pre> ok後面就開始解碼了 <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> </pre></pre></pre></pre></pre></pre></pre>