Android camera HAL框架

接下来将会了解Android HAL是如何与相机设备、Framework进行交互的,为了简单起见,我们使用androidP代码中的谷歌实例代码进行学习,代码路径为:android/hardware/libhardware/modules/camera/3_4。

一般的,各个平台的camera HAL将会有个 v4l2_camera_hal.cpp 文件。在这里,将是HAL对外的接口,该文件将会通过 HAL_MODULE_INFO_SYM 修饰一个 camera_module_t 结构体。camera Provider服务就是通过 HAL_MODULE_INFO_SYM 找到 camera_module_t,从而操作HAL。

全局静态对象 V4L2CameraHAL

在 v4l2_camera_hal.cpp 中定义了一个 V4L2CameraHAL 类的全局静态对象 gCameraHAL,所以在加载 HAL.so 时,将会调用 V4L2CameraHAL 类的构造函数。在构造函数中,主要是探测 /dev 目录下有多少个 video 节点且支持V4L2 video capture,并将探测结果保存在 V4L2CameraHAL.mCameras 容器中。

get_number_of_cameras()

最终将返回全局静态对象 gCameraHAL 的 mCameras 成员大小。

get_camera_info()

最终根据传递进来的 id 将调用到全局静态对象 gCameraHAL 的 mCameras[id]->getInfo(info),此时将调用到 Camera::getInfo()。注意,mCameras[id]在全局静态变量 gCameraHAL 的构造函数中已经创建 V4L2Camera 对象。

为了支持Android框架保存原始图像文件,需要有关传感器特性的大量元数据。这包括诸如色彩空间和镜头阴影功能之类的信息。这些信息大部分都是相机子系统的静态属性,可以在配置输出管道或者提交请求之前进行查询。

set_callbacks()

设置回调函数,保存在 gCameraHAL.mCallbacks。mCallbacks为 camera_module_callbacks_t 类型,其中包含两个回调,一个是相机设备状态改变时的回调;一个是闪光灯状态改变时的回调。

hw_module_methods_t 变量

在 camera_module_t 的comon成员中,通过 methods 指向了一个 hw_module_methods_t 类型变量。通过 hw_module_methods_t->open() ,我们最终调用到 Camera::openDevice() 函数(V4L2Camera是Camera的派生类,V4L2Camera没有覆盖openDevice()函数),从而和相机设备连接起来,该函数的参数 module 指向 HAL_MODULE_INFO_SYM.common,而 device 则是将返回给 Framework 的 camera device,device 指向 hw_device_t 类型对象。

Camera::openDevice()

在该函数中,将会通过调用 connect() 函数完成与相机设备的连接,接着将填充device,返回Framework。

而 connect() 将是调用到 V4L2Camera::connect()。V4L2Camera::connect() 则是创建一个 V4L2Wrapper::Connection 实例对象,在 V4L2Wrapper::Connection 的构造函数中,又将调用 V4L2Wrapper::Connect() 函数。

这一切,就这样就到了V4L2Wrapper::Connect() 。而在 V4L2Wrapper::Connect() 中,将会 open video节点,进行查询支持的格式以及分辨率等操作。

看完 v4l2_camera_hal.cpp 中的接口后,似乎上面就已经介绍完了,就没有其他接口控制设备了,那么 camera Provider服务进程又是如何多样操作相机设备的呢?下面继续看。

我们回头看 Camera::openDevice() 函数的实现:

int Camera::openDevice(const hw_module_t *module, hw_device_t **device)
{
    ALOGI("%s:%d: Opening camera device", __func__, mId);
    ATRACE_CALL();
    android::Mutex::Autolock al(mDeviceLock);

    if (mBusy) {
        ALOGE("%s:%d: Error! Camera device already opened", __func__, mId);
        return -EBUSY;
    }

    int connectResult = connect();
    if (connectResult != 0) {
      return connectResult;
    }
    mBusy = true;
    mDevice.common.module = const_cast<hw_module_t*>(module);
    /* 这里,是我们返回给Framework的 hw_device_t,那么这个
     * mDevice 是什么呢,看 Camera 的构造函数
     */
    *device = &mDevice.common;
    return 0;
}

