1.9. 用户控制¶
设备通常具有许多用户可设置的控制项,例如亮度、饱和度等,这些控制项将在图形用户界面上呈现给用户。但是,不同的设备将具有不同的可用控制项,并且可能值的范围和默认值也会因设备而异。控制 ioctls 提供信息和一种机制,用于为这些控制项创建友好的用户界面,该界面将与任何设备正确工作。
所有控制项都通过一个 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
(integer)
调整相机中的锐度滤镜。最小值禁用滤镜,更高值可提供更锐利的图像。
V4L2_CID_BACKLIGHT_COMPENSATION
(integer)
调整相机中的背光补偿。最小值禁用背光补偿。
V4L2_CID_CHROMA_AGC
(boolean)
色度自动增益控制。
V4L2_CID_CHROMA_GAIN
(integer)
调整色度增益控制(在色度 AGC 禁用时使用)。
V4L2_CID_COLOR_KILLER
(boolean)
启用色杀(即,在视频信号弱时强制生成黑白图像)。
V4L2_CID_COLORFX
(enum)
选择一种颜色效果。定义了以下值
|
颜色效果已禁用。 |
|
一种老旧(老照片)效果。 |
|
霜冻颜色效果。 |
|
水彩,冷色调。 |
|
黑白。 |
|
浮雕,高光和阴影取代明暗边界,低对比度区域设置为灰色背景。 |
|
草绿色。 |
|
负片。 |
|
棕褐色调。 |
|
素描。 |
|
皮肤美白。 |
|
天蓝色。 |
|
反转,图像色调部分反转,只有高于或低于某个阈值的颜色值被反转。 |
|
剪影(轮廓)。 |
|
鲜艳色彩。 |
|
Cb 和 Cr 色度分量被 |
|
RGB 分量被 |
V4L2_CID_COLORFX_RGB
(integer)
确定
V4L2_COLORFX_SET_RGB
颜色效果的红色、绿色和蓝色系数。提供的 32 位值中,[7:0] 位被解释为蓝色分量,[15:8] 位为绿色分量,[23:16] 位为红色分量,[31:24] 位必须为零。V4L2_CID_COLORFX_CBCR
(integer)
确定
V4L2_COLORFX_SET_CBCR
颜色效果的 Cb 和 Cr 系数。提供的 32 位值中,[7:0] 位被解释为 Cr 分量,[15:8] 位为 Cb 分量,[31:16] 位必须为零。V4L2_CID_AUTOBRIGHTNESS
(boolean)
启用自动亮度。
V4L2_CID_ROTATE
(integer)
将图像旋转指定角度。常见角度为 90、270 和 180 度。将图像旋转 90 度和 270 度将反转显示窗口的高度和宽度。需要使用 VIDIOC_S_FMT ioctl 根据所选旋转角度设置图像的新高度和宽度。
V4L2_CID_BG_COLOR
(integer)
设置当前输出设备的背景颜色。背景颜色需要以 RGB24 格式指定。提供的 32 位值中,0-7 位解释为红色颜色信息,8-15 位为绿色颜色信息,16-23 位为蓝色颜色信息,24-31 位必须为零。
V4L2_CID_ILLUMINATORS_1 V4L2_CID_ILLUMINATORS_2
(boolean)
打开或关闭设备(通常是显微镜)的照明器 1 或 2。
V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
(integer)
这是一个只读控制项,应用程序可以读取并用作提示,以确定传递给 REQBUFS 的 CAPTURE 缓冲区的数量。该值是硬件工作所需的最小 CAPTURE 缓冲区数量。状态解码器需要此控制项。
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT
(integer)
这是一个只读控制项,应用程序可以读取并用作提示,以确定传递给 REQBUFS 的 OUTPUT 缓冲区的数量。该值是硬件工作所需的最小 OUTPUT 缓冲区数量。状态编码器需要此控制项。
V4L2_CID_ALPHA_COMPONENT
(integer)
设置 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 ioctls 枚举可用控制项,并使用 VIDIOC_G_CTRL 和 VIDIOC_S_CTRL ioctls 获取和设置控制值。当设备有一个或多个控制项时,驱动程序必须实现 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);