天天看點

高通camera架構_流程淺析(1)1.    Introduction2.   高通Camera架構簡介

該文檔主要淺析camera架構,後續會增加機制相關内容:

1.    Introduction

本文檔主要講解高通Camera整體架構。

部分内容或參考資料可參考個人部落格android開發欄目:http://blog.sina.com.cn/betterforlife 

2.   高通Camera架構簡介

總體框圖如下:

下面簡要走一下流程,不涉及具體代碼:

1、初始化CameraService

在frameworks/av/media/mediaserver/Main_mediaserver.cpp中會對CameraService進行初始化:

CameraService::instantiate();

CameraService的父類BinderService定義了instantiate()函數:

static void instantiate() { publish(); }

CameraService的啟動發生在init.rc中:

service media /system/bin/mediaserver

class main

user media

group audio camerainet net_bt net_bt_admin net_bw_acct drmrpc

在CameraService初始化過程會從hal層擷取一些基本資訊,如支援的最大camera數目,如下圖所示:

2、連接配接CameraService

如下圖所示:

2.1 Hal 1.0版本架構分析

以設定camera sharpness(銳度)參數為例:

資料流app parameter->java interface->jni->cameraclient->binder->camera service->hal->daemon->kernel

    如下圖所示:

2.2 Hal v3與v1差別與過渡

2.2.1 簡介

在Android 5.0上,Google正式的将Camera HAL 3.0作為一個标準配置進行了發行,當然Camera HALV1也是作為相容标準可用。

HAL V3與V1本質差別是把幀的參數和幀的圖像資料綁定到了一起,比如V1的時候一張preview上來的YUV幀,APP是不知道這個 YUV幀采用的Gain和曝光時間究竟是多少,但是在V3

裡面,每一幀都有一個資料結構來描述,其中包括了幀的參數和幀的資料,當APP發送一個request的時候是需要指定使用什麼樣的參數,到request傳回的時候,傳回資料中就有圖像資料和相關的參數配置。

2.2.2 HAL 1.0參數設定

A、V1增加設定參數:增加OIS光學防抖參數設定(ois參數一般不作為設定參數,在本文檔僅作實驗測試),僅作流程分析對比。

1)  添加接口函數,參考public void setSaturation(int saturation)設定

在code/frameworks/base/core/java/android/hardware/Camera.java檔案增加接口:

        publicvoid setOis(int saturation){

              …………

             set(KEY_QC_OIS, String.valueOf(OIS));

}

2)  App設定參數調用,假設設定ois值為1

參考packages/apps/SnapdragonCamera/src/com/android/camera/PhotoModule.java

mParameters.setSaturation(saturation);函數調用;

mParameters.setOis(ois);

由于HAL V1參數傳遞是通過字元串來完成的,最後傳遞到HAL層的字元串裡面會有“ois=1”,在HAL層進行解析。

   B、Hal層相關修改:

1、    添加相關定義

1.1、 檔案:hardware/qcom/camera/QCamera2/HAL/QCameraParameters.h

     static const char KEY_QC_SCE_FACTOR_STEP[];

+    static const char KEY_QC_OIS[];

     staticconst char KEY_QC_HISTOGRAM[] ;

     int32_tsetSharpness(const QCameraParameters& );

+    int32_t setOis(const QCameraParameters&);

     int32_tsetSaturation(const QCameraParameters& );

     int32_tsetSharpness(int sharpness);

+    int32_t setOis(int ois);

     int32_tsetSaturation(int saturation);

1.2、 檔案:hardware/qcom/camera/QCamera2/stack/common/cam_types.h

            typedef enum {

                CAM_INTF_PARM_FLASH_BRACKETING,

                CAM_INTF_PARM_GET_IMG_PROP,

                    CAM_INTF_PARM_MAX

+    CAM_INTF_PARM_OIS

            }cam_intf_parm_type_t;

1.3、 檔案:hardware/qcom/camera/QCamera2/stack/common/cam_intf.h

typedefstruct{

     cam_af_bracketing_t  mtf_af_bracketing_parm;

     cam_sensor_type_t sensor_type;

+   

+   int32_t ois_default_value;

 } cam_capability_t;

2、    添加相關設定

檔案:hardware/qcom/camera/QCamera2/HAL/QCameraParameters.cpp

const charQCameraParameters::KEY_QC_SCE_FACTOR_STEP[] = "sce-factor-step";

+const char QCameraParameters::KEY_QC_OIS[] = "ois";

//open camera時OIS預設值,該值在vendor中設定

int32_t QCameraParameters::initDefaultParameters()

{

       ………

+   // Set Ois

+   setOis(m_pCapability->ois_default_value);

+   ALOGE("the default_ois = %d",m_pCapability->ois_default_value);

     // Set Contrast

    set(KEY_QC_MIN_CONTRAST,m_pCapability->contrast_ctrl.min_value);

    set(KEY_QC_MAX_CONTRAST, m_pCapability->contrast_ctrl.max_value);

    ………

}

+int32_t QCameraParameters::setOis(constQCameraParameters& params)

+{

+    int ois = params.getInt(KEY_QC_OIS);

+    int prev_ois = getInt(KEY_QC_OIS);

+    if(params.get(KEY_QC_OIS) == NULL) {

+       CDBG_HIGH("%s: Ois not set by App",__func__);

+       return NO_ERROR;

+    }

+    ALOGE("haljay ois=%dprev_ois=%d",ois, prev_ois);

+    if (prev_ois !=  ois) {

+        if((ois >= 0) && (ois <=2)) {

+            CDBG(" new ois value : %d", ois);

+            return setOis(ois);

+        } else {

+            ALOGE("%s: invalid value%d",__func__, ois);

+            return BAD_VALUE;

+        }

+    } else {

+        ALOGE("haljay no valuechange");

+        CDBG("%s: No value change inois", __func__);

+        return NO_ERROR;

+    }

+}

+int32_t QCameraParameters::setOis(intois)

