編輯:關於Android編程
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 ¶ms, 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到底實現什麼功能呢
先到這裡了,以後再詳細說了
實現原理首先就是自定義個WaveView 繼承View,然後再WaveView 內部實現代碼邏輯: ① 水波就
前言這篇文章是這個系列的開篇,作為移動開發者,開發的應用不免會對網絡進行訪問,雖然現在已經有很多的開源庫幫助我們可以輕而易舉的訪問網絡,但是我們仍要去了解網絡訪問的原理,
本文實例為大家分享了Android app應用實現多語言切換功能,供大家參考,具體內容如下1.添加多語言文件 在不同的 value 文件夾下(例如 value 、valu
Android Handler的使用,在講Handler之前,我們先提個小問題,就是如何讓程序5秒鐘更新一下Title.首先我們看一下習慣了Java編程的人,在不知道Ha