尝试使用 Android Camera2 API 捕获图像时出错:CaptureRequest 包含未配置的输入/输出表面

Posted

技术标签:

【中文标题】尝试使用 Android Camera2 API 捕获图像时出错:CaptureRequest 包含未配置的输入/输出表面【英文标题】:Error while trying to capture Image using Android Camera2 API : CaptureRequest contains unconfigured Input/Output Surface 【发布时间】:2021-03-26 13:36:33 【问题描述】:

我正在尝试使用 android 的 Camera2 API 捕获图像。但是,当我尝试将ImageReader 的表面添加为监听表面时,我遇到了这个错误。我已经尝试查看 API 的源代码,当 Surface 无法转换为 Stream 时,似乎会引发错误。这可以在 Android 的源代码中查看 CaptureRequest.java 靠近第 738 行。

我的代码如下。

private fun startCameraSession() 
        val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        if (cameraManager.cameraIdList.isEmpty()) 
            // no cameras
            return
        
        val firstCamera = cameraManager.cameraIdList[1]
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) 
            val toast = Toast(this)
            toast.setText("No Permission Granted!")
            toast.show()
            return
        
        cameraManager.openCamera(firstCamera, object: CameraDevice.StateCallback() 
            override fun onDisconnected(p0: CameraDevice)  
            override fun onError(p0: CameraDevice, p1: Int)  

            override fun onOpened(cameraDevice: CameraDevice) 
                // use the camera
                val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraDevice.id)

                cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]?.let  streamConfigurationMap ->
                    streamConfigurationMap.getOutputSizes(ImageFormat.YUV_420_888)?.let  yuvSizes ->
                        val previewSize = yuvSizes.last()
                        // cont.
                        val displayRotation = windowManager.defaultDisplay.rotation
                        val swappedDimensions = areDimensionsSwapped(displayRotation, cameraCharacteristics)
                        // swap width and height if needed
                        val rotatedPreviewWidth = if (swappedDimensions) previewSize.height else previewSize.width
                        val rotatedPreviewHeight = if (swappedDimensions) previewSize.width else previewSize.height

                        surfaceView.holder.setFixedSize(rotatedPreviewWidth, rotatedPreviewHeight)

                        // Configure Image Reader
                        val imageReader = ImageReader.newInstance(rotatedPreviewWidth, rotatedPreviewHeight,
                            ImageFormat.YUV_420_888, 2)
                        imageReader.setOnImageAvailableListener(
                            val previewSurface = surfaceView.holder.surface

                            imageReader.setOnImageAvailableListener(
                                Log.d("camera","setOnImageAvailableListener")
                                imageReader.acquireLatestImage()?.let  image ->
                                    Log.d("camera","acquireLatestImage")
                                
                            , Handler  true )

                        , Handler  true )

                        val previewSurface = surfaceView.holder.surface
                        val recordingSurface = imageReader.surface

                        val captureCallback = object : CameraCaptureSession.StateCallback()
                        
                            override fun onConfigureFailed(session: CameraCaptureSession) 

                            override fun onConfigured(session: CameraCaptureSession) 
                                // session configured
                                val previewRequestBuilder = cameraDevice.createCaptureRequest(TEMPLATE_PREVIEW).apply 
                                    addTarget(recordingSurface)
                                
                                session.setRepeatingRequest(
                                    previewRequestBuilder.build(),
                                    object: CameraCaptureSession.CaptureCallback() ,
                                    Handler  true 
                                )
                            
                        
                        cameraDevice.createCaptureSession(mutableListOf(previewSurface), captureCallback, Handler  true )
                    
                
            
        , Handler  true )
    

以下是错误的堆栈跟踪。

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.security.camerapractice, PID: 15957
    java.lang.IllegalArgumentException: CaptureRequest contains unconfigured Input/Output Surface!
        at android.hardware.camera2.CaptureRequest.convertSurfaceToStreamId(CaptureRequest.java:738)
        at android.hardware.camera2.impl.CameraDeviceImpl.submitCaptureRequest(CameraDeviceImpl.java:1179)
        at android.hardware.camera2.impl.CameraDeviceImpl.setRepeatingRequest(CameraDeviceImpl.java:1227)
        at android.hardware.camera2.impl.CameraCaptureSessionImpl.setRepeatingRequest(CameraCaptureSessionImpl.java:312)
        at com.security.camerapractice.MainActivity$startCameraSession$1$onOpened$$inlined$let$lambda$2.onConfigured(MainActivity.kt:179)
        at android.hardware.camera2.impl.CallbackProxies$SessionStateCallbackProxy.lambda$onConfigured$0$CallbackProxies$SessionStateCallbackProxy(CallbackProxies.java:53)
        at android.hardware.camera2.impl.-$$Lambda$CallbackProxies$SessionStateCallbackProxy$soW0qC12Osypoky6AfL3P2-TeDw.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

【问题讨论】:

我无法从您的 sn-p 中看出,但您是否在等待 SurfaceView 的表面准备好后再将其传递给相机? SurfaceView 的表面只能在其surfaceCreated() 和surfaceDestroyed() 回调之间使用。 不是SurfaceView的Surface。它是 ImageReader 的一个 Surface。 从 sn-p 看来,您正在使用错误的目标构建捕获会话,您正在传递 surfaceView 的表面,但您正在使用 imageReader 的表面构建捕获请求。 【参考方案1】:

正如@Husayn 在他的 cmets 中所指出的,错误的目标被传递给捕获请求。以下更改解决了该问题。

cameraDevice.createCaptureSession(mutableListOf(recordingSurface), captureCallback, Handler  true )

【讨论】:

以上是关于尝试使用 Android Camera2 API 捕获图像时出错:CaptureRequest 包含未配置的输入/输出表面的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Android的Camera2 API

Android Camera2 API:捕获视频而不预览

使用 Camera2(Android 版本 21)API 录制 60fps 视频

使用 Android Camera2 API 进行人脸检测和画圆

如何在 Camera2 API Android 5.0 中获取单个预览帧?

Android Camera API/Camera2 API 相机预览及滤镜贴纸等处理