在Android设备中,做预览拍照的需求的时候,我们会指定
CameraSelector DEFAULT_FRONT_CAMERA前置
或者后置CameraSelector DEFAULT_BACK_CAMERA
如果你使用的是平板或者工业平板,那么就会遇到多摄像头以及外置摄像头问题,简单的指定前后置是不行的,哪怕是USB 外置也有挂载两个的情况 ,本文就是写于两个外置的特殊情况作为验证,所以我们必须能获取所有可用的摄像头,我们以最新的预览拍照控件为例(
单摄像头推荐PreviewView+CameraSelector 与挂载的位置相关 且绑定了activity lifecycle
多摄像头推荐Surfaceview+CameraId 与id相关 surfaceview方式获取id在下面
PreviewView
为例初始化的时候需要使用
CameraSelector去绑定拍照的UseCase
java
@MainThread
@NonNull
public Camera bindToLifecycle(@NonNull LifecycleOwner lifecycleOwner,
@NonNull CameraSelector cameraSelector,
@NonNull UseCase... useCases) {
if (getCameraOperatingMode() == CAMERA_OPERATING_MODE_CONCURRENT) {
throw new UnsupportedOperationException("bindToLifecycle for single camera is not "
+ "supported in concurrent camera mode, call unbindAll() first");
}
setCameraOperatingMode(CAMERA_OPERATING_MODE_SINGLE);
Camera camera = bindToLifecycle(lifecycleOwner, cameraSelector, null, emptyList(),
useCases);
return camera;
}
因为我们需要获取所有可用的摄像头类似旧版的CameraId
将下面代码放入activity获取
Kotlin
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
LogUtils.log("摄像头结果返回")
val cameraProvider = cameraProviderFuture.get()
var textC = ""
val cameraInfos = cameraProvider.availableCameraInfos
var isSupportBack = false
var isSupportFront = false
var isExternal = false
var isUnknow = false
for (cameraInfo in cameraInfos) {
when (cameraInfo.lensFacing) {
CameraSelector.LENS_FACING_FRONT -> {
isSupportFront = true
textC += "\n前置 lensFacing:[${cameraInfo.lensFacing},${cameraInfo.cameraSelector}]"
}
CameraSelector.LENS_FACING_BACK -> {
isSupportBack = true
textC += "\n后置 lensFacing:[${cameraInfo.lensFacing},${cameraInfo.cameraSelector}]"
}
CameraSelector.LENS_FACING_EXTERNAL -> {
isExternal = true
textC += "\n外置 lensFacing:[${cameraInfo.lensFacing},${cameraInfo.cameraSelector}]"
}
CameraSelector.LENS_FACING_UNKNOWN -> {
isUnknow = true
textC += "\n未知 lensFacing:[${cameraInfo.lensFacing},${cameraInfo.cameraSelector}]"
}
}
}
LogUtils.logI(
"init",
"设备支持情况:后置:${isSupportBack},前置:${isSupportFront},外置:${isExternal},未知:${isUnknow}"
)
binding.tvCheckResult.text = "len:$textC\n" +
"设备支持情况:后置:${isSupportBack},前置:${isSupportFront},外置:${isExternal},未知:${isUnknow}"
}, ContextCompat.getMainExecutor(this))
工业平板做的产品摄像头一般固定获取之后指定即可,也可以自己编辑优先级逻辑,下面是插两个摄像头的例子
可以使用上述打印的具体摄像头(推荐) 也可以自己bulid,自己build有局限性比如两个外置,你使用2去build就不行了
Kotlin
val selector: CameraSelector = if (isSupportBack) {
CameraSelector.DEFAULT_BACK_CAMERA
} else if (isSupportFront) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else if (isExternal) {
CameraSelector.Builder().requireLensFacing(2)
.build()
} else if (isUnknow) {
CameraSelector.Builder().requireLensFacing(1)
.build()
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
// 如果你使用的是surfaceview+cameraId 而且是多预览
Kotlin
val cameraManager =
activity.getSystemService(AppCompatActivity.CAMERA_SERVICE) as CameraManager
val cameraIds = cameraManager.cameraIdList
for (cameraId in cameraIds) {
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
}
明确了cameraId 其他通过预览的示例代码 我也传一下吧,由于每个摄像头是独立 有多少个预览就new多少个出来
Kotlin
class CameraUtils {
val TAG = "CameraUtils"
private var mCameraDevice: CameraDevice? = null
private var mCaptureSession: CameraCaptureSession? = null
private var mPreviewBuilder: CaptureRequest.Builder? = null
fun bindSurfaceView(
activity: Activity,
surfaceView: SurfaceView,
len: Int,
cId: String?
): String {
val cameraManager =
activity.getSystemService(AppCompatActivity.CAMERA_SERVICE) as CameraManager
surfaceView.holder.setFixedSize(9999, 9999)
LogUtils.logI(TAG, "bindSurfaceView")
val cameraId = cId ?: getCameraId(cameraManager, len)
try {
LogUtils.log("len:$len cameraId:$cameraId")
if (TextUtils.isEmpty(cameraId)) {
LogUtils.logE(TAG, "cameraId null")
return "cameraId null"
}
if (ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
LogUtils.logE(TAG, "没有权限")
return "没有权限"
}
cameraManager.openCamera(cameraId!!, object : CameraDevice.StateCallback() {
override fun onOpened(camera: CameraDevice) {
LogUtils.logE(TAG, "onOpened cameraId:$cameraId")
mCameraDevice = camera
createCaptureSession(camera, surfaceView)
}
override fun onDisconnected(camera: CameraDevice) {
LogUtils.logE(TAG, "onDisconnected cameraId:$cameraId")
}
override fun onError(camera: CameraDevice, error: Int) {
LogUtils.logE(TAG, "onError:$error cameraId:$cameraId")
}
}, null)
} catch (e: CameraAccessException) {
e.printStackTrace()
LogUtils.logE(TAG, "onError:$e")
return "onError:$e"
}
return "cameraId:$cameraId"
}
/**
* 创建CaptureSession
*/
private fun createCaptureSession(camera: CameraDevice, surfaceView: SurfaceView) {
try {
surfaceView.run {
val surfaceHolder: SurfaceHolder = getHolder()
surfaceHolder.setFixedSize(getWidth(), getHeight())
val surfaces: MutableList<Surface> = ArrayList()
surfaces.add(surfaceHolder.surface)
mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
mPreviewBuilder?.addTarget(surfaceHolder.surface)
camera.createCaptureSession(
surfaces,
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
mCaptureSession = session
updatePreview()
}
override fun onConfigureFailed(session: CameraCaptureSession) {
// 配置会话失败
}
},
null
)
}
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
private fun updatePreview() {
if (mCameraDevice == null) {
LogUtils.logE(TAG, "mCameraDevice null")
return
}
if (mCaptureSession == null) {
LogUtils.logE(TAG, "mCaptureSession null")
return
}
if (mPreviewBuilder == null) {
LogUtils.logE(TAG, "mPreviewBuilder null")
return
}
LogUtils.logI(TAG, "updatePreview.")
try {
mCaptureSession?.setRepeatingRequest(mPreviewBuilder!!.build(), null, null)
} catch (e: Exception) {
e.printStackTrace()
LogUtils.logE(TAG, "updatePreview: $e")
}
}
@Throws(CameraAccessException::class)
private fun getCameraId(cameraManager: CameraManager, len: Int): String? {
val cameraIds = cameraManager.cameraIdList
for (cameraId in cameraIds) {
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
// CameraCharacteristics.LENS_FACING_BACK CameraCharacteristics.LENS_FACING_FRONT
if (facing != null && facing == len) {
return cameraId
}
}
return null
}
}
比如用RecyclerView 多个item预览 可以这样初始化
Kotlin
fun getOneUtils(index: Int): CameraUtils {
var cache = mCameraUnitsMap[index]
if (cache == null) {
cache = CameraUtils()
mCameraUnitsMap[index] = cache
}
return cache
}
使用
Kotlin
val text = getOneUtils(position).bindSurfaceView(
this@CameraInfoActivity,
holder.getView(R.id.surfaceView),
len, cameraId
)
如果是uniapp同学需要买相关插件的可以留言联系