第一章:HAL层Stream架构概述
大家好,我是你们的讲师。今天咱们正式开始Camera HAL层开发的实战之旅。第一节课,我想先聊聊Stream架构。说白了,Stream就是摄像头数据流动的管道。你想想看,从镜头进来的光线,最终变成你手机屏幕上的一张照片,中间经历了多少环节?Stream就是这些环节的抽象。
1.1 Camera子系统整体框架
先看一张大图。Camera子系统从上到下,大致分这么几层:
- 应用层:你写的那个拍照App,调用Camera2 API
- Framework层:Android的CameraService,负责调度
- HAL层:我们今天的重点,硬件抽象层
- Kernel层:驱动,V4L2或者厂商私有驱动
- 硬件层:Sensor、ISP、MIPI接口这些物理器件
我个人习惯把HAL层比作「翻译官」。上层Framework说标准Android语,下层硬件说厂商方言。HAL层负责把两者打通。嗯,这里要注意,HAL层不是简单的透传,它要做大量的适配工作。
核心要点:HAL层是Camera性能优化的主战场。我见过太多项目,Framework层调得再好,HAL层适配不到位,最终效果就是卡顿、延迟、功耗高。
1.2 Stream在HAL中的位置与作用
Stream到底是什么?我举个例子。你打开相机预览,这时候有一个预览Stream。你按下快门,这时候多了一个拍照Stream。如果你同时录像,那就又多了一个录像Stream。每个Stream都有自己的目标:分辨率、帧率、格式、用途。
在HAL层,Stream是camera3_stream_t结构体的实例。它承载了所有配置信息。我记得第一次接触这个结构体时,被它的字段数量吓了一跳。但后来发现,真正需要关心的核心字段其实不多。
Stream的作用可以归纳为三点:
- 定义数据流向:从Sensor到ISP,再到内存,最后到Framework
- 承载配置参数:分辨率、格式、旋转角度、裁剪区域等
- 提供性能约束:比如最大帧率、最小延迟要求
避坑指南:我曾经在一个项目中,因为Stream的usage字段设置错误,导致预览画面一直黑屏。查了两天才发现,原来是Buffer的分配方式不对。所以,Stream的每个字段都值得你认真对待。
1.3 关键数据结构预览
咱们先快速过一遍HAL层最核心的几个数据结构。后面章节会逐个深入。
| 数据结构 | 作用 | 我常用的字段 |
|---|---|---|
camera3_stream_t |
描述单个Stream的配置 | width, height, format, usage, max_buffers |
camera3_stream_configuration_t |
描述一组Stream的集合 | num_streams, streams, operation_mode |
camera3_buffer_request_t |
Buffer请求的描述 | num_buffers, stream, buffer_handle |
camera3_capture_request_t |
一次Capture请求 | frame_number, settings, input_buffer, output_buffers |
你可能会问,为什么需要这么多结构体?其实每个结构体对应一个生命周期阶段。Stream配置阶段用camera3_stream_configuration_t,运行时用camera3_capture_request_t。各司其职,清晰明了。
来看一个实际的Stream配置代码片段:
// 典型的Stream配置流程
camera3_stream_t preview_stream;
preview_stream.stream_type = CAMERA3_STREAM_OUTPUT;
preview_stream.width = 1920;
preview_stream.height = 1080;
preview_stream.format = HAL_PIXEL_FORMAT_YCbCr_420_888;
preview_stream.usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_TEXTURE;
preview_stream.max_buffers = 4; // 我习惯给预览留4个Buffer
camera3_stream_configuration_t config;
config.num_streams = 1;
config.streams = &preview_stream;
config.operation_mode = CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE;
这段代码看起来简单,但每个字段背后都有坑。比如max_buffers设得太小,预览会掉帧;设得太大,内存占用高。我一般根据帧率和分辨率来算,1080p@30fps给4个Buffer基本够用。
重要提醒:Stream配置必须在configure_streams()回调中完成。一旦配置完成,运行时不能动态修改Stream参数。如果你想改分辨率,必须重新配置。这一点和V4L2的StreamOn/StreamOff机制不同,很多从Linux驱动转过来的同事容易搞混。
1.4 我的实战经验总结
做了这么多年HAL开发,我总结了几条Stream配置的黄金法则:
- 先规划,后编码:拿到需求后,先画出Stream拓扑图,再动手写代码
- Buffer数量要留余量:理论计算值+1到2个Buffer,应对突发情况
- 格式选择要谨慎:不是所有格式硬件都支持,提前查Sensor手册
- 测试要覆盖边界:比如最大分辨率、最小分辨率、多Stream并发场景
嗯,今天就先聊到这里。Stream架构是HAL开发的基础,后面所有章节都会围绕它展开。下一节我们会深入configure_streams()的实现细节,到时候我会分享一个我踩过的坑——关于Stream重配置导致的内存泄漏问题。
记住,Stream配置做得好,后面开发事半功倍。配置做得糙,后面全是坑。咱们下节课见。