Android Camera API2中採用CameraMetadata用於從APP到HAL的參數交互

前沿:

在全新的Camera API2架構下,常常會有人疑問再也看不到熟悉的SetParameter/Paramters等相關的身影,取而代之的是一種全新的CameraMetadata結構的出現,他不僅很早就出現在Camera API1/API2結構下的Camera2Device、Camera3Device中用於和HAL3的數據交互,而現在在API2的驅使下都取代了Parameter,實現了Java到native到hal3的參數傳遞。那麼現在假如需要在APP中設置某一項控制參數,對於Camera API2而言,涉及到對Sensor相關參數的set/control時又需要做哪些工作呢?


1. camera_metadata類整體佈局結構

主要涉及到的源文件包括camera_metadata_tags.h,camera_metadata_tag_info.c,CameraMetadata.cpp,camera_metadata.c。對於每個Metadata數據,其通過不同業務控制需求,將整個camera工作需要的參數劃分成多個不同的Section,其中在camera_metadata_tag_info.c表定義了所有Camera需要使用到的Section段的Name:

  1. const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {  
  2.     [ANDROID_COLOR_CORRECTION]     = "android.colorCorrection",  
  3.     [ANDROID_CONTROL]              = "android.control",  
  4.     [ANDROID_DEMOSAIC]             = "android.demosaic",  
  5.     [ANDROID_EDGE]                 = "android.edge",  
  6.     [ANDROID_FLASH]                = "android.flash",  
  7.     [ANDROID_FLASH_INFO]           = "android.flash.info",  
  8.     [ANDROID_GEOMETRIC]            = "android.geometric",  
  9.     [ANDROID_HOT_PIXEL]            = "android.hotPixel",  
  10.     [ANDROID_HOT_PIXEL_INFO]       = "android.hotPixel.info",  
  11.     [ANDROID_JPEG]                 = "android.jpeg",  
  12.     [ANDROID_LENS]                 = "android.lens",  
  13.     [ANDROID_LENS_INFO]            = "android.lens.info",  
  14.     [ANDROID_NOISE_REDUCTION]      = "android.noiseReduction",  
  15.     [ANDROID_QUIRKS]               = "android.quirks",  
  16.     [ANDROID_REQUEST]              = "android.request",  
  17.     [ANDROID_SCALER]               = "android.scaler",  
  18.     [ANDROID_SENSOR]               = "android.sensor",  
  19.     [ANDROID_SENSOR_INFO]          = "android.sensor.info",  
  20.     [ANDROID_SHADING]              = "android.shading",  
  21.     [ANDROID_STATISTICS]           = "android.statistics",  
  22.     [ANDROID_STATISTICS_INFO]      = "android.statistics.info",  
  23.     [ANDROID_TONEMAP]              = "android.tonemap",  
  24.     [ANDROID_LED]                  = "android.led",  
  25.     [ANDROID_INFO]                 = "android.info",  
  26.     [ANDROID_BLACK_LEVEL]          = "android.blackLevel",  
  27. };  

        對於每個Section端而言,其都佔據一個索引區域section_bounds,比如ANDROID_CONTROL Section他所代表的control區域是從ANDROID_CONTROL_START到ANDROID_CONTROL_END之間,且每個Section所擁有的Index範圍理論最大可到(1 << 16)大小,完全可以滿足統一Section下不同的控制參數的維護。

以ANDROID_CONTROL爲列,他的Section index = 1,即對應的section index區間可到(1<<16,2<<16),但一般以實際section中維護的tag的數量來結束,即ANDROID_CONTROL_END決定最終的section index區間。對於每一個section,其下具備不同數量的tag,這個tag是一個指定section下的index值,通過該值來維護一個tag所在的數據區域,此外每個tag都有相應的string  name,在camera_metadata_tag_info.c通過struct tag_info_t來維護一個tag的相關屬性:

  1. typedef struct tag_info {  
  2.     const char *tag_name;  
  3.     uint8_t     tag_type;  
  4. } tag_info_t;  
