Android Camera 流程學習記錄(五,完結)—— Camera.takePicture() 流程解析

簡介
在前面的幾篇筆記中,我已經把 Camera 控制流的部分梳理得比較清楚了。在 Camera 流程中,還有一個重要的部分,即數據流。
Camera API 1 中,數據流主要是經過函數回調的方式,依照從下往上的方向,逐層 return 到 Applications 中。
因爲數據流的部分相對來講比較簡單,因此我就將其與 Camera 的控制流結合起來,從 takePicture() 方法切入,追蹤一個比較完整的 Camera 流程,這個系列的筆記到這篇也就能夠結束了。
takePicture() flow
1. Open 流程
Camera Open 的流程,在以前的一篇筆記中已經比較詳細地描述了。
在這裏,再關注一下這個流程中,HAL 層的部分。
1.1 CameraHardwareInterface.h
位置:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
setCallback(): 
設置 notify 回調,這用來通知數據已經更新。
設置 data 回調以及 dataTimestamp 回調,對應的是函數指針 mDataCb 與 mDataCvTimestamp 。
注意到,設置 mDevice->ops 對應回調函數時,傳入的不是以前設置的函數指針,而是 __data_cb 這樣的函數。在該文件中,實現了 __data_cb ,將回調函數作了一層封裝。
/** Set the notification and data callbacks */
void setCallbacks(notify_callback notify_cb,
                  data_callback data_cb,
                  data_callback_timestamp data_cb_timestamp,
                  void* user)
{
    mNotifyCb = notify_cb;
    mDataCb = data_cb;
    mDataCbTimestamp = data_cb_timestamp;
    mCbUser = user;java

    ALOGV("%s(%s)", __FUNCTION__, mName.string());android

    if (mDevice->ops->set_callbacks) {
        mDevice->ops->set_callbacks(mDevice,
                               __notify_cb,
                               __data_cb,
                               __data_cb_timestamp,
                               __get_memory,
                               this);
    }
}

__data_cb(): 
對原 callback 函數簡單封裝,附加了一個防止數組越界判斷。
static void __data_cb(int32_t msg_type,
                      const camera_memory_t *data, unsigned int index,
                      camera_frame_metadata_t *metadata,
                      void *user)
{
    ALOGV("%s", __FUNCTION__);
    CameraHardwareInterface *__this =
            static_cast<CameraHardwareInterface *>(user);
    sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
    if (index >= mem->mNumBufs) {
        ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
             index, mem->mNumBufs);
        return;
    }
    __this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);
}

2. 控制流
2.1 Framework
2.1.1 Camera.java
位置:frameworks/base/core/java/android/hardware/Camera.java
takePicture(): 
設置快門回調。
設置各類類型的圖片數據回調。
調用 JNI takePicture 方法。
注意,傳入的參數 msgType 是根據相應 CallBack 是否存在而肯定的,每種 Callback 應該對應一個二進制中的數位(如 1,10,100 中 1 的位置),因而這裏採用 |= 操做給它賦值。
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
        PictureCallback postview, PictureCallback jpeg) {
    mShutterCallback = shutter;
    mRawImageCallback = raw;
    mPostviewCallback = postview;
    mJpegCallback = jpeg;c++

    // 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;
    }api

    native_takePicture(msgType);
    mFaceDetectionRunning = false;
}

2.2 Android Runtime
2.2.1 android_hardware_Camera.cpp
位置:frameworks/base/core/jni/android_hardware_Camera.cpp 
takePicture():
獲取已經打開的 camera 實例,調用其 takePicture() 接口。
注意,在這個函數中,對於 RAW_IMAGE 有一些附加操做: 
若是設置了 RAW 的 callback ,則要檢查上下文中,是否能找到對應 Buffer。
若沒法找到 Buffer ,則將 CAMERA_MSG_RAW_IMAGE 的信息去掉,換成 CAMERA_MSG_RAW_IMAGE_NOTIFY。
替換後,就只會得到 notification 的消息,而沒有對應的圖像數據。
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;
    }
}

2.3 C/C++ Libraries
2.3.1 Camera.cpp
位置:frameworks/av/camera/Camera.cpp
takePicture(): 
獲取一個 ICamera,調用其 takePicture 接口。
這裏直接用 return 的方式調用,比較簡單。
// take a picture
status_t Camera::takePicture(int msgType)
{
    ALOGV("takePicture: 0x%x", msgType);
    sp <::android::hardware::ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->takePicture(msgType);
}

