閱讀48 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android Camera數據流分析全程記錄(overlay方式)

https://blog.chinaunix.net/uid-26215986-id-3573400.html


這裏為什麼要研究overlay方式呢?android camera需要driver和app層需要有大量數據需要傳輸,如果使用非overlay方式進行數據從driver到app層的傳輸,使係統性能受到很到影響,使係統速度變慢,同時會影響功耗等,而在camera preview module時,通常我們是不必要將采集的數據保存下來的,而不像錄像module下,需要將數據保存下來,所以overlay方式就是不經過數據回傳,直接顯示從driver的數據方式,采用這種方式app從無法獲取到數據,所以這種方式應用在preview方式下

這裏我是針對android4.0版本的,相對android2.x版本的overlay已經發生了很大的變化,想要研究這方麵的可以自己去了解一下,這裏不再多說了

開始部分我就直接在這裏帶過了,係統初始打開camera時,調用到app的onCreate方法,這裏主要做了一下工作:
1.開始一個openCamera線程打開camera
2.實例化很多的對象,用於camera工作使用
3.實例化surfaceview和surfaceholder,並且填充了其中的surfacechanged,surfacedestoryed和surfacecreated這三個方式
4.開始一個preview線程用於preview過程
這其中3.4是我們這裏要關注的重點,上麵實例化了這個surfaceview將決定了我們到底是否使用overlay方式
在這裏第三遍完成之後,係統會自動執行surfacechanged這個方式,每次顯示區域發生改變都會自動調用這個方法,剛開始打開camera時,顯示區域從無到有,因此必要這裏會想調用到surfacechanged方法
我們就還是看看在這裏都做了些什麼事情

  1. public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
  2.         // Make sure we have a surface in the holder before proceeding.
  3.         if (holder.getSurface() == null) {
  4.             Log.d(TAG, "holder.getSurface() == null");
  5.             return;
  6.         }

  7.         Log.v(TAG, "surfaceChanged. w=" + w + ". h=" + h);

  8.         // We need to save the holder for later use, even when the mCameraDevice
  9.         // is null. This could happen if onResume() is invoked after this
  10.         // function.
  11.         mSurfaceHolder = holder;

  12.         // The mCameraDevice will be null if it fails to connect to the camera
  13.         // hardware. In this case we will show a dialog and then finish the
  14.         // activity, so it's OK to ignore it.
  15.         if (mCameraDevice == null) return;

  16.         // Sometimes surfaceChanged is called after onPause or before onResume.
  17.         // Ignore it.
  18.         if (mPausing || isFinishing()) return;

  19.         setSurfaceLayout();

  20.         // Set preview display if the surface is being created. Preview was
  21.         // already started. Also restart the preview if display rotation has
  22.         // changed. Sometimes this happens when the device is held in portrait
  23.         // and camera app is opened. Rotation animation takes some time and
  24.         // display rotation in onCreate may not be what we want.
  25.         if (mCameraState == PREVIEW_STOPPED) {//這裏表示第一次打開camera時,那麼調用startpreview
  26.             startPreview(true);
  27.             startFaceDetection();
  28.         } else {//這裏則表示camera已經打開過程中發生的顯示變化,比如橫屏豎頻轉換,所以zheli隻需要重新設置previewdisplay
  29.             if (Util.getDisplayRotation(this) != mDisplayRotation) {
  30.                 setDisplayOrientation();
  31.             }
  32.             if (holder.isCreating()) {
  33.                 // Set preview display if the surface is being created and preview
  34.                 // was already started. That means preview display was set to null
  35.                 // and we need to set it now.
  36.                 setPreviewDisplay(holder);
  37.             }
  38.         }

  39.         // If first time initialization is not finished, send a message to do
  40.         // it later. We want to finish surfaceChanged as soon as possible to let
  41.         // user see preview first.
  42.         if (!mFirstTimeInitialized) {
  43.             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
  44.         } else {
  45.             initializeSecondTime();
  46.         }

  47.         SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);
  48.         CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
  49.         boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
  50.         int displayRotation = Util.getDisplayRotation(this);
  51.         int displayOrientation = Util.getDisplayOrientation(displayRotation, mCameraId);

  52.         mTouchManager.initialize(preview.getHeight() / 3, preview.getHeight() / 3,
  53.                preview, this, mirror, displayOrientation);

  54.     }
