配色: 字号:
Android 5
2016-10-21 | 阅:  转:  |  分享 
  
Android5.0Camera系统源码分析(3):Camera预览流程控制流

1.前言



本文分析的是Android系统源码,从frameworks层到hal层,记录了Camera进入预览模式的重点代码,主要为控制流程的代码,有关图像buffer的传递暂不涉及,硬件平台基于mt6735。由于某些函数比较复杂,在贴出代码时会适当对其进行简化。



2.APP层



这里将分析app层令Camera进入预览模式的两个重点api:setPreviewDisplay和startPreview



mCamera.setPreviewDisplay(mSurfaceHolder);

mCamera.startPreview();



3.setPreviewDisplay函数分析



预览图像最终是要在lcd上显示的,想要在lcd上显示图像就需要用到Surface。填充Surface有两种方法,一种是注册callback函数,预览数据将在callback函数中返回,得到数据后再把它送到Surface里面;另一种是在开始预览之前就为底层设置好Surface,底层获取数据后直接把数据送到Surface里面,为底层设置好Surface就是setPreviewDisplay的作用,



3.1frameworks层



先来看frameworks层的实现



publicfinalvoidsetPreviewDisplay(SurfaceHolderholder)throwsIOException{

if(holder!=null){

setPreviewSurface(holder.getSurface());

}else{

setPreviewSurface((Surface)null);

}

}



setPreviewSurface是一个jni函数,它的实现在android_hardware_Camera.cpp中



staticvoidandroid_hardware_Camera_setPreviewSurface(JNIEnvenv,jobjectthiz,jobjectjSurface)

{

spcamera=get_native_camera(env,thiz,NULL);

if(camera==0)return;



spgbp;

spsurface;

if(jSurface){

surface=android_view_Surface_getSurface(env,jSurface);

if(surface!=NULL){

gbp=surface->getIGraphicBufferProducer();

}

}



if(camera->setPreviewTarget(gbp)!=NO_ERROR){

jniThrowException(env,"java/io/IOException","setPreviewTexturefailed");

}

}



//passthebufferedIGraphicBufferProducertothecameraservice

status_tCamera::setPreviewTarget(constsp&bufferProducer)

{

spc=mCamera;

returnc->setPreviewTarget(bufferProducer);

}



//setthebufferconsumerthatthepreviewwilluse

status_tCameraClient::setPreviewTarget(

constsp&bufferProducer){

spbinder;

spwindow;

if(bufferProducer!=0){

binder=bufferProducer->asBinder();

window=newSurface(bufferProducer,/controlledByApp/true);

}

returnsetPreviewWindow(binder,window);

}



ANativeWindow顾名思义“本地窗口”,Surface类继承了ANativeWindow类。按照网上的说法,ANativeWindow类是连接OpenGL和Android窗口系统的桥梁,即OpenGL需要通过ANativeWindow类来间接地操作Android窗口系统。但我们接下来要操作ANativeWindow的不是OpenGL,而是CameraClient



status_tCameraClient::setPreviewWindow(constsp&binder,

constsp&window){

if(window!=0){

result=native_window_api_connect(window.get(),NATIVE_WINDOW_API_CAMERA);

if(result!=NO_ERROR){

ALOGE("native_window_api_connectfailed:%s(%d)",strerror(-result),

result);

returnresult;

}

}



//Ifpreviewhasbeenalreadystarted,registerpreviewbuffersnow.

if(mHardware->previewEnabled()){

if(window!=0){

native_window_set_scaling_mode(window.get(),

NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);

native_window_set_buffers_transform(window.get(),mOrientation);

result=mHardware->setPreviewWindow(window);

}

}

returnresult;

}



/SettheANativeWindowtowhichpreviewframesaresent/

status_tsetPreviewWindow(constsp&buf)

{

mPreviewWindow=buf;

mHalPreviewWindow.user=this;

returnmDevice->ops->set_preview_window(mDevice,

buf.get()?&mHalPreviewWindow.nw:0);

}



ANativeWindow最终保存在mPreviewWindow变量中,而传到Hal层的则是mHalPreviewWindow.nw操作集,Hal层将通过它来间接的操作mPreviewWindow。



mDevice就是上篇文章中最后讲到的从Hal返回的mDevice对象,而它的ops指针指向的是gCameraDevOps结构体,从这里开始进入Hal层



3.2Hal层



gCameraDevOps就在Cam1Device.cpp中定义



staticmtk_camera_device_opsconst

gCameraDevOps=