2.3.2 ICamera.cpp
位置:frameworks/av/camera/ICamera.cpp
takePicture(): 
利用 Binder 機制發送相應指令到服務端。
實際調用到的是 CameraClient::takePicture() 函數。
// take a picture - returns an IMemory (ref-counted mmap)
status_t takePicture(int msgType)
{
    ALOGV("takePicture: 0x%x", msgType);
    Parcel data, reply;
    data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
    data.writeInt32(msgType);
    remote()->transact(TAKE_PICTURE, data, &reply);
    status_t ret = reply.readInt32();
    return ret;
}

2.3.3 CameraClient.cpp
位置:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
takePicture(): 
注意,CAMERA_MSG_RAW_IMAGE 指令與 CAMERA_MSG_RAW_IMAGE_NOTIFY 指令不能同時有效,須要進行對應的檢查。
對傳入的指令過濾,只留下與 takePicture() 操做相關的。
調用 CameraHardwareInterface 中的 takePicture() 接口。
// take a picture - image is returned in callback
status_t CameraClient::takePicture(int msgType) {
    LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);app

    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;
    }ide

    // 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);

    return mHardware->takePicture();
}

2.4 HAL
2.4.1 CameraHardwareInterface.h
位置:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
takePicture(): 
經過 mDevice 中設置的函數指針,調用 HAL 層中具體平臺對應的 takePicture 操做的實現邏輯。
接下來就是與具體的平臺相關的流程了,這部份內容對我並不是主要,並且在上一篇筆記中已經有比較深刻的探索,因此在這裏就不繼續向下挖掘了。
控制流程到了 HAL 層後,再向 Linux Drivers 發送控制指令,從而使具體的 Camera 設備執行指令,並獲取數據。
/**
 * Take a picture.
 */
status_t takePicture()
{
    ALOGV("%s(%s)", __FUNCTION__, mName.string());
    if (mDevice->ops->take_picture)
        return mDevice->ops->take_picture(mDevice);
    return INVALID_OPERATION;
}

3. 數據流
因爲數據流是經過 callback 函數實現的,因此探究其流程的時候我是從底層向上層進行分析的。
3.1 HAL
3.1.1 CameraHardwareInterface.h
位置:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
這裏咱們只選擇 dataCallback 相關流程進行分析。
__data_cb(): 
該回調函數是在同文件中實現的 setCallbacks() 函數中設置的。
Camera 設備得到數據後,就會往上傳輸,在 HAL 層中會調用到這個回調函數。
經過函數指針 mDataCb 調用從上一層傳入的回調,從而將數據上傳。
這個 mDataCb 指針對應的,是 CameraClient 類中實現的 dataCallback()。
static void __data_cb(int32_t msg_type,
                      const camera_memory_t *data, unsigned int index,
                      camera_frame_metadata_t *metadata,
                      void *user)
{
    ALOGV("%s", __FUNCTION__);
    CameraHardwareInterface *__this =
            static_cast<CameraHardwareInterface *>(user);
    sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
    if (index >= mem->mNumBufs) {
        ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
             index, mem->mNumBufs);
        return;
    }
    __this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);
}

3.2 C/C++ Libraries
3.2.1 CameraClient.cpp
位置:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
dataCallback(): 
這個回調在該文件實現的 initialize() 函數中設置到 CameraHardwareInterface 中。
啓動這個回調後,就從 Cookie 中獲取已鏈接的客戶端。
根據 msgType,啓動對應的 handle 操做。
選擇其中一個分支的 handle 函數來看。
void CameraClient::dataCallback(int32_t msgType,
        const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
    LOG2("dataCallback(%d)", msgType);

    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
    if (client.get() == nullptr) return;

    if (!client->lockIfMessageWanted(msgType)) return;
    if (dataPtr == 0 && metadata == NULL) {
        ALOGE("Null data returned in data callback");
        client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        return;
    }

    switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
        case CAMERA_MSG_PREVIEW_FRAME:
            client->handlePreviewData(msgType, dataPtr, metadata);
            break;
        case CAMERA_MSG_POSTVIEW_FRAME:
            client->handlePostview(dataPtr);
            break;
        case CAMERA_MSG_RAW_IMAGE:
            client->handleRawPicture(dataPtr);
            break;
        case CAMERA_MSG_COMPRESSED_IMAGE:
            client->handleCompressedPicture(dataPtr);
            break;
        default:
            client->handleGenericData(msgType, dataPtr, metadata);
            break;
    }
}

handleRawPicture(): 
在 open 流程中,connect() 函數調用時,mRemoteCallback 已經設置爲一個客戶端實例,其對應的是 ICameraClient 的強指針。
經過這個實例,這裏基於 Binder 機制來啓動客戶端的 dataCallback。
客戶端的 dataCallback 是實如今 Camera 類中。
// picture callback - raw image ready
void CameraClient::handleRawPicture(const sp<IMemory>& mem) {
    disableMsgType(CAMERA_MSG_RAW_IMAGE);

    ssize_t offset;
    size_t size;
    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

    sp<hardware::ICameraClient> c = mRemoteCallback;
    mLock.unlock();
    if (c != 0) {
        c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);
    }
}

