3. Camera3Stream 结构体详解
在 Camera HAL3 中,camera3_stream_t 是连接 Framework 与 HAL 的图像数据通道描述符。每一个 Stream 代表一条独立的图像数据流,其配置直接决定了 HAL 输出的图像格式、分辨率、缓冲池大小以及使用场景。理解该结构体的每个字段,是正确配置 Stream 的基础。
configure_streams() 调用失败或预览黑屏。
3.1 结构体总览
camera3_stream_t 定义在 hardware/libhardware/include/hardware/camera3.h 中,其核心字段如下表所示:
| 字段名 | 类型 | 方向 | 说明 |
|---|---|---|---|
stream_type |
int |
Framework → HAL | 流类型:输出、输入、双向 |
width |
uint32_t |
Framework → HAL | 图像宽度(像素) |
height |
uint32_t |
Framework → HAL | 图像高度(像素) |
format |
int |
Framework → HAL | 像素格式(HAL_PIXEL_FORMAT_*) |
usage |
uint32_t |
Framework → HAL | Gralloc 使用标志位,描述 buffer 用途 |
max_buffers |
uint32_t |
HAL → Framework | HAL 请求的最大 buffer 数量 |
priv |
void * |
HAL 私有 | HAL 内部关联的私有数据指针 |
data_space |
android_dataspace_t |
Framework → HAL | 色彩空间与动态范围描述 |
rotation |
int |
Framework → HAL | 输出旋转角度(0/90/180/270) |
physical_camera_id |
const char * |
Framework → HAL | 物理 camera ID(多摄逻辑相机使用) |
configure_streams() 回调中,HAL 可以修改 max_buffers 和 priv 字段,但 不得修改 stream_type、width、height、format、usage 等输入字段。若 HAL 无法支持请求的配置,应返回 -EINVAL 或 -ENODEV。
3.2 核心字段深度解析
3.2.1 stream_type —— 流的方向与角色
stream_type 定义了 Stream 的数据流向,取值如下:
- CAMERA3_STREAM_OUTPUT (0):输出流。数据从 HAL 流向 Framework,用于预览、拍照、录像。
- CAMERA3_STREAM_INPUT (1):输入流。数据从 Framework 流向 HAL,用于 reprocessing(如美颜、HDR 合成)。
- CAMERA3_STREAM_BIDIRECTIONAL (2):双向流。数据可双向流动,用于零快门延迟(ZSL)等场景。
CAMERA3_STREAM_INPUT 会导致 process_capture_result() 无法输出数据,造成预览黑屏。务必根据实际用途选择正确的类型。
3.2.2 width / height —— 分辨率约束
分辨率必须严格匹配 Sensor 输出能力或 ISP 缩放范围。HAL 在 configure_streams() 中应检查:
- 是否在
android.scaler.availableStreamConfigurations列出的分辨率范围内。 - width 和 height 是否满足对齐要求(通常为 16 像素对齐,部分平台要求 32 或 64 像素对齐)。
- 多个 Stream 之间是否存在分辨率依赖(如 YUV reprocessing 流需要与 RAW 流分辨率一致)。
// 典型的分辨率合法性检查
if (width % 16 != 0 || height % 16 != 0) {
ALOGE("Stream resolution %dx%d not 16-pixel aligned", width, height);
return -EINVAL;
}
if (width > sensor_max_width || height > sensor_max_height) {
ALOGE("Stream resolution %dx%d exceeds sensor limit %dx%d",
width, height, sensor_max_width, sensor_max_height);
return -EINVAL;
}
3.2.3 format —— 像素格式的选择
format 使用 Android 定义的 HAL_PIXEL_FORMAT_* 常量。常见格式如下:
| 格式常量 | 值 | 典型用途 | BPP(每像素比特) |
|---|---|---|---|
HAL_PIXEL_FORMAT_YCbCr_420_888 |
0x23 | 预览、录像(灵活 YCbCr) | 12 |
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED |
0x22 | GPU/Display 直接消费(平台优化格式) | 平台相关 |
HAL_PIXEL_FORMAT_BLOB |
0x21 | JPEG 编码输出 | 可变 |
HAL_PIXEL_FORMAT_RAW16 |
0x20 | RAW 数据输出(DNG) | 16 |
HAL_PIXEL_FORMAT_RAW10 |
0x25 | RAW10 压缩格式 | 10 |
HAL_PIXEL_FORMAT_Y16 |
0x20363159 | 深度图输出 | 16 |
IMPLEMENTATION_DEFINED,让 Gralloc 选择平台最优格式(如 NV12、NV21 或 P010)。对于需要 CPU 访问的流,使用 YCbCr_420_888 保证可移植性。
3.2.4 usage —— Gralloc 使用标志
usage 是 Gralloc buffer 分配时的标志位组合,描述了 buffer 将被如何使用。常见标志:
- GRALLOC_USAGE_SW_READ_OFTEN:CPU 频繁读取(如 JPEG 编码器)。
- GRALLOC_USAGE_SW_WRITE_OFTEN:CPU 频繁写入(如图像处理算法)。
- GRALLOC_USAGE_HW_TEXTURE:GPU 纹理采样(如预览显示)。
- GRALLOC_USAGE_HW_VIDEO_ENCODER:视频编码器输入。
- GRALLOC_USAGE_HW_CAMERA_WRITE:Camera ISP 直接写入。
- GRALLOC_USAGE_HW_CAMERA_READ:Camera ISP 读取(用于 reprocessing)。
Framework 会根据 Stream 的用途自动组合这些标志。HAL 可以在 configure_streams() 中通过 usage 判断 buffer 的最终消费者,从而选择适当的 ISP 输出格式。
// 解析 usage 判断消费者
if (stream->usage & GRALLOC_USAGE_HW_TEXTURE) {
// 送显:优先使用平台原生格式
stream->format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
} else if (stream->usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) {
// 录像:使用 NV12 或 YV12
stream->format = HAL_PIXEL_FORMAT_YCbCr_420_888;
}
3.2.5 max_buffers —— 缓冲池深度
max_buffers 是 HAL 向 Framework 返回的值,表示该 Stream 需要的最大 buffer 数量。Framework 会根据此值分配 buffer 队列深度。
- 典型值范围: 3 ~ 8 个 buffer。
- 预览流: 通常 3~4 个 buffer(双缓冲 + 1 个备用)。
- 拍照流: 通常 4~6 个 buffer(考虑 JPEG 编码延迟)。
- ZSL 流: 可能需要 6~8 个 buffer 以维持环形缓冲区。
max_buffers 设置过大浪费内存,设置过小会导致 dequeue 阻塞,造成帧率下降。建议根据 pipeline 深度 + 1 计算:max_buffers = pipeline_depth + 1。
3.2.6 priv —— HAL 私有数据指针
priv 是 HAL 用于关联私有数据结构的指针。在 configure_streams() 中,HAL 可以分配自定义的 Stream 上下文(如 ISP 通道句柄、格式转换器状态),并将指针赋值给 priv。后续在 process_capture_request() 和 process_capture_result() 中,通过 camera3_stream_t * 即可取出私有数据。
// HAL 端典型的 priv 使用模式
typedef struct hal_stream_ctx {
int isp_channel_id;
int format_converter_handle;
int buffer_count;
// ... 其他私有状态
} hal_stream_ctx_t;
// configure_streams() 中初始化
for (int i = 0; i < num_streams; i++) {
camera3_stream_t *stream = stream_list[i];
hal_stream_ctx_t *ctx = calloc(1, sizeof(hal_stream_ctx_t));
ctx->isp_channel_id = allocate_isp_channel(stream->width, stream->height);
stream->priv = ctx;
}
// process_capture_request() 中使用
void process_capture_request(camera3_capture_request_t *request) {
for (int i = 0; i < request->num_output_buffers; i++) {
camera3_stream_buffer_t *buf = &request->output_buffers[i];
hal_stream_ctx_t *ctx = (hal_stream_ctx_t *)buf->stream->priv;
// 使用 ctx->isp_channel_id 配置 ISP
}
}
3.3 Stream 配置流程与字段交互
下图展示了从 Framework 下发 Stream 配置到 HAL 处理完成的完整流程,以及各字段在流程中的角色:
3.4 字段配置避坑指南
| 字段 | 常见错误 | 后果 | 正确做法 |
|---|---|---|---|
stream_type |
预览流误配为 INPUT | 预览黑屏 | 预览/录像/拍照使用 OUTPUT |
width/height |
未对齐 16 像素 | Gralloc 分配失败 | 始终检查对齐要求 |
format |
使用不支持的格式 | configure_streams 返回 -EINVAL | 查询 availableStreamConfigurations |
usage |
遗漏 GRALLOC_USAGE_HW_CAMERA_WRITE | ISP 无法写入 buffer | 确保包含 Camera 写入标志 |
max_buffers |
设置过小(如 2) | 帧率下降、dequeue 阻塞 | pipeline_depth + 1 |
priv |
未初始化或重复释放 | 内存泄漏或野指针 crash | configure_streams 分配,close 时释放 |
configure_streams() 入口处打印所有 Stream 字段,使用 ALOGD 输出到 logcat。对比 Framework 期望值与 HAL 实际处理值,可以快速定位配置不匹配问题。
3.5 多 Stream 配置示例
以下是一个典型的 3 路 Stream 配置场景(预览 + 拍照 + 录像),展示各字段的实际取值:
// 典型 3 路 Stream 配置
camera3_stream_t preview_stream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 1920,
.height = 1080,
.format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
.usage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE,
.max_buffers = 4, // HAL 回填
.priv = NULL, // HAL 回填
.data_space = HAL_DATASPACE_UNKNOWN,
.rotation = CAMERA3_STREAM_ROTATION_0,
};
camera3_stream_t snapshot_stream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 3840,
.height = 2160,
.format = HAL_PIXEL_FORMAT_BLOB, // JPEG 输出
.usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_CAMERA_WRITE,
.max_buffers = 6, // JPEG 编码需要更多 buffer
.priv = NULL,
.data_space = HAL_DATASPACE_V0_SRGB,
.rotation = CAMERA3_STREAM_ROTATION_0,
};
camera3_stream_t video_stream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 1920,
.height = 1080,
.format = HAL_PIXEL_FORMAT_YCbCr_420_888,
.usage = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_CAMERA_WRITE,
.max_buffers = 5,
.priv = NULL,
.data_space = HAL_DATASPACE_BT709,
.rotation = CAMERA3_STREAM_ROTATION_0,
};
// 配置数组
camera3_stream_t *stream_list[3] = {
&preview_stream,
&snapshot_stream,
&video_stream
};
IMPLEMENTATION_DEFINED 格式 + HW_TEXTURE usage,让平台选择最优显示格式;拍照流使用 BLOB 格式 + SW_READ_OFTEN 标志,便于 CPU 读取 JPEG 数据;录像流使用 YCbCr_420_888 格式 + HW_VIDEO_ENCODER 标志,直接喂给硬件编码器。