1.9. 用户控制

设备通常具有许多用户可设置的控件,例如亮度、饱和度等等,这些控件会在图形用户界面上呈现给用户。但是,不同的设备可用的控件会有所不同,此外,可能值的范围和默认值也会因设备而异。控制 ioctl 提供了信息和一个机制,用于为这些控件创建一个美观的用户界面,该界面可以与任何设备正确工作。

所有控件都使用 ID 值进行访问。V4L2 为特定目的定义了几个 ID。驱动程序还可以使用 V4L2_CID_PRIVATE_BASE [1] 和更高的值来实现他们自己的自定义控件。预定义的控件 ID 具有前缀 V4L2_CID_,并在 控件 ID 中列出。ID 用于查询控件的属性,以及获取或设置当前值。

一般来说,应用程序应该在不对其目的进行假设的情况下向用户呈现控件。每个控件都带有一个用户应该理解的名称字符串。当目的不直观时,驱动程序编写者应提供用户手册、用户界面插件或驱动程序特定的面板应用程序。引入预定义的 ID 是为了以编程方式更改一些控件,例如在通道切换期间使设备静音。

驱动程序可能会在切换当前视频输入或输出、调谐器或调制器、或音频输入或输出后枚举不同的控件。不同之处在于其他边界、另一个默认值和当前值、步长或其他菜单项。具有特定自定义 ID 的控件也可以更改名称和类型。

如果控件不适用于设备的当前配置(例如,它不适用于当前视频输入),则驱动程序会设置 V4L2_CTRL_FLAG_INACTIVE 标志。

控制值是全局存储的,它们在切换时不会更改,除非保持在报告的边界内。例如,它们也不会在设备打开或关闭时、调谐器无线电频率更改时或通常在没有应用程序请求的情况下更改。

V4L2 指定了一种事件机制,用于在控件更改值时通知应用程序(请参阅 ioctl VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT,事件 V4L2_EVENT_CTRL),面板应用程序可能希望利用该机制以便始终反映正确的控件值。

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

1.9.1. 控件 ID

V4L2_CID_BASE

第一个预定义的 ID,等于 V4L2_CID_BRIGHTNESS

V4L2_CID_USER_BASE

V4L2_CID_BASE 的同义词。

V4L2_CID_BRIGHTNESS (integer)

图片亮度,或更准确地说,黑电平。

V4L2_CID_CONTRAST (integer)

图片对比度或亮度增益。

V4L2_CID_SATURATION (integer)

图片色彩饱和度或色度增益。

V4L2_CID_HUE (integer)

色调或色彩平衡。

V4L2_CID_AUDIO_VOLUME (integer)

整体音频音量。请注意,某些驱动程序还提供 OSS 或 ALSA 混音器接口。

V4L2_CID_AUDIO_BALANCE (integer)

音频立体声平衡。最小值对应于完全向左,最大值对应于向右。

V4L2_CID_AUDIO_BASS (integer)

音频低音调整。

V4L2_CID_AUDIO_TREBLE (integer)

音频高音调整。

V4L2_CID_AUDIO_MUTE (boolean)

静音音频,即将音量设置为零,但不会影响 V4L2_CID_AUDIO_VOLUME。与 ALSA 驱动程序一样,V4L2 驱动程序必须在加载时静音以避免过度的噪音。实际上,整个设备应重置为低功耗状态。

V4L2_CID_AUDIO_LOUDNESS (boolean)

响度模式(低音增强)。

V4L2_CID_BLACK_LEVEL (integer)

亮度的另一个名称(不是 V4L2_CID_BRIGHTNESS 的同义词)。此控件已弃用,不应在新驱动程序和应用程序中使用。

V4L2_CID_AUTO_WHITE_BALANCE (boolean)

自动白平衡(相机)。

V4L2_CID_DO_WHITE_BALANCE (button)

这是一个操作控制。当设置时(该值将被忽略),设备将执行白平衡,然后保持当前设置。将其与布尔值 V4L2_CID_AUTO_WHITE_BALANCE 进行对比,后者在激活后会持续调整白平衡。

V4L2_CID_RED_BALANCE (integer)

