4.13. 子设备接口

V4L2 设备的复杂性在于,硬件通常由多个需要以受控方式相互交互的集成电路组成,这导致了复杂的 V4L2 驱动程序。这些驱动程序通常在软件中反映硬件模型,并将不同的硬件组件建模为称为子设备的软件块。

V4L2 子设备通常是仅限内核的对象。如果 V4L2 驱动程序实现了媒体设备 API,它们将自动继承自媒体实体。应用程序将能够枚举子设备,并使用媒体实体、衬垫和链接枚举 API 来发现硬件拓扑。

除了使子设备可被发现之外,驱动程序还可以选择使应用程序可以直接配置它们。当子设备驱动程序和 V4L2 设备驱动程序都支持此功能时,子设备将具有一个字符设备节点,在该节点上可以调用 ioctl 来

  • 查询、读取和写入子设备控件

  • 订阅和取消订阅事件并检索它们

  • 在单个衬垫上协商图像格式

  • 检查和修改同一实体的衬垫之间的内部数据路由

子设备字符设备节点,通常命名为 /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. 衬垫级格式

警告

衬垫级格式仅适用于需要向用户空间公开低级格式配置的非常复杂的设备。通用的 V4L2 应用程序 不需要 使用本节中描述的 API。

注意

为了本节的目的,术语 格式 指的是媒体总线数据格式、帧宽度和帧高度的组合。

图像格式通常在使用格式和 选择 ioctl 的视频捕获和输出设备上进行协商。驱动程序负责根据管道输入和/或输出处请求的格式配置视频管道中的每个块。

对于复杂的设备,例如嵌入式系统中常见的设备,可以使用不同的硬件配置来实现管道输出端相同的图像大小。一个这样的例子在 管道上的图像格式协商 中显示,其中图像缩放可以在视频传感器和主机图像处理硬件上执行。

pipeline.dot

管道上的图像格式协商

高质量和高速管道配置

传感器缩放器通常质量不如主机缩放器,但需要传感器上的缩放才能实现更高的帧速率。根据用例(质量与速度),必须以不同的方式配置管道。应用程序需要显式地配置管道中每个点的格式。

实现 媒体 API 的驱动程序可以向应用程序公开衬垫级图像格式配置。当它们这样做时,应用程序可以使用 VIDIOC_SUBDEV_G_FMTVIDIOC_SUBDEV_S_FMT ioctl 来协商每个衬垫的格式。

应用程序负责配置整个管道上的连贯参数,并确保连接的衬垫具有兼容的格式。将在 VIDIOC_STREAMON 时检查管道是否存在格式不匹配的情况,如果配置无效,则返回 EPIPE 错误代码。

可以通过在衬垫 0 上调用 ioctl VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT ioctl 来测试衬垫级图像格式配置的支持。如果驱动程序返回 EINVAL 错误代码,则子设备不支持衬垫级格式配置。

4.13.3.1. 格式协商

衬垫上可接受的格式可以(并且通常)依赖于许多外部参数,例如其他衬垫上的格式、活动链接,甚至是控件。找到一个应用程序和驱动程序都可接受的视频管道中所有衬垫上的格式组合,不能仅依赖于格式枚举。需要一种格式协商机制。

格式协商机制的核心是获取/设置格式操作。当调用时,which 参数设置为 V4L2_SUBDEV_FORMAT_TRY 时,VIDIOC_SUBDEV_G_FMTVIDIOC_SUBDEV_S_FMT ioctl 在一组未连接到硬件配置的格式参数上运行。修改这些“尝试”格式不会影响设备状态(这适用于存储在驱动程序中的软件状态和存储在设备本身中的硬件状态)。

虽然不作为设备状态的一部分保留,但尝试格式存储在子设备文件句柄中。 VIDIOC_SUBDEV_G_FMT 调用将返回 在同一子设备文件句柄上 设置的最后一个尝试格式。因此,同时查询同一子设备的多个应用程序将不会相互影响。

