zoukankan      html  css  js  c++  java
  • uvc摄像头代码解析7

    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后面就开始解码了
    
    
     
     
     
     
     


     
  • 相关阅读:
    201521044091《Java程序设计》第7周学习总结
    201521044091《java程序设计》第四次总结
    201521044091 《java程序设计》第八周学习总结
    201521044091 《Java程序设计》第5周学习总结
    201521044091 《Java程序设计》第2周学习总结
    201521044091 《Java程序设计》第3周学习总结
    MySQL设置字符集CHARACTER SET
    Create My MySQL configuration by Percona
    How to use jQuery to manipulate Cookies
    How to use OpenXml to import xml data to Sql server
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3315512.html
Copyright © 2011-2022 走看看