Patel Pinkal
Patel Pinkal

Reputation: 9512

How to zoom camera using Android CameraX API?

I have tried to zoom camera with below code using CameraX.

First I get a Preview from CameraX and tried to perform zoom using Preview like below.

var config = CameraX.getDefaultUseCaseConfig(PreviewConfig::class.java, lensFacing)
var preview = Preview(config)
preview.zoom(zoom)

After preview.zoom() I just bind again with CameraX and get some error and it's not working.

CameraX.bindToLifecycle(this, preview, imageCapture, videoCapture)

When the above code was not working, I tried with CameraX.unbindAll() first and then I call CameraX.bindToLifecycle(). But face some error again and not get success in zoom.

Let me know how we can zoom the camera using CameraX API.

Update error log below:

Error log using only CameraX.bindToLifecycle():

java.lang.IllegalArgumentException: Exceeded max simultaneously bound image capture use cases.
   at androidx.camera.camera2.impl.UseCaseSurfaceOccupancyManager.checkUseCaseLimitNotExceeded(UseCaseSurfaceOccupancyManager.java:61)
   at androidx.camera.camera2.impl.Camera2DeviceSurfaceManager.getSuggestedResolutions(Camera2DeviceSurfaceManager.java:146)
   at androidx.camera.core.CameraX.calculateSuggestedResolutions(CameraX.java:449)
   at androidx.camera.core.CameraX.bindToLifecycle(CameraX.java:144)
   at com.android.example.cameraxbasic.fragments.CameraFragment$updateCameraUi$2.onTouch(CameraFragment.kt:408)
   at android.view.View.dispatchTouchEvent(View.java:12512)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:475)
   at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1836)
   at android.app.Activity.dispatchTouchEvent(Activity.java:3404)
   at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
   at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:433)
   at android.view.View.dispatchPointerEvent(View.java:12755)
   at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5150)
   at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4953)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4523)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4489)
   at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4629)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4497)
   at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4686)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4523)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4489)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4497)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7192)
   at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7126)
   at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7087)
   at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7295)
   at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:193)
   at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
   at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:184)
   at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7266)
   at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7318)
   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
   at android.view.Choreographer.doCallbacks(Choreographer.java:761)
   at android.view.Choreographer.doFrame(Choreographer.java:690)
   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
   at android.os.Handler.handleCallback(Handler.java:873)
   at android.os.Handler.dispatchMessage(Handler.java:99)
   at android.os.Looper.loop(Looper.java:193)
   at android.app.ActivityThread.main(ActivityThread.java:6912)
   at java.lang.reflect.Method.invoke(Native Method)
   at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)

Error log using CameraX.unbindAll() and CameraX.bindToLifecycle():

   E/Legacy-CameraDevice-JNI: getNativeWindow: Surface had no valid native window.
   E/Legacy-CameraDevice-JNI: LegacyCameraDevice_nativeDetectSurfaceDimens: Could not retrieve native window from surface.

--------- beginning of crash
   2019-05-09 16:49:29.155 31123-31144/com.android.example.cameraxbasic E/AndroidRuntime: FATAL EXCEPTION: CameraX-
Process: com.android.example.cameraxbasic, PID: 31123
java.lang.IllegalArgumentException: Surface was abandoned
    at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:84)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:260)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:145)
    at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:518)
    at androidx.camera.camera2.impl.CaptureSession.open(CaptureSession.java:196)
    at androidx.camera.camera2.impl.Camera.openCaptureSession(Camera.java:535)
    at androidx.camera.camera2.impl.Camera$StateCallback.onOpened(Camera.java:743)
    at androidx.camera.core.CameraDeviceStateCallbacks$ComboDeviceStateCallback.onOpened(CameraDeviceStateCallbacks.java:99)
    at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:152)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.os.HandlerThread.run(HandlerThread.java:65)
 Caused by: android.hardware.camera2.legacy.LegacyExceptionUtils$BufferQueueAbandonedException
    at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnError(LegacyExceptionUtils.java:73)
    at android.hardware.camera2.legacy.LegacyCameraDevice.getSurfaceSize(LegacyCameraDevice.java:606)
    at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:82)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:260) 
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:145) 
    at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:518) 
    at androidx.camera.camera2.impl.CaptureSession.open(CaptureSession.java:196) 
    at androidx.camera.camera2.impl.Camera.openCaptureSession(Camera.java:535) 
    at androidx.camera.camera2.impl.Camera$StateCallback.onOpened(Camera.java:743) 
    at androidx.camera.core.CameraDeviceStateCallbacks$ComboDeviceStateCallback.onOpened(CameraDeviceStateCallbacks.java:99) 
    at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:152) 
    at android.os.Handler.handleCallback(Handler.java:873) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.os.HandlerThread.run(HandlerThread.java:65) 


