前段时间,我在android8.1的系统层上,新增了一个usbcamera hal,和一个虚拟摄像头hal。在实际使用的过程中,遇到了一个问题——客户app在用camera api接口调用usbcamera或virtual camera时,希望能够知道当前调用的是系统本身的mipi摄像头,还是usbcamera或virtualcamera。也就是说,客户想知道,我当前调用的摄像头,是个什么东西。
要实现这个功能,有三种方法。第一种,是最下乘的方法,也就是在hal层,open usbcamera或virtual camera的时候,设置一个属性camera.type值为usb或virtual,在close的时候置为空。在app上可以通过这个属性来判断当前打开的是什么摄像头。这个方法,之所以说是最下乘的,那是因为它不是camera标准流程的东西,不能通过camera的标准接口来判断。并且,当我同时打开了usbcamera或者virtual camera时,这就没法搞了。
第二种,是在hal层,构建cameraInfo的时候,将camera_info->facing的值,设为CAMERA_FACING_EXTERNAL。不过在android8.1上,好像这个值很多地方都没有相应的配置。并且有第一种方法同样的问题,比如我同时打开了usbcamera和virtualcamera,就没法判断各种打开的摄像头是什么类型的了。
第三种,是在hal层,新增一个vendor tag ANDROID_CAMERA_TYPE,然后如下方法去设置它的值:
static const uint8_t cameraType = 1;
cm.update(HalModule::ANDROID_CAMERA_TYPE, &cameraType, 1);
再在app层,就可以通过mCharacteristics.get(CameraCharacteristics.JPEG_ORIENTATION);这样类似的方法去获取它的值了。这种方法最灵活,要使用这种方法,新增一个vendor tag,我们必须要先理解camera 的tag是怎么工作。下面我们先来讲一下它们的工作流程。
在android camera hal3上,从app到hal层的参数传递,都是通过metadata来实现的。在app上如果要设置一个tag是通过下面的代码实现的:
CaptureRequest.Builder builder;
builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
这个CONTROL_MODE定义在CaptureRequest.java
@PublicKey
public static final Key<Integer> CONTROL_MODE =
new Key<Integer>("android.control.mode", int.class);
这个key也定义在这个文件里:
public final static class Key<T> {
private final CameraMetadataNative.Key<T> mKey;
/**
* Visible for testing and vendor extensions only.
*
* @hide
*/
public Key(String name, Class<T> type, long vendorId) {
mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
}
/**
* Visible for testing and vendor extensions only.
*
* @hide
*/
public Key(String name, Class<T> type) {
mKey = new CameraMetadataNative.Key<T>(name, type);
}
......
}
从这里可以看出,app上调用set时传进来的CaptureRequest.CONTROL_MOD,实际上就是一个CameraMetadataNative。然后这个set一步步的跟进去的话,就可以看到它调用是CameraMetadataNative里的set,它定义在frameworks\base\core\java\android\hardware\camera2\impl\CameraMetadataNative.java里。然后又调到了CameraMetadata_writeValues,它定义在frameworks\base\core\jni\android_hardware_camera2_CameraMetadata.cpp里。然后它又调到了updateAny,这里会去调用metadata->update。这个update定义在frameworks\av\camera\CameraMetadata.cpp里,然后会调到updateImpl。
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_local_camera_metadata_tag_type(tag, mBuffer);
if (type == -1) {
ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
return BAD_VALUE;
}
// Safety check - ensure that data isn't pointing to this metadata, since
// that would get invalidated if a resize is needed
size_t bufferSize = get_camera_metadata_size(mBuffer);
uintptr_t bufAddr = reinterpret_cast<uintptr_t>(mBuffer);
uintptr_t dataAddr = reinterpret_cast<uintptr_t>(data);
if (dataAddr > bufAddr && dataAddr < (bufAddr + bufferSize)) {
ALOGE("%s: Update attempted with data from the same metadata buffer!",
__FUNCTION__);
return INVALID_OPERATION;
}
size_t data_size = calculate_camera_metadata_entry_data_size(type,
data_count);
res = resizeIfNeeded(1, data_size);
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);
} 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_local_camera_metadata_section_name(tag, mBuffer),
get_local_camera_metadata_tag_name(tag, mBuffer), 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;
}
这里通过update_camera_metadata_entry,将app上传下来的值,设置到hal里去。然后在hal层,就可以通过CameraMetadata::find(uint32_t tag)来读取设置的值就可以了。
上面说的是app上设置参数,到hal层读取。当然我们也可以在hal层去设置,然后在app层读取。比如
CameraMetadata cm;
static const int32_t jpegOrientation = 0;
cm.update(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
这样就设置好了ANDROID_JPEG_ORIENTATION这个key的值,它定义在system\media\camera\src\camera_metadata_tag_info.c里:
static tag_info_t android_jpeg[ANDROID_JPEG_END -
ANDROID_JPEG_START] = {
......
[ ANDROID_JPEG_ORIENTATION - ANDROID_JPEG_START ] =
{ "orientation", TYPE_INT32 },
......
};
这里的ANDROID_JPEG_START,它的值定义在system\media\camera\include\system\camera_metadata_tags.h里
typedef enum camera_metadata_section_start {
......
ANDROID_JPEG_START = ANDROID_JPEG << 16,
......
} camera_metadata_section_start_t;
ANDROID_JPEG定义在同一个文件里:
typedef enum camera_metadata_section {
......
ANDROID_JPEG,
......
ANDROID_SECTION_COUNT,
VENDOR_SECTION = 0x8000
} camera_metadata_section_t;
然后在system\media\camera\src\camera_metadata_tag_info.c里,声明并定义了一个数组变量:
const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {
......
[ANDROID_JPEG] = "android.jpeg",
......
};
这里的camera_metadata_section_names,就是指jpeg相关的tag的一个集合的名字前缀。比如jpeg相关的tag有很多。有android.jpeg.quality, 对应在CaptureRequest.java里的key为 “public static final Key<Integer> JPEG_ORIENTATION = new Key<Integer>("android.jpeg.orientation", int.class);”对应hal层的名字为ANDROID_JPEG_QUALITY。还比如android.jpeg.gpsTimestamp,它对应在CaptureRequest.java里的key为“public static final Key<Long> JPEG_GPS_TIMESTAMP =new Key<Long>("android.jpeg.gpsTimestamp", long.class);”,对应在hal层的名字为ANDROID_JPEG_GPS_TIMESTAMP。 等等等之类的,只要是跟jpeg相关的tag,都以camera_metadata_section_names这个数组里定义的ANDROID_JPEG为前缀。
再联系到刚刚说的在hal层设置的ANDROID_JPEG_ORIENTATION值,它的全名就是ANDROID_JPEG这个做前缀,加上它自己在static tag_info_t android_jpeg[ANDROID_JPEG_END - ANDROID_JPEG_START]这个数组里的对应位置的值"orientation",加起来就是"android.jpeg.orientation"。
在hal层通过cm.update(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);它来设置这个值后,在app层,可以通过如下的代码取得hal层设置的值:
private Integer mJpegOrientation;
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
mCharacteristics = manager.getCameraCharacteristics(cameraId);
mJpegOrientation = mCharacteristics.get(CameraCharacteristics.JPEG_ORIENTATION);
细心的读者可能发现了,CameraCharacteristics.JPEG_ORIENTATION这个值在CameraCharacteristics.java里并没有定义。没错,是没有定义,但是我们可以在这里自己增加一条就可以了。
@PublicKey
public static final Key<Integer> JPEG_ORIENTATION =
new Key<Integer>("android.jpeg.orientation", int.class);
这么一来,我们从hal层cm.update(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);这样设置,然后从app层通过mCharacteristics.get(CameraCharacteristics.JPEG_ORIENTATION);去获取这条线就走通了。另外在app层通过builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);去设置到hal层,然后在hal层通过cm.find(ANDROID_CONTROL_MODE)获取设置的值这条线也通过了。(CaptureRequest.CONTROL_MODE的值为"android.control.mode",在camera_metadata_tag_info.c里很容易就找到了它对应的section name ——[ANDROID_CONTROL] = "android.control",,然后找到对应的tag ANDROID_CONTROL_MODE的值mode,就和CaptureRequest里的"android.control.mode"对应起来了.)
看到这里,大家应该都明白了在camera hal3上, 参数tag是如何声明定义的,以及从app到hal层间,tag是如何传递的.现在我们来深入一点—— 新增自己的tag。
通过上面的讲解,我们知道了,要新增一个tag,首先需要在camera_metadata_tags.h里新增自己的vendor tag的section值:
typedef enum camera_metadata_section_start {
......
ANDROID_JPEG_START = ANDROID_JPEG << 16,
......
VENDOR_SECTION_START = VENDOR_SECTION << 16
} camera_metadata_section_start_t;
然后定义对应的每一个tag项的值:
typedef enum camera_metadata_tag {
........
ANDROID_JPEG_START,
ANDROID_JPEG_GPS_PROCESSING_METHOD, // byte | ndk_public
ANDROID_JPEG_GPS_TIMESTAMP, // int64 | ndk_public
ANDROID_JPEG_ORIENTATION, // int32 | public
ANDROID_JPEG_QUALITY, // byte | public
ANDROID_JPEG_THUMBNAIL_QUALITY, // byte | public
ANDROID_JPEG_THUMBNAIL_SIZE, // int32[] | public
ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, // int32[] | public
ANDROID_JPEG_MAX_SIZE, // int32 | system
ANDROID_JPEG_SIZE, // int32 | system
ANDROID_JPEG_END,
........
}
它以ANDROID_JPEG_START开始,以ANDROID_JPEG_END结束。 然后在camera_metadata_tag_info.c里定义对应的section的名字:
const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {
......
[ANDROID_JPEG] = "android.jpeg",
......
};
再定义需要增加的tag section一共有多少个子项:
unsigned int camera_metadata_section_bounds[ANDROID_SECTION_COUNT][2] = {
......
[ANDROID_JPEG] = { ANDROID_JPEG_START,
ANDROID_JPEG_END },
......
}
然后将需要添加的tag的每一项名字名出来
static tag_info_t android_jpeg[ANDROID_JPEG_END -
ANDROID_JPEG_START] = {
[ ANDROID_JPEG_GPS_COORDINATES - ANDROID_JPEG_START ] =
{ "gpsCoordinates", TYPE_DOUBLE },
[ ANDROID_JPEG_GPS_PROCESSING_METHOD - ANDROID_JPEG_START ] =
{ "gpsProcessingMethod", TYPE_BYTE },
[ ANDROID_JPEG_GPS_TIMESTAMP - ANDROID_JPEG_START ] =
{ "gpsTimestamp", TYPE_INT64 },
[ ANDROID_JPEG_ORIENTATION - ANDROID_JPEG_START ] =
{ "orientation", TYPE_INT32 },
[ ANDROID_JPEG_QUALITY - ANDROID_JPEG_START ] =
{ "quality", TYPE_BYTE },
[ ANDROID_JPEG_THUMBNAIL_QUALITY - ANDROID_JPEG_START ] =
{ "thumbnailQuality", TYPE_BYTE },
[ ANDROID_JPEG_THUMBNAIL_SIZE - ANDROID_JPEG_START ] =
{ "thumbnailSize", TYPE_INT32 },
[ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES - ANDROID_JPEG_START ] =
{ "availableThumbnailSizes", TYPE_INT32 },
[ ANDROID_JPEG_MAX_SIZE - ANDROID_JPEG_START ] =
{ "maxSize", TYPE_INT32 },
[ ANDROID_JPEG_SIZE - ANDROID_JPEG_START ] =
{ "size", TYPE_INT32 },
};
最后再在对应的hal层里实现自己的get_vendor_tag_ops函数就可以了。下面我来举个完整的添加自己的tag的例子。
1.)在我的usbcamera或virtualcamera 的hal3目录里,新增一个HalModule.h,内容如下:
namespace android {
namespace HalModule {
//vendor tag对应的每一项的值,这里只有一个。google规定,第三方的tag,都要在
//VENDOR_SECTION_START后面添加,所以ANDROID_CAMERA_TYPE的值就设为了
//VENDOR_SECTION_START
typedef enum camera_ext_tags{
/*sprd add flag start*/
ANDROID_CAMERA_TYPE =
VENDOR_SECTION_START,
VENDOR_SECTION_END,
/*not parameter but only flag between framework and hal*/
/*sprd add flag end*/
}camera_metadata_tag_t;
//vendor tag对应的section索引值
typedef enum cam_hal_metadata_section {
ANDROID_ADD_PARAMETERS,
ANDROID_VENDOR_SECTION_COUNT
} cam_hal_metadata_section_t;
typedef struct tags_info {
const char *tag_name;
uint8_t tag_type;
} tags_info_t;
};
};
2.)在HalModule.cpp里添加如下代码:
//这里定义vendor tag的section的名字为com.addParameters
const char *cam_hal_metadata_section_names[ANDROID_VENDOR_SECTION_COUNT] = {
"com.addParameters",
};
//这里定义vendor tag的子项的名字为camera-type
static tags_info_t android_add_parameters[android::HalModule::VENDOR_SECTION_END - VENDOR_SECTION_START] = {
{ "camera-type", TYPE_INT32 },
};
tags_info_t *cam_tag_info[ANDROID_VENDOR_SECTION_COUNT] = {
android_add_parameters,
};
//定义新增的vendor tag 的section的范围
int cam_hal_metadata_section_bounds[ANDROID_VENDOR_SECTION_COUNT][2] = {
{ android::HalModule::ANDROID_CAMERA_TYPE, android::HalModule::VENDOR_SECTION_END },
};
//返回vendor tag的个数,有多少个返回多少个
static int get_tag_count(const vendor_tag_ops_t* ops)
{
//UNUSED(ops);
return (android::HalModule::VENDOR_SECTION_END - VENDOR_SECTION_START);
}
把所有vendor tag挨个放在service传下来的uint32_t * tag_array里面,这样上层就知道每一个tag对应的序号值了
static void get_all_tags(const vendor_tag_ops_t* ops, uint32_t* tag_array)
{
uint32_t *tag_array_tmp = tag_array;
//UNUSED(ops);
for(int i = android::HalModule::ANDROID_CAMERA_TYPE; i<android::HalModule::VENDOR_SECTION_END; i++) {
*tag_array_tmp = i;
tag_array_tmp++;
}
}
//获取vendor tag的section对应的section名称,我这里会返回"com.addParameters"
static const char* get_section_name(const vendor_tag_ops_t* ops, uint32_t tag)
{
uint32_t tag_section = (tag >> 16) - VENDOR_SECTION;
//UNUSED(ops);
if (tag_section >= ANDROID_VENDOR_SECTION_COUNT) {
return NULL;
}
return cam_hal_metadata_section_names[tag_section];
}
//用于获取每一个tag的名称,比如我这个地方返回“camera-type”就可以了
static const char* get_tag_name(const vendor_tag_ops_t* ops, uint32_t tag)
{
uint32_t tag_section = (tag >> 16) - VENDOR_SECTION;
uint32_t tag_index = tag & 0xFFFF;
//UNUSED(ops);
if (tag_section >= ANDROID_VENDOR_SECTION_COUNT
|| tag >= (uint32_t)(cam_hal_metadata_section_bounds[tag_section][1])) {
return NULL;
}
return cam_tag_info[tag_section][tag_index].tag_name;
}
//返回tag对应的设置数据的类型,可以用TYPE_INT32, TYPE_FLOAT等多种数据格式,我这里是TYPE_INT32。
static int get_tag_type(const vendor_tag_ops_t* ops, uint32_t tag)
{
uint32_t tag_section = (tag >> 16) - VENDOR_SECTION;
uint32_t tag_index = tag & 0xFFFF;
//UNUSED(ops);
if (tag_section >= ANDROID_VENDOR_SECTION_COUNT
|| tag >= (uint32_t)(cam_hal_metadata_section_bounds[tag_section][1])) {
ALOGE("####get_tag_type: hal %d, tag=0x%x, tag_section=%x, bounds=%x\n", __LINE__, tag, tag_section, cam_hal_metadata_section_bounds[tag_section][1]);
return -1;
}
return cam_tag_info[tag_section][tag_index].tag_type;
}
//下面这个函数,会赋值给get_vendor_tag_ops,这函数里引用的get_tag_count、get_all_tags等等,都是hal3的标准接口。
static void getVendorTagOps(vendor_tag_ops_t* ops)
{
ALOGE("getVendorTagOps start");
ops->get_tag_count = get_tag_count;
ops->get_all_tags = get_all_tags;
ops->get_section_name = get_section_name;
ops->get_tag_name = get_tag_name;
ops->get_tag_type = get_tag_type;
}
camera_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_3,
.hal_api_version = HARDWARE_HAL_API_VERSION,
//.id = CAMERA_HARDWARE_MODULE_ID,
.id = "virtual_camera",
.name = "virtual_camera",
.author = "Antmicro Ltd.",
.methods = &android::HalModule::moduleMethods,
.dso = NULL,
.reserved = {0}
},
.get_number_of_cameras = android::HalModule::getNumberOfCameras,
.get_camera_info = android::HalModule::getCameraInfo,
.set_callbacks = android::HalModule::setCallbacks,
//将上面自己定义的get_tag_type等接口传给hal3标准接口
.get_vendor_tag_ops = android::HalModule::getVendorTagOps,
};
3.)在自定义的virtual camera的hal3目录的camera.cpp里的staticCharacteristics() 函数中,调用如下代码:
static const int32_t cameraType = 1;
cm.update(HalModule::ANDROID_CAMERA_TYPE, &cameraType, 1);
4.)在system\media\camera\src\camera_metadata_tag_info.c里的camera_metadata_enum_snprint函数里,新增如下代码:
int camera_metadata_enum_snprint(uint32_t tag,
uint32_t value,
char *dst,
size_t size) {
const char *msg = "error: not an enum";
int ret = -1;
switch(tag) {
......
case ANDROID_CAMERA_TYPE: {
switch (value) {
case 0:
msg = "mipi";
ret = 0;
break;
case 1:
msg = "virtual";
ret = 0;
break;
case 2:
msg = "usb";
ret = 0;
break;
default:
msg = "error: enum value out of range";
}
break;
}
......
}
}
5.)在frameworks\base\core\java\android\hardware\camera2\CameraCharacteristics.java里新增:
@PublicKey
public static final Key<Integer> CAMERA_TYPE =
new Key<Integer>("com.addParameters.camera-type", int.class);
6.)在frameworks\base\core\java\android\hardware\camera2\CaptureRequest.java里新增:
@PublicKey
public static final Key<Integer> CAMERA_TYPE =
new Key<Integer>("com.addParameters.camera-type", int.class);
7.)添加完上面这些后,就可以在app上通过下面代码来获取和设置这个tag了:
//设置
CaptureRequest.Builder builder;
builder.set(CaptureRequest.CAMERA_TYPE, CameraMetadata.1);
//获取
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
mCharacteristics = manager.getCameraCharacteristics(cameraId);
int type = mCharacteristics.get(CameraCharacteristics.CAMERA_TYPE);
按上面配置后,我们可能在adb shell里通过"dumpsys media.camera"来查看一下:
看到没,这里就可以看到我们新增的tag的值了。
好了,到这里为止,就全部讲完了。
本人建了个android camera系统 微信群,不过现在群成员超200了,不能通过二维码扫码加入,有兴趣的,可以加我微信号:xuhui_7810,到时拉你们入群。