【Android Camera】之 Preview

Android Camera小系統



嗯……直接看Camera HAL層,它實現是主要的工做, 它通常經過ioctl調用V4L2 command ①從linux kernel中的camera driver①獲得preview數據. 而後交給surface(或overlay)顯示或者保存爲文件.在HAL層須要打開對應的設備文件,並經過ioctrl訪問camera driver. Android經過這個HAL層來保證底層硬件(驅動)改變,只需修改對應的HAL層代碼,FrameWork層與JAVA Ap的都不用改變. java

      註釋:①V4L2(video 4 linux 2) linux

      備註:①這個驅動並非camera自己而是控制camera的主設備,這個camera控制器在linux裏被抽象成爲v4l2層通用,最後由(*attach)鏈接到具體每一個不一樣的camera設備驅動裏。camera=camera控制器+外接的camera sensor,控制器集成在cpu裏,linux下的設備結點就是/dev/video0. android

preview數據的顯示過程:

app

ide

函數

post

優化

Java app 呼叫  Jni Jni調用各類.so  ui

libandroid_runtime.so ---> libcamera_client.so ---> Binder IPC---> libcameraservice.so ---> libcamera.so spa

     註釋:請原諒我用【呼叫】這個動詞,實在想不出更加形象的詞彙了。


      1.打開linux kernel中的camera driver的設備文件,調用CameraHardwareInterface.h 中定義的openCameraHardware(),打開camera driver的設備文件(例如/dev/video0).

     2.CameraHardwareInterface.h 中定義的 setParameters()函數,傳參告訴camera HAL使用哪個硬件攝像頭,以及它工做的參數(size, format等等),並在HAL層分配存儲preview數據的buffers(若是buffers是在linux kernel中的camera driver中分配的,並拿到這些buffers mmap後的地址指針).

     3.若是不使用overlay那設置顯示目標就在libcameraservice.so ,不會進Camera HAL動態庫.並將上一步拿到的preview數據buffers地址註冊到surface若是使用overlay那在libcameraservice.so 中會經過傳進來的Isurface建立Overlay類的實例,而後調用CameraHardwareInterface.h 中定義的 setOverlay()設置到Camera HAL動態庫中.

     4.開始preview調用到CameraHardwareInterface.h 中定義的 startPreview()函數.startPreviewMode會處理preview的顯示介質,若是使用Overlay顯示,會設置相應的Overlay,同時調用mHardware->startPreview()以啓動preview;不然先調用mHardware->startPreview()啓動preview,而後設置buffer:調用函數registerPreviewBuffers(),它會調用mHardware->getPreviewHeap(),從HAL層得到previewbuffer,將其設置給Surface去顯示preview的結果。


Preview數據能夠經過OverlaySurface兩種介質去顯示

1.使用Overlay顯示

     overlay 通常用在 camera preview, 視頻播放等須要高幀率的地方還有可能 UI 界面設計的需求, map 地圖查看軟件需兩層顯示信息. overlay須要硬件與驅動的支持.Overlay 沒有 java 層的 code, 也就沒有 JNI 調用通常都在 native 中使用.

     若是要使用Overlay,底層硬件必須支持Overlay。在CameraService::Client的構造函數中,有相應的判斷。

CameraService::Client::Client(const sp<CameraService>& cameraService,const sp<ICameraClient>& cameraClient, pid_t clientPid){}

若mUseOverlay = mHardware->useOverlay();返回值爲true,則表示硬件支持Overlay;不然只能使用Surface顯示。

 

Android系統中提供了Overlay的接口,其具體實現須要本身作.

  

    關於多層 overlay:例如須要同時支持 overlay1  overlay2.需在overlay hal  overlay_control_device_t 中要添加 overlay1  overlay2 的結構.:

struct overlay_control_context_t {
    struct overlay_control_device_t device;
    /* our private state goes below here */
    struct overlay_t* overlay_video1;//overlay1
    struct overlay_t* overlay_video2;//overlay2
};

每一個 overlay_t 表明一層 overlay, 每層 ovelay 有本身的 handle.可使用自定義參數調用 overlay_control_device_t:: setParameter()來指明. Hal 層具體來實現,經過 Overlay object 來拿到 overlay1  overlay2  buffer 指針.

2 使用Surface顯示

     若是使用Surface,會調用函數registerPreviewBuffers()Surface註冊buffers

