【camera专题】 Camera Open/Close (1)

系列文章

基于HAL1: camera hal层框架源码系列:
HAL1 – Camera Open/Close (1)

一、代码流程图

整个流程就很清晰了。

二、源码及log分析

1.camera service启动(开机时调用一次)

I CameraService: CameraService started (pid=648)
I CameraService: CameraService process starting

2.get_num_of_cameras:获取摄像头数量(开机时调用一次)

I QCamera : <MCI><INFO> get_num_of_cameras: 2783: dev_info[id=0,name='video1']
I QCamera : <MCI><INFO> get_num_of_cameras: 2783: dev_info[id=1,name='video2']
I QCamera : <MCI><INFO> sort_camera_info: 2617: Camera id: 0 facing: 0, type: 1 is_yuv: 0
I QCamera : <MCI><INFO> sort_camera_info: 2617: Camera id: 1 facing: 1, type: 1 is_yuv: 0
I QCamera : <MCI><INFO> sort_camera_info: 2623: Number of cameras 2 sorted 2
I QCamera : <MCI><INFO> get_num_of_cameras: 2801: num_cameras=2
I [email protected]: Loaded "QCamera Module" camera module
QCamera : <HAL><INFO> getCameraInfo: 342: Camera id 0 API version 768

开机的时候,会去调用get_num_of_cameras获取摄像头数量,sort_camera_info查询摄像头信息,
只会调用一次。

  • 摄像头节点 : /dev/video1 ,/dev/video2

  • 摄像头数量:num_cameras=2

  • 摄像头信息:
    Camera id: 0 facing: 0(后摄), type: 1 is_yuv: 0(不是YUV类型)
    Camera id: 1 facing: 1(前摄), type: 1 is_yuv: 0(不是YUV类型)
    ps:判断是前摄或者后置,要根据facing来,camera id 0 可能是前摄或者后摄!
    不能根据camera id判断前后摄!

  • 摄像头加载的模块: [email protected]: Loaded “QCamera Module” camera module

以上都是开机时调用的,也是准备工作,接下来就是每次打开camera都会调用的步骤

3.service连接client实例

I CameraService: CameraService::connect call (PID -1 "org.codeaurora.snapcam", camera ID 0) for HAL 
version 256 and Camera API version 1

可以看到Log打印的信息有App API的版本和HAL版本, API版本只有API1 和API2, HAL版本一般有三个结果: 256, 768, default.
256代表使用HAL1, 768为HAL3, default一般表示调用的是没有特别指定版本, 根据平台配置来决定,这种情况如果想知道到底使用的哪个版本, 需要看平台代码或者一些Log.

4.openCamera

I QCamera : <HAL><INFO> openLegacy: 503: openLegacy halVersion: 256 cameraId = 0  
I QCamera : <HAL><INFO> openCamera: 1915: [KPI Perf]: E PROFILE_OPEN_CAMERA camera id 0
I mm-camera: <SHIM  >< INFO> 538: mct_shimlayer_start_session: Session start session =2
I mm-camera: <SHIM  >< INFO> 132: mct_shimlayer_module_init: Module name : iface: E
  • openLegacy:
    ![](https://img-blog.csdnimg.cn/img_convert/5ed70260d82aa68dd34f9a67b979843c.png
    openLegacy可以设置 用hal1的方式还是hal3的方式打开camera。
    这是版本是256,对应hal1!

  • regiseter ops(绑定camera操作函数)

QCamera2HardwareInterface::QCamera2HardwareInterface(uint32_t cameraId)
   : mCameraId(cameraId),
···
      m_bNeedHalPP(FALSE)
{
    
    
#ifdef TARGET_TS_MAKEUP
    memset(&mFaceRect, -1, sizeof(mFaceRect));
#endif
    getLogLevel();
    ATRACE_CAMSCOPE_CALL(CAMSCOPE_HAL1_QCAMERA2HWI);
    mCameraDevice.common.tag = HARDWARE_DEVICE_TAG;
    mCameraDevice.common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
    mCameraDevice.common.close = close_camera_device;
    mCameraDevice.ops = &mCameraOps;
    mCameraDevice.priv = this;

这里 mCameraDevice.ops = &mCameraOps;

这里分析一下Open camera 流程:

1. QCamera2HardwareInterface::openCamera(struct hw_device_t **hw_device)  --HWI
|
2.rc = openCamera();  --mm_camera_interface.c
|
3.rc = mm_camera_open(cam_obj);  --mm_camera.c

mm_camera_open

int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
    
    
···
    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;
//第1步:Open Video node
        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);
    }