为了确定设备是否支持特定格式,应用程序使用 VIDIOC_SUBDEV_S_FMT ioctl。驱动程序会验证,如果需要,则根据设备要求更改请求的 format,并返回可能修改的值。然后,应用程序可以选择尝试不同的格式或接受返回的值并继续。

在协商迭代期间驱动程序返回的格式保证受设备支持。特别是,驱动程序保证如果将返回的格式原封不动地传递给 VIDIOC_SUBDEV_S_FMT 调用(只要外部参数,例如其他衬垫上的格式或链接配置没有更改),则不会进一步更改返回的格式。

驱动程序会自动在子设备内部传播格式。当在衬垫上设置尝试或活动格式时,驱动程序可以修改同一子设备的其他衬垫上的相应格式。驱动程序可以根据设备的要求自由修改格式。但是,它们应在可能的情况下遵守以下规则

  • 格式应从接收衬垫传播到源衬垫。修改源衬垫上的格式不应修改任何接收衬垫上的格式。

  • 使用可变缩放因子缩放帧的子设备应在修改接收衬垫格式时将缩放因子重置为默认值。如果支持 1:1 的缩放比率,则表示应将源衬垫格式重置为接收衬垫格式。

格式不会跨链接传播,因为这将涉及将格式从一个子设备文件句柄传播到另一个子设备文件句柄。因此,应用程序必须注意使用兼容的格式显式配置每个链接的两端。保证链接两端的相同格式是兼容的。驱动程序可以自由接受符合设备要求的不同格式为兼容格式。

示例管道配置 显示了 管道上的图像格式协商 中描述的管道的示例配置序列(表格列出实体名称和衬垫编号)。

示例管道配置

传感器/0

格式

前端/0

格式

前端/1

格式

缩放器/0

格式

缩放器/0

合成选择矩形

缩放器/1

格式

初始状态

2048x1536

SGRBG8_1X8

(默认)

(默认)

(默认)

(默认)

(默认)

配置前端接收格式

2048x1536

SGRBG8_1X8

2048x1536

SGRBG8_1X8

2046x1534

SGRBG8_1X8

(默认)

(默认)

(默认)

配置缩放器接收格式

2048x1536

SGRBG8_1X8

2048x1536

SGRBG8_1X8

2046x1534

SGRBG8_1X8

2046x1534

SGRBG8_1X8

0,0/2046x1534

2046x1534

SGRBG8_1X8

配置缩放器接收合成选择

2048x1536

SGRBG8_1X8

2048x1536

SGRBG8_1X8

2046x1534

SGRBG8_1X8

2046x1534

SGRBG8_1X8

0,0/1280x960

1280x960

SGRBG8_1X8

  1. 初始状态。传感器源衬垫格式设置为其本地 3MP 大小和 V4L2_MBUS_FMT_SGRBG8_1X8 媒体总线代码。主机前端以及缩放器接收和源衬垫上的格式都具有默认值,缩放器接收衬垫上的合成矩形也是如此。

  2. 应用程序将前端接收衬垫格式的大小配置为 2048x1536,并将其媒体总线代码配置为 V4L2_MBUS_FMT_SGRBG_1X8。驱动程序将格式传播到前端源衬垫。

  3. 应用程序将缩放器接收端衬垫的格式大小配置为 2046x1534,并将媒体总线代码配置为 V4L2_MBUS_FMT_SGRBG_1X8,以匹配前端源大小和媒体总线代码。接收端衬垫上的媒体总线代码设置为 V4L2_MBUS_FMT_SGRBG_1X8。驱动程序将大小传播到缩放器接收端衬垫上的合成选择矩形,并将格式传播到缩放器源端衬垫。

  4. 应用程序将缩放器接收端衬垫的合成选择矩形大小配置为 1280x960。驱动程序将大小传播到缩放器的源端衬垫格式。