--------- beginning of system

Upvotes: 10

Views: 21197

Answers (7)

Ummer Siddique
Ummer Siddique

Reputation: 397

You can get reference to camera control when calling to bindToLifecycle

val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

// use one of following functions to zoom
camera.cameraControl.setLinearZoom(0 - 1)
camera.cameraControl.setZoomRatio(ratio)

Official docs : https://developer.android.com/media/camera/camerax/configuration#cameracontrol-instance

Upvotes: 0

risabvishwakarma
risabvishwakarma

Reputation: 11

  ScaleGestureDetector.OnScaleGestureListener listener = new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
                ZoomState f = cam1.getCameraInfo().getZoomState().getValue();
                Log.d("Zoom", String.valueOf(f.getZoomRatio()));

                float scale = scaleGestureDetector.getScaleFactor();
                cam1.getCameraControl().setZoomRatio(scale * f.getZoomRatio());
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {

                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

            }
        };
        ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getApplicationContext(), listener);

        previewView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return scaleGestureDetector.onTouchEvent(motionEvent);

            }
        });

Upvotes: 1

latsson
latsson

Reputation: 670

This is what I did on the latest CameraX 1.0.0-beta01 sample project. Added to bottom of onViewCreated

val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
    val scale = camera!!.cameraInfo.zoomState.value!!.zoomRatio * detector.scaleFactor
        camera!!.cameraControl.setZoomRatio(scale)
        return true
    }
}

val scaleGestureDetector = ScaleGestureDetector(context, listener)

viewFinder.setOnTouchListener { _, event ->
    scaleGestureDetector.onTouchEvent(event)
    return@setOnTouchListener true
}

Upvotes: 5

Patel Pinkal
Patel Pinkal

Reputation: 9512

Finally, we get the Zoom API in CameraX. Please check below two way which you can use to zoom the preview of camera.

Pinch to zoom:

val scaleGestureDetector = ScaleGestureDetector(context, listener)

cameraTextureView.setOnTouchListener { _, event ->
    scaleGestureDetector.onTouchEvent(event)
    return@setOnTouchListener true
}

val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
    override fun onScale(detector: ScaleGestureDetector): Boolean {
        val scale = cameraInfo.zoomRatio.value * detector.scaleFactor
        cameraControl.setZoomRatio(scale)
        return true
    }
}

Slide to zoom(using seekbar):

zoomSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {        
    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        cameraControl.setLinearZoom(progress / 100.toFloat())
    }

    override fun onStartTrackingTouch(seekBar: SeekBar?) {}

    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})

Use below to get cameraControl:

val cameraControl = CameraX.getCameraControl(lensFacing)

Code reference link: https://github.com/Pinkal7600/camera-samples/tree/master/CameraXBasic

Upvotes: 19

Akshay Raiyani
Akshay Raiyani

Reputation: 1323

I am able to do zoom in/out using this code

val my = Rect(left, top, right, bottom)
preview.zoom(my)

see full-example on github and just check my answer

It's perfectly working for me.

Upvotes: -1

Scott Nien
Scott Nien

Reputation: 51

For zoom to work properly, you also need to call zoom() after bindToLifecycle(). Also note that zoom() accepts a Rect which is in sensor coordinate. see here for more details.

You may also need to use camera2 API to get the sensor active array and get the camera id (please use the first camera id that has the correct LENS_FACING). We know these are a lot of works so we are developing a new higher level zoom API that is much easier and simpler to use.

Upvotes: 3

Oscar Wahltinez
Oscar Wahltinez

Reputation: 1195

It looks like the error is unrelated to preview zoom: java.lang.IllegalArgumentException: Exceeded max simultaneously bound image capture use cases.

This indicates that you are calling CameraX.bindToLifecycle(imageCapture) more than once before unbinding it, and that is what is causing the crash. I would also recommend using the configuration builders instead of getDefaultUseCaseConfig, since they are more explicit and easier to read and getDefaultUseCaseConfig is a hidden API.

Please refer to the official sample for an example implementation and the documentation for more details.

Upvotes: 0

Related Questions