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_GAIN
和V4L2_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
(枚举)
选择颜色效果。定义了以下值
|
禁用颜色效果。 |
|
老化(旧照片)效果。 |
|
冰冻颜色效果。 |
|
水彩,冷色调。 |
|
黑白。 |
|
浮雕,高光和阴影替换明暗边界,低对比度区域设置为灰色背景。 |
|
草绿色。 |
|
负片。 |
|
棕褐色调。 |
|
素描。 |
|
美白皮肤。 |
|
天蓝色。 |
|
太阳化,图像的色调部分反转,只有高于或低于某个阈值的颜色值会被反转。 |
|
轮廓(外形)。 |
|
鲜艳的色彩。 |
|
Cb 和 Cr 色度分量被由 |
|
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_QUERYMENU 和 VIDIOC_QUERYMENU ioctl 枚举可用的控制项,并使用 VIDIOC_G_CTRL 和 VIDIOC_S_CTRL ioctl 获取和设置控制项值。当设备具有一个或多个控制项时,驱动程序必须实现 VIDIOC_QUERYCTRL
、VIDIOC_G_CTRL
和 VIDIOC_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);