再谈USB摄像头UVC视频流接口控制请求dwMaxVideoFrameSize和dwMaxPayloadTransferSize
刚开始的时候,我们做一件事是认真,一丝不苟。对于各个数据的考量力求做到精度,其实这个追求可能并不是因为我们因为项目上的要求,而是我们为了弄清一个事实,有了这个事实我们就可以随处吹牛逼,也成了我们在做新的类似的项目上的优势之资。
言归正转,以前在windows下通过USB总线驱动虚拟的UVC摄像头都只支持一个分辨率,由于项目上的实际应用,也并未曾想过支持多个分辨率,甚至是多个多种数据格式。
当然,市面上基于UVC规范的USB摄像头,我们通过视频播放软件或usbtreeview分析其描述符,一般是支持大量的分辨率和数据格式的。当然这里的数据格式并不是UVC规范中的负载数据头,而是真实的视频数据格式,上层是需要通过解码显示的,例如常见的MJPG格式,NV12格式,H264数据格式。这些数据格式都是通过负载数据头进行打包在USB总线传输的。
在打开一个基于UVC规范的USB摄像头时,我们在应用层一般都是选择,确认即可,但对于UVC摄像头的固件来说,可真是忙的一踏糊涂,基主要功能是数据通读的协商。
如本人打开电脑中的摄像头,其抓包过程如下:
CTL a1 81 00 01 01 00 1a 00 GET CUR
26 IN 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL 21 01 00 01 01 00 1a 00 SET CUR
26 OUT 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 00 00 00 .............
CTL a1 81 00 01 01 00 1a 00 GET CUR
26 IN 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL a1 83 00 01 01 00 1a 00 GET MAX
26 IN 00 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL a1 82 00 01 01 00 1a 00 GET MIN
26 IN 00 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL 21 01 00 01 01 00 1a 00 SET CUR
26 OUT 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 00 00 00 .............
CTL a1 81 00 01 01 00 1a 00 GET CUR
26 IN 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL 21 01 00 02 01 00 1a 00 SET CUR
26 OUT 00 00 01 01 15 16 05 00 00 00 00 00 00 00 1e 00 00 00 4d 22 1c 00 00 0c 00 00 .............
CTL 01 0b 06 00 01 00 00 00 SET INTERFACE
可以看到,对于视频流接口VS前其进行大量的特定类请求,如GET_CUR,SET_CUR,GET_MIN,GET_MAX,选择子使用的是VS_PROBE_CONTROL。
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x01
bAlternateSetting : 0x00
bNumEndpoints : 0x00 (Default Control Pipe only)
bInterfaceClass : 0x0E (Video)
bInterfaceSubClass : 0x02 (Video Streaming)
bInterfaceProtocol : 0x00
iInterface : 0x00 (No String Descriptor)
Data (HexDump) : 09 04 01 00 00 0E 02 00 00
而到最后一个SET_CUR时,这时换一个选择子VS_COMMIT_CONTROL,进行提交选择,这这个结构体中定义了协商好了的数据格式。这个格式是由视频控制结构体即VS_COMMIT_CONTROL对应的数据结构决定的。
这个结构体是26字节,是一个典型的UVC1.0版本。在其类特定视频控制接口头描述符中也可以看到:
------- Video Control Interface Header Descriptor -----
bLength : 0x0D (13 bytes)
bDescriptorType : 0x24 (Video Control Interface)
bDescriptorSubtype : 0x01 (Video Control Header)
bcdUVC : 0x0100 (UVC Version 1.00)
wTotalLength : 0x0067 (103 bytes)
dwClockFreq : 0x00E4E1C0 (15 MHz)
bInCollection : 0x01 (1 VideoStreaming interface)
baInterfaceNr[1] : 0x01
Data (HexDump) : 0D 24 01 00 01 67 00 C0 E1 E4 00 01 01 .$...g.......
对于这个结构体,其结构体定义如下:
typedef struct _VideoControl
{
#pragam pack(1)
UINT16 bmHint;
UINT8 bFormatIndex;
UINT8 bFrameIndex;
UINT32 dwFrameInterval;
UINT16 wKeyFrameRate;
UINT16 wPFrameRate;
UINT16 wCompQuality;
UINT16 wCompWindowSize;
UINT16 wDelay;
UINT32 dwMaxVideoFrameSize;
UINT32 dwMaxPayloadTransferSize; //UVC1.0 分隔符
#pragma pack()
} VideoControl;
完整的支持各版本的结构体可见:
- VC摄像头VS_PROBE_CONTROL和VS_COMMIT_CONTROL对应的数据结构定义: http://www.usbzh.com/article/detail-549.html
这里有一个dwMaxVideoFrameSize字段比较有意思。是最大的视频单帧大小。其实在一般的摄像头固件中这个值是很少变化的,如从1920x1080分辨率换到1280x720或从MJPG换到H264,这个值很少变或者不变。其实这个值是只要大于单帧数据流的大小即可。而dwMaxPayloadTransferSize是指这一帧分多少包或者说多少次传输,这个包的最大值。所以前几小包的大小可以是dwMaxPayloadTransferSize,但最后一包一般小于dwMaxPayloadTransferSize这个值 。
所以类推到dwMaxVideoFrameSize上,这个字段表示一帧的最大值,一般的帧的最大小都是小于这个值,也可能永远不会出出现和这个值设定的值一样的帧,最大不一定存在。
这让我又不得想起一个电视剧中的台词:都一样,都一样。只要在合理的范围,都一样,都一样。都可以的….