本来是想经过CameraX完成一同预览多个摄像头,经过官网文档介绍,在CameraX 1.3 后经过ConcurrentCamera运转多个摄像头,但实践在小米10(Android 13)运转,报错当时设备不支撑ConcurrentCamera,代码CameraProvider.availableConcurrentCameraInfos查询也是回来数量0,表示设备不支撑。

讨教ChatGPT回答,来进行编写,回答能够经过代码创立多个previewrequireLensFacing,但是实践运转时不可行的。程序会报下面代码问题,选择摄像头设备反常。

val cameraSelector =builder
    .requireLensFacing(CameraSelector.LENS_FACING_BACK)
    .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
    .build()

因而个人下定义是在cameraX 1.3.0-alpha07前应该是不支撑预览多摄像头的。如果有小伙伴验证OK,期望能够奉告,多谢。

故采用Camera2来完成多摄像头一同预览。

Camera2 一同预览摄像头

记住先请求权限,以及动态请求!!

    <uses-feature android:name="android.hardware.camera.any" />
    <uses-permission android:name="android.permission.CAMERA" />

记住先请求权限,以及动态请求!!

1、判别设备是否支撑摄像头

fun isSupportCamera(): Boolean {
    initCameraManager()
    return cameraManager!!.cameraIdList.isNotEmpty()
}

initCameraManager首要是初始化CameraManager目标cameraManager。咱们经过cameraIdList列表是否空来判别是否有摄像头。

private fun initCameraManager() {
    if (cameraManager == null) {
        cameraManager = getApplication<Application>().getSystemService(AppCompatActivity.CAMERA_SERVICE) as 			CameraManager
    }
}

2、获取摄像头列表

咱们遍历第1步获取到的摄像头ID列表,然后经过getCameraCharacteristics查询该摄像头相关的数据,封装到NCameraInfo目标中。这儿咱们只查询几个简略的信息。

fun getCameraListInfo() {
    initCameraManager()
    if (cameraManager.cameraIdList.isNotEmpty()) {
        for (cameraId in cameraManager.cameraIdList) {
            val cameInfo = NCameraInfo()
            val characteristics = cameraManager.getCameraCharacteristics(cameraId)
            val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
            cameInfo.id = cameraId
            cameInfo.face ="${ getFaceStr(facing)},CameraId:${cameraId}"
            cameraMap[cameraId] = cameInfo
        }
        cameraInfo.value = cameraMap.values.toList()
    }
}

3、翻开摄像头

翻开摄像头非常简略,只需要调用openCamera函数即可,首要是stateCallback函数的完成。其中handler,是用来切换到主线程var handler = Handler(Looper.getMainLooper())

fun openCamera(cameraId: String) {
    initCameraManager()
    cameraManager?.openCamera(cameraId, stateCallback, handler)
}

咱们一同看看stateCallback函数的完成。也便是当咱们翻开摄像头,摄像头相关状况会经过下面三个函数进行回调,因为这儿采用ViewModel方法,所以会多一份回调到Activity。不用着急,最终有完好代码。

   private val stateCallback=object : StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            cameraMap[camera.id]?.apply {
                cameraDevice = camera
                state = 1
                cameraCallback?.onCameraOpen(this)
            }
        }
        override fun onDisconnected(camera: CameraDevice) {
            cameraMap[camera.id]?.apply {
                cameraDevice = camera
                state = 0
                cameraCallback?.onCameraClose(this)
            }
        }
        override fun onError(camera: CameraDevice, error: Int) {
            Log.e(TAG, "camera ${camera.id} error code:${error}")
            cameraMap[camera.id]?.apply {
                cameraDevice = camera
                state = 3
                cameraCallback?.onCameraError(this,error)
            }
        }
    }

咱们查看Activity中的完成。onCameraOpen函数首要动态创立TextureView目标,添加到界面中,用于预览摄像头内容。

	 override fun onCameraOpen(camera: NCameraInfo) {
        adapter.notifyItemChanged(adapter.items.indexOf(camera))
        //创立TextureView
        val textureView = TextureView(this)
        textureView.id = View.generateViewId()
        camera.previewId=textureView.id
        val layoutParams = LinearLayout.LayoutParams(previewWidth, LayoutParams.MATCH_PARENT)
        viewBinding.llCameraPreview.addView(textureView, layoutParams)
		//textureview 与摄像头绑定
        textureView.surfaceTextureListener=object:SurfaceTextureListener{
            override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
               //创立Surface并用于摄像头烘托
               val surface = Surface(textureView.surfaceTexture)
               val builder = camera.cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)!!
               builder.addTarget(surface)
                camera.cameraDevice?.createCaptureSession(listOf(surface), object : StateCallback() {
                    override fun onConfigured(session: CameraCaptureSession) {
                        session.setRepeatingRequest(builder.build(),null,model.handler)
                    }
                    override fun onConfigureFailed(session: CameraCaptureSession) {
                    }
                }, model.handler)
            }
            override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
                Log.d(TAG,"onSurfaceTextureSizeChanged")
            }
            override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
                Log.d(TAG,"onSurfaceTextureDestroyed")
                return true
            }
            override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
                //Log.d(TAG,"onSurfaceTextureUpdated")
            }
        }
    }
    override fun onCameraClose(camera: NCameraInfo) {
        Log.d(TAG,"onCameraClose:${camera}")
        adapter.notifyItemChanged(adapter.items.indexOf(camera))
        camera.cameraDevice?.close()
        val view=viewBinding.llCameraPreview.findViewById<TextureView>(camera.previewId)
        viewBinding.llCameraPreview.removeView(view)
    }
    override fun onCameraError(camera: NCameraInfo, error: Int) {
        Log.e(TAG,"onCameraError:${camera}${error}")
        adapter.notifyItemChanged(adapter.items.indexOf(camera))
        camera.cameraDevice?.close()
    }

4、效果

Camera2 同时预览多个摄像头,CameraX不行?

5、小坑

  • 实测在小米10手机,先敞开后摄,再敞开前摄,前摄无法翻开=》反常。先开前摄,再开后摄正常。

  • 小米11、诺基亚x7实测正常。

项目地址,点我跳战,要害类:Camera2Activity