7、Camera HAL层实现:从open到process_capture_request的完整解析

各位同学,今天我们来啃一块硬骨头——Camera HAL层的核心流程。说实话,HAL层是Android Camera框架里最"接地气"的一层,它直接跟硬件打交道。我当年刚接触这块时,也被那一堆回调函数和buffer流转搞得头大。但别怕,咱们一步步拆解。

7.1 open/initialize/configure_streams流程详解

这三个接口,说白了就是Camera HAL的"三段式"启动流程。每个阶段都有它存在的意义,咱们一个一个看。

7.1.1 open()——打开相机设备

open()是HAL层被调用的第一个接口。它的任务很简单:创建一个camera3_device_t实例,并绑定好ops函数表。

// 典型的open实现
int camera_device_open(const struct hw_module_t* module,
                       const char* id,
                       struct hw_device_t** device) {
    // 1. 分配设备结构体
    camera3_device_t* cam_dev = (camera3_device_t*)malloc(sizeof(camera3_device_t));
    if (!cam_dev) return -ENOMEM;

    // 2. 初始化ops函数表
    cam_dev->ops = &camera3_device_ops;
    cam_dev->priv = new CameraDevice(atoi(id));

    // 3. 填充公共字段
    cam_dev->common.tag = HARDWARE_DEVICE_TAG;
    cam_dev->common.version = CAMERA_DEVICE_API_VERSION_3_4;
    cam_dev->common.close = camera_device_close;

    *device = (hw_device_t*)cam_dev;
    return 0;
}
我的经验:这里有个坑——open()里不要做耗时操作。我曾经在open里初始化ISP,结果导致相机启动慢了一秒多,被测试同学追着骂。open()只做资源预留,真正的初始化放到initialize里做。

7.1.2 initialize()——真正的初始化

initialize()会传入一个camera3_callback_ops_t指针,这是HAL跟framework通信的"电话线"。你得把它保存好,后面所有的事件上报、数据回调都靠它。

int CameraDevice::initialize(const camera3_callback_ops_t* callback_ops) {
    // 保存回调接口
    mCallbackOps = callback_ops;

    // 初始化ISP硬件
    mIspDriver->init();

    // 创建请求队列
    mRequestQueue = new MessageQueue();

    // 启动预览线程
    mPreviewThread = new std::thread(&CameraDevice::previewLoop, this);

    return 0;
}

initialize()完成后,framework会认为相机设备已经就绪,可以开始配置数据流了。

7.1.3 configure_streams()——配置数据流

这个接口是HAL层最复杂的部分之一。它接收一个camera3_stream_configuration_t结构体,里面包含了所有需要配置的stream信息。

Stream类型 用途 典型分辨率
CAMERA3_STREAM_OUTPUT 预览/拍照输出 1920x1080
CAMERA3_STREAM_INPUT 输入流(如YUV reprocessing) 取决于输入源
CAMERA3_STREAM_BIDIRECTIONAL 双向流(少见)
int CameraDevice::configure_streams(
    camera3_stream_configuration_t* stream_config) {
    // 1. 验证配置
    if (stream_config->num_streams > MAX_STREAMS) {
        return -EINVAL;
    }

    // 2. 遍历所有stream,配置硬件
    for (size_t i = 0; i < stream_config->num_streams; i++) {
        camera3_stream_t* stream = stream_config->streams[i];
        stream->usage = GRALLOC_USAGE_SW_READ_OFTEN;
        stream->max_buffers = 4;

        // 根据格式配置ISP管线
        switch (stream->format) {
            case HAL_PIXEL_FORMAT_YCbCr_420_888:
                mIspDriver->configYuvPipe(stream->width, stream->height);
                break;
            case HAL_PIXEL_FORMAT_BLOB:
                mIspDriver->configJpegPipe(stream->width, stream->height);
                break;
        }
    }

    // 3. 计算并返回stall duration
    return 0;
}
注意:configure_streams可能会被多次调用(比如切换分辨率时)。每次调用都要重新配置硬件管线,但不要重新初始化整个ISP。我见过有人每次configure_streams都重新加载固件,结果切换分辨率要等两秒...

