天天看點

uvc攝像頭代碼解析3

6.uvc解析uvc視訊流

6.1 重要結構體

6.1.1 uvc資料流

struct uvc_streaming {
	struct list_head list;	//uvc視訊流連結清單頭
	struct uvc_device *dev;	//uvc裝置
	struct video_device *vdev;	//V4L2視訊裝置
	struct uvc_video_chain *chain;	//uvc視訊鍊
	atomic_t active;
	struct usb_interface *intf;	//usb接口裝置
	int intfnum;	//usb接口号
	__u16 maxpsize;	//最大包尺寸
	struct uvc_streaming_header header;	//uvc視訊流頭部
	enum v4l2_buf_type type;	//V4L2緩沖區類型 輸入/輸出
	unsigned int nformats;	//uvc格式個數
	struct uvc_format *format;	//uvc格式指針
	struct uvc_streaming_control ctrl;	//uvc資料流控制
	struct uvc_format *cur_format;	//目前uvc格式指針
	struct uvc_frame *cur_frame;	//目前uvc幀指針
	struct mutex mutex;
	unsigned int frozen : 1;
	struct uvc_video_queue queue;	//uvc視訊隊列
	void (*decode) (struct urb *urb, struct uvc_streaming *video,struct uvc_buffer *buf);//解碼函數
	struct {
		__u8 header[256];
		unsigned int header_size;
		int skip_payload;
		__u32 payload_size;
		__u32 max_payload_size;
	} bulk;
	struct urb *urb[UVC_URBS];//urb數組
	char *urb_buffer[UVC_URBS];	//urb緩沖區
	dma_addr_t urb_dma[UVC_URBS];//urb DMA緩沖區
	unsigned int urb_size;	
	__u32 sequence;
	__u8 last_fid;
};
           
uvc攝像頭代碼解析3

6.1.2 uvc格式

struct uvc_format {	//uvc格式
	__u8 type;	//類型
	__u8 index;	//索引
	__u8 bpp;	//bits per pixel 每像素位數
	__u8 colorspace;	//顔色空間
	__u32 fcc;	//壓縮格式
	__u32 flags;	//标記
	char name[32];	//名字
	unsigned int nframes;	//所含uvc幀個數
	struct uvc_frame *frame;	//uvc幀指針
};
           
uvc攝像頭代碼解析3

6.1.3 uvc幀

struct uvc_frame {	//uvc幀
	__u8  bFrameIndex;	//幀索引号
	__u8  bmCapabilities;	//uvc幀相容性
	__u16 wWidth;	//寬度
	__u16 wHeight;	//高度
	__u32 dwMinBitRate;	//最新位流
	__u32 dwMaxBitRate;	//最大位流
	__u32 dwMaxVideoFrameBufferSize;	//最大視訊幀緩沖區
	__u8  bFrameIntervalType;	//間隙類型
	__u32 dwDefaultFrameInterval;	//預設幀間隙
	__u32 *dwFrameInterval;	//幀間隙指針
};
           
uvc攝像頭代碼解析3

6.2 uvc_parse_streaming函數