從上麵代碼我們必須知道,在surface發生變化時必須調用setPreviewDisplay,根據之後的學習,在startpreview方式中真正startpreview之前同樣要調用setPreviewDisplay,在setPreviewDisplay的方法中完成了很多初始化,也是在這裏決定是否使用overlay方式的,我們就先看看startpreview這個方法吧
  1. private void startPreview(boolean updateAll) {
  2.         if (mPausing || isFinishing()) return;

  3.         mFocusManager.resetTouchFocus();

  4.         mCameraDevice.setErrorCallback(mErrorCallback);

  5.         // If we're previewing already, stop the preview first (this will blank
  6.         // the screen).
  7.         if (mCameraState != PREVIEW_STOPPED) stopPreview();

  8.         setPreviewDisplay(mSurfaceHolder);
  9.         setDisplayOrientation();

  10.         if (!mSnapshotOnIdle) {
  11.             // If the focus mode is continuous autofocus, call cancelAutoFocus to
  12.             // resume it because it may have been paused by autoFocus call.
  13.             if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
  14.                 mCameraDevice.cancelAutoFocus();
  15.             }
  16.             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
  17.         }

  18.         if ( updateAll ) {
  19.             Log.v(TAG, "Updating all parameters!");
  20.             setCameraParameters(UPDATE_PARAM_INITIALIZE | UPDATE_PARAM_ZOOM | UPDATE_PARAM_PREFERENCE);
  21.         } else {
  22.             setCameraParameters(UPDATE_PARAM_MODE);
  23.         }

  24.         //setCameraParameters(UPDATE_PARAM_ALL);

  25.         // Inform the mainthread to go on the UI initialization.
  26.         if (mCameraPreviewThread != null) {
  27.             synchronized (mCameraPreviewThread) {
  28.                 mCameraPreviewThread.notify();
  29.             }
  30.         }

  31.         try {
  32.             Log.v(TAG, "startPreview");
  33.             mCameraDevice.startPreview();
  34.         } catch (Throwable ex) {
  35.             closeCamera();
  36.             throw new RuntimeException("startPreview failed", ex);
  37.         }

  38.         mZoomState = ZOOM_STOPPED;
  39.         setCameraState(IDLE);
  40.         mFocusManager.onPreviewStarted();
  41.         if ( mTempBracketingEnabled ) {
  42.             mFocusManager.setTempBracketingState(FocusManager.TempBracketingStates.ACTIVE);
  43.         }

  44.         if (mSnapshotOnIdle) {
  45.             mHandler.post(mDoSnapRunnable);
  46.         }
  47.     }
上麵大家看到了,先調用了setPreviewDisplay,最後調用mCameraDevice.startPreview()開始preview
這裏過程如下:app-->frameworks-->JNI-->camera client-->camera service-->hardware interface-->HAL
1.setPreviewDisplay方法調用時在app層最初的傳入的參數是surfaceholder結構
2.到了JNI層setPreviewDisplay方法傳入的參數已經是surface結構了
3.到了camera service層
    sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);
    sp<ANativeWindow> window(surface);
    return setPreviewWindow(binder, window);
    通過上麵的轉換調用同名不同參數的另外一個方法,到這裏調用的參數已經轉變為IBinder和ANativeWindow
4.調用hardware interface的setPreviewWindow(window),這裏隻有一個ANativeWindow類型的參數
5.到了camerahal_module中轉站時又發生了變化
,看看下麵的定義,參數變為preview_stream_ops 這個類型的結構
    int camera_set_preview_window(struct camera_device * device, struct preview_stream_ops *window)