其中tag_name爲對應section下不同tag的name值 ,tag_type指定了這個tag所維護的數據類型,包括如下:

  1. enum {  
  2.     // Unsigned 8-bit integer (uint8_t)  
  3.     TYPE_BYTE = 0,  
  4.     // Signed 32-bit integer (int32_t)  
  5.     TYPE_INT32 = 1,  
  6.     // 32-bit float (float)  
  7.     TYPE_FLOAT = 2,  
  8.     // Signed 64-bit integer (int64_t)  
  9.     TYPE_INT64 = 3,  
  10.     // 64-bit float (double)  
  11.     TYPE_DOUBLE = 4,  
  12.     // A 64-bit fraction (camera_metadata_rational_t)  
  13.     TYPE_RATIONAL = 5,  
  14.     // Number of type fields  
  15.     NUM_TYPES  
  16. };  
對每一個section所擁有的tag_info信息,通過全局結構體tag_info_t *tag_info[ANDROID_SECTION_COUNT] 來定義。

下圖是對整個Camera Metadata對不同section以及相應section下不同tag的佈局圖,下圖以最常見的Android.control Section爲例進行了描述:





2. CameraMetadata通過camera_metadata來維護數據信息

      假設現在存在一個CameraMetadata對象,那麼他是如何將一個tag標記的參數維護起來的呢?

  1. CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :  
  2.         mLocked(false)  
  3. {  
  4.     mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);  
  5. }  
  1. camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,  
  2.                                             size_t data_capacity) {  
  3.     if (entry_capacity == 0) return NULL;  
  4.   
  5.     size_t memory_needed = calculate_camera_metadata_size(entry_capacity,  
  6.                                                           data_capacity);  
  7.     void *buffer = malloc(memory_needed);  
  8.     return place_camera_metadata(buffer, memory_needed,  
  9.                                  entry_capacity,  
  10.                                  data_capacity);  
  11. }  
一個CameraMetadata數據內存塊中組成的最小基本單元是struct camera_metadata_buffer_entry,總的entry數目等信息需要struct camera_metadata_t來維護
  1. struct camera_metadata {  
  2.     size_t                   size;  
  3.     uint32_t                 version;  
  4.     uint32_t                 flags;  
  5.     size_t                   entry_count;//當前實際的entry數目  
  6.     size_t                   entry_capacity;//entry最大可以存儲的數目  
  7.     uptrdiff_t               entries_start; // Offset from camera_metadata  
  8.     size_t                   data_count;//當前佔據的數據空間  
  9.     size_t                   data_capacity;//最大可操作的數據容量  
  10.     uptrdiff_t               data_start; // Offset from camera_metadata,大容量數據存儲的起始地址  
  11.     void                    *user; // User set pointer, not copied with buffer  
  12.     uint8_t                  reserved[0];  
  13. };  

對於每一個entry主要記錄他的所代表的TAG,以及這個TAG的需要存儲的數據類型,此外還需要記錄這個entry是否是需要一個union offset來表示他當前數據量過大時的數據存儲位置,

  1. typedef struct camera_metadata_buffer_entry {  
  2.     uint32_t tag;//表示當時這個entry代表的tag值,即上文提到的section中不同的tag index值  
  3.     size_t   count;  
  4.     union {  
  5.         size_t  offset;  
  6.         uint8_t value[4];  
  7.     } data;//如果存儲的數據量不大於4則直接存儲。否則需要指點一個offset來表示便宜  
  8.     uint8_t  type;//維護的數據類型  
  9.     uint8_t  reserved[3];  
  10. } camera_metadata_buffer_entry_t;  


3. update更新並建立參數

CameraMetadata支持不同類型的數據更新或者保存到camera_metadata_t中tag所在的entry當中去,以一個更新單字節的數據爲例,data_count指定了數據的個數,而tag指定了要更新的entry。

  1. status_t CameraMetadata::update(uint32_t tag,  
  2.         const uint8_t *data, size_t data_count) {  
  3.     status_t res;  
  4.     if (mLocked) {  
  5.         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);  
  6.         return INVALID_OPERATION;  
  7.     }  
  8.     if ( (res = checkType(tag, TYPE_BYTE)) != OK) {  
  9.         return res;  
  10.     }  
  11.     return updateImpl(tag, (const void*)data, data_count);  
  12. }  
