Media Module之Camera(四) 拍照 底層分析

下面主要介紹拍照流程的底層實現(xiàn)。


流程圖
流程圖2
狀態(tài)機
狀態(tài)機2

當(dāng)指定了Camera的預(yù)覽類,并開始預(yù)覽之后,就可以通過takePicture()方法進行拍照了。它將以異步的方式從Camera中獲取圖像,具有多個回調(diào)類作為參數(shù),并且都可以為null,下面分別介紹這些參數(shù)的意義:
? shutter:在按下快門的時候回調(diào),這里可以播放一段聲音。
? raw:從Camera獲取到未經(jīng)處理的圖像。
? postview:從Camera獲取一個快速預(yù)覽的圖片,不是所有設(shè)備都支持。
? jpeg:從Camera獲取到一個經(jīng)過壓縮的jpeg圖片。
  雖然raw、postview、jpeg都是Camera.PictureCallback回調(diào),但是一般我們只需要獲取jpeg,其他傳null即可,Camera.PictureCallback里需要實現(xiàn)一個方法onPictureTaken(byte[] data,Camera camera),data及為圖像數(shù)據(jù)。值得注意的是,一般taskPicture()方法拍照完成之后,SurfaceView都會停留在拍照的瞬間,需要重新調(diào)用startPreview()才會繼續(xù)預(yù)覽。
  如果直接使用taskPicture()進行拍照的話,Camera是不會進行自動對焦的,這里需要使用Camera.autoFocus()方法進行對焦,它傳遞一個Camera.AutoFocusCallback參數(shù),用于自動對焦完成后回調(diào),一般會在它對焦完成在進行taskPicture()拍照。

4.2.1 java framework

首先拍照的流程直接從Camera.java的takePicture開始分析。

public final void takePicture(ShutterCallback shutter, PictureCallback raw,
        PictureCallback postview, PictureCallback jpeg) {
    android.util.SeempLog.record(65);
    mShutterCallback = shutter;
    mRawImageCallback = raw;
    mPostviewCallback = postview;
    mJpegCallback = jpeg;

    // If callback is not set, do not send me callbacks.
    int msgType = 0;
    if (mShutterCallback != null) {
        msgType |= CAMERA_MSG_SHUTTER;
    }
    if (mRawImageCallback != null) {
        msgType |= CAMERA_MSG_RAW_IMAGE;
    }
    if (mPostviewCallback != null) {
        msgType |= CAMERA_MSG_POSTVIEW_FRAME;
    }
    if (mJpegCallback != null) {
        msgType |= CAMERA_MSG_COMPRESSED_IMAGE;
    }

    native_takePicture(msgType);
    mFaceDetectionRunning = false;
}

可以看出,在方法中對各種回調(diào)的值進行了賦值,繼續(xù)看底層對調(diào)函數(shù)的處理。