ISurface::BufferHeap buffers(w, h, w, h,PIXEL_FORMAT_YCbCr_420_SP,transform,0,mHardware->getPreviewHeap());
status_t ret = mSurface->registerBuffers(buffers);

其將mHardwarepreview heap傳遞給了Surface

關於Previewdata callback:

     上層Java 中 調用setPreviewCallback, 這個方法調用的是android_hardware_Camera_setHasPreviewCallback,最終實現落到 JNICameraContext::copyAndPost()身上。

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);
        LOGV("postData: off=%d, size=%d", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();
 
        if (heapBase != NULL) {
            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
            obj = env->NewByteArray(size);
            if (obj == NULL) {
                LOGE("Couldn't allocate byte array for JPEG data");
                env->ExceptionClear();
            } else {
                env->SetByteArrayRegion(obj, 0, size, data);
            }
        } else {
            LOGE("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);
    }
}


其中 obj = env->NewByteArray(size); 每次都分配ByteArray .

    按frame 480×320 pixels計算 ,意味着  230kb per call ,而後花費大量的時間去釋放這些資源。 爲了優化preview大量數據的回調,有人提出引入:

static Mutex sPostDataLock; // A mutex that synchronizes calls to sCameraPreviewArrayGlobal
static jbyteArray sCameraPreviewArrayGlobal; // Buffer that is reused
static size_t sCameraPreviewArraySize=0; // Size of the buffer (or 0 if the buffer is not yet used)

爲的是隻申請一次空間,每幀重複使用ByteArray。

void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) {
    if (dataPtr != NULL) {
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
        LOGV("postData: off=%d, size=%d", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();
 
        if (heapBase != NULL) {
            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
            //HACK
            if ((sCameraPreviewArraySize==0)
 || (sCameraPreviewArraySize!=size)) {
                if(sCameraPreviewArraySize!=0) env->DeleteGlobalRef(sCameraPreviewArrayGlobal);
                sCameraPreviewArraySize=size;
                jbyteArray mCameraPreviewArray = env->NewByteArray(size);
                sCameraPreviewArrayGlobal=(jbyteArray)env->NewGlobalRef(mCameraPreviewArray);
                env->DeleteLocalRef(mCameraPreviewArray);
            }
            if (sCameraPreviewArrayGlobal == NULL) {
                LOGE("Couldn't allocate byte array for JPEG data");
                env->ExceptionClear();
            } else {
                env->SetByteArrayRegion(sCameraPreviewArrayGlobal, 0, size, data);
            }
        } else {
            LOGE("image heap is NULL");
        }
    }
    // post image data to Java
    env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal);
}


我又看了Android 2.3是如何處理的

讓我感嘆,長遠性的策略思考,和實踐中的優化完善多麼重要。若是咱們平時寫程序,僅僅是爲了實現某某功能,解決某某BUG,那麼真的實屬下等了……

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);   
        LOGV("postData: off=%d, size=%d", offset, size);   
        uint8_t *heapBase = (uint8_t*)heap->base();   
  
        if (heapBase != NULL) {   
            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);   
  
            if (!mManualBufferMode) {   
                LOGV("Allocating callback buffer");   
                obj = env->NewByteArray(size);   
            } else {   
                // Vector access should be protected by lock in postData()   
                if(!mCallbackBuffers.isEmpty()) {   
                    LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());   
                    jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);   
                    mCallbackBuffers.removeAt(0);   
  
                    obj = (jbyteArray)env->NewLocalRef(globalBuffer);   
                    env->DeleteGlobalRef(globalBuffer);   
  
                    if (obj != NULL) {   
                        jsize bufferLength = env->GetArrayLength(obj);   
                        if ((int)bufferLength < (int)size) {   
                            LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",   
                                 size, bufferLength);   
                            env->DeleteLocalRef(obj);   
                            return;   
                        }   
                    }   
                }   
  
                if(mCallbackBuffers.isEmpty()) {   
                    LOGV("Out of buffers, clearing callback!");   
                    mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);   
                    mManualCameraCallbackSet = false;   
  
                    if (obj == NULL) {   
                        return;   
                    }   
                }   
            }   
  
            if (obj == NULL) {   
                LOGE("Couldn't allocate byte array for JPEG data");   
                env->ExceptionClear();   
            } else {   
                env->SetByteArrayRegion(obj, 0, size, data);   
            }   
        } else {   
            LOGE("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);   
    }   
}
相關文章
相關標籤/搜索