[Android O] HAL3 之 Open Camera2 流程(三,完结)—— 从 HAL Service 到 Camera HAL

相关文章

  • [Android O] Camera 服务启动流程简析
  • [Android O] HAL3 之 Open Camera2 流程(零)—— 概览
  • [Android O] HAL3 之 Open Camera2 流程(一)—— 从 App 到 CameraService
  • [Android O] HAL3 之 Open Camera2 流程(二)—— 从 CameraService 到 HAL Service
  • [Android O] HAL3 之 Open Camera2 流程(三,完结)—— 从 HAL Service 到 Camera HAL

打开相机流程中,从 APP 到 CameraService 再到 HAL Service 的连路创建流程都已经简略分析了一遍。现在需要分析最后的阶段,即从 HAL Service 连接到 Camera HAL 的部分。
其实 HAL 层真正的运作流程我也还没真正弄清楚,好在现在只需要分析它的构造与初始化部分,这相对来说还是比较简单的。

在 HAL3 中,Camera HAL 的接口转化层(以及流解析层)由 QCamera3HardwareInterface 担当,而接口层与实现层与 HAL1 中基本没什么差别,都是在 mm_camera_interface.cmm_camera.c 中。

那么接口转化层的实例是何时创建的,又是怎么初始化的,创建它的时候,与接口层、实现层又有什么交互?通过下图展示的主要调用流程可以简单了解了解。
[Android O] HAL3 之 Open Camera2 流程(三,完结)—— 从 HAL Service 到 Camera HAL_第1张图片

接下来可以看看代码流程。

HAL Service

CameraModule

文件路径:hardware\interfaces\camera\common\1.0\default\CameraModule.cpp

上回说到,CameraDevice::open 的实现中,调用了 mModule->open,即 CameraModule::open。来看看它的具体实现。

通过代码来看,它做的事并不多,主要是调用 mModule->common.methods->open,来进入下一层级的流程。
而这里则需要注意了,open 是一个函数指针,它指向的是 QCamera2Factory 的 camera_device_open 方法,至于为什么和 QCamera2Factory 有关,这就要回头看 HAL Service 的启动初始化流程了。对我来说这不重要,就不赘述了。

int CameraModule::open(const char* id, struct hw_device_t** device) {
    int res;
    ATRACE_BEGIN("camera_module->open");
    res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
    ATRACE_END();
    return res;
}

Camera HAL

QCamera2Factory

文件路径:hardware\qcom\camera\qcamera2\QCamera2Factory.cpp

省略一些不关心的内容,这里实际上做的事也不多。
- 首先注意第 31~33 行,这里就将前面所说的 open 函数指针指定为了 camera_device_open 这个方法。
- 第 22~27 行,注意到这里通过宏定义添加了对 HAL1 的兼容操作。实际上是要调用 cameraDeviceOpen 来进行下一步操作。

/*===========================================================================
 * FUNCTION   : camera_device_open
 *
 * DESCRIPTION: static function to open a camera device by its ID
 *
 * PARAMETERS :
 *   @camera_id : camera ID
 *   @hw_device : ptr to struct storing camera hardware device info
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int QCamera2Factory::camera_device_open(
    const struct hw_module_t *module, const char *id,
    struct hw_device_t **hw_device)
{
    /* Do something in */
    ......
    /* Do something out */

#ifdef QCAMERA_HAL1_SUPPORT
    if(gQCameraMuxer)
        rc =  gQCameraMuxer->camera_device_open(module, id, hw_device);
    else
#endif
        rc = gQCamera2Factory->cameraDeviceOpen(atoi(id), hw_device);
    return rc;
}

struct hw_module_methods_t QCamera2Factory::mModuleMethods = {
    .open = QCamera2Factory::camera_device_open,
};

cameraDeviceOpen 做了什么呢:

  • 第 22 行,首先创建了 QCamera3HardwareInterface 的实例
  • 第 28 行,调用实例的 openCamera 方法。
