前沿:

在全新的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_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 = ,
// Signed 32-bit integer (int32_t)
TYPE_INT32 = ,
// 32-bit float (float)
TYPE_FLOAT = ,
// Signed 64-bit integer (int64_t)
TYPE_INT64 = ,
// 64-bit float (double)
TYPE_DOUBLE = ,
// A 64-bit fraction (camera_metadata_rational_t)
TYPE_RATIONAL = ,
// 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。

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

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

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 == -) {
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(, 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;
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 Camera API2中采用CameraMetadata用于从APP到HAL的参数交互的更多相关文章

  1. 浅析Android Camera开发中的三个尺寸和三种变形 (贡献一个自适配Picturesize和Previewsize的工具类)

    转至 (http://blog.csdn.net/yanzi1225627/article/details/17652643) 经常听人问Camera开发中,各种变形问题,今天有空就在此梳理总结下. ...

  2. Android开发 listitem中采用倒计时

    采用单个倒计时控件会紊乱,故采用在activity中倒计时异步刷新通知的方式全局更新数据源  notifyDataSetChanged 效果如下: 大致思路: 若出现刷新倒计时有的显示有的不显示的情况 ...

  3. Android Camera 使用一例,视频聊天app

    视频聊天的应用可以从下面的框图示意.  所以需要从camera获取视频数据(YUV420sp),压缩成H264/MPEG4/H263的包,再传递到对方.接收对方的压缩包,解压出来显示到LCD上. An ...

  4. Android : Camera HAL3的参数传递(CameraMetadata)

    一.camera_metadata简介 Camera API2/HAL3架构下使用了全新的CameraMetadata结构取代了之前的SetParameter/Paramters等操作,实现了Java ...

  5. Android已有的原生Camera框架中加入自己的API的实现方案。

    版权声明:本文为CSDN博主(天才2012)原创文章.未经博主同意不得转载. https://blog.csdn.net/gzzaigcn/article/details/25707389     在 ...

  6. Android Camera MSM HAL

    高通新的camera驱动架构设计发生了一些变化,借用互联网上常用的一种结构,大致的原理如此:将camera的所有功能划分为不同的模块,让模块自己来决定自己的事情(高内聚,低耦合),模块需要有统一的接口 ...

  7. android Camera模块分析

    Android Camera Module Architecture and Bottom layer communication mechanism              ----------- ...

  8. android camera jni调用

    http://www.mamicode.com/info-detail-1002139.html how to compile  library of native camera for androi ...

  9. Android manifest文件中的标签详细介绍

    官方文档 概要 每一个Android应用都应该包含一个manifest文件,即AndroidManifest.xml.它包含了程序运行的一些必备信息,比如:--为Java应用程序指定一个独一无二的名字 ...

随机推荐

  1. SQLServer (2005/2008) 日志清理方法

    --数据库日志名称查询 USE DBNAME GO SELECT file_id, name,* FROM sys.database_files; GO------------------------ ...

  2. FckEditor2.65使用范例源码

    原文发布时间为:2009-10-12 -- 来源于本人的百度文章 [由搬家工具导入] 下载地址:http://www.xmaspx.com/Services/FileAttachment.ashx?A ...

  3. 域名解析系统DNS诊断命令nslookup详解【转】

    转自:http://www.renhaibo.com/archives/29.html Ping指令我们很熟悉了,它是一个检查网络状况的命令,在输入的参数是域名的情况下会通过DNS进行查询,但只能查询 ...

  4. check source code after macro expand

    Some time I'd like check source code after macro expand. We can use -E option to stop after the prep ...

  5. python第三方库安装和卸载

    库的安装与卸载 pip install requests pip uninstall requests 查看安装好的库 pip list   第三方库的各种安装方式如下: 一.包管理器 Python有 ...

  6. C#图片转成流,流转成图片,字节转图片,图片转字节的方法

    图片转成流 Bitmap b = new Bitmap(Server.MapPath(ppath)); Stream ms = new MemoryStream(); b.Save(ms, Syste ...

  7. Scala不使用null 而使用Option,None,Some的好处

    刚接触Scala时就很奇怪, 为什么Java已经有null了,却偏偏还要弄出个None 后来依然我行我素在Scala里使用null, 结果就是经常被NullPointerException折磨得阴魂不 ...

  8. JavaScript变量提升和函数声明预解析

    1.首先理解函数作用域 在JavaScript中,变量的定义并不是以代码块作为作用域的,而是以函数作用作用域的.也就是说,如果变量是在某个函数中定义的,那么它在函数以外的地方是不可见的.而如果该变量是 ...

  9. 某考试 T1 str

    一开始死磕sam,发现根本没法做...... 后来想了想,反正匹配子串的大部分不是sam就是 二分+hash啊,,,于是就想了想二分+hash,发现好像可以做啊! 就是假设我们要让 s1[1] 映射到 ...

  10. Java NIO.2 使用Path接口来监听文件、文件夹变化

    Java7对NIO进行了大的改进,新增了许多功能: 对文件系统的访问提供了全面的支持 提供了基于异步Channel的IO 这些新增的IO功能简称为 NIO.2,依然在java.nio包下. 早期的Ja ...