红色色度平衡。

V4L2_CID_BLUE_BALANCE (integer)

蓝色色度平衡。

V4L2_CID_GAMMA (integer)

伽马调整。

V4L2_CID_WHITENESS (integer)

灰度设备的白度。这是 V4L2_CID_GAMMA 的同义词。此控件已弃用,不应在新驱动程序和应用程序中使用。

V4L2_CID_EXPOSURE (integer)

曝光(相机)。[单位?]

V4L2_CID_AUTOGAIN (boolean)

自动增益/曝光控制。

V4L2_CID_GAIN (integer)

增益控制。

主要用于控制电视调谐器和网络摄像头上的增益。大多数设备仅使用此控件控制数字增益,但在某些设备上,这也可能包括模拟增益。识别数字和模拟增益之间差异的设备使用控件 V4L2_CID_DIGITAL_GAINV4L2_CID_ANALOGUE_GAIN

V4L2_CID_HFLIP (boolean)

水平镜像图片。

V4L2_CID_VFLIP (boolean)

垂直镜像图片。

V4L2_CID_POWER_LINE_FREQUENCY (enum)

启用电源线频率滤波器以避免闪烁。enum v4l2_power_line_frequency 的可能值为

V4L2_CID_POWER_LINE_FREQUENCY_DISABLED

0

V4L2_CID_POWER_LINE_FREQUENCY_50HZ

1

V4L2_CID_POWER_LINE_FREQUENCY_60HZ

2

V4L2_CID_POWER_LINE_FREQUENCY_AUTO

3

V4L2_CID_HUE_AUTO (boolean)

启用设备自动色调控制。在启用自动色调控制的情况下设置 V4L2_CID_HUE 的效果是未定义的,驱动程序应忽略此类请求。

V4L2_CID_WHITE_BALANCE_TEMPERATURE (integer)

此控制项将白平衡设置指定为开尔文色温。驱动程序应具有至少 2800(白炽灯)到 6500(日光)的范围。有关色温的更多信息,请参阅 维基百科

V4L2_CID_SHARPNESS (整数)

调整相机中的锐度滤镜。最小值为禁用滤镜,更高的值会使图像更清晰。

V4L2_CID_BACKLIGHT_COMPENSATION (整数)

调整相机中的背光补偿。最小值为禁用背光补偿。

V4L2_CID_CHROMA_AGC (布尔值)

色度自动增益控制。

V4L2_CID_CHROMA_GAIN (整数)

调整色度增益控制(在禁用色度 AGC 时使用)。

V4L2_CID_COLOR_KILLER (布尔值)

启用色彩抑制器(即在视频信号较弱时强制显示黑白图像)。

V4L2_CID_COLORFX (枚举)

选择颜色效果。定义了以下值

V4L2_COLORFX_NONE

禁用颜色效果。

V4L2_COLORFX_ANTIQUE

老化(旧照片)效果。

V4L2_COLORFX_ART_FREEZE

冰冻颜色效果。

V4L2_COLORFX_AQUA

水彩,冷色调。

V4L2_COLORFX_BW

黑白。

V4L2_COLORFX_EMBOSS

浮雕,高光和阴影替换明暗边界,低对比度区域设置为灰色背景。

V4L2_COLORFX_GRASS_GREEN

草绿色。

V4L2_COLORFX_NEGATIVE

负片。

V4L2_COLORFX_SEPIA

棕褐色调。

V4L2_COLORFX_SKETCH

素描。

V4L2_COLORFX_SKIN_WHITEN

美白皮肤。

V4L2_COLORFX_SKY_BLUE

天蓝色。

V4L2_COLORFX_SOLARIZATION

太阳化,图像的色调部分反转,只有高于或低于某个阈值的颜色值会被反转。

V4L2_COLORFX_SILHOUETTE

轮廓(外形)。

V4L2_COLORFX_VIVID

鲜艳的色彩。

V4L2_COLORFX_SET_CBCR

Cb 和 Cr 色度分量被由 V4L2_CID_COLORFX_CBCR 控制项确定的固定系数替换。

V4L2_COLORFX_SET_RGB

