展会信息港展会大全

Android Camera OMX方式Preview完整过程分析
来源:互联网   发布日期:2015-09-29 10:13:55   浏览:2503次  

导读:Android Camera OMX方式Preview完整过程分析在之前的文章中已经说过OMXCameraAdapter的初始化了,为了更好的了解A9和Ducati的数据交互过程,这里很...

Android Camera OMX方式Preview完整过程分析

在之前的文章中已经说过OMXCameraAdapter的初始化了,为了更好的了解A9和Ducati的数据交互过程,这里很有必要深入研究一下Camera采用OMX方式的Preview过程

这里我们还是从CameraHal开始我们对preview过程的分析吧,因为hal层的preview方法对整个preview过程做了一些很重要的初始化,看看代码吧@brief Start preview mode.

@param none

@todo Update function header with the different errors that are possible

下面调用的这个方法是我们关注的重点,他实现了很多preview开始前的初始化

/**

@param none

@todo Update function header with the different errors that are possible

这里是我添加的注释,这里这个mPreviewStartInProgress表示camera preview是否正在进行,false则表示不在进行,mDisplayPaused表示camera已经开始显示,只是暂时停止了,这两个状态的检查表明这里是第一次调用preview,初次使用要查询camera匹配的分辨率,所以这里查询获得宽和高,同时保持在外面的全局变量中,以备之后使用

if ((mPreviewStartInProgress == false) && (mDisplayPaused == false)){

ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_QUERY_RESOLUTION_PREVIEW,( int ) &frame);

if ( NO_ERROR != ret ){

CAMHAL_LOGEB("Error: CAMERA_QUERY_RESOLUTION_PREVIEW %d", ret);

return ret;

}

///Update the current preview width and height

mPreviewWidth = frame.mWidth;

mPreviewHeight = frame.mHeight;

}

这里我们没有设置preview callback同时也没有使能display adapter,那么我们既没有使用VL4CameraAdapter方式,也没有使用overlay方式,那么OMX方式就是我们唯一的选择了,所以这里让组件进入到Excuting state

///If we don't have the preview callback enabled and display adapter,

if(!mSetPreviewWindowCalled || (mDisplayAdapter.get() == NULL)){

CAMHAL_LOGD("Preview not started. Preview in progress flag set");

mPreviewStartInProgress = true;

ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_SWITCH_TO_EXECUTING);

if ( NO_ERROR != ret ){

CAMHAL_LOGEB("Error: CAMERA_SWITCH_TO_EXECUTING %d", ret);

return ret;

}

return NO_ERROR;

}

这里判断我们使用overlay方式,但是这里其实只是暂停了preview,这里做的工作只是从新开启preview,并且开始preview callback

if( (mDisplayAdapter.get() != NULL) && ( !mPreviewEnabled ) && ( mDisplayPaused ) )

{

CAMHAL_LOGDA("Preview is in paused state");

mDisplayPaused = false;

mPreviewEnabled = true;

if ( NO_ERROR == ret )

{

ret = mDisplayAdapter->pauseDisplay(mDisplayPaused);

if ( NO_ERROR != ret )

{

CAMHAL_LOGEB("Display adapter resume failed %x", ret);

}

}

//restart preview callbacks

if(mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)

{

mAppCallbackNotifier->enableMsgType (CAMERA_MSG_PREVIEW_FRAME);

}

signalEndImageCapture();

return ret;

}

获取到属性中的指定的buffer count

required_buffer_count = atoi(mCameraProperties->get(CameraProperties::REQUIRED_PREVIEW_BUFS));

///Allocate the preview buffers<span color:teal;"="" style="word-wrap: break-word; font-size: 10pt;">

ret = allocPreviewBufs(mPreviewWidth, mPreviewHeight, mParameters.getPreviewFormat(), required_buffer_count,max_queueble_buffers);

if ( NO_ERROR != ret )

{

CAMHAL_LOGEA("Couldn't allocate buffers for Preview");

goto error;

}

这里其实我一直想不清楚这个MeasurementEnable到底是哪个功能的flag,暂认为是测试数据专用回调吧

if ( mMeasurementEnabled )

{

这里先获取分辨率中的长度

ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_QUERY_BUFFER_SIZE_PREVIEW_DATA,

( int ) &frame,

required_buffer_count);

if ( NO_ERROR != ret )

{

return ret;

}

///Allocate the preview data buffers

ret = allocPreviewDataBufs(frame.mLength, required_buffer_count);

if ( NO_ERROR != ret ) {

CAMHAL_LOGEA("Couldn't allocate preview data buffers");

goto error;

}

if ( NO_ERROR == ret )

{

desc.mBuffers = mPreviewDataBuffers;

desc.mOffsets = mPreviewDataOffsets;

desc.mFd = mPreviewDataFd;

desc.mLength = mPreviewDataLength;

desc.mCount = ( size_t ) required_buffer_count;

desc.mMaxQueueable = (size_t) required_buffer_count;

上面通过desc这个变量打包我们的数据,他是BuffersDescriptor类型的变量,也就是buffer属性之类的包,然后调用sendCommand,使用自己申请好的buffer,这里其实是我看这个初始化的重点,当然还有后面的一个sendCommandmCameraAdapter->sendCommand(CameraAdapter::CAMERA_USE_BUFFERS_PREVIEW_DATA,

( int ) &desc);

}

}

///Pass the buffers to Camera Adapter

desc.mBuffers = mPreviewBuffers;

desc.mOffsets = mPreviewOffsets;

desc.mFd = mPreviewFd;

desc.mLength = mPreviewLength;

desc.mCount = ( size_t ) required_buffer_count;

desc.mMaxQueueable = (size_t) max_queueble_buffers;

还有就是这里的这个sendCommand了

ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_USE_BUFFERS_PREVIEW,

( int ) &desc);

if ( NO_ERROR != ret )

{

CAMHAL_LOGEB("Failed to register preview buffers: 0x%x", ret);

freePreviewBufs();

return ret;

}

mAppCallbackNotifier->startPreviewCallbacks(mParameters, mPreviewBuffers, mPreviewOffsets, mPreviewFd, mPreviewLength,required_buffer_count);

///Start the callback notifier

ret = mAppCallbackNotifier->start();

if( ALREADY_EXISTS == ret )

{

//Already running, do nothing

CAMHAL_LOGDA("AppCallbackNotifier already running");

ret = NO_ERROR;

}

else if ( NO_ERROR == ret ) {

CAMHAL_LOGDA("Started AppCallbackNotifier..");

mAppCallbackNotifier->setMeasurements(mMeasurementEnabled);

}

else

{

CAMHAL_LOGDA("Couldn't start AppCallbackNotifier");

goto error;

}

if (ret == NO_ERROR) mPreviewInitializationDone = true;

return ret;

error:

CAMHAL_LOGEA("Performing cleanup after error");

//Do all the cleanup

freePreviewBufs();

mCameraAdapter->sendCommand(CameraAdapter::CAMERA_STOP_PREVIEW);

if(mDisplayAdapter.get() != NULL)

{

mDisplayAdapter->disableDisplay(false);

}

mAppCallbackNotifier->stop();

mPreviewStartInProgress = false;

mPreviewEnabled = false;

LOG_FUNCTION_NAME_EXIT;

return ret;

}

这里我们还是分析一下下面这个方法的实现

这个调用最终调用到BaseCameraAdapter下的sendCommand然后调用到OMXCameraAdapter下的方法switchToExecuting,这个方法的实现在下面

我们看看这个方法的实现

status_t OMXCameraAdapter::doSwitchToExecuting()

{

status_t ret = NO_ERROR;

OMX_ERRORTYPE eError = OMX_ErrorNone;

LOG_FUNCTION_NAME;

if ( (mComponentState == OMX_StateExecuting) || (mComponentState == OMX_StateInvalid) ){

CAMHAL_LOGDA("Already in OMX_Executing state or OMX_StateInvalid state");

mStateSwitchLock.unlock();

return NO_ERROR;

}

if ( 0 != mSwitchToExecSem.Count() ){

CAMHAL_LOGEB("Error mSwitchToExecSem semaphore count %d", mSwitchToExecSem.Count());

goto EXIT;

}

///Register for Preview port DISABLEevent

ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp,

OMX_EventCmdComplete,

OMX_CommandPortDisable,

mCameraAdapterParameters.mPrevPortIndex,

mSwitchToExecSem);

if ( NO_ERROR != ret ){

CAMHAL_LOGEB("Error in registering Port Disable for event %d", ret);

goto EXIT;

}

///Disable Preview Port

eError = OMX_SendCommand(mCameraAdapterParameters.mHandleComp,

OMX_CommandPortDisable,

mCameraAdapterParameters.mPrevPortIndex,

NULL);

ret = mSwitchToExecSem.WaitTimeout(OMX_CMD_TIMEOUT);

if (ret != NO_ERROR){

CAMHAL_LOGEB("Timeout PREVIEW PORT DISABLE %d", ret);

}

CAMHAL_LOGVB("PREV PORT DISABLED %d", ret);

///Register for IDLE state switch event

ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp,

OMX_EventCmdComplete,

OMX_CommandStateSet,

OMX_StateIdle,

mSwitchToExecSem);

if(ret!=NO_ERROR)

{

CAMHAL_LOGEB("Error in IDLE STATE SWITCH %d", ret);

goto EXIT;

}

eError = OMX_SendCommand (mCameraAdapterParameters.mHandleComp ,

OMX_CommandStateSet,

OMX_StateIdle,

NULL);

GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);

ret = mSwitchToExecSem.WaitTimeout(OMX_CMD_TIMEOUT);

if (ret != NO_ERROR){

CAMHAL_LOGEB("Timeout IDLE STATE SWITCH %d", ret);

goto EXIT;

}

mComponentState = OMX_StateIdle;

CAMHAL_LOGVB("OMX_SendCommand(OMX_StateIdle) 0x%x", eError);

///Register for EXECUTING state switch event

ret = RegisterForEvent(mCameraAdapterParameters.mHandleComp,

OMX_EventCmdComplete,

OMX_CommandStateSet,

OMX_StateExecuting,

mSwitchToExecSem);

if(ret!=NO_ERROR)

{

CAMHAL_LOGEB("Error in EXECUTING STATE SWITCH %d", ret);

goto EXIT;

}

eError = OMX_SendCommand (mCameraAdapterParameters.mHandleComp ,

OMX_CommandStateSet,

OMX_StateExecuting,

NULL);

GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);

ret = mSwitchToExecSem.WaitTimeout(OMX_CMD_TIMEOUT);

if (ret != NO_ERROR){

CAMHAL_LOGEB("Timeout EXEC STATE SWITCH %d", ret);

goto EXIT;

}

mComponentState = OMX_StateExecuting;

CAMHAL_LOGVB("OMX_SendCommand(OMX_StateExecuting) 0x%x", eError);

mStateSwitchLock.unlock();

LOG_FUNCTION_NAME_EXIT;

return ret;

EXIT:

CAMHAL_LOGEB("Exiting function %s because of ret %d eError=%x", __FUNCTION__, ret, eError);

performCleanupAfterError();

mStateSwitchLock.unlock();

LOG_FUNCTION_NAME_EXIT;

return (ret | ErrorUtils::omxToAndroidError(eError));

}

上面一连串做了三件事情:1、disable preview port,注册事件处理通知,等待组件返回处理通知2、转换状态到IDLE STATE,注册事件处理通知,等待组件返回处理通知3、转换状态到EXCUTING STATE,注册事件处理通知,等待组件返回处理通知

接下来重点看一下,我们自己申请了buffer,看看怎么通知底层使用我们的buffer而不要从新申请buffer,这个调用最会调用到底层的useBuffer方法,直接看看这个方法的实现吧

status_t OMXCameraAdapter::useBuffers(CameraMode mode, CameraBuffer * bufArr, int num, size_t length, unsigned int queueable)

{

OMX_ERRORTYPE eError = OMX_ErrorNone;

status_t ret = NO_ERROR;

LOG_FUNCTION_NAME;

switch(mode)

{

case CAMERA_PREVIEW:

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mPrevPortIndex].mNumBufs =num;

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mPrevPortIndex].mMaxQueueable = queueable;

ret = UseBuffersPreview(bufArr, num);

break;

case CAMERA_IMAGE_CAPTURE:

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mImagePortIndex].mNumBufs = num;

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mImagePortIndex].mMaxQueueable = queueable;

ret = UseBuffersCapture(bufArr, num);

break;

case CAMERA_VIDEO:

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoPortIndex].mNumBufs =num;

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoPortIndex].mMaxQueueable = queueable;

ret = UseBuffersRawCapture(bufArr, num);

break;

case CAMERA_MEASUREMENT:

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mMeasurementPortIndex].mNumBufs = num;

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mMeasurementPortIndex].mMaxQueueable =queueable;

ret = UseBuffersPreviewData(bufArr, num);

break;

case CAMERA_REPROCESS:

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex].mNumBufs = num;

mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mVideoInPortIndex].mMaxQueueable =queueable;

ret = UseBuffersReprocess(bufArr, num);

break;

}

LOG_FUNCTION_NAME_EXIT;

return ret;

}

这个看看UseBufferPreview这个方法

eError = OMX_SendCommand(mCameraAdapterParameters.mHandleComp,

OMX_CommandPortEnable,

mCameraAdapterParameters.mPrevPortIndex,

NULL);

}

///Configure DOMX to use either gralloc handles or vptrs

status_t AppCallbackNotifier::startPreviewCallbacks(CameraParameters &params, CameraBuffer *buffers, uint32_t *offsets, int fd, size_t length, size_t count)

{

sp<MemoryHeapBase> heap;

sp<MemoryBase> buffer;

unsigned int *bufArr;

int size = 0;

LOG_FUNCTION_NAME;

Mutex::Autolock lock(mLock);

if ( NULL == mFrameProvider )

{

CAMHAL_LOGEA("Trying to start video recording without FrameProvider");

return -EINVAL;

}

if ( mPreviewing )

{

CAMHAL_LOGDA("+Already previewing");

return NO_INIT;

}

int w,h;

///Get preview size

params.getPreviewSize(&w, &h);

// save preview pixel format, size and stride

mPreviewWidth = w;

mPreviewHeight = h;

mPreviewStride = 4096;

mPreviewPixelFormat = getContstantForPixelFormat(params.getPreviewFormat());

size = calculateBufferSize(w, h, mPreviewPixelFormat);

这里根据传入的尺寸信息申请memory,

mPreviewMemory = mRequestMemory(-1, size, AppCallbackNotifier::MAX_BUFFERS, NULL);

if (!mPreviewMemory) {

return NO_MEMORY;

}

for (int i=0; i < AppCallbackNotifier::MAX_BUFFERS; i++) {

mPreviewBuffers[i].type = CAMERA_BUFFER_MEMORY;

mPreviewBuffers[i].opaque = (unsigned char*) mPreviewMemory->data + (i*size);

mPreviewBuffers[i].mapped = mPreviewBuffers[i].opaque;

}

if ( mCameraHal->msgTypeEnabled(CAMERA_MSG_PREVIEW_FRAME ) ) {

mFrameProvider->enableFrameNotification(CameraFrame::PREVIEW_FRAME_SYNC);

}

if ( mCameraHal->msgTypeEnabled(CAMERA_MSG_POSTVIEW_FRAME) ) {

mFrameProvider->enableFrameNotification(CameraFrame::SNAPSHOT_FRAME);

}

mPreviewBufCount = 0;

mPreviewing = true;

LOG_FUNCTION_NAME_EXIT;

return NO_ERROR;

}

到这里startPreview的初始化过程就结束了,下面咱们就进到底层看看OMXCameraAdapter是怎样实现开始preview的

/*========================================================*/

