2. Camera HAL接口演进

各位同学,今天我们来聊聊Camera HAL接口的演进史。说实话,我刚接触Android Camera时,HAL1还是主流。那时候做相机开发,感觉就像在刀尖上跳舞——性能差、延迟高、还经常掉帧。后来Google推了HAL3,整个局面才彻底改变。

为什么会有这样的变化?说白了,就是手机拍照需求越来越复杂。你想想看,从单纯的拍照录像,到现在的HDR、夜景、人像模式、AI美颜……HAL1那套老架构根本扛不住。Google也是被逼无奈,才重新设计了HAL3。

HAL1的痛点:为什么它被淘汰了?

HAL1的设计其实很简单——一个摄像头设备,一次只能做一件事。要么预览,要么拍照,要么录像。你想同时做两件事?对不起,不行。

HAL1的核心问题:

  • 单线程模型:所有操作串行执行,无法并发
  • 无缓冲管理:应用层直接操作硬件buffer,容易出问题
  • 功能受限:不支持多帧合成、不支持逐帧参数控制
  • 扩展性差:厂商想加新功能?得改HAL接口,Google不答应

我记得有一次做项目,客户要求在预览的同时抓拍一张高分辨率照片。用HAL1实现?那叫一个痛苦。先停止预览,再切换模式拍照,拍完再切回预览。中间那几秒黑屏,客户直接投诉。嗯,这就是HAL1的硬伤。

HAL3的诞生:Google的破局之道

HAL3的设计理念,说白了就四个字:流水线并行。Google把整个相机系统重新抽象,引入了两个核心概念——Pipeline模型和Request/Result机制。

Pipeline模型:让数据流动起来

Pipeline模型,你可以把它想象成一条工厂流水线。每个工位(Stage)负责一道工序,数据从一端流入,经过各个工位处理,最终从另一端输出。关键是什么?多个工位可以同时工作!

我的理解:Pipeline模型就像CPU的指令流水线。一条指令在执行,下一条已经在解码,再下一条已经在取指。这样吞吐量就上去了。Camera也是这个道理。

在HAL3中,Pipeline分为三个主要阶段:

  • Capture Stage:传感器曝光,输出原始数据
  • Processing Stage:ISP处理,包括去噪、色彩校正、缩放等
  • Output Stage:输出最终图像到不同目标(预览、拍照、录像)

这三个阶段可以并行执行。比如,传感器正在曝光第N帧,ISP同时处理第N-1帧,而第N-2帧已经输出到屏幕。这样帧率就上去了。

Request/Result机制:精准控制每一帧

这是HAL3最核心的设计。应用层通过发送CaptureRequest来告诉HAL:我要拍一帧,参数是这样的——曝光时间、ISO、白平衡、对焦位置……HAL处理完后,返回一个CaptureResult,包含实际使用的参数和图像数据。

// HAL3的Request/Result交互示例
// 应用层发送请求
CaptureRequest request = new CaptureRequest();
request.set(CaptureRequest.SENSOR_EXPOSURE_TIME, 10000000L); // 10ms
request.set(CaptureRequest.SENSOR_SENSITIVITY, 400);        // ISO 400
request.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF); // 手动曝光

// HAL处理并返回结果
CaptureResult result = cameraDevice.capture(request);
long actualExposure = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
int actualIso = result.get(CaptureResult.SENSOR_SENSITIVITY);
// 实际参数可能与请求参数不同(受硬件限制)

避坑指南:我曾经遇到过一个坑——Request中的参数不一定会被HAL完全采纳。比如你请求ISO 100,但硬件最低只能到ISO 200,那HAL会返回ISO 200。所以一定要检查Result中的实际值,别想当然。

这个机制的好处是什么?每一帧都可以独立控制参数。你想拍HDR?发三个Request,分别用不同曝光时间。HAL会返回三帧结果,你在应用层合成。这在HAL1时代想都不敢想。

为什么Google要推HAL3?

这个问题,我个人的看法是:Google被逼无奈,也是顺势而为。

先说被逼无奈。2012-2014年,手机拍照竞争白热化。HDR、夜景、连拍、慢动作……这些功能HAL1根本支持不了。厂商只能自己魔改HAL,导致碎片化严重。Google想统一生态,必须推新接口。

