4、HAL层关键数据结构:camera_module_t、camera_device_t、camera3_device_t结构体解析
好,咱们今天来啃几块硬骨头。HAL层的数据结构,说白了就是相机硬件和上层框架之间的「翻译官」。你搞懂了它们,调试HAL就像看自家地图一样清晰。
我个人习惯,拿到一个新平台的相机HAL代码,第一件事就是去翻这几个结构体。为什么?因为它们定义了整个相机系统的骨架。今天咱们就一个一个拆开看。
4.1 camera_module_t:HAL的「大门」
这个结构体,是所有相机HAL的入口。上层框架要跟相机打交道,第一步就是找到这个结构体。
typedef struct camera_module {
hw_module_t common;
int (*get_number_of_cameras)(void);
int (*get_camera_info)(int camera_id, struct camera_info *info);
int (*set_callbacks)(const camera_module_callbacks_t *callbacks);
void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
int (*open_legacy)(const struct hw_module_t* module, const char* id,
uint32_t halVersion, struct hw_device_t** device);
int (*set_torch_mode)(const char* camera_id, bool enabled);
int (*init)(void);
} camera_module_t;
你看,它继承自 hw_module_t。这是Android硬件抽象层的通用套路。我刚开始接触时也觉得奇怪,为什么要套一层?后来明白了,这是为了统一管理所有硬件模块。
这里有几个关键函数指针,我重点说说:
- get_number_of_cameras:返回设备上的摄像头数量。嗯,这里有个坑——有些平台会虚拟出一些摄像头,比如用于屏幕录制或AR的。你调试时如果发现数量对不上,别急着怀疑驱动。
- get_camera_info:获取单个摄像头的静态信息。包括朝向、是否支持闪光灯等。我在项目中遇到过,某款手机的前置摄像头信息里orientation写错了,导致预览画面一直是倒的。
- set_callbacks:注册回调函数。比如摄像头热插拔事件,就是通过这里通知框架的。
调试小技巧:当你发现相机打不开时,先检查 get_number_of_cameras 的返回值。如果返回0,说明HAL根本没加载成功。我曾经花了一整天排查一个驱动问题,最后发现是权限没配好。
4.2 camera_device_t:通往具体摄像头的「钥匙」
拿到大门之后,你需要一把钥匙来打开具体的摄像头。这把钥匙就是 camera_device_t。
typedef struct camera_device {
hw_device_t common;
int (*set_preview_window)(struct camera_device *dev,
struct preview_stream_ops *window);
void (*set_callbacks)(struct camera_device *dev,
camera_notify_callback notify_cb,
camera_data_callback data_cb,
camera_data_timestamp_callback data_cb_timestamp,
camera_request_memory get_memory);
void (*enable_msg_type)(struct camera_device *dev, int32_t msg_type);
void (*disable_msg_type)(struct camera_device *dev, int32_t msg_type);
int (*msg_type_enabled)(struct camera_device *dev, int32_t msg_type);
int (*start_preview)(struct camera_device *dev);
void (*stop_preview)(struct camera_device *dev);
int (*preview_enabled)(struct camera_device *dev);
int (*store_meta_data_in_buffers)(struct camera_device *dev, int enable);
int (*start_recording)(struct camera_device *dev);
void (*stop_recording)(struct camera_device *dev);
int (*recording_enabled)(struct camera_device *dev);
void (*release_recording_frame)(struct camera_device *dev,
const void *opaque);
int (*auto_focus)(struct camera_device *dev);
int (*cancel_auto_focus)(struct camera_device *dev);
int (*take_picture)(struct camera_device *dev);
int (*cancel_picture)(struct camera_device *dev);
int (*set_parameters)(struct camera_device *dev, const char *params);
char* (*get_parameters)(struct camera_device *dev);
void (*put_parameters)(struct camera_device *dev, char *params);
int (*send_command)(struct camera_device *dev,
int32_t cmd, int32_t arg1, int32_t arg2);
void (*release)(struct camera_device *dev);
int (*dump)(struct camera_device *dev, int fd);
} camera_device_t;
这个结构体是Camera 1.0 HAL的核心。你想想看,它包含了预览、拍照、对焦、参数设置等所有操作。每个函数指针都对应一个具体的硬件操作。
我个人觉得,调试时最常打交道的是这几个:
- set_preview_window:设置预览窗口。如果这里传进来的窗口句柄是空的,预览就会黑屏。我踩过这个坑——某个平台上,SurfaceFlinger的buffer分配出了问题,导致窗口一直为NULL。
- set_parameters / get_parameters:参数设置和获取。注意,参数是以字符串形式传递的,格式是"key=value;key2=value2"。解析时一定要小心,我曾经见过某个厂商在参数里加了换行符,导致解析出错。
- start_preview / stop_preview:预览的开关。调试时如果预览卡住,可以试试先stop再start,看能不能恢复。
注意:Camera 1.0 HAL在Android 7.0之后已经被标记为废弃。但很多低端设备或特殊场景(比如车载)还在用。如果你维护的是老项目,这个结构体你绕不开。
4.3 camera3_device_t:新时代的「引擎」
到了Camera 3.0 HAL,整个架构都变了。不再是简单的函数指针集合,而是引入了更灵活的请求-响应模型。
typedef struct camera3_device {
hw_device_t common;
int (*initialize)(const struct camera3_device *,
const camera3_callback_ops_t *callback_ops);
int (*configure_streams)(const struct camera3_device *,
camera3_stream_configuration_t *stream_list);
int (*register_stream_buffers)(const struct camera3_device *,
const camera3_stream_buffer_set_t *buffer_set);
const camera_metadata_t* (*construct_default_request_settings)(
const struct camera3_device *, int type);
int (*process_capture_request)(const struct camera3_device *,
camera3_capture_request_t *request);
void (*dump)(const struct camera3_device *, int fd);
int (*flush)(const struct camera3_device *);
void (*signal_stream_flush)(const struct camera3_device*,
uint32_t* streams, uint32_t num_streams);
int (*is_reconfiguration_required)(const struct camera3_device*,
const camera_metadata_t* old_session_params,
const camera_metadata_t* new_session_params);
} camera3_device_t;
你看,函数数量少了很多。但每个函数的复杂度都上去了。为什么?因为Camera 3.0把控制逻辑从HAL移到了框架层,HAL只负责执行。
我重点讲几个核心函数:
- initialize:初始化设备,注册回调。这个回调很重要,HAL通过它把结果返回给框架。如果回调没注册成功,整个流程就断了。
- configure_streams:配置数据流。这是Camera 3.0最关键的函数之一。你需要告诉HAL:我要几个流?每个流的分辨率、格式是什么?我调试时经常在这里加日志,看配置是否被正确接收。
- process_capture_request:处理捕获请求。这是Camera 3.0的核心。框架发一个请求,HAL处理完后通过回调返回结果。注意,这个函数是异步的,HAL可以缓存多个请求。
- flush:清空所有待处理的请求。调试时如果发现相机卡住了,可以试试调用flush。我曾经遇到一个bug,某个请求一直没返回,导致后续请求都被阻塞了。
核心区别:Camera 1.0是「你叫我做啥我就做啥」,Camera 3.0是「你给我一个任务清单,我做完通知你」。前者是同步的,后者是异步的。这个区别决定了调试思路完全不同。
4.4 三个结构体的关系与调试要点
咱们来捋一捋这三个结构体的关系:
| 结构体 | 作用 | 对应HAL版本 | 调试关注点 |
|---|---|---|---|
| camera_module_t | HAL入口,管理所有摄像头 | 1.0 / 3.0 | get_number_of_cameras返回值、get_camera_info内容 |
| camera_device_t | 单个摄像头的操作接口 | 1.0 | set_parameters解析、start_preview时序 |
| camera3_device_t | 单个摄像头的操作接口(新架构) | 3.0 | configure_streams配置、process_capture_request异步处理 |
调试时,我建议你按这个顺序排查:
- 先确认
camera_module_t是否正确加载。检查get_number_of_cameras返回值。 - 再确认
get_camera_info返回的信息是否合理。比如分辨率列表、帧率范围等。 - 如果是Camera 1.0,重点看
camera_device_t的set_parameters和start_preview。 - 如果是Camera 3.0,重点看
configure_streams和process_capture_request的调用时序。
避坑指南:我曾经调试一个Camera 3.0的问题,预览画面一直黑屏。折腾了两天,最后发现是 configure_streams 里stream的usage字段没设置正确。上层框架要求的是GRALLOC_USAGE_HW_TEXTURE,我设成了GRALLOC_USAGE_SW_READ_OFTEN。就这一位之差,画面出不来。
嗯,这三个结构体就讲到这里。你想想看,它们其实就是一个层层递进的关系:从模块到设备,从老架构到新架构。理解了它们,你就掌握了HAL层的「地图」。下一节咱们聊聊更具体的调试方法,比如怎么加日志、怎么抓取HAL层的trace。