上麵過程參數類型一直在變化,不過從app層一直傳到這裏,其實是對同一個內存地址的傳輸,就像張三換了身衣服,但是他還是張三一樣
現在我們就直接看看HAL層的實現
  1. /**
  2.    @brief Sets ANativeWindow object.

  3.    Preview buffers provided to CameraHal via this object. DisplayAdapter will be interfacing with it
  4.    to render buffers to display.

  5.    @param[in] window The ANativeWindow object created by Surface flinger
  6.    @return NO_ERROR If the ANativeWindow object passes validation criteria
  7.    @todo Define validation criteria for ANativeWindow object. Define error codes for scenarios

  8.  */
  9. status_t CameraHal::setPreviewWindow(struct preview_stream_ops *window)
  10. {
  11.     status_t ret = NO_ERROR;
  12.     CameraAdapter::BuffersDescriptor desc;

  13.     LOG_FUNCTION_NAME;
  14.     mSetPreviewWindowCalled = true;

  15.    ///If the Camera service passes a null window, we destroy existing window and free the DisplayAdapter
  16.     if(!window)//這種情況下,window是null,表示不采用overlay方式,則不需要新建displayadapter
  17.     {
  18.         if(mDisplayAdapter.get() != NULL)
  19.         {
  20.             ///NULL window passed, destroy the display adapter if present
  21.             CAMHAL_LOGD("NULL window passed, destroying display adapter");
  22.             mDisplayAdapter.clear();
  23.             ///@remarks If there was a window previously existing, we usually expect another valid window to be passed by the client
  24.             ///@remarks so, we will wait until it passes a valid window to begin the preview again
  25.             mSetPreviewWindowCalled = false;
  26.         }
  27.         CAMHAL_LOGD("NULL ANativeWindow passed to setPreviewWindow");
  28.         return NO_ERROR;
  29.     }else if(mDisplayAdapter.get() == NULL)//傳入的window不是null,但是還沒有未使用overlay方式創建displayadapter,創建displayadapter
  30.     {
  31.         // Need to create the display adapter since it has not been created
  32.         // Create display adapter
  33.         mDisplayAdapter = new ANativeWindowDisplayAdapter();
  34.         ret = NO_ERROR;
  35.         if(!mDisplayAdapter.get() || ((ret=mDisplayAdapter->initialize())!=NO_ERROR))
  36.         {
  37.             if(ret!=NO_ERROR)
  38.             {
  39.                 mDisplayAdapter.clear();
  40.                 CAMHAL_LOGEA("DisplayAdapter initialize failed");
  41.                 LOG_FUNCTION_NAME_EXIT;
  42.                 return ret;
  43.             }
  44.             else
  45.             {
  46.                 CAMHAL_LOGEA("Couldn't create DisplayAdapter");
  47.                 LOG_FUNCTION_NAME_EXIT;
  48.                 return NO_MEMORY;
  49.             }
  50.         }

  51.         // DisplayAdapter needs to know where to get the CameraFrames from inorder to display
  52.         // Since CameraAdapter is the one that provides the frames, set it as the frame provider for DisplayAdapter
  53.         mDisplayAdapter->setFrameProvider(mCameraAdapter);

  54.         // Any dynamic errors that happen during the camera use case has to be propagated back to the application
  55.         // via CAMERA_MSG_ERROR. AppCallbackNotifier is the class that notifies such errors to the application
  56.         // Set it as the error handler for the DisplayAdapter
  57.         mDisplayAdapter->setErrorHandler(mAppCallbackNotifier.get());

  58.         // Update the display adapter with the new window that is passed from CameraService
  59.         ret = mDisplayAdapter->setPreviewWindow(window);
  60.         if(ret!=NO_ERROR)
  61.             {
  62.             CAMHAL_LOGEB("DisplayAdapter setPreviewWindow returned error %d", ret);
  63.             }

  64.         if(mPreviewStartInProgress)
  65.         {
  66.             CAMHAL_LOGDA("setPreviewWindow called when preview running");
  67.             // Start the preview since the window is now available
  68.             ret = startPreview();
  69.         }
  70.     } else {//傳入的window不是null,並且displaadaper已經創建好,那麼這裏隻需要將新的window與已經創建好的displayadapter關聯即可
  71.         // Update the display adapter with the new window that is passed from CameraService
  72.         ret = mDisplayAdapter->setPreviewWindow(window);
  73.         if ( (NO_ERROR == ret) && previewEnabled() ) {
  74.             restartPreview();
  75.         } else if (ret == ALREADY_EXISTS) {
  76.             // ALREADY_EXISTS should be treated as a noop in this case
  77.             ret = NO_ERROR;
  78.         }
  79.     }
  80.     LOG_FUNCTION_NAME_EXIT;

  81.     return ret;

  82. }
