当前位置: 代码迷 >> 综合 >> Android 源码 Camera2 获取 CameraId 列表
  详细解决方案

Android 源码 Camera2 获取 CameraId 列表

热度:4   发布时间:2024-02-22 00:03:49.0

获取 CameraId 列表通过调用 CameraManager 类 getCameraIdList() 实现。

getCameraIdList() 按标识符返回当前连接的摄像头设备列表,包括其他 camera API 客户端可能正在使用的摄像头。

不可移动摄像头的标识符使用以 0 开头的整数,而可移动摄像头即使是同一型号,也为每个设备都分配唯一的标识符。
在这里插入图片描述
frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public final class CameraManager {
    ......@NonNullpublic String[] getCameraIdList() throws CameraAccessException {
    synchronized (mLock) {
    // 创建 ID 列表可处理设备枚举中的各种已知故障,// 因此只有它抛出的异常是意外的,并且应该向上传播。return getOrCreateDeviceIdListLocked().toArray(new String[0]);}}    ......
}

getOrCreateDeviceIdListLocked() 返回或创建当前连接的相机设备列表。如果连接到相机服务出现错误,将返回一个空列表。

  1. 获取 CameraService 服务代理
  2. 获取 Camera 个数
  3. 获取描述每个 Camera 的元信息
  4. 将 Camera 从 0 开始的整数标识符添加到设备 ID 列表

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public final class CameraManager {
    ......private ArrayList<String> mDeviceIdList;......private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
    if (mDeviceIdList == null) {
    int numCameras = 0;// 1. 获取 CameraServiceICameraService cameraService = CameraManagerGlobal.get().getCameraService();ArrayList<String> deviceIdList = new ArrayList<>();// 如果 CameraService 为 null,返回空列表if (cameraService == null) {
    return deviceIdList;}try {
    // 2. 获取 Camera 个数numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);} catch(CameraRuntimeException e) {
    throw e.asChecked();} catch (RemoteException e) {
    // camera service just died - if no camera service, then no devicesreturn deviceIdList;}CameraMetadataNative info = new CameraMetadataNative();for (int i = 0; i < numCameras; ++i) {
    // 不可移动摄像头使用从 0 开始的整数作为标识符boolean isDeviceSupported = false;try {
    // 3. 获取描述 Camera 元信息cameraService.getCameraCharacteristics(i, info);if (!info.isEmpty()) {
    isDeviceSupported = true;} else {
    throw new AssertionError("Expected to get non-empty characteristics");}} catch(IllegalArgumentException  e) {
    // 从服务获得 BAD_VALUE,表示不支持此设备。} catch(CameraRuntimeException e) {
    // DISCONNECTED 意味着 HAL 在获取设备信息时报告了一个底层错误;// 跳过设备。其他错误,继续传播异常。if (e.getReason() != CameraAccessException.CAMERA_DISCONNECTED) {
    throw e.asChecked();}} catch(RemoteException e) {
    // 相机服务死亡,没有设备列出deviceIdList.clear();return deviceIdList;}if (isDeviceSupported) {
    // 4. 将 Camera 从 0 开始的整数标识符添加到设备 ID 列表deviceIdList.add(String.valueOf(i));} else {
    Log.w(TAG, "Error querying camera device " + i + " for listing.");}}mDeviceIdList = deviceIdList;}return mDeviceIdList;}......
}

获取 CameraService,首先调用了 CameraManager 类内部静态类 CameraManagerGlobal 的 get() 方法获取 CameraManagerGlobal 单例对象。

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public final class CameraManager {
    ......private static final class CameraManagerGlobal extends ICameraServiceListener.Stubimplements IBinder.DeathRecipient {
    ......// Singleton instanceprivate static final CameraManagerGlobal gCameraManager =new CameraManagerGlobal();......// Singleton, don't allow constructionprivate CameraManagerGlobal() {
    }public static CameraManagerGlobal get() {
    return gCameraManager;}        ......}......
}

然后调用 CameraManagerGlobal 对象的 getCameraService() 方法获取 CameraService(实现了 ICameraService 接口)。getCameraService() 方法内部调用 connectCameraServiceLocked() 处理。如果相机服务当前不可用,则为 null。如果自上次使用相机服务以来相机服务已失效,则将尝试重新连接该服务。

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public final class CameraManager {
    ......private static final class CameraManagerGlobal extends ICameraServiceListener.Stubimplements IBinder.DeathRecipient {
    ......public ICameraService getCameraService() {
    synchronized(mLock) {
    connectCameraServiceLocked();if (mCameraService == null) {
    Log.e(TAG, "Camera service is unavailable");}return mCameraService;}}            ......}......
}

