← 返回课程

Camera2 API

Camera全栈开发(Qcom Camx) 第 6 / 30 节

第 4 章:Camera2 API 核心概念


本章导读

你可能觉得:"我是做 HAL 的,App 层的 API 关我什么事?"

但事实是:App 层调用的每一个 Camera2 API,在 HAL 层都有一个对应的函数

App 层                           HAL 层
CameraManager.openCamera()  →   camera3_device_t.open()
CameraDevice.createSession() →   configure_streams()
CameraCaptureSession.capture()    →   process_capture_request()
CameraCaptureSession.CaptureCallback 回调         →   process_capture_result()

你在 HAL 层写的代码,就是为这些 API 提供底层实现的。不理解 App 层的行为,你就不知道 HAL 层为什么那样设计。

这一章从 App 层看 Camera——但你记住,我们最终都会回到 HAL 层去。


4.1 Camera2 的五大核心类

对照图中各类关系来读这一节

Camera2 API 围绕五个核心类展开。它们的调用顺序就是一次拍照的完整流程:

CameraManager                       ① 获取相机服务
       ↓
CameraCharacteristics              ② 查询相机能力
       ↓
CameraDevice                       ③ 打开相机
       ↓
CameraCaptureSession               ④ 创建会话
       ↓
CaptureRequest → CaptureResult     ⑤ 请求 → 结果(可循环)

4.1.1 CameraManager

入口。获取系统 Camera 服务,枚举可用相机。

// 获取 CameraManager
CameraManager manager = (CameraManager)
    context.getSystemService(Context.CAMERA_SERVICE);

// 获取所有 Camera ID
String[] cameraIds = manager.getCameraIdList();
// 典型输出: ["0", "1"]
// 0 = 后摄, 1 = 前摄

// 多摄手机可能有: ["0", "1", "2"]
// 2 = 逻辑 Camera(如主摄+广角的组合)

💡 Camera ID 不是固定的

你不知道 Google 哪天会把 ID 顺序换一下。永远不要硬编码 cameraId=0 表示后摄。用 LENS_FACING 来判断。

4.1.2 CameraCharacteristics

查询相机的"能力边界"——支持多大分辨率、什么帧率、是否支持 RAW。

CameraCharacteristics chars =
    manager.getCameraCharacteristics(cameraId);

// 前后摄
int facing = chars.get(CameraCharacteristics.LENS_FACING);
// LENS_FACING_FRONT = 0, LENS_FACING_BACK = 1

// 安装角度(影响预览方向)
int orientation = chars.get(
    CameraCharacteristics.SENSOR_ORIENTATION);
// 后摄通常 90°,前摄通常 270°

// 最重要的一个 Key —— StreamConfigurationMap
StreamConfigurationMap configMap = chars.get(
    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

// 支持的输出分辨率
Size[] jpegSizes = configMap.getOutputSizes(ImageFormat.JPEG);
Size[] yuvSizes  = configMap.getOutputSizes(ImageFormat.YUV_420_888);
Size[] rawSizes  = configMap.getOutputSizes(ImageFormat.RAW_SENSOR);

// 支持的帧率范围
Range<Integer>[] fpsRanges = chars.get(
    CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
// 例如: [15, 30] 表示 15~30fps 可调

💡 getOutputSizes() 返回的第一个不一定是使用建议

它按面积降序排列。第一个通常是最大分辨率(如 48MP),但预览用 1080p 就够了。用太大的预览分辨率会浪费 ISP 带宽。

4.1.3 CameraDevice

代表一个物理相机的连接。所有操作通过它发起。

manager.openCamera(cameraId, new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice device) {
        // 相机已打开,可以创建 CaptureSession
    }

    @Override
    public void onDisconnected(CameraDevice device) {
        // 相机断连——另一个更高优先级的 App 占用了
    }

    @Override
    public void onError(CameraDevice device, int error) {
        // 相机出错——HAL 返回了错误
    }
}, handler);

CameraDevice 的三个核心方法

