USB UVC 3-- uvc gadget

1 简介

USB分为USB Host和USB Device两种,PC一般是USB Host,手机等终端数码设备一般是USB Device。OTG是指设备既可以作为USB Device,又可以作为USB Host。在Linux中,USB Device设备称为Gadget。

还是从代码开始看,这次的代码来自:

camera / uvc-gadget · GitLab

这个代码是应用层的,在驱动层之上。

还有一个代码是GitHub - wlhe/uvc-gadget: enhance uvc-gadget test application

可以的话比对着一起看吧,兼听则明啊。。。

最权威的资料还是来自于内核:

Linux UVC Gadget Driver — The Linux Kernel documentation

2 uvc-gadget基本说明

从代码结构来看,UVC整体架构如下:

1 USB Gadget 框架: Linux USB Gadget 框架允许设备模拟成为 USB 外设。通过 Gadget 框架,Linux 设备可以被其他主机识别为不同类型的 USB 设备,如 USB 存储、串口设备或 UVC 设备。

2 libcomposite: libcomposite 是一个用于 USB Gadget 框架的配置工具,能够帮助组合不同的 USB 功能,如串口和存储。uvc-gadget 通过 libcomposite 来实现 UVC 功能,并将设备注册为视频设备。

3 UVC (USB Video Class): UVC 是一种标准化的 USB 视频类设备协议,允许主机系统(如 Windows、Linux、macOS)通过 USB 连接从设备获取视频数据。UVC 使得无需额外的驱动,主机系统就可以自动识别和使用 UVC 设备。

4 Video4Linux (V4L2): Linux 的视频捕捉框架,uvc-gadget 使用 V4L2 接口将视频流捕获或模拟发送到 UVC 驱动,V4L2 设备通常对应 /dev/videoX。

5 libuvc: UVC Gadget 中可能用到 libuvc 来帮助处理 UVC 设备的操作和配置。它可以处理视频格式、分辨率等参数,完成 UVC 视频流的创建和传输。uvc gadget就处在这一层。

从代码来看,最简单一句脚本也可以实现这个功能:

ffmpeg -f v4l2 -i /dev/video0 -vcodec rawvideo -pix_fmt yuv420p -f v4l2 /dev/video1

从这里可以看出,uvc gadget的核心功能就是将通过 V4L2 捕获的视频帧映射到 USB 端点(endpoint)

详细看了下程序的help,说明如下:

administrator@raspberrypi:~ $ uvc-gadget -h
Usage: uvc-gadget [options] <uvc device>
Available options are
 -c <index|id> libcamera camera name
 -d device      V4L2 source device
 -i image       MJPEG image
 -s directory   directory of slideshow images
 -h             Print this help screen and exit

 <uvc device>   UVC device instance specifier

  For ConfigFS devices the <uvc device> parameter can take the form of a shortened
  function specifier such as: 'uvc.0', or if multiple gadgets are configured, the
  gadget name should be included to prevent ambiguity: 'g1/functions/uvc.0'.

  For legacy g_webcam UVC instances, this parameter will identify the UDC that the
  UVC function is bound to.

  The parameter is optional, and if not provided the first UVC function on the first
  gadget identified will be used.

Example usage:
    uvc-gadget uvc.1
    uvc-gadget g1/functions/uvc.1

    uvc-gadget musb-hdrc.0.auto

在树莓派的例程中,是

uvc-gadget -c 0 uvc.0

也就是将video0,映射到uvc.0

3 uvc gadget代码速通

