4. 打开摄像头设备

任务说明

本案例实现Android Camera HAL层中打开摄像头设备的核心流程:通过Camera Module接口获取摄像头信息,调用open()方法建立与底层硬件的会话,并完成设备能力查询与初始化。最终交付一个可独立编译运行的HAL层测试程序,验证open流程的正确性。

核心代码实现


// camera_device_open_test.c
// 编译: gcc -o camera_open_test camera_device_open_test.c -I./include
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hardware/camera_common.h>
#include <hardware/camera.h>
#include <camera_metadata.h>

// 模拟底层硬件设备结构(真实HAL中对应具体sensor驱动)
typedef struct {
    int32_t device_id;
    int32_t facing;
    int32_t orientation;
    int32_t max_fps;
} mock_sensor_t;

static mock_sensor_t mock_sensors[] = {
    {0, CAMERA_FACING_BACK, 90, 30},
    {1, CAMERA_FACING_FRONT, 270, 30},
};

// 模拟打开硬件设备(真实场景中调用V4L2或ISP驱动)
static int mock_hw_open(int32_t id, void **hw_handle) {
    if (id < 0 || id >= (int32_t)(sizeof(mock_sensors)/sizeof(mock_sensors[0]))) {
        return -ENODEV;
    }
    *hw_handle = &mock_sensors[id];
    return 0;
}

// HAL open回调:对应camera_module_t.common.methods->open
static int hal_device_open(const struct hw_module_t* module,
                           const char* name,
                           struct hw_device_t** device) {
    if (module == NULL || name == NULL || device == NULL) {
        return -EINVAL;
    }

    // 解析摄像头ID(name格式为"0"、"1"等)
    char *endptr;
    long id = strtol(name, &endptr, 10);
    if (*endptr != '\0' || id < 0 || id >= (long)(sizeof(mock_sensors)/sizeof(mock_sensors[0]))) {
        return -EINVAL;
    }

    // 打开底层硬件
    void *hw_handle = NULL;
    int ret = mock_hw_open((int32_t)id, &hw_handle);
    if (ret != 0) {
        return ret;
    }

    // 分配并初始化camera_device_t
    camera_device_t *cam_dev = calloc(1, sizeof(camera_device_t));
    if (cam_dev == NULL) {
        return -ENOMEM;
    }

    cam_dev->common.tag = HARDWARE_DEVICE_TAG;
    cam_dev->common.version = CAMERA_DEVICE_API_VERSION_1_0;
    cam_dev->common.module = (struct hw_module_t*)module;
    cam_dev->common.close = NULL; // 本案例不实现close

    // 保存硬件句柄到私有数据(真实HAL中放入priv字段)
    cam_dev->priv = hw_handle;

    *device = (struct hw_device_t*)cam_dev;
    return 0;
}

// 获取摄像头信息回调
static int hal_get_camera_info(struct camera_module_t* module,
                               int camera_id,
                               struct camera_info* info) {
    if (module == NULL || info == NULL) {
        return -EINVAL;
    }
    if (camera_id < 0 || camera_id >= (int32_t)(sizeof(mock_sensors)/sizeof(mock_sensors[0]))) {
        return -ENODEV;
    }

    mock_sensor_t *sensor = &mock_sensors[camera_id];
    info->device_id = camera_id;
    info->facing = sensor->facing;
    info->orientation = sensor->orientation;
    info->resource_cost = 0;
    info->conflicting_devices = NULL;
    info->conflicting_devices_length = 0;

    // 构造静态相机特征(仅包含必要字段)
    info->static_camera_characteristics = NULL; // 本案例不填充完整metadata
    return 0;
}

// 模块定义
static struct hw_module_methods_t hal_module_methods = {
    .open = hal_device_open,
};

camera_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = CAMERA_MODULE_API_VERSION_1_0,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = CAMERA_HARDWARE_MODULE_ID,
        .name = "Mock Camera HAL Module",
        .author = "HAL Course",
        .methods = &hal_module_methods,
        .dso = NULL,
        .reserved = {0},
    },
    .get_number_of_cameras = NULL, // 本案例不实现
    .get_camera_info = hal_get_camera_info,
    .set_callbacks = NULL,
    .get_vendor_tag_ops = NULL,
    .open_legacy = NULL,
    .set_torch_mode = NULL,
    .init = NULL,
    .reserved = {0},
};

