本章导读
你可能觉得:"我是做 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.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: 三种可能:
AE/AF 在自动模式,手动值被覆盖——先关掉自动模式
传感器不支持该值——用 get*Available*() 先查询
模板预设了约束——改用 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 全栈的钥匙
(课程相关内容,会陆续在下面专栏更新,感兴趣的同学可以扫码订阅)
评论 (0)