预览数据流分析(一) 数据的获取
mCameraDevice.startPreview();
/*
上篇文章已经有知道这个mCameraDevice就是对应的CameraClient的客户端,所以这个的startPreview就是CameraClient::startPreview()
*/
Step 1:
\frameworks\av\services\camera\libcameraservice\api1\CameraClient.cpp
// start preview mode
status_t CameraClient::startPreview() {
LOG1("startPreview (pid %d)", getCallingPid());
return startCameraMode(CAMERA_PREVIEW_MODE);
}
Step 2:
\frameworks\av\services\camera\libcameraservice\api1\CameraClient.cpp
// start preview or recording
status_tCameraClient::startCameraMode(camera_mode mode) {
LOG1("startCameraMode(%d)", mode);
Mutex::Autolock lock(mLock);
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
switch(mode) {
case CAMERA_PREVIEW_MODE:
if (mSurface == 0 && mPreviewWindow == 0) {
LOG1("mSurface is not setyet.");
// still able to startpreview in this case.
}
return startPreviewMode();
case CAMERA_RECORDING_MODE:
if (mSurface == 0 && mPreviewWindow == 0) {
ALOGE("mSurface ormPreviewWindow must be set before startRecordingMode.");
return INVALID_OPERATION;
}
return startRecordingMode();
default:
return UNKNOWN_ERROR;
}
}
Step 3:
\frameworks\av\services\camera\libcameraservice\api1\CameraClient.cpp
status_t CameraClient::startPreviewMode() {
LOG1("startPreviewMode");
status_t result = NO_ERROR;
// if preview has been enabled, nothing needs to be done
if (mHardware->previewEnabled()) {
return NO_ERROR;
}
if (mPreviewWindow != 0) {
native_window_set_scaling_mode(mPreviewWindow.get(),
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
native_window_set_buffers_transform(mPreviewWindow.get(),
mOrientation);
}
mHardware->setPreviewWindow(mPreviewWindow);
result = mHardware->startPreview();
return result;
}
/*
主要分析这两个接口:
1, mHardware->setPreviewWindow
2, mHardware->startPreview();
mHardware前面已经有说明说,对应的就是Cam1Device类,而其具体的实现则是在其父类Cam1DeviceBase中实现的。所以mHardware->setPreviewWindow
对应就是Cam1DeviceBase::
setPreviewWindow(preview_stream_ops*window)
mHardware->startPreview();对应的是status_t
Cam1DeviceBase::
startPreview()
*/
Step 4.1.1:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\v1\device\Cam1DeviceBase.cpp
status_t
Cam1DeviceBase::
setPreviewWindow(preview_stream_ops* window)
{
CAM_TRACE_CALL();
MY_LOGI("+ window(%p)", window);
/*
这里主要是初始化了一个DisplayClient的类。具体这个DisplayClient的功能。后面再分析。
DisplayClient创建的时候,生成了一个createDisplayThread的线程,用来处理displayclient发送过来的消息,
createImgBufQueue 则用于存储数据,具体如何运作的待分析。
bool
DisplayClient::
init()
{
bool ret =false;
//
MY_LOGD("+");
//
ret = createDisplayThread()
&& createImgBufQueue()
;
//
MY_LOGD("-ret(%d)", ret);
return ret;
}
*/
status_t status = initDisplayClient(window);
if ( OK == status &&previewEnabled() && mpDisplayClient != 0 )
{
status = enableDisplayClient();
if(mbWindowReady)
{
waitStartPreviewDone();
}
}
//
return status;
}
Step 4.1.2:
status_t
Cam1DeviceBase::
initDisplayClient(preview_stream_ops*window)
{
CAM_TRACE_CALL();
#if '1'!=MTKCAM_HAVE_DISPLAY_CLIENT
#warning "Not Build Display Client"
MY_LOGD("Not Build Display Client");
return OK;
#else
status_t status = OK;
Size previewSize;
//
MY_LOGD("+ window(%p)", window);
//
//
// [1] Check to see whether thepassed window is NULL or not.
if ( ! window )
{
MY_LOGW("NULL window is passed into...");
mbWindowReady = false;
//
if ( mpDisplayClient != 0 )
{
MY_LOGW("destroy the current display client(%p)...",mpDisplayClient.get());
mpDisplayClient->uninit();
mpDisplayClient.clear();
}
status = OK;
goto lbExit;
}
mbWindowReady = true;
//
//
// [2] Get preview size.
if ( !queryPreviewSize(previewSize.width, previewSize.height) )
{
MY_LOGE("queryPreviewSize");
status = DEAD_OBJECT;
goto lbExit;
}
//
//
// [3] Initialize Display Client.
if ( mpDisplayClient != 0 )
{
if ( previewEnabled() )
{
MY_LOGW("Do nothing since Display Client(%p) is already createdafter startPreview()", mpDisplayClient.get());
// This method must be called before startPreview(). The one exception isthat
// if the preview surface texture is not set (or set to null) beforestartPreview() is called,
// then this method may be called once with a non-null parameter to set thepreview surface.
status = OK;
goto lbExit;
}
else
{
MY_LOGW("New window is set after stopPreview or takePicture.Destroy the current display client(%p)...", mpDisplayClient.get());
mpDisplayClient->uninit();
mpDisplayClient.clear();
}
}
// [3.1] create a Display Client.
mpDisplayClient = IDisplayClient::createInstance();
if ( mpDisplayClient == 0 )
{
MY_LOGE("Cannot create mpDisplayClient");
status = NO_MEMORY;
goto lbExit;
}
// Display Rotation
if(mpParamsMgr->getDisplayRotationSupported())
{
MY_LOGD("orientation = %d", mOrientation);
mpDisplayClient->SetOrientationForDisplay(mOrientation);
}
// [3.2] initialize thenewly-created Display Client.
if ( ! mpDisplayClient->init())
{
MY_LOGE("mpDisplayClient init()failed");
mpDisplayClient->uninit();
mpDisplayClient.clear();
status = NO_MEMORY;
goto lbExit;
}
// [3.3] set preview_stream_ops& related window info.
if ( ! mpDisplayClient->setWindow(window,previewSize.width, previewSize.height, queryDisplayBufCount()) )
{
status = INVALID_OPERATION;
goto lbExit;
}
// [3.4] set Image BufferProvider Client if it exist.
if ( mpCamAdapter != 0 &&! mpDisplayClient->setImgBufProviderClient(mpCamAdapter) )
{
status = INVALID_OPERATION;
goto lbExit;
}
//
//
status = OK;
//
lbExit:
if ( OK != status )
{
MY_LOGD("Cleanup...");
mpDisplayClient->uninit();
mpDisplayClient.clear();
}
//
MY_LOGD("- status(%d)", status);
return status;
#endif//MTKCAM_HAVE_DISPLAY_CLIENT
}
Step 4.1.3:
status_t
Cam1DeviceBase::
enableDisplayClient()
{
status_t status = OK;
Size previewSize;
//
MY_LOGD("+");
//
// [1] Get preview size.
if ( !queryPreviewSize(previewSize.width, previewSize.height) )
{
MY_LOGE("queryPreviewSize");
status = DEAD_OBJECT;
goto lbExit;
}
//
if(mpParamsMgr->getIfFirstPreviewFrameAsBlack())
{
mpDisplayClient->setFirstFrameBlack();
mpParamsMgr->set(MtkCameraParameters::KEY_FIRST_PREVIEW_FRAME_BLACK,0);
}
// [2] Enable
if ( !mpDisplayClient->enableDisplay(previewSize.width, previewSize.height,queryDisplayBufCount(), mpCamAdapter) )
{
MY_LOGE("mpDisplayClient(%p)->enableDisplay()",mpDisplayClient.get());
status = INVALID_OPERATION;
goto lbExit;
}
//
status = OK;
lbExit:
MY_LOGD("- status(%d)",status);
return status;
}
4.2.1:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\v1\device\Cam1DeviceBase.cpp
/*
这个是preview的具体实现过程,很重要的一个函数。
*/
status_t
Cam1DeviceBase::
startPreview()
{
CAM_TRACE_CALL();
MY_LOGI("+");
//
status_t status = OK;
bool usePreviewThread = false;
//
if( mpParamsMgr->getIfFirstPreviewFrameAsBlack() &&
mbWindowReady == false)
{
usePreviewThread = true;
disableWaitSensorThread(true);
}
//
{
CAM_TRACE_NAME("deviceStartPreview");
if ( mpCamAdapter != 0 &&mpCamAdapter->isTakingPicture() )
{
MY_LOGE("Capture is not done");
status = INVALID_OPERATION;
return status;
}
//
if ( previewEnabled() )
{
MY_LOGD("Preview already running");
status = ALREADY_EXISTS;
return status;
}
/*
onStartPreview主要是初始化了一个CameraAdapter的类,用于提供BUFF数据,并且将该类设置到displayclient和 camclient中去了。具体过程可以参照:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\v1\device\Cam1DeviceBase.cpp
initCameraAdapter
*/
if ( ! onStartPreview())
{
MY_LOGE("onStartPreviewLocked() fail");
status = INVALID_OPERATION;
goto lbExit;
}
}
//
{
CAM_TRACE_NAME("clientStartPreview");
if ( mpDisplayClient == 0 )
{
MY_LOGD("DisplayClient is not ready.");
}
/*
对displayclinet进行了相关设置,将数据输出到pStreamOps,这个就是我们要输出的数据。具体如何使用的,待学习。
mpDisplayClient->enableDisplay(previewSize.width,previewSize.height, queryDisplayBufCount(), mpCamAdapter)
*/
else if ( OK != (status = enableDisplayClient()))
{
goto lbExit;
}
//
if ( mpCamClient != 0 )
{
/*
这里这个CamClient想说明下,以前与CameraClient搞混淆了。这里的CamClient对应的是:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\v1\client\CamClient\CamClient.cpp
里面有previewClient和recoudClient,这里只关心previewClient .里面主要是设置了相关参数,申请了一块BUFF :
bool
PreviewClient::
initBuffers()
{
bool ret = false;
//
// (1) Lock
Mutex::Autolock _l(mModuleMtx);
//
// (2) Allocate buffers.
muImgBufIdx = 0;
mpImgBufMgr = ImgBufManager::alloc(
ms8PrvTgtFmt,
mi4PrvWidth,
mi4PrvHeight,
eMAX_PREVIEW_BUFFER_NUM,
"PreviewClientCb",
mpCamMsgCbInfo->mRequestMemory,
0,
0);
if (mpImgBufMgr == 0 )
{
MY_LOGE("ImgBufManager::alloc()fail");
goto lbExit;
}
//
//
mpExtImgProc =ExtImgProc::createInstance();
if(mpExtImgProc != NULL)
{
mpExtImgProc->init();
}
//
//
ret = true;
lbExit:
return ret;
}
*/
if ( ! mpCamClient->startPreview())
{
status = INVALID_OPERATION;
goto lbExit;
}
}
// forward to registered clients
Vector
for (it = vmpCamClient.begin(); it != vmpCamClient.end(); ++it)
{
(*it)->startPreview();
}
}
//
// startPreview in CameraAdapter.
{
if(usePreviewThread)
{
if( pthread_create(&mStartPreviewTThreadHandle, NULL,startPreviewThread, this) != 0 )
{
ALOGE("startPreviewpthread create failed");
}
}
else
{
CAM_TRACE_NAME("adapterStartPreview");
/*
下面的步骤重点跟进下mpCamAdapter->startPreview();
*/
status = mpCamAdapter->startPreview();
if ( OK != status )
{
MY_LOGE("startPreview() inCameraAdapter returns: [%s(%d)]", ::strerror(-status), -status);
goto lbExit;
}
}
}
//
//
enableMsgType(CAMERA_MSG_PREVIEW_METADATA);
//
mIsPreviewEnabled = true;
//
status = OK;
lbExit:
if ( OK != status )
{
MY_LOGD("Cleanup after error");
//
if ( mpCamClient != 0 )
{
mpCamClient->stopPreview();
}
// forward to registered clients
Vector
for (it = vmpCamClient.begin(); it != vmpCamClient.end(); ++it)
{
(*it)->stopPreview();
}
//
disableDisplayClient();
}
//
MY_LOGI("- status(%d)", status);
return status;
}
Step 4.2.2:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\platform\mt8127\v1\hal\adapter\MtkDefault\Preview\MtkDefaultCamAdapter.Preview.cpp
status_t
CamAdapter::
startPreview()
{
return mpStateManager->getCurrentState()->onStartPreview(this);
}
Step 4.2.3:
/*
mpStateManager->getCurrentState()->onStartPreview(this);到onHandleStartPreview的转换下面有注释,这里就不往里面分析了。
*/
/******************************************************************************
* CamAdapter::startPreview() ->IState::onStartPreview() ->
* IStateHandler::onHandleStartPreview() ->CamAdapter::onHandleStartPreview()
*******************************************************************************/
status_t
CamAdapter::
onHandleStartPreview()
{
CAM_TRACE_NAME("Adapter::onHandleStartPreview");
MY_LOGD("+");
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start, CPTFlagStart);
//
int32_t eResourceType = ( getParamsManager()->getRecordingHint() )
? ResourceLock::eMTKVIDEO_PRV :ResourceLock::eMTKPHOTO_PRV;
//
if ( !mpResourceLock->SetMode((ResourceLock::ECamAdapter)eResourceType) )
{
CAM_LOGE("Resource SetMode fail");
return INVALID_OPERATION;
}
//
if ( ! mpResourceLock->Lock((ResourceLock::ECamAdapter)eResourceType))
{
CAM_LOGE("Resource Lock fail");
return INVALID_OPERATION;
}
//
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start_init, CPTFlagStart);
if ( ! mpPreviewCmdQueThread->postCommand(PrvCmdCookie::eStart, PrvCmdCookie::eSemAfter) )
{
MY_LOGE("StartPreview stage 1 (start): fail");
goto lbExit;
}
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start_init, CPTFlagEnd);
//
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start_stable, CPTFlagStart);
if ( ! mpPreviewCmdQueThread->postCommand(PrvCmdCookie::eDelay, PrvCmdCookie::eSemAfter) )
{
MY_LOGE("StartPreview stage 2 (delay): fail");
goto lbExit;
}
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start_stable, CPTFlagEnd);
//
if ( ! mpPreviewCmdQueThread->postCommand(PrvCmdCookie::eUpdate, PrvCmdCookie::eSemBefore) )
{
MY_LOGE("StartPreview stage 3 (udpate): fail");
goto lbExit;
}
//
CPTLog(Event_Hal_Adapter_MtkDefaultPreview_start, CPTFlagEnd);
MY_LOGD("-");
return OK;
//
lbExit:
return INVALID_OPERATION;
}
/*
向mpPreviewCmdQueThread线程发了三个消息,eStartà eDelay-à eUpdate.下面我们进入mpPreviewCmdQueThread线程里面看看这三个消息的处理过程。
*/
Step 4.2.4:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\platform\mt8127\v1\hal\adapter\MtkDefault\Preview\PreviewCmdQueThread.cpp
bool
PreviewCmdQueThread::threadLoop()
{
FUNCTION_IN;
//
bool ret = true;
//
sp
//
if (getCommand(pCmdCookie))
{
if(pCmdCookie != 0)
{
pCmdCookie->postSem(PrvCmdCookie::eSemBefore);
}
//
bool isvalid = true;
//
switch (pCmdCookie->getCmd())
{
/*
PrvCmdCookie::eDelay只是一个延时的处理,我们下面跟进下start和update。
*/
casePrvCmdCookie::eStart:
isvalid = start();
break;
casePrvCmdCookie::eDelay:
isvalid =delay(EQueryType_Init);
break;
casePrvCmdCookie::eUpdate:
isvalid = update();
break;
case PrvCmdCookie::ePrecap:
isvalid = precap();
break;
case PrvCmdCookie::eStop:
isvalid = stop();
break;
case PrvCmdCookie::eExit:
default:
break;
}
//
if(pCmdCookie != 0)
{
pCmdCookie->setValid(isvalid);
pCmdCookie->postSem(PrvCmdCookie::eSemAfter);
}
}
//
FUNCTION_OUT;
//
return ret;
}
Step 4.2.5:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\platform\mt8127\v1\hal\adapter\MtkDefault\Preview\PreviewCmdQueThread.cpp
bool
PreviewCmdQueThread::
start()
{
CAM_TRACE_NAME("PrvCQT_DEF::start");
FUNCTION_IN;
//
bool ret = false;
vector
vector
ImgBufQueNode Pass1Node;
IhwScenario::PortBufInfo BufInfo;
//
EIspProfile_T eIspProfile = ( mspParamsMgr->getRecordingHint() )
? EIspProfile_VideoPreview :EIspProfile_NormalPreview;
ECmd_T eCmd = ( mspParamsMgr->getRecordingHint() )
? ECmd_CamcorderPreviewStart :ECmd_CameraPreviewStart;
mbRecordingHint = ( mspParamsMgr->getRecordingHint() )
? true : false;
//(0) scenario ID is decided by recording hint
//
int32_t eScenarioId = ( mspParamsMgr->getRecordingHint() )
?ACDK_SCENARIO_ID_VIDEO_PREVIEW : ACDK_SCENARIO_ID_CAMERA_PREVIEW;
//(1) sensor (singleton)
//
CPTLogStr(Event_Hal_Adapter_MtkDefaultPreview_start_init,CPTFlagSeparator, "Init Sensor");
//
if ( ! (ret = mSensorInfo.init((ACDK_SCENARIO_ID_ENUM)eScenarioId)))
{
MY_LOGE("Init sensor fail!!");
goto lbExit;
}
//(2) Hw scenario
//
CPTLogStr(Event_Hal_Adapter_MtkDefaultPreview_start_init,CPTFlagSeparator, "Init Hw");
//
mpHwScenario = IhwScenario::createInstance(eHW_VSS,mSensorInfo.getSensorType(),
mSensorInfo.meSensorDev,
mSensorInfo.mSensorBitOrder);
if(mpHwScenario != NULL)
{
if(!(mpHwScenario->init()))
{
MY_LOGE("init Hw Scenario fail!!");
goto lbExit;
}
}
else
{
MY_LOGE("mpHwScenario is NULL!!");
goto lbExit;
}
// (2.1) hw config
//
getCfg(eID_Pass1In|eID_Pass1Out, vimgInfo);
getHw()->setConfig(&vimgInfo);
// (2.2) enque pass 1 buffer
// must do this earlier thanhw start
CAM_TRACE_BEGIN("PrvBufHdl::allocBuffer");
mspPreviewBufHandler->allocBuffer(
mSensorInfo.getImgWidth(),
mSensorInfo.getImgHeight(),
mSensorInfo.getImgFormat(),
PASS1BUFCNT+PASS1BUFCNT_VSS);
CAM_TRACE_END();
for (int32_t i = 0; i < PASS1BUFCNT; i++)
{
mspPreviewBufHandler->dequeBuffer(eID_Pass1Out, Pass1Node);
mapNode2BufInfo(eID_Pass1Out, Pass1Node, BufInfo);
vBufPass1Out.push_back(BufInfo);
}
getHw()->enque(NULL, &vBufPass1Out);
//
#if VSS_ENABLE
mspPreviewBufHandler->dequeBuffer(eID_Pass1Out, Pass1Node);
mapNode2BufInfo(eID_Pass1Out, Pass1Node, BufInfo);
mvBufPass1OutVss.clear();
mvBufPass1OutVss.push_back(BufInfo);
#endif
//(3) 3A
//!! must be set after hw->enque; otherwise, over-exposure.
CPTLogStr(Event_Hal_Adapter_MtkDefaultPreview_start_init,CPTFlagSeparator, "Init 3A");
//
mp3AHal =Hal3ABase::createInstance(DevMetaInfo::queryHalSensorDev(gInfo.openId));
if ( ! mp3AHal )
{
MY_LOGE("init 3A fail!!");
goto lbExit;
}
CAM_TRACE_BEGIN("3A::setZoom");
mp3AHal->setZoom(100,0, 0, mSensorInfo.getImgWidth(), mSensorInfo.getImgHeight());
CAM_TRACE_END();
CAM_TRACE_BEGIN("3A::setIspProfile");
mp3AHal->setSensorMode(eScenarioId);
mp3AHal->setIspProfile(eIspProfile);
CAM_TRACE_END();
CAM_TRACE_BEGIN("3A::sendCommand");
mp3AHal->sendCommand(eCmd);
CAM_TRACE_END();
// (4) EIS
//
CPTLogStr(Event_Hal_Adapter_MtkDefaultPreview_start_init,CPTFlagSeparator, "Init EIS");
mpEisHal = EisHalBase::createInstance("mtkdefaultAdapter");
if(mpEisHal != NULL)
{
eisHal_config_t eisHalConfig;
eisHalConfig.imageWidth = mSensorInfo.getImgWidth();
eisHalConfig.imageHeight = mSensorInfo.getImgHeight();
CAM_TRACE_BEGIN("EIS::configEIS");
mpEisHal->configEIS(
eHW_VSS,
eisHalConfig);
CAM_TRACE_END();
}
else
{
MY_LOGE("mpEisHal is NULL");
goto lbExit;
}
//
#if VSS_ENABLE
mpVideoSnapshotScenario = IVideoSnapshotScenario::createInstance();
#endif
// (5) hw start
//!!enable pass1 SHOULD BE last step!!
CPTLogStr(Event_Hal_Adapter_MtkDefaultPreview_start_init,CPTFlagSeparator, "Hw start");
//
if ( ! getHw()->start())
{
goto lbExit;
}
//
ret = true;
lbExit:
//
FUNCTION_OUT;
//
return ret;
}
/*
Start和update获取到了sensor的数据,然后交给ISP进行了3A,EIS等的处理,然后向外发出来。对于如何获取的sensor数据,ISP又是如何处理的,这里还是不明白。待学习。
最后是通过mspPreviewBufHandler->enqueBuffer(dispNode)向外传递BUFF的。我想如果有办法把各个阶段的BUFF保存起来看看,应该很不错。单不知道如何实现。
*/
官方有个数据流程图,供参考:
Step 4.2.6:
\vendor\mediatek\proprietary\hardware\mtkcam\legacy\platform\mt8127\v1\hal\adapter\MtkDefault\Preview\PreviewBufMgr.cpp
bool
PreviewBufMgr::
enqueBuffer(ImgBufQueNode const& node)
{
// (1) set DONE tag into package
const_cast
// (2) choose the correct "client"
switch (node.getCookieDE())
{
case eBuf_Pass1:
{
if (mspHwBufPvdr != 0)
{
mspHwBufPvdr->enque(node.getImgBuf());
}
}
break;
//
case eBuf_Disp:
{
sp
if (bufProvider != 0)
{
bufProvider->enqueProvider(node);
}
}
break;
//
case eBuf_AP:
{
sp
{
bufProvider =mspImgBufProvidersMgr->getPrvCBPvdr();
if ( bufProvider != 0 )
{
const_cast
bufProvider->enqueProvider(node);
}
// If fd exists, copy to it
bufProvider =mspImgBufProvidersMgr->getFDBufPvdr();
ImgBufQueNode FDnode;
if (bufProvider != 0 &&bufProvider->dequeProvider(FDnode))
{
if (FDnode.getImgBuf()->getBufSize() >= node.getImgBuf()->getBufSize())
{
memcpy(FDnode.getImgBuf()->getVirAddr(),
node.getImgBuf()->getVirAddr(),
node.getImgBuf()->getBufSize());
const_cast
}
else
{
MY_LOGE("fd buffer size < apbuffer size");
const_cast
}
//
bufProvider->enqueProvider(FDnode);
}
}
}
break;
//
case eBuf_FD:
{
sp
if (bufProvider != 0)
{
bufProvider->enqueProvider(node);
}
}
break;
//
case eBuf_Rec:
{
sp
if (bufProvider != 0)
{
bufProvider->enqueProvider(node);
}
}
break;
//
default:
MY_LOGE("unknown port(%d)!!", node.getCookieDE());
break;
}
return true;
}
/*
到这里为止,数据已经获取到了,放到了这个bufProvider= mspImgBufProvidersMgr里面,上面是如何传递和使用的,待下篇分析。
*/