+{

+    charval[16];

+   sprintf(val, "%d", ois);

+   updateParamEntry(KEY_QC_OIS, val);

+   CDBG("%s: Setting ois %s", __func__, val);

+    ALOGE("haljay%s set ois=%s OIS=%d", __func__, val, CAM_INTF_PARM_OIS);

+    int32_tvalue = ois;

+    returnAddSetParmEntryToBatch(m_pParamBuf,

+                                 CAM_INTF_PARM_OIS,

+                                 sizeof(value),

+                                  &value);

+}

函數int32_tQCameraParameters::updateParameters添加setOis:

     if ((rc =setBrightness(params)))                  final_rc = rc;

     if ((rc =setZoom(params)))                        final_rc = rc;

     if ((rc = setSharpness(params)))                    final_rc = rc;

+    if ((rc = setOis(params)))                          final_rc = rc;

     if ((rc =setSaturation(params)))                   final_rc = rc;

   C、Vendor層相關修改:

1、    添加相關定義

1.1、 檔案:kernel/include/media/msm_cam_sensor.h

enum msm_actuator_cfg_type_t {

  CFG_SET_POSITION,

  CFG_ACTUATOR_POWERDOWN,

  CFG_ACTUATOR_POWERUP,

+ CFG_ACTUATOR_OIS,

 };

struct msm_actuator_cfg_data {

      struct msm_actuator_get_info_t get_info;

      struct msm_actuator_set_position_t setpos;

      enum af_camera_name cam_name;

+      void*setting;

  } cfg;

1.2、 檔案:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/mct/pipeline/mct_pipeline.c

      在函數boolean mct_pipeline_populate_query_cap_buffer(mct_pipeline_t*pipeline)中添加:

                hal_data->sharpness_ctrl.min_value= 0;

                hal_data->sharpness_ctrl.step= 6;

+  hal_data->ois_default_value= 1;

                hal_data->contrast_ctrl.def_value= 5;

                hal_data->contrast_ctrl.max_value= 10;

1.3、 檔案:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/module/sensor_common.h

typedefenum {

   SENSOR_SET_AWB_UPDATE,

+ ACTUATOR_SET_OIS

 } sensor_submodule_event_type_t;

2、    添加相關設定

檔案:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/module/module_sensor.c

2.1、 擷取hal層參數

在函數static boolean module_sensor_event_control_set_parm中增加:

+  case CAM_INTF_PARM_OIS:{

+    if (!event_control->parm_data) {

+        SERR("failed parm_dataNULL");

+        ret = FALSE;

+        break;

+      }

+    module_sensor_params_t        *ois_module_params = NULL;

+    ois_module_params =s_bundle->module_sensor_params[SUB_MODULE_ACTUATOR];

+    if (ois_module_params->func_tbl.process != NULL) {

+      rc =ois_module_params->func_tbl.process(

+        ois_module_params->sub_module_private,

+        ACTUATOR_SET_OIS,event_control->parm_data);

+    }

+    if (rc < 0) {

+      SERR("failed");

+      ret = FALSE;

+    }

+     break;

+  }

檔案:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/actuators/actuator.c

2.2、在函數int32_t actuator_process中增加:

       case ACTUATOR_SET_POSITION:

            rc =actuator_set_position(actuator_ctrl, data);

            break;

+   case ACTUATOR_SET_OIS:

+   rc = actuator_set_ois(actuator_ctrl,data);

+   break;

2.3、将參數通過ioctl方法下至核心

        +staticint actuator_set_ois(void *ptr, void*data) {

+  int rc = 0;

+  int32_t *ois_level = (int32_t*)data;

+  actuator_data_t *ois_actuator_ptr =(actuator_data_t *)ptr;

+  struct msm_actuator_cfg_data cfg;

+  if (ois_actuator_ptr->fd <= 0)

+    return -EINVAL;

+  cfg.cfgtype = CFG_ACTUATOR_OIS;

+  cfg.cfg.setting = ois_level;

+  rc = ioctl(ois_actuator_ptr->fd,VIDIOC_MSM_ACTUATOR_CFG, &cfg);

+  if (rc < 0) {

+    SERR("failed-errno:%s!!!",strerror(errno));

+  }

+  return rc;

+}

2.2.3 HAL 3.0參數設定

V3增加設定參數:對于HAL V3,從framework到HAL層的參數傳遞是通過metadata方式完成的,即每一個設定現在都變成了一個參數對,例如:設定AE mode為auto,V1版本參數可能是“AE mode=auto”字元串;V3版本假設AE mode功能序号是10,參數auto為1,傳到HAL層的參數類似(10,1)這樣的參數對,在HAL層需要通過10這個參數,擷取設定值1;對于在V1版本對ois的設定需要在V3中添加新的處理來實作。

如何在V3中定義自己特定參數(如ois設定):谷歌考慮到廠商可能需要定義自己特定的參數,是以在metadata裡面定義了vendor tag的資料範圍來讓vendor可以添加自己特定的操作,如ois設定,可以通過vendor tag來實作。

步驟:

1)  定義自己的vendor tag序号值

vim system/media/camera/include/system/camera_metadata_tags.h

          typedefenum camera_metadata_tag {

             ANDROID_SYNC_START,

             ANDROID_SYNC_MAX_LATENCY,

             ANDROID_SYNC_END,

+ VENDOR_TAG_OIS =

+ VENDOR_SECTION_START,  //由于參數少,沒有重新定義section,使用預設section 0x8000

                ......................

           } camera_metadata_tag_t;

2)  所需支援配置

Vendor Tag都需要在VENDOR_SECTION_START後面添加,此處添加了VENDOR_TAG_OIS。在HAL裡面如果需要處理 Vendor Tag,一個是需要camera module的版本是2.2以上,因為Google在這個版本之後才穩定支援vendor tag。一個是需要vendor tag的的operations函數。

vim ./hardware/libhardware/modules/camera/CameraHAL.cpp +186

版本和操作函數如下圖所示:

vim ./hardware/qcom/camera/QCamera2/HAL3/QCamera3VendorTags.cpp +184

           get_tag_count:傳回所有vendor tag的個數;

