2. 枚举摄像头设备

任务说明

本案例实现 Camera HAL 层设备枚举能力:通过实现 get_number_of_cameras() 和 get_camera_info() 两个核心接口,使上层框架能够获取当前设备支持的摄像头数量及每个摄像头的静态能力信息(ID、朝向、 facing 方向等)。代码基于 Android Camera HAL3 接口规范,以标准 camera_module_t 结构体为载体。

核心实现

// camera_hal_enum.c
#include <hardware/camera_common.h>
#include <hardware/camera.h>
#include <cutils/log.h>
#include <string.h>

#define CAMERA_ID_BACK  0
#define CAMERA_ID_FRONT 1
#define NUM_CAMERAS     2

/* 静态摄像头信息表 */
static camera_info_t g_camera_info[NUM_CAMERAS];

/* 初始化静态信息 */
static void init_camera_info(void) {
    /* 后置摄像头 */
    g_camera_info[CAMERA_ID_BACK].device_version = CAMERA_DEVICE_API_VERSION_3_2;
    g_camera_info[CAMERA_ID_BACK].device_id = strdup("0");
    g_camera_info[CAMERA_ID_BACK].resource_cost = 100;
    g_camera_info[CAMERA_ID_BACK].conflicting_devices = NULL;
    g_camera_info[CAMERA_ID_BACK].conflicting_devices_length = 0;
    g_camera_info[CAMERA_ID_BACK].facing = CAMERA_FACING_BACK;
    g_camera_info[CAMERA_ID_BACK].orientation = 90;
    g_camera_info[CAMERA_ID_BACK].static_camera_characteristics = NULL; /* 由 HAL 填充完整 metadata */

    /* 前置摄像头 */
    g_camera_info[CAMERA_ID_FRONT].device_version = CAMERA_DEVICE_API_VERSION_3_2;
    g_camera_info[CAMERA_ID_FRONT].device_id = strdup("1");
    g_camera_info[CAMERA_ID_FRONT].resource_cost = 100;
    g_camera_info[CAMERA_ID_FRONT].conflicting_devices = NULL;
    g_camera_info[CAMERA_ID_FRONT].conflicting_devices_length = 0;
    g_camera_info[CAMERA_ID_FRONT].facing = CAMERA_FACING_FRONT;
    g_camera_info[CAMERA_ID_FRONT].orientation = 270;
    g_camera_info[CAMERA_ID_FRONT].static_camera_characteristics = NULL;
}

/* 返回摄像头数量 */
static int get_number_of_cameras(void) {
    return NUM_CAMERAS;
}

/* 获取指定摄像头的静态信息 */
static int get_camera_info(int camera_id, struct camera_info *info) {
    if (camera_id < 0 || camera_id >= NUM_CAMERAS) {
        ALOGE("Invalid camera_id: %d", camera_id);
        return -EINVAL;
    }
    if (info == NULL) {
        ALOGE("info is NULL");
        return -EINVAL;
    }
    memcpy(info, &g_camera_info[camera_id], sizeof(camera_info_t));
    return 0;
}

/* HAL 模块结构体 */
static hw_module_methods_t g_module_methods = {
    .open = NULL  /* 设备打开接口,本案例不涉及 */
};

camera_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag           = HARDWARE_MODULE_TAG,
        .module_api_version = CAMERA_MODULE_API_VERSION_2_4,
        .hal_api_version   = HARDWARE_HAL_API_VERSION,
        .id            = CAMERA_HARDWARE_MODULE_ID,
        .name          = "Demo Camera HAL Module",
        .author        = "Camera HAL Course",
        .methods       = &g_module_methods,
        .dso           = NULL,
        .reserved      = {0},
    },
    .get_number_of_cameras = get_number_of_cameras,
    .get_camera_info       = get_camera_info,
    .set_callbacks         = NULL,
    .get_vendor_tag_ops    = NULL,
    .open_legacy           = NULL,
    .set_torch_mode        = NULL,
    .init                  = NULL,
    .reserved              = {0},
};

关键实现说明

上述代码中 HAL_MODULE_INFO_SYM 是 HAL 模块的入口符号,Android 框架通过 hw_get_module() 加载该符号。核心逻辑如下:

  • 数量枚举:get_number_of_cameras() 直接返回常量 NUM_CAMERAS(值为2),表示系统中有后置和前置两个摄像头。
  • 信息查询:get_camera_info() 首先校验 camera_id 是否越界以及 info 指针是否有效,然后通过 memcpy 将预置的静态信息拷贝到调用者提供的缓冲区。
  • 静态信息表:g_camera_info[] 在 init_camera_info() 中初始化,包含设备版本(API 3.2)、设备ID字符串、朝向(后置/前置)、安装角度(orientation)等关键字段。实际产品中 static_camera_characteristics 应指向完整的 android::CameraMetadata 数据,本案例为聚焦枚举逻辑将其置为 NULL。

编译与集成

# Android.bp 示例
cc_library_shared {
    name: "camera.demo.hal",
    vendor: true,
    srcs: ["camera_hal_enum.c"],
    shared_libs: [
        "libhardware",
        "libcutils",
        "liblog",
    ],
    stl: "none",
}

将源码放入 HAL 实现目录后,通过 mmm 或 soong 编译生成 camera.demo.hal.so,并替换系统 /vendor/lib64/hw/camera.demo.so(或对应设备路径)。

验证方法

在设备端执行以下命令验证枚举功能:

# 查看 HAL 模块是否被正确加载
adb shell "ls -l /vendor/lib64/hw/camera*"

# 通过 CameraService 日志确认枚举结果
adb logcat -s CameraService | grep -E "Number of cameras|Camera ID"

判定标准:

  1. 系统启动后 CameraService 日志显示 Number of cameras: 2;
  2. 调用 getCameraInfo(0) 返回后置摄像头(facing=0),getCameraInfo(1) 返回前置摄像头(facing=1);
  3. 传入非法 ID(如 camera_id=5)时,函数返回 -EINVAL 且不产生内存访问异常。

若日志中数量不符或信息错误,检查 NUM_CAMERAS 定义与 g_camera_info 初始化是否匹配实际硬件配置。