Reputation: 2128
I have a flow with my custom CameraX like this:
The question is when running all the process (in step 3)
have a delayed 2 seconds
and the camera preview still live
(not freeze
or lock
). How to make camera preview freeze or lock when running the process
?
This is my code to running camera preview in Camera X:
class CameraFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_camera, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewFinder.post { setupCamera() }
}
private fun setupCamera() {
CameraX.unbindAll()
CameraX.bindToLifecycle(
this,
buildPreviewUseCase(),
buildImageCaptureUseCase()
)
}
private fun buildPreviewUseCase(): Preview {
val preview = Preview(
UseCaseConfigBuilder.buildPreviewConfig(
viewFinder.display
)
)
preview.setOnPreviewOutputUpdateListener { previewOutput ->
updateViewFinderWithPreview(previewOutput)
correctPreviewOutputForDisplay(previewOutput.textureSize)
}
return preview
}
private fun buildImageCaptureUseCase(): ImageCapture {
val capture = ImageCapture(
UseCaseConfigBuilder.buildImageCaptureConfig(
viewFinder.display
)
)
cameraCaptureImageButton.setOnClickListener {
capture.takePicture(
FileCreator.createTempFile(JPEG_FORMAT),
Executors.newSingleThreadExecutor(),
object : ImageCapture.OnImageSavedListener {
override fun onImageSaved(file: File) {
// I want make a freeze camera preview when execute this before launch *launchGalleryFragment(path)*
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
val rotatedBitmap = bitmap.rotate(90)
val croppedImage = cropImage(rotatedBitmap, viewFinder, rectangle)
val path = saveImage(croppedImage)
requireActivity().runOnUiThread {
launchGalleryFragment(path)
}
}
override fun onError(
imageCaptureError: ImageCapture.ImageCaptureError,
message: String,
cause: Throwable?
) {
Toast.makeText(requireContext(), "Error: $message", Toast.LENGTH_LONG)
.show()
Log.e("CameraFragment", "Capture error $imageCaptureError: $message", cause)
}
})
}
return capture
}
private fun launchGalleryFragment(path: String) {
val action = CameraFragmentDirections.actionLaunchGalleryFragment(path)
findNavController().navigate(action)
}
}
Upvotes: 5
Views: 6702
Reputation: 606
Maybe you can try to unbind the preview use case :
version 1.0.0-alpha06: CameraX.unbind(preview);
version > 1.0.0-alpha07: cameraProvider.unbind(preview);
In your case, you need to save the preview use case into a variable to then unbind it:
// Declare the preview use case variable (as in the CameraXBasic example)
private var preview: Preview? = null
Then instantiate the variable (like you did):
private fun buildPreviewUseCase(): Preview {
preview = Preview(
UseCaseConfigBuilder.buildPreviewConfig(
viewFinder.display
)
)
preview.setOnPreviewOutputUpdateListener { previewOutput ->
updateViewFinderWithPreview(previewOutput)
correctPreviewOutputForDisplay(previewOutput.textureSize)
}
return preview
}
Then, when you want to freeze the preview just unbind the use case:
CameraX.unbind(preview);
EDIT As @Billda said in this post : CameraX - crash when unbinding Preview UseCase :
To freeze a preview you should not unbind Preview usecase. There may API for that in the future, but currently the recommended way is to store latest frame from ImageAnalysis and put it to ImageView overlapping the preview.
So I decided to update my answer to give another solution implementing an analyser with ImageAnalysis (1.0.0-beta02).
1- Create the FreezeAnalyzer class:
class FreezeAnalyzer(private val callback: FreezeCallback) : ImageAnalysis.Analyzer {
private var flag = false
override fun analyze(image: ImageProxy) {
if(flag){
flag = false
val bitmap = toBitmap(image)
callback.onLastFrameCaptured(bitmap)
}
image.close()
}
fun freeze(){
flag = true
}
private fun toBitmap(image: ImageProxy): Bitmap {
// Convert the imageProxy to Bitmap
// ref https://stackoverflow.com/questions/56772967/converting-imageproxy-to-bitmap
// ISSUE, on my android 7 when converting the imageProxy to Bitmap I have a problem with the colors...
var bitmap = ...
// Rotate the bitmap
val rotationDegrees = image.imageInfo.rotationDegrees.toFloat()
if (rotationDegrees != 0f) {
val matrix = Matrix()
matrix.postRotate(rotationDegrees)
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
return bitmap
}
}
2- XML
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_view"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
3- Initialize the imageAnalyser
val resolutionSize = Size(preview_view.width, preview_view.height)
// Set up analyser
imageAnalysis = ImageAnalysis.Builder().apply {
setTargetResolution(resolutionSize)
setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
}.build()
val analyzer = FreezeAnalyzer(object : FreezeCallback {
override fun onLastFrameCaptured(bitmap: Bitmap) {
runOnUiThread {
preview_view.visibility = View.INVISIBLE
image_view.visibility = View.VISIBLE
image_view.setImageBitmap(bitmap)
}
}
})
imageAnalysis.setAnalyzer(executor, analyzer)
4- Bind the imageAnalysis use case
try {
val camera = cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview,
imageAnalysis,
imageCapture
)
preview.setSurfaceProvider(preview_view.createSurfaceProvider(camera.cameraInfo))
}
5- Capture the picture
btn_capture.setOnClickListener {
file = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val outputFileOptions: ImageCapture.OutputFileOptions =
ImageCapture.OutputFileOptions.Builder(file!!).build()
analyzer.freeze()
imageCapture.takePicture(outputFileOptions, executor, onImageSavedCallback)
}
6- Release
btn_release.setOnClickListener {
preview_view.visibility = View.VISIBLE
image_view.visibility = View.INVISIBLE
}
I hope it helps, I'm not an expert so if you have some improvements you are welcome !
Upvotes: 6