get_all_tags:把所有vendor tag依次放在service傳下來的uint32_t * tag_array裡面,這樣上層就知道每一個tag對應的序号值了;

get_section_name:擷取vendor tag的section對應的section名稱,比如可以把某幾個vendor tag放在一個section裡面,其它的放在其它的section裡面。檢視metadata.h裡面的定義很好了解,如果你想增加自己的section,就可以在VENDOR_SECTION = 0x8000後面添加自己的section。由于本次隻設定ois參數,沒有分類的必要,是以就使用預設的VENDOR_SECTION.

vim system/media/camera/include/system/camera_metadata_tags.h

get_tag_name:用于擷取每一個vendor tag的名稱,比如我們這個地方傳回“VENDOR_TAG_OIS”就可以了;

get_tag_type:這個函數傳回vendor tag對應的設定資料的類型,可以用TYPE_INT32, TYPE_FLOAT等多種資料格式,取決于需求,我們ois參數隻要是INT32就行了。

3)  加載vendor tag

這樣CameraService.cpp在啟動的時候就會調用onFirstRef裡面的下面代碼來加載我們所寫的vendor tag

if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) {

                       setUpVendorTags();

        }

4)  V1到V3參數轉化

由于我們這個ois設定是在V1的APP裡面使用,是以首先需要實作V1和V3參數的轉換,Google在services/camera/libcameraservice/api1/client2/Parameters.cpp實作相應的轉換,是以首先需要在如下函數裡面擷取V1 APP傳下來的OIS的值,其中的paramString就是V1的參數設定的字元串

status_t Parameters::set(const String8& paramString)

{

    …………

    mOis = newParams.get(CameraParameters::KEY_OIS);

    …………

}

由于V3的參數都是在request frame的時候一起下發的,是以需要講mSaturation的值在Parameters::updateRequest(CameraMetadata *request)裡面下發到HAL,即

+  res = request->update(VENDOR_TAG_SATURATION,&mOis, 1);

 這樣就将saturation的vendor tag和其設定值發送到了HAL V3。

5)  HAL V3擷取設定的OIS參數

使用CameraMetadata::find(uint32_ttag)函數來擷取參數:

oisMapMode =                frame_settings.find(VENDOR_TAG_OIS).data.i32[0];

通過ADD_SET_PARAM_ENTRY_TO_BATCH函數将設定下到vendor層:

ADD_SET_PARAM_ENTRY_TO_BATCH(hal_metadata, CAM_INTF_META_OIS,

oisMapMode);

2.3 Hal 3.0版本架構分析

2.3.1 Frameworks層總體架構

Frameworks之CameraService部分架構圖如下圖所示:

v3将更多的工作集中在了Framework去完成,将更多的控制權掌握在自己的手裡,進而與HAL的互動的資料資訊更少,也進一步減輕了一些在舊版本中HAL層所需要做的事情,也更加子產品化。

Camera2Client建立與初始化過程如下圖所示:

由上圖可知建立好Camera2Client後會進行initialize操作,完成各個處理子產品的建立:

代碼目錄:frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp

status_tCamera2Client::initialize(CameraModule *module)

{

    ………

    mStreamingProcessor = new StreamingProcessor(this);//preview和recorder

    threadName =String8::format(C2-%d-StreamProc, mCameraId);

   mStreamingProcessor->run(threadName.string());//預覽與錄像

    mFrameProcessor = new FrameProcessor(mDevice, this);// 3A

    threadName = String8::format(C2-%d-FrameProc,mCameraId);

   mFrameProcessor->run(threadName.string()); //3A

    mCaptureSequencer = new CaptureSequencer(this);

    threadName =String8::format(C2-%d-CaptureSeq, mCameraId);

   mCaptureSequencer->run(threadName.string());//錄像,拍照

   mJpegProcessor = new JpegProcessor(this,mCaptureSequencer);

    threadName =String8::format(C2-%d-JpegProc, mCameraId);

   mJpegProcessor->run(threadName.string());

………

    mCallbackProcessor = new CallbackProcessor(this);//回調處理

    threadName = String8::format(C2-%d-CallbkProc,mCameraId);

   mCallbackProcessor->run(threadName.string());

    ………

}

依次分别建立了:

1、StreamingProcessor并啟動一個它所屬的thread,該子產品主要負責處理previews與record兩種視訊流的處理,用于從hal層擷取原始的視訊資料

2、FrameProcessor并啟動一個thread,該子產品專門用于處理回調回來的每一幀的3A等資訊,即每一幀視訊除去原始視訊資料外,還應該有其他附加的資料資訊,如3A值。

3、CaptureSequencer并啟動一個thread,該子產品需要和其他子產品配合使用,主要用于向APP層告知capture到的picture。

4、JpegProcessor并啟動一個thread,該子產品和streamprocessor類似,他啟動一個拍照流,一般用于從HAL層擷取jpeg編碼後的圖像照片資料。

5、另外ZslProcessor子產品稱之為0秒快拍,其本質是直接從原始的Preview流中擷取預存着的最近的幾幀,直接編碼後傳回給APP,而不 需要再經過take picture去請求擷取jpeg資料。0秒快拍技術得意于當下處理器CSI2 MIPI性能的提升以及Sensor支援全像素高幀率的實時輸出。一般手機拍照在按下快門後都會有一定的延時,是因為需要切換底層Camera以及ISP 等的工作模式,并重新設定參數以及重新對焦等等,都需要花一定時間後才抓取一幀用于編碼為jpeg圖像。

以上5個子產品整合在一起基本上實作了Camera應用開發所需的基本業務功能。

2.3.2 Preview模式下的控制流

代碼目錄,直接以Camera2Client::startPreview()作為入口來分析整個Framework層中Preview相關的資料流:

   1、調用Camera2Client::startPreview函數

代碼目錄-1:frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp

status_t Camera2Client::startPreview() {

    ATRACE_CALL();

    ALOGV(%s: E, __FUNCTION__);

    Mutex::Autolockicl(mBinderSerializationLock);

    status_t res;

    if ( (res = checkPid(__FUNCTION__) ) != OK)return res;

    SharedParameters::Lock l(mParameters);

    return startPreviewL(l.mParameters,false);

}

startPreview通過startPreviewL提取參數後真正的開始執行Preview相關的控制流。該函數看上去内容雖然較多,但基本采用了同一種處理方式:

2、    調用Camera2Client::startPreviewL函數

代碼目錄-1:frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp

後面會詳細介紹2.1-2.6粗體标注部分;

status_tCamera2Client::startPreviewL(Parameters &params, bool restart){

......

//擷取上一層Preview stream id

intlastPreviewStreamId = mStreamingProcessor->getPreviewStreamId();

//2.1建立camera3device stream, Camera3OutputStream

    res =mStreamingProcessor->updatePreviewStream(params);

.....

intlastJpegStreamId = mJpegProcessor->getStreamId();

//2.2預覽啟動時就建立一個jpeg的outstream

res= updateProcessorStream(mJpegProcessor,params);

.....

//2.3回調處理建立一個Camera3outputstream

res= mCallbackProcessor->updateStream(params);

………

//2.4

outputStreams.push(getCallbackStreamId());

......

outputStreams.push(getPreviewStreamId());//預覽stream

......

if(!params.recordingHint) {

   if (!restart) {

      //2.5 request處理,更新了mPreviewrequest

      res = mStreamingProcessor->updatePreviewRequest(params); 

......

    }

        //2.6

        res = mStreamingProcessor->startStream(StreamingProcessor::PREVIEW,

                outputStreams);//啟動stream,傳入outputStreams即stream 的id

    }

......

}

2.1、調用mStreamingProcessor->updatePreviewStream函數

   代碼目錄-2:

    frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp

status_t StreamingProcessor::updatePreviewStream (constParameters &params) {

......

    sp<cameradevicebase> device =mDevice.promote();//Camera3Device

......

    if (mPreviewStreamId != NO_STREAM) {

        // Check if stream parameters have tochange

       uint32_t currentWidth, currentHeight;

        res =device->getStreamInfo(mPreviewStreamId,

                &tWidth, &tHeight, 0);

    ......

        if (currentWidth !=(uint32_t)params.previewWidth ||

                currentHeight != (uint32_t)params.previewHeight){

        ......    

            res =device->waitUntilDrained();

        ......   

            res =device->deleteStream(mPreviewStreamId);

            ......

            mPreviewStreamId = NO_STREAM;

        }

    }

if (mPreviewStreamId == NO_STREAM) {//首次create stream

        //建立一個Camera3OutputStream

        res = device->createStream(mPreviewWindow,

                params.previewWidth,params.previewHeight,

               CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mPreviewStreamId);

        ......

        }

    }

    res =device->setStreamTransform(mPreviewStreamId,

            params.previewTransform);

    ......

}

該函數首先是檢視目前StreamingProcessor子產品下是否存在Stream,沒有的話,則交由Camera3Device建立一個 stream。顯然,一個StreamingProcessor隻能擁有一個PreviewStream,而一個Camera3Device顯然控制着所 有的Stream。

注意:在Camera2Client中,5大子產品的資料互動均以stream作為基礎。

下面我們來重點關注Camera3Device的接口createStream,他是5個子產品建立stream的基礎:

      代碼目錄-3:

       frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

status_tCamera3Device::createStream(spconsumer,

        uint32_t width, uint32_t height, intformat, int *id) {

    ......

    assert(mStatus != STATUS_ACTIVE);

    sp<camera3outputstream> newStream;

    if (format == HAL_PIXEL_FORMAT_BLOB) {//圖檔

        ssize_t jpegBufferSize =getJpegBufferSize(width, height);

       ......

        newStream = new Camera3OutputStream(mNextStreamId, consumer,

                width, height, jpegBufferSize,format);//jpeg 緩存的大小

    } else {

        newStream = new Camera3OutputStream(mNextStreamId, consumer,

                width, height, format);//Camera3OutputStream

    }

newStream->setStatusTracker(mStatusTracker);

//一個streamid與Camera3OutputStream綁定

    res = mOutputStreams.add(mNextStreamId,newStream);

    ......

    *id = mNextStreamId++;//至少一個previewstream 一般還有CallbackStream

    mNeedConfig = true;

    // Continue captures if active at start

    if (wasActive) {

        ALOGV(%s: Restarting activity toreconfigure streams, __FUNCTION__);

        res = configureStreamsLocked();

       ......

        internalResumeLocked();

    }

    ALOGV(Camera %d: Created new stream, mId);

    return OK;

}

該函數重點是關注一個new Camera3OutputStream,在Camera3Device主要存在Camera3OutputStream和Camera3InputStream,兩種stream,前者主要作為HAL的輸出,是請求HAL填充資料的OutPutStream,後者是由Framework将Stream進行填充。無論是Preview、record還是capture均是從HAL層擷取資料,故都會以OutPutStream的形式存在,是我們關注的重點,後面在描述Preview的資料流時還會進一步的闡述。

每當建立一個OutPutStream後,相關的stream資訊被push維護在一個mOutputStreams的KeyedVector表中,分别是該stream在Camera3Device中建立時的ID以及Camera3OutputStream的sp值。同時對mNextStreamId記錄下一個Stream的ID号。

上述過程完成StreamingProcessor子產品中一個PreviewStream的建立,其中Camera3OutputStream建立時的ID值被傳回記錄作為mPreviewStreamId的值,此外每個Stream都會有一個對應的ANativeWindow,這裡稱之為Consumer。

2.2、調用updateProcessorStream(mJpegProcessor, params)函數

    代碼目錄-2:

    frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp

       status_tCamera2Client::updateProcessorStream(sp<processort> processor,

                                             camera2::Parameters params) {

            //No default template arguments until C++11, so we need this overload

             return updateProcessorStream<processort,processort::updatestream="">(

                processor,params);

}

template <typename const="" parameters=""status_t="">

