3. Stream配置流程:CameraDevice.createCaptureSession、OutputConfiguration、SessionConfiguration详解
Stream配置是Camera HAL3 pipeline的起点,决定了数据从Sensor到应用层的完整路径。本章深入分析三个核心API:createCaptureSession、OutputConfiguration和SessionConfiguration,揭示它们如何协同完成Stream的创建、复用与性能调优。
3.1 配置流程全景图
从应用调用到HAL底层,Stream配置经历三个层次:Framework API层、Camera Service层、HAL实现层。下图展示了完整的调用链路与数据流。
3.2 SessionConfiguration:配置的顶层容器
SessionConfiguration是Android 9引入的API,用于在一次调用中完成所有Stream的配置。它替代了旧版多次调用createCaptureSession的方式,为Stream复用提供了结构基础。
3.2.1 核心字段与作用
| 字段 | 类型 | 说明 | 复用相关 |
|---|---|---|---|
| sessionType | int | 会话类型:普通/高速/受限 | 影响HAL的stream复用策略 |
| outputConfigurations | List<OutputConfiguration> | 所有输出流的配置列表 | 每个OutputConfiguration可携带复用标记 |
| inputConfiguration | InputConfiguration | 输入流配置(如ZSL) | 与输出流复用决策相关 |
| sessionParameters | TotalCaptureResult | 会话级参数(可选) | 影响Stream的初始化配置 |
SessionConfiguration时,应一次性传入所有OutputConfiguration,避免后续动态添加Stream。HAL在configure_streams()中会基于完整列表做复用优化,分批配置可能导致复用机会丢失。
3.2.2 创建示例
// 构建OutputConfiguration列表
List<OutputConfiguration> outputs = new ArrayList<>();
// 预览流:使用复用标记
OutputConfiguration previewConfig = new OutputConfiguration(previewSurface);
previewConfig.setStreamUseCase(StreamUseCase.PREVIEW);
outputs.add(previewConfig);
// 拍照流:高分辨率,不参与复用
OutputConfiguration photoConfig = new OutputConfiguration(photoSurface, 0);
photoConfig.setStreamUseCase(StreamUseCase.STILL_CAPTURE);
outputs.add(photoConfig);
// 构建SessionConfiguration
SessionConfiguration sessionConfig = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
outputs,
new Executor() { /* 回调线程 */ },
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
// 配置成功,开始捕获
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
// 配置失败,检查Stream参数
}
}
);
// 创建会话
cameraDevice.createCaptureSession(sessionConfig);
3.3 OutputConfiguration:单个输出流的精细控制
OutputConfiguration描述单个输出Stream的属性,是复用决策的最小单元。每个OutputConfiguration对应HAL层的一个stream_t。
3.3.1 关键属性与复用标记
| 属性 | API级别 | 复用影响 |
|---|---|---|
| Surface | 1 | 目标Surface,决定buffer流向 |
| surfaceGroupId | 28 | 同一group内的Surface可共享buffer |
| streamUseCase | 30 | 标识Stream用途,HAL据此优化复用 |
| physicalCameraId | 28 | 多摄场景指定物理相机 |
| isDeferred | 29 | 延迟Surface设置,影响Stream生命周期 |
| rotation | 26 | 输出旋转,HAL可能据此调整Stream尺寸 |
3.3.2 SurfaceGroupId:显式复用标记
surfaceGroupId是Android 10引入的显式复用机制。当多个OutputConfiguration具有相同的surfaceGroupId时,HAL可以(但不必须)为它们分配同一个物理buffer。
// 创建两个Surface,共享同一个buffer
Surface surface1 = new Surface(texture1);
Surface surface2 = new Surface(texture2);
OutputConfiguration config1 = new OutputConfiguration(surface1);
OutputConfiguration config2 = new OutputConfiguration(surface2);
// 设置相同的group ID,指示HAL可以复用
int groupId = 42;
config1.setSurfaceGroupId(groupId);
config2.setSurfaceGroupId(groupId);
List<OutputConfiguration> outputs = Arrays.asList(config1, config2);
SessionConfiguration sessionConfig = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
outputs,
executor,
callback
);
surfaceGroupId只是提示而非强制约束。HAL可能因为分辨率不匹配、格式不同或硬件限制而拒绝复用。应用层应始终准备好接收独立的buffer。
3.4 createCaptureSession:从配置到会话的桥梁
createCaptureSession是触发实际配置的入口。它接收SessionConfiguration,经过Camera Service的校验和转换,最终调用HAL的configure_streams()。
3.4.1 内部处理流程
3.4.2 HAL层stream_t结构
HAL接收的camera3_stream_t是复用决策的核心数据结构。关键字段如下:
| 字段 | 方向 | 说明 | 复用影响 |
|---|---|---|---|
| stream_type | 应用→HAL | OUTPUT / INPUT / BIDIRECTIONAL | 不同类型不能复用 |
| width / height | 应用→HAL | 分辨率 | 分辨率必须完全一致才能复用 |
| format | 应用→HAL | 像素格式(如HAL_PIXEL_FORMAT_YCbCr_420_888) | 格式必须一致 |
| usage | 双向 | Gralloc使用标记,HAL可修改 | HAL通过修改usage来指示复用策略 |
| max_buffers | HAL→应用 | HAL请求的最大buffer数 | 复用后max_buffers可能增加 |
| priv | HAL私有 | HAL内部状态指针 | 用于跟踪复用关系 |
3.5 复用决策在配置阶段的体现
Stream复用决策发生在HAL的configure_streams()中。HAL根据所有stream_t的集合,决定哪些stream可以共享物理buffer。
3.5.1 复用决策条件
- 分辨率完全一致:width和height必须相等
- 像素格式兼容:相同格式,或HAL支持格式转换
- usage标记兼容:HAL可以合并usage标记
- stream_type相同:同为OUTPUT或同为INPUT
- 硬件路径允许:ISP/VPU有足够的处理能力
3.5.2 复用决策示例
// HAL伪代码:configure_streams中的复用决策
int configure_streams(const camera3_device_t *device,
camera3_stream_configuration_t *config) {
// 遍历所有stream,寻找可复用对
for (int i = 0; i < config->num_streams; i++) {
camera3_stream_t *stream_i = config->streams[i];
for (int j = i + 1; j < config->num_streams; j++) {
camera3_stream_t *stream_j = config->streams[j];
// 检查复用条件
if (can_reuse(stream_i, stream_j)) {
// 标记复用关系
stream_j->priv = stream_i; // stream_j复用stream_i的buffer
// 合并usage标记
stream_i->usage |= stream_j->usage;
// 调整max_buffers
stream_i->max_buffers = max(stream_i->max_buffers,
stream_j->max_buffers);
// 通知framework:stream_j不再需要独立buffer
stream_j->usage |= GRALLOC_USAGE_SW_READ_OFTEN;
}
}
}
return 0;
}
bool can_reuse(camera3_stream_t *a, camera3_stream_t *b) {
return (a->width == b->width) &&
(a->height == b->height) &&
(a->format == b->format) &&
(a->stream_type == b->stream_type);
}
3.6 配置阶段的性能优化要点
3.6.1 配置延迟优化清单
| 优化项 | 影响 | 建议 |
|---|---|---|
| 减少Stream数量 | 降低configure_streams复杂度 | 只创建必要的Stream,避免冗余 |
| 复用相同分辨率的Stream | 减少buffer分配次数 | 预览和录像使用相同分辨率 |
| 预创建Surface | 避免配置时动态创建 | 在onOpened回调中提前创建Surface |
| 使用SessionConfiguration批量配置 | 减少IPC次数 | 一次性传入所有OutputConfiguration |
| 避免配置后动态添加Stream | 防止重新configure_streams | 提前规划所有Stream需求 |
3.6.2 常见配置失败原因
- Surface已销毁:在配置前检查Surface.isValid()
- 分辨率超出限制:检查CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
- 格式不支持:使用推荐的格式(如YUV_420_888)
- Stream数量超限:不同设备有不同限制,通常为3-5个
- 复用条件不满足:分辨率/格式不一致导致复用失败
adb shell dumpsys media.camera查看当前设备的Stream配置限制。关注maxOutputStreams和streamConfigurationMap字段。