/*===========================================================================
 * FUNCTION   : cameraDeviceOpen
 *
 * DESCRIPTION: open a camera device with its ID
 *
 * PARAMETERS :
 *   @camera_id : camera ID
 *   @hw_device : ptr to struct storing camera hardware device info
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int QCamera2Factory::cameraDeviceOpen(int camera_id,
                    struct hw_device_t **hw_device)
{
    /* Do something in */
    ......
    /* Do something out */

    if ( mHalDescriptors[camera_id].device_version == CAMERA_DEVICE_API_VERSION_3_0 ) {
        QCamera3HardwareInterface *hw = new QCamera3HardwareInterface(mHalDescriptors[camera_id].cameraId,
                mCallbacks);
        if (!hw) {
            LOGE("Allocation of hardware interface failed");
            return NO_MEMORY;
        }
        rc = hw->openCamera(hw_device);
        if (rc != 0) {
            delete hw;
        }
    }

    /* Do something in */
    ......
    /* Do something out */

    return rc;
}

QCamera3HardwareInterface

文件路径:hardware\qcom\camera\qcamera2\hal3\QCamera3HWI.cpp

首先需要注意的是内部成员 mCameraOps 的定义。
在构造实例时,有 mCameraDevice.ops = &mCameraOps;,这点需要记住。

camera3_device_ops_t QCamera3HardwareInterface::mCameraOps = {
    .initialize                         = QCamera3HardwareInterface::initialize,
    .configure_streams                  = QCamera3HardwareInterface::configure_streams,
    .register_stream_buffers            = NULL,
    .construct_default_request_settings = QCamera3HardwareInterface::construct_default_request_settings,
    .process_capture_request            = QCamera3HardwareInterface::process_capture_request,
    .get_metadata_vendor_tag_ops        = NULL,
    .dump                               = QCamera3HardwareInterface::dump,
    .flush                              = QCamera3HardwareInterface::flush,
    .reserved                           = {0},
};

再来继续看看 openCamera 是怎么实现的。

  • 第 7 行,调用另一个 openCamera 方法,这是具体实现的部分。
  • 第 8~11 行,打开相机成功后,将设备结构中的 common 部分通过双重指针 hw_device 返回。
  • 第 25 行,这里就开始进入接口层了,调用的是接口层中的 camera_open 接口。注意此处获取到了 mCameraHandle,应该可以说,它就是接口转化层到接口层连路的一个载体。
  • 第 31 行,注意这里传入了一个 camEvtHandle
int QCamera3HardwareInterface::openCamera(struct hw_device_t **hw_device)
{
    /* Do something in */
    ......
    /* Do something out */

    rc = openCamera();
    if (rc == 0) {
        *hw_device = &mCameraDevice.common;
    } else
        *hw_device = NULL;

    /* Do something in */
    ......
    /* Do something out */
    return rc;
}

int QCamera3HardwareInterface::openCamera()
{
    /* Do something in */
    ......
    /* Do something out */

    rc = camera_open((uint8_t)mCameraId, &mCameraHandle);

    /* Do something in */
    ......
    /* Do something out */

    rc = mCameraHandle->ops->register_event_notify(mCameraHandle->camera_handle,
            camEvtHandle, (void *)this);

    /* Do something in */
    ......
    /* Do something out */ 

    rc = mCameraHandle->ops->get_session_id(mCameraHandle->camera_handle,
        &sessionId[mCameraId]);

    /* Do something in */
    ......
    /* Do something out */

    return NO_ERROR;
}

上面是接口转化层中,关于 openCamera 的部分,下面继续看看它的初始化函数。
在前一篇文章中已经分析过,创建 CameraDeviceSession 实例时,会调用它内部的初始化方法,而这其中包含了调用 QCamera3HWI 的初始化方法 initialize

  • 第 12 行,调用了真正实现的初始化逻辑的函数。
  • 第 37 行,参数(mParameters)初始化,注意这里的参数和 CameraParameter 是不同的,它是 metadata_buffer 相关参数的结构。
  • 第 42 行,注意,这里将 camera3_call_back_opsmCallbackOps 关联了起来。
  • 第 44 行,获取 mChannelHandle 这一句柄,调用的方法实际是 mm_camera_interface.c 中的 mm_camera_intf_add_channel