{

#defineOPS(name)name:camera_##name



{

OPS(set_preview_window),

OPS(set_callbacks),

OPS(enable_msg_type),

OPS(disable_msg_type),

OPS(msg_type_enabled),

OPS(start_preview),

OPS(stop_preview),

OPS(preview_enabled),

OPS(store_meta_data_in_buffers),

OPS(start_recording),

OPS(stop_recording),

OPS(recording_enabled),

OPS(release_recording_frame),

OPS(auto_focus),

OPS(cancel_auto_focus),

OPS(take_picture),

OPS(cancel_picture),

OPS(set_parameters),

OPS(get_parameters),

OPS(put_parameters),

OPS(send_command),

OPS(release),

OPS(dump)

},

OPS(mtk_set_callbacks),



#undefOPS

};



可以看到有关Camera的所有操作都这这里,接着看函数set_preview_window的实现



//Implementationofcamera_device_ops

staticintcamera_set_preview_window(

structcamera_devicedevice,

structpreview_stream_opswindow

)

{

interr=-EINVAL;



Cam1DeviceconstpDev=Cam1Device::getDevice(device);

if(pDev)

{

err=pDev->setPreviewWindow(window);

}



returnerr;

}



Cam1Device::getDevice函数获取到的将是DefaultCam1Device对象,而setPreviewWindow函数则在它的父类Cam1DeviceBase中实现



/

Setthepreview_stream_opstowhichpreviewframesaresent.

/

status_t

Cam1DeviceBase::

setPreviewWindow(preview_stream_opswindow)

{

status_tstatus=initDisplayClient(window);

if(OK==status&&previewEnabled()&&mpDisplayClient!=0)

{

status=enableDisplayClient();

}



returnstatus;

}



第9行,初始化DisplayClient

第11行,通知DisplayClient开始工作

重点关注下函数initDisplayClient的实现



status_t

Cam1DeviceBase::

initDisplayClient(preview_stream_opswindow)

{

status_tstatus=OK;

SizepreviewSize;



//[1]ChecktoseewhetherthepassedwindowisNULLornot.

if(!window)

{

if(mpDisplayClient!=0)

{

mpDisplayClient->uninit();

mpDisplayClient.clear();

}

status=OK;

gotolbExit;

}



//[2]Getpreviewsize.

if(!queryPreviewSize(previewSize.width,previewSize.height))

{

status=DEAD_OBJECT;

gotolbExit;

}

//[3]InitializeDisplayClient.

if(mpDisplayClient!=0)

{

......

}

//[3.1]createaDisplayClient.

mpDisplayClient=IDisplayClient::createInstance();

if(mpDisplayClient==0)

{

MY_LOGE("CannotcreatempDisplayClient");

status=NO_MEMORY;

gotolbExit;

}

//[3.2]initializethenewly-createdDisplayClient.

if(!mpDisplayClient->init())

{

MY_LOGE("mpDisplayClientinit()failed");

mpDisplayClient->uninit();

mpDisplayClient.clear();

status=NO_MEMORY;

gotolbExit;

}

//[3.3]setpreview_stream_ops&relatedwindowinfo.

if(!mpDisplayClient->setWindow(window,previewSize.width,previewSize.height,queryDisplayBufCount()))

{

status=INVALID_OPERATION;

gotolbExit;

}

//[3.4]setImageBufferProviderClientifitexist.

if(mpCamAdapter!=0&&!mpDisplayClient->setImgBufProviderClient(mpCamAdapter))

{

status=INVALID_OPERATION;

gotolbExit;

}



status=OK;



lbExit:

if(OK!=status)

{

MY_LOGD("Cleanup...");

......

}



returnstatus;

}



initDisplayClient函数都做了些什么事情注释已经写得很清楚

第31-47行,创建并初始化DisplayClient,其中DisplayClient是图像消费者,由它负责将图像数据送往Surface

第48-53行,DisplayClient想要操作Surface只能通过preview_stream_ops,也就是从上层传下来mHalPreviewWindow.nw操作集,setWindow函



数会通过preview_stream_ops对Surface设置一些参数,并把preview_stream_ops保存在DisplayClient的mpStreamOps变量中,以后用到的时候



才找得到。

第54-59行,DisplayClient作为消费者,那么就会有生产者,也就是CamAdapter。由CamAdapter提供图像数据,再由DisplayClient将数据送往



Surface。但由于这个时候的mpCamAdapter为空,所以这里的setImgBufProviderClient函数暂时不会被调用。



4.startPreview函数分析



app层通过调用startPreview函数来进入预览模式,与setPreviewWindow的流程一样,最终会调到Cam1DeviceBase的startPreview函数



4.1Cam1DeviceBase::startPreview函数分析



/

Startpreviewmode.

/

status_t

Cam1DeviceBase::