status_tCamera2Client::updateProcessorStream(sp<processort> processor,

                                             Parameters params) {

            status_tres;

            //Get raw pointer since sp<t> doesn't have operator->*

            ProcessorT*processorPtr = processor.get();

            res= (processorPtr->*updateStreamF)(params);

.......

}

該模闆函數處理過程最終通過非顯示執行個體到顯示執行個體調用JpegProcessor::updateStream,該函數處理的邏輯基本和Callback 子產品處理一緻,建立的一個OutPutStream和CaptureWindow互相綁定,同時Stream的ID儲存在 mCaptureStreamId中。

此外需要說明一點:

在preview模式下,就去建立一個jpeg處理的stream,目的在于啟動takepicture時,可以更快的進行capture操作,是通過犧牲記憶體空間來提升效率。

2.3、調用mCallbackProcessor->updateStream函數

代碼目錄-2:

    frameworks/av/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp

對比StreamingProcessor子產品建立previewstream的過程,很容易定位到Callback子產品是需要建立一個 callback流,同樣需要建立一個Camera3OutputStream來接收HAL傳回的每一幀幀資料,是否需要callback可以通過 callbackenable來控制。一般但預覽階段可能不需要回調每一幀的資料到APP,但涉及到相應的其他業務如視訊處理時,就需要進行 callback的enable。

status_t CallbackProcessor::updateStream(constParameters &params) {

    ………

    sp<cameradevicebase> device =mDevice.promote();

    ………

    // If possible, use the flexible YUV format

    int32_t callbackFormat =params.previewFormat;

    if (mCallbackToApp) {

        // TODO: etalvala: This should use theflexible YUV format as well, but

        // need to reconcile HAL2/HAL3requirements.

        callbackFormat = HAL_PIXEL_FORMAT_YV12;

    } else if(params.fastInfo.useFlexibleYuv&&

            (params.previewFormat ==HAL_PIXEL_FORMAT_YCrCb_420_SP ||

             params.previewFormat ==HAL_PIXEL_FORMAT_YV12) ) {

        callbackFormat =HAL_PIXEL_FORMAT_YCbCr_420_888;

    }

    if (!mCallbackToApp &&mCallbackConsumer == 0) {

        // Create CPU buffer queue endpoint,since app hasn't given us one

        // Make it async to avoid disconnectdeadlocks

        sp<igraphicbufferproducer>producer;

        sp<igraphicbufferconsumer>consumer;

       //BufferQueueProducer與BufferQueueConsumer

        BufferQueue::createBufferQueue(&producer, &consumer);

        mCallbackConsumer = new CpuConsumer(consumer,kCallbackHeapCount);

//目前CallbackProcessor繼承于CpuConsumer::FrameAvailableListener

        mCallbackConsumer->setFrameAvailableListener(this);

       mCallbackConsumer->setName(String8(Camera2Client::CallbackConsumer));

//用于queue操作,這裡直接進行本地的buffer操作

        mCallbackWindow = new Surface(producer);

    }

    if (mCallbackStreamId != NO_STREAM) {

        // Check if stream parameters have tochange

        uint32_t currentWidth, currentHeight,currentFormat;

        res =device->getStreamInfo(mCallbackStreamId,

                &tWidth, &tHeight, &tFormat);

       ………

    }

    if (mCallbackStreamId == NO_STREAM) {

        ALOGV(Creating callback stream: %d x%d, format 0x%x, API format 0x%x,

                params.previewWidth,params.previewHeight,

                callbackFormat,params.previewFormat);

        res = device->createStream(mCallbackWindow,

               params.previewWidth, params.previewHeight,

                callbackFormat,&mCallbackStreamId);//Creating callback stream

        ………

    }

    return OK;

}

2.4、整合startPreviewL中所有的stream 到Vector outputStreams

outputStreams.push(getPreviewStreamId());//預覽stream

outputStreams.push(getCallbackStreamId())//Callback stream

目前一次Preview建構的stream數目至少為兩個。

2.5、調用mStreamingProcessor->updatePreviewRequest函數

代碼目錄-2:

    frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp

在建立好多路stream後,由StreamingProcessor子產品來将所有的stream資訊交由Camera3Device去打包成Request請求。

注意:

Camera HAL2/3的特點是:将所有stream的請求都轉化為幾個典型的Request請求,而這些Request需要由HAL去解析,進而處理所需的業務,這也是Camera3資料處理複雜化的原因所在。

status_t StreamingProcessor::updatePreviewRequest(constParameters &params) {

    ………

    if (mPreviewRequest.entryCount()== 0) {

        sp<camera2client> client =mClient.promote();

        if (client == 0) {

            ALOGE(%s: Camera %d: Client doesnot exist, __FUNCTION__, mId);

            return INVALID_OPERATION;

        }

        // UseCAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.

        if (client->getCameraDeviceVersion()>= CAMERA_DEVICE_API_VERSION_3_0) {

            if (params.zslMode &&!params.recordingHint) {

                res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,

                        &mPreviewRequest);

            } else {

                res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,

                        &mPreviewRequest);

            }

        } else {

          //建立一個Preview相關的request,由底層的hal來完成default建立

            res =device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,

                    &mPreviewRequest);

        ………

}

//根據參數來更新CameraMetadatarequest,用于app設定參數,如antibanding設定

res= params.updateRequest(&mPreviewRequest);  

    ………

    res = mPreviewRequest.update(ANDROID_REQUEST_ID,

            &mPreviewRequestId,1);//mPreviewRequest的ANDROID_REQUEST_ID

    ………

}

a mPreviewRequest是一個CameraMetadata類型資料,用于封裝目前previewRequest;

b 調用device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,&mPreviewRequest)函數

代碼目錄-3:

frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

status_t Camera3Device::createDefaultRequest(int templateId, CameraMetadata*request) {

    ………

const camera_metadata_t *rawRequest;

 ATRACE_BEGIN(camera3->construct_default_request_settings);

 rawRequest = mHal3Device->ops->construct_default_request_settings(

    mHal3Device, templateId);

 ATRACE_END();

 if (rawRequest == NULL) {

    SET_ERR_L(HAL is unable to construct default settings for template %d,

             templateId);

    return DEAD_OBJECT;

 }

 *request = rawRequest;

 mRequestTemplateCache[templateId] =rawRequest;

………

}

