前段时间,我在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"来查看一下:

android摄像头监控开发sdk android 虚拟摄像头 开发_camera参数设置

 

android摄像头监控开发sdk android 虚拟摄像头 开发_android摄像头监控开发sdk_02

        看到没,这里就可以看到我们新增的tag的值了。

        好了,到这里为止,就全部讲完了。

        本人建了个android camera系统 微信群,不过现在群成员超200了,不能通过二维码扫码加入,有兴趣的,可以加我微信号:xuhui_7810,到时拉你们入群。