1.10. 扩展控件 API

1.10.1. 引言

最初设计的控件机制旨在用于用户设置(亮度、饱和度等)。然而,它被证明是一个非常有用的模型,可用于实现更复杂的驱动程序 API,其中每个驱动程序仅实现较大 API 的子集。

MPEG 编码 API 是设计和实现这种扩展控件机制的驱动力:MPEG 标准非常庞大,而当前支持的硬件 MPEG 编码器每个都只实现了该标准的一个子集。此外,许多与视频如何编码成 MPEG 流相关的参数特定于 MPEG 编码芯片,因为 MPEG 标准只定义了最终 MPEG 流的格式,而不是视频实际如何编码成该格式。

不幸的是,原始的控件 API 缺少这些新用途所需的一些功能,因此它被扩展成了(命名不怎么原创的)扩展控件 API。

尽管 MPEG 编码 API 是首次使用扩展控件 API 的尝试,但如今还有其他类型的扩展控件,例如摄像头控件和调频发射机控件。扩展控件 API 以及所有扩展控件类别将在以下文本中描述。

1.10.2. 扩展控件 API

提供了三个新的 ioctls:VIDIOC_G_EXT_CTRLSVIDIOC_S_EXT_CTRLSVIDIOC_TRY_EXT_CTRLS。这些 ioctls 作用于控件数组(与作用于单个控件的 VIDIOC_G_CTRLVIDIOC_S_CTRL ioctls 不同)。这是必要的,因为通常需要原子性地同时更改多个控件。

每个新的 ioctl 都需要一个指向结构体 v4l2_ext_controls 的指针。此结构体包含一个指向控件数组的指针、该数组中控件的数量以及一个控件类别。控件类别用于将相似的控件分组到单个类别中。例如,控件类别 V4L2_CTRL_CLASS_USER 包含所有用户控件(即,所有也可以使用旧的 VIDIOC_S_CTRL ioctl 设置的控件)。控件类别 V4L2_CTRL_CLASS_CODEC 包含与编解码器相关的控件。

控件数组中的所有控件必须属于指定的控件类别。如果情况并非如此,则会返回错误。

也可以使用空控件数组(count == 0)来检查是否支持指定的控件类别。

控件数组是结构体 v4l2_ext_control 的数组。结构体 v4l2_ext_control 与结构体 v4l2_control 非常相似,不同之处在于它还允许传递 64 位值和指针。

由于结构体 v4l2_ext_control 支持指针,现在也可以使用具有复合类型(如 N 维数组和/或结构体)的控件。在枚举控件时,您需要指定 V4L2_CTRL_FLAG_NEXT_COMPOUND 才能实际看到这些复合控件。换句话说,这些具有复合类型的控件应该只通过编程方式使用。

由于此类复合控件需要公开比 VIDIOC_QUERYCTRL 所能提供的更多信息,因此添加了 VIDIOC_QUERY_EXT_CTRL ioctl。特别是,如果此控件包含多个元素,此 ioctl 会给出 N 维数组的维度。

注意

  1. 重要的是要认识到,由于控件的灵活性,有必要检查您想要设置的控件是否在驱动程序中受支持以及有效值的范围是多少。因此,请使用 ioctls VIDIOC_QUERYCTRL、VIDIOC_QUERY_EXT_CTRL 和 VIDIOC_QUERYMENU 来进行检查。

  2. 类型为 V4L2_CTRL_TYPE_MENU 的控件中某些菜单索引可能不受支持(VIDIOC_QUERYMENU 将返回错误)。一个很好的例子是支持的 MPEG 音频比特率列表。有些驱动程序只支持一两种比特率,另一些则支持更广泛的范围。

所有控件都使用机器字节序。

1.10.3. 枚举扩展控件

枚举扩展控件的推荐方法是结合使用 ioctls VIDIOC_QUERYCTRL、VIDIOC_QUERY_EXT_CTRL 和 VIDIOC_QUERYMENU 以及 V4L2_CTRL_FLAG_NEXT_CTRL 标志。

struct v4l2_queryctrl qctrl;

qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
while (0 == ioctl (fd, VIDIOC_QUERYCTRL, &qctrl)) {
    /* ... */
    qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
}

初始控件 ID 设置为 0 或与 V4L2_CTRL_FLAG_NEXT_CTRL 标志进行按位或运算。VIDIOC_QUERYCTRL ioctl 将返回 ID 高于指定 ID 的第一个控件。如果未找到此类控件,则返回错误。

如果您想获取特定控件类别内的所有控件,那么您可以将初始的 qctrl.id 值设置为该控件类别,并添加一个额外的检查,以便在找到另一个控件类别的控件时跳出循环。

qctrl.id = V4L2_CTRL_CLASS_CODEC | V4L2_CTRL_FLAG_NEXT_CTRL;
while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &qctrl)) {
    if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_CODEC)
        break;
    /* ... */
    qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
}

32 位 qctrl.id 值细分为三个位范围:最高 4 位保留给标志(例如 V4L2_CTRL_FLAG_NEXT_CTRL),实际上不属于 ID 的一部分。其余 28 位构成控件 ID,其中最高 12 位定义控件类别,最低 16 位标识控件在控件类别内的身份。对于控件,保证这最后 16 位始终非零。0x1000 及以上的范围保留给驱动程序特定的控件。宏 V4L2_CTRL_ID2CLASS(id) 根据控件 ID 返回控件类别 ID。

如果驱动程序不支持扩展控件,则当与 V4L2_CTRL_FLAG_NEXT_CTRL 结合使用时,VIDIOC_QUERYCTRL 将失败。在这种情况下,应使用旧的控件枚举方法(参见示例:枚举所有控件)。但如果支持,则保证会枚举所有控件,包括驱动程序私有控件。

1.10.4. 创建控件面板

可以为图形用户界面创建控件面板,用户可以在其中选择各种控件。基本上,您必须使用上述方法遍历所有控件。每个控件类别都以类型为 V4L2_CTRL_TYPE_CTRL_CLASS 的控件开始。VIDIOC_QUERYCTRL 将返回此控件类别的名称,该名称可以用作控件面板中选项卡页的标题。

结构体 v4l2_queryctrl 的 flags 字段也包含有关控件行为的提示。有关更多详细信息,请参阅 ioctls VIDIOC_QUERYCTRL, VIDIOC_QUERY_EXT_CTRL and VIDIOC_QUERYMENU 文档。