RGB 分量被由 V4L2_CID_COLORFX_RGB 控制项确定的固定 RGB 分量替换。

V4L2_CID_COLORFX_RGB (整数)

确定 V4L2_COLORFX_SET_RGB 颜色效果的红色、绿色和蓝色系数。提供的 32 位值的 [7:0] 位解释为蓝色分量,[15:8] 位为绿色分量,[23:16] 位为红色分量,[31:24] 位必须为零。

V4L2_CID_COLORFX_CBCR (整数)

确定 V4L2_COLORFX_SET_CBCR 颜色效果的 Cb 和 Cr 系数。提供的 32 位值的 [7:0] 位解释为 Cr 分量,[15:8] 位为 Cb 分量,[31:16] 位必须为零。

V4L2_CID_AUTOBRIGHTNESS (布尔值)

启用自动亮度。

V4L2_CID_ROTATE (整数)

将图像旋转指定的角度。常见的角度为 90、270 和 180。将图像旋转 90 度和 270 度将反转显示窗口的高度和宽度。根据选择的旋转角度,有必要使用 VIDIOC_S_FMT ioctl 设置图片的新高度和宽度。

V4L2_CID_BG_COLOR (整数)

在当前输出设备上设置背景颜色。背景颜色需要以 RGB24 格式指定。提供的 32 位值解释为 0-7 位为红色颜色信息,8-15 位为绿色颜色信息,16-23 位为蓝色颜色信息,24-31 位必须为零。

V4L2_CID_ILLUMINATORS_1 V4L2_CID_ILLUMINATORS_2 (布尔值)

打开或关闭设备的照明器 1 或 2(通常是显微镜)。

V4L2_CID_MIN_BUFFERS_FOR_CAPTURE (整数)

这是一个只读控制项,应用程序可以读取它,并将其用作确定传递给 REQBUFS 的 CAPTURE 缓冲区数量的提示。该值是硬件工作所需的最小 CAPTURE 缓冲区数量。此控制项对于有状态解码器是必需的。

V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (整数)

这是一个只读控制项,应用程序可以读取它,并将其用作确定传递给 REQBUFS 的 OUTPUT 缓冲区数量的提示。该值是硬件工作所需的最小 OUTPUT 缓冲区数量。此控制项对于有状态编码器是必需的。

V4L2_CID_ALPHA_COMPONENT (整数)

设置 alpha 颜色分量。当捕获设备(或内存到内存设备的捕获队列)生成包含 alpha 分量的帧格式(例如,打包的 RGB 图像格式),并且 alpha 值不是由设备或内存到内存的输入数据定义的,则此控制项允许您选择所有像素的 alpha 分量值。当输出设备(或内存到内存设备的输出队列)消耗不包含 alpha 分量的帧格式,并且该设备支持 alpha 通道处理时,此控制项允许您设置所有像素的 alpha 分量值,以便在设备中进行进一步处理。

V4L2_CID_LASTP1

预定义控制 ID 的结尾(当前为 V4L2_CID_ALPHA_COMPONENT + 1)。

V4L2_CID_PRIVATE_BASE

第一个自定义(驱动程序特定)控制项的 ID。依赖特定自定义控制项的应用程序应检查驱动程序名称和版本,请参阅 查询功能

应用程序可以使用 ioctls VIDIOC_QUERYCTRL、VIDIOC_QUERY_EXT_CTRL 和 VIDIOC_QUERYMENUVIDIOC_QUERYMENU ioctl 枚举可用的控制项,并使用 VIDIOC_G_CTRLVIDIOC_S_CTRL ioctl 获取和设置控制项值。当设备具有一个或多个控制项时,驱动程序必须实现 VIDIOC_QUERYCTRLVIDIOC_G_CTRLVIDIOC_S_CTRL,当它具有一个或多个菜单类型的控制项时,必须实现 VIDIOC_QUERYMENU

1.9.2. 示例:枚举所有控制项

struct v4l2_queryctrl queryctrl;
struct v4l2_querymenu querymenu;