這裏我們重點看看新建displayadapter的過程:
1.實例化一個ANativeWindowDisplayAdapter對象
2.mDisplayAdapter->initialize()
3.mDisplayAdapter->setFrameProvider(mCameraAdapter)//這一步是關鍵,之後會遇到的
4.
mDisplayAdapter->setErrorHandler(mAppCallbackNotifier.get())
5.mDisplayAdapter->setPreviewWindow(window);
做完了上麵這些步驟之後,就是startpreview了
  1. /**
  2.    @brief Start preview mode.

  3.    @param none
  4.    @return NO_ERROR Camera switched to VF mode
  5.    @todo Update function header with the different errors that are possible

  6.  */
  7. status_t CameraHal::startPreview() {
  8.     LOG_FUNCTION_NAME;

  9.     // When tunneling is enabled during VTC, startPreview happens in 2 steps:
  10.     // When the application sends the command CAMERA_CMD_PREVIEW_INITIALIZATION,
  11.     // cameraPreviewInitialization() is called, which in turn causes the CameraAdapter
  12.     // to move from loaded to idle state. And when the application calls startPreview,
  13.     // the CameraAdapter moves from idle to executing state.
  14.     //
  15.     // If the application calls startPreview() without sending the command
  16.     // CAMERA_CMD_PREVIEW_INITIALIZATION, then the function cameraPreviewInitialization()
  17.     // AND startPreview() are executed. In other words, if the application calls
  18.     // startPreview() without sending the command CAMERA_CMD_PREVIEW_INITIALIZATION,
  19.     // then the CameraAdapter moves from loaded to idle to executing state in one shot.
  20.     status_t ret = cameraPreviewInitialization();

  21.     // The flag mPreviewInitializationDone is set to true at the end of the function
  22.     // cameraPreviewInitialization(). Therefore, if everything goes alright, then the
  23.     // flag will be set. Sometimes, the function cameraPreviewInitialization() may
  24.     // return prematurely if all the resources are not available for starting preview.
  25.     // For example, if the preview window is not set, then it would return NO_ERROR.
  26.     // Under such circumstances, one should return from startPreview as well and should
  27.     // not continue execution. That is why, we check the flag and not the return value.
  28.     if (!mPreviewInitializationDone) return ret;

  29.     // Once startPreview is called, there is no need to continue to remember whether
  30.     // the function cameraPreviewInitialization() was called earlier or not. And so
  31.     // the flag mPreviewInitializationDone is reset here. Plus, this preserves the
  32.     // current behavior of startPreview under the circumstances where the application
  33.     // calls startPreview twice or more.
  34.     mPreviewInitializationDone = false;

  35.     ///Enable the display adapter if present, actual overlay enable happens when we post the buffer
  36.     if(mDisplayAdapter.get() != NULL) {
  37.         CAMHAL_LOGDA("Enabling display");
  38.         int width, height;
  39.         mParameters.getPreviewSize(&width, &height);

  40. #if PPM_INSTRUMENTATION || PPM_INSTRUMENTATION_ABS
  41.         ret = mDisplayAdapter->enableDisplay(width, height, &mStartPreview);
  42. #else
  43.         ret = mDisplayAdapter->enableDisplay(width, height, NULL);
  44. #endif

  45.         if ( ret != NO_ERROR ) {
  46.             CAMHAL_LOGEA("Couldn't enable display");

  47.             // FIXME: At this stage mStateSwitchLock is locked and unlock is supposed to be called
  48.             // only from mCameraAdapter->sendCommand(CameraAdapter::CAMERA_START_PREVIEW)
  49.             // below. But this will never happen because of goto error. Thus at next
  50.             // startPreview() call CameraHAL will be deadlocked.
  51.             // Need to revisit mStateSwitch lock, for now just abort the process.
  52.             CAMHAL_ASSERT_X(false,
  53.                 "At this stage mCameraAdapter->mStateSwitchLock is still locked, "
  54.                 "deadlock is guaranteed");

  55.             goto error;
  56.         }

  57.     }

  58.     ///Send START_PREVIEW command to adapter
  59.     CAMHAL_LOGDA("Starting CameraAdapter preview mode");

  60.     ret = mCameraAdapter->sendCommand(CameraAdapter::CAMERA_START_PREVIEW);

  61.     if(ret!=NO_ERROR) {
  62.         CAMHAL_LOGEA("Couldn't start preview w/ CameraAdapter");
  63.         goto error;
  64.     }
  65.     CAMHAL_LOGDA("Started preview");

  66.     mPreviewEnabled = true;
  67.     mPreviewStartInProgress = false;
  68.     return ret;

  69.     error:

  70.         CAMHAL_LOGEA("Performing cleanup after error");

  71.         //Do all the cleanup
  72.         freePreviewBufs();
  73.         mCameraAdapter->sendCommand(CameraAdapter::CAMERA_STOP_PREVIEW);
  74.         if(mDisplayAdapter.get() != NULL) {
  75.             mDisplayAdapter->disableDisplay(false);
  76.         }
  77.         mAppCallbackNotifier->stop();
  78.         mPreviewStartInProgress = false;
  79.         mPreviewEnabled = false;
  80.         LOG_FUNCTION_NAME_EXIT;

  81.         return ret;
  82. }
