[译] Android 的多摄像头支持
override fun onError(device: CameraDevice, error: Int) {onDisconnected(device)}}, null)
第一个并不是最好的选择
上述代码目前看起来没什么问题。如果我们所需要的只是一个能够打开第一个存在的摄像头的应用程序,那么它在大部分的 Android 手机上都有效。但是考虑到以下场景:
如果设备没有摄像头,那么应用程序会崩溃。这看起来似乎不太可能,但是要知道 Android 运用在各种设备上,包括 Android Things、Android Wear 和 Android TV 等这些有数百万用户的设备。
如果设备至少有一个后置摄像头,它将会映射到列表中的第一个摄像头。但是当应用程序运行在没有后置摄像头的设备上,比如 PixelBooks 或者其他一些 ChromeOS 的笔记本电脑,将会打开唯一一个前置摄像头。
那么我们应该怎么做?检查摄像头列表和摄像头特性:
val cameraIdList = cameraManager.cameraIdList // may be emptyval characteristics = cameraManager.getCameraCharacteristics(cameraId)val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
变量 cameraLensFacing
有以下取值:
更多有关摄像头配置的信息,请查看文档.
合理的默认设置
根据应用程序的使用情况,我们希望默认打开特定的相机镜头配置(如果可以提供这样的功能)。比如,自拍应用程序很可能想要打开前置摄像头,而一款增强现实类的应用程序应该希望打开后置摄像头。我们可以将这样的一个逻辑包装成一个函数,它可以正确地处理上面提到的情况:
fun getFirstCameraIdFacing(cameraManager: CameraManager,facing: Int = CameraMetadata.LENS_FACING_BACK): String? {val cameraIds = cameraManager.cameraIdList// Iterate over the list of cameras and return the first one matching desired// lens-facing configurationcameraIds.forEach {val characteristics = cameraManager.getCameraCharacteristics(it)if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {return it}}// If no camera matched desired orientation, return the first one from the listreturn cameraIds.firstOrNull()}
切换摄像头
目前为止,我们讨论了如何基于应用程序的用途选择默认摄像头。很多相机应用程序还为用户提供切换摄像头的功能:
Google 相机应用中切换摄像头按钮
要实现这个功能,尝试从CameraManager.getCameraIdList()提供的列表中选择下一个摄像头,但是这并不是个好的方式。因为从 Android P 开始,我们将会看到在同样的情况下更多的设备有多个摄像头,甚至有通过 USB 连接的外部摄像头。如果我们想要提供给用户切换不同摄像头的 UI,建议(按照文档)是为每个可能的镜头配置选择第一个可用的摄像头。
尽管没有一个通用的逻辑可以用来选择下一个摄像头,但是下述代码适用于大部分情况:
fun filterCameraIdsFacing(cameraIds: Array<String>, cameraManager: CameraManager,facing: Int): List<String> {return cameraIds.filter {val characteristics = cameraManager.getCameraCharacteristics(it)characteristics.get(CameraCharacteristics.LENS_FACING) == facing}}
fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {// Get all front, back and external cameras in 3 separate listsval cameraIds = cameraManager.cameraIdListval backCameras = filterCameraIdsFacing(cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK)val frontCameras = filterCameraIdsFacing(cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT)val externalCameras = filterCameraIdsFacing(cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL)
// The recommended order of iteration is: all external, first back, first frontval allCameras = (externalCameras + listOf(backCameras.firstOrNull(), frontCameras.firstOrNull())).filterNotNull()
// Get the index of the currently selected camera in the listval cameraIndex = allCameras.indexOf(currCameraId)
// The selected camera may not be on the list, for example it could be an// external camera that has been
removed by the userreturn if (cameraIndex == -1) {// Return the first camera from the listallCameras.getOrNull(0)} else {// Return the next camera from the list, wrap around if necessaryallCameras.getOrNull((cameraIndex + 1) % allCameras.size)}}
这看起来可能有点复杂,但是我们需要考虑到大量的有不同配置的设备。
评论