13.uvc视频初始化
13.1 uvc数据流控制
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
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. "); 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. "); 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. "); return -EINVAL; } } return 0; }
13.2.1 初始化uvc队列
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操作函数集
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 相关结构体
struct uvc_fh {//uvc句柄 struct uvc_video_chain *chain; //uvc视频链 struct uvc_streaming *stream; //uvc视频流 enum uvc_handle_state state; };
14.2.2 open
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 "); 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启动状态
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
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
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 开始视频显示函数
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; }
a.1 uvc视频流使能
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初始化视频 }
a.1.1 uvc使能队列
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; }
a.1.2 uvc提交视频参数
int uvc_commit_video(struct uvc_streaming *stream,struct uvc_streaming_control *probe) { return uvc_set_video_ctrl(stream, probe, 0); //uvc设置视频控制 }
a.1.3 uvc初始化视频
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. "); bandwidth = 1; } else { uvc_trace(UVC_TRACE_VIDEO, "Device requested %u B/frame bandwidth. ", 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. "); return -EIO; } uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u (%u B/frame bandwidth). ", 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). ", i, ret); uvc_uninit_video(stream, 1); return ret; } } return 0; }
a.1.3.1 同步方式
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; }
a.1.3.2 Bluk方式
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; }
a.1.3.1 同步方式和a.1.3.2 Bluk方式 两种方式初始化uvc视频主要是分配设置urb,然后在uvc_init_video函数中又通过usb_submit_urb提交了urb,
两种方法的urb回调函数都是uvc_video_complete
a.2 urb回调函数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: break; default: uvc_printk(KERN_WARNING, "Non-zero status (%d) in video completion handler. ", 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). ",ret); } }
对于同步和bilk方式的decode方法分别是
stream->decode = uvc_video_decode_isoc
stream->decode = uvc_video_encode_bulk;
这个在前面uvc_video_init函数中设置了
ok后面就开始解码了