// ① 创建 CaptureRequest.Builder
CaptureRequest.Builder builder =
    device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

// ② 创建 CaptureSession
device.createCaptureSession(surfaces, callback, handler);

// ③ 关闭
device.close();

TEMPLATE 参数模板

模板 用途 特点
TEMPLATE_PREVIEW 预览 最大帧率优先
TEMPLATE_STILL_CAPTURE 拍照 图像质量优先
TEMPLATE_RECORD 录像 帧率和质量平衡
TEMPLATE_VIDEO_SNAPSHOT 录像同时拍照 HFR + JPEG
TEMPLATE_ZERO_SHUTTER_LAG 零延时拍照 ZSL 模式
TEMPLATE_MANUAL 手动控制 AE/AF 全关

💡 TEMPLATE 预设了什么?

模板不是"模式",而是一组默认参数值。比如 TEMPLATE_PREVIEW 会让 AE 偏向高帧率,TEMPLATE_STILL_CAPTURE 让 AE 偏向低 ISO。你可以在 Builder 中覆盖任何预设值。

4.1.4 CaptureRequest

一次拍照/预览的参数集合。

CaptureRequest.Builder builder =
    device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

// 设置输出目标
builder.addTarget(previewSurface);  // SurfaceView / TextureView / ImageReader

// 设置参数
builder.set(CaptureRequest.CONTROL_AE_MODE,
            CaptureRequest.CONTROL_AE_MODE_ON);
