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

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

相关推荐
2501_916008894 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
我科绝伦(Huanhuan Zhou)4 小时前
MySQL一键升级脚本(5.7-8.0)
android·mysql·adb
怪兽20145 小时前
Android View, SurfaceView, GLSurfaceView 的区别
android·面试
龚礼鹏6 小时前
android 图像显示框架二——流程分析
android
消失的旧时光-19436 小时前
kmp需要技能
android·设计模式·kotlin
帅得不敢出门7 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
雨白8 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
TimeFine9 小时前
kotlin协程 容易被忽视的CompletableDeferred
android
czhc114007566311 小时前
Linux1023 mysql 修改密码等
android·mysql·adb
GOATLong11 小时前
MySQL内置函数
android·数据库·c++·vscode·mysql