How to switch to front camera on CameraX?
Asked Answered
T

5

19

I've followed the steps here to get CameraX setup, and now I am trying to get a front facing camera button working.

Here is my set up code:

private lateinit var preview: Preview

private fun startCamera() {

    // Create configuration object for the viewfinder use case
    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(CameraX.LensFacing.BACK)
    }.build()

    // Build the viewfinder use case
    preview = Preview(previewConfig)

    // Every time the viewfinder is updated, recompute layout
    preview.setOnPreviewOutputUpdateListener {

        // To update the SurfaceTexture, we have to remove it and re-add it
        val parent = viewFinder.parent as ViewGroup
        parent.removeView(viewFinder)
        parent.addView(viewFinder, 0)

        viewFinder.surfaceTexture = it.surfaceTexture
        updateTransform()
    }

    // Bind use cases to lifecycle
    CameraX.bindToLifecycle(this, preview)
}

When a user clicks the "switch" button I re-configure the preview to use the front camera, then reinitialize the Preview.

private fun initSwitchButton(view: View) {
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        val previewConfig = PreviewConfig.Builder().apply { setLensFacing(CameraX.LensFacing.FRONT) }.build()
        preview = Preview(previewConfig)
    }
}

However, this doesn't switch to the front camera. What am I missing?

Telespectroscope answered 16/5, 2019 at 23:8 Comment(2)
Since you are creating a new Preview instance, wouldn't you need the setOnPreviewOutputUpdateListener and bindToLifecycle() bits from your first code snippet to be run on this new Preview? (in addition to cleaning up the old Preview instance, if that wasn't done already) I haven't used CameraX yet, so I may be off-base -- this is just comparing and contrasting your two code snippets.Grot
Just found an example, github.com/android/camera/blob/master/CameraXBasic/app/src/main/… and it does look like you need to call bindToLifecycle once again. I will update with an answer when I find a clean solution!Telespectroscope
T
10

It looks like the recommended way to achieve this is to store the LensFacing position as an instance variable and then call bindToLifecycle() to switch the camera.

Here is a code snippet that worked for me:

private var lensFacing = CameraX.LensFacing.BACK
private var imageCapture: ImageCapture? = null

@SuppressLint("RestrictedApi")
private fun startCamera() {
    bindCameraUseCases()

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
            CameraX.LensFacing.BACK
        } else {
            CameraX.LensFacing.FRONT
        }
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing)
            bindCameraUseCases()
        } catch (exc: Exception) {
            // Do nothing
        }
    }
}

private fun bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll()

    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    val preview = Preview(previewConfig)

    val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    imageCapture = ImageCapture(imageCaptureConfig)

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture)
}
Telespectroscope answered 17/5, 2019 at 3:47 Comment(2)
how can i achive this same in javaEnwreathe
There is no CameraX.LensFacing.BACK constant. It should be CameraSelector.LENS_FACING_BACKGoulette
U
22

Since 2021, an update to CameraX has rendered CameraX.LensFacing unusable. Use CameraSelector instead.

    private CameraSelector lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;

    private void flipCamera() {
        if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) lensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
        else if (lensFacing == CameraSelector.DEFAULT_BACK_CAMERA) lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;
        startCamera();
    }

    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraFuture = ProcessCameraProvider.getInstance(requireContext());

        cameraFuture.addListener(() -> {
            imageCapture = new ImageCapture.Builder()
                .setTargetRotation(cameraPreview.getDisplay().getRotation())
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();
            videoCapture = new VideoCapture.Builder().build();

        try {
            ProcessCameraProvider processCameraProvider = cameraFuture.get();
            Preview preview = new Preview.Builder().build();
            preview.setSurfaceProvider(cameraPreview.getSurfaceProvider());
            processCameraProvider.unbindAll(); 

            // lensFacing is used here
            processCameraProvider.bindToLifecycle(getViewLifecycleOwner(), lensFacing, imageCapture, videoCapture, preview);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        }, ContextCompat.getMainExecutor(requireContext()));
    }
Ullyot answered 3/6, 2021 at 17:11 Comment(0)
T
10

It looks like the recommended way to achieve this is to store the LensFacing position as an instance variable and then call bindToLifecycle() to switch the camera.

Here is a code snippet that worked for me:

private var lensFacing = CameraX.LensFacing.BACK
private var imageCapture: ImageCapture? = null

@SuppressLint("RestrictedApi")
private fun startCamera() {
    bindCameraUseCases()

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
            CameraX.LensFacing.BACK
        } else {
            CameraX.LensFacing.FRONT
        }
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing)
            bindCameraUseCases()
        } catch (exc: Exception) {
            // Do nothing
        }
    }
}

