相机系统(CameraService):Camera HAL3架构、Camera启动与预览流程、拍照与视频录制、Camera权限与安全
相机系统,说实话,是Android Framework里最复杂的一块之一。我早年刚接触Camera HAL3时,也被它的Pipeline设计绕得晕头转向。但一旦你理解了它的设计哲学,你会发现它其实非常优雅——它把控制权完全交给了上层,让App能精细控制每一帧的捕获参数。
Camera HAL3架构:从“黑盒”到“管道”
在HAL3之前,HAL1是个典型的黑盒。App调用open、startPreview、takePicture,底层驱动怎么干活,上层完全不知道。HAL3不一样,它把相机抽象成一条捕获管道(Capture Pipeline)。
说白了,HAL3的核心就三个概念:CameraDevice、CaptureRequest、CaptureResult。
- CameraDevice:代表物理相机设备。每个摄像头对应一个Device。
- CaptureRequest:你告诉相机“我想要什么”。包含输出目标(Surface)、控制参数(AE、AF、AWB等)。
- CaptureResult:相机告诉你“我拍到了什么”。包含实际使用的参数、时间戳、元数据。
我习惯把HAL3比作一个“可编程的流水线”。你可以往流水线上放不同的Request,每个Request可以指定不同的输出Surface。比如一个Request同时输出到预览Surface和拍照Surface,这就是“同帧输出”。
关键点:HAL3的Pipeline是异步的。你提交一个Request,不会阻塞等待结果。结果通过回调返回。这种设计让高帧率、低延迟成为可能。
下面这张图展示了HAL3的核心数据流:
Camera启动与预览流程:从App到Sensor的旅程
你按下App里的“打开相机”按钮,背后发生了什么?我拆解一下这个流程,你会发现每一步都有讲究。
- App调用CameraManager.openCamera():这其实是个异步调用。CameraService收到请求后,会去HAL层打开设备。
- CameraService创建CameraDevice:内部会初始化一个CameraDeviceClient,负责与App通信。
- App创建CaptureSession:通过createCaptureSession(),把预览Surface传进去。CameraService会调用HAL的configureStreams(),告诉底层“我要往这些Surface上送数据”。
- App提交重复Request:setRepeatingRequest(),告诉HAL“持续往预览Surface送帧”。
- HAL开始送帧:底层驱动每拿到一帧数据,就通过HAL回调往上送,最终到达SurfaceView或TextureView。
我的经验:预览启动慢,很多时候卡在configureStreams()这一步。尤其是当你同时配置了多个输出Surface(比如预览+拍照+视频),HAL需要重新协商Stream配置。我建议尽量复用Stream配置,不要每次拍照都重新创建Session。
这里有个细节:预览Surface必须是ANativeWindow。App端传过来的Surface,最终会映射到BufferQueue的生产者端。HAL作为消费者,从BufferQueue里拿Buffer填充数据。
拍照与视频录制:单帧与连续帧的博弈
拍照和视频录制,本质区别在于:拍照是单次CaptureRequest,视频是连续的CaptureRequest。
拍照流程:
- App构造一个CaptureRequest,设置TEMPLATE_STILL_CAPTURE模板。
- 调用capture()提交这个Request。注意,这不会中断预览的RepeatingRequest。
- HAL处理完这个Request后,通过CaptureResult回调返回JPEG数据或YUV数据。
- App拿到数据后,可以保存到文件或进一步处理。
视频录制流程:
- App需要创建一个MediaCodec或MediaRecorder的Surface,作为输出目标。
- 在createCaptureSession时,把这个Surface加进去。
- 提交RepeatingRequest,让HAL持续往这个Surface送帧。
- MediaCodec/MediaRecorder从Surface读取数据,编码成视频流。
注意:我曾经遇到过一个坑——视频录制时预览卡顿。原因是预览Surface和录制Surface的Buffer格式不匹配。预览通常用RGBA_8888,录制用YUV_420_888。HAL需要做格式转换,如果硬件不支持,就会走软件转换,性能直接崩掉。解决办法是尽量让两个Surface使用相同的Buffer格式,或者确认硬件支持直接输出多种格式。
说到拍照,还有一个重要概念:ZSL(Zero Shutter Lag,零快门延迟)。ZSL的原理是:预览时HAL一直在内部环形缓冲区里缓存最近几帧。用户按下快门时,直接从缓冲区里取一帧,而不是重新捕获。这样快门延迟几乎为零。
但ZSL也有代价——它需要额外的内存和带宽。低端设备上,我建议关闭ZSL,否则拍照时预览帧率会掉得很厉害。
Camera权限与安全:别让相机成为后门
相机权限,不只是AndroidManifest里加个android.permission.CAMERA那么简单。从Android 6.0开始,权限变成了运行时权限。从Android 10开始,还有隐私保护的加强。
我整理了一下相机相关的权限体系:
| 权限 | 级别 | 说明 |
|---|---|---|
| android.permission.CAMERA | 危险权限 | 必须运行时申请。没有这个权限,CameraManager.openCamera()会抛SecurityException。 |
| android.permission.RECORD_AUDIO | 危险权限 | 录制视频时如果需要录音,必须申请。 |
| android.permission.ACCESS_FINE_LOCATION | 危险权限 | 如果App需要读取照片的EXIF地理位置信息,需要申请。 |
| android.permission.CAPTURE_VIDEO_OUTPUT | 系统权限 | 只有系统App才能用。用于截屏或录屏。 |
安全方面,Android Framework做了几层保护:
- 进程隔离:CameraService运行在独立的mediaserver进程。App不能直接访问HAL,必须通过Binder IPC。
- 权限检查:CameraService在openCamera()时会检查调用方的PID/UID是否有CAMERA权限。
- Surface验证:App传过来的Surface,CameraService会验证它是否属于当前App的进程。防止恶意App通过传别人的Surface来偷数据。
- 隐私指示灯:Android 12+要求,相机打开时必须有明显的指示(比如状态栏图标或LED灯)。
避坑指南:我曾经遇到一个Case——App在后台偷偷打开相机被系统杀掉。原因是Android 11引入了后台相机限制。除非App有android.permission.CAMERA且用户在前台可见,否则CameraService会拒绝打开相机。如果你的App需要在后台使用相机(比如视频通话的悬浮窗),必须使用MediaProjection或Foreground Service。
还有一个容易被忽略的点:相机元数据泄露。CaptureResult里包含了很多传感器信息,比如焦距、光圈、曝光时间。恶意App可以通过这些信息推断用户的环境。所以Google在Android 10+中,对非系统App隐藏了部分元数据字段。如果你在开发系统App,记得在android.hardware.camera2.params.SessionConfiguration里设置setSessionParameter来获取完整数据。
嗯,相机系统这块内容确实不少。但只要你把HAL3的Pipeline模型吃透,剩下的都是围绕这个模型展开的细节。我个人建议,初学者可以先从Camera2Basic这个官方Sample入手,跑通一遍预览+拍照+视频的流程,然后再去深究HAL层的实现。
记住一句话:相机系统,本质上就是“提交Request,拿到Result”的循环。理解了这个循环,你就掌握了Camera Framework的钥匙。
公众号:蓝海资料掘金营,微信deep3321