3. Stream配置流程:CameraDevice.createCaptureSession、OutputConfiguration、SessionConfiguration详解

Stream配置是Camera HAL3 pipeline的起点,决定了数据从Sensor到应用层的完整路径。本章深入分析三个核心API:createCaptureSession、OutputConfiguration和SessionConfiguration,揭示它们如何协同完成Stream的创建、复用与性能调优。

核心观点: Stream配置的本质是资源契约——应用层通过SessionConfiguration声明需求,HAL层通过configure_streams()确认能力,最终在createCaptureSession中完成绑定。复用策略在这一阶段就已埋下伏笔。

3.1 配置流程全景图

从应用调用到HAL底层,Stream配置经历三个层次:Framework API层、Camera Service层、HAL实现层。下图展示了完整的调用链路与数据流。

Stream配置流程全景图 应用层 (App/Framework) SessionConfiguration OutputConfiguration createCaptureSession Camera Service Stream配置校验 Surface绑定管理 configure_streams调用 HAL实现层 configure_streams() Stream复用决策 BufferQueue分配 1. 配置请求 2. configure_streams 3. 返回配置结果 4. 回调onConfigured 关键时序说明: • 应用层构建SessionConfiguration时,OutputConfiguration已包含Surface和复用标记 • Camera Service负责将OutputConfiguration转换为HAL的stream_t结构 • HAL在configure_streams()中决定哪些stream可以复用物理buffer

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 内部处理流程

createCaptureSession 内部处理流程 1. 参数校验 检查Surface有效性 2. Stream转换 Output→stream_t 3. configure_streams HAL执行配置 OK? 成功 onConfigured 失败 onConfigureFailed 各步骤详细说明: 1. 参数校验:检查Surface是否有效、是否已被销毁、分辨率是否在设备支持范围内 2. Stream转换:将OutputConfiguration转换为HAL的camera3_stream_t结构,包括: - stream_type: CAMERA3_STREAM_OUTPUT - width/height: 从Surface获取 - format: 从Surface的GraphicBuffer格式推导 - usage: 包含GRALLOC_USAGE_HW_CAMERA_WRITE等标记 3. configure_streams:HAL收到stream_t数组后,执行复用决策、分配硬件资源 返回时,HAL会修改stream_t中的usage和max_buffers字段

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 配置阶段的性能优化要点

性能关键指标: 配置延迟(从createCaptureSession到onConfigured回调)应控制在50ms以内。超过100ms会导致应用启动黑屏时间过长。

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字段。

3.7 本章小结