startPreview()

{

status_tstatus=OK;



if(!onStartPreview())

{

MY_LOGE("onStartPreviewLocked()fail");

status=INVALID_OPERATION;

gotolbExit;

}



if(mpDisplayClient==0)

{

MY_LOGD("DisplayClientisnotready.");

}

elseif(OK!=(status=enableDisplayClient()))

{

gotolbExit;

}



......



//startPreviewinCameraAdapter.

{

status=mpCamAdapter->startPreview();

if(OK!=status)

{

MY_LOGE("startPreview()inCameraAdapterreturns:[%s(%d)]",::strerror(-status),-status);

gotolbExit;

}

}



......



status=OK;

lbExit:

if(OK!=status)

{

......

}



MY_LOGI("-status(%d)",status);

returnstatus;

}



第10行,onStartPreview函数主要就是创建并初始化CameraAdapter

第21行,通知DisplayClient开始工作

第30行,mpCamAdapter->startPreview函数工作量巨大,包含了初始化buffer、3A,设置ISP和sensor驱动进入预览模式等工作。



先看CameraAdapter的初始化



DefaultCam1Device::

onStartPreview()

{

boolret=false;



......



//(2)InitializeCameraAdapter.

if(!initCameraAdapter())

{

MY_LOGE("NULLCameraAdapter");

gotolbExit;

}

//

ret=true;

lbExit:

returnret;

}



bool

Cam1DeviceBase::

initCameraAdapter()