最終是由hal來實作建構一個rawrequest,即對于Preview,而言是建構了一個CAMERA3_TEMPLATE_PREVIEW類型的 Request。其實對HAL而言,rawrequest本質是用于操作一個camera_metadata_t類型的資料:

struct camera_metadata {

    metadata_size_t          size;

    uint32_t                 version;

    uint32_t                 flags;

    metadata_size_t          entry_count;

    metadata_size_t          entry_capacity;

    metadata_uptrdiff_t      entries_start; // Offset fromcamera_metadata

    metadata_size_t          data_count;

    metadata_size_t          data_capacity;

    metadata_uptrdiff_t      data_start; // Offset fromcamera_metadata

    uint8_t                 reserved[];

};

該資料結構可以存儲多種資料,且可以根據entry tag的不同類型來存儲資料,同時資料量的大小也可以自動調整;

c mPreviewRequest.update(ANDROID_REQUEST_ID,&mPreviewRequestId,1)

将目前的PreviewRequest相應的ID儲存到camera metadata。

2.6、調用mStreamingProcessor->startStream函數啟動整個預覽的stream流

代碼目錄-2:

  frameworks/av/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp

該函數的處理過程較為複雜,可以說是整個Preview正常工作的核心控制:

tatus_tStreamingProcessor::startStream(StreamType type,

        const Vector<int32_t>&outputStreams) {

.....

CameraMetadata&request = (type == PREVIEW) ?

            mPreviewRequest :mRecordingRequest;//取preview的CameraMetadata request

//CameraMetadata中添加outputStreams

res = request.update(ANDROID_REQUEST_OUTPUT_STREAMS,outputStreams);

res= device->setStreamingRequest(request);//向hal發送request

.....

}

該函數首先是根據目前工作模式來确定StreamingProcessor需要處理的Request,該子產品負責Preview和Record兩個Request。

以PreviewRequest就是之前createDefaultRequest建構的,這裡先是将這個Request所需要操作的Outputstream打包到一個tag叫ANDROID_REQUEST_OUTPUT_STREAMS的entry當中。

      a 調用setStreamingRequest函數

      代碼目錄:

       frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

真正的請求Camera3Device去處理這個帶有多路stream的PreviewRequest。

a.1 status_t Camera3Device::setStreamingRequest(constCameraMetadata &request,

                                           int64_t* ) {

    ATRACE_CALL();

    List<constcamerametadata=""> requests;

    requests.push_back(request);

    return setStreamingRequestList(requests,NULL);

}

該函數将mPreviewRequest push到一個list,調用setStreamingRequestList

a.2 status_t Camera3Device::setStreamingRequestList(constList<const camerametadata=""> &requests, int64_t*lastFrameNumber) {

        ATRACE_CALL();

        returnsubmitRequestsHelper(requests,true, lastFrameNumber);

}

a.3 status_t Camera3Device::submitRequestsHelper(

       const List<const camerametadata=""> &requests, boolrepeating,

       int64_t *lastFrameNumber) {//repeating = 1;lastFrameNumber = NULL

   ………

   status_t res = checkStatusOkToCaptureLocked();

   ………

    RequestList requestList;

//傳回的是CaptureRequest RequestList

res = convertMetadataListToRequestListLocked(requests,&requestList);   

………

   if (repeating) {

//重複的request存入到RequestThread

res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber); 

}  else {

//capture模式,拍照單詞

       res = mRequestThread->queueRequestList(requestList,lastFrameNumber);  

 }

   if (res == OK) {

       waitUntilStateThenRelock(true, kActiveTimeout);

       if (res != OK) {

            SET_ERR_L(Can't transition toactive in %f seconds!,

                    kActiveTimeout/1e9);

       }

       ALOGV(Camera %d: Capture request % PRId32  enqueued, mId,

             (*(requestList.begin()))->mResultExtras.requestId);

   } else {

       CLOGE(Cannot queue request. Impossible.);

       return BAD_VALUE;

   }

   return res;

}

a.4 convertMetadataListToRequestListLocked

這個函數是需要将Requestlist中儲存的CameraMetadata資料轉換為List;

status_tCamera3Device::convertMetadataListToRequestListLocked(

const List<constcamerametadata=""> &metadataList, RequestList *requestList) {

   ………

   for (List<const camerametadata="">::const_iterator it =metadataList.begin();//CameraMetadata, mPreviewRequest

            it != metadataList.end(); ++it) {

        //建立CaptureRequest由CameraMetadata轉化而來

       sp<capturerequest>newRequest = setUpRequestLocked(*it);       

        ………

       // Setup burst Id and request Id

       newRequest->mResultExtras.burstId = burstId++;

       if (it->exists(ANDROID_REQUEST_ID)) {

            if(it->find(ANDROID_REQUEST_ID).count == 0) {

                CLOGE(RequestID entry exists;but must not be empty in metadata);

                return BAD_VALUE;

            }

        //設定該request對應的id

        newRequest->mResultExtras.requestId =it->find(ANDROID_REQUEST_ID).data.i32[0];

       } else {

            CLOGE(RequestID does not exist inmetadata);

            return BAD_VALUE;

       }

       requestList->push_back(newRequest);

        ………

   }

   return OK;

}