/* @ fn SampleTest_FillBufferDone ::Application callback*/

/*========================================================*/

OMX_ERRORTYPE OMXCameraAdapterFillBufferDone(OMX_IN OMX_HANDLETYPE hComponent,

OMX_IN OMX_PTR pAppData,

OMX_IN OMX_BUFFERHEADERTYPE* pBuffHeader)

{

TIUTILS::Message msg;

OMX_ERRORTYPE eError = OMX_ErrorNone;

if (UNLIKELY(mDebugFps)) {

debugShowFPS();

}

OMXCameraAdapter *adapter =( OMXCameraAdapter * ) pAppData;

if ( NULL != adapter )

{

msg.command = OMXCameraAdapter::OMXCallbackHandler::CAMERA_FILL_BUFFER_DONE;

msg.arg1 = ( void * ) hComponent;

msg.arg2 = ( void * ) pBuffHeader;

adapter->mOMXCallbackHandler->put(&msg);

}

return eError;

}

这里只是打包消息,并发送消息最终是由OMXCallbackHandler中的handle去处理这个消息的

bool OMXCameraAdapter::OMXCallbackHandler::Handler()

{

TIUTILS::Message msg;

volatile int forever = 1;

status_t ret = NO_ERROR;

LOG_FUNCTION_NAME;

while(forever){

TIUTILS::MessageQueue::waitForMsg(&mCommandMsgQ, NULL, NULL, -1);

{检查到消息,接着往下走

Mutex::Autolock lock(mLock);

mCommandMsgQ.get(&msg);

mIsProcessed = false;

}

switch ( msg.command ) {

case OMXCallbackHandler::CAMERA_FILL_BUFFER_DONE:

{

ret = mCameraAdapter->OMXCameraAdapterFillBufferDone(( OMX_HANDLETYPE ) msg.arg1,

( OMX_BUFFERHEADERTYPE *) msg.arg2);

break;

}

case OMXCallbackHandler::CAMERA_FOCUS_STATUS:

{

mCameraAdapter->handleFocusCallback();

break;

}

case CommandHandler::COMMAND_EXIT:

{

CAMHAL_LOGDA("Exiting OMX callback handler");

forever = 0;

break;

}

}

{

android::AutoMutex locker(mLock);

CAMHAL_UNUSED(locker);

mIsProcessed = mCommandMsgQ.isEmpty();

if ( mIsProcessed )

mCondition.signal();

}

}

// force the condition to wake

{

android::AutoMutex locker(mLock);

CAMHAL_UNUSED(locker);

mIsProcessed = true;

mCondition.signal();

}

LOG_FUNCTION_NAME_EXIT;

return false;

}

检查到fillBufferDone消息,调用OMXCameraAdapter下的fillBufferDone处理方法

那么这个returnFrame到底实现什么功能呢

先到这里了,以后再详细说了

赞助本站

人工智能实验室

相关热词: android开发 教程

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港