【Camera专题】Sprd-基于log的Camera流程分析1-【Open流程】

一、前言

天苍苍,野茫茫,风吹草低见牛羊!
高通的旗舰机项目基本搞定,就等6月底上市了,期望能卖得不错吧!
这半年做高通项目,学习到许多关于Camera的知识,当然还需要深入去学习!
不过,下半年要做展讯旗舰机器,又要开始踩展讯的坑!
前段时间写了一下Sprd的Camera流程分析,感觉之前写得不太详细,
因此,打算基于log分析源码,对整个架构更加深入的学习!

二、Open流程

1.整体架构

架构

整体架构图可知,这个流程

APP层->Framework层->通过Binder机制与Service进程通信
->CameraService->通过JNI->调用到HAL层->Kernel层

这也是本文的分析思路!
PS:由于CameraService会在开机的时候就初始化,因此我们优先简单分析!

2.log分析-【开机第一次启动Camera:冷启动】

2.1 CameraService的启动-开机初始化
开机时,就会初始化CameraService,这里就是你可以看到的跟Camera相关的第一行log

  • 2.1.1 log


    image.png
  • 2.1.2源码
    frameworks/av/services/camera/libcameraservice/CameraService.cpp

CameraService::CameraService()
    :mSoundRef(0), mModule(0)
{
    //这里就是我们开机时,打印的Cameralog,getpid获取进程号:223
    ALOGI("CameraService started (pid=%d)", getpid());
    gCameraService = this;

    for (size_t i = 0; i < MAX_CAMERAS; ++i) {
        mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
    }    

    this->camera_device_status_change = android::camera_device_status_change;
}

开机时,CameraService就会启动,进行初始化,具体的细节就不去追究。
流程:
CameraService: CameraService started (pid=223)//进程号:223
CameraService: CameraService::onFirstRef //第一次启动,执行以下动作

  • 1.CameraService: Loaded "Sprd Camera HAL3" camera module //加载HAL3 module
  • 2.CameraService: setCameraFree cameraId=0和cameraId=1 //设置camera当前的状态
  • 3.CameraDeviceFactory::registerService(this) 注册服务
void CameraService::onFirstRef()
{
    LOG1("CameraService::onFirstRef");

    BnCameraService::onFirstRef();

    if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
                (const hw_module_t **)&mModule) < 0) {
        ALOGE("Could not load camera HAL module");
        mNumberOfCameras = 0;
    }
    else {
        //加载HAL3 module
        ALOGI("Loaded \"%s\" camera module", mModule->common.name);
        mNumberOfCameras = mModule->get_number_of_cameras();
        if (mNumberOfCameras > MAX_CAMERAS) {
            ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
                    mNumberOfCameras, MAX_CAMERAS);
            mNumberOfCameras = MAX_CAMERAS;
        }
        //设置camera当前状态为空闲
        for (int i = 0; i < mNumberOfCameras; i++) {
            setCameraFree(i);
        }

        if (mModule->common.module_api_version >=
                CAMERA_MODULE_API_VERSION_2_1) {
            mModule->set_callbacks(this);
        }

        getVendorTagOps();
        //注册Camera服务
        CameraDeviceFactory::registerService(this);
    }
}

这里为什么要设置camera的状态呢?

void CameraService::setCameraFree(int cameraId) {
    //写入0表示camera处于空闲状态  
    android_atomic_write(0, &mBusy[cameraId]);
    ALOGV("setCameraFree cameraId=%d", cameraId);
}
void CameraService::setCameraBusy(int cameraId) {
    //写入1表示camera处于繁忙状态
    android_atomic_write(1, &mBusy[cameraId]);
    ALOGV("setCameraBusy cameraId=%d", cameraId);
}

我们需要这个busy位的原因是一个新的CameraService::connect()请求
可能在前一个客户端析构函数尚未运行或正在运行时进入仍在运行。
如果前一个client(客户端)的最后一个强引用已经消失,
但是析构函数还没有完成,我们不应该允许new Client
因为我们需要等待前面的客户端首先销毁硬件。


因此:
CameraService: setCameraFree cameraId=1
CameraService: setCameraBusy cameraId=1
会成对出现,初始化的时候,会设置成free,
打开摄像头时,会设置成busy,
关闭摄像头时,状态又会设置成free。

另外:说一下 getNumberOfCameras()函数


getNumberOfCameras=2表示的是系统支持前摄和后摄,2个摄像头

也许你的系统还可能支持前副摄像、后副摄像
那么应该在** kCameraInfo数组**再添加2个

这里CameraService开机的初始化就简单分析完了
既然是开机,所以接下来第一次打开Camera属于冷启动
需要初始化很多东西,这就是为啥冷启动会慢的原因!

2.2 APP层 和 Framwork层

  • 2.2.1 log


  • 2.2.2 源码
    APP这一块,懂得一些基本知识,
    由于学的不够深,Framwork也没研究过,
    因此就不做深入分析,以免误人子弟。
    以上就是你能看到的log!
    最后这里可以看到调用到getSensorStaticInfo这个Hal层的方法!

2.3 Hal层

  • 2.3.1 log


getSensorStaticInfo函数主要用来读取Sensor的信息,看源码分析!
由于是开机第
一次启动时,调用getSensorStaticInfo失败了,why?

先打开后摄,因为没有后摄,所以获取信息失败了

后面又重新调用一次getSensorStaticInfo,然后获取到sensor info

然后打开前摄,有摄,能获取到sensor信息

  • 2.3.2 源码
    vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3Setting.cpp
int SprdCamera3Setting::getSensorStaticInfo(int32_t cameraId) {
    struct sensor_drv_context *sensor_cxt = NULL;
···省略源码
    //因为是开机第一次运行,这里肯定是失败的,不会跑进去
    if (alreadyGetSensorStaticInfo[cameraId] != 0) { 
        HAL_LOGI("already get sensor info");
        return 0;
    }    

    HAL_LOGI("E");//可以看到这个log打印了
···省略源码
    ret = sensor_open_common(sensor_cxt, cameraId, 0);
    if (ret) {
        //这里的log打印了,我们跟进sensor_open_common函数去看
        HAL_LOGE("open camera (%d) failed", cameraId);
        setLargestSensorSize(cameraId, default_sensor_max_sizes[cameraId].width,
                             default_sensor_max_sizes[cameraId].height);
        goto exit;
    }
···后面先这些省略不看
}

这里我们继续跟进sensor_open_common函数去看
vendor/sprd/modules/libcamera/sensor/sensor_drv_u.c
打开Camera的步骤
NOTE:when open camera,you should do the following steps.
步骤1.register all sensor for the following first open sensor.
第一次打开摄像头的时候,注册所有的sensor

步骤2.if first open sensor failed, identify module list to save sensor index.
如果第一次打开失败,重新identify 所有的sensor并保存起来
步骤3.try open sensor second time.
尝试重新打开sensor
步骤4.if 3 steps failed,sensor open failed.
如果步骤3失败了,那么sensor打开失败

来看源码:

