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 ...")
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With