Camera::Camera(int id)
  : mId(id),
    mSettingsSet(false),
    mBusy(false),
    mCallbackOps(NULL),
    mInFlightTracker(new RequestTracker)
{
    memset(&mTemplates, 0, sizeof(mTemplates));
	/* 在Camera 的构造函数中,初始化 common 成员的部分变量之后,
	 * 赋值了 ops 操作集,接下来再看看 ops 的类型
	 */
    memset(&mDevice, 0, sizeof(mDevice));
    mDevice.common.tag    = HARDWARE_DEVICE_TAG;
    mDevice.common.version = CAMERA_DEVICE_API_VERSION_3_4;
    mDevice.common.close  = close_device;
    mDevice.ops           = const_cast<camera3_device_ops_t*>(&sOps);
    mDevice.priv          = this;
}

/* 从 mDevice.ops 的定义类型来看,很像就是操作相机设备的一些接口函数,
 * 我们怎么确认我们的猜测是否正确呢,看 camera Provider 服务进程的调用
 */
typedef struct camera3_device_ops {
    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);

    int (*process_capture_request)(const struct camera3_device *,
            camera3_capture_request_t *request);

    void (*get_metadata_vendor_tag_ops)(const struct camera3_device*,
            vendor_tag_query_ops_t* ops);

    void (*dump)(const struct camera3_device *, int fd);

    int (*flush)(const struct camera3_device *);

    void *reserved[8];
} camera3_device_ops_t;

/* camera Provider 服务进程的调用 */
/* 通过查看 CameraDeviceSession 类的实现,可以了解到,
 * 在创建流、处理Framework请求等操作,都是通过 Camera::openDevice()
 * 返回的 device 进行操作的,一般以以下的方式进行调用:
 */
mDevice->ops->initialize(mDevice, this);
mDevice->ops->dump(mDevice, fd->data[0]);
mDevice->ops->construct_default_request_settings(mDevice, (int) type);
mDevice->ops->configure_streams(mDevice, &stream_list);
mDevice->ops->process_capture_request(mDevice, &halRequest);
mDevice->ops->flush(mDevice);

所以通过以上分析得知,camera Provider 服务通过返回的 device 得到 camera3_device_ops_t 操作集,所以可以操作配置相机设备。下面再分析,这些操作集函数都进行了什么操作。

camera3_device_ops_t
const camera3_device_ops_t Camera::sOps = {
    .initialize = default_camera_hal::initialize,
    .configure_streams = default_camera_hal::configure_streams,
    .register_stream_buffers = nullptr,
    .construct_default_request_settings
        = default_camera_hal::construct_default_request_settings,
    .process_capture_request = default_camera_hal::process_capture_request,
    .get_metadata_vendor_tag_ops = nullptr,
    .dump = default_camera_hal::dump,
    .flush = default_camera_hal::flush,
    .reserved = {0},
};
initialize()

从上面我们了解到 initialize() 函数指针赋值为 default_camera_hal::initialize()。

namespace default_camera_hal {
extern "C" {
// Get handle to camera from device priv data
static Camera *camdev_to_camera(const camera3_device_t *dev)
{
	/* 实际上通过转换为 camera3_device_t 类型,
	 * 再获取 priv 成员转换为 Camera 类型指针,可
	 * 回头查看 Camera 的构造函数以及 openDevice()
	 */
    return reinterpret_cast<Camera*>(dev->priv);
}

static int initialize(const camera3_device_t *dev,
        const camera3_callback_ops_t *callback_ops)
{
    /* 调用到 Camera::initialize() */
    return camdev_to_camera(dev)->initialize(callback_ops);
}
} // extern "C"
} // namespace default_camera_hal

