該文檔主要淺析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 ¶ms, 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 ¶ms) {
......
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 ¶ms) {
………
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 ¶ms) {
………
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層參數設定如下圖所示: