安卓获取所有可用摄像头并指定预览

在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同学需要买相关插件的可以留言联系

相关推荐
阿巴斯甜5 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker5 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95276 小时前
Andorid Google 登录接入文档
android
黄林晴8 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab20 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android