static int uvc_parse_streaming(struct uvc_device *dev,struct usb_interface *intf)
{
	struct uvc_streaming *streaming = NULL;	//uvc資料流
	struct uvc_format *format;	//uvc格式
	struct uvc_frame *frame;	//uvc幀
	struct usb_host_interface *alts = &intf->altsetting[0];	//擷取usb接口第一個usb_host_interface (Alt.Setting 0)
	unsigned char *_buffer, *buffer = alts->extra;	//擷取額外描述符
	int _buflen, buflen = alts->extralen;	//擷取額外描述符長度
	unsigned int nformats = 0, nframes = 0, nintervals = 0;
	unsigned int size, i, n, p;
	__u32 *interval;
	__u16 psize;
	int ret = -EINVAL;
	if (intf->cur_altsetting->desc.bInterfaceSubClass != UVC_SC_VIDEOSTREAMING) {	//判讀usb接口描述符子類是否為視訊資料流接口子類
		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a video streaming interface\n", dev->udev->devnum,intf->altsetting[0].desc.bInterfaceNumber);
		return -EINVAL;
	}
	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {	//綁定uvc裝置的usb驅動和usb接口
		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already claimed\n", dev->udev->devnum,intf->altsetting[0].desc.bInterfaceNumber);
		return -EINVAL;
	}
	streaming = kzalloc(sizeof *streaming, GFP_KERNEL);	//配置設定uvc資料流記憶體
	if (streaming == NULL) {
		usb_driver_release_interface(&uvc_driver.driver, intf);
		return -EINVAL;
	}
	mutex_init(&streaming->mutex);
	streaming->dev = dev;	//uvc資料流和uvc裝置捆綁
	streaming->intf = usb_get_intf(intf);	//uvc資料流和usb接口捆綁,并增加引用計數
	streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;	//設定接口号
	/* The Pico iMage webcam has its class-specific interface descriptors after the endpoint descriptors. */
	if (buflen == 0) {	//Pico iMage webcam 特殊處理
		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
			struct usb_host_endpoint *ep = &alts->endpoint[i];
			if (ep->extralen == 0)
				continue;
			if (ep->extralen > 2 && ep->extra[1] == USB_DT_CS_INTERFACE) {
				uvc_trace(UVC_TRACE_DESCR, "trying extra data from endpoint %u.\n", i);
				buffer = alts->endpoint[i].extra;
				buflen = alts->endpoint[i].extralen;
				break;
			}
		}
	}
	/* Skip the standard interface descriptors. 跳過标準的接口描述符*/
	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {	
		buflen -= buffer[0];
		buffer += buffer[0];
	}
	if (buflen <= 2) {
		uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming interface descriptors found.\n");
		goto error;
	}
	/* Parse the header descriptor. 解析header描述符*/	//Class-specific VS Interface Input Header Descriptor
	switch (buffer[2]) {	//bDescriptorSubtype
	case UVC_VS_OUTPUT_HEADER:	//輸出類型的視訊流
		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;	//設定為V4L2視訊buf輸出
		size = 9;
		break;
	case UVC_VS_INPUT_HEADER:	//輸入類型的視訊流
		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	//設定為V4L2視訊buf輸入
		size = 13;
		break;
	default:
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface %d HEADER descriptor not found.\n", dev->udev->devnum,alts->desc.bInterfaceNumber);
		goto error;
	}
	p = buflen >= 4 ? buffer[3] : 0;	//bNumFormats uvc格式format個數
	n = buflen >= size ? buffer[size-1] : 0;	//bControlSize 控制位域大小
	if (buflen < size + p*n) {		//檢測buflen長度是否合适
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface %d HEADER descriptor is invalid.\n",dev->udev->devnum, alts->desc.bInterfaceNumber);
		goto error;
	}
	//初始化uvc視訊流頭部
	streaming->header.bNumFormats = p;	//uvc格式format格式個數
	streaming->header.bEndpointAddress = buffer[6];	//端點位址
	if (buffer[2] == UVC_VS_INPUT_HEADER) {	//輸入的視訊流
		streaming->header.bmInfo = buffer[7];			//資訊位圖(相容性)
		streaming->header.bTerminalLink = buffer[8];	//連接配接到的輸出Terminal ID号
		streaming->header.bStillCaptureMethod = buffer[9];	//靜态圖像捕捉方法(Method 1、Method 2、Method 3)
		streaming->header.bTriggerSupport = buffer[10];	//硬體觸發支援
		streaming->header.bTriggerUsage = buffer[11];	//觸發用例
	} 
	else {
		streaming->header.bTerminalLink = buffer[7];	//連接配接到的輸入Terminal ID号
	}
	streaming->header.bControlSize = n;	//控制位域大小
	streaming->header.bmaControls = kmemdup(&buffer[size], p * n,GFP_KERNEL);	//初始化bmaControls(x)位圖(大小=幀數*位域大小)
	if (streaming->header.bmaControls == NULL) {
		ret = -ENOMEM;
		goto error;
	}
	buflen -= buffer[0];
	buffer += buffer[0];	//指向下一個描述符
	_buffer = buffer;
	_buflen = buflen;	//指向同一個描述符
           

解析完vs header後解析剩下的vs描述符

第一次解析描述符 統計uvc幀、uvc格式、間隔,并配置設定記憶體

/* Count the format and frame descriptors. 計算格式描述符和幀描述符個數*/
	while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
		switch (_buffer[2]) {
		case UVC_VS_FORMAT_UNCOMPRESSED:
		case UVC_VS_FORMAT_MJPEG:
		case UVC_VS_FORMAT_FRAME_BASED:
			nformats++;
			break;
		case UVC_VS_FORMAT_DV:
			/* DV format has no frame descriptor. We will create a dummy frame descriptor with a dummy frame interval. */
			nformats++;
			nframes++;
			nintervals++;
			break;
		case UVC_VS_FORMAT_MPEG2TS:
		case UVC_VS_FORMAT_STREAM_BASED:
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface %d FORMAT %u is not supported.\n",dev->udev->devnum,alts->desc.bInterfaceNumber, _buffer[2]);
			break;
		case UVC_VS_FRAME_UNCOMPRESSED:
		case UVC_VS_FRAME_MJPEG:
			nframes++;
			if (_buflen > 25)
				nintervals += _buffer[25] ? _buffer[25] : 3;
			break;
		case UVC_VS_FRAME_FRAME_BASED:
			nframes++;
			if (_buflen > 21)
				nintervals += _buffer[21] ? _buffer[21] : 3;
			break;
		}	//計算uvc幀和uvc格式的個數及間隔個數
		_buflen -= _buffer[0];	
		_buffer += _buffer[0];	//跳到下一個描述符
	}
	if (nformats == 0) {
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface %d has no supported formats defined.\n",dev->udev->devnum, alts->desc.bInterfaceNumber);
		goto error;
	}
	//    uvc格式數 * uvc格式大小    + uvc幀   * uvc幀大小    + 間隔數     * 間隔大小
	size = nformats * sizeof *format + nframes * sizeof *frame+ nintervals * sizeof *interval;
	format = kzalloc(size, GFP_KERNEL);	//配置設定uvc格式和uvc幀的記憶體
	if (format == NULL) {
		ret = -ENOMEM;
		goto error;
	}
	frame = (struct uvc_frame *)&format[nformats];	//uvc幀存放在uvc格式數組後面
	interval = (__u32 *)&frame[nframes];	//間隔放在幀後面
	streaming->format = format;	//uvc視訊流捆綁uvc格式結構體
	streaming->nformats = nformats;	//uvc格式個數
           

第二次解析描述符

/* Parse the format descriptors.解析格式描述符 */
	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
		switch (buffer[2]) {	//bDescriptorSubtype描述符類型
		case UVC_VS_FORMAT_UNCOMPRESSED:
		case UVC_VS_FORMAT_MJPEG:
		case UVC_VS_FORMAT_DV:
		case UVC_VS_FORMAT_FRAME_BASED:
			format->frame = frame;	//uvc格式的幀指針 指向uvc幀位址
			ret = uvc_parse_format(dev, streaming, format,&interval, buffer, buflen);	//7.解析uvc格式描述符
			if (ret < 0)
				goto error;
			frame += format->nframes;	//uvc幀位址指向下一個uvc格式所屬的uvc幀位址
			format++;	//指向下一個uvc格式
			buflen -= ret;	
			buffer += ret;	//指向下一個uvc格式描述符
			continue;
           
uvc攝像頭代碼解析3
default:
			break;
		}
           
uvc攝像頭代碼解析3
buflen -= buffer[0];
		buffer += buffer[0];		//指向下一個描述符
	}
	if (buflen)
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage.\n",dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
	/* Parse the alternate settings to find the maximum bandwidth. 解析設定并查找最大帶寬*/
	for (i = 0; i < intf->num_altsetting; ++i) {
		struct usb_host_endpoint *ep;	//聲明usb_host_endpoint 指針
		alts = &intf->altsetting[i];//擷取usb_host_interface數組
		ep = uvc_find_endpoint(alts,streaming->header.bEndpointAddress);	//查找對應usb_host_endpoint 
		if (ep == NULL)
			continue;
		psize = le16_to_cpu(ep->desc.wMaxPacketSize);	//擷取最大包尺寸
		psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));	//換算
		if (psize > streaming->maxpsize)
			streaming->maxpsize = psize;	//設定uvc視訊流最大包尺寸對象
	}
	list_add_tail(&streaming->list, &dev->streams);	//添加到uvc裝置視訊流連結清單
	return 0;
error:
	usb_driver_release_interface(&uvc_driver.driver, intf);
	usb_put_intf(intf);
	kfree(streaming->format);
	kfree(streaming->header.bmaControls);
	kfree(streaming);
	return ret;
}
           

6.3 添加uvc視訊流到uvc裝置視訊流連結清單

list_add_tail(&streaming->list, &dev->streams);

uvc攝像頭代碼解析3

繼續閱讀