int Camera::initialize(const camera3_callback_ops_t *callback_ops)
{
    int res;

    ALOGV("%s:%d: callback_ops=%p", __func__, mId, callback_ops);
    /* 在Framework层,调用 initialize() 函数时,传递了
     * CameraDeviceSession 类对象的this指针,所以这里的
     * callback_ops 指向 CameraDeviceSession实例对象地
     * 址,而 CameraDeviceSession 类继承于 camera3_callback_ops,
     * 所以这里就可以对接起来了
     */
    mCallbackOps = callback_ops;
    // per-device specific initialization
    /* Camera类的 initDevice() 函数是纯虚函数,将会调用
     * V4L2Camera::initDevice(),接着创建
     * V4L2Camera::enqueueRequestBuffers() 线程和
     * V4L2Camera::dequeueRequestBuffers() 线程
     */
    res = initDevice();
    if (res != 0) {
        ALOGE("%s:%d: Failed to initialize device!", __func__, mId);
        return res;
    }
    return 0;
}

如上介绍,在camera HAL,将会通过 mCallbackOps 将状态等信息反馈到 Framework。我们可以查看,在 CameraDeviceSession 类的构造函数可以看到以下信息:

/* camera3_callback_ops 对象将初始化为以下值 */
camera3_callback_ops({&sProcessCaptureResult, &sNotify})

typedef struct camera3_callback_ops {
    void (*process_capture_result)(const struct camera3_callback_ops *,
            const camera3_capture_result_t *result);

    void (*notify)(const struct camera3_callback_ops *,
            const camera3_notify_msg_t *msg);

} camera3_callback_ops_t;

而在 V4L2Camera::initDevice() 函数中,则是创建处理Framework 请求的线程。

configure_streams()

与 initialize() 类似,将会调用至 Camera::configureStreams(camera3_stream_configuration_t *stream_config)。这个调用将使用stream_list中定义的数据流信息来代替之前的数据流配置。在initialize()之后,使用process_capture_request()提交请求之前,这个函数至少被调用一次。在了解 configureStreams() 的具体操作前,我们先看看,函数参数的具体定义是怎样的。

typedef struct camera3_stream_configuration {
    /* Framework请求的stream总数,至少为1,而且至少有一个具有输出能力的流 */
    uint32_t num_streams;

    /* 指向Framework配置的stream */
    camera3_stream_t **streams;

    uint32_t operation_mode;

    const camera_metadata_t *session_parameters;
} camera3_stream_configuration_t;

typedef struct camera3_stream {

    int stream_type;

    uint32_t width;

    uint32_t height;

    int format;

    uint32_t usage;

    uint32_t max_buffers;

    void *priv;

    android_dataspace_t data_space;

    int rotation;

    const char* physical_camera_id;

    /* reserved for future use */
    void *reserved[6];

} camera3_stream_t;

从 camera3_stream_configuration_t 的定义我们可以了解到,主要就是配置相机硬件输出的格式、分辨率、buf数量、以及stream的类型等。

而 configureStreams() 进行了什么操作呢?

int Camera::configureStreams(camera3_stream_configuration_t *stream_config)
{
	/* 检查参数是否正确、有效 */
    int res = validateStreamConfiguration(stream_config);
    if (res) {
        ALOGE("%s:%d: Failed to validate stream set", __func__, mId);
    } else {
        /* 设置stream */
        res = setupStreams(stream_config);
        if (res) {
            ALOGE("%s:%d: Failed to setup stream set", __func__, mId);
        }
    }

    if (!res) {
        /* 保存相应的配置 */
        mInFlightTracker->SetStreamConfiguration(*stream_config);
        // Must provide new settings for the new configuration.
        mSettingsSet = false;
    } else if (res != -EINVAL) {
        // Fatal error, the old configuration is invalid.
        mInFlightTracker->ClearStreamConfiguration();
    }
    // On a non-fatal error the old configuration, if any, remains valid.
    return res;
}

在 setupStreams() 函数又进行了什么操作呢,检查当前的多个stream的格式、分辨率等参数是否一致,然后设置格式、申请buf等。