// 测试入口
int main(void) {
    const hw_module_t *hw_module = &HAL_MODULE_INFO_SYM.common;
    camera_module_t *cam_module = (camera_module_t*)hw_module;

    printf("=== Camera HAL Open Test ===\n");

    // 1. 查询摄像头数量(本案例固定为2)
    int num_cameras = sizeof(mock_sensors)/sizeof(mock_sensors[0]);
    printf("[1] Number of cameras: %d\n", num_cameras);

    // 2. 遍历所有摄像头,执行open流程
    for (int i = 0; i < num_cameras; i++) {
        printf("\n--- Camera %d ---\n", i);

        // 2.1 获取摄像头信息
        struct camera_info info;
        int ret = cam_module->get_camera_info(cam_module, i, &info);
        if (ret != 0) {
            printf("[FAIL] get_camera_info(%d) returned %d\n", i, ret);
            return -1;
        }
        printf("[OK] get_camera_info: facing=%s orientation=%d\n",
               info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT",
               info.orientation);

        // 2.2 构造设备名并调用open
        char dev_name[8];
        snprintf(dev_name, sizeof(dev_name), "%d", i);

        struct hw_device_t *hw_dev = NULL;
        ret = hw_module->methods->open(hw_module, dev_name, &hw_dev);
        if (ret != 0) {
            printf("[FAIL] open(%s) returned %d\n", dev_name, ret);
            return -1;
        }

        // 2.3 验证设备句柄
        camera_device_t *cam_dev = (camera_device_t*)hw_dev;
        if (cam_dev->common.tag != HARDWARE_DEVICE_TAG) {
            printf("[FAIL] Invalid device tag\n");
            return -1;
        }

        // 2.4 验证底层硬件已正确关联
        mock_sensor_t *sensor = (mock_sensor_t*)cam_dev->priv;
        if (sensor == NULL || sensor->device_id != i) {
            printf("[FAIL] Hardware handle mismatch\n");
            return -1;
        }

        printf("[OK] open(%s) success, device_id=%d max_fps=%d\n",
               dev_name, sensor->device_id, sensor->max_fps);

        // 释放设备(真实HAL中调用close)
        free(cam_dev);
    }

    printf("\n=== All camera open tests PASSED ===\n");
    return 0;
}

代码说明

上述代码实现了一个最小化的Camera HAL打开设备流程:HAL_MODULE_INFO_SYM定义了标准的camera_module_t结构,其中get_camera_info回调返回模拟的摄像头朝向与旋转角信息;hal_device_open函数解析设备名字符串得到摄像头ID,调用mock_hw_open模拟底层驱动打开动作,并分配camera_device_t结构体,将硬件句柄存入priv字段。测试主函数遍历两个模拟摄像头,依次执行信息查询与设备打开,并校验返回的设备句柄与底层硬件关联是否正确。

编译与运行


# 需要Android HAL头文件路径(此处以标准路径示例)
gcc -o camera_open_test camera_device_open_test.c \
    -I./include \
    -L./lib -lcamera_metadata

# 运行测试
./camera_open_test

验证方法与判定标准

运行程序后,观察标准输出:

  • 程序依次打印[1] Number of cameras: 2,随后对Camera 0和Camera 1分别输出[OK] get_camera_info与[OK] open(...) success;
  • Camera 0应显示facing=BACK orientation=90,Camera 1应显示facing=FRONT orientation=270;
  • 最终输出=== All camera open tests PASSED ===,且进程退出码为0。

满足以上条件即可判定打开摄像头设备流程实现正确。若任一摄像头open失败或信息不匹配,程序会打印[FAIL]并提前退出,此时需检查设备ID解析或硬件模拟逻辑。

Camera HAL Open 调用时序 App/Client open("/dev/video0") camera_module_t HAL_MODULE_INFO_SYM mock_hw_open() 模拟Sensor驱动 camera_device_t priv=hw_handle 1 get_camera_info(id, &info) 2 返回 facing/orientation 3 methods->open(module, "0", &dev) 4 mock_hw_open(id) 5 返回 hw_handle 6 calloc + 赋值 7 返回 camera_device_t* 验证点:设备tag正确、priv指向mock_sensor、device_id匹配