int QCamera3HardwareInterface::initialize(const struct camera3_device *device,
                                  const camera3_callback_ops_t *callback_ops)
{
    LOGD("E");
    QCamera3HardwareInterface *hw =
        reinterpret_cast(device->priv);
    if (!hw) {
        LOGE("NULL camera device");
        return -ENODEV;
    }

    int rc = hw->initialize(callback_ops);
    LOGD("X");
    return rc;
}

int QCamera3HardwareInterface::initialize(
        const struct camera3_callback_ops *callback_ops)
{
    ATRACE_CALL();
    int rc;

    LOGI("E :mCameraId = %d mState = %d", mCameraId, mState);
    pthread_mutex_lock(&mMutex);

    // Validate current state
    switch (mState) {
        case OPENED:
            /* valid state */
            break;
        default:
            LOGE("Invalid state %d", mState);
            rc = -ENODEV;
            goto err1;
    }

    rc = initParameters();
    if (rc < 0) {
        LOGE("initParamters failed %d", rc);
        goto err1;
    }
    mCallbackOps = callback_ops;

    mChannelHandle = mCameraHandle->ops->add_channel(
            mCameraHandle->camera_handle, NULL, NULL, this);
    if (mChannelHandle == 0) {
        LOGE("add_channel failed");
        rc = -ENOMEM;
        pthread_mutex_unlock(&mMutex);
        return rc;
    }

    pthread_mutex_unlock(&mMutex);
    mCameraInitialized = true;
    mState = INITIALIZED;
    LOGI("X");
    return 0;

err1:
    pthread_mutex_unlock(&mMutex);
    return rc;
}

接口层:mm_camera_interface.c

文件路径:hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\mm_camera_interface.c

camera_open 中干的事也不多,我省略掉了关于为 cam_obj 分配内存以及初始化的部分。实际上是调用实现层中的 mm_camera_open 来真正实现打开相机设备的操作,设备的各种信息都填充到 cam_obj 结构中。

int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl)
{
    int32_t rc = 0;
    mm_camera_obj_t *cam_obj = NULL;

    /* Do something in */
    ......
    /* Do something out */

    rc = mm_camera_open(cam_obj);

    /* Do something in */
    ......
    /* Do something out */
}

而关于初始化时调用的 mm_camera_intf_add_channel 代码如下。在第 16 行,通过调用实现层的 mm_camera_add_channel 来获取一个 channel id,也就是其句柄。

static uint32_t mm_camera_intf_add_channel(uint32_t camera_handle,
                                           mm_camera_channel_attr_t *attr,
                                           mm_camera_buf_notify_t channel_cb,
                                           void *userdata)
{
    uint32_t ch_id = 0;
    mm_camera_obj_t * my_obj = NULL;

    LOGD("E camera_handler = %d", camera_handle);
    pthread_mutex_lock(&g_intf_lock);
    my_obj = mm_camera_util_get_camera_by_handler(camera_handle);

    if(my_obj) {
        pthread_mutex_lock(&my_obj->cam_lock);
        pthread_mutex_unlock(&g_intf_lock);
        ch_id = mm_camera_add_channel(my_obj, attr, channel_cb, userdata);
    } else {
        pthread_mutex_unlock(&g_intf_lock);
    }
    LOGD("X ch_id = %d", ch_id);
    return ch_id;
}

实现层:mm_camera.c

文件路径:hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\mm_camera.c

终于来到最底层的实现了,mm_camera_open 主要工作是填充 my_obj,并且启动、初始化一些线程相关的东西,关于线程的部分我这里就省略掉了。

  • 第 17 行,此处调用的函数是为了获取 my_obj 的句柄,这里不深入分析。
  • 第 21~38 行,读取设备文件的文件描述符,存到 my_obj->ctrl_fd 中。注意设备文件的路径是 /dev/video0(video 后面的数字表示打开设备的 id),并且在某些打开失败的情况下,会定时重新尝试打开直至成功。
  • 第 49 行,成功获取到文件描述符后,就要获取 session 的 id 了,这里的具体实现也不深究。