這裡是對List進行疊代解析處理,如目前模式下僅存在PreviewRequest這一個CameraMetadata,通過setUpRequestLocked将其轉換為一個CaptureRequest。

        a.5 setUpRequestLocked

           sp<camera3device::capturerequest>Camera3Device::setUpRequestLocked(

                constCameraMetadata &request) {//mPreviewRequest

                status_tres;

                if(mStatus == STATUS_UNCONFIGURED || mNeedConfig) {

                res= configureStreamsLocked();

                ......

    //CameraMetadata轉為CaptureRequest,包含mOutputStreams

   </strong>sp<capturerequest> newRequest = createCaptureRequest(request);

                return newRequest;

}

configureStreamsLocked函數主要是将Camera3Device側建立的所有Stream包括Output與InPut格式 的交由HAL3層的Device去實作處理的核心接口是configure_streams與register_stream_buffer。

createCaptureRequest函數是将一個CameraMetadata格式的資料如PreviewRequest轉換為一個CaptureRequest:

           a.6 sp<camera3device::capturerequest>Camera3Device::createCaptureRequest(

                constCameraMetadata &request) {//mPreviewRequest

                ………

                sp<capturerequest>newRequest = new CaptureRequest;

                newRequest->mSettings= request;//CameraMetadata

                camera_metadata_entry_tinputStreams =

                    newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS);

                if(inputStreams.count > 0) {

                    if(mInputStream == NULL ||

                        mInputStream->getId() != inputStreams.data.i32[0]) {

                        CLOGE(Requestreferences unknown input stream %d,

                        inputStreams.data.u8[0]);

                        returnNULL;

                    }

                ………

                    newRequest->mInputStream= mInputStream;

                    newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS);

                }

//讀取存儲在CameraMetadata的stream id資訊

                camera_metadata_entry_tstreams =

                    newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS);

                    ………

for (size_t i = 0; i < streams.count; i++) {

                    //Camera3OutputStream的id在mOutputStreams中

                    intidx = mOutputStreams.indexOfKey(streams.data.i32[i]);

                    ………

                 }

                //傳回的是Camera3OutputStream,preview/callback等stream

                sp<camera3outputstreaminterface>stream =

                     mOutputStreams.editValueAt(idx);

                ………

//Camera3OutputStream添加到CaptureRequest的mOutputStreams

                newRequest->mOutputStreams.push(stream);

    }

                newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS);

                returnnewRequest;

}

該函數主要處理指定的這個CameraMetadata mPreviewRequest下對應所擁有的Output與Input Stream,對于Preview而言,至少存在OutPutStream包括一路StreamProcessor與一路可選的 CallbackProcessor。

在建構這個PreviewRequest時,已經将ANDROID_REQUEST_OUTPUT_STREAMS這個Tag進行了初始化,相應的内容為Vector &outputStreams,包含着屬于PreviewRequest這個Request所需要的輸出stream的ID值,通過這個IDindex值,可以周遊到Camera3Device下所createstream創造的Camera3OutputStream,即說明不同類型的 Request在Camera3Device端存在多個Stream,而每次不同業務下所需要Request的對應的Stream又僅是其中的個别而已。

idx = mOutputStreams.indexOfKey(streams.data.i32[i])是通過屬于PreviewRequest中包含的一個 stream的ID值來查找到mOutputStreams這個KeyedVector中對應的标定值index。注意:兩個索引值不一定是一緻的。

mOutputStreams.editValueAt(idx)是擷取一個與該ID值(如Previewstream ID、CallbackStream ID等等)相對應的Camera3OutputStream。

在找到了目前Request中所有的Camera3OutputStream後,将其維護在CaptureRequest中:

class CaptureRequest : public LightRefBase<capturerequest> {

      public:

        CameraMetadata                      mSettings;

        sp<camera3::camera3stream>          mInputStream;

       Vector<sp<camera3::camera3outputstreaminterface> >

                                            mOutputStreams;

        CaptureResultExtras                 mResultExtras;

    };

mSettings是儲存CameraMetadata PreviewRequest,vectormOutPutStreams儲存着目前Request提取出來的Camera3OutputStream,至此建構了一個CaptureRequest。

           回到a.4:convertMetadataListToRequestListLocked

傳回到convertMetadataListToRequestListLocked中,現在已經完成了一個CameraMetadata Request的處理,生産的是一個CaptureRequest。我們将這個ANDROID_REQUEST_ID的ID值,保留在newRequest->mResultExtras.requestId =it->find(ANDROID_REQUEST_ID).data.i32[0]。

這個值在整個Camera3的架構中,僅存在3大種Request類型,說明了整個和HAL層互動的Request類型是不多的:

預覽RequestmPreviewRequest:mPreviewRequestId(Camera2Client::kPreviewRequestIdStart),

拍照RequestmCaptureRequest:mCaptureId(Camera2Client::kCaptureRequestIdStart),

錄像RequestmRecordingRequest: mRecordingRequestId(Camera2Client::kRecordingRequestIdStart);

staticconst int32_t kPreviewRequestIdStart = 10000000;

staticconst int32_t kPreviewRequestIdEnd   =20000000;

staticconst int32_t kRecordingRequestIdStart  =20000000;

staticconst int32_t kRecordingRequestIdEnd    =30000000;

staticconst int32_t kCaptureRequestIdStart = 30000000;

staticconst int32_t kCaptureRequestIdEnd   =40000000;

           回到a.3:mRequestThread->setRepeatingRequests(requestList)

對于Preview來說,一次Preview後底層硬體就該可以連續的工作,而不需要進行過多的切換,故Framework每次向HAL發送的Request均是一種repeat的操作模式,故調用了一個重複的RequestQueue來循環處理每次的Request。

status_tCamera3Device::RequestThread::setRepeatingRequests(

        const RequestList &requests,

        int64_t *lastFrameNumber) {

    Mutex::Autolock l(mRequestLock);

    if (lastFrameNumber != NULL) {//第一次進來為null

        *lastFrameNumber =mRepeatingLastFrameNumber;

    }

    mRepeatingRequests.clear();

    mRepeatingRequests.insert(mRepeatingRequests.begin(),

            requests.begin(), requests.end());

   unpauseForNewRequests();//signal request_thread in waitfornextrequest

    mRepeatingLastFrameNumber =NO_IN_FLIGHT_REPEATING_FRAMES;

    return OK;

}

将Preview線程送出的Request加入到mRepeatingRequests中後,喚醒RequestThread線程去處理目前新的Request。

2.7、經過2.6步驟将開啟RequestThread 請求處理線程

