4. 实现get_number_of_cameras

任务说明

在Camera HAL层开发中,get_number_of_cameras是HAL模块对外暴露的核心查询接口,上层框架通过它获知设备支持的摄像头总数,进而枚举每个摄像头的静态信息。本案例要求实现该函数,使其正确返回设备支持的相机数量,并处理多摄像头场景——包括主摄、广角、长焦等多摄像头配置,以及摄像头不可用时的降级处理。

HAL模块结构定义

// camera_hal.h
#ifndef CAMERA_HAL_H
#define CAMERA_HAL_H

#include <hardware/hardware.h>
#include <hardware/camera_common.h>

// 摄像头设备信息结构体
typedef struct {
    int camera_id;
    char device_path[64];      // 如 /dev/video0
    int facing;                // CAMERA_FACING_BACK / CAMERA_FACING_FRONT
    int is_available;          // 1: 可用, 0: 不可用
} camera_info_t;

// HAL模块结构体
typedef struct {
    hw_module_t common;
    camera_info_t *cameras;
    int num_cameras;
} camera_hal_module_t;

// 全局HAL模块实例
extern camera_hal_module_t HAL_MODULE_INFO_SYM;

#endif // CAMERA_HAL_H

实现代码

// camera_hal.c
#include "camera_hal.h"
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

// 摄像头设备配置表(实际项目中通常从配置文件或设备树读取)
static camera_info_t camera_config[] = {
    { .camera_id = 0, .device_path = "/dev/video0", .facing = CAMERA_FACING_BACK,  .is_available = 1 },
    { .camera_id = 1, .device_path = "/dev/video1", .facing = CAMERA_FACING_BACK,  .is_available = 1 },  // 广角
    { .camera_id = 2, .device_path = "/dev/video2", .facing = CAMERA_FACING_FRONT, .is_available = 1 },
    { .camera_id = 3, .device_path = "/dev/video3", .facing = CAMERA_FACING_BACK,  .is_available = 0 },  // 长焦(当前不可用)
};

#define MAX_CAMERAS 8

// 检查设备节点是否存在
static int is_device_present(const char *path) {
    struct stat st;
    return (stat(path, &st) == 0) ? 1 : 0;
}

// 初始化HAL模块
static int hal_module_init(camera_hal_module_t *mod) {
    int total = sizeof(camera_config) / sizeof(camera_config[0]);
    int available = 0;

    // 分配可用摄像头列表
    mod->cameras = (camera_info_t *)calloc(total, sizeof(camera_info_t));
    if (!mod->cameras) {
        return -ENOMEM;
    }

    // 筛选可用摄像头
    for (int i = 0; i < total; i++) {
        // 设备节点存在且标记为可用
        if (camera_config[i].is_available &&
            is_device_present(camera_config[i].device_path)) {
            memcpy(&mod->cameras[available], &camera_config[i], sizeof(camera_info_t));
            available++;
        }
    }

    mod->num_cameras = available;
    return 0;
}

// 获取摄像头数量
static int get_number_of_cameras(void) {
    camera_hal_module_t *mod = &HAL_MODULE_INFO_SYM;

    // 首次调用时初始化
    if (mod->cameras == NULL) {
        int ret = hal_module_init(mod);
        if (ret != 0) {
            return 0;  // 初始化失败,返回0个摄像头
        }
    }

    return mod->num_cameras;
}

// HAL模块入口
static struct hw_module_methods_t hal_module_methods = {
    .open = NULL,  // 本案例不涉及open操作
};

camera_hal_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 = "Multi Camera HAL Module",
        .author = "Camera HAL Team",
        .methods = &hal_module_methods,
    },
    .cameras = NULL,   // 延迟初始化
    .num_cameras = 0,
};

关键实现说明

上述实现的核心逻辑如下:

  • 静态配置表:camera_config[]定义了4个摄像头槽位,其中ID为3的长焦摄像头is_available = 0,模拟硬件未连接或驱动加载失败的情况。
  • 设备节点检测:is_device_present()通过stat()检查/dev/videoN节点是否存在,确保只有物理存在的摄像头才被计数。
  • 延迟初始化:首次调用get_number_of_cameras()时执行hal_module_init(),筛选出同时满足“配置可用”和“设备节点存在”的摄像头,避免每次查询都重复扫描。
  • 多摄像头支持:返回数量为实际可用的摄像头个数,而非配置表中的总条目数。在本例中,若/dev/video0、/dev/video1、/dev/video2均存在,则返回3;若/dev/video1不存在,则返回2。

编译与验证

在Android源码树中,该文件通常位于device/<vendor>/<platform>/camera/目录下,通过LOCAL_MODULE := camera.<platform>编译为HAL共享库。独立验证步骤如下:

# 1. 编译(在Android源码环境或NDK交叉编译环境)
mm -j8

# 2. 模拟设备节点(用于本地测试)
sudo touch /dev/video0 /dev/video1 /dev/video2
# 注意:不创建 /dev/video3,模拟长焦不可用

# 3. 编写测试程序调用 get_number_of_cameras()
# 测试代码片段:
// test_hal.c
#include <stdio.h>
#include "camera_hal.h"

int main() {
    int num = get_number_of_cameras();
    printf("Available cameras: %d\n", num);
    return 0;
}

判定标准:

  1. 当/dev/video0、/dev/video1、/dev/video2均存在时,程序输出Available cameras: 3;
  2. 删除/dev/video1后重新运行,输出变为Available cameras: 2;
  3. 若所有设备节点均不存在,输出Available cameras: 0,且不崩溃。

该实现正确处理了多摄像头场景下的数量统计,同时通过设备节点检测避免了将已配置但实际不可用的摄像头计入总数,符合Android Camera HAL对get_number_of_cameras的接口契约。