上麵標出的cameraPreviewInitialization()方法也十分關鍵,之前已經說過,之後如果需要會再做說明
Enable the display adapter if present, actual overlay enable happens when we post the buffer
說明如果display adapter不是null,這裏會enable,overlay方式就啟動了
我們接著往下看,看看driver獲取的數據到底是怎樣處理的,startpreview會通過camerahal-->cameraapapter-->V4Lcameradapter
調用到v4l2層的startpreview,下麵看看他的具體是實現
  1. status_t V4LCameraAdapter::startPreview()
  2. {
  3.     status_t ret = NO_ERROR;

  4.     LOG_FUNCTION_NAME;
  5.     Mutex::Autolock lock(mPreviewBufsLock);

  6.     if(mPreviewing) {
  7.         ret = BAD_VALUE;
  8.         goto EXIT;
  9.     }

  10.     for (int i = 0; i < mPreviewBufferCountQueueable; i++) {

  11.         mVideoInfo->buf.index = i;
  12.         mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  13.         mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;

  14.         ret = v4lIoctl(mCameraHandle, VIDIOC_QBUF, &mVideoInfo->buf);//請求分配內存
  15.         if (ret < 0) {
  16.             CAMHAL_LOGEA("VIDIOC_QBUF Failed");
  17.             goto EXIT;
  18.         }
  19.         nQueued++;
  20.     }

  21.     ret = v4lStartStreaming();

  22.     // Create and start preview thread for receiving buffers from V4L Camera
  23.     if(!mCapturing) {
  24.         mPreviewThread = new PreviewThread(this);//開啟PreviewThread
  25.         CAMHAL_LOGDA("Created preview thread");
  26.     }

  27.     //Update the flag to indicate we are previewing
  28.     mPreviewing = true;
  29.     mCapturing = false;

  30. EXIT:
  31.     LOG_FUNCTION_NAME_EXIT;
  32.     return ret;
  33. }
  1. int V4LCameraAdapter::previewThread()
  2. {
  3.     status_t ret = NO_ERROR;
  4.     int width, height;
  5.     CameraFrame frame;
  6.     void *y_uv[2];
  7.     int index = 0;
  8.     int stride = 4096;
  9.     char *fp = NULL;

  10.     mParams.getPreviewSize(&width, &height);

  11.     if (mPreviewing) {

  12.         fp = this->GetFrame(index);
  13.         if(!fp) {
  14.             ret = BAD_VALUE;
  15.             goto EXIT;
  16.         }
  17.         CameraBuffer *buffer = mPreviewBufs.keyAt(index);//獲取camerabuffer
  18.         CameraFrame *lframe = (CameraFrame *)mFrameQueue.valueFor(buffer);//獲取cameraframe
  19.         if (!lframe) {
  20.             ret = BAD_VALUE;
  21.             goto EXIT;
  22.         }

  23.         debugShowFPS();

  24.         if ( mFrameSubscribers.size() == 0 ) {
  25.             ret = BAD_VALUE;
  26.             goto EXIT;
  27.         }
  28.         y_uv[0] = (void*) lframe->mYuv[0];
  29.         //y_uv[1] = (void*) lframe->mYuv[1];
  30.         //y_uv[1] = (void*) (lframe->mYuv[0] + height*stride);
  31.         convertYUV422ToNV12Tiler ( (unsigned char*)fp, (unsigned char*)y_uv[0], width, height);//convert the data
  32.         CAMHAL_LOGVB("##...index= %d.;camera buffer= 0x%x; y= 0x%x; UV= 0x%x.",index, buffer, y_uv[0], y_uv[1] );

  33. #ifdef SAVE_RAW_FRAMES
  34.         unsigned char* nv12_buff = (unsigned char*) malloc(width*height*3/2);
  35.         //Convert yuv422i to yuv420sp(NV12) & dump the frame to a file
  36.         convertYUV422ToNV12 ( (unsigned char*)fp, nv12_buff, width, height);
  37.         saveFile( nv12_buff, ((width*height)*3/2) );//if you want to save the data,save it
  38.         free (nv12_buff);
  39. #endif
  40.         //填充frame結構,用於數據處理
  41.         frame.mFrameType = CameraFrame::PREVIEW_FRAME_SYNC;
  42.         frame.mBuffer = buffer;
  43.         frame.mLength = width*height*3/2;
  44.         frame.mAlignment = stride;
  45.         frame.mOffset = 0;
  46.         frame.mTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
  47.         frame.mFrameMask = (unsigned int)CameraFrame::PREVIEW_FRAME_SYNC;

  48.         if (mRecording)
  49.         {
  50.             frame.mFrameMask |= (unsigned int)CameraFrame::VIDEO_FRAME_SYNC;
  51.             mFramesWithEncoder++;
  52.         }
  53.     
  54.         //這裏是重點,數據回調,或者使用overlay方式顯示這裏是決定性調用
  55.         ret = setInitFrameRefCount(frame.mBuffer, frame.mFrameMask);
  56.         if (ret != NO_ERROR) {
  57.             CAMHAL_LOGDB("Error in setInitFrameRefCount %d", ret);
  58.         } else {
  59.             ret = sendFrameToSubscribers(&frame);
  60.         }
  61.     }
  62. EXIT:

  63.     return ret;
  64. }
