4.13. 子设备接口¶
V4L2 设备的复杂性在于其硬件通常由多个集成电路组成,这些电路需要以受控方式相互交互,这导致了复杂的 V4L2 驱动。驱动通常在软件中反映硬件模型,并将不同的硬件组件建模为称为子设备的软件块。
V4L2 子设备通常是仅存在于内核中的对象。如果 V4L2 驱动实现了媒体设备 API,它们将自动从媒体实体继承。应用程序将能够使用媒体实体、pad 和链接枚举 API 来枚举子设备并发现硬件拓扑。
除了使子设备可被发现外,驱动还可以选择让应用程序直接配置它们。当子设备驱动和 V4L2 设备驱动都支持此功能时,子设备将提供一个字符设备节点,可以在其上调用 ioctl 来
查询、读取和写入子设备控件
订阅和取消订阅事件并检索它们
在单独的 pad 上协商图像格式
检查和修改同一实体内 pad 之间的内部数据路由
子设备字符设备节点,通常命名为 /dev/v4l-subdev*
,使用主设备号 81。
驱动可以选择限制子设备字符设备,使其仅公开不修改设备状态的操作。在这种情况下,在本文档的其余部分,这些子设备被称为 只读
,相关的限制在各个 ioctl 中有详细说明。
4.13.1. 控件¶
大多数 V4L2 控件由子设备硬件实现。驱动通常会合并所有控件,并通过视频设备节点公开它们。应用程序可以通过单个接口控制所有子设备。
复杂的设备有时会在不同的硬件部件中实现相同的控制。这种情况在嵌入式平台中很常见,其中传感器和图像处理硬件都实现了相同的功能,例如对比度调整、白平衡或坏点校正。由于 V4L2 控件 API 不支持单个设备中有多个相同的控制,因此除了一个相同的控制外,其余都被隐藏了。
应用程序可以通过子设备节点,使用 用户控件 中描述的 V4L2 控制 API 访问这些隐藏控件。这些 ioctl 的行为与在 V4L2 设备节点上调用时相同,不同之处在于它们仅处理子设备中实现的控件。
根据驱动的不同,这些控件也可能通过一个(或多个)V4L2 设备节点公开。
4.13.2. 事件¶
V4L2 子设备可以按照 事件接口 中描述的方式通知应用程序事件。该 API 的行为与在 V4L2 设备节点上使用时相同,不同之处在于它只处理由子设备生成的事件。根据驱动的不同,这些事件也可能在一个(或多个)V4L2 设备节点上报告。
4.13.3. Pad 级格式¶
警告
Pad 级格式仅适用于需要向用户空间公开低级格式配置的非常复杂的设备。通用 V4L2 应用程序不需要使用本节中描述的 API。
注意
就本节而言,术语“格式”指媒体总线数据格式、帧宽度和帧高度的组合。
图像格式通常在视频捕获和输出设备上使用格式和 选择 ioctl 进行协商。驱动负责根据管线输入和/或输出处请求的格式配置视频管线中的每个块。
对于复杂的设备,例如嵌入式系统中常见的设备,管线输出的相同图像尺寸可以通过不同的硬件配置实现。一个这样的示例在 管线上的图像格式协商 中显示,其中图像缩放可以在视频传感器和主机图像处理硬件上进行。
管线上的图像格式协商¶
高质量和高速管线配置
传感器缩放器通常质量低于主机缩放器,但在传感器上进行缩放是实现更高帧率所必需的。根据用例(质量 vs. 速度),管线必须进行不同的配置。应用程序需要显式地配置管线中每个点的格式。
实现 媒体 API 的驱动可以将 pad 级的图像格式配置暴露给应用程序。当它们这样做时,应用程序可以使用 VIDIOC_SUBDEV_G_FMT 和 VIDIOC_SUBDEV_S_FMT ioctl 来按 pad 协商格式。
应用程序负责配置整个管线的连贯参数,并确保连接的 pad 具有兼容的格式。在 VIDIOC_STREAMON 时,会检查管线中的格式不匹配情况,如果配置无效,则返回 EPIPE
错误代码。
可以通过在 pad 0 上调用 ioctl VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT 来测试 pad 级图像格式配置支持。如果驱动返回 EINVAL
错误代码,则子设备不支持 pad 级格式配置。
4.13.3.1. 格式协商¶
pad 上可接受的格式可能(并且通常会)取决于许多外部参数,例如其他 pad 上的格式、活动链接甚至控件。在视频管线中的所有 pad 上找到应用程序和驱动都可接受的格式组合,不能仅依靠格式枚举。需要一种格式协商机制。
格式协商机制的核心是获取/设置格式操作。当调用时 which
参数设置为 V4L2_SUBDEV_FORMAT_TRY,VIDIOC_SUBDEV_G_FMT 和 VIDIOC_SUBDEV_S_FMT ioctl 将操作一组与硬件配置无关的格式参数。修改这些“尝试”格式不会触及设备状态(这适用于驱动中存储的软件状态和设备本身存储的硬件状态)。
虽然不作为设备状态的一部分保存,但尝试格式存储在子设备文件句柄中。一次 VIDIOC_SUBDEV_G_FMT 调用将返回在同一子设备文件句柄上设置的最后一个尝试格式。因此,多个应用程序同时查询同一子设备不会相互干扰。
为了确定设备是否支持特定格式,应用程序使用 VIDIOC_SUBDEV_S_FMT ioctl。驱动会验证并根据设备要求(如果需要)更改请求的 format
,并返回可能修改后的值。应用程序可以选择尝试不同的格式或接受返回的值并继续。
驱动在协商迭代期间返回的格式保证得到设备支持。特别是,驱动保证如果将返回的格式原样传递给 VIDIOC_SUBDEV_S_FMT 调用,它将不会进一步改变(只要外部参数,如其他 pad 上的格式或链接配置未改变)。
驱动会自动在子设备内部传播格式。当在 pad 上设置尝试或活动格式时,同一子设备上其他 pad 的相应格式可以由驱动修改。驱动可以根据设备要求自由修改格式。但是,如果可能,它们应遵守以下规则:
格式应从 sink pad 传播到 source pad。修改 source pad 上的格式不应修改任何 sink pad 上的格式。
使用可变缩放因子缩放帧的子设备在修改 sink pad 格式时,应将缩放因子重置为默认值。如果支持 1:1 缩放比例,这意味着 source pad 格式应重置为 sink pad 格式。
格式不会跨链接传播,因为这会涉及将它们从一个子设备文件句柄传播到另一个。因此,应用程序必须注意显式地为每个链接的两端配置兼容的格式。链接两端的相同格式保证兼容。驱动可以自由接受符合设备要求的不同格式作为兼容格式。
管线配置示例 展示了 管线上的图像格式协商 中描述的管线的配置序列示例(表格列出实体名称和 pad 编号)。
传感器/0 格式 |
前端/0 格式 |
前端/1 格式 |
缩放器/0 格式 |
缩放器/0 组合选择矩形 |
缩放器/1 格式 |
|
---|---|---|---|---|---|---|
初始状态 |
2048x1536 SGRBG8_1X8 |
(默认) |
(默认) |
(默认) |
(默认) |
(默认) |
配置前端 sink 格式 |
2048x1536 SGRBG8_1X8 |
2048x1536 SGRBG8_1X8 |
2046x1534 SGRBG8_1X8 |
(默认) |
(默认) |
(默认) |
配置缩放器 sink 格式 |
2048x1536 SGRBG8_1X8 |
2048x1536 SGRBG8_1X8 |
2046x1534 SGRBG8_1X8 |
2046x1534 SGRBG8_1X8 |
0,0/2046x1534 |
2046x1534 SGRBG8_1X8 |
配置缩放器 sink 组合选择 |
2048x1536 SGRBG8_1X8 |
2048x1536 SGRBG8_1X8 |
2046x1534 SGRBG8_1X8 |
2046x1534 SGRBG8_1X8 |
0,0/1280x960 |
1280x960 SGRBG8_1X8 |
初始状态。传感器 source pad 格式设置为其原生的 3MP 尺寸和 V4L2_MBUS_FMT_SGRBG8_1X8 媒体总线代码。主机前端和缩放器 sink 及 source pad 上的格式具有默认值,缩放器 sink pad 上的组合矩形也如此。
应用程序将前端 sink pad 格式的大小配置为 2048x1536,并将其媒体总线代码配置为 V4L2_MBUS_FMT_SGRBG_1X8。驱动将格式传播到前端 source pad。
应用程序将缩放器 sink pad 格式的大小配置为 2046x1534,并将媒体总线代码配置为 V4L2_MBUS_FMT_SGRBG_1X8,以匹配前端源大小和媒体总线代码。sink pad 上的媒体总线代码设置为 V4L2_MBUS_FMT_SGRBG_1X8。驱动将大小传播到缩放器 sink pad 上的组合选择矩形,并将格式传播到缩放器 source pad。
应用程序将缩放器 sink pad 的组合选择矩形的大小配置为 1280x960。驱动将大小传播到缩放器的 source pad 格式。
当对尝试结果满意时,应用程序可以通过将 which
参数设置为 V4L2_SUBDEV_FORMAT_ACTIVE
来设置活动格式。活动格式由驱动程序更改的方式与尝试格式完全相同。为了避免在格式协商期间修改硬件状态,应用程序应首先协商尝试格式,然后使用上次协商迭代中返回的尝试格式来修改活动设置。这保证了活动格式将由驱动程序原样应用而不会被修改。
4.13.3.2. 选择:裁剪、缩放和组合¶
许多子设备支持在其输入或输出 pad 上(或者甚至在两者上)裁剪帧。裁剪用于选择图像中的感兴趣区域,通常在图像传感器或视频解码器上。它也可以用作数字变焦实现的一部分,以选择将被放大的图像区域。
裁剪设置由裁剪矩形定义,并在 struct v4l2_rect
中表示,其中包含左上角坐标和矩形大小。坐标和大小均以像素表示。
与 pad 格式一样,驱动为选择目标 常见选择定义 存储尝试和活动矩形。
在 sink pad 上,裁剪是相对于当前 pad 格式应用的。pad 格式表示子设备从管线中前一个块接收到的图像大小,而裁剪矩形表示将进一步在子设备内部传输以进行处理的子图像。
缩放操作通过将图像缩放到新的尺寸来改变图像的大小。缩放比例没有明确指定,而是由原始图像和缩放图像的尺寸隐含得出。这两种尺寸都由 struct v4l2_rect
表示。
缩放支持是可选的。当子设备支持时,子设备 sink pad 上的裁剪矩形将缩放为使用同一 pad 上的 V4L2_SEL_TGT_COMPOSE
选择目标,通过 VIDIOC_SUBDEV_S_SELECTION IOCTL 配置的大小。如果子设备支持缩放但不支持组合,则不使用 top 和 left 值,并且必须始终设置为零。
在 source pad 上,裁剪与 sink pad 类似,不同之处在于执行裁剪的源大小是 sink pad 上的 COMPOSE 矩形。在 sink pad 和 source pad 上,裁剪矩形必须完全包含在源图像大小之内才能进行裁剪操作。
除非另有明确说明,否则驱动应始终在所有选择目标上使用用户请求的最接近的矩形。V4L2_SEL_FLAG_GE
和 V4L2_SEL_FLAG_LE
标志可用于向上或向下舍入图像大小。选择标志
4.13.3.3. 选择目标类型¶
4.13.3.3.1. 实际目标¶
实际目标(不带后缀)反映了任何时间点的实际硬件配置。每个实际目标都有一个对应的 BOUNDS 目标。
4.13.3.3.2. BOUNDS 目标¶
BOUNDS 目标是包含所有有效实际矩形的最小矩形。然而,可能无法将实际矩形设置得与 BOUNDS 矩形一样大。这可能是因为例如传感器的像素阵列不是矩形而是十字形或圆形。最大尺寸也可能小于 BOUNDS 矩形。
4.13.3.4. 配置和格式传播顺序¶
在子设备内部,图像处理步骤的顺序总是从 sink pad 到 source pad。这也反映在用户必须执行配置的顺序中:所做的更改将传播到任何后续阶段。如果不需要这种行为,用户必须设置 V4L2_SEL_FLAG_KEEP_CONFIG
标志。此标志导致在任何情况下都不允许传播更改。这还可能导致被访问的矩形被驱动调整,具体取决于底层硬件的特性。
一个步骤的坐标总是指前一个步骤的实际大小。此规则的例外是 sink 组合矩形,它指的是 sink 组合边界矩形——如果硬件支持的话。
Sink pad 格式。用户配置 sink pad 格式。此格式定义了实体通过该 pad 接收以进行进一步处理的图像参数。
Sink pad 实际裁剪选择。sink pad 裁剪定义了对 sink pad 格式执行的裁剪。
Sink pad 实际组合选择。sink pad 组合矩形的大小定义了与 sink pad 裁剪矩形大小相比的缩放比例。组合矩形的位置指定了实际 sink 组合矩形在 sink 组合边界矩形中的位置。
Source pad 实际裁剪选择。source pad 上的裁剪定义了对 sink 组合边界矩形中图像执行的裁剪。
Source pad 格式。source pad 格式定义了子设备的输出像素格式,以及除图像宽度和高度之外的其他参数。宽度和高度由 source pad 实际裁剪选择的大小定义。
访问子设备不支持的任何上述矩形将返回 EINVAL
。任何引用先前不支持的矩形坐标的矩形将转而引用先前支持的矩形。例如,如果不支持 sink 裁剪,则组合选择将转而引用 sink pad 格式尺寸。
图 4.5. 子设备中的图像处理:简单裁剪示例¶
在上面的示例中,子设备支持在其 sink pad 上进行裁剪。要配置它,用户在子设备的 sink pad 上设置媒体总线格式。现在可以在 sink pad 上设置实际裁剪矩形——此矩形的位置和大小反映了要从 sink 格式中裁剪的矩形的位置和大小。sink 裁剪矩形的大小也将是子设备 source pad 格式的大小。
图 4.6. 子设备中的图像处理:多源缩放¶
在此示例中,子设备能够首先裁剪,然后缩放,最后为两个 source pad 从生成的缩放图像中单独裁剪。缩放图像在裁剪图像中的位置在 sink 组合目标中被忽略。两个 source 裁剪矩形的位置都指的是 sink 缩放矩形,独立地从其中裁剪出由 source 裁剪矩形指定位置的区域。
图 4.7. 子设备中的图像处理:多 sink 和 source 的缩放和组合¶
子设备驱动支持两个 sink pad 和两个 source pad。来自两个 sink pad 的图像被单独裁剪,然后缩放并在组合边界矩形上进一步组合。由此,两个独立的流被裁剪并从 source pad 从子设备中发送出去。
4.13.3.5. 流、复用媒体 pad 和内部路由¶
简单的 V4L2 子设备不支持多个不相关的视频流,并且只有一个流可以穿过媒体链接和媒体 pad。因此,每个 pad 包含该单个流的格式和选择配置。子设备可以进行流处理,将一个流分成两个或将两个流组合成一个,但子设备的输入和输出仍然是每个 pad 一个流。
一些硬件,例如 MIPI CSI-2,支持复用流,即多个数据流在同一总线上进行传输,这由连接发射器 source pad 和接收器 sink pad 的媒体链接表示。例如,摄像头传感器可以产生两个不同的流,一个像素流和一个元数据流,它们在复用数据总线上进行传输,由连接单个传感器 source pad 和接收器 sink pad 的媒体链接表示。支持流的接收器将对在其 sink pad 上接收到的流进行解复用,并允许将它们单独路由到其一个 source pad。
支持复用流的子设备驱动与非复用子设备驱动兼容。然而,如果链接 sink 端的驱动不支持流,则只能捕获 source 端的流 0。可能还有特定于 sink 设备的额外限制。
4.13.3.5.1. 理解流¶
流是内容(例如像素数据或元数据)流,它通过媒体管线从源(例如传感器)流向最终的 sink(例如 SoC 中的接收器和解复用器)。每个媒体链接将所有启用的流从链接的一端传输到另一端,子设备具有路由表,描述了如何将来自 sink pad 的输入流路由到 source pad。
流 ID 是流的媒体 pad 局部标识符。同一流的流 ID 在链接的两端必须相等。换句话说,一个特定的流 ID 必须存在于媒体链接的两侧,但子设备的另一侧可以为同一流使用另一个流 ID。
媒体管线中特定点的一个流由子设备和(pad,流)对标识。对于不支持复用流的子设备,“流”字段始终为 0。
4.13.3.5.2. 路由、流、格式和选择之间的交互¶
在 V4L2 子设备接口中增加流,将子设备的格式和选择从 pad 移动到(pad,流)对。除了通常的 pad 外,设置格式和选择还需要提供流 ID。沿流配置格式和选择的顺序与没有流时相同(参见 配置和格式传播顺序)。
与子设备范围内的所有 sink pad 的流向所有 source pad 合并不同,每条路由的数据流彼此独立。从 sink pad 上的流到 source pad 上的流,允许任意数量的路由,只要驱动支持。然而,对于 source pad 上的每个流,只允许一条路由。
pad 内流的任何配置,例如格式或选择,都独立于其他流上的类似配置。这在将来可能会改变。
4.13.3.5.3. 设备类型和路由设置¶
不同类型的子设备在路由激活方面的行为不同,这取决于硬件。然而,在所有情况下,只有设置了 V4L2_SUBDEV_STREAM_FL_ACTIVE
标志的路由才是活动的。
生成流的设备可能允许启用和禁用某些路由,或者具有固定的路由配置。如果路由可以被禁用,则在 VIDIOC_SUBDEV_S_ROUTING
中不声明路由(或声明它们但未设置 V4L2_SUBDEV_STREAM_FL_ACTIVE
标志)将禁用这些路由。VIDIOC_SUBDEV_S_ROUTING
仍将在路由数组中将这些路由返回给用户,但 V4L2_SUBDEV_STREAM_FL_ACTIVE
标志未设置。
传输流的设备在路由方面几乎总是具有更高的可配置性。通常,子设备的 sink 和 source pad 之间的任何路由都是可能的,并且可以同时激活多条路由(通常达到某个有限数量)。对于此类设备,驱动不创建路由,并且当在子设备上调用 VIDIOC_SUBDEV_S_ROUTING
时,用户创建的路由会被完全替换。这些新创建的路由具有设备默认的格式和选择矩形配置。
4.13.3.5.4. 配置流¶
流的配置是针对每个子设备单独进行的,子设备之间的流的有效性在管线启动时进行验证。
配置流有三个步骤:
设置链接。使用 媒体控制器 API 连接子设备之间的 pad。
流。通过使用 VIDIOC_SUBDEV_S_ROUTING ioctl 为子设备设置路由表来声明流并配置其路由。请注意,设置路由表会将子设备中的格式和选择重置为默认值。
配置格式和选择。每个流的格式和选择单独配置,如 配置和格式传播顺序 中对普通子设备的说明。流 ID 设置为与使用 VIDIOC_SUBDEV_S_ROUTING ioctl 配置的路由的 sink 或 source pad 相关联的相同流 ID。
4.13.3.5.5. 复用流设置示例¶
一个复用流设置的简单示例如下:
两个相同的传感器(传感器 A 和传感器 B)。每个传感器都有一个单一的 source pad (pad 0),它承载一个像素数据流。
复用器桥(Bridge)。该桥有两个连接到传感器的 sink pad (pad 0, 1),和一个输出两个流的 source pad (pad 2)。
SoC 中的接收器(Receiver)。该接收器有一个连接到桥的单一 sink pad (pad 0),以及两个连接到 DMA 引擎的 source pad (pad 1-2)。接收器将传入流解复用至 source pad。
SoC 中的 DMA 引擎(DMA Engine),每个流一个。每个 DMA 引擎都连接到接收器中的一个单一 source pad。
传感器、桥和接收器被建模为 V4L2 子设备,通过 /dev/v4l-subdevX
设备节点暴露给用户空间。DMA 引擎被建模为 V4L2 设备,通过 /dev/videoX
节点暴露给用户空间。
要配置此管线,用户空间必须执行以下步骤:
设置实体之间的媒体链接:将传感器连接到桥,将桥连接到接收器,并将接收器连接到 DMA 引擎。此步骤与正常的非复用媒体控制器设置没有区别。
配置路由
Sink Pad/流 |
Source Pad/流 |
路由标志 |
注释 |
---|---|---|---|
0/0 |
2/0 |
V4L2_SUBDEV_ROUTE_FL_ACTIVE |
来自传感器 A 的像素数据流 |
1/0 |
2/1 |
V4L2_SUBDEV_ROUTE_FL_ACTIVE |
来自传感器 B 的像素数据流 |
Sink Pad/流 |
Source Pad/流 |
路由标志 |
注释 |
---|---|---|---|
0/0 |
1/0 |
V4L2_SUBDEV_ROUTE_FL_ACTIVE |
来自传感器 A 的像素数据流 |
0/1 |
2/0 |
V4L2_SUBDEV_ROUTE_FL_ACTIVE |
来自传感器 B 的像素数据流 |
配置格式和选择
配置路由后,下一步是配置流的格式和选择。这类似于在没有流的情况下执行此步骤,只有一个例外:
stream
字段需要赋值为流 ID 的值。实现此目的的一种常见方法是从传感器开始,并沿着流将配置传播到接收器,使用 VIDIOC_SUBDEV_S_FMT ioctl 配置每个子设备中的每个流端点。