zoukankan      html  css  js  c++  java
  • 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_STARTANDROID_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;

        其中,tag_name为对应section下不同tag的name值 ,tag_type指定了这个tag所维护的数据类型,包括如下:

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

        对每一个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标记的参数维护起来的呢?

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

    一个CameraMetadata数据内存块中组成的最小基本单元是struct camera_metadata_buffer_entry,总的entry数目等信息需要struct camera_metadata_t来维护:

    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。

     1 status_t CameraMetadata::update(uint32_t tag, const uint8_t *data, size_t data_count) {
     2     status_t res;
     3     if (mLocked) {
     4         ALOGE(%s: CameraMetadata is locked, __FUNCTION__);
     5         return INVALID_OPERATION;
     6     }
     7     if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
     8         return res;
     9     }
    10     return updateImpl(tag, (const void*)data, data_count);
    11 }

        首先,是通过checkType,主要是通过tag找到get_camera_metadata_tag_type其所应当支持的tag_type(因为具体的TAG是已经通过camera_metadata_tag_info.c源文件中的tag_info这个表指定了其应该具备的tag_type),比较两者是否一致,一致后才允许后续的操作,如这里需要TYPE_BYTE一致。

        updataImpl函数主要是讲所有要写入的数据进行update操作。

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

    主要分为以下几个过程:

    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;
    d .add_camera_metadata_entry完成全新的entry更新与写入,即这个TAG目前不存在于这个camera_metadata_t中;update_camera_metadata_entry则是直接完成数据的更新。

    3. Java层中CameraMetadata.java和CameraMetadataNative.java

    下面以API2中java层中设置AF的工作模式为例,来说明这个参数设置的过程:

    mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    其中CONTROL_AF_MODE定义在CaptureRequest,java中如下以一个Key的形式存在:

    public static final Key CONTROL_AF_MODE = new Key(android.control.afMode, int.class);

    public Key(String name, Class type) {
    mKey = new CameraMetadataNative.Key(name, type);
    }

    在CameraMetadataNative.java中Key的构造

    public Key(String name, Class 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();
    }

    其中CONTROL_AF_MODE_CONTINUOUS_PICTURE定义在CameraMetadata.java中

    public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4;
    逐一定位set的入口:

    a. mPreviewBuilder是CaptureRequest.java的build类,其会构建一个CaptureRequest

    public Builder(CameraMetadataNative template) {
    mRequest = new CaptureRequest(template);
    }

    private CaptureRequest() {
    mSettings = new CameraMetadataNative();
    mSurfaceSet = new HashSet();
    }
    mSetting建立的是一个CameraMetadataNative对象,主要用于和Native层进行接口交互,构造如下

    public CameraMetadataNative() {
    super();
    mMetadataPtr = nativeAllocate();
    if (mMetadataPtr == 0) {
    throw new OutOfMemoryError(Failed to allocate native CameraMetadata);
    }
    }

    b. CaptureRequest.Build.set()

    public void set(Key key, T value) {
    mRequest.mSettings.set(key, value);
    }
    public void set(CaptureRequest.Key key, T value) {
    set(key.getNativeKey(), value);
    }
    考虑到CaptureRequest extend CameraMetadata,则CaptureRequest.java中getNativeKey

    public CameraMetadataNative.Key getNativeKey() {
    return mKey;
    }
    mKey即为之前构造的CameraMetadataNative.Key.

    public void set(Key key, T value) {
    SetCommand s = sSetCommandMap.get(key);
    if (s != null) {
    s.setValue(this, value);
    return;
    }
    setBase(key, value);
    }
    private void setBase(Key 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 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);
    }
    是将Java层的String交由Native来转为一个Java层的tag值。

    再来看writeValues的实现,同样调用的是一个native接口,很好的阐明了CameraMetadataNative的意思:

    public void writeValues(int tag, byte[] src) {
    nativeWriteValues(tag, src);
    }

    相关native层的实现在下一小节说明。

    4. 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(new CameraMetadata());
    }
    最终可以明确的是CameraMetadata相关的参数是被Java层来set/get,但本质是在native层进行了实现,后续如果相关控制参数是被打包到CaptureRequest中时传入到native时即操作的还是native中的CameraMetadata。

    http://www.2cto.com/kf/201510/448174.html

    http://wenku.baidu.com/view/51dc14fd376baf1ffc4fadd3.html?re=view

  • 相关阅读:
    Android中Context具体解释 ---- 你所不知道的Context
    JDK6、Oracle11g、Weblogic10 For Linux64Bit安装部署说明
    matplotlib 可视化 —— 定制 matplotlib
    matplotlib 可视化 —— 移动坐标轴(中心位置)
    matplotlib 可视化 —— 移动坐标轴(中心位置)
    matplotlib 可视化 —— 定制画布风格 Customizing plots with style sheets(plt.style)
    matplotlib 可视化 —— 定制画布风格 Customizing plots with style sheets(plt.style)
    指数函数的研究
    指数函数的研究
    指数分布的研究
  • 原文地址:https://www.cnblogs.com/eustoma/p/5877914.html
Copyright © 2011-2022 走看看