当对尝试结果满意时,应用程序可以通过将 which 参数设置为 V4L2_SUBDEV_FORMAT_ACTIVE 来设置活动格式。驱动程序更改活动格式的方式与尝试格式完全相同。为了避免在格式协商期间修改硬件状态,应用程序应首先协商尝试格式,然后使用上次协商迭代期间返回的尝试格式修改活动设置。这保证了驱动程序将按原样应用活动格式,而不会进行修改。

4.13.3.2. 选择:裁剪、缩放和合成

许多子设备支持在其输入或输出衬垫上裁剪帧(或者甚至可能在两者上裁剪)。裁剪用于选择图像中感兴趣的区域,通常在图像传感器或视频解码器上。它也可以用作数字变焦实现的一部分,以选择要放大的图像区域。

裁剪设置由一个裁剪矩形定义,并在 struct v4l2_rect 中用左上角的坐标和矩形大小表示。坐标和大小都以像素为单位表示。

对于衬垫格式,驱动程序为选择目标存储尝试和活动矩形 通用选择定义

在接收端衬垫上,裁剪相对于当前衬垫格式应用。衬垫格式表示子设备从管道中的上一个块接收到的图像大小,而裁剪矩形表示将在子设备内部进一步传输以进行处理的子图像。

缩放操作通过将图像缩放到新的尺寸来更改图像的大小。缩放比例不是显式指定的,而是从原始和缩放的图像大小隐含的。两个大小都由 struct v4l2_rect 表示。

缩放支持是可选的。当子设备支持缩放时,子设备接收端衬垫上的裁剪矩形将缩放到使用 VIDIOC_SUBDEV_S_SELECTION IOCTL 在同一衬垫上使用 V4L2_SEL_TGT_COMPOSE 选择目标配置的大小。如果子设备支持缩放但不合成,则不使用顶部和左侧值,并且必须始终设置为零。

在源端衬垫上,裁剪与接收端衬垫类似,但不同之处在于执行裁剪的源大小是接收端衬垫上的 COMPOSE 矩形。在接收端和源端衬垫中,裁剪矩形必须完全包含在源图像大小内才能进行裁剪操作。

除非另有明确说明,否则驱动程序应始终使用用户在所有选择目标上请求的最接近的矩形。V4L2_SEL_FLAG_GEV4L2_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. 配置顺序和格式传播

在子设备内部,图像处理步骤的顺序始终是从接收端衬垫到源端衬垫。这也反映在用户必须执行配置的顺序中:所做的更改将传播到任何后续阶段。如果不需要此行为,则用户必须设置 V4L2_SEL_FLAG_KEEP_CONFIG 标志。此标志会导致在任何情况下都不允许传播更改。这也可能导致驱动程序调整访问的矩形,具体取决于底层硬件的属性。

步骤的坐标始终参考上一步的实际大小。此规则的例外是接收端合成矩形,它指的是接收端合成边界矩形 --- 如果硬件支持它。

  1. 接收端衬垫格式。用户配置接收端衬垫格式。此格式定义实体通过衬垫接收以进行进一步处理的图像的参数。

  2. 接收端衬垫实际裁剪选择。接收端衬垫裁剪定义对接收端衬垫格式执行的裁剪。

  3. 接收端衬垫实际合成选择。接收端衬垫合成矩形的大小定义了与接收端衬垫裁剪矩形大小相比的缩放比例。合成矩形的位置指定了接收端合成边界矩形中实际接收端合成矩形的位置。

  4. 源端衬垫实际裁剪选择。源端衬垫上的裁剪定义对接收端合成边界矩形中的图像执行的裁剪。

  5. 源端衬垫格式。源端衬垫格式定义子设备的输出像素格式,以及除图像宽度和高度之外的其他参数。宽度和高度由源端衬垫实际裁剪选择的大小定义。

访问子设备不支持的上述任何矩形将返回 EINVAL。任何引用先前不支持的矩形坐标的矩形都将改为引用先前支持的矩形。例如,如果不支持接收端裁剪,则合成选择将改为引用接收端衬垫格式尺寸。