首先是通過checkType,主要是通過tag找到get_camera_metadata_tag_type其所應當支持的tag_type(因爲具體的TAG是已經通過camera_metadata_tag_info.c源文件中的tag_info這個表指定了其應該具備的tag_type),比較兩者是否一致,一致後才允許後續的操作,如這裏需要TYPE_BYTE一致。
  1. const char *get_camera_metadata_tag_name(uint32_t tag) {  
  2.     uint32_t tag_section = tag >> 16;  
  3.     if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {  
  4.         return vendor_tag_ops->get_tag_name(  
  5.             vendor_tag_ops,  
  6.             tag);  
  7.     }  
  8.     if (tag_section >= ANDROID_SECTION_COUNT ||  
  9.         tag >= camera_metadata_section_bounds[tag_section][1] ) {  
  10.         return NULL;  
  11.     }  
  12.     uint32_t tag_index = tag & 0xFFFF;//取tag在section中的index,低16位  
  13.     return tag_info[tag_section][tag_index].tag_name;//定位section然後再說tag  
  14. }  
  15.   
  16. int get_camera_metadata_tag_type(uint32_t tag) {  
  17.     uint32_t tag_section = tag >> 16;  
  18.     if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {  
  19.         return vendor_tag_ops->get_tag_type(  
  20.             vendor_tag_ops,  
  21.             tag);  
  22.     }  
  23.     if (tag_section >= ANDROID_SECTION_COUNT ||  
  24.             tag >= camera_metadata_section_bounds[tag_section][1] ) {  
  25.         return -1;  
  26.     }  
  27.     uint32_t tag_index = tag & 0xFFFF;  
  28.     return tag_info[tag_section][tag_index].tag_type;  
  29. }  
分別是通過tag取貨section id即tag>>16,就定位到所屬的section tag_info_t[],再通過在在該section中定位tag index一般是tag&0xFFFF的低16位爲在該tag在section中的偏移值,進而找到tag自身的struct tag_info_t.

updataImpl函數主要是講所有要寫入的數據進行update操作。

  1. status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,  
  2.         size_t data_count) {  
  3.     status_t res;  
  4.     if (mLocked) {  
  5.         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);  
  6.         return INVALID_OPERATION;  
  7.     }  
  8.     int type = get_camera_metadata_tag_type(tag);  
  9.     if (type == -1) {  
  10.         ALOGE("%s: Tag %d not found", __FUNCTION__, tag);  
  11.         return BAD_VALUE;  
  12.     }  
  13.     size_t data_size = calculate_camera_metadata_entry_data_size(type,  
  14.             data_count);  
  15.   
  16.     res = resizeIfNeeded(1, data_size);//新建camera_metadata_t  
  17.   
  18.     if (res == OK) {  
  19.         camera_metadata_entry_t entry;  
  20.         res = find_camera_metadata_entry(mBuffer, tag, &entry);  
  21.         if (res == NAME_NOT_FOUND) {  
  22.             res = add_camera_metadata_entry(mBuffer,  
  23.                     tag, data, data_count);//將當前新的tag以及數據加入到camera_metadata_t  
  24.         } else if (res == OK) {  
  25.             res = update_camera_metadata_entry(mBuffer,  
  26.                     entry.index, data, data_count, NULL);  
  27.         }  
  28.     }  
  29.   
  30.     if (res != OK) {  
  31.         ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",  
  32.                 __FUNCTION__, get_camera_metadata_section_name(tag),  
  33.                 get_camera_metadata_tag_name(tag), tag, strerror(-res), res);  
  34.     }  
  35.   
  36.     IF_ALOGV() {  
  37.         ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) !=  
  38.                  OK,  
  39.   
  40.                  "%s: Failed to validate metadata structure after update %p",  
  41.                  __FUNCTION__, mBuffer);  
  42.     }  
  43.   
  44.     return res;  
  45. }  
主要分爲以下幾個過程:

a.通過tag_type存儲的數據類型,由calculate_camera_metadata_entry_data_size計算要寫入的entry中的數據量。