7.2 process_capture_request实现

这是HAL层的核心接口,每次拍照或预览帧都会走到这里。它接收一个camera3_capture_request_t,里面包含了metadata、buffer等信息。

7.2.1 Request解析

一个capture request包含三部分:settings(3A参数)、input_buffer(如果有)、output_buffers(目标buffer列表)。

int CameraDevice::process_capture_request(
    const camera3_capture_request_t* request) {
    // 1. 解析settings
    camera_metadata_ro_entry_t entry;
    if (find_camera_metadata_ro_entry(
            request->settings,
            ANDROID_SENSOR_EXPOSURE_TIME,
            &entry)) {
        mExposureTime = entry.data.i64[0];
    }

    // 2. 解析output buffers
    for (size_t i = 0; i < request->num_output_buffers; i++) {
        const camera3_stream_buffer_t* buf = &request->output_buffers[i];
        // 处理每个output buffer
        mPendingBuffers.push_back(buf);
    }

    // 3. 提交到硬件队列
    mRequestQueue->push(request);
    return 0;
}

7.2.2 ISP参数下发

ISP参数下发是HAL层最"硬核"的部分。你需要把Android的metadata转换成ISP寄存器能理解的值。

void CameraDevice::applyIspSettings(
    const camera_metadata_t* settings) {
    // 转换曝光时间
    int64_t exposure = getMetadataValue(
        settings, ANDROID_SENSOR_EXPOSURE_TIME);
    mIspDriver->setExposureTime(exposure / 1000); // ns -> us

    // 转换增益
    int32_t gain = getMetadataValue(
        settings, ANDROID_SENSOR_SENSITIVITY);
    mIspDriver->setAnalogGain(gain / 100.0f);

    // 转换白平衡
    camera_metadata_rational_t wb = getMetadataRational(
        settings, ANDROID_COLOR_CORRECTION_GAINS);
    mIspDriver->setWhiteBalance(wb.numerator / (float)wb.denominator);
}
避坑指南:我曾经在Rockchip平台上遇到一个问题——ISP参数下发后,画面闪烁。排查了半天,发现是曝光时间和帧率不匹配。曝光时间不能超过帧周期的90%,否则会出现丢帧。这个约束在Android metadata里没有体现,需要你自己在HAL层做校验。

7.2.3 Buffer处理

Buffer处理是HAL层性能的关键。你需要管理好buffer的生命周期,确保不丢帧、不重复使用正在处理的buffer。

void CameraDevice::handleBuffer(
    camera3_stream_buffer_t* buffer,
    const void* data, size_t data_size) {
    // 1. 获取buffer的handle
    buffer_handle_t* buf_handle = buffer->buffer;

    // 2. 锁定buffer,获取CPU访问地址
    void* cpu_addr;
    mGrallocModule->lock(*buf_handle,
                         GRALLOC_USAGE_SW_WRITE_OFTEN,
                         0, 0, width, height,
                         &cpu_addr);

    // 3. 拷贝ISP输出数据到buffer
    memcpy(cpu_addr, data, data_size);

    // 4. 解锁buffer
    mGrallocModule->unlock(*buf_handle);

    // 5. 通知framework buffer已就绪
    buffer->status = CAMERA3_BUFFER_STATUS_OK;
    buffer->release_fence = -1;
    mCallbackOps->process_capture_result(mCallbackOps, &result);
}

7.3 flush/flush_ongoing机制

flush机制是Android Camera HAL里一个容易被忽视但非常重要的部分。当framework需要快速关闭相机时(比如切换到其他应用),会调用flush来清空所有pending的request。