3.2.2 Camera.cpp
位置:frameworks/av/camera/Camera.cpp
dataCallback(): 
調用 CameraListener 的 postData 接口,將數據繼續向上傳輸。
postData 接口的實現是在 android_hardware_Camera.cpp 中。
// callback from camera service when frame or image is ready
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata)
{
    sp<CameraListener> listener;
    {
        Mutex::Autolock _l(mLock);
        listener = mListener;
    }
    if (listener != NULL) {
        listener->postData(msgType, dataPtr, metadata);
    }
}

3.3 Android Runtime
3.3.1 android_hardware_Camera.cpp
位置:frameworks/base/core/jni/android_hardware_Camera.cpp
postData(): 
是 JNICameraContext 類的成員函數,該類繼承了 CameraListener。
首先獲取虛擬機指針。
而後過濾掉 CAMERA_MSG_PREVIEW_METADATA 信息。
進入分支處理。
對於數據傳輸路徑,關鍵是在於 copyAndPost() 函數。
void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr,
                                camera_frame_metadata_t *metadata)
{
    // VM pointer will be NULL if object is released
    Mutex::Autolock _l(mLock);
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (mCameraJObjectWeak == NULL) {
        ALOGW("callback on dead camera object");
        return;
    }

    int32_t dataMsgType = msgType & ~CAMERA_MSG_PREVIEW_METADATA;

    // return data based on callback type
    switch (dataMsgType) {
        case CAMERA_MSG_VIDEO_FRAME:
            // should never happen
            break;

        // For backward-compatibility purpose, if there is no callback
        // buffer for raw image, the callback returns null.
        case CAMERA_MSG_RAW_IMAGE:
            ALOGV("rawCallback");
            if (mRawImageCallbackBuffers.isEmpty()) {
                env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                        mCameraJObjectWeak, dataMsgType, 0, 0, NULL);
            } else {
                copyAndPost(env, dataPtr, dataMsgType);
            }
            break;

        // There is no data.
        case 0:
            break;

        default:
            ALOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());
            copyAndPost(env, dataPtr, dataMsgType);
            break;
    }

    // post frame metadata to Java
    if (metadata && (msgType & CAMERA_MSG_PREVIEW_METADATA)) {
        postMetadata(env, CAMERA_MSG_PREVIEW_METADATA, metadata);
    }
}

copyAndPost(): 
首先確認 Memory 中數據是否存在。
申請 Java 字節數組(jbyteArray, jbyte*),並將 Memory 數據賦予到其中。
重點是這個函數: 
env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, obj);
它的功能是將圖像傳給 Java 端。
經過字段 post_event,在 c++ 中調用 Java 的方法,並傳入對應的參數。
最終調用到 Java 端的 postEventFromNative() 方法。
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{
    jbyteArray obj = NULL;

    // allocate Java byte array and copy data
    if (dataPtr != NULL) {
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
        ALOGV("copyAndPost: off=%zd, size=%zu", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();

        if (heapBase != NULL) {
            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);

            if (msgType == CAMERA_MSG_RAW_IMAGE) {
                obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size);
            } else if (msgType == CAMERA_MSG_PREVIEW_FRAME && mManualBufferMode) {
                obj = getCallbackBuffer(env, &mCallbackBuffers, size);

                if (mCallbackBuffers.isEmpty()) {
                    ALOGV("Out of buffers, clearing callback!");
                    mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
                    mManualCameraCallbackSet = false;

                    if (obj == NULL) {
                        return;
                    }
                }
            } else {
                ALOGV("Allocating callback buffer");
                obj = env->NewByteArray(size);
            }

            if (obj == NULL) {
                ALOGE("Couldn't allocate byte array for JPEG data");
                env->ExceptionClear();
            } else {
                env->SetByteArrayRegion(obj, 0, size, data);
            }
        } else {
            ALOGE("image heap is NULL");
        }
    }

    // post image data to Java
    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
            mCameraJObjectWeak, msgType, 0, 0, obj);
    if (obj) {
        env->DeleteLocalRef(obj);
    }
}