b. resizeIfNeeded通過已有entry的數量等,增加entry_capacity,或者重建整個camera_metadata_t,爲後續增加數據創建內存空間基礎。

c. 通過find_camera_metadata_entry獲取一個entry的入口camera_metadata_entry_t,如果存在這個tag對應的entry,則將camera_metadata_buffer_entry_t的屬性信息轉爲camera_metadata_entry_t。

  1. typedef struct camera_metadata_entry {  
  2.     size_t   index;//在當前的entry排序中,其所在的index值  
  3.     uint32_t tag;  
  4.     uint8_t  type;  
  5.     size_t   count;  
  6.     union {  
  7.         uint8_t *u8;  
  8.         int32_t *i32;  
  9.         float   *f;  
  10.         int64_t *i64;  
  11.         double  *d;  
  12.         camera_metadata_rational_t *r;  
  13.     } data;//針對不同數據類型,u8表示數據存儲的入口地址,不大於4字節即爲value[4].  
  14. } camera_metadata_entry_t;  
d .add_camera_metadata_entry完成全新的entry更新與寫入,即這個TAG目前不存在於這個camera_metadata_t中;update_camera_metadata_entry則是直接完成數據的更新。
  1. mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,  
  2.                         CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);  
  3.                 mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,  
  4.                         CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);  
  5.                 session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);  

4. Java層中CameraMetadata.java和CameraMetadataNative.java

下面以API2中java層中設置AF的工作模式爲例,來說明這個參數設置的過程:

  1. mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,  
  2.                         CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);  
  3.                 mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,  
  4.                         CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);  
  5.                 session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);  

其中CONTROL_AF_MODE定義在CaptureRequest,java中如下以一個Key的形式存在:
  1. public static final Key<Integer> CONTROL_AF_MODE =  
  2.         new Key<Integer>("android.control.afMode"int.class);  
  1. public Key(String name, Class<T> type) {  
  2.     mKey = new CameraMetadataNative.Key<T>(name, type);  
  3. }  
在CameraMetadataNative.java中Key的構造
  1. public Key(String name, Class<T> type) {  
  2.            if (name == null) {  
  3.                throw new NullPointerException("Key needs a valid name");  
  4.            } else if (type == null) {  
  5.                throw new NullPointerException("Type needs to be non-null");  
  6.            }  
  7.            mName = name;  
  8.            mType = type;  
  9.            mTypeReference = TypeReference.createSpecializedTypeReference(type);  
  10.            mHash = mName.hashCode() ^ mTypeReference.hashCode();  
  11.        }  


其中CONTROL_AF_MODE_CONTINUOUS_PICTURE定義在CameraMetadata.java中
  1. public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4;  
逐一定位set的入口:

a. mPreviewBuilder是CaptureRequest.java的build類,其會構建一個CaptureRequest

  1. public Builder(CameraMetadataNative template) {  
  2.     mRequest = new CaptureRequest(template);  
  3. }  
  1. private CaptureRequest() {  
  2.     mSettings = new CameraMetadataNative();  
  3.     mSurfaceSet = new HashSet<Surface>();  
  4. }  
mSetting建立的是一個CameraMetadataNative對象,主要用於和Native層進行接口交互,構造如下
  1. public CameraMetadataNative() {  
  2.     super();  
  3.     mMetadataPtr = nativeAllocate();  
  4.     if (mMetadataPtr == 0) {  
  5.         throw new OutOfMemoryError("Failed to allocate native CameraMetadata");  
  6.     }  
  7. }  


b. CaptureRequest.Build.set()
  1. public <T> void set(Key<T> key, T value) {  
  2.     mRequest.mSettings.set(key, value);  
  3. }  
  1. public <T> void set(CaptureRequest.Key<T> key, T value) {  
  2.     set(key.getNativeKey(), value);  
  3. }  
考慮到CaptureRequest extend CameraMetadata,則CaptureRequest.java中getNativeKey
  1. public CameraMetadataNative.Key<T> getNativeKey() {  
  2.     return mKey;  
  3. }  
