Search code examples
androidandroid-studiokotlinandroid-cameraandroid-camera2

Error while trying to capture Image using Android Camera2 API : CaptureRequest contains unconfigured Input/Output Surface


I am trying to capture image using Camera2 API of Android. However, I am having this error when I try to add an ImageReader's surface as a listening surface. I have tried looking into the source code of the API and it looks like the error is thrown when a Surface cannot be converted into a Stream. This can be looked in Android's source in CaptureRequest.java near line 738.

My code is following.

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 })
    }

The following is the stack trace for the error.

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)


Solution

  • As pointed out by @Husayn in his comments, the wrong target was being passed to the capture request. The following changes solved the problem.

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