Qcom Camx全栈开发

Android Camera 全栈课程(Qcom Camx):第 4 章:Camera2 API 核心概念

本章导读

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

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

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

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

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

4-camera2.png

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_BACK = 0, LENS_FACING_FRONT = 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[] 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(CaptureSession session,
            CaptureRequest request, long timestamp, long frameNumber) {
        // ISP 开始处理这一帧
    }

    @Override
    public void onCaptureCompleted(CaptureSession 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(CaptureSession session,
            CaptureRequest request, CaptureFailure failure) {
        // 处理失败
        int reason = failure.getReason();
        // CAPTURE_FAILURE_REASON_ERROR    = 0  硬件错误
        // CAPTURE_FAILURE_REASON_FLUSHED  = 1  被 flush 取消
    }
};

💡 onCaptureStarted 到 onCaptureCompleted 的时间差

这个差值就是 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()
CaptureSession.capture()        →   process_capture_request()
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;

可以看到:

  • settings = App 的 CaptureRequest.Builder.set() 设置的所有参数(serialized as CameraMetadata)

  • output_buffers = App 的 addTarget(surface) 对应的 Stream Buffer

  • frame_number = HAL 内部用于跟踪每帧

这就是 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 全栈的钥匙

(课程相关内容,会陆续在下面专栏更新,感兴趣的同学可以扫码订阅)

欢迎扫码关注「小驰行动派」公众号 10 年Camera开发 | Camera技术干货 | 行业洞察 | Camera实战分享
小驰行动派公众号 扫一扫关注
分享到: 复制链接
← 上一篇 Android Camera 全栈课程(Qcom Camx):Camera2 实战 — 预览/拍照/录像 下一篇 → 纯小白教程 | 10分钟,在Vs Code中接入Claude Code,使用Deepseek API

相关文章

推荐课程

想系统学习 Camera 开发?看看这些课程

评论 (0)

暂无评论,快来抢沙发吧