3.4 Framework
3.4.1 Camera.java
位置:frameworks/base/core/java/android/hardware/Camera.java
如下兩個方法都是 EventHandler 的成員,這個類繼承了 Handler 類。
postEventFromNative(): 
首先肯定 Camera 是否已經實例化。
確認後,經過 Camera 的成員 mEventHandler 的 obtainMessage 方法將從 Native 環境中得到的數據封裝成 Message 類的一個實例,而後調用 sendMessage() 方法將數據傳出。
private static void postEventFromNative(Object camera_ref,
                                        int what, int arg1, int arg2, Object obj)
{
    Camera c = (Camera)((WeakReference)camera_ref).get();
    if (c == null)
        return;

    if (c.mEventHandler != null) {
        Message m = c.mEventHandler.obtainMessage(what, arg1, arg2, obj);
        c.mEventHandler.sendMessage(m);
    }
}

handleMessage(): 
sendMessage() 方法傳出的數據會經過這個方法做出處理,從而發送到對應的回調類中。
注意到幾個不一樣的回調類(mRawImageCallback、mJpegCallback 等)中都有 onPictureTaken() 方法,經過調用這個方法,底層傳輸到此的數據最終發送到最上層的 Java 應用中,上層應用經過解析 Message 就能夠獲得拍到的圖像,從而得以進行後續的操做。
我所分析的數據流的流程到此就能夠結束了。
@Override
public void handleMessage(Message msg) {
    switch(msg.what) {
    case CAMERA_MSG_SHUTTER:
        if (mShutterCallback != null) {
            mShutterCallback.onShutter();
        }
        return;

    case CAMERA_MSG_RAW_IMAGE:
        if (mRawImageCallback != null) {
            mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;

    case CAMERA_MSG_COMPRESSED_IMAGE:
        if (mJpegCallback != null) {
            mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;

    case CAMERA_MSG_PREVIEW_FRAME:
        PreviewCallback pCb = mPreviewCallback;
        if (pCb != null) {
            if (mOneShot) {
                // Clear the callback variable before the callback
                // in case the app calls setPreviewCallback from
                // the callback function
                mPreviewCallback = null;
            } else if (!mWithBuffer) {
                // We're faking the camera preview mode to prevent
                // the app from being flooded with preview frames.
                // Set to oneshot mode again.
                setHasPreviewCallback(true, false);
            }
            pCb.onPreviewFrame((byte[])msg.obj, mCamera);
        }
        return;

    case CAMERA_MSG_POSTVIEW_FRAME:
        if (mPostviewCallback != null) {
            mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;

    case CAMERA_MSG_FOCUS:
        AutoFocusCallback cb = null;
        synchronized (mAutoFocusCallbackLock) {
            cb = mAutoFocusCallback;
        }
        if (cb != null) {
            boolean success = msg.arg1 == 0 ? false : true;
            cb.onAutoFocus(success, mCamera);
        }
        return;

    case CAMERA_MSG_ZOOM:
        if (mZoomListener != null) {
            mZoomListener.onZoomChange(msg.arg1, msg.arg2 != 0, mCamera);
        }
        return;

    case CAMERA_MSG_PREVIEW_METADATA:
        if (mFaceListener != null) {
            mFaceListener.onFaceDetection((Face[])msg.obj, mCamera);
        }
        return;

    case CAMERA_MSG_ERROR :
        Log.e(TAG, "Error " + msg.arg1);
        if (mErrorCallback != null) {
            mErrorCallback.onError(msg.arg1, mCamera);
        }
        return;

    case CAMERA_MSG_FOCUS_MOVE:
        if (mAutoFocusMoveCallback != null) {
            mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera);
        }
        return;

    default:
        Log.e(TAG, "Unknown message type " + msg.what);
        return;
    }
}

流程簡圖

小結 在這篇筆記中,咱們從 Camera.takePicture() 方法着手,聯繫以前學習的 Open 流程,將整個 Camera 流程簡單地追蹤了一遍。 無論是控制流仍是數據流,都是要經過五大層次依次執行下一步的。控制流是將命令從頂層流向底層,而數據流則是將底層的數據流向頂層。 若是要自定義一個對數據進行處理的 C++ 功能庫,並將其加入相機中,咱們能夠經過對 HAL 層進行一些修改,將 RAW 圖像流向咱們的處理過程,再將處理後的 RAW 圖像傳回 HAL 層(須要在 HAL 層對 RAW 格式進行一些處理才能把圖像上傳),最後經過正常的回調流程把圖像傳到頂層應用中,就能夠實現咱們的自定義功能了。 至此,對於整個 Camera 的框架,及其運做方式,咱們就已經有了比較清晰的瞭解了。 在 Android 5.0 版本後,Camera 推出了 Camera API 2,它有着全新的流程(但整體架構是不會有大變化的)。接下來我會找空餘的時間去學習學習這個新的東西,到時候再另開一系列的學習筆記吧。  

相關文章
相關標籤/搜索