int CameraDevice::flush() {
    // 1. 停止接收新request
    mRequestQueue->stop();

    // 2. 清空硬件队列
    mIspDriver->flushQueue();

    // 3. 返回所有pending buffer
    for (auto& buf : mPendingBuffers) {
        camera3_capture_result_t result = {};
        result.num_output_buffers = 1;
        result.output_buffers = &buf;
        result.frame_number = buf.frame_number;
        result.result = NULL;
        result.partial_result = 0;

        buf.status = CAMERA3_BUFFER_STATUS_ERROR;
        mCallbackOps->process_capture_result(mCallbackOps, &result);
    }
    mPendingBuffers.clear();

    return 0;
}
注意:flush()必须是同步的,也就是说返回时所有pending request都必须处理完毕。我见过一个实现,flush里只设了个标志位就返回了,结果framework以为已经清空,开始释放资源,但HAL线程还在处理旧request...后果就是crash。

7.4 Error Handling与Callback机制

Camera HAL的错误处理机制,说白了就是"出事了怎么告诉framework"。Android定义了几种错误类型:

  • CAMERA3_MSG_ERROR_DEVICE:设备级错误,比如ISP挂了
  • CAMERA3_MSG_ERROR_REQUEST:单个request处理失败
  • CAMERA3_MSG_ERROR_RESULT:metadata生成失败
  • CAMERA3_MSG_ERROR_BUFFER:buffer处理失败
void CameraDevice::notifyError(
    uint32_t frame_number,
    camera3_error_msg_code_t error_code) {
    camera3_notify_msg_t msg = {};
    msg.type = CAMERA3_MSG_ERROR;
    msg.message.error.frame_number = frame_number;
    msg.message.error.error_code = error_code;

    mCallbackOps->notify(mCallbackOps, &msg);
}

Callback机制则是HAL层主动上报的通道。除了错误通知,还有快门事件、3A状态变化等。

7.5 实战:基于Rockchip平台实现一个最小HAL

好了,理论说完了,咱们来点实际的。下面是一个基于Rockchip RK3588平台的最小HAL实现骨架。

// rk_camera_hal.cpp
class RkCameraDevice : public CameraDevice {
public:
    RkCameraDevice(int id) : mCameraId(id) {
        // Rockchip ISP驱动初始化
        mIspDrv = new RkIspDriver("/dev/video" + std::to_string(id));
    }

    int initialize(const camera3_callback_ops_t* callback_ops) override {
        mCallbackOps = callback_ops;
        mIspDrv->init();
        mIspDrv->loadTuningFile("/etc/camera/ov5640_tuning.xml");
        return 0;
    }

    int configure_streams(
        camera3_stream_configuration_t* stream_config) override {
        // Rockchip平台支持的最大stream数
        if (stream_config->num_streams > 3) {
            return -EINVAL;
        }

        for (size_t i = 0; i < stream_config->num_streams; i++) {
            auto* stream = stream_config->streams[i];
            // Rockchip ISP支持NV12和JPEG
            if (stream->format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
                mIspDrv->configChannel(0, stream->width, stream->height);
            } else if (stream->format == HAL_PIXEL_FORMAT_BLOB) {
                mIspDrv->configJpegChannel(stream->width, stream->height);
            }
        }
        return 0;
    }

    int process_capture_request(
        const camera3_capture_request_t* request) override {
        // 解析Rockchip特有的ISP参数
        RkIspParams params;
        parseRkIspParams(request->settings, &params);
        mIspDrv->setParams(params);

        // 提交到硬件
        mIspDrv->enqueueRequest(request);
        return 0;
    }

private:
    int mCameraId;
    RkIspDriver* mIspDrv;
};
我的经验:Rockchip平台的ISP驱动有个特点——它需要加载tuning文件。这个文件包含了镜头校正、颜色矩阵等参数。不同模组需要不同的tuning文件。我踩过最大的坑就是tuning文件加载失败后,ISP输出全黑画面,但没有任何错误日志。后来我在initialize里加了tuning文件校验,才定位到问题。

这个最小HAL虽然简单,但已经包含了所有核心流程。你可以在此基础上扩展,比如添加3A算法、多帧合成、HDR等功能。

嗯,HAL层的实现就讲到这里。记住,HAL层是连接Android框架和硬件驱动的桥梁,它的设计质量直接影响相机性能和稳定性。写HAL代码时,多想想边界情况,多想想异常处理,这样你的代码才能经得起考验。


公众号:蓝海资料掘金营,微信deep3321