简单总结 configureStreams() 进行了什么操作:

  1. 检查多个stream参数是否正确有效;
  2. 确认stream的格式和分辨率一致,然后 VIDIOC_S_FMT、VIDIOC_REQBUFS,并根据返回值填充 stream 信息;
  3. 保存 stream 信息到 buffers_in_flight_;
construct_default_request_settings()

该函数将调用至 Camera::constructDefaultRequestSettings(),上面有提到过,Framework将会通过一些请求操作camera HAL,但是可能各个平台的HAL有个别差异,所以需要Framework在提交请求前,先获取默认请求的设置模板(实质是 CameraMetadata),从而再精确化设置具体的参数,该函数就是构造默认请求设置的。

constructDefaultRequestSettings() 函数主要进行以下操作:

  • 在 Camera 类中,有个 mTemplates 指针数据指向了各中请求类型的 CameraMetadata,所以在检查请求类型的有效性之后,将会确认 mTemplates 是否已经保存了相应类型的数据,如果是,直接返回相应的数据;
  • 如果没有相应类型的 CameraMetadata,将通过 initTemplate() 初始化相应的数据并保存;
process_capture_request()

显然的,从该函数名我们就知道,它是负责处理Framework发过来的请求的,下面我们来看看,调用至 Camera::processCaptureRequest() 后又是怎么处理的?

Camera::processCaptureRequest() 主要进行以下操作:

  • 检查输入参数的有效性;
  • 在 preprocessCaptureBuffer() 函数中,获取 output_buffers 同步防护,防止HAL操作该buffer时Framework在读取;
  • 将该请求信息添加到 mInFlightTracker;
  • 通过 enqueueRequest() 函数,添加请求到 request_queue_ 队列中,同时通过条件变量 requests_available_ 通知到 V4L2Camera::enqueueRequestBuffers() 线程进行相应的处理;
V4L2Camera::enqueueRequestBuffers() 操作了什么?

上面我们说到,当HAL接收到Framework请求操作时,将会把请求信息添加到 request_queue_ 队列再通过条件变量通知到 V4L2Camera::enqueueRequestBuffers() 函数(在 Camera::initialize() 中已经创建线程运行该函数了)。

enqueueRequestBuffers() 操作如下:

  1. 通过 dequeueRequest() 函数,从 request_queue_ 队列中获取一个请求操作;
  2. 通过 SetRequestSettings() 函数设置请求中的参数;
  3. 通过 V4L2Wrapper::EnqueueRequest() 函数查询buf信息保存在 buffers_ 以及将buf添加到相机设备驱动;
  4. 接下来通过 V4L2Wrapper::StreamOn() 函数开启流传输;
  5. 最后将会通过条件变量通知到 V4L2Camera::dequeueRequestBuffers() 线程(buffer_dequeuer_线程);
  6. 在 dequeueRequestBuffers() 线程中,通过 DequeueRequest() 拿到图像数据之后,将会调用 completeRequest() 函数;
  7. 在 Camera::completeRequest() 中,处理该请求,而后从 mInFlightTracker 中移除,设置请求的时间戳,接着通过 Camera::notifyShutter() 函数调用了 mCallbackOps->notify() 函数,从而通知Framework;
  8. 最后还会通过 Camera::sendResult() 函数调用 mCallbackOps->process_capture_result() 从而Framework处理该请求信息;

以上,HAL与Framework完成了交互。

dump()、flush()

dump() 函数则是dump各种设置参数等操作,而 flush() 则是进行清除 mInFlightTracker 中保存的各个请求以及通过 V4L2Wrapper::StreamOff() 函数关闭相机设备流传输以及清除buffer等。

疑问:

这个 CameraMetadata 是怎么初始化的?

在 V4L2Camera::initStaticInfo() 中,将会初始化一些配置信息,就是在这里进行初始化操作的。详细可参考 天才2012 的:Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互

参考

Android Camera

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengwei_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值