[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 CamProvider@2.4-impl: 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判断前后摄!


  • 摄像头加载的模块: CamProvider@2.4-impl: 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:



    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!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容