因业务需求,需要同时打开两颗物理摄像头。 从Camera2 api来看 SdkVersion 28 以后已经支持此功能。 但是不得不说api文档不友好,又或者说我实在是菜。
先来一张 这个我也不知道叫什么的 Camera2 的结构图吧。
从这张图中,我们需要知道的其实就是 HAL层中的 Camera0,Camera1......是什么,又包含了什么。从官方的文档中,可以理解为
这些Camera 为逻辑摄像头。 什么是逻辑摄像头? 那就是软件层面的摄像头。 无论你的物理摄像头(手机上真实的摄像头)是什么样 你不需要考虑, 反正你拿到的只是这个逻辑摄像头。
问题就来了 我现在要同时打开两颗物理摄像头啊 , 给我一堆逻辑摄像头有什么用?
不急 。。 继续讲。。
这个逻辑摄像头是对物理摄像头的封装。 意思就是 这个逻辑摄像头中会包含一个 或者多个物理摄像头。摄像头客户端可以通过调用 getPhysicalCameraIds() 就可以得到这颗逻辑摄像头下的物理摄像头(当然,也可能没有,也就是不提供)。 当这个getPhysicalCameraIds()返回的物理摄像头Id个数大于2 也就是这个逻辑摄像头下有两颗以上的物理摄像头, 那么就说明有机会将这两科物理摄像头一起打开。 只能是有机会啊 实测。
不多说废话了。 下面直接走流程
一、选出有多个物理摄像头的逻辑摄像头
双镜类
public class DualCamera {
//逻辑相机ID(厂商分配)
private String logicCameraId;
//相机物理id 1
private String physicsCameraId1;
//相机物理id 2
private String physicsCameraId2;
public String getLogicCameraId() {
return logicCameraId;
}
public void setLogicCameraId(String logicCameraId) {
this.logicCameraId = logicCameraId;
}
public String getPhysicsCameraId1() {
return physicsCameraId1;
}
public void setPhysicsCameraId1(String physicsCameraId1) {
this.physicsCameraId1 = physicsCameraId1;
}
public String getPhysicsCameraId2() {
return physicsCameraId2;
}
public void setPhysicsCameraId2(String physicsCameraId2) {
this.physicsCameraId2 = physicsCameraId2;
}
}
得到可以开双镜的摄像头
public static DualCamera getDualCamera(Context context){
DualCamera dualCamera = new DualCamera();
//获取管理类
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
assert manager != null;
try {
//获取所有逻辑ID
String[] cameraIdList = manager.getCameraIdList();
//获取逻辑摄像头下拥有多个物理摄像头的类 作为双镜类
for (String id : cameraIdList) {
try {
CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(id);
Set<String> physicalCameraIds = cameraCharacteristics.getPhysicalCameraIds();
Log.d(TAG, "逻辑ID:" + id + " 下的物理ID: " + Arrays.toString(physicalCameraIds.toArray()));
if (physicalCameraIds.size() >= 2) {
dualCamera.setLogicCameraId(id);
Object[] objects = physicalCameraIds.toArray();
//获取前两个物理摄像头作为双镜头
dualCamera.setPhysicsCameraId1(String.valueOf(objects[0]));
dualCamera.setPhysicsCameraId2(String.valueOf(objects[1]));
return dualCamera;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return null;
}
二、打开多个物理摄像头
第一步 先开对应的逻辑摄像头
//开启摄像头
public void openCamera(){
HandlerThread thread = new HandlerThread("DualCamera");
thread.start();
handler = new Handler(thread.getLooper());
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
//权限检查
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//否则去请求相机权限
ActivityCompat.requestPermissions(context,new String[]{Manifest.permission.CAMERA},PERMISSIONS_REQUEST_CODE);
return;
}
manager.openCamera(dualCamera.getLogicCameraId(),AsyncTask.SERIAL_EXECUTOR, cameraOpenCallBack);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
第二步、在开启的回调中指定打开对应的物理摄像头
//打开相机时候的监听器,通过他可以得到相机实例,这个实例可以创建请求建造者
private CameraDevice.StateCallback cameraOpenCallBack = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
Log.d(TAG, "相机已经打开");
//当逻辑摄像头开启后, 配置物理摄像头的参数
config(cameraDevice);
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "相机连接断开");
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
Log.d(TAG, "相机打开失败");
}
};
/**
* 配置摄像头参数
* @param cameraDevice
*/
public void config(CameraDevice cameraDevice){
try {
//构建输出参数 在参数中设置物理摄像头
List<OutputConfiguration> configurations = new ArrayList<>();
CaptureRequest.Builder mPreViewBuidler = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//配置第一个物理摄像头
SurfaceTexture texture = textureView1.getSurfaceTexture();
OutputConfiguration outputConfiguration = new OutputConfiguration(new Surface(texture));
outputConfiguration.setPhysicalCameraId(dualCamera.getPhysicsCameraId1());
configurations.add(outputConfiguration);
mPreViewBuidler.addTarget(Objects.requireNonNull(outputConfiguration.getSurface()));
//配置第2个物理摄像头
SurfaceTexture texture2 = textureView2.getSurfaceTexture();
OutputConfiguration outputConfiguration2 = new OutputConfiguration(new Surface(texture2));
outputConfiguration2.setPhysicalCameraId(dualCamera.getPhysicsCameraId2());
configurations.add(outputConfiguration2);
mPreViewBuidler.addTarget(Objects.requireNonNull(outputConfiguration2.getSurface()));
//注册摄像头
SessionConfiguration sessionConfiguration = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
configurations,
AsyncTask.SERIAL_EXECUTOR,
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
try {
cameraSession = cameraCaptureSession;
cameraCaptureSession.setRepeatingRequest(mPreViewBuidler.build(), null, handler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
}
);
cameraDevice.createCaptureSession(sessionConfiguration);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
完成上面的操作,基本上就可以用camera2 打开两颗摄像头了, 文章写的比较粗略。
附上一份demo.
这个demo貌似有点问题, 授相机权限后需要重启应用才可以开启相机, 不过很明显我不准备修复它了。
最后付上一张 Huawei Mate30 的开双镜