@Override
public void handleMessage(Message msg) {
    switch(msg.what) {
    //有數(shù)據(jù)到達通知
    case CAMERA_MSG_SHUTTER:
        if (mShutterCallback != null) {
            mShutterCallback.onShutter();
        }
        return;
    //處理未壓縮照片函數(shù)
    case CAMERA_MSG_RAW_IMAGE:
        if (mRawImageCallback != null) {
            mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;
    //處理壓縮處理的照片函數(shù)
    case CAMERA_MSG_COMPRESSED_IMAGE:
        if (mJpegCallback != null) {
            mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;
    ... ...

在應(yīng)用層注冊回調(diào)。
packages\apps\SnapdragonCamera\src\com\android\camera\AndroidCameraManagerImpl.java

@Override
public void takePicture(
        Handler handler,
        CameraShutterCallback shutter,
        CameraPictureCallback raw,
        CameraPictureCallback post,
        CameraPictureCallback jpeg) {
    mCameraHandler.requestTakePicture(
            ShutterCallbackForward.getNewInstance(handler, this, shutter),
            PictureCallbackForward.getNewInstance(handler, this, raw),
            PictureCallbackForward.getNewInstance(handler, this, post),
            PictureCallbackForward.getNewInstance(handler, this, jpeg));
}

在應(yīng)用層實現(xiàn)回調(diào)。

@Override
public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
... ...
if (mRefocus) {
    final String[] NAMES = { "00.jpg", "01.jpg", "02.jpg", "03.jpg",
        "04.jpg", "DepthMapImage.y", "AllFocusImage.jpg" };
    try {
        FileOutputStream out = mActivity.openFileOutput(NAMES[mReceivedSnapNum - 1],
                Context.MODE_PRIVATE);
        out.write(jpegData, 0, jpegData.length);
        out.close();
    } catch (Exception e) {
    }
}
... ...

這里就是真正存儲數(shù)據(jù)的地方了,在android系統(tǒng)有四個地方可以存儲共同數(shù)據(jù)區(qū),
ContentProvider,sharedpreference、file、sqlite這幾種方式,這里利用的是file方式。

4.2.2 JNI

然后調(diào)用到JNI層相應(yīng)方法。

**static void **android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz,
 jint msgType)
 {
     ALOGV("takePicture");
     JNICameraContext* context;
     sp<Camera> camera =
 get_native_camera(env, thiz, &context);
     **if **(camera == 0) **return**;

     /*
      * When CAMERA_MSG_RAW_IMAGE is
 requested, if the raw image callback
      * buffer is available,
 CAMERA_MSG_RAW_IMAGE is enabled to get the
      * notification _and_ the data;
 otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY
      * is enabled to receive the
 callback notification but no data.
      ***
 **     * Note that
 CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the
      * Java application.
      */
     **if **(msgType &
 CAMERA_MSG_RAW_IMAGE) {
         ALOGV("Enable raw image
 callback buffer");
         **if **(!context->isRawImageCallbackBufferAvailable())
 {
             ALOGV("Enable raw
 image notification, since no callback buffer exists");
             msgType &=
 ~CAMERA_MSG_RAW_IMAGE;
             msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;
         }
     }

     **if **(camera->takePicture(msgType)
 != NO_ERROR) {
         jniThrowRuntimeException(env,
 "takePicture failed");
         **return**;
     }
 }

4.2.3 Native層

根據(jù)之前分析的binder機制,Camera.cpp -> ICamera.cpp -> CameraClient.cpp(server端)

// take a picture - image is returned in
 callback
 status_t CameraClient::takePicture(**int **msgType) {
     LOG1("takePicture (pid %d):
 0x%x", getCallingPid(), msgType);

     Mutex::Autolock lock(mLock);
     status_t result =
 checkPidAndHardware();
     **if **(result != NO_ERROR) **return
 **result;

     **if **((msgType &
 CAMERA_MSG_RAW_IMAGE) &&
         (msgType &
 CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
        
 ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
                 " cannot be both
 enabled");
         **return **BAD_VALUE;
     }

     // We only accept picture related
 message types
     // and ignore other types of
 messages for takePicture().
     **int **picMsgType = msgType
                         &
 (CAMERA_MSG_SHUTTER |
                           
 CAMERA_MSG_POSTVIEW_FRAME |
                           
 CAMERA_MSG_RAW_IMAGE |
                           
 CAMERA_MSG_RAW_IMAGE_NOTIFY |
                           
 CAMERA_MSG_COMPRESSED_IMAGE);

     enableMsgType(picMsgType);
     mBurstCnt =
 mHardware->getParameters().getInt("num-snaps-per-shutter");
     **if**(mBurstCnt <= 0)
         mBurstCnt = 1;
     LOG1("mBurstCnt = %d",
 mBurstCnt);

     **return **mHardware->takePicture();
 }

此處的takepicture是在CameraHardwareInterface.h定義的方法。

frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h

status_t takePicture()
 {
     ALOGV("%s(%s)",
 __FUNCTION__, mName.string());
     **if **(mDevice->ops->take_picture)
         **return **mDevice->ops->take_picture(mDevice);
     **return **INVALID_OPERATION;
 }

4.2.4 HAL層

在CameraClient.cpp開始調(diào)用到HAL層中進行處理,接下來主要分析在

hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp得具體實現(xiàn)。

**int **QCamera2HardwareInterface::take_picture(**struct
 **camera_device *device)
 {
     ATRACE_CALL();
     **int **ret = NO_ERROR;
     QCamera2HardwareInterface *hw =
         **reinterpret_cast**<QCamera2HardwareInterface
 *>(device->priv);
     ... ...
     /* Prepare snapshot in case LED
 needs to be flashed */
     **if **(hw->mFlashNeeded == **true
 **|| hw->mParameters.isChromaFlashEnabled()) {
         ret = hw->processAPI(QCAMERA_SM_EVT_PREPARE_SNAPSHOT,
 NULL);
         **if **(ret == NO_ERROR) {
             hw->waitAPIResult(QCAMERA_SM_EVT_PREPARE_SNAPSHOT,
 &apiResult);
             ret = apiResult.status;
         }
         hw->mPrepSnapRun = **true**;
     }

     /* Regardless what the result value
 for prepare_snapshot,
      * go ahead with capture anyway.
 Just like the way autofocus
      * is handled in capture case. */

     /* capture */
     ret = hw->processAPI(QCAMERA_SM_EVT_TAKE_PICTURE,
 NULL);
     **if **(ret == NO_ERROR) {
         hw->waitAPIResult(QCAMERA_SM_EVT_TAKE_PICTURE,
 &apiResult);
         ret = apiResult.status;
     }
     ... ...
 }

在此方法中去更新狀態(tài)機的狀態(tài)。

hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp

**case **QCAMERA_SM_EVT_PREPARE_SNAPSHOT:
     {
         rc =
 m_parent->prepareHardwareForSnapshot(FALSE);
         **if **(rc == NO_ERROR) {
             // Do not signal API result
 in this case.
             // Need to wait for
 snapshot done in metadta.
             m_state =
 QCAMERA_SM_STATE_PREPARE_SNAPSHOT;
         } **else **{
             // Do not change state in
 this case.
             ALOGE("%s:
 prepareHardwareForSnapshot failed %d",
                 __func__, rc);

             result.status = rc;
             result.request_api = evt;
             result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
            
 m_parent->signalAPIResult(&result);
         }
     }
     **break**;

首先調(diào)用回QCamera2HWI.cpp的prepareHardwareForSnapshot方法。

int32_t
 QCamera2HardwareInterface::prepareHardwareForSnapshot(int32_t afNeeded)
 {
     ATRACE_CALL();
     CDBG_HIGH("[KPI Perf] %s:
 Prepare hardware such as LED",__func__);
     **return **mCameraHandle->ops->prepare_snapshot(mCameraHandle->camera_handle,
                                               
 afNeeded);
 }

接著調(diào)用到mm_camera_interface.c的mm_camera_intf_prepare_snapshot方法。

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c

**static **int32_t
 mm_camera_intf_prepare_snapshot(uint32_t camera_handle,
                                               
 int32_t do_af_flag)
 {
     int32_t rc = -1;
     mm_camera_obj_t * my_obj = NULL;

    
 pthread_mutex_lock(&g_intf_lock);
     my_obj =
 mm_camera_util_get_camera_by_handler(camera_handle);

     **if**(my_obj) {
        
 pthread_mutex_lock(&my_obj->cam_lock);
         pthread_mutex_unlock(&g_intf_lock);
         rc =
 mm_camera_prepare_snapshot(my_obj, do_af_flag);
     } **else **{
        
 pthread_mutex_unlock(&g_intf_lock);
     }
     **return **rc;
 }

接著調(diào)用mm_camera.c的mm_camera_prepare_snapshot方法,去與V4L2通信,準備拍照。

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera.c

int32_t
 mm_camera_prepare_snapshot(mm_camera_obj_t *my_obj,
                                   
 int32_t do_af_flag)
 {
     int32_t rc = -1;
     int32_t value = do_af_flag;
     rc =
 mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_PREPARE_SNAPSHOT,
 &value);
    
 pthread_mutex_unlock(&my_obj->cam_lock);
     **return **rc;
 }

當(dāng)?shù)讓訉ε恼諟蕚渫瓿芍?,會調(diào)用到QCamera2HWICallbacks.cpp里,處理metadata

數(shù)據(jù)的方法metadata_stream_cb_routine中。

hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp

**if **(pMetaData->is_prep_snapshot_done_valid)
 {
     qcamera_sm_internal_evt_payload_t
 *payload =
        
 (qcamera_sm_internal_evt_payload_t *)malloc(**sizeof**(qcamera_sm_internal_evt_payload_t));
     **if **(NULL != payload) {
         memset(payload, 0, **sizeof**(qcamera_sm_internal_evt_payload_t));
         payload->evt_type =
 QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE;
         payload->prep_snapshot_state
 = pMetaData->prep_snapshot_done_state;
         int32_t rc =
 pme->processEvt(QCAMERA_SM_EVT_EVT_INTERNAL, payload);
         **if **(rc != NO_ERROR) {
             ALOGE("%s: processEvt
 prep_snapshot failed", __func__);
             free(payload);
             payload = NULL;
         }
     } **else **{
         ALOGE("%s: No memory for
 prep_snapshot qcamera_sm_internal_evt_payload_t", __func__);
     }
 }

此處給狀態(tài)機設(shè)置了新的狀態(tài)

**case **QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE:
     CDBG("%s: Received
 QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE event",
         __func__);
    
 m_parent->processPrepSnapshotDoneEvent(internal_evt->prep_snapshot_state);
     m_state =
 QCAMERA_SM_STATE_PREVIEWING;

     result.status = NO_ERROR;
     result.request_api =
 QCAMERA_SM_EVT_PREPARE_SNAPSHOT;
     result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
    
 m_parent->signalAPIResult(&result);
     **break**;

之后返回到QCamera2HWI.cpp類里的take_picture方法,繼續(xù)下面的操作。

ret =
 hw->processAPI(QCAMERA_SM_EVT_TAKE_PICTURE, NULL);
 **if **(ret == NO_ERROR) {
    
 hw->waitAPIResult(QCAMERA_SM_EVT_TAKE_PICTURE, &apiResult);
     ret = apiResult.status;
 }
此處改變了狀態(tài)機的狀態(tài),QCAMERA_SM_EVT_TAKE_PICTURE

**case **QCAMERA_SM_EVT_TAKE_PICTURE:
    {
        **if **(
 m_parent->mParameters.getRecordingHintValue() == **true**) {
             m_parent->stopPreview();
            
 m_parent->mParameters.updateRecordingHintValue(FALSE);
             // start preview again
             rc = m_parent->preparePreview();
             **if **(rc == NO_ERROR)
 {
                 rc =
 m_parent->startPreview();
                 **if **(rc !=
 NO_ERROR) {
                    
 m_parent->unpreparePreview();
                 }
             }
        }
        **if **(m_parent->isZSLMode()
 || m_parent->isLongshotEnabled()) {
            m_state =
 QCAMERA_SM_STATE_PREVIEW_PIC_TAKING;
            rc =
 m_parent->takePicture();
            **if **(rc != NO_ERROR) {
                // move state to
 previewing state
                m_state = QCAMERA_SM_STATE_PREVIEWING;
            }
        } **else **{
            m_state =
 QCAMERA_SM_STATE_PIC_TAKING;
            rc =
 m_parent->takePicture();
            **if **(rc != NO_ERROR) {
                // move state to preview
 stopped state
                m_state =
 QCAMERA_SM_STATE_PREVIEW_STOPPED;
            }
        }

        result.status = rc;
        result.request_api = evt;
        result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
       
 m_parent->signalAPIResult(&result);
     }
     **break**;

此處又返回到QCamera2HWI.cpp類里的takePicture()方法中。

**int **QCamera2HardwareInterface::takePicture()
 {
 **    ... ...**
             } **else **{
                 // normal capture case
                 // need to stop preview
 channel
                
 stopChannel(QCAMERA_CH_TYPE_PREVIEW);
                
 delChannel(QCAMERA_CH_TYPE_PREVIEW);
                 rc =
 addCaptureChannel();
             }
 **    ... ...**
 }

首先先停止并刪除了preview的channel,然后調(diào)用addCaptureChannel()方法。

int32_t
 QCamera2HardwareInterface::addCaptureChannel()
 {

... ...

rc = pChannel->init(&attr,
                    
 capture_channel_cb_routine,
                     **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_METADATA,
                        
 metadata_stream_cb_routine, **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_POSTVIEW,
                         NULL, **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_SNAPSHOT,
                         NULL, **this**);

... ...

可以看出,在此方法中創(chuàng)建并初始化了channel,并且添加mediadata,postview,snapshot數(shù)據(jù)流到channel中。

返回到hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp的takePicture()方法中,繼續(xù)調(diào)用到開啟capture的channel。

rc = 
 m_channels[QCAMERA_CH_TYPE_CAPTURE]->start();

channel的start方法在之前的preview流程中具體介紹過,在此不做分析。

之后又調(diào)用了QCameraChannel.cpp的startAdvancedCapture方法,然后是mm-camera-interface.c的process_advanced_capture方法,然后是mm-camera.c的mm_camera_channel_advanced_capture方法,這一系列方法設(shè)置了當(dāng)前管道是拍照模式。

4.2.5 jpeg數(shù)據(jù)流

通過mm-jpeg-interface.c處理數(shù)據(jù)流,并且生成jpeg文件,然后在QCamera2HWI.cpp中處理從mm-jpeg-interface.c發(fā)出的jpeg相關(guān)事件。

**void **QCamera2HardwareInterface::jpegEvtHandle(jpeg_job_status_t
 status,
                                              
 uint32_t /*client_hdl*/,
                                              
 uint32_t jobId,
                                              
 mm_jpeg_output_t *p_output,
                                              
 **void ***userdata)
 {
     QCamera2HardwareInterface *obj =
 (QCamera2HardwareInterface *)userdata;
     **if **(obj) {
         qcamera_jpeg_evt_payload_t
 *payload =
             (qcamera_jpeg_evt_payload_t
 *)malloc(**sizeof**(qcamera_jpeg_evt_payload_t));
         **if **(NULL != payload) {
             memset(payload, 0, **sizeof**(qcamera_jpeg_evt_payload_t));
             payload->status =
 status;
             payload->jobId = jobId;
             **if **(p_output !=
 NULL) {
                 payload->out_data =
 *p_output;
             }
            
 obj->processMTFDumps(payload);
            
 obj->processEvt(QCAMERA_SM_EVT_JPEG_EVT_NOTIFY, payload);
         }
     } **else **{
         ALOGE("%s: NULL
 user_data", __func__);
     }
 }

此處更改了狀態(tài)機的狀態(tài)。


**case **QCAMERA_SM_EVT_JPEG_EVT_NOTIFY:
     {
         qcamera_jpeg_evt_payload_t
 *jpeg_job =
             (qcamera_jpeg_evt_payload_t
 *)payload;
         rc =
 m_parent->processJpegNotify(jpeg_job);
     }
     **break**;

然后調(diào)用回QCamera2HWI.cpp的processJpegNotify方法

int32_t
 QCamera2HardwareInterface::processJpegNotify(qcamera_jpeg_evt_payload_t
 *jpeg_evt)
 {
     **return **m_postprocessor.processJpegEvt(jpeg_evt);
 }

此處調(diào)用的是QCameraPostProc.cpp的processJpegEvt方法。

hardware/qcom/camera/QCamera2/HAL/QCameraPostProc.cpp

int32_t
 QCameraPostProcessor::processJpegEvt(qcamera_jpeg_evt_payload_t *evt)
 {
     ... ...
         rc =
 sendDataNotify(CAMERA_MSG_COMPRESSED_IMAGE,
                             jpeg_mem,
                             0,
                             NULL,
                             &release_data);
     ... ...
     return rc;
 }

接著調(diào)用sendDataNotify方法。

int32_t
 QCameraPostProcessor::sendDataNotify(int32_t msg_type,
                                             
 camera_memory_t *data,
                                             
 uint8_t index,
                                             
 camera_frame_metadata_t *metadata,
                                             
 qcamera_release_data_t *release_data)
 {
     qcamera_data_argm_t *data_cb =
 (qcamera_data_argm_t *)malloc(**sizeof**(qcamera_data_argm_t));
     **if **(NULL == data_cb) {
         ALOGE("%s: no mem for
 acamera_data_argm_t", __func__);
         **return **NO_MEMORY;
     }
     memset(data_cb, 0, **sizeof**(qcamera_data_argm_t));
     data_cb->msg_type = msg_type;
     data_cb->data = data;
     data_cb->index = index;
     data_cb->metadata = metadata;
     **if **(release_data != NULL) {
         data_cb->release_data =
 *release_data;
     }

     qcamera_callback_argm_t cbArg;
     memset(&cbArg, 0, **sizeof**(qcamera_callback_argm_t));
     cbArg.cb_type =
 QCAMERA_DATA_SNAPSHOT_CALLBACK;
     cbArg.msg_type = msg_type;
     cbArg.data = data;
     cbArg.metadata = metadata;
     cbArg.user_data = data_cb;
     cbArg.cookie = **this**;
     cbArg.release_cb = releaseNotifyData;
     **int **rc =
 m_parent->m_cbNotifier.notifyCallback(cbArg);
     **if **( NO_ERROR != rc ) {
         ALOGE("%s: Error enqueuing
 jpeg data into notify queue", __func__);
         releaseNotifyData(data_cb, **this**,
 UNKNOWN_ERROR);
         **return **UNKNOWN_ERROR;
    
 }

     **return **rc;
 }

可以看出此處在給回調(diào)的對象裝填數(shù)據(jù),并且發(fā)出通知notifyCallback回調(diào)。并且,類型為CAMERA_MSG_COMPRESSED_IMAGE。

之后的流程與preview的流程相似,將數(shù)據(jù)向上層拋送,通過JNI返回到j(luò)ava層的回調(diào)函數(shù)中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容