RequestThread::threadLoop()函數主要用于響應并處理新加入到Request隊列中的請求。

代碼目錄-2:

frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

boolCamera3Device::RequestThread::threadLoop(){

....

//傳回的是mRepeatingRequests,mPreviewRequest

 sp<capturerequest> nextRequest = waitForNextRequest();  

………

    // Create request to HAL

//CaptureRequest轉為給HAL3.0的camera3_capture_request_t

camera3_capture_request_t request =camera3_capture_request_t();   request.frame_number = nextRequest->mResultExtras.frameNumber;//目前幀号

    Vector<camera3_stream_buffer_t>outputBuffers;

    // Get the request ID, if any

    int requestId;

    camera_metadata_entry_t requestIdEntry =

            nextRequest->mSettings.find(ANDROID_REQUEST_ID);

    if (requestIdEntry.count > 0) {

//擷取requestid,這裡是mPreviewRequest的id

        requestId = requestIdEntry.data.i32[0];

    }

         .....

   for (size_t i = 0; i <nextRequest->mOutputStreams.size(); i++) {

         res =nextRequest->mOutputStreams.editItemAt(i)->

                 getBuffer(&outputBuffers.editItemAt(i));

.....

    // Submit request and block until ready fornext one

    ATRACE_ASYNC_BEGIN(frame capture,request.frame_number);

   ATRACE_BEGIN(camera3->process_capture_request);

   //調用底層hal的process_capture_request,如antibanding參數設定

res = mHal3Device->ops->process_capture_request(mHal3Device,&request);    ATRACE_END();

     .......

}

a.1 waitForNextRequest()

    Camera3Device::RequestThread::waitForNextRequest() {

   ………

    while (mRequestQueue.empty()) {

        if (!mRepeatingRequests.empty()) {

            // Always atomically enqueue allrequests in a repeating request

            // list. Guarantees a completein-sequence set of captures to

            // application.

            const RequestList &requests =mRepeatingRequests;

            RequestList::const_iteratorfirstRequest =

                    requests.begin();

            nextRequest = *firstRequest;

            //把目前的mRepeatingRequests插入到mRequestQueue

           mRequestQueue.insert(mRequestQueue.end(),

                    ++firstRequest,

                    requests.end());

            // No need to wait any longer

            mRepeatingLastFrameNumber = mFrameNumber+ requests.size() - 1;

            break;

        }

        //等待下一個request

        res =mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);

       if ((mRequestQueue.empty() && mRepeatingRequests.empty()) ||

                exitPending()) {

            Mutex::Autolock pl(mPauseLock);

            if (mPaused == false) {

                ALOGV(%s: RequestThread: Goingidle, __FUNCTION__);

                mPaused = true;

                // Let the tracker know

                sp<statustracker>statusTracker = mStatusTracker.promote();

                if (statusTracker != 0) {

                   statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);

                }

            }

            // Stop waiting for now and letthread management happen

            return NULL;

        }

    }

    if (nextRequest == NULL) {

        // Don't have a repeating requestalready in hand, so queue

        // must have an entry now.

        RequestList::iterator firstRequest =

                mRequestQueue.begin();

        nextRequest = *firstRequest;

//取一根mRequestQueue中的CaptureRequest,來自于mRepeatingRequests的next

        mRequestQueue.erase(firstRequest);

    }

    ………

    if (nextRequest != NULL) {

        //對每一個非空的request需要幀号++

nextRequest->mResultExtras.frameNumber= mFrameNumber++;       nextRequest->mResultExtras.afTriggerId = mCurrentAfTriggerId;

       nextRequest->mResultExtras.precaptureTriggerId = mCurrentPreCaptureTriggerId;

    }

    return nextRequest;

}

該函數是響應RequestList的核心,通過不斷的輪訓休眠等待一旦mRepeatingRequests有Request可處理時,就将他内部所有的CaptureRequest加入到mRequestQueue 中去,理論來說每一個CaptureRequest對應着一幀的請求處理,每次響應時可能會出現mRequestQueue包含了多個 CaptureRequest。

通過nextRequest->mResultExtras.frameNumber= mFrameNumber++表示目前CaptureRequest在處理的一幀圖像号。

對于mRepeatingRequests而言,隻有其非空,在執行完一次queue操作後,在循環進入執行時,會自動對 mRequestQueue進行erase操作,是的mRequestQueue變為empty後再次重新加載mRepeatingRequests中的 内容,進而形成一個隊repeatRequest的重複響應過程。

a.2

camera_metadata_entry_t requestIdEntry =nextRequest->mSettings.find(ANDROID_REQUEST_ID);提取該CaptureRequest對應的 Request 類型值;

a.3 getBuffer操作

a.4 mHal3Device->ops->process_capture_request(mHal3Device,&request)

這裡的request是已經由一個CaptureRequest轉換為和HAL3.0互動的camera3_capture_request_t結構。

3、    總結

至此已經完成了一次向HAL3.0 Device發送一次完整的Request的請求。從最初Preview啟動建立多個OutPutStream,再是将這些Stream打包成一個 mPreviewRequest來啟動stream,随後将這個Request又轉變為一個CaptureRequest,直到轉為Capture list後交由RequestThread來處理這些請求。每一次的Request簡單可以說是Camera3Device向HAL3.0請求一幀資料, 當然每一次Request也可以包含各種控制操作,如AutoFocus等内容。

2.3.3 opencamera過程調用device3 initialize函數

    App至framework流程上面章節已做簡要分析,frameworks -> hal初始化框圖如下:

2.3.4 frameworks層設定參數流程

    設定參數setParameters流程圖如下所示:

    Frameworks層:

2.3.5設定參數下至hal層流程

由2.3.2節可知,開啟并在request線程中--Camera3Device::RequestThread::threadLoop調用hal層接口函數mHal3Device->ops->process_capture_request(mHal3Device, &request),接口函數中會完成參數設定相關工作,如antibanding的設定。

根據2.3.6節可知,antibanding等相關參數已經更新到requestlist中,hal層參數設定如下圖所示:

繼續閱讀