整体流程和K230那篇文章差不多(K230 USB应用实战-UVC传输YUV及编码码流 — K230 文档

程序的基本流程,有点像一个代理,就是不停获取HOST主机发过来的USB的各个指令,然后根据这些指令,去控制本地的V4L2摄像头。

从main的结构来看,主要是以下流程:

configfs_parse_uvc_function,这个是解析参数。就是从之前在/sys/kernel/config的地方取得配置。

所有的配置存入下面的结构体:

struct uvc_function_config {  

 char *video;  

 char *udc;    

struct uvc_function_config_control control;    

struct uvc_function_config_streaming streaming;

};

events_init,之后是初始化event。其实也就是老朋友select的初始化,不过它这里对event进行了一次自己的封装。

v4l2_video_source_create,之后就是初始化视频流的源,就是前面说的video0。

uvc_stream_init_uvc,之后对uvc进行了一个初始化。这里也注册了回调函数uvc_events_process。

然后就是核心操作events_loop。这个程序本质上是一个基于select的服务程序,所以最重要的就是响应各种事件。

事件的派发是在events_dispatch,本质是调用之前在uvc_stream_init_uvc注册的回调函数uvc_events_process

uvc_events_process:

首先用ioctl从vdev->fd(TODO,看代码是v4l2的事件,但是感觉有点奇怪,这个fd回头调试再看看)中去抓取事件,VIDIOC_DQEVENT,放在v4l2_event中。

事件有以下几类,UVC_EVENT_CONNECT,UVC_EVENT_DISCONNECT,UVC_EVENT_SETUP,UVC_EVENT_DATA,UVC_EVENT_STREAMON,UVC_EVENT_STREAMOFF。

其中,前两个是不需要处理的。处理后面的即可。这部分可以看看再后面的交互流程。

重点还是开关流。UVC_EVENT_STREAMON,和UVC_EVENT_STREAMOFF。这部分后面有时间再看吧。。。

还有一个比较关心的点就是设置,因为最近工作要用到这方面,所以也多看看。就是UVC_EVENT_SETUP这个事件。里面的uvc_events_process_class。这里会调到uvc_events_process_control去处理各种设置事件。不过可惜这个部分现在并没有实现。

static void
uvc_events_process_control(struct uvc_device *dev, uint8_t req, uint8_t cs, uint8_t len,
			   struct uvc_request_data *resp)
{
	printf("control request (req %s cs %s)\n", uvc_request_name(req), pu_control_name(cs));
	(void)dev;

	/*
	 * Responding to controls is not currently implemented. As an interim
	 * measure respond to say that both get and set operations are permitted.
	 */
	resp->data[0] = 0x03;
	resp->length = len;
}

也就是说现在树莓派的uvc是没有任何设置功能的。。。

4 UVC启动过程

想再看看UVC交互的流程,可惜实在没找到图。可以看看之前我抓的USB信令顶一下:

USB抓包(1)_usb转包工具-CSDN博客

控制信令解释

  • bRequestType: 请求类型,决定这是标准请求还是类特定请求,指明请求是从主机到设备还是从设备到主机。
  • bRequest: 请求命令,表示具体的请求类型,例如 GET_CUR, SET_CUR 等。
  • wValue: 请求的附加信息,比如请求的是哪种控制。
  • wIndex: 表示请求的接口或特定实体,比如视频控制接口或视频流接口。
  • wLength: 请求的数据长度。

这些信令定义了主机与设备之间控制信息和状态的交互。

UVC 设备通过 USB 与主机通信,分为控制接口和数据接口。主机通过控制端点配置 UVC 设备的工作参数,并通过数据端点接收视频流。信令流程主要围绕控制视频格式、帧率、分辨率的 SET_CUR 和 GET_CUR 命令展开,视频数据通过 USB 高带宽的等时端点或批量端点传输至主机。

下面是在树莓派上的一些SET,GET的log

Device /dev/video8 opened: 1000480000.usb (gadget.0).
bRequestType a1 bRequest 86 wValue 0400 wIndex 0100 wLength 0001
control request (req GET_INFO cs GAIN)
bRequestType a1 bRequest 82 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_MIN cs GAIN)
bRequestType a1 bRequest 83 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_MAX cs GAIN)
bRequestType a1 bRequest 84 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_RES cs GAIN)
bRequestType a1 bRequest 87 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_DEF cs GAIN)
bRequestType a1 bRequest 82 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_MIN cs GAIN)
bRequestType a1 bRequest 83 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_MAX cs GAIN)
bRequestType a1 bRequest 84 wValue 0400 wIndex 0100 wLength 0004
control request (req GET_RES cs GAIN)
bRequestType a1 bRequest 86 wValue 0900 wIndex 0100 wLength 0001
control request (req GET_INFO cs GAMMA)
bRequestType a1 bRequest 82 wValue 0900 wIndex 0100 wLength 0002
control request (req GET_MIN cs GAMMA)
bRequestType a1 bRequest 83 wValue 0900 wIndex 0100 wLength 0002
control request (req GET_MAX cs GAMMA)
bRequestType a1 bRequest 84 wValue 0900 wIndex 0100 wLength 0002
control request (req GET_RES cs GAMMA)
bRequestType a1 bRequest 87 wValue 0900 wIndex 0100 wLength 0002
control request (req GET_DEF cs GAMMA)
bRequestType a1 bRequest 86 wValue 0200 wIndex 0200 wLength 0001
control request (req GET_INFO cs BRIGHTNESS)
bRequestType a1 bRequest 82 wValue 0200 wIndex 0200 wLength 0002
control request (req GET_MIN cs BRIGHTNESS)
bRequestType a1 bRequest 83 wValue 0200 wIndex 0200 wLength 0002
control request (req GET_MAX cs BRIGHTNESS)
bRequestType a1 bRequest 84 wValue 0200 wIndex 0200 wLength 0002
control request (req GET_RES cs BRIGHTNESS)
bRequestType a1 bRequest 87 wValue 0200 wIndex 0200 wLength 0002
control request (req GET_DEF cs BRIGHTNESS)