private fun bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll()

    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    val preview = Preview(previewConfig)

    val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    imageCapture = ImageCapture(imageCaptureConfig)

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture)
}
Telespectroscope answered 17/5, 2019 at 3:47 Comment(2)
how can i achive this same in javaEnwreathe
There is no CameraX.LensFacing.BACK constant. It should be CameraSelector.LENS_FACING_BACKGoulette
R
1
private LensFacing lensFacing = CameraX.LensFacing.BACK;
private ImageCapture imageCapture = null;
private Button switchButton;


@SuppressLint("RestrictedApi")
private void startCamera() {
    bindCameraUseCases();

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button);
    switchButton.setOnClickListener(v -> {
        lensFacing = lensFacing == LensFacing.FRONT ? LensFacing.BACK : LensFacing.FRONT;
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing);
            bindCameraUseCases();
        } catch (CameraInfoUnavailableException e) {
            // Do nothing
        }
    });
}

private void bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll();

    PreviewConfig previewConfig = new PreviewConfig.Builder().
            setLensFacing(lensFacing)
            .build();
    Preview preview = new Preview(previewConfig);

    ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder()
            .setLensFacing(lensFacing)
            .build();
    imageCapture = new ImageCapture(imageCaptureConfig);

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture);
}

Java version

Roscoe answered 1/11, 2019 at 15:18 Comment(1)
There is no CameraX.LensFacing.BACK constant. It should be CameraSelector.LENS_FACING_BACKGoulette
G
1

Here is how i did mine

private var defaultCameraFacing = CameraSelector.DEFAULT_BACK_CAMERA

   btnFlipCamera.setOnClickListener {
        Log.d("CameraFacing", defaultCameraFacing.toString())
        defaultCameraFacing = if(defaultCameraFacing == CameraSelector.DEFAULT_FRONT_CAMERA){
            CameraSelector.DEFAULT_BACK_CAMERA
        }else{
            CameraSelector.DEFAULT_FRONT_CAMERA
        }

        try {
            // Only bind use cases if we can query a camera with this orientation
            startCamera(defaultCameraFacing)
        } catch (exc: Exception) {
            // Do nothing
        }
    }

private fun startCamera(defaultCameraFacing: CameraSelector) {
    llPictureCaptured.visibility = View.GONE
    tvLocationLabel.visibility= View.GONE
    pgLoadingLocation.visibility = View.GONE
    openCamera.visibility = View.GONE
    llCameraControl.visibility = View.VISIBLE
    viewFinder.visibility = View.VISIBLE


    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // Used to bind the lifecycle of cameras to the lifecycle owner
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // Preview
        val preview = Preview.Builder()
            .build()
            .also {
                it.setSurfaceProvider(viewFinder.surfaceProvider)
            }

        imageCapture = ImageCapture.Builder()
            .build()

        //set image analysis, i.e luminosity analysis
        val imageAnalyzer = ImageAnalysis.Builder()
            .build()
            .also {
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                    Log.d(TAG, "Average luminosity: $luma")
                })
            }

        // Set camera facing
        val cameraSelector = defaultCameraFacing

        try {
            // Unbind use cases before rebinding
            cameraProvider.unbindAll()

            // Bind use cases to camera
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageCapture, imageAnalyzer)

        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }

    }, ContextCompat.getMainExecutor(this))
}
Gammy answered 12/8, 2021 at 1:51 Comment(0)
P
0

Here is a Kotlin version of @Rig answer. The PreviewView is created in the XML file and its id is viewFinder eg. binding.viewFinder:

XML:

<androidx.camera.view.PreviewView
    android:id="@+id/viewFinder"
    // ...
/>

Activity:

var imageCapture: ImageCapture? = null
var lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA

fun toggleCamera() {

    if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) lensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
    else if (lensFacing == CameraSelector.DEFAULT_BACK_CAMERA) lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;

    startCamera()
}

fun startCamera() {

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({

        imageCapture = ImageCapture.Builder()
            .setTargetRotation(binding.viewFinder.display.rotation)
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
            .build()

        try {

            val processCameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            val preview = Preview.Builder().build()
            preview.setSurfaceProvider(binding.viewFinder.surfaceProvider)
            processCameraProvider.unbindAll()

            // lensFacing is used here
            processCameraProvider.bindToLifecycle(
                this,
                lensFacing,
                imageCapture,
                preview
            )

        } catch (e: ExecutionException) {
            Log.e(TAG, "Use case binding failed -1: ", e)
        } catch (e: InterruptedException) {
            Log.e(TAG, "Use case binding failed -2: ", e)
        }

    }, ContextCompat.getMainExecutor(this))
}
Parietal answered 24/9, 2023 at 0:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.