現在就開始看看setInitFrameCount方法都做了些什麼
  1. int BaseCameraAdapter::setInitFrameRefCount(CameraBuffer * buf, unsigned int mask)
  2. {
  3.   int ret = NO_ERROR;
  4.   unsigned int lmask;

  5.   LOG_FUNCTION_NAME;

  6.   if (buf == NULL)
  7.     {
  8.       return -EINVAL;
  9.     }

  10.   for( lmask = 1; lmask < CameraFrame::ALL_FRAMES; lmask <<= 1){
  11.     if( lmask & mask ){
  12.       switch( lmask ){

  13.       case CameraFrame::IMAGE_FRAME:
  14.         {
  15.           setFrameRefCount(buf, CameraFrame::IMAGE_FRAME, (int) mImageSubscribers.size());
  16.         }
  17.         break;
  18.       case CameraFrame::RAW_FRAME:
  19.         {
  20.           setFrameRefCount(buf, CameraFrame::RAW_FRAME, mRawSubscribers.size());
  21.         }
  22.         break;
  23.       case CameraFrame::PREVIEW_FRAME_SYNC:
  24.         {
  25.           setFrameRefCount(buf, CameraFrame::PREVIEW_FRAME_SYNC, mFrameSubscribers.size());//這裏這個mFrameSubscribers對應的key上保存著響應的callback方法
  26.         }
  27.         break;
  28.       case CameraFrame::SNAPSHOT_FRAME:
  29.         {
  30.           setFrameRefCount(buf, CameraFrame::SNAPSHOT_FRAME, mSnapshotSubscribers.size());
  31.         }
  32.         break;
  33.       case CameraFrame::VIDEO_FRAME_SYNC:
  34.         {
  35.           setFrameRefCount(buf,CameraFrame::VIDEO_FRAME_SYNC, mVideoSubscribers.size());
  36.         }
  37.         break;
  38.       case CameraFrame::FRAME_DATA_SYNC:
  39.         {
  40.           setFrameRefCount(buf, CameraFrame::FRAME_DATA_SYNC, mFrameDataSubscribers.size());
  41.         }
  42.         break;
  43.       case CameraFrame::REPROCESS_INPUT_FRAME:
  44.         {
  45.           setFrameRefCount(buf,CameraFrame::REPROCESS_INPUT_FRAME, mVideoInSubscribers.size());
  46.         }
  47.         break;
  48.       default:
  49.         CAMHAL_LOGEB("FRAMETYPE NOT SUPPORTED 0x%x", lmask);
  50.         break;
  51.       }//SWITCH
  52.       mask &= ~lmask;
  53.     }//IF
  54.   }//FOR
  55.   LOG_FUNCTION_NAME_EXIT;
  56.   return ret;
  57. }
