openCamera
【又是完美的一天,用了好久的Camera2,才发现对于openCamera模块的流程不太熟悉,所以来记录一下最近的学习笔记。参考谷歌官网以及 简书大佬JeffMony的博客】
Camera操作过程最重要的四个步骤:
- CameraManager———> openCamera 打开相机
- CameraDeviceImpl——> createCaptureSession 创建捕获会话
- CameraCaptureSession——> setRepeatingRequest 设置预览界面
- CameraDeviceImpl——> capture 开始捕获图片
下面来详细分析一下这个过程中重要的类以及方法。
1. Cameramanager
1.1 概念
官网定义:
A system service manager for detecting, characterizing, and connecting to CameraDevice.一个用于检测、获取相机设备特征和连接相机设备的系统服务管理器。
使用:
获取方式:通过Context类的 getSystemService() 方法获取,传入参数 Context.CAMEARA_SERVICE 或者 CameraManager.class即可。
示例:
activity.getSystemService(Context.CAMERA_SERVICE);
1.2 内部类
Cameramanager 包含两个公有的内部类,Cameramanager.AvailabilityCallback、Cameramanager.TorchCallback。
1.2.1 Cameramanager.Availability~~Callback
相机设备可用状态的回调。当相机设备可用状态改变时,回调这个类的onCameraAvailable(String cameraId)和onCameraUnavailable(String cameraId)方法。
那怎样区分相机设备是否可用呢?当设备不再处于使用状态或一个新的可移动的相机~~ 设备被连接时,我们认为它处于可用状态。当有应用或者服务开始使用相机或者可移动相机断开连接,此时认为设备不可用。
1.2.2 Cameramanager.TorchCallback
相机设备闪光灯的Torch模式可用状态的回调。
1.3 常用方法
1.3.1 CameraCharacteristics getCameraCharacteristics(String cameraId)
获取对应相机设备的特征。返回值为CameraCharacteristics,类似于Camera1中的Camera.Parameter类,里面封装了相机设备固有的所有属性功能。
1.3.2 String[] getCameraIdList()
获取当前连接的相机设备id列表,包括其他camera API客户机可能在使用的摄像头。
不可移动相机使用从0开始的整数作为其标识符,而可移动相机对每个单独的设备都有唯一的标识符,即使它们是相同的型号。此列表不包含只能作为逻辑多摄像头设备的一部分使用的物理摄像头。
1.3.3 openCamera
Cameramanager中有两个openCamera(…)。
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
public void openCamera(@NonNull String cameraId,
@NonNull @CallbackExecutor Executor executor,
@NonNull final CameraDevice.StateCallback callback)
handler 是传入的一个执行耗时操作的handler
executor 操作线程池。
2. openCamera
openCamera的调用流程
openCamera(…)——>openCameraForUid(…)——>openCameraDeviceUserAsync(…)——>……
openCameraDeviceUserAsync函数
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Executor executor, final int uid)
throws CameraAccessException {
/***
**function body.
**/
return device;
}
返回值类型CameraDevice,是抽象类,CameraDeviceImpl是其实现类。我们需要获取的就是CameraDeviceImpl的实例对象。这个函数主要作用就是到底层获取相机设备的信息,并获取当前指定cameraId的设备实例。本函数的主要工作可以分为五个步骤:
- 获取当前cameraId指定相机的设备信息
- 利用获取的设备信息创建CameraDeviceImpl实例
- 调用远程CameraService获取当前相机的远程服务
- 将获取的远程服务设置到CameraDeviceImpl实例中
- 返回CameraDeviceImpl实例
2.1 获取当前cameraId指定相机的设备信息
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraCharacteristics提供了CameraDevice的各种属性,可以通过getCameraCharacteristics函数查询。
public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException {
CameraCharacteristics characteristics = null;
if (CameraManagerGlobal.sCameraServiceDisabled) {
throw new IllegalArgumentException("No cameras available on device");
}
synchronized (mLock) {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable");
}
try {
if (!supportsCamera2ApiLocked(cameraId)) {
int id = Integer.parseInt(cameraId);
String parameters = cameraService.getLegacyParameters(id);
CameraInfo info = cameraService.getCameraInfo(id);
characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
} else {
CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
characteristics = new CameraCharacteristics(info);
}
} catch (ServiceSpecificException e) {
throwAsPublicException(e);
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable", e);
}
}
return characteristics;
}
上述函数中有个函数——>supportsCamera2APILocked(cameraId),这个函数是判断当前camera服务是否支持camera2 api,支持返回true,不支持返回false。该方法调用了supportsCameraApiLocked(String cameraId, int apiVersion)。
private boolean supportsCamera2ApiLocked(String cameraId) {
return supportsCameraApiLocked(cameraId, API_VERSION_2);
}
private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
/*
* Possible return values:
* - NO_ERROR => CameraX API is supported
* - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
* - Remote exception => If the camera service died
*
* Anything else is an unexpected error we don't want to recover from.
*/
try {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
// If no camera service, no support
if (cameraService == null) return false;
return cameraService.supportsCameraApi(cameraId, apiVersion);
} catch (RemoteException e) {
// Camera service is now down, no support for any API level
}
return false;
}
调用的CameraService对应的是偶ICameraService,aidl,对应的实现类在frameworks/av/services/camera/libcameraservice/CameraService.h。代码如下:
Status CameraService::supportsCameraApi(const String16& cameraId, int apiVersion,
/*out*/ bool *isSupported) {
ATRACE_CALL();
const String8 id = String8(cameraId);
ALOGV("%s: for camera ID = %s", __FUNCTION__, id.string());
switch (apiVersion) {
case API_VERSION_1:
case API_VERSION_2:
break;
default:
String8 msg = String8::format("Unknown API version %d", apiVersion);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
int deviceVersion = getDeviceVersion(id);
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
if (apiVersion == API_VERSION_2) {
ALOGV("%s: Camera id %s uses HAL version %d <3.2, doesn't support api2 without shim",
__FUNCTION__, id.string(), deviceVersion);
*isSupported = false;
} else { // if (apiVersion == API_VERSION_1) {
ALOGV("%s: Camera id %s uses older HAL before 3.2, but api1 is always supported",
__FUNCTION__, id.string());
*isSupported = true;
}
break;
case CAMERA_DEVICE_API_VERSION_3_2:
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
ALOGV("%s: Camera id %s uses HAL3.2 or newer, supports api1/api2 directly",
__FUNCTION__, id.string());
*isSupported = true;
break;
case -1: {
String8 msg = String8::format("Unknown camera ID %s", id.string());
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
default: {
String8 msg = String8::format("Unknown device version %x for device %s",
deviceVersion, id.string());
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_INVALID_OPERATION, msg.string());
}
}
return Status::ok();
}
注意:
这里的API_VERSION_2不是api level 2,而是camera1还是camera2.
采用camera2 api来获取相机设备的信息。
CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
try {
info.setCameraId(Integer.parseInt(cameraId));
} catch (NumberFormatException e) {
Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
info.setDisplaySize(displaySize);
characteristics = new CameraCharacteristics(info);
camera整体调用流程:
2.2 利用获取相机的设备信息创建CameraDeviceImpl实例
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
executor,
characteristics,
mContext.getApplicationInfo().targetSdkVersion);
创建完CameraDeviceImpl实例,传入刚才获取的characteristics。
2.3 调用远程CameraService获取当前相机的远程服务
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, cameraId, mContext.getOpPackageName(), uid);
这个方法主要目的是连接当前的cameraDevice设备。调用到CameraService::connectDevice中。
2.4 将获取的远程服务设置到CameraDeviceImpl实例中
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
此处cameraUser就是cameraService端设置的connectDevice方法返回的对象(ICameraDeviceUser.Stub对象)。
public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
synchronized(mInterfaceLock) {
// TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
if (mInError) return;
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
// asBinder returns NULL.
if (remoteDeviceBinder != null) {
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
mDeviceExecutor.execute(mCallOnOpened);
mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
这个mRemoteDevice是应用程序进程和android camera service 端之间链接的桥梁,上层操作camera的方法会通过调用mRemoteDevice来调用到camera service端来实现操作底层camera驱动的目的。
3. Camera状态的回调
3.1 openCameraDeviceUserAsync中的StateCallback
openCamera调用到openCameraDeviceUserAsync(…),同时也把它的StateCallback参数传入。这个参数和获取到的CameraCharacteristics一起传入CameraDeviceImpl的构造函数中。但传入CameraService的回调参数不是这个回调,而是CameraDeviceImpl实例中的参数,使用deviceImpl.getCallbacks()获得。在CameraDeviceImpl的构造函数中传入StateCallback,然后赋值给CameraDeviceCallbacks类型的mDeviceCallback。而CameraDeviceCallbacks继承自ICameraDeviceCallbacks.Stub。ICameraDeviceCallbacks.Stub是可以在Binder IPC中传输的对象,这个才是应用程序与CameraService通信的回调,在这个回调中的执行方法标识当前的camera的执行状态。
3.2 CameraDeviceCallback回调
ICameraDeviceCallbacks.aidl自动生成的android/handware/camera2/ICameraDeviceCallbacks.h文件。
这个回调函数是从CameraService中调上来的。下面的回调包含了Camera执行过程中的各种状态:
- onDeviceError
- onDeviceIdle
- onCaptureStarted
- onResultReceived
- onPrepared
- onRepeatingRequestError
- onRequestQueueEmpty
3.3 StateCallback回调
StateCallback是openCamera传入的3个参数中的一个,这是一个标识当前camera连接状态的回调。
public static abstract class StateCallback {
public static final int ERROR_CAMERA_IN_USE = 1;
public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
public static final int ERROR_CAMERA_DISABLED = 3;
public static final int ERROR_CAMERA_DEVICE = 4;
public static final int ERROR_CAMERA_SERVICE = 5;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERROR_"}, value =
{ERROR_CAMERA_IN_USE,
ERROR_MAX_CAMERAS_IN_USE,
ERROR_CAMERA_DISABLED,
ERROR_CAMERA_DEVICE,
ERROR_CAMERA_SERVICE })
public @interface ErrorCode {};
public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
public abstract void onError(@NonNull CameraDevice camera,
@ErrorCode int error); // Must implement
}
- onOpened回调
当前cameraDevice已经被打开时会触发这个回调。指明camera的状态是opened了,这时可以开始createCaptureSession、开始使用camera捕捉图片或者视频。
触发onOPened回调的地方在setRemoteDevice(…),这个函数在connectDevice(…)成功之后执行,表名当前的cameraDevice已经连接成功了,出发camera能够打开的回调。
-
onClosed回调:
camera device已经被关闭,这个回调被触发。一般是终端开发者closeCamera的时候会释放当前持有的camera device。 -
onDisconnected回调:
camera device不可再用,打开camera device失败了,一般是因为权限或者安全策略问题导致camera device打不开。一旦连接camera device出现ERROR_CAMERA_DISCONNECTED问题,这时函数就会被回调,表示当前camera device处于断开的状态。 -
onError回调:
调用camera device的时候出现了严重的问题。执行CameraService–>connectDevice 出现异常了。
deviceImpl.setRemoteFailure(e);是执行onError回调的函数。