再说顺势而为。当时硬件也在进步——多核ISP、大容量buffer、高速接口……这些硬件能力HAL1用不上,太浪费了。HAL3的设计,说白了就是让软件能充分利用硬件。

HAL3带来的核心价值:

  1. 高性能:Pipeline并行,帧率提升2-3倍
  2. 低延迟:Request/Result机制,逐帧控制,延迟降低50%以上
  3. 灵活性:支持多帧合成、逐帧参数调整、多路输出
  4. 可扩展:厂商可以通过扩展metadata来添加新功能,不影响接口稳定性
  5. 跨平台:同一套接口,从手机到车载到安防都能用

我记得2015年做第一个HAL3项目时,心里还犯嘀咕:这玩意儿靠谱吗?结果一跑起来,预览延迟从HAL1的120ms降到了40ms,连拍速度从3fps飙到了15fps。当时我就知道,HAL1回不去了。

HAL3的核心架构图

下面这张图,是我自己画的HAL3核心架构。你看一眼就能明白Pipeline和Request/Result是怎么配合的。

HAL3 Pipeline模型与Request/Result机制 应用层(Camera App / Framework) 发送 CaptureRequest → 接收 CaptureResult Request Result Camera HAL3 实现 Stage 1: Capture 传感器曝光 输出RAW数据 参数:曝光、ISO、对焦 Stage 2: Processing ISP处理 去噪、色彩校正 缩放、格式转换 Stage 3: Output 输出到目标 预览/拍照/录像 多路输出 三个Stage可以并行执行! 例如:Stage1曝光第N帧,Stage2处理第N-1帧,Stage3输出第N-2帧 Buffer队列管理 每个Stage都有独立的Buffer队列,避免阻塞 图:HAL3 Pipeline模型,三个Stage并行工作,通过Buffer队列解耦

这张图你看懂了吗?应用层发Request下来,HAL3把它拆成三个Stage并行处理。每个Stage都有自己的Buffer队列,互不阻塞。最终Result返回给应用层。整个过程就像一条高效的流水线。

从HAL1到HAL3:一个真实的迁移案例

我2016年做过一个项目,把一款老手机从HAL1迁移到HAL3。那款手机的Sensor是IMX214,ISP是骁龙810内置的。迁移前,预览延迟120ms,连拍3fps,HDR需要3秒合成。迁移后,预览延迟降到35ms,连拍15fps,HDR实时合成只要0.5秒。

关键改动就三点:

  1. 实现Pipeline:把原来的单线程改成三阶段流水线
  2. 支持Request/Result:每个Request携带完整参数,Result返回实际值
  3. 优化Buffer管理:用Gralloc分配GraphicBuffer,减少内存拷贝

嗯,这里要注意——迁移过程中最头疼的是metadata的适配。HAL3有几百个metadata tag,每个都要正确实现。我当时漏了一个ANDROID_SENSOR_TIMESTAMP,结果预览画面一直卡顿。查了两天才发现,原来是时间戳没对齐。

我的建议:如果你要迁移到HAL3,先别急着写代码。把Google的camera_metadata_tags.h从头到尾读一遍,搞清楚每个tag的用途。尤其是ANDROID_CONTROL_*和ANDROID_SENSOR_*这两类,最容易出问题。

总结:HAL3到底好在哪?

说了这么多,总结一下。HAL3的核心设计理念,就是用Pipeline模型解决性能问题,用Request/Result机制解决灵活性问题。Google推HAL3,不是为了炫技,而是因为手机拍照的需求已经超出了HAL1的能力范围。

我个人觉得,HAL3最厉害的地方不是性能提升,而是给了应用层对每一帧的完全控制权。你想拍什么效果,自己发Request就行。HAL只负责执行,不替你做决定。这种设计思路,后来也被用到CameraX和Camera2 API中。

好了,这一章就到这里。下一章我们会深入HAL3的代码实现,看看Pipeline到底是怎么跑起来的。

本章要点回顾:

  • HAL1的痛点:单线程、无缓冲管理、功能受限
  • HAL3的Pipeline模型:三阶段并行,提升吞吐量
  • Request/Result机制:逐帧控制,灵活精准
  • Google推HAL3的原因:性能、延迟、灵活性、可扩展性