#ifdef DAEMON_PRESENT
    /* open domain socket*/
    n_try = MM_CAMERA_DEV_OPEN_TRIES;
    do {
    
    
        n_try--;
//第2步:Create Socket
        my_obj->ds_fd = mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
        l_errno = errno;
        LOGD("ds_fd = %d, errno = %d", my_obj->ds_fd, l_errno);
        if((my_obj->ds_fd >= 0) || (n_try <= 0 )) {
    
    
            LOGD("opened, break out while loop");
            break;
        }
        LOGD("failed with I/O error retrying after %d milli-seconds",
              sleep_msec);
        usleep(sleep_msec * 1000U);
    } while (n_try > 0);

    if (my_obj->ds_fd < 0) {
    
    
        LOGE("cannot open domain socket fd of '%s'(%s)\n",
                  dev_name, strerror(l_errno));
        rc = -1;
        goto on_error;
    }
#else /* DAEMON_PRESENT */
    cam_status_t cam_status;
//第3步:open session
    cam_status = mm_camera_module_open_session(my_obj->sessionid,
            mm_camera_module_event_handler);
    if (cam_status < 0) {
    
    
        LOGE("Failed to open session");
        if (cam_status == CAM_STATUS_BUSY) {
    
    
            rc = -EUSERS;
        } else {
    
    
            rc = -1;
        }
        goto on_error;
    }
#endif /* DAEMON_PRESENT */

    pthread_condattr_init(&cond_attr);
    pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);

    pthread_mutex_init(&my_obj->msg_lock, NULL);
    pthread_mutex_init(&my_obj->cb_lock, NULL);
    pthread_mutex_init(&my_obj->evt_lock, NULL);
    pthread_cond_init(&my_obj->evt_cond, &cond_attr);
    pthread_condattr_destroy(&cond_attr);

//第4步:Launch evt Thread 
    LOGD("Launch evt Thread in Cam Open");
    snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Dispatch");
    mm_camera_cmd_thread_launch(&my_obj->evt_thread,
                                mm_camera_dispatch_app_event,
                                (void *)my_obj);

//第5步:Launch evt Poll
    /* launch event poll thread
     * we will add evt fd into event poll thread upon user first register for evt */
    LOGD("Launch evt Poll Thread in Cam Open");
    snprintf(my_obj->evt_poll_thread.threadName, THREAD_NAME_SIZE, "CAM_evntPoll");
    mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
                                 MM_CAMERA_POLL_TYPE_EVT);
    mm_camera_evt_sub(my_obj, TRUE);

    /* 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);
    LOGD("end (rc = %d)\n", rc);
    return rc;

on_error:

    if (NULL == dev_name_value) {
    
    
        LOGE("Invalid device name\n");
        rc = -1;
    }

    if (NULL == my_obj) {
    
    
        LOGE("Invalid camera object\n");
        rc = -1;
    } else {
    
    
        if (my_obj->ctrl_fd >= 0) {
    
    
            close(my_obj->ctrl_fd);
            my_obj->ctrl_fd = -1;
        }
#ifdef DAEMON_PRESENT
        if (my_obj->ds_fd >= 0) {
    
    
            mm_camera_socket_close(my_obj->ds_fd);
            my_obj->ds_fd = -1;
        }
#endif
    }

    /* 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;
}

关键步骤:

  • 1.Open Video node

老架构的代码是通过Socket

新架构的代码

  • 2. 创建session ->调用到 mct_shimlayer_start_session

  • 3.Launch evt cmd Thread
    给注册了事件的应用 分派事件

  • 4.Launch poll Thread
    该线程会创建一个管道,用poll(轮询)的方式监听内核的event事件

继续当一名咸鱼(* ̄︶ ̄)!

Stay Hungry,Stay Foolish!

猜你喜欢

转载自blog.csdn.net/justXiaoSha/article/details/121225063