cmr_int sensor_open_common(struct sensor_drv_context *sensor_cxt,
                           cmr_u32 sensor_id, cmr_uint is_autotest) {
    ATRACE_BEGIN(__FUNCTION__);
···
    //这条log打印了
    SENSOR_LOGI("0, start,id %d autotest %ld", sensor_id, is_autotest);

    /* init ctx, exif, load sensor file, open hw driver with sensor id.*/
    ret_val = sensor_context_init(sensor_cxt, sensor_id, is_autotest);
···
    /* create sensor process thread. */
    ret_val = sensor_create_ctrl_thread(sensor_cxt);
    /*这个函数会 打印 sns_drv_u: 1593, sensor_create_ctrl_thread: is_inited 0*/
···
    /* create the hw obj of kernel driver. */
···
    /*该函数 打印 hw_sensor: 107, hw_sensor_drv_create: sensor_id:0*/
    fd_sensor = hw_sensor_drv_create(&input_ptr, &hw_drv_handle);
     /*该函数会继续调用_hw_sensor_dev_init函数 */
    sensor_cxt->fd_sensor = fd_sensor;
    sensor_cxt->hw_drv_handle = hw_drv_handle;
    sensor_cxt->sensor_hw_handler = hw_drv_handle;

    /*load all the sensor ICs' info according the indexs stored in sensor idx file*/
    /*根据存储在sensor idx文件中的索引 加载传感器ICs的所有信息*/
    sensor_load_idx_inf_file(sensor_cxt);
    if (sensor_cxt->sensor_identified) {
        //这里调用sns_load_drv -> 里面继续调用sensor_get_match_info -> sensor_get_module_tab
        if (SENSOR_SUCCESS == sns_load_drv(sensor_cxt, SENSOR_MAIN)){
            sensor_num++;
        }
    if (SENSOR_SUCCESS == sns_load_drv(sensor_cxt, SENSOR_SUB)) {
            sensor_num++;
        }
···
        SENSOR_LOGI("1 is identify, register OK");
        /*first open sensor*/
        ret_val = sensor_open(sensor_cxt, sensor_id);
        if (ret_val != SENSOR_SUCCESS) {
            SENSOR_LOGI("first open sensor failed,start identify");
        }

    /* scan the devices in cfg list and find out the correct sensor driver */
    if ((!sensor_cxt->sensor_identified) || (ret_val != SENSOR_SUCCESS)) {
        sensor_num = 0;
        SENSOR_LOGI("register sensor fail, start identify");
        if (sensor_identify(sensor_cxt, SENSOR_MAIN))
            sensor_num++;
        if (sensor_identify(sensor_cxt, SENSOR_SUB))
            sensor_num++;
···
        /*再一次打开*/
        ret_val = sensor_open(sensor_cxt, sensor_id);
    }
    sensor_cxt->sensor_identified = SCI_TRUE;
    //保存识别到的摄像头信息,后续再打开摄像头,直接读idx文件即可
    sensor_save_idx_inf_file(sensor_cxt);
    sensor_rid_save_sensor_info(sensor_cxt);

···
    SENSOR_LOGI("total camera number %d", sensor_num);

init_exit:
    if (SENSOR_SUCCESS != ret_val) {
        //打开摄像头失败,删除相关资源
        sensor_destroy_ctrl_thread(sensor_cxt);
        hw_sensor_drv_delete(hw_drv_handle);
        sensor_cxt->hw_drv_handle = NULL;
        sensor_cxt->sensor_hw_handler = NULL;
    }
    return ret_val;
}

加了很多注释,就不在赘述。
根据源码,我们继续看具体的log


第一段log
第一段log续

这份log执行的就是步骤1和步骤2
步骤1.register all sensor for the following first open sensor.
第一次打开摄像头的时候,注册所有的sensor

步骤2.if first open sensor failed, identify module list to save sensor index.
如果第一次打开失败,重新identify 所有的sensor并保存相关信息

第二段log

加载的cfg表格源码
vendor/sprd/modules/libcamera/sensor/sensor_cfg.c


sp2609宏定义的位置
device/sprd/sharkle/sp9820e_xtc_i17/BoardConfig.mk

#camera sensor type
#CAMERA_SENSOR_TYPE_BACK := "SP2609"
CAMERA_SENSOR_TYPE_FRONT := "SP2609"
#CAMERA_SENSOR_TYPE_BACK_EXT :=
#CAMERA_SENSOR_TYPE_FRONT_EXT :=

接下来调用
sns_ops->create_handle(&sns_init_para,&sensor_cxt->sns_ic_drv_handle);
这个函数如下


继续看log

第二段log续
第二段log续2
第三段log
第三段log续2
第四段log
第四段log续2
第5段log
第6段
第7段log

步骤3.try open sensor second time.
尝试重新打开sensor
步骤4.if 3 steps failed,sensor open failed.
如果步骤3失败了,那么sensor打开失败

第一次Camera启动分析就结束了

3.log分析-【第二次启动分析】

log 1
log 2
log 3
log 4
log 5
log 6
log 7

总结

对比第一次冷启动热启动
第一次冷启动主要是多了CameraService的创建,
还有getSensorStaticinfo时,先去search所有的camera信息,保存起来,
后续再次打开Camera,直接从缓存读取信息,因此冷启动慢,热启动快。

其他的调用流程都是一致的!

Stay Hungry,Stay Foolish!

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

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,696评论 0 3
  • 把之前开发遇到的一些问题总结一下, 由于很多东西都是和具体平台(高通/MKT)相关的, 本来有更多的内容可以总结,...
    幽客阅读 25,359评论 10 30
  • 2. 初始化 初始化主要分为几个部分,SM注册service,app层和framework层去打开相机操作,底层的...
    Lemon_Home阅读 5,876评论 0 10
  • 现在的工作需要用到camera模块,所以打算分析Zxing中的camera实现,来了解android的camera...
    暴风雨1024阅读 3,939评论 1 5
  • 最近拜读了吴晓波老师的两部《大败局》。 《大败局》介绍了19家企业如何迅速壮大,而又如何快速消亡的,其跌宕起伏让人...
    十个雨点阅读 3,285评论 0 4