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函数里面完成。
|
|