From 894ec4f4b169c991fb9e3bd0ca272d74f6470d60 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 30 Aug 2019 13:05:33 +0200 Subject: [PATCH] Extend auto focus functionality to more cameras --- .../cameraview/CameraOptions.java | 27 ++++++----- .../cameraview/engine/Camera2Engine.java | 46 ++++++++++++++----- .../engine/metering/AutoExposure.java | 5 ++ .../cameraview/engine/metering/AutoFocus.java | 1 + .../engine/metering/AutoWhiteBalance.java | 3 +- .../picture/Full2PictureRecorder.java | 2 +- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index 559d24dc..d0149014 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -196,22 +196,24 @@ public class CameraOptions { if (value != null) supportedHdr.add(value); } - //zoom + // Zoom Float maxZoom = cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); if(maxZoom != null) { zoomSupported = maxZoom > 1; } - // autofocus - int[] afModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); - autoFocusSupported = false; - //noinspection ConstantConditions - for (int afMode : afModes) { - if (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO) { - autoFocusSupported = true; - } - } + // AutoFocus + // This now means 3A metering with respect to a specific region of the screen. + // Some controls (AF, AE) have special triggers that might or might not be supported. + // But they can also be on some continuous search mode so that the trigger is not needed. + // What really matters in my opinion is the availability of regions. + Integer afRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF); + Integer aeRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE); + Integer awbRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB); + autoFocusSupported = (afRegions != null && afRegions > 0) + || (aeRegions != null && aeRegions > 0) + || (awbRegions != null && awbRegions > 0); // Exposure correction Range exposureRange = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); @@ -430,8 +432,9 @@ public class CameraOptions { /** - * Whether auto focus is supported. This means you can map gestures to - * {@link GestureAction#AUTO_FOCUS} and focus will be changed on tap. + * Whether auto focus (metering with respect to a specific region of the screen) is + * supported. If it is, you can map gestures to {@link GestureAction#AUTO_FOCUS} + * and metering will change on tap. * * @return whether auto focus is supported. */ diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 364cbd51..6c8db698 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -811,6 +811,29 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } } + private void applyFocusForMetering(@NonNull CaptureRequest.Builder builder) { + // All focus modes support the AF trigger, except OFF and EDOF. + // However, unlike the preview, we'd prefer AUTO to any CONTINUOUS value. + int[] modesArray = readCharacteristic(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, new int[]{}); + List modes = new ArrayList<>(); + for (int mode : modesArray) { modes.add(mode); } + if (modes.contains(CaptureRequest.CONTROL_AF_MODE_AUTO)) { + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); + return; + } + if (getMode() == Mode.VIDEO && + modes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) { + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); + return; + } + + if (modes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) { + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + //noinspection UnnecessaryReturnStatement + return; + } + } + @Override public void setFlash(@NonNull Flash flash) { final Flash old = mFlash; @@ -1135,23 +1158,22 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv // Even without this it would need the bind state at least, since we need the preview size. if (getPreviewState() < STATE_STARTED) return; + // The camera options API still has the auto focus API but it really + // refers to 3A metering. + if (!mCameraOptions.isAutoFocusSupported()) return; + // Reset the old meter if present. if (mMeter != null) { mMeter.resetMetering(); } - // The meter will check the current state to see if AF/AE/AWB should be run. - // - AE should be on CONTROL_AE_MODE_ON* (depends on setFlash()) - // - AWB should be on CONTROL_AWB_MODE_AUTO (depends on setWhiteBalance()) - // - AF should be on CONTROL_AF_MODE_AUTO (or others) - // The last one depends on us because the library has no focus API and we have - // just been asked to fo auto focus. So let's do this - go to auto and remove any - // CONTINUOUS value that might not be what we want here. - // Note that this operation is reverted during onMeteringReset(). - if (!mCameraOptions.isAutoFocusSupported()) return; - mRepeatingRequestBuilder.set( - CaptureRequest.CONTROL_AF_MODE, - CaptureRequest.CONTROL_AF_MODE_AUTO); + // The meter will check the current configuration to see if AF/AE/AWB should run. + // - AE should be on CONTROL_AE_MODE_ON* (this depends on setFlash()) + // - AWB should be on CONTROL_AWB_MODE_AUTO (this depends on setWhiteBalance()) + // - AF should be on CONTROL_AF_MODE_AUTO or others + // The last one is under our control because the library has no focus API. + // So let's set a good af mode here. This operation is reverted during onMeteringReset(). + applyFocusForMetering(mRepeatingRequestBuilder); // Create the meter and start. mMeter = new Meter(Camera2Engine.this, diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java index 2830e74e..3641f450 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java @@ -66,6 +66,11 @@ public class AutoExposure extends MeteringParameter { if (!isStarted) { if (aeState == CaptureRequest.CONTROL_AE_STATE_PRECAPTURE) { isStarted = true; + } else if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED + || aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) { + // PRECAPTURE is a transient state, so also check for the final states. + isMetered = true; + isSuccessful = true; } } else { if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java index 5b172be2..6c61639c 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java @@ -27,6 +27,7 @@ public class AutoFocus extends MeteringParameter { isMetered = false; Integer afMode = builder.get(CaptureRequest.CONTROL_AF_MODE); + // Exclude OFF and EDOF as per docs. isSupported = afMode != null && (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java index 6fd5f3ea..a9eaf03c 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java @@ -38,7 +38,8 @@ public class AutoWhiteBalance extends MeteringParameter { } // Even if auto is not supported, change the regions anyway. - int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); + int maxRegions = readCharacteristic(characteristics, + CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); if (maxRegions > 0) { int max = Math.min(maxRegions, areas.size()); builder.set(CaptureRequest.CONTROL_AWB_REGIONS, diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java index 80fe7eca..0ccccecd 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java @@ -81,7 +81,7 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader private boolean supportsAutoFocus() { //noinspection ConstantConditions int afMode = mBuilder.get(CaptureRequest.CONTROL_AF_MODE); - // Exclude OFF and EDOF as per their docs. + // Exclude OFF and EDOF as per docs. return afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO