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


     
  • 相关阅读:
    生成随机数
    ES集群开启X-pack认证
    部署ceph
    分布式存储ceph理论
    kvm虚拟机迁移
    kvm虚拟机网络管理
    kvm虚拟机存储管理
    机电传动控制个人课程报告
    第11周机电传动控制作业
    补充第九周手工画图
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3315512.html
Copyright © 2020-2023  润新知