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

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,這裡其實是我看這個初始化的重點,當然還有後面的一個sendCommand         mCameraAdapter->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 DISABLE  event

  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到底實現什麼功能呢

先到這裡瞭,以後再詳細說瞭

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。