JNICameraContext::copyAndPost()

  Android Camera小系統:

 

 

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

      註釋:①V4L2(video 4 linux 2)

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


preview數據的顯示過程:

Java app 呼叫  Jni ,Jni調用各種.so :

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

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

 

    

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層獲得preview的buffer,將其設置給Surface去顯示preview的結果。

 

     

Preview數據可以通過Overlay和Surface兩種介質去顯示

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 的結構.如:

1

2

3

4

5

6

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

其將mHardware的preview 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,那麼真的實屬下等了…… 

  1. void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)   
  2. {   
  3.     jbyteArray obj = NULL;   
  4.   
  5.     // allocate Java byte array and copy data   
  6.     if (dataPtr != NULL) {   
  7.         ssize_t offset;   
  8.         size_t size;   
  9.         sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);   
  10.         LOGV("postData: off=%d, size=%d", offset, size);   
  11.         uint8_t *heapBase = (uint8_t*)heap->base();   
  12.   
  13.         if (heapBase != NULL) {   
  14.             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);   
  15.   
  16.             if (!mManualBufferMode) {   
  17.                 LOGV("Allocating callback buffer");   
  18.                 obj = env->NewByteArray(size);   
  19.             } else {   
  20.                 // Vector access should be protected by lock in postData()   
  21.                 if(!mCallbackBuffers.isEmpty()) {   
  22.                     LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());   
  23.                     jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);   
  24.                     mCallbackBuffers.removeAt(0);   
  25.   
  26.                     obj = (jbyteArray)env->NewLocalRef(globalBuffer);   
  27.                     env->DeleteGlobalRef(globalBuffer);   
  28.   
  29.                     if (obj != NULL) {   
  30.                         jsize bufferLength = env->GetArrayLength(obj);   
  31.                         if ((int)bufferLength < (int)size) {   
  32.                             LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",   
  33.                                  size, bufferLength);   
  34.                             env->DeleteLocalRef(obj);   
  35.                             return;   
  36.                         }   
  37.                     }   
  38.                 }   
  39.   
  40.                 if(mCallbackBuffers.isEmpty()) {   
  41.                     LOGV("Out of buffers, clearing callback!");   
  42.                     mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);   
  43.                     mManualCameraCallbackSet = false;   
  44.   
  45.                     if (obj == NULL) {   
  46.                         return;   
  47.                     }   
  48.                 }   
  49.             }   
  50.   
  51.             if (obj == NULL) {   
  52.                 LOGE("Couldn't allocate byte array for JPEG data");   
  53.                 env->ExceptionClear();   
  54.             } else {   
  55.                 env->SetByteArrayRegion(obj, 0, size, data);   
  56.             }   
  57.         } else {   
  58.             LOGE("image heap is NULL");   
  59.         }   
  60.     }   
  61.   
  62.     // post image data to Java   
  63.     env->CallStaticVoidMethod(mCameraJClass, fields.post_event,   
  64.             mCameraJObjectWeak, msgType, 0, 0, obj);   
  65.     if (obj) {   
  66.         env->DeleteLocalRef(obj);   
  67.     }   
  68. }  
轉載fromhttp://www.linuxidc.com/Linux/2011-06/37908.htm


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章