int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
    int32_t rc = 0;
    int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES;
    uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP;
    int cam_idx = 0;
    const char *dev_name_value = NULL;
    int l_errno = 0;
    pthread_condattr_t cond_attr;

    LOGD("begin\n");

    if (NULL == my_obj) {
        goto on_error;
    }
    dev_name_value = mm_camera_util_get_dev_name(my_obj->my_hdl);
    if (NULL == dev_name_value) {
        goto on_error;
    }
    snprintf(dev_name, sizeof(dev_name), "/dev/%s",
             dev_name_value);
    sscanf(dev_name, "/dev/video%d", &cam_idx);
    LOGD("dev name = %s, cam_idx = %d", dev_name, cam_idx);

    do{
        n_try--;
        errno = 0;
        my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
        l_errno = errno;
        LOGD("ctrl_fd = %d, errno == %d", my_obj->ctrl_fd, l_errno);
        if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 )) {
            break;
        }
        LOGE("Failed with %s error, retrying after %d milli-seconds",
              strerror(errno), sleep_msec);
        usleep(sleep_msec * 1000U);
    }while (n_try > 0);

    if (my_obj->ctrl_fd < 0) {
        LOGE("cannot open control fd of '%s' (%s)\n",
                  dev_name, strerror(l_errno));
        if (l_errno == EBUSY)
            rc = -EUSERS;
        else
            rc = -1;
        goto on_error;
    } else {
        mm_camera_get_session_id(my_obj, &my_obj->sessionid);
        LOGH("Camera Opened id = %d sessionid = %d", cam_idx, my_obj->sessionid);
    }

    /* Do something in */
    ......
    /* Do something out */

    /* unlock cam_lock, we need release global intf_lock in camera_open(),
     * in order not block operation of other Camera in dual camera use case.*/
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

初始化的相关部分,mm_camera_add_channel 代码如下。

  • 第 10~15 行,从现有的 Channel 中找到第一个状态为 NOTUSED 的,获取到 ch_obj 中。
  • 第 17~27 行,初始化 ch_obj 结构。首先调用 mm_camera_util_generate_handler 为其生成一个句柄(也是该函数的返回值),然后将状态设置为 STOPPED,注意这里还保存了 my_obj 的指针及其 session id,最后调用 mm_channel_init 完成了 Channel 的初始化。
uint32_t mm_camera_add_channel(mm_camera_obj_t *my_obj,
                               mm_camera_channel_attr_t *attr,
                               mm_camera_buf_notify_t channel_cb,
                               void *userdata)
{
    mm_channel_t *ch_obj = NULL;
    uint8_t ch_idx = 0;
    uint32_t ch_hdl = 0;

    for(ch_idx = 0; ch_idx < MM_CAMERA_CHANNEL_MAX; ch_idx++) {
        if (MM_CHANNEL_STATE_NOTUSED == my_obj->ch[ch_idx].state) {
            ch_obj = &my_obj->ch[ch_idx];
            break;
        }
    }

    if (NULL != ch_obj) {
        /* initialize channel obj */
        memset(ch_obj, 0, sizeof(mm_channel_t));
        ch_hdl = mm_camera_util_generate_handler(ch_idx);
        ch_obj->my_hdl = ch_hdl;
        ch_obj->state = MM_CHANNEL_STATE_STOPPED;
        ch_obj->cam_obj = my_obj;
        pthread_mutex_init(&ch_obj->ch_lock, NULL);
        ch_obj->sessionid = my_obj->sessionid;
        mm_channel_init(ch_obj, attr, channel_cb, userdata);
    }

    pthread_mutex_unlock(&my_obj->cam_lock);

    return ch_hdl;
}

简图总结

在这一部分,连路的最终面貌可以简单地表示如下图。
注意图中的 captureResultCb 相关的内容我并没有分析,这是一个回调函数,应该是在拍照和预览数据上传时用到的,把它画出来是因为在培训的时候有提到,所以这里也提及一下,表明它与 mCallbackOps 有这么一个联系。
[Android O] HAL3 之 Open Camera2 流程(三,完结)—— 从 HAL Service 到 Camera HAL_第2张图片

总而言之,上面这一顿操作下来后,相机从上到下的整个连路就已经打通,接下来应该只要 APP 按照流程下发 Preview 的 Request 就可以开始获取预览数据了。

如果不出意外的话…下一步的学习计划就是了解 Preview,Capture 的控制、数据流。

你可能感兴趣的:([Android O] HAL3 之 Open Camera2 流程(三,完结)—— 从 HAL Service 到 Camera HAL)