mKey即爲之前構造的CameraMetadataNative.Key.
  1. public <T> void set(Key<T> key, T value) {  
  2.     SetCommand s = sSetCommandMap.get(key);  
  3.     if (s != null) {  
  4.         s.setValue(this, value);  
  5.         return;  
  6.     }  
  7.     setBase(key, value);  
  8. }  
  1. private <T> void setBase(Key<T> key, T value) {  
  2.     int tag = key.getTag();  
  3.   
  4.     if (value == null) {  
  5.         // Erase the entry  
  6.         writeValues(tag, /*src*/null);  
  7.         return;  
  8.     } // else update the entry to a new value  
  9.   
  10.     Marshaler<T> marshaler = getMarshalerForKey(key);  
  11.     int size = marshaler.calculateMarshalSize(value);  
  12.   
  13.     // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.  
  14.     byte[] values = new byte[size];  
  15.   
  16.     ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());  
  17.     marshaler.marshal(value, buffer);  
  18.   
  19.     writeValues(tag, values);  
  20. }  

首先來看key.getTag()函數的實現,他是將這個key交由Native層後轉爲一個真正的在Java層中的tag值:

  1. public final int getTag() {  
  2.     if (!mHasTag) {  
  3.         mTag = CameraMetadataNative.getTag(mName);  
  4.         mHasTag = true;  
  5.     }  
  6.     return mTag;  
  7. }  
  1. public static int getTag(String key) {  
  2.     return nativeGetTagFromKey(key);  
  3. }  
是將Java層的String交由Native來轉爲一個Java層的tag值。再來看writeValues的實現,同樣調用的是一個native接口,很好的闡明瞭CameraMetadataNative的意思:
  1. public void writeValues(int tag, byte[] src) {  
  2.     nativeWriteValues(tag, src);  
  3. }  

相關native層的實現在下一小節說明。

5. Native層的CameraMetadata結構完成camera參數的傳遞

    在描述萬了CameraMetadata數據的相關操作之後,可明確的一點是SECTION下的TAG是操作他的核心所在。

這裏先說明一個在API1 Camera2Client 參數傳遞的過程,他採用的邏輯是還是在Java層預留了setParameters接口,只是當Parameter在設置時比起CameraClient而言,他是將這個Parameter根據不同的TAG形式直接綁定到CameraMetadata mPreviewRequest/mRecordRequest/mCaptureRequest中,這些數據會由Capture_Request轉爲camera3_capture_request中的camera_metadata_t settings完成參數從Java到native到HAL3的傳遞。

但是在Camera API2下,不再需要那麼複雜的轉換過程,在Java層中直接對參數進行設置並將其封裝到Capture_Request即可,即參數控制由Java層來完成。這也體現了API2中Request和Result在APP中就大量存在的原因。對此爲了和Framework Native層相關TAG數據的統一,在Java層中大量出現的參數設置是通過Section Tag的name來交由Native完成轉換生成在Java層的TAG。

對於第三小節中提到的native層的實現,其對應的實現函數位於android_hardware_camera2_CameraMetadata.c中,如CameraMetadata_getTagFromKey是實現將一個Java層的string轉爲一個tag的值,他的主要原理如下:根據傳入的key string值本質是由一個字符串組成的如上文中提到的android.control.mode。對比最初不同的Section name就可以發現前面兩個x.y的字符串就是代表是Section name.而後面mode即是在該section下的tag數值,所以通過對這個string的分析可知,就可以定位他的section以及tag值。這樣返回到Java層的就是key相應的tag值了。

如果要寫數據,那麼在native同樣需要一個CameraMetadata對象,這裏是在Java構造CameraMetadataNative時實現的,調用的native接口是nativeAllocate():

  1. static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {  
  2.     ALOGV("%s", __FUNCTION__);  
  3.   
  4.     return reinterpret_cast<jlong>(new CameraMetadata());  
  5. }  
最終可以明確的是CameraMetadata相關的參數是被Java層來set/get,但本質是在native層進行了實現,後續如果相關控制參數是被打包到CaptureRequest中時傳入到native時即操作的還是native中的CameraMetadata。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章