{

boolret=false;



//Create&initanewCamAdapter.

mpCamAdapter=ICamAdapter::createInstance(mDevName,mi4OpenId,mpParamsMgr);

if(mpCamAdapter!=0&&mpCamAdapter->init())

{

//(.1)init.

mpCamAdapter->setCallbacks(mpCamMsgCbInfo);

mpCamAdapter->enableMsgType(mpCamMsgCbInfo->mMsgEnabled);



//(.2)InvokeitssetParameters

if(OK!=mpCamAdapter->setParameters())

{

//Iffail,itshoulddestroyinstancebeforereturn.

MY_LOGE("mpCamAdapter->setParameters()fail");

gotolbExit;

}



//(.3)Sendto-docommands.

{

Mutex::Autolock_lock(mTodoCmdMapLock);

for(size_ti=0;i
{

CommandInfoconst&rCmdInfo=mTodoCmdMap.valueAt(i);

MY_LOGD("sendqueuedcmd(%#x),args(%d,%d)",rCmdInfo.cmd,rCmdInfo.arg1,rCmdInfo.arg2);

mpCamAdapter->sendCommand(rCmdInfo.cmd,rCmdInfo.arg1,rCmdInfo.arg2);

}

mTodoCmdMap.clear();

}



//(.4)[DisplayClient]setImageBufferProviderClientifneeded.

if(mpDisplayClient!=0&&!mpDisplayClient->setImgBufProviderClient(mpCamAdapter))

{

MY_LOGE("mpDisplayClient->setImgBufProviderClient()fail");

gotolbExit;

}

}



ret=true;

lbExit:

returnret;

}



创建CamAdapter实例并对它进行初始化。其中第35-40行,之前在setPreviewWindow里没机会调用的mpDisplayClient->setImgBufProviderClient函数将在这里调用。DisplayClient和CamAdapter将会通过setImgBufProviderClient函数关联起来,也就是告诉DisplayClient图像数据将由CamAdapter提供。至于CamAdpter如何获取图像数据和DisplayClient如何将数据送往Surface将在以后解析。



4.2mpCamAdapter->startPreview函数分析



既然数据由CamAdapter提供,那么怎么告诉它开始向DisplayClient提供数据呢,还的继续分析mpCamAdapter->startPreview函数



status_t

CamAdapter::

startPreview()

{

returnmpStateManager->getCurrentState()->onStartPreview(this);

}



status_t

StateIdle::

onStartPreview(IStateHandlerpHandler)

{

......

status=pHandler->onHandleStartPreview();

......

returnstatus;

}



mpStateManager->getCurrentState函数获取到的是idle状态,在上文提到mpCamAdapter->init函数中设置。而StateIdle::onStartPreview函数将会回调CamAdapter的onHandleStartPreview函数,这个函数很长,非常长,相当长。



/

CamAdapter::startPreview()->IState::onStartPreview()->

IStateHandler::onHandleStartPreview()->CamAdapter::onHandleStartPreview()

/

status_t

CamAdapter::

onHandleStartPreview()

{

......



mpPass2Node=Pass2Node::createInstance(PASS2_FEATURE);

mpCamGraph=ICamGraph::createInstance(

getOpenId(),

mUserName.string());

mpPass1Node=Pass1Node::createInstance(p1NodeInitCfg);

mpCamGraph->setBufferHandler(PASS1_RESIZEDRAW,mpAllocBufHdl);

mpCamGraph->setBufferHandler(PASS1_FULLRAW,mpAllocBufHdl);

mpCamGraph->connectData(PASS1_RESIZEDRAW,CONTROL_RESIZEDRAW,mpPass1Node,mpDefaultwww.shanxiwang.netCtrlNode);

mpCamGraph->connectData(CONTROL_PRV_SRC,PASS2_PRV_SRC,mpDefaultCtrlNode,mpPass2Node);

mpCamGraph->connectNotify(PASS1_START_ISP,mpPass1Node,mpDefaultCtrlNode);

mpCamGraph->connectNotify(PASS1_STOP_ISP,mpPass1Node,mpDefaultCtrlNode);

mpCamGraph->connectNotify(PASS1_EOF,mpPass1Node,mpDefaultCtrlNode);



if(!mpCamGraph->init()){

......

}

if(!mpCamGraph->start()){

......

}

lbExit:

......

returnret;

}



暂时先把那些乱七八糟的参数设置的代码忽略掉,重点关注下Pass1Node、Pass2Node和DefaultCtlNode,以及作为各个Node通讯的桥梁的CamGraph。



CamGraph代表了整个系统,而使用不同的Node来描述不同的buffer处理,所有的Node都需要连接到CamGraph。各个Node之间的通讯就需要用到connectData和connectNotify函数,connectData为两个node之间buffer传输的连接,而connectNotify为两个node之间消息传输的连接。



例如第18行调用了connectData(PASS1_RESIZEDRAW,CONTROL_RESIZEDRAW,mpPass1Node,mpDefaultCtrlNode)之后Pass1Node和DefaultCtrlNode就连接在一起,事件是PASS1_RESIZEDRAW,也就是说当Pass1Node调用handlePostBuffer(PASS1_RESIZEDRAW,buffer)的时候,DefaultCtrlNode里面的onPostBuffer函数将会接受到Pass1Node的buffer。



同理第20行调用了connectNotify(PASS1_START_ISP,mpPass1Node,mpDefaultCtrlNode),事件是PASS1_START_ISP,当Pass1Node调用handleNotify(PASS1_START_ISP)的时候,DefaultCtrlNode里面的onNotify函数将会接收到PASS1_START_ISP消息。



connectData和connectNotify的不同之处在于,一个可以传输整个buffer,但只能一对一连接,一个只能传输消息,但可以一对多连接,这两个函数的实现这里就不解析了,里面各种子类、父类的关系比较复杂,整理起来比较麻烦。需要关注的是mpCamGraph->init和mpCamGraph->start这两个函数,先来看看init



MBOOL

ICamGraph::

init()

{

returnmpImpl->init();

}



这里的mpImpl指的是ICamGraphImpl



MBOOL

ICamGraphImpl::

init()

{

Mutex::Autolock_l(mLock);

MY_LOGD("init+");

MY_ASSERT_STATE(mState==State_Connected,mState);



MBOOLret=MTRUE;

vector::const_iteratoriter;

for(iter=mvNodeImpls.begin();iter!=mvNodeImpls.end();iter++)

{

MY_ASSERT_NODE_OP(ret,(iter),init);

}



lbExit:

if(!ret)

{

......

}

else

{

mState=State_Initiated;

}

MY_LOGD("init-");

returnret;

}



mvNodeImpls里保存的是ICamThreadImpl对象,每一个ICamThreadImpl代表一个CamNode,例如Pass1Node。这个函数所做的事情就是循环遍历所有的ICamThreadImpl,并且调用它们的init函数



MBOOL

ICamThreadImpl::

init()

{

Mutex::Autolock_l(mLock);

MY_ASSERT_STATE(mState==State_Connected,mState);



MY_LOGV("init");

MY_ASSERT(mpSelf->onInit());

MY_ASSERT(mpThread->createThread()

&&mpThread->sendThreadCmd(TCmd_Sync)

&&mpThread->sendThreadCmd(TCmd_Init)

&&mpThread->sendThreadCmd(TCmd_Sync));



mState=State_Initiated;

returnMTRUE;

}



ICamThreadImpl里的mySelf成员就指向了它所代表的CamNode,例如Pass1Node。也就是说接下来所有保存在mvNodeImpls里面的CamNode的onInit函数都会被调用。保存在mvNodeImpls里面的CamNode有很多,例如Pass1Node、Pass2Node、DefaultCtrlNode等。Pass1Node负责和SensorDriver、ISPDriver打交道,进入预览模式的重点工作都由它来完成,所以这里只分析Pass1Node,来看看Pass1Node的onInit函数



MBOOL

Pass1NodeImpl::

onInit()

{

......

mpIspSyncCtrlHw=IspSyncControlHw::createInstance(getSensorIdx());

mpIspSyncCtrlHw->setIspEnquePeriod(mIspEnquePeriod);

mpIspSyncCtrlHw->setSensorInfo(

mInitCfg.muScenario,

sensorSize.w,

sensorSize.h,

mSensorInfo.sensorType);

......

mpCamIO=(IHalCamIO)INormalPipe::createInstance(getSensorIdx(),getName(),mIspEnquePeriod);

if(!mpCamIO)

{

MY_LOGE("createNormalPipefailed");

gotolbExit;

}

if(!mpCamIO->init())

{

MY_LOGE("camioinitfailed");

gotolbExit;

}

ret=MTRUE;

lbExit:

returnret;

}



主要就是对IspSyncCtrl和CamIO进行初始化,一个用来和ISP打交道,另一个用来和驱动打交道



回到onHandleStartPreview函数,在执行完mpCamGraph->init函数之后就到mpCamGraph->start函数了。和mpCamGraph->init的流程一样,mpCamGraph->start所做的事情就是循环遍历所有的CamNode,并且回调它们的onStart函数,直接看Pass1Node的onStart函数



MBOOL

Pass1NodeImpl::

onStart()

{

listlHwPortCfg;

if(!getHwPortConfig(&lHwPortCfg))

{

MY_LOGE("getHwPortConfigfailed");

gotolbExit;

}



if(!startHw(lHwPortCfg))

{

MY_LOGE("startHwfailed");

gotolbExit;

}

ret=MTRUE;

lbExit:

FUNC_END;

returnret;

}



接着看startHw函数的实现



MBOOL

Pass1NodeImpl::

startHw(list&plPortCfg)

{

//1.Allocatedringbuffers.

if(pthread_create(&mThreadHandle,NULL,doThreadAllocBuf,&th_data)!=0)

{

MY_LOGE("pthreadcreatefailed");

gotolbExit;

}



//2.LockPass1HW

if(!mpIspSyncCtrlHw->lockHw(IspSyncControlHw::HW_PASS1))

{

MY_LOGE("ispsynclockpass1failed");

gotolbExit;

}



......

//3.ConfigureRRZOandIMGO

if(!mpCamIO->configPipe(halCamIOinitParam)){

MY_LOGE("configPipefailed");

gotolbExit;

}



newMagicNum=mpIspSyncCtrlHw->getMagicNum(MTRUE);

if(!configFrame(newMagicNum)){

MY_LOGE("configFramefailed");

gotolbExit;

}



//4.SendPASS1_START_ISPevent

handleNotify(PASS1_START_ISP,newMagicNum,0);



......

//5.Enquebuffer

if(!mpCamIO->enque(halCamIOQBuf)){

MY_LOGE("enquefailed");

gotolbExit;

}



//6.StartISP

if(!mpCamIO->start()){

MY_LOGE("startfailed");

gotolbExit;

}



ret=MTRUE;

lbExit:

if(!ret){

......

}

returnret;

}



这个函数做的事情比较多,上面标记的每个步骤都很复杂

第5-10行:创建一个线程来分配ringbuffers,用于存放从驱动获取到的图像数据

第20-24行:配置ISP和Sensor驱动预览相关的参数,记得sensor驱动中(例如imx214mipiraw_Sensor.c)的preview_setting函数吗,就是在这个时候被调用的



第33行:发送PASS1_START_ISP事件,其它的CamNode接收到该事件后会做相应的处理,例如DefaultCtlNode,会通知Hal3A进入CameraPreview状态

第42-46行:让ISP开始工作,到这里准备工作都已经完成,Camera已经进入了预览模式,接下来就是不断获取图像数据,并将它送到显示器了。



5.总结



setPreviewWindow函数就是为hal层准备好Surface,hal层只能通过上层传下来的mHalPreviewWindow.nw来间接的操作Surface,而mHalPreviewWindow.nw保存在DisplayClient里面,也就是说DisplayClient是lcd显示图像的关键



startPreview函数的工作重点在CamAdapter,它代表Camera硬件,由它提供图像数据给DisplayClient。CamAdapter包含了多个CamNode,不同的CamNode用来描述不同的buffer处理,例如Pass1Node,它负责和驱动打交道,进入预览模式的重点工作都在它的startHw函数里面完成。

献花(0)
+1
(本文系网络学习天...首藏)