第三阶段应用层——2.4 视频监控—从0写USB摄像头驱动(1)-描述符的分析与打印

视频监控—从0写USB摄像头驱动(1)-描述符的分析与打印

  • 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 参考资料:USB_Video_Example 1.5、UVC 1.5 Class specification
  • 开发环境:Linux-4.13.0-41内核(虚拟机)、arm-linux-gcc-4.3.2工具链
  • 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3


一、描述符


1、什么是描述符

关于这个概念也没有一个准确的定义,可以理解为与 “被描述物” 相关联一组数据或数据结构,用来记录被描述物的 “属性”。
具体体现如下(举例说明,并非所有)

  1. 数字,如Linux中的文件描述符,表示一个打开的文件
  2. 结构体,这个在C语言中比较常见,结构体中存储的成员变量的数据就是该设备的具体描述
  3. ,这个在C++与Java中比较常见,其成员变量的数据就是该设备的具体描述

2、USB设备的描述符

对于usb描述符,用来描述该usb设备的属性,如:设备类型、设备型号、配置、容量大小等等
对于一个usb设备,其必有如下固定描述符

  • USB设备描述符(usb_device_descriptor):描述该USB设备的一般信息,如厂家信息、协议信息、usb版本等,一个USB设备只有一个设备描述符
    访问方式USB设备结构体usb_device的descriptor成员变量中
struct usb_device_descriptor {
	 __u8  bLength;               //本描述符的size
	 __u8  bDescriptorType;       //描述符的类型,这里是设备描述符DEVICE
	 __u16 bcdUSB;                //指明usb的版本,比如usb2.0
	 __u8  bDeviceClass;          //类
	 __u8  bDeviceSubClass;       //子类
	 __u8  bDeviceProtocol;       //指定协议
	 __u8  bMaxPacketSize0;       //端点0对应的最大包大小
	 __u16 idVendor;              //厂家ID
	 __u16 idProduct;             //产品ID
	 __u16 bcdDevice;             //设备的发布号
	 __u8  iManufacturer;         //字符串描述符中厂家ID的索引
	 __u8  iProduct;              //字符串描述符中产品ID的索引
	 __u8  iSerialNumber;         //字符串描述符中设备序列号的索引
	 __u8  bNumConfigurations;    //配置描述符的个数,表示有多少个配置描述符
} __attribute__ ((packed));
  • USB配置描述符(usb_config_descriptor):描述有关特定设备配置的信息主机获取到设备描述符之后,就会获取配置描述符一个设置描述符可以有一个或多个配置描述符
    访问方式USB设备结构体usb_device->config[ ](所有配置).desc
    usb_device->actconfig(当前激活的配置)->desc
 struct usb_config_descriptor {   
	__u8  bLength;                 //描述符的长度
	__u8  bDescriptorType;         //描述符类型的编号

	__le16 wTotalLength;           //配置 所返回的所有数据的大小
	__u8  bNumInterfaces;          //配置 所支持的接口个数, 表示有多少个接口描述符
	__u8  bConfigurationValue;     //Set_Configuration命令需要的参数值
	__u8  iConfiguration;          //描述该配置的字符串的索引值
	__u8  bmAttributes;            //供电模式的选择
	__u8  bMaxPower;               //设备从总线提取的最大电流
 } __attribute__ ((packed));
  • USB接口描述符(usb_interface_descriptor):描述配置中的特定接口一个配置提供一个或多个接口(对于一个声卡设备,拥有音频输入接口与输出接口)
    访问方式usb_interface->cur_altsetting(当前激活的接口)->desc
    usb_interface->altsetting[ ](所有的接口).desc
struct usb_interface_descriptor {  
	__u8  bLength;                 //描述符的长度
	__u8  bDescriptorType;         //描述符类型的编号

	__u8  bInterfaceNumber;        //接口的编号
	__u8  bAlternateSetting;       //备用的接口描述符编号,提供不同质量的服务参数.
	__u8  bNumEndpoints;           //要使用的端点个数(不包括端点0), 表示有多少个端点描述符,比如鼠标就只有一个端点
	__u8  bInterfaceClass;         //接口类型,与驱动的id_table 
	__u8  bInterfaceSubClass;      //接口子类型
	__u8  bInterfaceProtocol;      //接口所遵循的协议
	__u8  iInterface;              //描述该接口的字符串索引值
} __attribute__ ((packed)
  • USB端点描述符(usb_endpoint_descriptor):描述该接口中每个端点的带宽要求所需的信息
    访问方式usb_interface->cur_altsetting(当前激活的接口)->endpoint[] (所有端点).desc
struct usb_endpoint_descriptor {
	__u8  bLength;                 //描述符的长度
	__u8  bDescriptorType;         //描述符类型的编号

	__u8  bEndpointAddress;        //端点编号,比如端点1,就是1
	__u8  bmAttributes;            //端点的属性, 比如中断传输类型,输入类型
	__le16 wMaxPacketSize;         //一个端点的最大包大小,
	__u8  bInterval;               //间隔时间,用在中断传输上,比如间隔时间查询鼠标的数据

	/* NOTE:  these two are _only_ in audio endpoints. */
	/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
	__u8  bRefresh;
	__u8  bSynchAddress;
} __attribute__ ((packed));

四者的关系如下:

  • 一个USB设备只有一个USB设备描述符

  • 一个设置描述符可以有一个或多个配置描述符

  • 一个配置描述符可以有一个或多个接口描述符

  • 一个接口描述符可以有一个或多个端点描述符
    在这里插入图片描述


3、UVC-Video类中的描述符

参考手册,得到如下图:其中白色底为固定的描述符灰色底为此设备拓展的描述符
在这里插入图片描述


二、打印USB摄像头的描述符

使用到的USB摄像头为百问网的usb-cmos二合一摄像头,参考libusb-1.0.16-rc10源码


1、代码的编写

/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    myuvc.c
 * @brief   打印usb的设备、配置、IAD、接口、自定义、端点描述符并解析自定义描述符
 * @author  K
 * @version 0.0.1
 * @date    2021-07-21
 * @license MulanPSL-1.0
 *
 * 文件修改历史:
 * <时间>         | <版本>      | <作者>    | <描述>
 * 2021-07-21   | v0.0.1    | Kcode   | 打印并解析描述符
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>

#include <media/v4l2-common.h>

/*!
 * 所支持的usb设备类
 */
static struct usb_device_id myuvc_ids[] = {
	/* Generic USB Video Class */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },	 /**< VideoControl interface */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /**< VideoStreaming interface */
	{}
};

