Search code examples
androidkotlinandroid-camera2image-reader

ImageReader in Kotlin & Camera 2 - saving .jpg file from a camera reader surface


I am trying to get a photo out of my ImageReader.surface that i have added as a target on my capture request for a CaptureSession, I am struggling on this as i have tried to use the imageReader.acquireLatestImage() but this alawys reference me to a null object wither in a new imagewriter.newInstance(...).dequeueinputimage which i use ImageReader.surface in it or when i aquire the image from the reader, it seems a problem with my imagereader surface, but i have tried so many ways and failed to figure out how to simply pull an image out of this capture with knowing that im closing my images on the reader ImageAVailablelistener:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.amjadshuk.roadanalysia, PID: 18251
java.lang.NullPointerException: Attempt to invoke virtual method 'android.media.Image$Plane[] android.media.Image.getPlanes()' on a null object reference
    at com.amjadshuk.roadanalysia.MainActivity.onCreate$lambda-14(MainActivity.kt:482)
    at com.amjadshuk.roadanalysia.MainActivity.$r8$lambda$NXtavG2wfAwvFvETtEdf5FaiLSE(Unknown Source:0)
    at com.amjadshuk.roadanalysia.MainActivity$$ExternalSyntheticLambda0.onImageAvailable(Unknown Source:2)
    at android.media.ImageReader$ListenerHandler.handleMessage(ImageReader.java:812)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:246)
    at android.app.ActivityThread.main(ActivityThread.java:8506)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)

My whole code:

   @RequiresApi(Build.VERSION_CODES.P)
fun camera3() {


    val handlerThread = HandlerThread("",HandlerThread.NORM_PRIORITY).apply {
        start()
    }
    val handler =  HandlerCompat.createAsync(handlerThread.looper).apply {
        post {
            //-----------------------------------------------------------------

            GlobalScope.launch {
                Looper.prepare()

                val cm = getSystemService(Context.CAMERA_SERVICE) as CameraManager

                val cc: CameraDevice.StateCallback = object : CameraDevice.StateCallback() {

                    @RequiresApi(Build.VERSION_CODES.Q)
                    override fun onOpened(cm: CameraDevice) {
                        findViewById<TextView>(R.id.textView29).text = "Camera 1 opened"


                        val surface1 = findViewById<SurfaceView>(R.id.surfaceView3).holder.surface


                        val cr = cm.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {


                            addTarget(surface1)
                            addTarget(imageReader.surface)

                        }.build()

                        val outputConfiguration = OutputConfiguration(surface1)
                        val outputConfiguration2 = OutputConfiguration(imageReader.surface)
                        val out = mutableListOf(outputConfiguration, outputConfiguration2)


                        cm.createCaptureSession(
                            SessionConfiguration(SessionConfiguration.SESSION_REGULAR,
                                out,
                                mainExecutor,
                                object : CameraCaptureSession.StateCallback() {
                                    override fun onConfigured(p0: CameraCaptureSession) {


                                        p0.setRepeatingRequest(
                                            cr, object : CameraCaptureSession.CaptureCallback() {

                                                override fun onCaptureCompleted(
                                                    session: CameraCaptureSession,
                                                    request: CaptureRequest,
                                                    result: TotalCaptureResult
                                                ) {
                                                    super.onCaptureCompleted(
                                                        session,
                                                        request,
                                                        result
                                                    )





                                                    Toast.makeText(applicationContext,"capture completed",Toast.LENGTH_SHORT).show()

                                                }
                                                                                                },
                                            null
                                        )

                                    }

                                    override fun onConfigureFailed(p0: CameraCaptureSession) {

                                    }

                                })
                        )
                    }

                    override fun onDisconnected(cm: CameraDevice) {


                    }

                    override fun onError(cm: CameraDevice, p1: Int) {


                    }


                }

                if (ActivityCompat.checkSelfPermission(
                        applicationContext,
                        Manifest.permission.CAMERA
                    ) != PackageManager.PERMISSION_GRANTED
                ) {


                    [email protected](arrayOf(Manifest.permission.CAMERA), 9998)


                }



                if (cm.cameraIdList.isNotEmpty()) {
                    cm.getCameraCharacteristics(cm.cameraIdList.first())
                        .get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
                    cm.openCamera(cm.cameraIdList.first(), mainExecutor, cc)
                }

                val cameraAccessException = CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED)
                cameraAccessException.printStackTrace()
                val message = cameraAccessException.message!!
                Log.e("camera2", "camera2: ${message}", cameraAccessException.fillInStackTrace())

                Looper.loop()
            }



        }
    }
        }

OnCreate:

 imageReader.setOnImageAvailableListener({

            it.acquireLatestImage().close()
        val image = imageReader.acquireLatestImage()
      val byteArray =   image.planes.last().buffer.array()
        val file = FileOutputStream(File.createTempFile("image",".jpg",filesDir))
        file.write(byteArray)

    },null)

Solution

  •             it.acquireLatestImage().close()
            val image = imageReader.acquireLatestImage()
    

    If this code is what you actually have, you're acquiring the latest available image and then immediately closing it. And then you try to get another image right off. It's unlikely a second image will have been produced by the camera at that point, so you'll get back null.

    Just remove the first acquireLatestImage line.