builder.set(CaptureRequest.CONTROL_AF_MODE,
            CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

// 构建(变成不可变对象)
CaptureRequest request = builder.build();

CaptureRequest 的参数分类(对应 HAL 层的 metadata):

曝光:     CONTROL_AE_MODE, SENSOR_EXPOSURE_TIME, SENSOR_SENSITIVITY
对焦:     CONTROL_AF_MODE, LENS_FOCUS_DISTANCE
白平衡:   CONTROL_AWB_MODE, COLOR_CORRECTION_GAINS
输出:     addTarget() → 指向 Surface
JPEG:     JPEG_QUALITY, JPEG_ORIENTATION
3A 结果:  CaptureResult 中读取

4.1.5 CaptureResult

请求的结果。包含 metadata 和图像数据。

CameraCaptureSession.CaptureCallback callback =
    new CameraCaptureSession.CaptureCallback() {

    @Override
    public void onCaptureStarted(CameraCaptureSession session,
            CaptureRequest request, long timestamp, long frameNumber) {
        // ISP 开始处理这一帧
    }

    @Override
    public void onCaptureCompleted(CameraCaptureSession session,
            CaptureRequest request, TotalCaptureResult result) {
        // 整帧处理完成
        Integer aeMode = result.get(CaptureResult.CONTROL_AE_MODE);
        Long exposure = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
    }

    @Override
    public void onCaptureFailed(CameraCaptureSession session,
            CaptureRequest request, CaptureFailure failure) {
        // 处理失败
        int reason = failure.getReason();
        // CAPTURE_FAILURE_REASON_ERROR    = 0  硬件错误
        // CAPTURE_FAILURE_REASON_FLUSHED  = 1  被 flush 取消
    }
};

💡 onCaptureStartedonCaptureCompleted 的时间差

这个差值就是 HAL 层的处理时间。

正常 preview: ~33ms @30fps
正常拍照:    ~80~200ms(取决于 JPEG 编码速度)
如果远大于正常值 → 有性能瓶颈

第 23 章会详细讲怎么用这个时间差做性能分析。


4.2 StreamConfigurationMap——相机能力的查询入口

这个 Key 告诉你相机能做什么

StreamConfigurationMap map = chars.get(
    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

// 支持的输出格式
int[] formats = map.getOutputFormats();
// 可能: [JPEG, YUV_420_888, RAW_SENSOR, PRIVATE]

// 各格式下的可用分辨率
Size[] jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
Size[] yuvSizes  = map.getOutputSizes(ImageFormat.YUV_420_888);

// 高帧率配置(>60fps 时需要查这个)
HighSpeedVideoConfiguration[] hsvc =
    map.getHighSpeedVideoConfigurations();

PRIVATE 格式的特殊性

PRIVATE 是一个抽象格式——具体实现由硬件厂商定义。你不能直接读取它的像素

// PRIVATE 的 Surface —— 你只能把它传给 Display 或 MediaCodec
Size[] privSizes = map.getOutputSizes(ImageFormat.PRIVATE);

各格式的选择策略

场景 格式 原因
显示预览 PRIVATE(SurfaceView) 性能最好(硬件 overlay)
需要访问像素 YUV_420_888(ImageReader) CPU 可读
拍照保存 JPEG 直接存文件
RAW 处理 RAW_SENSOR 保留传感器全部信息

4.3 从 Camera2 API 看 HAL 层的映射

App 层                               HAL 层
─────────────────────               ─────────────────────
CameraManager.getCameraIdList()  →   get_number_of_cameras()
CameraCharacteristics           →   get_camera_info() + Static Metadata
CameraDevice.openCamera()       →   camera_module_t.open()
CameraDevice.createCaptureSession() → configure_streams()
CameraCaptureSession.capture()        →   process_capture_request()
CameraCaptureSession.CaptureCallback.onCaptureCompleted() → process_capture_result() 回调
CameraDevice.close()            →   close()

这个映射关系是理解整条 Camera 知识链的关键。

当你写 HAL 层代码时,你处理的每一个函数,都是 App 层某个调用的直接或间接响应。

深入一步:App 的 Request 在 HAL 层长什么样?

App 层的 Java CaptureRequest 到了 HAL 层,变成 C 的结构体 camera3_capture_request_t

// AOSP: hardware/libhardware/include/hardware/camera3.h
typedef struct camera3_capture_request {
    uint32_t                    frame_number;       // 帧序号
    const camera_metadata_t*    settings;           // App 设置的参数(Metadata)
    camera3_stream_buffer_t*    output_buffers;     // 输出 Buffer 列表
    uint32_t                    num_output_buffers; // 输出 Buffer 个数
    const camera_metadata_t*    input_buffer_settings; // 重处理输入参数(可选)
    const camera3_stream_buffer_t* input_buffer;    // 重处理输入 Buffer(可选)
} camera3_capture_request_t;

可以看到:

这就是 App 和 HAL 之间的"协议"——App 发 camera3_capture_request_t,HAL 处理完后回 camera3_capture_result_t


4.4 常见问题

Q: getCameraIdList() 返回的 ID 顺序是什么?

A: 通常是 0=后摄,1=前摄。但不要依赖这个顺序。用 LENS_FACING 判断:

for (String id : manager.getCameraIdList()) {
    int facing = chars.get(CameraCharacteristics.LENS_FACING);
    if (facing == LENS_FACING_BACK) { /* 后摄 */ }
}

Q: 设置的参数为什么不生效?

A: 三种可能:

  1. AE/AF 在自动模式,手动值被覆盖——先关掉自动模式
  2. 传感器不支持该值——用 get*Available*() 先查询
  3. 模板预设了约束——改用 TEMPLATE_MANUAL

Q: 相机启动为什么慢?

A: Camera2 是全异步设计。从 openCamera() 到第一帧预览:

openCamera() → onOpened()        ~200ms   (HAL 初始化)
createSession() → onConfigured()  ~100ms   (Stream 配置)
setRepeatingRequest() → 第一帧     ~50ms   (Sensor 曝光 + ISP 处理)
                                ─────
                                约 350ms  (正常范围)

4.5 本章总结

Camera2 API 的五大核心类
──────────────────────────────────
CameraManager           → 入口,获取相机列表
CameraCharacteristics   → 查询相机能力
CameraDevice            → 代表物理相机连接
CameraCaptureSession    → 管理请求队列
CaptureRequest/Result   → 请求和结果(异步回调模型)
──────────────────────────────────

App 层的每个调用在 HAL 层都有对应
理解这个映射 = 理解 Camera 全栈的钥匙