static const char *get_guid(const unsigned char *buf)
{
	static char guid[39];

	/* NOTE:  see RFC 4122 for more information about GUID/UUID
	 * structure.  The first fields fields are historically big
	 * endian numbers, dating from Apollo mc68000 workstations.
	 */
	sprintf(guid, "{%02x%02x%02x%02x"
			"-%02x%02x"
			"-%02x%02x"
			"-%02x%02x"
			"-%02x%02x%02x%02x%02x%02x}",
	       buf[0], buf[1], buf[2], buf[3],
	       buf[4], buf[5],
	       buf[6], buf[7],
	       buf[8], buf[9],
	       buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
	return guid;
}

/*!
 * 解析VideoControl Interface接口的自定义描述符
 */
static void parse_videocontrol_interface(struct usb_interface *intf,
							unsigned char *buf, int buflen)
{
    static const char * const ctrlnames[] = {
        "Brightness", "Contrast", "Hue", "Saturation", "Sharpness", "Gamma",
        "White Balance Temperature", "White Balance Component", "Backlight Compensation",
        "Gain", "Power Line Frequency", "Hue, Auto", "White Balance Temperature, Auto",
        "White Balance Component, Auto", "Digital Multiplier", "Digital Multiplier Limit",
        "Analog Video Standard", "Analog Video Lock Status"
    };
    static const char * const camctrlnames[] = {
        "Scanning Mode", "Auto-Exposure Mode", "Auto-Exposure Priority",
        "Exposure Time (Absolute)", "Exposure Time (Relative)", "Focus (Absolute)",
        "Focus (Relative)", "Iris (Absolute)", "Iris (Relative)", "Zoom (Absolute)",
        "Zoom (Relative)", "PanTilt (Absolute)", "PanTilt (Relative)",
        "Roll (Absolute)", "Roll (Relative)", "Reserved", "Reserved", "Focus, Auto",
        "Privacy"
    };
    static const char * const stdnames[] = {
        "None", "NTSC - 525/60", "PAL - 625/50", "SECAM - 625/50",
        "NTSC - 625/50", "PAL - 525/60" };
    unsigned int i, ctrls, stds, n, p, termt, freq;

	/*!
	 * 打印所有的在VideoControl Interface接口下的自定义描述符
	 */
    while (buflen > 0) {

        if (buf[1] != USB_DT_CS_INTERFACE)
            printk("      Warning: Invalid descriptor\n");
        else if (buf[0] < 3)
            printk("      Warning: Descriptor too short\n");
        printk("      VideoControl Interface Descriptor:\n"
               "        bLength             %5u\n"
               "        bDescriptorType     %5u\n"
               "        bDescriptorSubtype  %5u ",
               buf[0], buf[1], buf[2]);
        switch (buf[2]) {
        case 0x01:  /* HEADER */
            printk("(HEADER)\n");
            n = buf[11];
            if (buf[0] < 12+n)
                printk("      Warning: Descriptor too short\n");
            freq = buf[7] | (buf[8] << 8) | (buf[9] << 16) | (buf[10] << 24);
            printk("        bcdUVC              %2x.%02x\n"
                   "        wTotalLength        %5u\n"
                   "        dwClockFrequency    %5u.%06uMHz\n"
                   "        bInCollection       %5u\n",
                   buf[4], buf[3], buf[5] | (buf[6] << 8), freq / 1000000,
                   freq % 1000000, n);
            for (i = 0; i < n; i++)
                printk("        baInterfaceNr(%2u)   %5u\n", i, buf[12+i]);
            break;

        case 0x02:  /* INPUT_TERMINAL */
            printk("(INPUT_TERMINAL)\n");
            termt = buf[4] | (buf[5] << 8);
            n = termt == 0x0201 ? 7 : 0;
            if (buf[0] < 8 + n)
                printk("      Warning: Descriptor too short\n");
            printk("        bTerminalID         %5u\n"
                   "        wTerminalType      0x%04x\n"
                   "        bAssocTerminal      %5u\n",
                   buf[3], termt, buf[6]);
            printk("        iTerminal           %5u\n",
                   buf[7]);
            if (termt == 0x0201) {
                n += buf[14];
                printk("        wObjectiveFocalLengthMin  %5u\n"
                       "        wObjectiveFocalLengthMax  %5u\n"
                       "        wOcularFocalLength        %5u\n"
                       "        bControlSize              %5u\n",
                       buf[8] | (buf[9] << 8), buf[10] | (buf[11] << 8),
                       buf[12] | (buf[13] << 8), buf[14]);
                ctrls = 0;
                for (i = 0; i < 3 && i < buf[14]; i++)
                    ctrls = (ctrls << 8) | buf[8+n-i-1];
                printk("        bmControls           0x%08x\n", ctrls);
                for (i = 0; i < 19; i++)
                    if ((ctrls >> i) & 1)
                        printk("          %s\n", camctrlnames[i]);
            }
            break;

        case 0x03:  /* OUTPUT_TERMINAL */
            printk("(OUTPUT_TERMINAL)\n");
            termt = buf[4] | (buf[5] << 8);
            if (buf[0] < 9)
                printk("      Warning: Descriptor too short\n");
            printk("        bTerminalID         %5u\n"
                   "        wTerminalType      0x%04x\n"
                   "        bAssocTerminal      %5u\n"
                   "        bSourceID           %5u\n"
                   "        iTerminal           %5u\n",
                   buf[3], termt, buf[6], buf[7], buf[8]);
            break;

        case 0x04:  /* SELECTOR_UNIT */
            printk("(SELECTOR_UNIT)\n");
            p = buf[4];
            if (buf[0] < 6+p)
                printk("      Warning: Descriptor too short\n");

            printk("        bUnitID             %5u\n"
                   "        bNrInPins           %5u\n",
                   buf[3], p);
            for (i = 0; i < p; i++)
                printk("        baSource(%2u)        %5u\n", i, buf[5+i]);
            printk("        iSelector           %5u\n",
                   buf[5+p]);
            break;

        case 0x05:  /* PROCESSING_UNIT */
            printk("(PROCESSING_UNIT)\n");
            n = buf[7];
            if (buf[0] < 10+n)
                printk("      Warning: Descriptor too short\n");
            printk("        bUnitID             %5u\n"
                   "        bSourceID           %5u\n"
                   "        wMaxMultiplier      %5u\n"
                   "        bControlSize        %5u\n",
                   buf[3], buf[4], buf[5] | (buf[6] << 8), n);
            ctrls = 0;
            for (i = 0; i < 3 && i < n; i++)
                ctrls = (ctrls << 8) | buf[8+n-i-1];
            printk("        bmControls     0x%08x\n", ctrls);
            for (i = 0; i < 18; i++)
                if ((ctrls >> i) & 1)
                    printk("          %s\n", ctrlnames[i]);
            stds = buf[9+n];
            printk("        iProcessing         %5u\n"
                   "        bmVideoStandards     0x%2x\n", buf[8+n], stds);
            for (i = 0; i < 6; i++)
                if ((stds >> i) & 1)
                    printk("          %s\n", stdnames[i]);
            break;

        case 0x06:  /* EXTENSION_UNIT */
            printk("(EXTENSION_UNIT)\n");
            p = buf[21];
            n = buf[22+p];
            if (buf[0] < 24+p+n)
                printk("      Warning: Descriptor too short\n");
            printk("        bUnitID             %5u\n"
                   "        guidExtensionCode         %s\n"
                   "        bNumControl         %5u\n"
                   "        bNrPins             %5u\n",
                   buf[3], get_guid(&buf[4]), buf[20], buf[21]);
            for (i = 0; i < p; i++)
                printk("        baSourceID(%2u)      %5u\n", i, buf[22+i]);
            printk("        bControlSize        %5u\n", buf[22+p]);
            for (i = 0; i < n; i++)
                printk("        bmControls(%2u)       0x%02x\n", i, buf[23+p+i]);
            printk("        iExtension          %5u\n",
                   buf[23+p+n]);
            break;

        default:
            printk("(unknown)\n"
                   "        Invalid desc subtype:");
            break;
        }

        buflen -= buf[0];
        buf    += buf[0];
    }
}

/*!
 * 打印端点描述符
 */
static void dump_endpoint(const struct usb_endpoint_descriptor *endpoint)
{
	static const char * const typeattr[] = {
		"Control",
		"Isochronous",
		"Bulk",
		"Interrupt"
	};
	static const char * const syncattr[] = {
		"None",
		"Asynchronous",
		"Adaptive",
		"Synchronous"
	};
	static const char * const usage[] = {
		"Data",
		"Feedback",
		"Implicit feedback Data",
		"(reserved)"
	};
	static const char * const hb[] = { "1x", "2x", "3x", "(?\?)" };
	unsigned wmax = le16_to_cpu(endpoint->wMaxPacketSize);

	printk("	  Endpoint Descriptor:\n"
		   "		bLength 			%5u\n"
		   "		bDescriptorType 	%5u\n"
		   "		bEndpointAddress	 0x%02x  EP %u %s\n"
		   "		bmAttributes		%5u\n"
		   "		  Transfer Type 		   %s\n"
		   "		  Synch Type			   %s\n"
		   "		  Usage Type			   %s\n"
		   "		wMaxPacketSize	   0x%04x  %s %d bytes\n"
		   "		bInterval			%5u\n",
		   endpoint->bLength,
		   endpoint->bDescriptorType,
		   endpoint->bEndpointAddress,
		   endpoint->bEndpointAddress & 0x0f,
		   (endpoint->bEndpointAddress & 0x80) ? "IN" : "OUT",
		   endpoint->bmAttributes,
		   typeattr[endpoint->bmAttributes & 3],
		   syncattr[(endpoint->bmAttributes >> 2) & 3],
		   usage[(endpoint->bmAttributes >> 4) & 3],
		   wmax, hb[(wmax >> 11) & 3], wmax & 0x7ff,
		   endpoint->bInterval);
	
	/* only for audio endpoints */
	if (endpoint->bLength == 9)
		printk("		bRefresh			%5u\n"
			   "		bSynchAddress		%5u\n",
			   endpoint->bRefresh, endpoint->bSynchAddress);

}

/*!
 * 解析VideoStreaming Interface接口的自定义描述符
 */
static void parse_videostreaming_interface(struct usb_interface *intf, 
										unsigned char *buf, int buflen)
{
	static const char * const colorPrims[] = { "Unspecified", "BT.709,sRGB",
		"BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M" };
	static const char * const transferChars[] = { "Unspecified", "BT.709",
		"BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M",
		"Linear", "sRGB"};
	static const char * const matrixCoeffs[] = { "Unspecified", "BT.709",
		"FCC", "BT.470-2 (B,G)", "SMPTE 170M (BT.601)", "SMPTE 240M" };
	unsigned int i, m, n, p, flags, len;

	/*!
	 * 打印所有的在VideoSteaming Interface接口下的自定义描述符
	 */
	while (buflen > 0) {

		if (buf[1] != USB_DT_CS_INTERFACE)
			printk("	  Warning: Invalid descriptor\n");
		else if (buf[0] < 3)
			printk("	  Warning: Descriptor too short\n");
		printk("	  VideoStreaming Interface Descriptor:\n"
			   "		bLength 						%5u\n"
			   "		bDescriptorType 				%5u\n"
			   "		bDescriptorSubtype				%5u ",
			   buf[0], buf[1], buf[2]);
		switch (buf[2]) {
		case 0x01: /* INPUT_HEADER */
			printk("(INPUT_HEADER)\n");
			p = buf[3];
			n = buf[12];
			if (buf[0] < 13+p*n)
				printk("	  Warning: Descriptor too short\n");
			printk("		bNumFormats 					%5u\n"
				   "		wTotalLength					%5u\n"
				   "		bEndPointAddress				%5u\n"
				   "		bmInfo							%5u\n"
				   "		bTerminalLink					%5u\n"
				   "		bStillCaptureMethod 			%5u\n"
				   "		bTriggerSupport 				%5u\n"
				   "		bTriggerUsage					%5u\n"
				   "		bControlSize					%5u\n",
				   p, buf[4] | (buf[5] << 8), buf[6], buf[7], buf[8],
				   buf[9], buf[10], buf[11], n);
			for (i = 0; i < p; i++)
				printk(
				"		 bmaControls(%2u)				  %5u\n",
					i, buf[13+p*n]);
			break;

		case 0x02: /* OUTPUT_HEADER */
			printk("(OUTPUT_HEADER)\n");
			p = buf[3];
			n = buf[8];
			if (buf[0] < 9+p*n)
				printk("	  Warning: Descriptor too short\n");
			printk("		bNumFormats 				%5u\n"
				   "		wTotalLength				%5u\n"
				   "		bEndpointAddress			%5u\n"
				   "		bTerminalLink				%5u\n"
				   "		bControlSize				%5u\n",
				   p, buf[4] | (buf[5] << 8), buf[6], buf[7], n);
			for (i = 0; i < p; i++)
				printk(
				"		 bmaControls(%2u)			  %5u\n",
					i, buf[9+p*n]);
			break;

		case 0x03: /* STILL_IMAGE_FRAME */
			printk("(STILL_IMAGE_FRAME)\n");
			n = buf[4];
			m = buf[5+4*n];
			if (buf[0] < 6+4*n+m)
				printk("	  Warning: Descriptor too short\n");
			printk("		bEndpointAddress				%5u\n"
				   "		bNumImageSizePatterns			  %3u\n",
				   buf[3], n);
			for (i = 0; i < n; i++)
				printk("		wWidth(%2u) 					 %5u\n"
					   "		wHeight(%2u)					 %5u\n",
					   i, buf[5+4*i] | (buf[6+4*i] << 8),
					   i, buf[7+4*i] | (buf[8+4*i] << 8));
			printk("		bNumCompressionPatterns 		  %3u\n", n);
			for (i = 0; i < m; i++)
				printk("		bCompression(%2u)				 %5u\n",
					   i, buf[6+4*n+i]);
			break;

		case 0x04: /* FORMAT_UNCOMPRESSED */
		case 0x10: /* FORMAT_FRAME_BASED */
			if (buf[2] == 0x04) {
				printk("(FORMAT_UNCOMPRESSED)\n");
				len = 27;
			} else {
				printk("(FORMAT_FRAME_BASED)\n");
				len = 28;
			}
			if (buf[0] < len)
				printk("	  Warning: Descriptor too short\n");
			flags = buf[25];
			printk("		bFormatIndex					%5u\n"
				   "		bNumFrameDescriptors			%5u\n"
				   "		guidFormat							  %s\n"
				   "		bBitsPerPixel					%5u\n"
				   "		bDefaultFrameIndex				%5u\n"
				   "		bAspectRatioX					%5u\n"
				   "		bAspectRatioY					%5u\n"
				   "		bmInterlaceFlags				 0x%02x\n",
				   buf[3], buf[4], get_guid(&buf[5]), buf[21], buf[22],
				   buf[23], buf[24], flags);
			printk("		  Interlaced stream or variable: %s\n",
				   (flags & (1 << 0)) ? "Yes" : "No");
			printk("		  Fields per frame: %u fields\n",
				   (flags & (1 << 1)) ? 1 : 2);
			printk("		  Field 1 first: %s\n",
				   (flags & (1 << 2)) ? "Yes" : "No");
			printk("		  Field pattern: ");
			switch ((flags >> 4) & 0x03) {
			case 0:
				printk("Field 1 only\n");
				break;
			case 1:
				printk("Field 2 only\n");
				break;
			case 2:
				printk("Regular pattern of fields 1 and 2\n");
				break;
			case 3:
				printk("Random pattern of fields 1 and 2\n");
				break;
			}
			printk("		  bCopyProtect					%5u\n", buf[26]);
			if (buf[2] == 0x10)
				printk("		  bVariableSize 				%5u\n", buf[27]);
			break;

		case 0x05: /* FRAME UNCOMPRESSED */
		case 0x07: /* FRAME_MJPEG */
		case 0x11: /* FRAME_FRAME_BASED */
			if (buf[2] == 0x05) {
				printk("(FRAME_UNCOMPRESSED)\n");
				n = 25;
			} else if (buf[2] == 0x07) {
				printk("(FRAME_MJPEG)\n");
				n = 25;
			} else {
				printk("(FRAME_FRAME_BASED)\n");
				n = 21;
			}
			len = (buf[n] != 0) ? (26+buf[n]*4) : 38;
			if (buf[0] < len)
				printk("	  Warning: Descriptor too short\n");
			flags = buf[4];
			printk("		bFrameIndex 					%5u\n"
				   "		bmCapabilities					 0x%02x\n",
				   buf[3], flags);
			printk("		  Still image %ssupported\n",
				   (flags & (1 << 0)) ? "" : "un");
			if (flags & (1 << 1))
				printk("		  Fixed frame-rate\n");
			printk("		wWidth							%5u\n"
				   "		wHeight 						%5u\n"
				   "		dwMinBitRate				%9u\n"
				   "		dwMaxBitRate				%9u\n",
				   buf[5] | (buf[6] <<	8), buf[7] | (buf[8] << 8),
				   buf[9] | (buf[10] << 8) | (buf[11] << 16) | (buf[12] << 24),
				   buf[13] | (buf[14] << 8) | (buf[15] << 16) | (buf[16] << 24));
			if (buf[2] == 0x11)
				printk("		dwDefaultFrameInterval		%9u\n"
					   "		bFrameIntervalType				%5u\n"
					   "		dwBytesPerLine				%9u\n",
					   buf[17] | (buf[18] << 8) | (buf[19] << 16) | (buf[20] << 24),
					   buf[21],
					   buf[22] | (buf[23] << 8) | (buf[24] << 16) | (buf[25] << 24));
			else
				printk("		dwMaxVideoFrameBufferSize	%9u\n"
					   "		dwDefaultFrameInterval		%9u\n"
					   "		bFrameIntervalType				%5u\n",
					   buf[17] | (buf[18] << 8) | (buf[19] << 16) | (buf[20] << 24),
					   buf[21] | (buf[22] << 8) | (buf[23] << 16) | (buf[24] << 24),
					   buf[25]);
			if (buf[n] == 0)
				printk("		dwMinFrameInterval			%9u\n"
					   "		dwMaxFrameInterval			%9u\n"
					   "		dwFrameIntervalStep 		%9u\n",
					   buf[26] | (buf[27] << 8) | (buf[28] << 16) | (buf[29] << 24),
					   buf[30] | (buf[31] << 8) | (buf[32] << 16) | (buf[33] << 24),
					   buf[34] | (buf[35] << 8) | (buf[36] << 16) | (buf[37] << 24));
			else
				for (i = 0; i < buf[n]; i++)
					printk("		dwFrameInterval(%2u)		 %9u\n",
						   i, buf[26+4*i] | (buf[27+4*i] << 8) |
						   (buf[28+4*i] << 16) | (buf[29+4*i] << 24));
			break;

		case 0x06: /* FORMAT_MJPEG */
			printk("(FORMAT_MJPEG)\n");
			if (buf[0] < 11)
				printk("	  Warning: Descriptor too short\n");
			flags = buf[5];
			printk("		bFormatIndex					%5u\n"
				   "		bNumFrameDescriptors			%5u\n"
				   "		bFlags							%5u\n",
				   buf[3], buf[4], flags);
			printk("		  Fixed-size samples: %s\n",
				   (flags & (1 << 0)) ? "Yes" : "No");
			flags = buf[9];
			printk("		bDefaultFrameIndex				%5u\n"
				   "		bAspectRatioX					%5u\n"
				   "		bAspectRatioY					%5u\n"
				   "		bmInterlaceFlags				 0x%02x\n",
				   buf[6], buf[7], buf[8], flags);
			printk("		  Interlaced stream or variable: %s\n",
				   (flags & (1 << 0)) ? "Yes" : "No");
			printk("		  Fields per frame: %u fields\n",
				   (flags & (1 << 1)) ? 2 : 1);
			printk("		  Field 1 first: %s\n",
				   (flags & (1 << 2)) ? "Yes" : "No");
			printk("		  Field pattern: ");
			switch ((flags >> 4) & 0x03) {
			case 0:
				printk("Field 1 only\n");
				break;
			case 1:
				printk("Field 2 only\n");
				break;
			case 2:
				printk("Regular pattern of fields 1 and 2\n");
				break;
			case 3:
				printk("Random pattern of fields 1 and 2\n");
				break;
			}
			printk("		  bCopyProtect					%5u\n", buf[10]);
			break;

		case 0x0a: /* FORMAT_MPEG2TS */
			printk("(FORMAT_MPEG2TS)\n");
			len = buf[0] < 23 ? 7 : 23;
			if (buf[0] < len)
				printk("	  Warning: Descriptor too short\n");
			printk("		bFormatIndex					%5u\n"
				   "		bDataOffset 					%5u\n"
				   "		bPacketLength					%5u\n"
				   "		bStrideLength					%5u\n",
				   buf[3], buf[4], buf[5], buf[6]);
			if (len > 7)
				printk("		guidStrideFormat					  %s\n",
					   get_guid(&buf[7]));
			break;

		case 0x0d: /* COLORFORMAT */
			printk("(COLORFORMAT)\n");
			if (buf[0] < 6)
				printk("	  Warning: Descriptor too short\n");
			printk("		bColorPrimaries 				%5u (%s)\n",
				   buf[3], (buf[3] <= 5) ? colorPrims[buf[3]] : "Unknown");
			printk("		bTransferCharacteristics		%5u (%s)\n",
				   buf[4], (buf[4] <= 7) ? transferChars[buf[4]] : "Unknown");
			printk("		bMatrixCoefficients 			%5u (%s)\n",
				   buf[5], (buf[5] <= 5) ? matrixCoeffs[buf[5]] : "Unknown");
			break;

		default:
			printk("		Invalid desc subtype:");
			break;
		}
		buflen -= buf[0];
		buf    += buf[0];
	}

}

static int myuvc_probe(struct usb_interface *intf,
		     const struct usb_device_id *id)
{	
	unsigned char *buffer;
	int i, j, k, l, m;
	int buflen;
    int desc_len;
    int desc_cnt;
	static int cnt = 0;
	struct usb_device *dev = interface_to_usbdev(intf);
	struct usb_device_descriptor *descriptor = &dev->descriptor;
	struct usb_host_config *hostconfig;
	struct usb_config_descriptor *config;
	struct usb_interface_assoc_descriptor *assoc_desc;
	struct usb_interface_descriptor *interface;
    struct usb_endpoint_descriptor  *endpoint;

	printk("myuvc_probe : cnt = %d\n", cnt++);

    /*!
     * 打印设备描述符
     */
	printk("Device Descriptor:\n"
	       "  bLength             %5u\n"
	       "  bDescriptorType     %5u\n"
	       "  bcdUSB              %2x.%02x\n"
	       "  bDeviceClass        %5u \n"
	       "  bDeviceSubClass     %5u \n"
	       "  bDeviceProtocol     %5u \n"
	       "  bMaxPacketSize0     %5u\n"
	       "  idVendor           0x%04x \n"
	       "  idProduct          0x%04x \n"
	       "  bcdDevice           %2x.%02x\n"
	       "  iManufacturer       %5u\n"
	       "  iProduct            %5u\n"
	       "  iSerial             %5u\n"
	       "  bNumConfigurations  %5u\n",
	       descriptor->bLength, descriptor->bDescriptorType,
	       descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff,
	       descriptor->bDeviceClass, 
	       descriptor->bDeviceSubClass,
	       descriptor->bDeviceProtocol, 
	       descriptor->bMaxPacketSize0,
	       descriptor->idVendor,  descriptor->idProduct,
	       descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff,
	       descriptor->iManufacturer, 
	       descriptor->iProduct, 
	       descriptor->iSerialNumber, 
	       descriptor->bNumConfigurations);

	/*!
	 * 打印配置描述符
	 */
	for (i = 0; i < descriptor->bNumConfigurations; i++) {
		hostconfig = &dev->config[i];
		config     = &hostconfig->desc;
		printk("  Configuration Descriptor %d:\n"
		       "    bLength             %5u\n"
		       "    bDescriptorType     %5u\n"
		       "    wTotalLength        %5u\n"
		       "    bNumInterfaces      %5u\n"
		       "    bConfigurationValue %5u\n"
		       "    iConfiguration      %5u\n"
		       "    bmAttributes         0x%02x\n",
		       i, 
		       config->bLength, config->bDescriptorType,
		       le16_to_cpu(config->wTotalLength),
		       config->bNumInterfaces, config->bConfigurationValue,
		       config->iConfiguration,
		       config->bmAttributes);

		/*!
		 * 打印IAD描述符
		 */
		assoc_desc = hostconfig->intf_assoc[0];
		printk("    Interface Association:\n"
			   "      bLength             %5u\n"
			   "      bDescriptorType     %5u\n"
			   "      bFirstInterface     %5u\n"
			   "      bInterfaceCount     %5u\n"
			   "      bFunctionClass      %5u\n"
			   "      bFunctionSubClass   %5u\n"
			   "      bFunctionProtocol   %5u\n"
			   "      iFunction           %5u\n",
			assoc_desc->bLength,
			assoc_desc->bDescriptorType,
			assoc_desc->bFirstInterface,
			assoc_desc->bInterfaceCount,
			assoc_desc->bFunctionClass,
			assoc_desc->bFunctionSubClass,
			assoc_desc->bFunctionProtocol,
			assoc_desc->iFunction);

		/*!
		 * 可能有多个接口,根据intf->num_altsetting数
		 * 打印每一个接口的描述符
		 */
		for (j = 0; j < intf->num_altsetting; j++) {
			interface = &intf->altsetting[j].desc;
			printk("    Interface Descriptor altsetting %d:\n"
			       "      bLength             %5u\n"
			       "      bDescriptorType     %5u\n"
			       "      bInterfaceNumber    %5u\n"
			       "      bAlternateSetting   %5u\n"
			       "      bNumEndpoints       %5u\n"
			       "      bInterfaceClass     %5u\n"
			       "      bInterfaceSubClass  %5u\n"
			       "      bInterfaceProtocol  %5u\n"
			       "      iInterface          %5u\n",
			       j, 
			       interface->bLength, interface->bDescriptorType, 
			       interface->bInterfaceNumber, interface->bAlternateSetting, 
			       interface->bNumEndpoints, interface->bInterfaceClass,
			       interface->bInterfaceSubClass, interface->bInterfaceProtocol,
			       interface->iInterface);

			/*!
			 * 打印端点描述符
			 */
			for (m = 0; m < interface->bNumEndpoints; m++) {
				endpoint = &intf->altsetting[j].endpoint[m].desc;
				dump_endpoint(endpoint);
			}
		}

		/*!
		 * 打印设备本身自定义的描述符,二进制形式
		 */
		buffer = intf->cur_altsetting->extra;
		buflen = intf->cur_altsetting->extralen;
		printk("extra buffer of interface %d:\n", cnt-1);
		k = 0;
		desc_cnt = 0;
		while (k < buflen)
		{
		    desc_len = buffer[k];
		    printk("extra desc %d: ", desc_cnt);
		    for (l = 0; l < desc_len; l++, k++) {
		        printk("%02x ", buffer[k]);
		    }

		    desc_cnt++;
		    printk("\n");
		}

		interface = &intf->cur_altsetting->desc;	 /**< 获取接口描述符 */

		/*!
		 *	解析VideoControl Interface接口下的自定义描述符
		 */
        if ((buffer[1] == USB_DT_CS_INTERFACE) && (interface->bInterfaceSubClass == 1))
            parse_videocontrol_interface(intf, buffer, buflen);

		/*!
		 *	解析VideoStreaming Interface接口下的自定义描述符
		 */
		if ((buffer[1] == USB_DT_CS_INTERFACE) && (interface->bInterfaceSubClass == 2))
            parse_videostreaming_interface(intf, buffer, buflen);			    
	}
	
    return 0;
}
			 
static void myuvc_disconnect(struct usb_interface *intf)
{
	static int cnt = 0;
    printk("myuvc_disconnect : cnt = %d\n", cnt++);
}

struct usb_driver myuvc_driver = {
	.name		= "myuvcvideo",
	.probe		= myuvc_probe,
	.disconnect	= myuvc_disconnect,
	.id_table	= myuvc_ids,
};

static int myuvc_init(void)
{
	int result;
	
	result = usb_register(&myuvc_driver);
	if (result == 0)
		printk("USB register error!\n");
	return result;
}

static void myuvc_cleanup(void)
{
	usb_deregister(&myuvc_driver);
}

module_init(myuvc_init);
module_exit(myuvc_cleanup);
MODULE_LICENSE("GPL")

2、Makefile编写

KERN_DIR = /usr/src/linux-headers-4.13.0-41-generic

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= myuvc.o

3、运行与分析

3.1 运行

执行make生成驱动文件并插入USB摄像头之后,卸载原先虚拟机的摄像头驱动sudo rmmod uvcvideo装载新驱动sudo insmod myuvc.ko,后执行dmesg > dmesg.txt把信息输出到txt中

3.2 分析

整理后的dmesg.txt信息如下:
由于代码中支持列表填写了VideoControl InterfaceVideoStreaming Interface,且摄像头都支持,所以会打印两次设备描述符(两次打印的是同一个设备描述符),实际使用只需要保留第一个

对于使用的摄像头

  • 有一个配置: bDeviceProtocol 1
  • 该配置下有4个接口:bNumInterfaces 4
  • 打印所有接口下的端点信息
  • 打印并解析了VideoControl InterfaceVideoStreaming Interface接口下的自定义配置
 myuvc_disconnect : cnt = 0
 myuvc_disconnect : cnt = 1
 myuvc_probe : cnt = 0
 Device Descriptor:
   bLength                18
   bDescriptorType         1
   bcdUSB               2.00
   bDeviceClass          239 
   bDeviceSubClass         2 
   bDeviceProtocol         1 
   bMaxPacketSize0        64
   idVendor           0x1b3b 
   idProduct          0x2977 
   bcdDevice            1.0a
   iManufacturer           0
   iProduct                0
   iSerial                 0
   bNumConfigurations      1
   Configuration Descriptor 0:
     bLength                 9
     bDescriptorType         2
     wTotalLength          492
     bNumInterfaces          4
     bConfigurationValue     1
     iConfiguration          0
     bmAttributes         0x80
     Interface Association:
       bLength                 8
       bDescriptorType        11
       bFirstInterface         0
       bInterfaceCount         2
       bFunctionClass         14
       bFunctionSubClass       3
       bFunctionProtocol       0
       iFunction               0
     Interface Descriptor altsetting 0:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        0
       bAlternateSetting       0
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      1
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x81  EP 1 IN
 		bmAttributes		    3
 		  Transfer Type 		   Interrupt
 		  Synch Type			   None
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0040  1x 64 bytes
 		bInterval			    6
 extra buffer of interface 0:
 extra desc 0: 
 0d 24 01 00 01 4d 00 80 8d 5b 00 01 01 

 extra desc 1: 
 12 24 02 01 01 02 00 00 01 00 03 00 01 00 03 80 2a 00 

 extra desc 2: 
 0b 24 05 03 01 00 00 02 3f 05 00 

 extra desc 3: 
 1a 24 06 04 3a ab 91 99 ef b2 c9 48 8f e9 8f e3 63 47 71 d0 08 01 03 01 0f 00 

 extra desc 4: 
 09 24 03 02 01 01 00 04 00 

       VideoControl Interface Descriptor:
         bLength                13
         bDescriptorType        36
         bDescriptorSubtype      1 
 (HEADER)
         bcdUVC               1.00
         wTotalLength           77
         dwClockFrequency        6.000000MHz
         bInCollection           1
         baInterfaceNr( 0)       1
       VideoControl Interface Descriptor:
         bLength                18
         bDescriptorType        36
         bDescriptorSubtype      2 
 (INPUT_TERMINAL)
         bTerminalID             1
         wTerminalType      0x0201
         bAssocTerminal          0
         iTerminal               0
         wObjectiveFocalLengthMin      1
         wObjectiveFocalLengthMax      3
         wOcularFocalLength            1
         bControlSize                  3
         bmControls           0x00002a80
           Iris (Absolute)
           Zoom (Absolute)
           PanTilt (Absolute)
           Roll (Absolute)
       VideoControl Interface Descriptor:
         bLength                11
         bDescriptorType        36
         bDescriptorSubtype      5 
 (PROCESSING_UNIT)
       Warning: Descriptor too short
         bUnitID                 3
         bSourceID               1
         wMaxMultiplier          0
         bControlSize            2
         bmControls     0x0000053f
           Brightness
           Contrast
           Hue
           Saturation
           Sharpness
           Gamma
           Backlight Compensation
           Power Line Frequency
         iProcessing             0
         bmVideoStandards     0x1a
           NTSC - 525/60
           SECAM - 625/50
           NTSC - 625/50
       VideoControl Interface Descriptor:
         bLength                26
         bDescriptorType        36
         bDescriptorSubtype      6 
 (EXTENSION_UNIT)
         bUnitID                 4
         guidExtensionCode         {3aab9199-efb2-c948-8fe9-8fe3634771d0}
         bNumControl             8
         bNrPins                 1
         baSourceID( 0)          3
         bControlSize            1
         bmControls( 0)       0x0f
         iExtension              0
       VideoControl Interface Descriptor:
         bLength                 9
         bDescriptorType        36
         bDescriptorSubtype      3 
 (OUTPUT_TERMINAL)
         bTerminalID             2
         wTerminalType      0x0101
         bAssocTerminal          0
         bSourceID               4
         iTerminal               0
 myuvc_probe : cnt = 1
 Device Descriptor:
   bLength                18
   bDescriptorType         1
   bcdUSB               2.00
   bDeviceClass          239 
   bDeviceSubClass         2 
   bDeviceProtocol         1 
   bMaxPacketSize0        64
   idVendor           0x1b3b 
   idProduct          0x2977 
   bcdDevice            1.0a
   iManufacturer           0
   iProduct                0
   iSerial                 0
   bNumConfigurations      1
   Configuration Descriptor 0:
     bLength                 9
     bDescriptorType         2
     wTotalLength          492
     bNumInterfaces          4
     bConfigurationValue     1
     iConfiguration          0
     bmAttributes         0x80
     Interface Association:
       bLength                 8
       bDescriptorType        11
       bFirstInterface         0
       bInterfaceCount         2
       bFunctionClass         14
       bFunctionSubClass       3
       bFunctionProtocol       0
       iFunction               0
     Interface Descriptor altsetting 0:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       0
       bNumEndpoints           0
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
     Interface Descriptor altsetting 1:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       1
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0080  1x 128 bytes
 		bInterval			    1
     Interface Descriptor altsetting 2:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       2
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0100  1x 256 bytes
 		bInterval			    1
     Interface Descriptor altsetting 3:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       3
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0200  1x 512 bytes
 		bInterval			    1
     Interface Descriptor altsetting 4:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       4
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0258  1x 600 bytes
 		bInterval			    1
     Interface Descriptor altsetting 5:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       5
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x0320  1x 800 bytes
 		bInterval			    1
     Interface Descriptor altsetting 6:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        1
       bAlternateSetting       6
       bNumEndpoints           1
       bInterfaceClass        14
       bInterfaceSubClass      2
       bInterfaceProtocol      0
       iInterface              0
 	  Endpoint Descriptor:
 		bLength 			    7
 		bDescriptorType 	    5
 		bEndpointAddress	 0x82  EP 2 IN
 		bmAttributes		    5
 		  Transfer Type 		   Isochronous
 		  Synch Type			   Asynchronous
 		  Usage Type			   Data
 		wMaxPacketSize	   0x03bc  1x 956 bytes
 		bInterval			    1
 extra buffer of interface 1:
 extra desc 0: 
 0e 24 01 01 79 00 82 00 02 00 00 01 01 00 

 extra desc 1: 
 0b 24 06 01 03 00 01 00 00 00 00 

 extra desc 2: 
 1e 24 07 01 01 80 02 e0 01 00 28 23 00 00 28 23 00 00 2c 01 00 15 16 05 00 01 15 16 05 00 

 extra desc 3: 
 1e 24 07 02 01 40 01 f0 00 00 ca 08 00 00 ca 08 00 00 4b 00 00 15 16 05 00 01 15 16 05 00 

 extra desc 4: 
 1e 24 07 03 01 a0 00 78 00 80 32 02 00 80 32 02 00 c0 12 00 00 15 16 05 00 01 15 16 05 00 

 extra desc 5: 
 06 24 0d 01 01 04 

 	  VideoStreaming Interface Descriptor:
 		bLength 						   14
 		bDescriptorType 				   36
 		bDescriptorSubtype				    1 
 (INPUT_HEADER)
 		bNumFormats 					    1
 		wTotalLength					  121
 		bEndPointAddress				  130
 		bmInfo							    0
 		bTerminalLink					    2
 		bStillCaptureMethod 			    0
 		bTriggerSupport 				    0
 		bTriggerUsage					    1
 		bControlSize					    1
 		 bmaControls( 0)				     11
 	  VideoStreaming Interface Descriptor:
 		bLength 						   11
 		bDescriptorType 				   36
 		bDescriptorSubtype				    6 
 (FORMAT_MJPEG)
 		bFormatIndex					    1
 		bNumFrameDescriptors			    3
 		bFlags							    0
 		  Fixed-size samples: No
 		bDefaultFrameIndex				    1
 		bAspectRatioX					    0
 		bAspectRatioY					    0
 		bmInterlaceFlags				 0x00
 		  Interlaced stream or variable: No
 		  Fields per frame: 1 fields
 		  Field 1 first: No
 		  Field pattern: 
 Field 1 only
 		  bCopyProtect					    0
 	  VideoStreaming Interface Descriptor:
 		bLength 						   30
 		bDescriptorType 				   36
 		bDescriptorSubtype				    7 
 (FRAME_MJPEG)
 		bFrameIndex 					    1
 		bmCapabilities					 0x01
 		  Still image supported
 		wWidth							  640
 		wHeight 						  480
 		dwMinBitRate				  2304000
 		dwMaxBitRate				  2304000
 		dwMaxVideoFrameBufferSize	    76800
 		dwDefaultFrameInterval		   333333
 		bFrameIntervalType				    1
 		dwFrameInterval( 0)		    333333
 	  VideoStreaming Interface Descriptor:
 		bLength 						   30
 		bDescriptorType 				   36
 		bDescriptorSubtype				    7 
 (FRAME_MJPEG)
 		bFrameIndex 					    2
 		bmCapabilities					 0x01
 		  Still image supported
 		wWidth							  320
 		wHeight 						  240
 		dwMinBitRate				   576000
 		dwMaxBitRate				   576000
 		dwMaxVideoFrameBufferSize	    19200
 		dwDefaultFrameInterval		   333333
 		bFrameIntervalType				    1
 		dwFrameInterval( 0)		    333333
 	  VideoStreaming Interface Descriptor:
 		bLength 						   30
 		bDescriptorType 				   36
 		bDescriptorSubtype				    7 
 (FRAME_MJPEG)
 		bFrameIndex 					    3
 		bmCapabilities					 0x01
 		  Still image supported
 		wWidth							  160
 		wHeight 						  120
 		dwMinBitRate				   144000
 		dwMaxBitRate				   144000
 		dwMaxVideoFrameBufferSize	     4800
 		dwDefaultFrameInterval		   333333
 		bFrameIntervalType				    1
 		dwFrameInterval( 0)		    333333
 	  VideoStreaming Interface Descriptor:
 		bLength 						    6
 		bDescriptorType 				   36
 		bDescriptorSubtype				   13 
 (COLORFORMAT)
 		bColorPrimaries 				    1 (BT.709,sRGB)
 		bTransferCharacteristics		    1 (BT.709)
 		bMatrixCoefficients 			    4 (SMPTE 170M (BT.601))

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/107497622