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

本来是想通过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、效果

image-20230615220353385

5、小坑

  • 实测在小米10手机,先开启后摄,再开启前摄,前摄无法打开=》异常。先开前摄,再开后摄正常。

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

项目地址,点我跳战,关键类:Camera2Activity

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYdrwCHl' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片