前沿:
在全新的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:
- const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {
- [ANDROID_COLOR_CORRECTION] = "android.colorCorrection",
- [ANDROID_CONTROL] = "android.control",
- [ANDROID_DEMOSAIC] = "android.demosaic",
- [ANDROID_EDGE] = "android.edge",
- [ANDROID_FLASH] = "android.flash",
- [ANDROID_FLASH_INFO] = "android.flash.info",
- [ANDROID_GEOMETRIC] = "android.geometric",
- [ANDROID_HOT_PIXEL] = "android.hotPixel",
- [ANDROID_HOT_PIXEL_INFO] = "android.hotPixel.info",
- [ANDROID_JPEG] = "android.jpeg",
- [ANDROID_LENS] = "android.lens",
- [ANDROID_LENS_INFO] = "android.lens.info",
- [ANDROID_NOISE_REDUCTION] = "android.noiseReduction",
- [ANDROID_QUIRKS] = "android.quirks",
- [ANDROID_REQUEST] = "android.request",
- [ANDROID_SCALER] = "android.scaler",
- [ANDROID_SENSOR] = "android.sensor",
- [ANDROID_SENSOR_INFO] = "android.sensor.info",
- [ANDROID_SHADING] = "android.shading",
- [ANDROID_STATISTICS] = "android.statistics",
- [ANDROID_STATISTICS_INFO] = "android.statistics.info",
- [ANDROID_TONEMAP] = "android.tonemap",
- [ANDROID_LED] = "android.led",
- [ANDROID_INFO] = "android.info",
- [ANDROID_BLACK_LEVEL] = "android.blackLevel",
- };
對於每個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的相關屬性:
- typedef struct tag_info {
- const char *tag_name;
- uint8_t tag_type;
- } tag_info_t;
- enum {
- // Unsigned 8-bit integer (uint8_t)
- TYPE_BYTE = 0,
- // Signed 32-bit integer (int32_t)
- TYPE_INT32 = 1,
- // 32-bit float (float)
- TYPE_FLOAT = 2,
- // Signed 64-bit integer (int64_t)
- TYPE_INT64 = 3,
- // 64-bit float (double)
- TYPE_DOUBLE = 4,
- // A 64-bit fraction (camera_metadata_rational_t)
- TYPE_RATIONAL = 5,
- // Number of type fields
- NUM_TYPES
- };
下圖是對整個Camera Metadata對不同section以及相應section下不同tag的佈局圖,下圖以最常見的Android.control
Section爲例進行了描述:
2. CameraMetadata通過camera_metadata來維護數據信息
假設現在存在一個CameraMetadata對象,那麼他是如何將一個tag標記的參數維護起來的呢?
- CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
- mLocked(false)
- {
- mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
- }
- camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
- size_t data_capacity) {
- if (entry_capacity == 0) return NULL;
- size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
- data_capacity);
- void *buffer = malloc(memory_needed);
- return place_camera_metadata(buffer, memory_needed,
- entry_capacity,
- data_capacity);
- }
- struct camera_metadata {
- size_t size;
- uint32_t version;
- uint32_t flags;
- size_t entry_count;//當前實際的entry數目
- size_t entry_capacity;//entry最大可以存儲的數目
- uptrdiff_t entries_start; // Offset from camera_metadata
- size_t data_count;//當前佔據的數據空間
- size_t data_capacity;//最大可操作的數據容量
- uptrdiff_t data_start; // Offset from camera_metadata,大容量數據存儲的起始地址
- void *user; // User set pointer, not copied with buffer
- uint8_t reserved[0];
- };
對於每一個entry主要記錄他的所代表的TAG,以及這個TAG的需要存儲的數據類型,此外還需要記錄這個entry是否是需要一個union offset來表示他當前數據量過大時的數據存儲位置,
- typedef struct camera_metadata_buffer_entry {
- uint32_t tag;//表示當時這個entry代表的tag值,即上文提到的section中不同的tag index值
- size_t count;
- union {
- size_t offset;
- uint8_t value[4];
- } data;//如果存儲的數據量不大於4則直接存儲。否則需要指點一個offset來表示便宜
- uint8_t type;//維護的數據類型
- uint8_t reserved[3];
- } camera_metadata_buffer_entry_t;
3. update更新並建立參數
CameraMetadata支持不同類型的數據更新或者保存到camera_metadata_t中tag所在的entry當中去,以一個更新單字節的數據爲例,data_count指定了數據的個數,而tag指定了要更新的entry。
- status_t CameraMetadata::update(uint32_t tag,
- const uint8_t *data, size_t data_count) {
- status_t res;
- if (mLocked) {
- ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
- return INVALID_OPERATION;
- }
- if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
- return res;
- }
- return updateImpl(tag, (const void*)data, data_count);
- }
- const char *get_camera_metadata_tag_name(uint32_t tag) {
- uint32_t tag_section = tag >> 16;
- if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
- return vendor_tag_ops->get_tag_name(
- vendor_tag_ops,
- tag);
- }
- if (tag_section >= ANDROID_SECTION_COUNT ||
- tag >= camera_metadata_section_bounds[tag_section][1] ) {
- return NULL;
- }
- uint32_t tag_index = tag & 0xFFFF;//取tag在section中的index,低16位
- return tag_info[tag_section][tag_index].tag_name;//定位section然後再說tag
- }
- int get_camera_metadata_tag_type(uint32_t tag) {
- uint32_t tag_section = tag >> 16;
- if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
- return vendor_tag_ops->get_tag_type(
- vendor_tag_ops,
- tag);
- }
- if (tag_section >= ANDROID_SECTION_COUNT ||
- tag >= camera_metadata_section_bounds[tag_section][1] ) {
- return -1;
- }
- uint32_t tag_index = tag & 0xFFFF;
- return tag_info[tag_section][tag_index].tag_type;
- }
updataImpl函數主要是講所有要寫入的數據進行update操作。
- status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
- size_t data_count) {
- status_t res;
- if (mLocked) {
- ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
- return INVALID_OPERATION;
- }
- int type = get_camera_metadata_tag_type(tag);
- if (type == -1) {
- ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
- return BAD_VALUE;
- }
- size_t data_size = calculate_camera_metadata_entry_data_size(type,
- data_count);
- res = resizeIfNeeded(1, data_size);//新建camera_metadata_t
- if (res == OK) {
- camera_metadata_entry_t entry;
- res = find_camera_metadata_entry(mBuffer, tag, &entry);
- if (res == NAME_NOT_FOUND) {
- res = add_camera_metadata_entry(mBuffer,
- tag, data, data_count);//將當前新的tag以及數據加入到camera_metadata_t
- } else if (res == OK) {
- res = update_camera_metadata_entry(mBuffer,
- entry.index, data, data_count, NULL);
- }
- }
- if (res != OK) {
- ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",
- __FUNCTION__, get_camera_metadata_section_name(tag),
- get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
- }
- IF_ALOGV() {
- ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) !=
- OK,
- "%s: Failed to validate metadata structure after update %p",
- __FUNCTION__, mBuffer);
- }
- return res;
- }
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。
- typedef struct camera_metadata_entry {
- size_t index;//在當前的entry排序中,其所在的index值
- uint32_t tag;
- uint8_t type;
- size_t count;
- union {
- uint8_t *u8;
- int32_t *i32;
- float *f;
- int64_t *i64;
- double *d;
- camera_metadata_rational_t *r;
- } data;//針對不同數據類型,u8表示數據存儲的入口地址,不大於4字節即爲value[4].
- } camera_metadata_entry_t;
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
- session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);
4. Java層中CameraMetadata.java和CameraMetadataNative.java
下面以API2中java層中設置AF的工作模式爲例,來說明這個參數設置的過程:
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
- session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);
其中CONTROL_AF_MODE定義在CaptureRequest,java中如下以一個Key的形式存在:
- public static final Key<Integer> CONTROL_AF_MODE =
- new Key<Integer>("android.control.afMode", int.class);
- public Key(String name, Class<T> type) {
- mKey = new CameraMetadataNative.Key<T>(name, type);
- }
- public Key(String name, Class<T> type) {
- if (name == null) {
- throw new NullPointerException("Key needs a valid name");
- } else if (type == null) {
- throw new NullPointerException("Type needs to be non-null");
- }
- mName = name;
- mType = type;
- mTypeReference = TypeReference.createSpecializedTypeReference(type);
- mHash = mName.hashCode() ^ mTypeReference.hashCode();
- }
- public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4;
a. mPreviewBuilder是CaptureRequest.java的build類,其會構建一個CaptureRequest
- public Builder(CameraMetadataNative template) {
- mRequest = new CaptureRequest(template);
- }
- private CaptureRequest() {
- mSettings = new CameraMetadataNative();
- mSurfaceSet = new HashSet<Surface>();
- }
- public CameraMetadataNative() {
- super();
- mMetadataPtr = nativeAllocate();
- if (mMetadataPtr == 0) {
- throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
- }
- }
- public <T> void set(Key<T> key, T value) {
- mRequest.mSettings.set(key, value);
- }
- public <T> void set(CaptureRequest.Key<T> key, T value) {
- set(key.getNativeKey(), value);
- }
- public CameraMetadataNative.Key<T> getNativeKey() {
- return mKey;
- }
- public <T> void set(Key<T> key, T value) {
- SetCommand s = sSetCommandMap.get(key);
- if (s != null) {
- s.setValue(this, value);
- return;
- }
- setBase(key, value);
- }
- private <T> void setBase(Key<T> key, T value) {
- int tag = key.getTag();
- if (value == null) {
- // Erase the entry
- writeValues(tag, /*src*/null);
- return;
- } // else update the entry to a new value
- Marshaler<T> marshaler = getMarshalerForKey(key);
- int size = marshaler.calculateMarshalSize(value);
- // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
- byte[] values = new byte[size];
- ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
- marshaler.marshal(value, buffer);
- writeValues(tag, values);
- }
首先來看key.getTag()函數的實現,他是將這個key交由Native層後轉爲一個真正的在Java層中的tag值:
- public final int getTag() {
- if (!mHasTag) {
- mTag = CameraMetadataNative.getTag(mName);
- mHasTag = true;
- }
- return mTag;
- }
- public static int getTag(String key) {
- return nativeGetTagFromKey(key);
- }
- public void writeValues(int tag, byte[] src) {
- nativeWriteValues(tag, src);
- }
相關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():
- static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
- ALOGV("%s", __FUNCTION__);
- return reinterpret_cast<jlong>(new CameraMetadata());
- }