subdev-image-processing-crop.svg

图 4.5. 子设备中的图像处理:简单的裁剪示例

在上面的示例中,子设备支持在其接收端衬垫上进行裁剪。为了配置它,用户在子设备的接收端衬垫上设置媒体总线格式。现在可以在接收端衬垫上设置实际裁剪矩形 --- 此矩形的位置和大小反映了要从接收端格式裁剪的矩形的位置和大小。接收端裁剪矩形的大小也将是子设备源端衬垫的格式大小。

subdev-image-processing-scaling-multi-source.svg

图 4.6. 子设备中的图像处理:具有多个源的缩放

在此示例中,子设备能够首先裁剪,然后缩放,最后从生成的缩放图像中为两个源端衬垫单独裁剪。在接收端合成目标中,将忽略缩放图像在裁剪图像中的位置。两个源裁剪矩形的位置都引用接收端缩放矩形,独立地从接收端缩放矩形中裁剪由源裁剪矩形指定的位置的区域。

subdev-image-processing-full.svg

图 4.7. 子设备中的图像处理:具有多个接收端和源的缩放和合成

子设备驱动程序支持两个接收端衬垫和两个源端衬垫。来自两个接收端衬垫的图像分别被裁剪,然后缩放,并在合成边界矩形上进一步合成。从中,裁剪两个独立的流,并从源端衬垫从子设备发送出去。

4.13.3.5. 流、多路复用媒体衬垫和内部路由

简单的 V4L2 子设备不支持多个不相关的视频流,并且只有一个流可以穿过媒体链接和媒体衬垫。因此,每个衬垫都包含该单个流的格式和选择配置。子设备可以进行流处理并将一个流分成两个流,或将两个流组合成一个流,但子设备的输入和输出仍然是每个衬垫一个流。

某些硬件(例如 MIPI CSI-2)支持多路复用流,也就是说,多个数据流在同一总线上进行传输,该总线由媒体链接表示,该链接将发送器的源端衬垫与接收器上的接收端衬垫连接起来。例如,摄像头传感器可以产生两个不同的流,即像素流和元数据流,它们在多路复用数据总线上进行传输,该总线由媒体链接表示,该链接将单个传感器的源端衬垫与接收器的接收端衬垫连接起来。支持流的接收器将对在其接收端衬垫上接收到的流进行解复用,并允许将它们单独路由到其源端衬垫之一。

支持多路复用流的子设备驱动程序与非多路复用子设备驱动程序兼容。但是,如果链接接收端的驱动程序不支持流,则只能捕获发送端流 0。可能还有特定于接收设备的附加限制。

4.13.3.5.1. 了解流

流是从源(例如传感器)到最终接收器(例如 SoC 中的接收器和解复用器)流经媒体管道的内容流(例如像素数据或元数据)。每个媒体链接都承载着从链接一端到另一端的所有已启用流,并且子设备具有路由表,这些路由表描述了如何将来自接收端衬垫的传入流路由到源端衬垫。

流 ID 是流的媒体衬垫本地标识符。同一流的流 ID 在链接的两端必须相等。换句话说,特定的流 ID 必须存在于媒体链接的两侧,但是另一个流 ID 可以用于子设备另一侧的同一流。

媒体管道中特定点的流由子设备和一个(衬垫、流)对标识。对于不支持多路复用流的子设备,“流”字段始终为 0。

4.13.3.5.2. 路由、流、格式和选择之间的交互

将流添加到 V4L2 子设备接口后,子设备的格式和选择从管脚转移到(管脚,流)对。除了通常的管脚之外,还需要提供流 ID 来设置格式和选择。沿着流配置格式和选择的顺序与没有流时相同(请参阅配置顺序和格式传播)。

