3. V4L2 Selection API:V4L2_SEL_TGT_CROP 与 V4L2_SEL_TGT_COMPOSE 目标类型详解
V4L2 Selection API 是驱动与应用程序之间协商图像裁剪(Crop)与合成(Compose)窗口的标准接口。该 API 通过 VIDIOC_G_SELECTION 和 VIDIOC_S_SELECTION ioctl 配合不同的目标类型(Target Type)来操作。本章深入剖析最核心的两个目标类型:V4L2_SEL_TGT_CROP 和 V4L2_SEL_TGT_COMPOSE,并对比它们的语义、数据流位置及配置约束。
3.1 Selection API 核心数据结构
所有 Selection 操作均通过 struct v4l2_selection 结构体传递参数。该结构体定义在 linux/v4l2-common.h 中,关键字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
type |
__u32 | 缓冲区类型,如 V4L2_BUF_TYPE_VIDEO_CAPTURE 或 V4L2_BUF_TYPE_VIDEO_OUTPUT |
target |
__u32 | 目标类型,如 V4L2_SEL_TGT_CROP、V4L2_SEL_TGT_COMPOSE |
flags |
__u32 | 标志位,如 V4L2_SEL_FLAG_GE / V4L2_SEL_FLAG_LE 用于约束协商 |
r |
struct v4l2_rect | 矩形区域,包含 left, top, width, height |
reserved |
__u32[11] | 保留字段,必须清零 |
关键点:
v4l2_rect 中的 left/top 是相对于源图像或目标缓冲区的坐标原点(0,0)的偏移量,单位为像素。width/height 定义裁剪或合成窗口的尺寸。
3.2 V4L2_SEL_TGT_CROP:裁剪源窗口
V4L2_SEL_TGT_CROP 用于定义从传感器或视频源中选取的感兴趣区域(ROI)。该区域决定了后续处理管线实际使用的像素数据范围。对于 Capture 设备,CROP 窗口位于“源图像”之上;对于 Output 设备,CROP 窗口定义从输入缓冲区中选取的区域。
3.2.1 语义与数据流位置
- 方向: 从源图像中“切出”一个子矩形。
- 默认值: 通常等于源图像的全尺寸(即 left=0, top=0, width=源宽, height=源高)。
- 硬件约束: 驱动可能要求对齐到特定步长(如 2 像素对齐),或限制最小/最大尺寸。
- 典型应用: 数字变焦、传感器 binning/skipping 后的有效区域提取、多路复用场景下的子画面选择。
实战提示: 在设置 CROP 之前,建议先调用
VIDIOC_G_SELECTION 获取当前驱动支持的默认 CROP 矩形和边界(通过 V4L2_SEL_TGT_CROP_BOUNDS 查询最大范围)。然后使用 V4L2_SEL_FLAG_GE 或 V4L2_SEL_FLAG_LE 标志让驱动自动调整到最接近的合法值。
3.2.2 CROP 配置流程
- 查询设备支持的 CROP 边界(
V4L2_SEL_TGT_CROP_BOUNDS)。 - 查询当前 CROP 默认值(
V4L2_SEL_TGT_CROP)。 - 应用程序设置期望的 CROP 矩形,可携带
V4L2_SEL_FLAG_GE(驱动只增不减)或V4L2_SEL_FLAG_LE(驱动只减不增)。 - 驱动返回实际生效的 CROP 矩形(可能经过硬件对齐修正)。
- 应用程序读取修正后的矩形,用于后续的 COMPOSE 或格式配置。
3.3 V4L2_SEL_TGT_COMPOSE:合成目标窗口
V4L2_SEL_TGT_COMPOSE 用于定义裁剪后的图像如何放置到最终输出缓冲区中。它控制图像在目标帧缓冲中的位置和缩放行为。对于 Capture 设备,COMPOSE 窗口决定裁剪后的数据在用户空间缓冲区中的布局;对于 Output 设备,COMPOSE 窗口定义输入缓冲区中的图像如何映射到显示或编码区域。
3.3.1 语义与数据流位置
- 方向: 将裁剪后的图像“合成”到目标缓冲区中的指定矩形。
- 默认值: 通常等于裁剪后的尺寸(即无缩放、无偏移),但驱动可能支持缩放。
- 硬件约束: 缩放比例可能受限于硬件 scaler 能力;位置可能要求对齐到像素边界。
- 典型应用: 画中画(PiP)、图像缩放后放置到指定区域、多路视频拼接中的子窗口定位。
注意: 并非所有 V4L2 设备都支持 COMPOSE。对于不支持硬件 scaler 的简单设备,COMPOSE 窗口必须等于 CROP 窗口尺寸,且 left/top 必须为 0。在设置前应通过
VIDIOC_QUERYCAP 检查设备能力标志 V4L2_CAP_VIDEO_CAPTURE 和 V4L2_CAP_VIDEO_OUTPUT,并尝试查询 COMPOSE 边界确认支持。
3.4 CROP 与 COMPOSE 的对比与协作
| 对比维度 | V4L2_SEL_TGT_CROP | V4L2_SEL_TGT_COMPOSE |
|---|---|---|
| 数据流阶段 | 源图像阶段(输入侧) | 目标缓冲区阶段(输出侧) |
| 主要作用 | 选择感兴趣区域,丢弃外部像素 | 定位与缩放裁剪后的图像到最终缓冲区 |
| 坐标参考系 | 源图像坐标系(0,0 为源左上角) | 目标缓冲区坐标系(0,0 为缓冲区左上角) |
| 缩放能力 | 无缩放(仅裁剪) | 支持缩放(如果硬件 scaler 可用) |
| 典型默认值 | 全源尺寸 | 等于 CROP 尺寸,无偏移 |
| 硬件对齐要求 | 常见 2 像素或 16 像素对齐 | 位置可能要求偶数坐标 |
3.5 数据流与窗口关系图
下图展示了从传感器原始图像到最终用户缓冲区的完整窗口链,清晰标出 CROP 与 COMPOSE 的协作关系:
3.6 代码示例:查询与设置 CROP 和 COMPOSE
以下代码片段演示了在 Capture 设备上查询 CROP 边界、设置自定义 CROP 窗口,并读取 COMPOSE 默认值的完整流程:
#include <linux/v4l2-common.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int configure_crop_compose(int fd) {
struct v4l2_selection sel;
int ret;
/* 1. 查询 CROP 边界(最大允许范围) */
memset(&sel, 0, sizeof(sel));
sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
ret = ioctl(fd, VIDIOC_G_SELECTION, &sel);
if (ret < 0) {
perror("G_SELECTION CROP_BOUNDS failed");
return -1;
}
printf("CROP bounds: left=%d top=%d width=%d height=%d\n",
sel.r.left, sel.r.top, sel.r.width, sel.r.height);
/* 2. 设置自定义 CROP 窗口(带 LE 标志,驱动只减不增) */
memset(&sel, 0, sizeof(sel));
sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
sel.target = V4L2_SEL_TGT_CROP;
sel.flags = V4L2_SEL_FLAG_LE;
sel.r.left = 100;
sel.r.top = 50;
sel.r.width = 800;
sel.r.height = 600;
ret = ioctl(fd, VIDIOC_S_SELECTION, &sel);
if (ret < 0) {
perror("S_SELECTION CROP failed");
return -1;
}
printf("CROP set to: left=%d top=%d width=%d height=%d\n",
sel.r.left, sel.r.top, sel.r.width, sel.r.height);
/* 3. 查询当前 COMPOSE 默认值 */
memset(&sel, 0, sizeof(sel));
sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
sel.target = V4L2_SEL_TGT_COMPOSE;
ret = ioctl(fd, VIDIOC_G_SELECTION, &sel);
if (ret < 0) {
/* 设备可能不支持 COMPOSE,此时忽略 */
printf("COMPOSE not supported, using CROP size directly\n");
} else {
printf("COMPOSE default: left=%d top=%d width=%d height=%d\n",
sel.r.left, sel.r.top, sel.r.width, sel.r.height);
}
return 0;
}
代码要点:
- 先查询
CROP_BOUNDS了解硬件限制,避免设置越界值。 - 使用
V4L2_SEL_FLAG_LE让驱动自动缩小到合法值,适合用户期望“不超过指定尺寸”的场景。 - COMPOSE 查询失败不代表设备不可用,许多简单摄像头不支持 COMPOSE,此时应默认使用 CROP 尺寸作为输出尺寸。
3.7 常见配置陷阱与调试建议
- 陷阱1: 未先设置 CROP 就直接设置 COMPOSE。部分驱动要求 CROP 必须先于 COMPOSE 配置,否则 COMPOSE 设置可能被拒绝或产生意外结果。
- 陷阱2: 忽略
V4L2_SEL_FLAG_GE/V4L2_SEL_FLAG_LE标志。不带标志时,驱动可能直接返回错误(EINVAL)如果请求的矩形不精确匹配硬件支持的值。 - 陷阱3: 假设 COMPOSE 支持任意缩放。实际硬件 scaler 通常有最小/最大缩放比限制(如 1/4x 到 4x),超出范围会导致设置失败。
- 调试建议: 使用
v4l2-ctl --get-crop和v4l2-ctl --set-crop命令行工具快速验证设备行为;在驱动中增加dev_dbg打印实际生效的矩形值。
兼容性提醒: 部分老旧驱动或硬件不支持 Selection API,此时
VIDIOC_G_SELECTION 和 VIDIOC_S_SELECTION 会返回 EINVAL 或 ENOTTY。应用程序应优雅降级,回退到使用 VIDIOC_S_FMT 的 bytesperline 和 sizeimage 字段间接推断裁剪信息。