camera2是API21出的一套方便与camera devices进行交互的包。要使用camera2 API,我们首先得对其中的一些基本的类有一定的了解。
基础类
CameraManager
它是一个系统服务,通过它我们可以访问CameraDevice以及获取关于CameraDevice的一些特性。
一般我们通过Context的getSystemService()方法就能获取到CameraManager的实例。1
val camaraManager = context.getSystemService(Context.CAMERA_SERVICE)
它是在SystemServiceRegistry的static代码块中注册的。1
2
3
4
5
6registerService(Context.CAMERA_SERVICE, CameraManager.class,
new CachedServiceFetcher<CameraManager>() {
public CameraManager createService(ContextImpl ctx) {
return new CameraManager(ctx);
}});
内部类
AvailabilityCallback
就是监听Camera是否有用的一个回调。1
2
3
4
5
6
7
8public static abstract class AvailabilityCallback {
public void onCameraAvailable(@NonNull String cameraId) {
}
public void onCameraUnavailable(@NonNull String cameraId) {
}
}
TorchCallback
监听闪光灯模式的一个回调。1
2
3
4
5
6
7
8
9public static abstract class TorchCallback {
public void onTorchModeUnavailable(@NonNull String cameraId) {
}
public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
}
}
常用方法
getCameraIdList()
getCameraIdList()可以获取到硬件设备所有的可用camera id。1
2
3
4
public String[] getCameraIdList() throws CameraAccessException {
return CameraManagerGlobal.get().getCameraIdList();
}
我们可以看到它里面实际上是调用的CameraManagerGlobal的getCameraIdList()方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public String[] getCameraIdList() {
String[] cameraIds = null;
synchronized(mLock) {
// Try to make sure we have an up-to-date list of camera devices.
connectCameraServiceLocked();
int idCount = 0;
for (int i = 0; i < mDeviceStatus.size(); i++) {
int status = mDeviceStatus.valueAt(i);
if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
status == ICameraServiceListener.STATUS_ENUMERATING) continue;
idCount++;
}
cameraIds = new String[idCount];
idCount = 0;
for (int i = 0; i < mDeviceStatus.size(); i++) {
int status = mDeviceStatus.valueAt(i);
if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
status == ICameraServiceListener.STATUS_ENUMERATING) continue;
cameraIds[idCount] = mDeviceStatus.keyAt(i);
idCount++;
}
}
return cameraIds;
}
mDeviceStatus是一个map集合,key是camera id,value是camera id对应的camera status,mDeviceStatus的元素是在connectCameraServiceLocked()方法调用的时候存放的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38private void connectCameraServiceLocked() {
// 重连
if (mCameraService != null) return;
//获取IBinder对象,进而获取到ICameraService的对象
IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
if (cameraServiceBinder == null) {
// Camera service下线
return;
}
try {
cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
} catch (RemoteException e) {
// Camera service下线
return;
}
ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
try {
CameraMetadataNative.setupGlobalVendorTagDescriptor();
} catch (ServiceSpecificException e) {
handleRecoverableSetupErrors(e);
}
try {
CameraStatus[] cameraStatuses = cameraService.addListener(this);
for (CameraStatus c : cameraStatuses) {
onStatusChangedLocked(c.status, c.cameraId);
}
mCameraService = cameraService;
} catch(ServiceSpecificException e) {
// Unexpected failure
throw new IllegalStateException("Failed to register a camera service listener", e);
} catch (RemoteException e) {
// Camera service is now down, leave mCameraService as null
}
}
我们再来看看onStatusChangeLocked()方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private void onStatusChangedLocked(int status, String id) {
// 判断当前camera id对应的状态是否是有效状态
if (!validStatus(status)) {
return;
}
// 存放到mDeviceStatus中
Integer oldStatus = mDeviceStatus.put(id, status);
if (oldStatus != null && oldStatus == status) {
return;
}
if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
return;
}
// 如果camera id对应的状态发生改变,则回调给通过CameraManager的register方法注册的AvailabilityCallback
final int callbackCount = mCallbackMap.size();
for (int i = 0; i < callbackCount; i++) {
Handler handler = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
postSingleUpdate(callback, handler, id, status);
}
}
getCameraCharacteristics
通过这个方法,我们可以获取到对应camera id的CameraCharacteristics对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException {
CameraCharacteristics characteristics = null;
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)) {
// 遗留问题不兼容camera2 api locked
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;
}
openCamera()
通过这个方法我们就可以获取到CameraDevice对象了,我们可以看到这个方法接受一个CameraDevice.StateCallback对象,当CameraDevice连接成功和失败的时候就会回调到这个Callback的对应方法中。
(un)registerAvailabilityCallback
注册和取消注册AvailabilityCallback,最终会将其放到CameraManagerGlobal的mCallbackMap中。
(un)registerTorchCallback
注册和取消注册TorchCallback,最终会将其放到CameraManagerGlobal的mTorchCallbackMap中。
CameraService的重连
在查看CameraManager的源码过程中,发现CameraService下线重连机制,通过IBinder的死亡代理进行重连,我们在connectCameraServiceLocked()中看到,在调用linkToDeath()的时候,如果发现CameraService下线了,会回调到binderDied()方法中,我们看看binderDied()方法做的事情是什么。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public void binderDied() {
synchronized(mLock) {
if (mCameraService == null) return;
mCameraService = null;
// 更新cameraId对应的status
for (int i = 0; i < mDeviceStatus.size(); i++) {
String cameraId = mDeviceStatus.keyAt(i);
onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
}
//
for (int i = 0; i < mTorchStatus.size(); i++) {
String cameraId = mTorchStatus.keyAt(i);
onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE,
cameraId);
}
// 这个方法里面进行cameraService的重连
scheduleCameraServiceReconnectionLocked();
}
}
我们可以看scheduleCameraServiceReocnnectionLock()方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28private void scheduleCameraServiceReconnectionLocked() {
final Handler handler;
if (mCallbackMap.size() > 0) {
handler = mCallbackMap.valueAt(0);
} else if (mTorchCallbackMap.size() > 0) {
handler = mTorchCallbackMap.valueAt(0);
} else {
// 如果没有注册callback,则不进行重连操作
return;
}
handler.postDelayed(
new Runnable() {
public void run() {
// 重连
ICameraService cameraService = getCameraService();
if (cameraService == null) {
synchronized(mLock) {
// 重连失败,继续重连
scheduleCameraServiceReconnectionLocked();
}
}
}
},
CAMERA_SERVICE_RECONNECT_DELAY_MS);
}
CameraDevice
它表示的就是Android设备上的一个摄像头,允许你在高帧率的情况下对图像进行捕获以及后期处理有一个精细的控制。
TEMPLATE
CameraDevice里面定义了一些TEMPLATE,使我们在创建CaptureRequest.Builder的时候需要的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51/*
* 创建一个适合于相机预览窗口的Request,这意味着高帧率优先于高质量处理,这个Request正常
* 情况下被用于CameraCaptureSession的setRepeatingRequest(),这个TEMPLATE在所有的
* camera devices上都支持
*/
public static final int TEMPLATE_PREVIEW = 1;
/*
* 创建一个静态图像捕获的Request,这意味着优先考虑图片质量,而不是帧率。通常情况下被用于
* CameraCaptureSession的capture(),这个TEMPLATE在所有的camera devices上都支持
*/
public static final int TEMPLATE_STILL_CAPTURE = 2;
/*
* 创建一个适合于视频录制的Request,这意味着需要一个稳定的帧率和质量。通常情况下被用于
* CameraCaptureSession的setRepeatingRequest(),这个TEMPLATE在所有的camera devices
* 上都支持
*/
public static final int TEMPLATE_RECORD = 3;
/*
* 创建一个在视频录制过程中静态图像捕获的Request,这意味着在不中断的录制过程中最大化图像质量。
* 通常情况下,你通过CameraDevice的createCaptureRequest(TEMPLATE_RECORD)创建一个
* Request,然后通过CameraCaptureSession的setRepeatingRequest()开启视频录制请求,
* 然后你调用CameraCaptureSession的capture()可以使用这个TEMPLATE,这个TEMPLATE
* 基本所有的camera devices都支持,除非旧设备的CameraCharacteristics的
* INFO_SUPPORTED_HARDWARE_LEVEL字段为INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
*/
public static final int TEMPLATE_VIDEO_SNAPSHOT = 4;
/*
* 创建一个零延时的静态图像捕获Request,这意味着在不影响预览帧率的情况下,最大化图像质量。
* 这个TEMPLATE被所有支持
* CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING
* CameraMetadata#PRIVATE_REPROCESSING
* CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
* CameraMetadata#YUV_REPROCESSING
* 的camera devices所支持
*/
public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5;
/*
* 用于直接应用程序控制捕获参数的basic TEMPLATE,所有的自动控制都会失效,比如自动曝光,
* 自动白平衡,自动聚焦等。手动设置的参数诸如曝光等都被设置为合理的默认值,但是你可以根据
* 程序的需要修改这些值。
* 这个TEMPLATE被所有的支持
* CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
* CameraMetadata#MANUAL_SENSOR
* 的camera devices所支持
*/
public static final int TEMPLATE_MANUAL = 6;
内部类
StateCallback
Camera Device的各种状态回调。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public static abstract class StateCallback {
// 打开camera失败,camera device被一个优先级更高的client在使用
public static final int ERROR_CAMERA_IN_USE = 1;
// 打开camera失败,所有的camera device正在被使用
public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
// setCameraDisabled();因为设备原因无法打开camera
public static final int ERROR_CAMERA_DISABLED = 3;
// camera device遇到一个致命的错误,需要重新打开以便使用
public static final int ERROR_CAMERA_DEVICE = 4;
// Android设备遇到一个致命的错误,需要重新启动来加载camera相关功能
public static final int ERROR_CAMERA_SERVICE = 5;
// camera device被打开的时候回调,表示camera可以被使用了
public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
// 当调用CameraDevice的close()方法的时候会回调,
public void onClosed(@NonNull CameraDevice camera) {
}
/*
* camera device不在可用的时候会回调,可能是安全策略或权限的改变,也可能是可拆卸摄像头连接
* 断开,亦或者是一个更高优先级的client需要使用camera device。此方法调用之后仍然会触发capture
* 回调,或者将新的image buffer递送给活跃的输出。
*/
public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
// camera device发生错误的时候会回调这个方法
public abstract void onError(@NonNull CameraDevice camera,
@ErrorCode int error); // Must implement
}
常用方法
close
尽可能快的关闭camera device的连接,一旦这个方法被调用,后续访问camera device将会抛出IllegalStateException。当和camera device的连接完全关闭的时候,会回调到StateCallback的onClosed()方法,此时camera device是可以被重新打开的。
这个方法调用之后,在StateCallback的onClosed()方法调用之前,camera device和活跃的session的callback都不会发生,那些已经提交的capture请求也会被丢弃,就好像CameraCaptureSession的abortCaptures()被调用了一样,但是失败的callback会被调用。
createCaptureSession
通过提供给camera device一个目标输出的Surface集合来创建一个新的camera capture session。活跃的camera session为camera device的每个capture request确定一组潜在的输出Surface集合,每个capture request可能使用其中的某个或者所有的输出Surface。一旦CameraCaptureSession被创建了,你就可以通过CameraCaptureSession的
capture(),captureBurst(),setRepeatingCapture(),setRepeatingBurst()来发起capture请求。1
2
3public abstract void createCaptureSession(@NonNull List<Surface> outputs,
@NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException;
createCaptureRequest
接受一个TEMPLATE来创建CaptureRequest.Builder,Request是用于CameraCaptureSession和硬件设备进行交互的。1
2
3
public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
throws CameraAccessException;
CameraCaptureSession
CaptureRequest
使用
常见问题
拍摄图片方向反转
这个问题应该算是遇到的比较多的,图片拍摄保存后是倒着的。要弄清楚这个问题产生的原因我们得知道两个东西,一个是屏幕rotation,一个是camera sensor的角度,这两个因素决定了你拍摄的图片后的方向。一般我们竖屏的界面,屏幕rotation是Surface.ROTATION_0,然后一般的手机camera sensor的角度是90度,这个时候我们拍摄的图片保存是正常,但是有些手机的camera sensor是270度,这样拍摄就会导致图片保存的时候是倒着的。