连接到 CameraService(如果有),并设置侦听器。 如果服务已经连接,则什么也不做。将 mCameraService 设置为有效的指针;如果连接失败,则将其设置为 null。

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public final class CameraManager {
    ......private static final class CameraManagerGlobal extends ICameraServiceListener.Stubimplements IBinder.DeathRecipient {
    ......private void connectCameraServiceLocked() {
    // 只有在必要时才重新连接if (mCameraService != null) return;Log.i(TAG, "Connecting to camera service");// 从 ServiceManager 中查询并获取 CameraService Binder 对象IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);if (cameraServiceBinder == null) {
    // CameraService 现在已经关闭,将 mCameraService 设置为 nullreturn;}try {
    // 为 Binder 对象设置死亡代理cameraServiceBinder.linkToDeath(this, /*flags*/ 0);} catch (RemoteException e) {
    // CameraService 现在已经关闭,将 mCameraService 设置为 nullreturn;}// 将 Binder 对象转化为 CameraServiceICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);/*** 将 CameraService 包装在装饰器中,该装饰器会自动将返回码转换为异常。*/ICameraService cameraService =CameraServiceBinderDecorator.newInstance(cameraServiceRaw);try {
    // 设置供应商标签CameraServiceBinderDecorator.throwOnError(CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());} catch (CameraRuntimeException e) {
    handleRecoverableSetupErrors(e, "Failed to set up vendor tags");}try {
    // 添加监听器cameraService.addListener(this);mCameraService = cameraService;} catch(CameraRuntimeException e) {
    // 意外故障throw new IllegalStateException("Failed to register a camera service listener",e.asChecked());} catch (RemoteException e) {
    // CameraService 现在已经关闭,将 mCameraService 设置为 null}}                 ......}......
}

mediaserver 中运行的本地摄像头服务的 Binder 接口。

frameworks/base/core/java/android/hardware/ICameraService.aidl

interface ICameraService
{
    /*** Keep up-to-date with frameworks/av/include/camera/ICameraService.h*/int getNumberOfCameras(int type);......int getCameraCharacteristics(int cameraId, out CameraMetadataNative info);......// 确定是否支持特定的 API 版本;参见 ICameraService.h 了解版本定义int supportsCameraApi(int cameraId, int apiVersion);......
}

接下来研究一下设置供应商标签。nativeSetupGlobalVendorTagDescriptor() 是个 jni 方法。它设置全局客户端供应商标签描述符,以允许在相机应用程序中使用供应商标签。

frameworks/base/core/java/android/hardware/camera2/impl/CameraMetadataNative.java

public class CameraMetadataNative implements Parcelable {
    ......public static native int nativeSetupGlobalVendorTagDescriptor();    ......
}
  1. 获取 CameraService 服务代理
  2. 获取 VendorTagDescriptor
  3. 调用 VendorTagDescriptor setAsGlobalVendorTagDescriptor(…) 设置供应商标签

frameworks/base/core/jni/android_hardware_camera2_CameraMetadata.cpp

static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz) {
    const String16 NAME("media.camera");sp<ICameraService> cameraService;// 1. 获取 CameraServicestatus_t err = getService(NAME, /*out*/&cameraService);if (err != OK) {
    ALOGE("%s: Failed to get camera service, received error %s (%d)", __FUNCTION__,strerror(-err), err);return err;}sp<VendorTagDescriptor> desc;// 2. 获取 VendorTagDescriptorerr = cameraService->getCameraVendorTagDescriptor(/*out*/desc);if (err == -EOPNOTSUPP) {
    // Camera HAL 太旧了,不支持供应商标签ALOGW("%s: Camera HAL too old; does not support vendor tags", __FUNCTION__);VendorTagDescriptor::clearGlobalVendorTagDescriptor();return OK;} else if (err != OK) {
    ALOGE("%s: Failed to setup vendor tag descriptors, received error %s (%d)",__FUNCTION__, strerror(-err), err);return err;}// 3. 设置供应商标签err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);return err;
}

getNumberOfCameras(…)、getCameraCharacteristics(…) 和 getCameraVendorTagDescriptor(…) 三个方法都调用了远端实现。现在拿 getNumberOfCameras(…) 方法作为例子分析一下。结合前面 Native CameraService 启动过程,不难知道入口点在 BpCameraService 中。

frameworks/av/camera/ICameraService.cpp

class BpCameraService: public BpInterface<ICameraService>
{
    
public:BpCameraService(const sp<IBinder>& impl): BpInterface<ICameraService>(impl){
    }// get number of cameras available that support standard camera operationsvirtual int32_t getNumberOfCameras(){
       // CAMERA_TYPE_BACKWARD_COMPATIBLE 代表向后兼容的类型return getNumberOfCameras(CAMERA_TYPE_BACKWARD_COMPATIBLE);}// get number of cameras available of a given typevirtual int32_t getNumberOfCameras(int type){
    Parcel data, reply;data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());data.writeInt32(type);remote()->transact(BnCameraService::GET_NUMBER_OF_CAMERAS, data, &reply);if (readExceptionCode(reply)) return 0;return reply.readInt32();}
......
}

BpCameraService 类中的 getNumberOfCameras() 方法最终调用了其重载版本的方法 getNumberOfCameras(int type)。最终会在 BnCameraService 类得到处理,CameraService 继承了 BnCameraService 类,实际是在 CameraService 中得到处理的。首先在 BnCameraService 类 onTransact 方法中收到写过来的数据。然后在其中调用了 getNumberOfCameras(…) 作为返回值又给客户端写了回去。

frameworks/av/camera/ICameraService.cpp

status_t BnCameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
    case GET_NUMBER_OF_CAMERAS: {
    CHECK_INTERFACE(ICameraService, data, reply);reply->writeNoException();reply->writeInt32(getNumberOfCameras(data.readInt32()));return NO_ERROR;} break;......}
}

CameraService 类 getNumberOfCameras(…) 只是简单的返回了 mNumberOfNormalCameras 成员变量的值。这个值是在 CameraService 类 onFirstRef() 方法中赋值的,具体可以参见《Android 源码 Camera2 CameraService 启动》一节。

frameworks/av/services/camera/libcameraservice/CameraService.cpp

int32_t CameraService::getNumberOfCameras(int type) {
    ATRACE_CALL();switch (type) {
    case CAMERA_TYPE_BACKWARD_COMPATIBLE:return mNumberOfNormalCameras;case CAMERA_TYPE_ALL:return mNumberOfCameras;default:ALOGW("%s: Unknown camera type %d, returning 0",__FUNCTION__, type);return 0;}
}
  相关解决方案