Android-CameraX: Switch between multiple front cameras
Asked Answered
I

3

5

I´m trying to write my first Android Camera App, but it always selects the zoom camera instead of the main camera. (I tested it on a Huawei P30 Pro)

And the code is based on the offical camerax sample application (https://github.com/android/camera-samples/tree/master/CameraXBasic)

The relevant code:

/** Declare and bind preview, capture and analysis use cases */
private fun bindCameraUseCases() {

    // Get screen metrics used to setup camera for full screen resolution
    val metrics = DisplayMetrics().also { viewFinder.display.getRealMetrics(it) }
    Log.d(TAG, "Screen metrics: ${metrics.widthPixels} x ${metrics.heightPixels}")

    val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
    Log.d(TAG, "Preview aspect ratio: $screenAspectRatio")

    val rotation = viewFinder.display.rotation

    // Bind the CameraProvider to the LifeCycleOwner

    val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    cameraProviderFuture.addListener(Runnable {

        // CameraProvider
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // Preview
        preview = Preview.Builder()
            // We request aspect ratio but no resolution
            .setTargetAspectRatio(screenAspectRatio)
            // Set initial target rotation
            .setTargetRotation(rotation)
            .build()

        // ImageCapture
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            // We request aspect ratio but no resolution to match preview config, but letting
            // CameraX optimize for whatever specific resolution best fits our use cases
            .setTargetAspectRatio(screenAspectRatio)
            // Set initial target rotation, we will have to call this again if rotation changes
            // during the lifecycle of this use case
            .setTargetRotation(rotation)
            .build()

        // Must unbind the use-cases before rebinding them
        cameraProvider.unbindAll()

        try {
            // A variable number of use-cases can be passed here -
            // camera provides access to CameraControl & CameraInfo
            camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture /**, imageAnalyzer*/)

            // Attach the viewfinder's surface provider to preview use case
            preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
        } catch(exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }

    }, ContextCompat.getMainExecutor(requireContext()))
}
Icecap answered 12/5, 2020 at 9:40 Comment(0)
T
5

With the current CameraX APIs, I don't think you can choose which specific camera to use, you can only select the lens facing (CameraSelector.Builder().requireLensFacing(int)), which can either be front or back. When you bind use cases, CameraX chooses the first camera that meets the use cases requirements. For example, 2 different back cameras may be used when binding the preview use case with extensions enabled vs disabled.

The CameraSelector API seems to still have room to grow, so in the future, you may have more control over which camera is selected. If you need that kind of control right now, you may need to use Camera2.

Tirado answered 13/5, 2020 at 0:56 Comment(3)
Hi. Do you maybe have some examples regarding how cameraX chooses which camera to use in different use cases?Intuitionism
@masterR0shi CameraSelector selects cameras from the pool of available cameras on the device depending on the filters it's using. CameraSelector.Builder provides the methods requireLensFacing(int) and addCameraFilter(CameraFilter). You can use a CameraFilter to filter cameras via their CameraInfo, or its subclass Camera2CameraFilter to filter cameras via their id and CameraCharacteristics. If there's a specific use case you're asking about you can create a new question for it, I'll be happy to help if I can.Tirado
I just posted a question a couple of hours ago, actually xD #65994027 any support would be greatly appreciated!Intuitionism
N
4

You can change camera by setting cameraSelector. For example, to set the camera with index=2, use this code.

List<CameraInfo> availableCameraInfos = cameraProvider.getAvailableCameraInfos();
Log.i(TAG, "[startCamera] available cameras:"+availableCameraInfos);
    
// Select back camera as a default
//CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
CameraSelector cameraSelector = availableCameraInfos.get(2).getCameraSelector();
Nairobi answered 21/9, 2022 at 12:28 Comment(1)
This does work however it is missing some cameras, maybe limitation of CameraX api. For ex on Pixel 6a it has 1/2 back cameras and Samsung S20 it has 2/3 back cameras. In both cases its missing the wide angle lens.Lungwort
T
1

I have a similar issue but with the back camera. I checked some devices and found that on all my tested device I can select the camera id and get the right camera:

  • Camera ID "0" -> Normal/default back facing camera
  • Camera ID "1" -> Normal/default front facing camera

So I created a custom CameraFilter where I filter for camera id "0":

class CameraIdFilter(private val cameraId: String) : CameraFilter {

override fun filterCameras(cameras: Set<CameraInternal>): Set<CameraInternal> {
    val resultCameras: MutableSet<CameraInternal> = LinkedHashSet()
    for (camera in cameras) {
        val cameraId = camera.cameraInfoInternal.cameraId
        if (cameraId == this.cameraId) {
            resultCameras.add(camera)
        }
    }
    return resultCameras
}

}

And append the filter to the camera selector:

        val cameraSelectorBuilder = CameraSelector.Builder()
            .requireLensFacing(lensFacing)
            .appendFilter(CameraIdFilter("0"))

I don't recommend using this in a production app, because I'm sure there are some devices that don't follow this convention/pattern. But you can use it for an internal app where you know which devices your users use

Triplenerved answered 29/5, 2020 at 15:19 Comment(2)
As of version 1.0.0-beta12 this code doesn't work anymore because the APIs have changed significantly and after fiddling around with it for a while I can't seem to find another way to access cameraId. That being said, it was probably never intended to work like this in the first place.Clipboard
Yes, it was never intended to be used like this. There is/was no other way to select the correct camera for our use case. We needed to scan QR Codes and this only works with the normal camera. Maybe it would be possible to access both cameras or change while scanning. The camera API should provide a possibility to add the requirement for selecting a camera that can be used for scanning. Maybe it gets added later on.Triplenerved

© 2022 - 2025 — McMap. All rights reserved.