22

In the latest alpha (alpha08) I can't seem to figure out how to correctly configure everything so that the my Analyzer runs normally. I can see it work once, then it never runs again.

For various reasons I need to use a TextureView so I cannot swap to CameraView, etc.

I'm almost positive it's due to something related to the Futures, but I can't seem to nail it down.

I'm fresh out of ideas. Any thoughts/help appreciated.

I've configured my Application class with the following:

override fun getCameraXConfig(): CameraXConfig {
    return Camera2Config.defaultConfig()
}

And then the following is my MainActivity code (layout is just a single TextureView inside a ConstraintLayout):

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.TextureView
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {

    private lateinit var viewFinder: TextureView
    private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
    private val executor = Executors.newSingleThreadExecutor()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        viewFinder = findViewById(R.id.view_finder)     // TextureView
        startCamera()
    }

    private fun startCamera() {
        val preview = Preview.Builder().apply {
            setTargetResolution(Size(640, 480))
        }.build()
        preview.setPreviewSurfaceProvider { resolution, surfaceReleaseFuture ->
            viewFinder.surfaceTexture.setDefaultBufferSize(resolution.width, resolution.height)
            val surface = Surface(viewFinder.surfaceTexture)
            surfaceReleaseFuture.addListener(
                Runnable {
                    surface.release()
                    viewFinder.surfaceTexture.release()
                },
                ContextCompat.getMainExecutor(this)
            )
            CallbackToFutureAdapter.getFuture<Surface> { completer -> completer.set(surface) }
        }

        val analyzer = ImageAnalysis.Builder().build()
        val analyzerUseCase = analyzer.apply {
            setAnalyzer(executor, MyTestAnalyzer())
        }

        val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
        cameraProviderFuture.addListener(Runnable {
            val cameraProvider = cameraProviderFuture.get()
            cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, analyzerUseCase, preview)
        }, ContextCompat.getMainExecutor(this))
    }
}

private class MyTestAnalyzer : ImageAnalysis.Analyzer {
    override fun analyze(image: ImageProxy) {
        Log.d("Sandbox", "### Would analyze the image here ...")
    }
}
Luke
  • 4,675
  • 3
  • 23
  • 33

1 Answers1

69

I decided to answer my own question here for posterity as I got the answer from another channel.

There was a recent change to the API that means you need to call ImageProxy#close manually. CameraX used to call this automatically. From the documentation:

It is the responsibility of the application to close the image once done with it. If the images are not closed then it may block further images from being produced (causing the preview to stall) or drop images as determined by the configured backpressure strategy. The exact behavior is configurable via ImageAnalysis.Builder.setBackpressureStrategy(int).

This change allows for more flexibility regarding how you can do frame analysis (e.g.: multi-frame analysis) as you now have total control over when the frame is cleared.

So your Analyzer code should be:

override fun analyze(image: ImageProxy) {
    Log.d("Sandbox", "### Would analyze the image here ...")
    image.close()
}

Full details here: https://developer.android.com/training/camerax/analyze.

Luke
  • 4,675
  • 3
  • 23
  • 33
  • I'mg gettimg a "NoSuchMethodError: No static method metafactory" error based on the "cameraProviderFuture = ProcessCameraProvider.getInstance(this)" line. Do you know why that is? – Erick Adam Jun 24 '20 at 17:24
  • I had the same problem and this solution is worked! Thank you. – delayKg Sep 23 '20 at 07:41