引言:Camera2 只是冰山一角
如果你花时间学完了 Camera2 API,你大概会有一种感觉:这套 API 设计得挺精妙,但有时候行为很"神秘"——同样的参数,不同手机效果天差地别;明明已经配置了对焦模式,实际却不生效;相机帧率时快时慢……
这些问题的答案,不在 Camera2,而在 Camera HAL3。
Camera2 API 是给应用开发者看的门面,真正干活的是深藏在 HAL 层的代码。HAL3(Hardware Abstraction Layer 3)定义了 Android Framework 和底层相机硬件之间的通信协议,是整个相机系统的"契约"。理解了 HAL3,你才能真正明白一帧图像是怎么从传感器到应用的,才能解释那些"玄学"行为,才能写出真正高质量的相机代码。
本文深入 HAL3 的核心设计:Pipeline 流水线、Request/Result 异步机制、Stream 流与 Buffer 管理,最后以一个 HAL3 Vendor 实现框架收尾。
一、HAL3 设计目标:从 HAL1 的教训说起
1.1 HAL1 的痛点
要理解 HAL3 的设计,先看 HAL1 为什么被废弃。
HAL1(Camera1 时代)是一套同步命令式接口:
// HAL1 接口(简化)
int (*start_preview)(struct camera_device *);
int (*take_picture)(struct camera_device *);
int (*set_parameters)(struct camera_device *, const char *params);
这套接口有几个致命问题:
| 问题 | 表现 | 根因 |
|---|---|---|
| 同步阻塞 | 拍照时预览卡顿 | 全局锁,拍照阻塞预览管道 |
| 无法多流 | 不能同时预览+录像+拍照 | 接口设计不支持多输出 |
| 参数不透明 | set_parameters("key=value") | 字符串参数,无法类型检查 |
| 结果延迟未定义 | 帧率不稳,延迟不确定 | 没有时序约束 |
随着智能手机相机能力爆发式增长——多摄像头、慢动作、ZSL、RAW 格式——HAL1 的局限性越来越明显。于是 Android 5.0 开始强制要求新设备支持 HAL3。
1.2 HAL3 的三大设计原则
原则一:Pipeline 流水线,完全异步
HAL3 的核心是一个单方向的异步 Pipeline:Framework 把 Request 推进去,HAL 把 Result 吐出来,两端完全解耦。Framework 不需要等待单个 Request 完成才能发下一个——就像工厂流水线,每道工序独立运转。
原则二:多流并发,零拷贝
HAL3 在配置阶段就定义好所有输出 Stream(预览/拍照/录像),硬件可以同时向多个 Stream 输出图像,通过 BufferQueue 机制实现零拷贝传递。
原则三:metadata 驱动,精确控制
告别字符串参数,HAL3 使用结构化的 camera_metadata_t 传递所有参数和结果,每个字段都有明确的类型和取值范围,Framework 和 HAL 之间的契约清晰可验证。
二、HAL3 架构全景
2.1 Camera3Device:HAL3 的总调度
Camera3Device(frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp)是 Framework 侧与 HAL 通信的核心类,承担:
- Stream 生命周期管理:创建、配置、销毁 Stream
- Request 队列管理:维护 in-flight request 队列,按序发送给 HAL
- Result 回调分发:接收 HAL 的 Result 回调,分发给上层
- Error 处理:HAL 错误恢复、Request Timeout 检测
// Camera3Device 核心数据结构(简化)
class Camera3Device : public virtual Camera3DeviceBase {
// HAL 设备句柄
sp<hardware::camera::device::V3_2::ICameraDevice> mInterface;
// 已配置的 Stream 列表
camera3_stream_configuration mStreamConfig;
Vector<camera3_stream_t*> mOutputStreams;
// In-flight Request 队列:已提交给 HAL 但未收到 Result 的请求
InFlightMap mInFlightMap;
// Request 线程:专门负责向 HAL 提交 Request
sp<RequestThread> mRequestThread;
// 状态机
enum Status {
STATUS_UNINITIALIZED,
STATUS_UNCONFIGURED,
STATUS_CONFIGURED,
STATUS_ACTIVE,
STATUS_ERROR,
} mStatus;
};
三、HAL3 四大核心接口
HAL3 的接口定义在 hardware/interfaces/camera/device/3.2/ICameraDeviceSession.hal(Android 15 已迁移到 AIDL),核心方法只有四个,但每个都大有学问。
3.1 configureStreams():一切的起点
// HAL 实现侧接口(camera3.h,已被 HIDL/AIDL 封装)
int (*configure_streams)(const struct camera3_device *,
camera3_stream_configuration_t *stream_list);
configureStreams() 做了什么?
- Stream 格式协商:Framework 告诉 HAL 需要哪些输出(预览 1080p YUV、拍照 12MP JPEG、录像 4K H264 Surface),HAL 检查硬件能力,决定是否支持
- Buffer 数量协商:HAL 回填每个 Stream 需要的 Buffer 数量(
usage和max_buffers),Framework 据此向 Gralloc 申请 Buffer - ISP Pipeline 重配置:HAL 内部需要重新配置 ISP 流水线、分配内部缓冲区,这是一个重量级操作(通常需要几百毫秒)
// Stream 配置结构(简化)
typedef struct camera3_stream {
int stream_type; // OUTPUT / INPUT / BIDIRECTIONAL
uint32_t width;
uint32_t height;
int format; // HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, JPEG, YCbCr_420_888...
uint32_t usage; // Gralloc usage flags,HAL 填写
uint32_t max_buffers; // HAL 需要的最大 Buffer 数,HAL 填写
void *priv; // HAL 私有数据
} camera3_stream_t;
坑点:
configureStreams()必须在开始 Repeating Request 之前调用,且调用后所有之前的 Request 状态都会被清空。如果你在录像中途需要新增一个 ImageReader 输出,必须先停止所有 Request,重新调用 configureStreams(),再重新启动。这也是为什么切换相机模式(如从预览切换到慢动作)会有明显的黑屏停顿。
3.2 processCaptureRequest():Request 下发
int (*process_capture_request)(const struct camera3_device *,
camera3_capture_request_t *request);
Framework 每次调用这个接口,就向 HAL 发送一个 Request。关键设计:
非阻塞:此函数必须立即返回,不能等待图像采集完成。HAL 收到 Request 后应该立即返回,异步处理。
// Capture Request 结构
typedef struct camera3_capture_request {
uint32_t frame_number; // 单调递增的帧号,Framework 维护
const camera_metadata_t *settings; // 本帧的参数(曝光/对焦/白平衡...)
camera3_stream_buffer_t *input_buffer; // 输入 Buffer(ZSL Reprocessing 时使用)
uint32_t num_output_buffers; // 输出 Buffer 数量
const camera3_stream_buffer_t *output_buffers; // 输出 Buffer 数组
} camera3_capture_request_t;
frame_number 的重要性:每个 Request 有唯一的 frame_number,对应的 Result 必须用同一个 frame_number 返回,Framework 靠它匹配 Request 和 Result。帧号严格递增,不允许乱序。
Pipeline 深度:HAL 通常在同一时刻处理多个 in-flight 的 Request,这个数量就是 Pipeline 深度(通常 3-8 个),决定了相机的帧率上限和快门延迟。Pipeline 越深,吞吐量越高,但延迟越大。
3.3 processCaptureResult():Result 回传
这是 HAL → Framework 的回调,HAL 完成一帧处理后通过此回调返回结果:
// HAL 调用此回调向 Framework 返回结果
typedef struct camera3_callback_ops {
void (*process_capture_result)(const struct camera3_callback_ops *,
const camera3_capture_result_t *result);
void (*notify)(const struct camera3_callback_ops *,
const camera3_notify_msg_t *msg);
} camera3_callback_ops_t;
// Capture Result 结构
typedef struct camera3_capture_result {
uint32_t frame_number; // 对应的 Request 帧号
const camera_metadata_t *result; // 实际生效的参数(3A 结果等)
uint32_t num_output_buffers;
const camera3_stream_buffer_t *output_buffers; // 已填充数据的 Buffer
const camera3_stream_buffer_t *input_buffer; // ZSL 时归还输入 Buffer
uint32_t partial_result; // 部分结果标记(见下文)
} camera3_capture_result_t;
Partial Result(部分结果):这是 HAL3 的一个高级特性。很多 metadata(如 3A 状态)在图像数据准备好之前就已经可知,HAL 可以先通过 partial_result=1 返回这部分 metadata,而不用等完整图像。这样 App 可以更早更新 UI(如对焦框状态)。
// HAL 分两次返回结果:
// 第一次:早期 metadata(3A 状态),partial_result = 1
result.partial_result = 1;
result.result = early_metadata; // 只含 3A 结果
callbacks->process_capture_result(callbacks, &result);
// 第二次:完整结果(含图像 Buffer),partial_result = 2(等于 partialResultCount)
result.partial_result = 2;
result.result = full_metadata;
result.output_buffers = image_buffers;
callbacks->process_capture_result(callbacks, &result);
3.4 notify():事件通知
HAL 通过 notify() 上报两类事件:
// notify 消息类型
typedef enum camera3_msg_type {
CAMERA3_MSG_ERROR = 1, // 错误事件
CAMERA3_MSG_SHUTTER, // 快门时刻(用于快门音和照片时间戳)
} camera3_msg_type_t;
// 快门消息:记录传感器曝光开始的精确时刻
typedef struct camera3_shutter_msg {
uint32_t frame_number;
uint64_t timestamp; // 纳秒,传感器开始曝光的时间戳
} camera3_shutter_msg_t;
Shutter Timestamp 为什么重要?
CAMERA3_MSG_SHUTTER 中的 timestamp 是相机系统中最精确的时间基准:它记录了传感器开始曝光的精确时刻(硬件时间戳,纳秒级精度)。这个时间戳用于:
- 照片 EXIF 的精确拍摄时间
- 音视频同步(录像时视频帧的 PTS)
- 多摄同步(前后摄像头时间对齐)
四、Request/Result 完整流转
一帧图像从 App 发出请求到拿到图像,经历以下步骤:
① App 调用 capture(CaptureRequest)
↓
② Camera3Device 将 CaptureRequest 转换为 camera3_capture_request_t
调用 processCaptureRequest() 发给 HAL(立即返回,非阻塞)
↓
③ Camera3Device 向 BufferQueue 申请空 Buffer(dequeueBuffer)
填入 output_buffers,此时 Buffer 所有权交给 HAL
↓
④ HAL 内部:Sensor 曝光 → ISP 处理(降噪/色彩/锐化)→ 写入 Buffer
同时 notify() 上报 SHUTTER 时间戳
↓
⑤ HAL 完成处理,调用 processCaptureResult() 回传结果
Buffer 所有权归还 Framework(queueBuffer)
↓
⑥ Camera3Device 收到 Result,匹配对应 frame_number
通知 CaptureCallback:onCaptureCompleted(CaptureResult)
↓
⑦ App 收到回调,从 ImageReader 消费图像(acquireLatestImage)
4.1 In-Flight Request 追踪
Camera3Device 维护一个 InFlightMap,记录所有已发送给 HAL 但未收到完整 Result 的 Request:
struct InFlightRequest {
nsecs_t shutterTimestamp; // 快门时间戳
bool shutterNotified; // 是否收到 SHUTTER 通知
CaptureResultExtras resultExtras;
CameraMetadata collectedResult; // 累积 partial results
Vector<StreamBuffer> pendingOutputBuffers; // 待收回的 Buffer
int numBuffersLeft; // 还有几个 Buffer 未返回
};
当 HAL 多次返回 partial result 时,Camera3Device 会把它们合并进 collectedResult,等 partial_result == partialResultCount 时才认为这帧处理完成。
4.2 Request Throttle 控制
Framework 不会无限制地向 HAL 堆积 Request。Camera3Device 通过 Pipeline 深度限制进行流量控制:
// 获取 HAL 支持的 Pipeline 深度
int pipelineDepth = staticInfo(ANDROID_REQUEST_PIPELINE_MAX_DEPTH);
// 通常为 4,意味着同一时刻最多 4 个 Request 在 HAL 处理中
// RequestThread 在发送新 Request 前检查
if (mInFlightMap.size() >= pipelineDepth) {
// 等待 HAL 返回至少一个 Result 再继续
waitForResult();
}
这个机制保证了 HAL 不会被淹没,同时也解释了为什么相机的真实帧率 = min(传感器最大帧率, 1秒 / 单帧处理时间 × PipelineDepth)。
五、Stream 流管理:三种类型
HAL3 定义了三种 Stream 类型,满足不同的使用场景:
5.1 OUTPUT Stream(最常用)
数据从 HAL 流向 App,是最常见的类型。预览、拍照、录像都是 OUTPUT Stream。
// App 侧创建 OUTPUT Stream(通过 Surface)
// 预览 Surface(TextureView)
Surface previewSurface = new Surface(surfaceTexture);
// 拍照 Surface(ImageReader)
ImageReader imageReader = ImageReader.newInstance(4032, 3024,
ImageFormat.JPEG, 2);
Surface captureSurface = imageReader.getSurface();
// 提交给 CameraDevice,内部转化为 OUTPUT Stream 传给 HAL
device.createCaptureSession(Arrays.asList(previewSurface, captureSurface), ...);
5.2 INPUT Stream(ZSL 专用)
数据从 App 流向 HAL,用于 ZSL Reprocessing——把之前缓存的图像重新送进 HAL 进行后处理(HDR 合成、降噪等)。
// ZSL 需要同时配置 INPUT 和 OUTPUT Stream
ImageWriter imageWriter = ImageWriter.newInstance(reprocessSurface, 2);
ImageReader zslReader = ImageReader.newInstance(width, height,
ImageFormat.YUV_420_888, 8);
SessionConfiguration config = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
Arrays.asList(new OutputConfiguration(zslReader.getSurface())),
executor, callback
);
config.setInputConfiguration(
new InputConfiguration(width, height, ImageFormat.YUV_420_888)
);
5.3 BIDIRECTIONAL Stream(罕见)
同一个 Stream 既可作为输入也可作为输出,主要用于 in-place 处理(读一帧、处理后写回同一 Buffer),实际应用场景较少。
5.4 Stream 格式选择技巧
选错格式会触发 HAL 内部的软件转换,严重影响性能:
// 预览:推荐 IMPLEMENTATION_DEFINED(让 HAL 自选最优格式,通常是私有 YUV)
OutputConfiguration previewConfig = new OutputConfiguration(previewSurface);
// 底层格式:HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED → 实际多为 NV12
// 从预览帧做处理(如人脸检测):明确指定 YUV_420_888
ImageReader yuvReader = ImageReader.newInstance(w, h,
ImageFormat.YUV_420_888, 4);
// 底层:HAL_PIXEL_FORMAT_YCbCr_420_888
// 拍照:JPEG(节省内存)或 RAW_SENSOR(专业模式)
ImageReader jpegReader = ImageReader.newInstance(w, h, ImageFormat.JPEG, 2);
ImageReader rawReader = ImageReader.newInstance(w, h, ImageFormat.RAW_SENSOR, 3);
六、Buffer 管理:深入 Gralloc
6.1 BufferQueue 机制回顾
HAL3 的 Buffer 流转依托 Android 的 BufferQueue 机制——生产者消费者模型:
HAL(生产者) BufferQueue App(消费者)
│ │ │
│ dequeueBuffer() │ │
│◄───────────────────┤ │
│ (获取空 Buffer) │ │
│ │ │
│ [写入图像数据] │ │
│ │ │
│ queueBuffer() │ │
│───────────────────►│ │
│ (归还已填充Buffer) │ │
│ │ acquireBuffer() │
│ │◄───────────────────┤
│ │ (取走已填充Buffer) │
│ │ │
│ │ (消费完成) │
│ │ releaseBuffer() │
│ │◄───────────────────┤
6.2 Gralloc:内存分配器
Gralloc(Graphics Memory Allocator)负责分配 GPU/ISP 可访问的共享内存:
// HAL 侧 Buffer 操作(camera3_stream_buffer_t)
typedef struct camera3_stream_buffer {
camera3_stream_t *stream; // 归属的 Stream
buffer_handle_t *buffer; // Gralloc 句柄(跨进程共享)
int acquire_fence; // HAL 等待此 fence 信号后才能访问 Buffer(Buffer 准备好的信号)
int release_fence; // HAL 写入完成后设置此 fence(告知消费者可以读了)
int status; // CAMERA3_BUFFER_STATUS_OK 或 ERROR
} camera3_stream_buffer_t;
Fence 机制:Gralloc Buffer 配合 sync fence(同步栅栏)实现零拷贝的异步访问。HAL 不需要轮询 Buffer 是否可用,而是等待 acquire_fence 信号(由 GPU/显示子系统设置),完成写入后设置 release_fence(通知消费者)。这避免了不必要的 CPU 等待,大幅降低延迟。
6.3 Buffer 复用策略
Camera3Device 维护一个 Buffer 池,对每个 Stream 的 Buffer 进行复用:
// Buffer 分配:configureStreams() 阶段
// HAL 声明 max_buffers = 4,则 Framework 预分配 4+2 个 Buffer
// (额外 2 个是给 App 消费用的,保证流水不断)
// 运行时:Buffer 在三方之间循环
// [App 持有] → releaseImage() → [BufferQueue 空闲池]
// ↑ ↓
// acquireLatestImage() dequeueBuffer() 给 HAL
// ↓
// [HAL 填充] → queueBuffer()
七、Android 15 的演进:HIDL → AIDL
7.1 为什么要从 HIDL 迁移到 AIDL?
Android 12 开始,Google 推动将 Camera HAL 接口从 HIDL(Hardware Interface Definition Language)迁移到 AIDL(Android Interface Definition Language):
| 对比项 | HIDL | AIDL(稳定版) |
|---|---|---|
| 接口描述 | .hal 文件,Hidl-gen 生成 | .aidl 文件,Aidl-cpp/java 生成 |
| 版本管理 | 不可变,每次改接口需要新版本号 | 支持可向后兼容的接口演进 |
| 性能 | 独立 hwbinder | 与 binder 统一,减少开销 |
| 工具链 | Hidl 独立工具链 | 统一 Aidl 工具链 |
| Android 15 状态 | 逐步废弃 | 推荐使用 |
7.2 Android 15 Camera AIDL 接口
// AIDL 接口路径(Android 15)
hardware/interfaces/camera/device/aidl/
android/hardware/camera/device/
ICameraDevice.aidl
ICameraDeviceSession.aidl
ICameraDeviceCallback.aidl
// 核心方法(与 HIDL 语义等同,但语法不同)
interface ICameraDeviceSession {
ConfigureStreamsRet configureStreams(
in StreamConfiguration requestedConfiguration);
void processCaptureRequest(
in CaptureRequest[] requests,
in BufferCache[] cachesToRemove,
out SubmitInfo submitInfo);
// ...
}
interface ICameraDeviceCallback {
void processCaptureResult(in CaptureResult[] results);
void notify(in NotifyMsg[] msgs);
void requestStreamBuffers(in BufferRequest[] bufReqs,
out StreamBuffersVal[] buffers);
void returnStreamBuffers(in StreamBuffer[] buffers);
}
Android 15 新增:requestStreamBuffers / returnStreamBuffers
这是 HAL3.5 引入的 Buffer Management API,允许 HAL 主动向 Framework 请求和归还 Buffer,而不必被动等待 Framework 传递:
// 传统模式:Framework 在 processCaptureRequest 时传入 Buffer
// 新模式:HAL 按需主动请求 Buffer
HAL Framework
│ requestStreamBuffers() │
│────────────────────────────────►│
│ (我需要 N 个 Buffer) │
│◄────────────────────────────────┤
│ (给你 N 个 Buffer 句柄) │
│ │
│ [完成处理] │
│ returnStreamBuffers() │
│────────────────────────────────►│
好处:HAL 可以更灵活地管理 Buffer 生命周期,对于有内部 Buffer 池的 ISP 实现(如高通 CamX),可以进一步减少不必要的内存拷贝。
八、HAL3 实现框架:Vendor 如何落地
了解接口之后,来看 Vendor 是如何实现的。以高通 CamX 为例(其他平台架构类似):
8.1 典型的 Vendor HAL 目录结构
vendor/qcom/proprietary/camx/ # 高通 CamX 框架
├── src/
│ ├── core/
│ │ ├── camxsession.cpp # Session 管理,对应 Camera3Device
│ │ ├── camxpipeline.cpp # Pipeline 管理,ISP 流水线
│ │ ├── camxnode.cpp # Node 基类(传感器/ISP/后处理节点)
│ │ └── camxrequest.cpp # Request/Result 管理
│ ├── hwl/
│ │ ├── isp/ # ISP 节点
│ │ ├── sensor/ # Sensor 节点
│ │ └── jpeg/ # JPEG 编码节点
│ └── chi-cdk/ # CHI(Camera Hardware Interface)
│ └── core/
│ └── chioverride.cpp # HAL3 接口适配层
8.2 一个简化的 HAL3 实现骨架
// 1. HAL 模块注册(camera_module_t)
static camera_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "Example Camera HAL3",
},
.get_number_of_cameras = hal3_get_number_of_cameras,
.get_camera_info = hal3_get_camera_info,
.open_legacy = nullptr,
};
// 2. 打开相机设备
static int hal3_open(const hw_module_t *module, const char *id,
hw_device_t **device) {
ExampleCameraDevice *cam = new ExampleCameraDevice(atoi(id));
// 填充 camera3_device_t
cam->device.common.tag = HARDWARE_DEVICE_TAG;
cam->device.common.version = CAMERA_DEVICE_API_VERSION_3_5;
cam->device.ops = &hal3_ops; // 绑定操作函数
cam->device.priv = cam;
*device = &cam->device.common;
return 0;
}
// 3. 核心操作表
static camera3_device_ops_t hal3_ops = {
.initialize = hal3_initialize,
.configure_streams = hal3_configure_streams,
.register_stream_buffers = nullptr, // HAL3.2+ 废弃
.construct_default_request_settings = hal3_construct_default_settings,
.process_capture_request = hal3_process_capture_request,
.get_metadata_vendor_tag_ops = nullptr,
.dump = hal3_dump,
.flush = hal3_flush,
};
// 4. configure_streams 实现
static int hal3_configure_streams(const camera3_device_t *dev,
camera3_stream_configuration_t *config) {
ExampleCameraDevice *cam = (ExampleCameraDevice*)dev->priv;
// 检查 Stream 组合是否支持
for (uint32_t i = 0; i < config->num_streams; i++) {
camera3_stream_t *s = config->streams[i];
// 填写 HAL 侧参数
s->usage = GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_CAMERA_WRITE;
s->max_buffers = 4; // 告诉 Framework 最多缓存 4 帧
}
// 重新配置 ISP Pipeline
cam->isp->reconfigure(config);
return 0;
}
// 5. process_capture_request 实现(异步!)
static int hal3_process_capture_request(const camera3_device_t *dev,
camera3_capture_request_t *request) {
ExampleCameraDevice *cam = (ExampleCameraDevice*)dev->priv;
// 解析 metadata(曝光/对焦/白平衡参数)
camera_metadata_ro_entry entry;
find_camera_metadata_ro_entry(request->settings,
ANDROID_CONTROL_AE_TARGET_FPS_RANGE, &entry);
// 把 Request 放入内部队列,立即返回
cam->requestQueue.push(CopyRequest(request));
// 后台线程异步处理
// ProcessingThread 会从队列取出 Request,驱动 ISP,然后回调 processCaptureResult
return 0;
}
8.3 HAL 内部 Processing Thread
真正的图像处理在独立线程中完成:
void ExampleCameraDevice::ProcessingThread() {
while (running) {
// 1. 取出待处理的 Request
PendingRequest req = requestQueue.pop();
// 2. 等待 Buffer acquire fence(Buffer 已准备好供 HAL 写入)
for (auto &buf : req.outputBuffers) {
if (buf.acquire_fence != -1) {
sync_wait(buf.acquire_fence, FENCE_TIMEOUT_MS);
close(buf.acquire_fence);
buf.acquire_fence = -1;
}
}
// 3. 驱动传感器曝光,获取原始图像
RawImage raw = sensor->capture(req.settings);
// 4. 上报 SHUTTER 时间戳(非常重要!)
camera3_notify_msg_t shutter;
shutter.type = CAMERA3_MSG_SHUTTER;
shutter.message.shutter.frame_number = req.frameNumber;
shutter.message.shutter.timestamp = raw.sensorTimestamp;
callbacks->notify(callbacks, &shutter);
// 5. ISP 处理:降噪、锐化、色彩映射
for (auto &buf : req.outputBuffers) {
void *ptr = lockBuffer(buf.buffer);
isp->process(raw, buf.stream->format, ptr);
unlockBuffer(buf.buffer, &buf.release_fence); // 设置 release fence
}
// 6. 构造 Result metadata(实际 3A 结果)
camera_metadata_t *result_meta = buildResultMetadata(raw.stats);
// 7. 回调 processCaptureResult
camera3_capture_result_t result;
result.frame_number = req.frameNumber;
result.result = result_meta;
result.output_buffers = req.outputBuffers.data();
result.num_output_buffers = req.outputBuffers.size();
result.partial_result = 1; // 只有一次返回,直接是最终结果
callbacks->process_capture_result(callbacks, &result);
free_camera_metadata(result_meta);
}
}
九、实战:用 adb 诊断 HAL3 问题
遇到相机问题,第一步永远是看 HAL3 层的状态。
9.1 查看 HAL3 接口调用情况
# 查看 Camera3Device 当前状态(最有用的命令)
adb shell dumpsys media.camera
# 关键输出解读:
# "Active session" → 当前有 App 使用相机
# "In-flight requests" → 当前在 HAL 处理中的帧数(正常应该 < pipelineDepth)
# "Output streams" → 当前配置的 Stream 列表
# "Repeating request" → 当前的 Repeating Request 配置
# 实时抓取 HAL 层日志(需要 root 或开发者设备)
adb shell setprop log.tag.Camera3Device VERBOSE
adb logcat -s Camera3Device:V CameraHAL:V
9.2 典型问题案例
问题:预览帧率突然下降到 15fps
# 查看当前帧率
adb logcat -s Camera3Device | grep "Capture result"
# HAL3 层日志中搜索 Throttle 相关
adb logcat -s CameraHAL | grep "throttle\|pipeline\|backpressure"
# 根因通常是:
# 1. in-flight requests 积压(HAL 处理慢)→ pipelineDepth 不足或 ISP 过载
# 2. Buffer 不够用(Gralloc 分配失败)→ max_buffers 设置太小
# 3. Thermal 降频 → ISP 降低处理频率
问题:configureStreams 失败(ERROR_INVALID_OPERATION)
// 常见原因:
// 1. 请求的 Stream 组合超出 HAL 能力(如同时要求 4K + 12MP + 高速帧率)
// 解决:先查询 StreamConfigurationMap
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// 检查是否支持该 Size + Format 组合
Size[] sizes = map.getOutputSizes(ImageFormat.YUV_420_888);
// 2. Session 尚未完全关闭就重新 configure(常见于横竖屏切换)
// 解决:在 onClosed() 回调后再重新配置
9.3 使用 Perfetto 抓 Camera Pipeline 耗时
# 抓取 Camera HAL 的 trace(Android 10+)
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/camera.pftrace \
<<EOF
buffers: { size_kb: 65536 }
data_sources: { config { name: "android.camera" } }
data_sources: { config { name: "linux.ftrace"
ftrace_config { ftrace_events: "camera/*" }
} }
duration_ms: 10000
EOF
# 在 https://ui.perfetto.dev/ 分析
# 重点看:
# - configureStreams 耗时(正常 < 200ms,如果 > 500ms 说明 ISP 重配置很慢)
# - processCaptureRequest → processCaptureResult 间隔(即单帧处理时间)
# - Buffer 等待 fence 的时间(acquire fence 阻塞时间过长说明 GPU/ISP 竞争)
十、Android 15 新特性:Ultra HDR 与多摄同步
10.1 Ultra HDR 的 HAL 支持
Android 15 中,Ultra HDR 拍照需要 HAL3 同时输出两个流:
// Ultra HDR 需要同时配置 JPEG/R 格式的 Stream
OutputConfiguration ultraHdrOutput = new OutputConfiguration(
new Size(4032, 3024), ImageReader.class
);
// JPEG_R 格式:在 JPEG 中内嵌 HDR gainmap
ImageReader ultraHdrReader = ImageReader.newInstance(
4032, 3024, ImageFormat.JPEG_R, 2
);
HAL 内部需要在 processCaptureRequest 中同时:
- 输出标准 SDR JPEG(向下兼容)
- 输出 HDR gainmap 数据并合成为 JPEG/R
10.2 多摄同步的 HAL 实现
逻辑多摄(如广角+长焦同时运行)需要多个物理相机的 HAL 严格同步:
// 多摄同步的关键:SENSOR_SYNC_TYPE
// HAL 需要在 StaticMetadata 中声明同步类型
ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE // 近似同步
| ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED // 精确校准同步
// 物理摄像头的 Result 必须携带相同的 timestamp
// Camera3Device 会将物理摄像头 result 合并到逻辑摄像头 result
总结
Camera HAL3 是整个 Android 相机系统最核心的接口层,它用四个接口(configureStreams / processCaptureRequest / processCaptureResult / notify)定义了一套优雅的异步 Pipeline 协议。
本文核心要点:
- HAL3 设计精髓:全异步 Pipeline,多流并发,metadata 驱动——三个原则解决了 HAL1 的所有痛点
- Request/Result 机制:frame_number 一一对应,Partial Result 提前返回早期 metadata,Pipeline 深度控制吞吐与延迟的权衡
- Stream 类型:OUTPUT(常用)/ INPUT(ZSL)/ BIDIRECTIONAL,格式选择直接影响 ISP 是否需要软件转换
- Buffer 管理:BufferQueue + Gralloc + sync fence 三位一体,实现跨进程零拷贝和异步同步
- Android 15 演进:HIDL → AIDL 迁移,Buffer Management API 让 HAL 主动申请 Buffer,更灵活高效
- 调试技巧:
dumpsys media.camera看实时状态,Perfetto 分析 Pipeline 耗时,logcat Camera3Device 追踪 in-flight 请求
理解了 HAL3,再去看高通 CamX、MTK 的相机 HAL 实现,你会发现它们都是在这套框架上的具体落地。而那些曾经困扰你的"玄学行为"——比如为什么某款手机的对焦特别准、为什么有的设备在夜景下帧率更稳——都能在 HAL3 的实现细节中找到答案。
下一篇我们进入 MediaCodec 编解码基础——视频录制和播放的核心组件。