static void enumerate_menu(__u32 id)
{
    printf("  Menu items:\\n");

    memset(&querymenu, 0, sizeof(querymenu));
    querymenu.id = id;

    for (querymenu.index = queryctrl.minimum;
         querymenu.index <= queryctrl.maximum;
         querymenu.index++) {
        if (0 == ioctl(fd, VIDIOC_QUERYMENU, &querymenu)) {
            printf("  %s\\n", querymenu.name);
        }
    }
}

memset(&queryctrl, 0, sizeof(queryctrl));

queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) {
    if (!(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
        printf("Control %s\\n", queryctrl.name);

        if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
            enumerate_menu(queryctrl.id);
    }

    queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
}
if (errno != EINVAL) {
    perror("VIDIOC_QUERYCTRL");
    exit(EXIT_FAILURE);
}

1.9.3. 示例:枚举所有控制项,包括复合控制项

struct v4l2_query_ext_ctrl query_ext_ctrl;

memset(&query_ext_ctrl, 0, sizeof(query_ext_ctrl));

query_ext_ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
while (0 == ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl)) {
    if (!(query_ext_ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
        printf("Control %s\\n", query_ext_ctrl.name);

        if (query_ext_ctrl.type == V4L2_CTRL_TYPE_MENU)
            enumerate_menu(query_ext_ctrl.id);
    }

    query_ext_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
}
if (errno != EINVAL) {
    perror("VIDIOC_QUERY_EXT_CTRL");
    exit(EXIT_FAILURE);
}

1.9.4. 示例:枚举所有用户控制项(旧样式)

memset(&queryctrl, 0, sizeof(queryctrl));

for (queryctrl.id = V4L2_CID_BASE;
     queryctrl.id < V4L2_CID_LASTP1;
     queryctrl.id++) {
    if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) {
        if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
            continue;

        printf("Control %s\\n", queryctrl.name);

        if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
            enumerate_menu(queryctrl.id);
    } else {
        if (errno == EINVAL)
            continue;

        perror("VIDIOC_QUERYCTRL");
        exit(EXIT_FAILURE);
    }
}

for (queryctrl.id = V4L2_CID_PRIVATE_BASE;;
     queryctrl.id++) {
    if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) {
        if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
            continue;

        printf("Control %s\\n", queryctrl.name);

        if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
            enumerate_menu(queryctrl.id);
    } else {
        if (errno == EINVAL)
            break;

        perror("VIDIOC_QUERYCTRL");
        exit(EXIT_FAILURE);
    }
}

1.9.5. 示例:更改控制项

struct v4l2_queryctrl queryctrl;
struct v4l2_control control;

memset(&queryctrl, 0, sizeof(queryctrl));
queryctrl.id = V4L2_CID_BRIGHTNESS;

if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) {
    if (errno != EINVAL) {
        perror("VIDIOC_QUERYCTRL");
        exit(EXIT_FAILURE);
    } else {
        printf("V4L2_CID_BRIGHTNESS is not supported\n");
    }
} else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
    printf("V4L2_CID_BRIGHTNESS is not supported\n");
} else {
    memset(&control, 0, sizeof (control));
    control.id = V4L2_CID_BRIGHTNESS;
    control.value = queryctrl.default_value;

    if (-1 == ioctl(fd, VIDIOC_S_CTRL, &control)) {
        perror("VIDIOC_S_CTRL");
        exit(EXIT_FAILURE);
    }
}

memset(&control, 0, sizeof(control));
control.id = V4L2_CID_CONTRAST;

if (0 == ioctl(fd, VIDIOC_G_CTRL, &control)) {
    control.value += 1;

    /* The driver may clamp the value or return ERANGE, ignored here */

    if (-1 == ioctl(fd, VIDIOC_S_CTRL, &control)
        && errno != ERANGE) {
        perror("VIDIOC_S_CTRL");
        exit(EXIT_FAILURE);
    }
/* Ignore if V4L2_CID_CONTRAST is unsupported */
} else if (errno != EINVAL) {
    perror("VIDIOC_G_CTRL");
    exit(EXIT_FAILURE);
}

control.id = V4L2_CID_AUDIO_MUTE;
control.value = 1; /* silence */

/* Errors ignored */
ioctl(fd, VIDIOC_S_CTRL, &control);