数据流不再像以前那样在子设备范围内将所有接收管脚的流合并到所有发送管脚,而是每个路由的数据流相互独立。允许从接收管脚上的流到发送管脚上的流的任意数量的路由,只要驱动程序支持。但是,对于发送管脚上的每个流,只允许一条路由。

管脚内任何流的配置,例如格式或选择,都独立于其他流上的类似配置。未来可能会发生变化。

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 标志未设置。

传输流的设备在路由方面几乎总是具有更高的可配置性。通常,子设备的接收和发送管脚之间的任何路由都是可能的,并且可以同时激活多个路由(通常最多为有限数量)。对于此类设备,驱动程序不会创建任何路由,并且当在子设备上调用 VIDIOC_SUBDEV_S_ROUTING 时,用户创建的路由将被完全替换。此类新创建的路由具有设备格式和选择矩形的默认配置。

4.13.3.5.4. 配置流

每个子设备的流配置是单独完成的,并且当启动管道时,子设备之间的流的有效性会进行验证。

配置流有三个步骤

  1. 设置链接。使用 媒体控制器 API 连接子设备之间的管脚。

  2. 流。通过使用 VIDIOC_SUBDEV_S_ROUTING ioctl 设置子设备的路由表,声明流并配置它们的路由。请注意,设置路由表会将子设备中的格式和选择重置为默认值。

  3. 配置格式和选择。每个流的格式和选择都是单独配置的,就像在配置顺序和格式传播中记录的普通子设备一样。流 ID 设置为与使用 VIDIOC_SUBDEV_S_ROUTING ioctl 配置的路由的接收或发送管脚关联的相同流 ID。

4.13.3.5.5. 多路复用流设置示例

一个简单的多路复用流设置示例如下:

  • 两个相同的传感器(传感器 A 和传感器 B)。每个传感器都有一个单独的发送管脚(管脚 0),该管脚携带像素数据流。

  • 多路复用器桥(桥)。该桥有两个接收管脚,连接到传感器(管脚 0、1),以及一个发送管脚(管脚 2),输出两个流。

  • SoC 中的接收器(接收器)。接收器有一个单独的接收管脚(管脚 0),连接到桥,以及两个发送管脚(管脚 1-2),通往 DMA 引擎。接收器将传入的流解复用到发送管脚。

  • SoC 中的 DMA 引擎(DMA 引擎),每个流一个。每个 DMA 引擎都连接到接收器中的一个发送管脚。

传感器、桥和接收器被建模为 V4L2 子设备,通过 /dev/v4l-subdevX 设备节点向用户空间公开。DMA 引擎被建模为 V4L2 设备,通过 /dev/videoX 节点向用户空间公开。

要配置此管道,用户空间必须执行以下步骤:

  1. 设置实体之间的媒体链接:将传感器连接到桥,桥连接到接收器,以及接收器连接到 DMA 引擎。此步骤与普通的非多路复用媒体控制器设置没有区别。

  2. 配置路由

桥路由表

接收管脚/流

发送管脚/流

路由标志

备注

0/0

2/0

V4L2_SUBDEV_ROUTE_FL_ACTIVE

来自传感器 A 的像素数据流

1/0

2/1

V4L2_SUBDEV_ROUTE_FL_ACTIVE

来自传感器 B 的像素数据流

接收器路由表

接收管脚/流

发送管脚/流

路由标志

备注

0/0

1/0

V4L2_SUBDEV_ROUTE_FL_ACTIVE

来自传感器 A 的像素数据流

0/1

2/0

V4L2_SUBDEV_ROUTE_FL_ACTIVE

来自传感器 B 的像素数据流

  1. 配置格式和选择

    配置路由后,下一步是配置流的格式和选择。这与在没有流的情况下执行此步骤类似,只有一个例外:stream 字段需要分配给流 ID 的值。

    完成此操作的一种常见方法是从传感器开始,并使用 VIDIOC_SUBDEV_S_FMT ioctl 沿流向接收器传播配置,以配置每个子设备中的每个流端点。