上麵我標注的部分通過enableMsgType方法實現mFrameSubscribers.add的,經callback添加到對應的key處,算是實現關聯,
同樣的通過disableMsgType方法實現mFrameSubscribers.removeItem的,具體在哪裏調用enableMsgType和disableMsgType之後再給予說明
  1. void BaseCameraAdapter::setFrameRefCount(CameraBuffer * frameBuf, CameraFrame::FrameType frameType, int refCount)
  2. {

  3.     LOG_FUNCTION_NAME;

  4.     switch ( frameType )
  5.         {
  6.         case CameraFrame::IMAGE_FRAME:
  7.         case CameraFrame::RAW_FRAME:
  8.                 {
  9.                 Mutex::Autolock lock(mCaptureBufferLock);
  10.                 mCaptureBuffersAvailable.replaceValueFor(frameBuf, refCount);
  11.                 }
  12.             break;
  13.         case CameraFrame::SNAPSHOT_FRAME:
  14.                 {
  15.                 Mutex::Autolock lock(mSnapshotBufferLock);
  16.                 mSnapshotBuffersAvailable.replaceValueFor( ( unsigned int ) frameBuf, refCount);
  17.                 }
  18.             break;
  19.         case CameraFrame::PREVIEW_FRAME_SYNC:
  20.                 {
  21.                 Mutex::Autolock lock(mPreviewBufferLock)
  22.                 mPreviewBuffersAvailable.replaceValueFor(frameBuf, refCount);//這裏我的理解是refCount和frameBuf實現了綁定,即camerabuf保存在mPreviewBuffersAvailable對應的key處
  23.                 }
  24.             break;
  25.         case CameraFrame::FRAME_DATA_SYNC:
  26.                 {
  27.          

    最後更新:2017-04-03 18:52:05

      上一篇:go Android 動態添加控件
      下一篇:go Java中的HashMap和ConcurrentHashMap的並發性能測試