UVC(USB Video Class)设备通过USB接口提供视频流,支持标准的视频控制和数据传输。UVC设备还支持扩展单元(XU,Extension Unit),它允许设备制造商定义非标准的、专有的功能,通过控制XU可以实现自定义的设备配置。下面详细介绍UVC XU(扩展单元)信息的处理流程,尤其是从客户端到设备应用的流转过程。
1. UVC XU概述
UVC扩展单元(XU)是在标准UVC控制命令之外提供的额外接口,通常用来控制摄像头的非标准功能。XU可以在UVC的Video Control Interface (VC) 描述符中定义,通过设置不同的属性(如亮度、对比度、特殊效果等),允许客户端应用对设备进行控制。
2. 客户端对UVC XU的访问
客户端应用程序可以使用操作系统提供的标准接口访问UVC XU,通常通过V4L2 (Video for Linux 2) 或 DirectShow 等多媒体框架进行访问。
操作步骤:
-
发现设备:当客户端启动时,它会通过USB查询连接的UVC设备。USB设备会返回设备描述符(Device Descriptor),包括接口信息。
-
请求XU单元信息:
- 在初始化过程中,客户端会通过UVC标准的Video Control Requests与设备交互,获取设备所支持的功能,包括扩展单元(XU)的信息。
- 这个过程可以通过 V4L2 IOCTLs 访问,如
VIDIOC_QUERY_EXT_CTRL
,该接口允许客户端获取支持的扩展控制项的属性,包括控制ID、范围、步进值等。
-
发送XU控制请求:
- 客户端通过Control Requests与扩展单元进行交互。扩展单元的访问需要通过特定的控制命令(通常是
GET_CUR
或SET_CUR
)。 - 例如,使用 V4L2 时,
VIDIOC_G_EXT_CTRLS
和VIDIOC_S_EXT_CTRLS
可以分别用于获取和设置XU的属性。 - 在Windows上,可以通过 IOCTL_VIDEO_PROPERTY_CONTROL 等控制代码发送自定义的XU控制请求。
- 客户端通过Control Requests与扩展单元进行交互。扩展单元的访问需要通过特定的控制命令(通常是
3. XU请求的USB传输
在客户端发送请求到USB设备时,UVC协议会将这些命令打包成USB传输进行通信。流程如下:
-
设置XU请求:
- 当客户端通过系统接口发出XU请求后,内核会根据请求打包成USB控制传输(Control Transfer)。
- 具体地,这些请求会通过标准的USB SETUP包发送到设备,典型的UVC控制命令包括
bmRequestType
,bRequest
,wValue
,wIndex
等字段。
-
控制传输的过程:
- bmRequestType 指示是一个设备的控制请求,通常设置为向接口发送数据的方向。
- bRequest 代表要进行的UVC控制命令,比如GET_CUR、SET_CUR。
- wValue 和 wIndex 对应于具体的XU控制单元和属性。
- wLength 表示数据的长度,当控制信息通过控制传输阶段成功发送后,数据会通过
Data
阶段被送达设备。
4. 设备端的XU处理
UVC设备接收到扩展单元(XU)请求后,需要解析这些命令,并对设备内部的应用逻辑做出响应。
-
解析控制命令:
- UVC设备固件会通过USB堆栈接收并解析XU命令。根据请求的类型(GET/SET),设备可以选择返回当前属性值或更新设备内部配置。
-
应用逻辑的处理:
- 如果是GET请求,设备会查询内部当前配置值,并通过USB控制传输返回给客户端。
- 如果是SET请求,设备将会根据收到的值更新自身的状态,可能会改变某些硬件配置(如改变摄像头的图像处理方式、更新传感器的增益值等)。
- 这些处理逻辑通常由设备厂商在固件层面实现。
-
返回数据:
- 当设备完成控制命令的处理后,通过USB返回确认包或实际的数据(如当前设置的值)给主机,完成整个控制交互。
5. 客户端处理设备响应
一旦设备处理了XU请求并返回数据,客户端需要处理该数据,通常包括更新UI或将结果应用到视频流处理。
-
获取响应数据:
- 在GET请求的情况下,客户端会接收到设备返回的数据,通常是通过 V4L2 或 Windows API 获取,结果会包含在控制请求响应中。
-
UI更新:
- 客户端可能会根据设备返回的状态更新UI。例如,如果用户调整了摄像头的对比度,摄像头返回成功响应后,UI会更新显示新对比度值。
-
应用到视频处理:
- 有时,扩展单元的控制会直接影响到视频流。例如,通过扩展单元设置特殊的滤镜效果,设备端在处理XU命令时,会改变视频流的特性,客户端就能看到视频输出的变化。
6. 实例:通过V4L2访问XU
假设我们想通过V4L2设置一个UVC摄像头的扩展单元控制项(例如调整一个定制的镜头增益)。
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl[1];
memset(&ctrls, 0, sizeof(ctrls));
memset(&ctrl, 0, sizeof(ctrl));
ctrl[0].id = CUSTOM_XU_CTRL_ID; // XU控制项的ID
ctrl[0].value = desired_gain_value; // 要设置的值
ctrls.ctrl_class = V4L2_CTRL_CLASS_USER;
ctrls.count = 1;
ctrls.controls = ctrl;
// 使用VIDIOC_S_EXT_CTRLS设置扩展控制
if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) {
perror("Failed to set XU control");
}
总结
UVC的XU(扩展单元)机制允许自定义和专有控制功能。数据从客户端发送到设备的过程通常涉及V4L2或DirectShow等框架,UVC协议用于封装和传输这些控制请求,设备端固件解析请求后执行相应的动作,最后返回结果给客户端应用。这种机制不仅增强了UVC设备的可扩展性,还为设备制造商提供了灵活的自定义功能接口。
当一个UVC(USB Video Class)设备注册了多个扩展单元(XU)并为每个XU分配不同的GUID时,客户端需要通过以下步骤来识别和调用自己需要的XU。整个流程涉及查询设备的描述符、匹配GUID、确定XU的控制ID,以及使用正确的XU来进行操作。
1. 查询设备的扩展单元信息
- UVC设备通过其设备描述符上报所有扩展单元的信息。描述符中包含各个扩展单元的GUID,以及其他相关的信息如控制ID(Control ID)、属性和功能。
- 客户端可以通过标准的接口,如V4L2或Windows的IOCTL,来查询设备的扩展单元列表和其对应的GUID。
在Linux的V4L2中,VIDIOC_QUERY_EXT_CTRL
可以获取所有已注册的扩展单元控制项。以下是如何获取XU控制项的步骤:
-
打开设备文件(通常在
/dev/videoX
下):int fd = open("/dev/video0", O_RDWR); if (fd == -1) { perror("Error opening device"); return -1; }
-
查询扩展单元控制信息:
使用VIDIOC_QUERY_EXT_CTRL
循环遍历设备中注册的扩展单元控制项,并获取每个控制项的GUID。struct v4l2_queryctrl query_ctrl; memset(&query_ctrl, 0, sizeof(query_ctrl)); // 控制ID的初始值 query_ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; // 遍历所有的控制项 while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &query_ctrl)) { if (query_ctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { // 这是一个控制类,不是扩展控制,跳过 continue; } // 如果是扩展单元控制项,获取GUID信息 if (query_ctrl.flags & V4L2_CTRL_FLAG_NEXT_COMPOUND) { printf("Found XU control with ID: 0x%x\n", query_ctrl.id); // 这里可以获取更多关于扩展控制的信息,GUID可以通过其他相关调用获取 } // 查询下一个控制项 query_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; }
-
匹配GUID:
- 在遍历控制项时,客户端可以使用设备上报的GUID来与自己预期的GUID进行匹配,以确定哪个XU是客户端需要的。
- 一旦匹配成功,客户端就知道了该XU的控制ID,并可以通过该ID对XU进行操作。
2. 匹配GUID
- UVC扩展单元的GUID是设备制造商定义的,用来标识设备的特定功能。因此,客户端需要有预先的知识,知道设备上注册了哪些GUID,以及每个GUID对应的功能。
- 当客户端获取到设备的所有扩展单元信息后,它会对这些GUID进行匹配。匹配过程可以通过简单的比较算法来完成(例如将设备返回的GUID与客户端预期的GUID进行字节比较)。
示例:匹配GUID
uint8_t desired_guid[16] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
// 获取设备XU的GUID
uint8_t device_guid[16];
// 设备GUID从扩展单元描述符中获取
get_device_xu_guid(device_guid); // 假设这是获取设备XU GUID的函数
if (memcmp(desired_guid, device_guid, sizeof(desired_guid)) == 0) {
printf("Matched desired XU GUID\n");
// 可以继续访问该扩展单元
} else {
printf("GUID mismatch\n");
}
3. 通过控制ID操作XU
一旦匹配了GUID,客户端就知道了对应的控制ID(通常是扩展单元的Control ID)。客户端可以使用这个控制ID来对该扩展单元进行具体的操作,如设置值或获取当前状态。
操作扩展单元的控制项(设置或获取控制)
使用V4L2中的VIDIOC_S_EXT_CTRLS
或VIDIOC_G_EXT_CTRLS
操作扩展单元控制项:
-
设置扩展单元控制项:
struct v4l2_ext_control ctrl; struct v4l2_ext_controls ctrls; ctrl.id = CONTROL_ID; // 通过GUID匹配后获取的XU控制ID ctrl.value = desired_value; // 设置值 ctrls.ctrl_class = V4L2_CTRL_CLASS_USER; ctrls.count = 1; ctrls.controls = &ctrl; if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) == -1) { perror("Failed to set XU control"); }
-
获取扩展单元控制项:
struct v4l2_ext_control ctrl; struct v4l2_ext_controls ctrls; ctrl.id = CONTROL_ID; // 通过GUID匹配后获取的XU控制ID ctrls.ctrl_class = V4L2_CTRL_CLASS_USER; ctrls.count = 1; ctrls.controls = &ctrl; if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls) == -1) { perror("Failed to get XU control"); } else { printf("XU control value: %d\n", ctrl.value); }
4. 其他考虑
- 多XU支持:当UVC设备注册了多个XU并且每个XU都有不同的GUID时,客户端必须确保遍历所有可用的XU并检查每个XU的GUID,以确定其是否支持需要的功能。
- 功能预期:客户端通常已经知道要寻找哪些GUID,具体GUID的意义通常是设备制造商定义的。例如,一个XU可能用于调整镜头的曝光,另一个XU可能用于设置特定的视频滤镜效果。
- 错误处理:如果客户端无法匹配任何GUID,它可能会记录错误日志或提示用户设备不支持所需的功能。
总结
当UVC设备注册了多个GUID时,客户端通过查询设备的扩展单元描述符来获取每个扩展单元的GUID,并根据预期的GUID进行匹配。一旦匹配成功,客户端就可以使用对应的控制ID对扩展单元进行操作。这一过程通常通过标准的操作系统接口如V4L2或DirectShow来实现。