3. Session 配置基础:createCaptureSession 的调用方式、参数详解及常见错误

在 Android Camera2 框架中,createCaptureSession 是连接 CameraDevice 与 Surface 输出目标的桥梁。正确理解其调用方式与参数含义,是构建高性能多路流并发管线的第一步。本节将深入剖析该 API 的完整签名、参数语义、典型调用模式以及开发者最容易踩入的陷阱。

3.1 API 签名与参数详解

createCaptureSession 方法定义在 CameraDevice 类中,自 API 21 起提供,并在后续版本中增加了重载形式。核心签名如下:

// 基础版本 (API 21+)
public abstract void createCaptureSession(
    List<Surface> outputs,
    CameraCaptureSession.StateCallback callback,
    Handler handler
);

// 带 SessionConfiguration 版本 (API 28+)
public abstract void createCaptureSession(
    SessionConfiguration config
);

下表列出了各参数的类型、用途及版本要求:

参数 类型 说明 最低 API
outputs List<Surface> Session 输出目标列表,每个 Surface 对应一路输出流(预览、拍照、录像等) 21
callback CameraCaptureSession.StateCallback Session 状态回调,通知配置成功、失败或断开连接 21
handler Handler 回调执行的 Handler,若为 null 则使用主线程 Looper 21
config SessionConfiguration 封装 outputs、callback、executor、sessionType 及参数配置的容器 28
最佳实践:API 28 及以上应优先使用 SessionConfiguration 版本。它支持设置 SessionType(普通/高速)、Executor(避免主线程阻塞)以及 InputConfiguration(用于 reprocess 场景),扩展性远优于基础版本。

3.2 调用方式与典型流程

一个标准的 Session 创建流程包含以下步骤:

  1. 准备 Surface 列表:从 SurfaceView、ImageReader、MediaRecorder 等目标获取 Surface。
  2. 创建 StateCallback:实现 onConfigured、onConfigureFailed 等回调方法。
  3. 调用 createCaptureSession:传入 Surface 列表与回调。
  4. 等待回调:在 onConfigured 中获取 CameraCaptureSession 实例,开始发送请求。

以下是一个典型的调用代码示例:

// 1. 准备输出 Surface
Surface previewSurface = surfaceView.getHolder().getSurface();
ImageReader imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 2);
Surface captureSurface = imageReader.getSurface();

List<Surface> outputSurfaces = new ArrayList<>();
outputSurfaces.add(previewSurface);
outputSurfaces.add(captureSurface);

// 2. 创建回调
CameraCaptureSession.StateCallback sessionCallback =
    new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            // Session 就绪,可以开始重复请求或单次捕获
            captureSession = session;
            startPreview(session);
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            Log.e(TAG, "Session 配置失败");
        }
    };

// 3. 调用 API
cameraDevice.createCaptureSession(outputSurfaces, sessionCallback, backgroundHandler);
常见陷阱:Surface 列表的顺序不影响功能,但 不能包含重复的 Surface 对象。如果同一 Surface 需要被多路复用(例如预览和拍照共用),必须通过 createCaptureRequest 的 addTarget 方法分别添加,而非在 outputs 列表中重复添加。

3.3 Session 配置状态机与回调时序

Session 的创建过程是一个异步状态机。下图展示了从调用 API 到 Session 就绪的完整状态流转:

createCaptureSession 状态机流转 调用 API 配置协商中 onConfigured onConfigureFailed onDisconnected 说明 绿色:成功路径 红色:失败路径 紫色:断开路径

回调方法触发时机与含义:

回调方法 触发时机 开发者应执行的操作
onConfigured Camera HAL 确认配置有效,Session 就绪 保存 Session 引用,开始发送 CaptureRequest
onConfigureFailed HAL 拒绝配置(如输出组合不合法) 释放资源,尝试降级配置或提示用户
onDisconnected CameraDevice 意外断开 关闭 Session,重新打开 Camera
onReady Session 空闲,无正在处理的请求 可用于触发低延迟操作(如快速拍照)
onActive Session 开始处理请求 更新 UI 状态(如显示录制指示器)
onClosed Session 完全关闭 置空 Session 引用,避免内存泄漏

3.4 常见错误与排查方法

根据实际项目经验,以下是最常遇到的 createCaptureSession 错误及其根因:

错误 1:IllegalArgumentException — Surface 已被释放
原因:传入的 Surface 对应的 BufferQueue 已被销毁(如 SurfaceView 已 detach)。
解决:在调用 createCaptureSession 前检查 Surface.isValid(),并确保 Surface 持有者生命周期与 Session 对齐。
错误 2:CameraAccessException — 最大并发流数超限
原因:outputs 列表中的 Surface 数量超过 Camera 设备支持的 MAXIMUM_OUTPUT_COUNT。
解决:通过 CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS 查询上限,按优先级裁剪流数量。
错误 3:onConfigureFailed — 输出组合不被 HAL 支持
原因:某些分辨率、格式或流类型的组合在特定设备上不被允许(如同时使用 YUV_420_888 和 JPEG 流时尺寸不匹配)。
解决:使用 StreamConfigurationMap.getOutputSizes() 和 getHighSpeedVideoFpsRange() 验证组合合法性。
错误 4:Session 创建后立即报错 — 重复创建未关闭旧 Session
原因:在未关闭旧 Session 的情况下再次调用 createCaptureSession,导致 HAL 资源冲突。
解决:调用 session.close() 并等待 onClosed 回调后再创建新 Session。

3.5 多路流并发配置的 Surface 组合规则

当配置多路输出流时,Surface 的组合必须遵循 Camera HAL 的约束。下图展示了典型的多路流配置架构:

多路流并发 Surface 组合架构 CameraDevice createCaptureSession 配置层 预览 Surface (SurfaceView) 拍照 Surface (ImageReader/JPEG) 录像 Surface (MediaRecorder)

配置多路流时必须遵守以下规则:

  • 分辨率匹配:所有输出流的分辨率必须来自 StreamConfigurationMap.getOutputSizes() 中针对该格式的合法列表。
  • 格式兼容:同一 Session 中不能同时包含 RAW_SENSOR 和 YUV_420_888 流(部分设备限制)。
  • 帧率一致:高速录像模式下,所有流必须使用相同的帧率范围(通过 getHighSpeedVideoFpsRange() 查询)。
  • 最大流数:普通模式通常支持 3-4 路输出流,高速模式通常限制为 2 路。
调试技巧:当遇到 onConfigureFailed 时,使用 adb shell dumpsys media.camera 查看 HAL 日志,其中会明确输出拒绝配置的原因,如 "Stream configuration not supported" 或 "Too many streams"。