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

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

相关推荐
雨白8 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空11 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日13 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑13 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟17 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡18 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0018 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体