具体流程如下:

4.1. USB 设备识别与枚举

当 UVC 设备通过 USB 连接到主机时,首先进行标准的 USB 设备识别和枚举过程:

Device Attached: 设备连接到主机后,主机会检测到新设备插入并启动设备枚举。
获取设备描述符: 主机通过控制端点请求设备描述符,了解设备的类型、供应商 ID、产品 ID、版本号等基本信息。
获取配置描述符: 主机继续获取设备的配置描述符,这包括设备支持的接口类型、端点数量、端点属性等信息。对于 UVC 设备,主机会检测到该设备具有视频控制接口和视频流接口。
设置配置: 主机设置配置描述符,通知设备开始工作。
此时,主机已将设备识别为 USB 视频类设备,并为其加载适当的驱动程序,设备将进入工作状态。

4.2. UVC 控制接口通信

接下来,主机与 UVC 设备的控制接口进行通信,控制接口通过 USB 的控制端点(通常是端点 0)处理控制命令,这些命令用于配置设备的视频流参数,如分辨率、帧率等。主要的控制流程包括:

GET_CUR 请求: 主机通过 GET_CUR 请求读取设备当前的设置,如当前视频格式、分辨率和帧率等。
SET_CUR 请求: 主机通过 SET_CUR 请求设置视频格式、分辨率、帧率等参数。这个阶段可以定义视频流的特性,比如设置成 1280x720 分辨率和 MJPEG 格式。
GET_INFO 请求: 主机通过 GET_INFO 请求获取设备支持的控制项和属性。
这些控制命令依赖 UVC 协议中的 Video Control (VC) 和 Video Streaming (VS) 相关类定义。主机可以通过这些控制命令来配置设备的工作模式和视频格式。

4.3. 启动视频流

当主机完成了对 UVC 设备的配置后,主机会请求开始视频流数据传输:

请求视频帧: 主机发送请求,通知设备准备好传输视频数据。数据通过专门的数据端点传输,通常是等时端点 (isochronous endpoint) 或批量端点 (bulk endpoint),用于传输高带宽的多媒体数据。

在这一步中,视频数据按照配置的分辨率和格式(如 YUV 或 MJPEG 格式)通过数据端点发送到主机。主机将根据请求的帧率,周期性地接收这些帧数据。

视频数据传输: 设备将捕获到的视频帧分段打包,通过数据端点发送到主机,主机将数据进行解码和显示。

4.4. 主机与设备的持续交互

在视频流传输过程中,主机与 UVC 设备保持持续交互,主机可以动态地调整视频参数,或者请求特定的控制操作。

调整视频设置: 如果用户在主机侧更改视频设置(例如分辨率或帧率),主机会通过控制端点发送相应的 SET_CUR 请求,设备收到后相应地调整输出的视频流格式。

暂停/继续视频流: 主机可以通过控制端点请求暂停或继续视频传输,暂停视频流后,数据端点会暂时停止传输视频数据。

错误处理: 在数据传输过程中,如果出现传输错误或其他问题(如数据丢失),设备会通过控制端点通知主机,并通过重传机制来确保数据的完整性。

4.5. 视频流停止与断开

当主机不再需要视频数据时,会停止数据传输并关闭 UVC 设备:

停止视频流: 主机发送控制请求,通知设备停止传输视频流。设备会停止通过数据端点发送视频帧。
设备断开: 当设备从主机断开时,主机会向设备发送终止命令,设备关闭数据流和控制接口,进入待机状态。

5 UVC-gadget实战

结合上面的内容,还有基于USB总线的虚拟摄像头(UVC)实现原理

可以看出,一个UVC摄像头最重要的其实就两步。发送设备描述符和传输视频流。

5.1 发送设备描述符

5.2 传输视频流

参考:

USB Gadget 驱动程序框架-腾讯云开发者社区-腾讯云

K230 USB应用实战-UVC传输YUV及编码码流 — K230 文档

V4l2视频输出实现流程_v4l2_type_is_output-CSDN博客

https://cloud.tencent.com/developer/article/2315297
guvcview调试UVC摄像头-CSDN博客

V4l-utils-CSDN博客

USB协议 - UVC标准协议规范(二)_uvc协议文档-CSDN博客

USB 的UVC协议分析-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值