Adapt Meter and metering package to picture use

pull/580/head
Mattia Iavarone 6 years ago
parent f7e9372caf
commit 5725276553
  1. 20
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 111
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java
  3. 46
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java
  4. 54
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java
  5. 41
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java
  6. 57
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/MeteringParameter.java

@ -83,6 +83,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private CaptureRequest.Builder mRepeatingRequestBuilder; private CaptureRequest.Builder mRepeatingRequestBuilder;
private CaptureRequest mRepeatingRequest; private CaptureRequest mRepeatingRequest;
private CameraCaptureSession.CaptureCallback mRepeatingRequestCallback; private CameraCaptureSession.CaptureCallback mRepeatingRequestCallback;
private CaptureResult mLastRepeatingResult;
private final Camera2Mapper mMapper = Camera2Mapper.get(); private final Camera2Mapper mMapper = Camera2Mapper.get();
// Frame processing // Frame processing
@ -240,6 +241,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@Override @Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result); super.onCaptureCompleted(session, request, result);
mLastRepeatingResult = result;
if (mPictureRecorder instanceof Full2PictureRecorder) { if (mPictureRecorder instanceof Full2PictureRecorder) {
((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); ((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result);
} }
@ -1175,7 +1177,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
mRepeatingRequestBuilder, mRepeatingRequestBuilder,
mCameraCharacteristics, mCameraCharacteristics,
Camera2Engine.this); Camera2Engine.this);
mMeter.startMetering(point, gesture); mMeter.startMetering(mLastRepeatingResult, point, gesture);
} }
}); });
} }
@ -1187,9 +1189,11 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
* @param gesture gesture * @param gesture gesture
*/ */
@Override @Override
public void onMeteringStarted(@NonNull PointF point, @Nullable Gesture gesture) { public void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture) {
LOG.w("onMeteringStarted - point:", point, "gesture:", gesture); LOG.w("onMeteringStarted - point:", point, "gesture:", gesture);
mCallback.dispatchOnFocusStart(gesture, point); if (point != null) {
mCallback.dispatchOnFocusStart(gesture, point);
}
applyRepeatingRequestBuilder(); applyRepeatingRequestBuilder();
} }
@ -1201,9 +1205,11 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
* @param success success * @param success success
*/ */
@Override @Override
public void onMeteringEnd(@NonNull PointF point, @Nullable Gesture gesture, boolean success) { public void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success) {
LOG.w("onMeteringEnd - point:", point, "gesture:", gesture, "success:", success); LOG.w("onMeteringEnd - point:", point, "gesture:", gesture, "success:", success);
mCallback.dispatchOnFocusEnd(gesture, success, point); if (point != null) {
mCallback.dispatchOnFocusEnd(gesture, success, point);
}
} }
/** /**
@ -1214,7 +1220,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
* @return true if metering can be reset * @return true if metering can be reset
*/ */
@Override @Override
public boolean canResetMetering(@NonNull PointF point, @Nullable Gesture gesture) { public boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture) {
return getEngineState() == STATE_STARTED; return getEngineState() == STATE_STARTED;
} }
@ -1225,7 +1231,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
* @param gesture gesture * @param gesture gesture
*/ */
@Override @Override
public void onMeteringReset(@NonNull PointF point, @Nullable Gesture gesture) { public void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture) {
applyDefaultFocus(mRepeatingRequestBuilder); applyDefaultFocus(mRepeatingRequestBuilder);
applyRepeatingRequestBuilder(); // only if preview started already applyRepeatingRequestBuilder(); // only if preview started already
} }

@ -24,6 +24,7 @@ import com.otaliastudios.cameraview.gesture.Gesture;
import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -52,7 +53,7 @@ public class Meter {
* @param point point * @param point point
* @param gesture gesture * @param gesture gesture
*/ */
void onMeteringStarted(@NonNull PointF point, @Nullable Gesture gesture); void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture);
/** /**
* Notifies that metering has ended. No action is required for implementors. * Notifies that metering has ended. No action is required for implementors.
@ -62,16 +63,16 @@ public class Meter {
* @param gesture gesture * @param gesture gesture
* @param success success * @param success success
*/ */
void onMeteringEnd(@NonNull PointF point, @Nullable Gesture gesture, boolean success); void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success);
/** /**
* Notifies that metering has been reset. From now on, this meter instance * Notifies that metering has been reset. From now on, this meter instance
* is done, although in theory it could be reused by calling * is done, although in theory it could be reused by calling
* {@link #startMetering(PointF, Gesture)} again. * {@link #startMetering(CaptureResult, PointF, Gesture)} again.
* @param point point * @param point point
* @param gesture gesture * @param gesture gesture
*/ */
void onMeteringReset(@NonNull PointF point, @Nullable Gesture gesture); void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture);
/** /**
* Whether metering can be reset. Since it happens at a future time, this should * Whether metering can be reset. Since it happens at a future time, this should
@ -81,7 +82,7 @@ public class Meter {
* @return true if can reset * @return true if can reset
*/ */
// TODO is this useful? engine could do its checks onMeteringReset() // TODO is this useful? engine could do its checks onMeteringReset()
boolean canResetMetering(@NonNull PointF point, @Nullable Gesture gesture); boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture);
} }
private static final String TAG = Meter.class.getSimpleName(); private static final String TAG = Meter.class.getSimpleName();
@ -121,53 +122,61 @@ public class Meter {
/** /**
* Starts a metering sequence. * Starts a metering sequence.
* @param lastResult the last result
* @param point point * @param point point
* @param gesture gesture * @param gesture gesture
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public void startMetering(@NonNull PointF point, @Nullable Gesture gesture) { public void startMetering(@NonNull CaptureResult lastResult, @Nullable PointF point, @Nullable Gesture gesture) {
mPoint = point; mPoint = point;
mGesture = gesture; mGesture = gesture;
mIsMetering = true; mIsMetering = true;
// This is a good Q/A. https://stackoverflow.com/a/33181620/4288782 List<MeteringRectangle> areas = new ArrayList<>();
// At first, the point is relative to the View system and does not account our own cropping. if (point != null) {
// Will keep updating these two below. // This is a good Q/A. https://stackoverflow.com/a/33181620/4288782
final PointF referencePoint = new PointF(mPoint.x, mPoint.y); // At first, the point is relative to the View system and does not account our own cropping.
Size referenceSize = mEngine.mPreview.getSurfaceSize(); // Will keep updating these two below.
final PointF referencePoint = new PointF(mPoint.x, mPoint.y);
// 1. Account for cropping. Size referenceSize = mEngine.mPreview.getSurfaceSize();
// This will enlarge the preview size so that aspect ratio matches.
referenceSize = applyPreviewCropping(referenceSize, referencePoint); // 1. Account for cropping.
// This will enlarge the preview size so that aspect ratio matches.
// 2. Scale to the preview stream coordinates. referenceSize = applyPreviewCropping(referenceSize, referencePoint);
// This will move to the preview stream coordinates by scaling.
referenceSize = applyPreviewScale(referenceSize, referencePoint); // 2. Scale to the preview stream coordinates.
// This will move to the preview stream coordinates by scaling.
// 3. Rotate to the stream coordinate system. referenceSize = applyPreviewScale(referenceSize, referencePoint);
// This leaves us with sensor stream coordinates.
referenceSize = applyPreviewToSensorRotation(referenceSize, referencePoint); // 3. Rotate to the stream coordinate system.
// This leaves us with sensor stream coordinates.
// 4. Move to the crop region coordinate system. referenceSize = applyPreviewToSensorRotation(referenceSize, referencePoint);
// The crop region is the union of all currently active streams.
referenceSize = applyCropRegionCoordinates(referenceSize, referencePoint); // 4. Move to the crop region coordinate system.
// The crop region is the union of all currently active streams.
// 5. Move to the active array coordinate system. referenceSize = applyCropRegionCoordinates(referenceSize, referencePoint);
referenceSize = applyActiveArrayCoordinates(referenceSize, referencePoint);
// 5. Move to the active array coordinate system.
// 6. Now we can compute the metering regions. referenceSize = applyActiveArrayCoordinates(referenceSize, referencePoint);
// We want to define them as a fraction of the visible size which (apart from cropping)
// can be obtained through the SENSOR rotated preview stream size. // 6. Now we can compute the metering regions.
Size visibleSize = mEngine.getPreviewStreamSize(Reference.SENSOR); // We want to define them as a fraction of the visible size which (apart from cropping)
//noinspection ConstantConditions // can be obtained through the SENSOR rotated preview stream size.
MeteringRectangle area1 = createMeteringRectangle(referenceSize, referencePoint, visibleSize, 0.05F, 1000); Size visibleSize = mEngine.getPreviewStreamSize(Reference.SENSOR);
MeteringRectangle area2 = createMeteringRectangle(referenceSize, referencePoint, visibleSize, 0.1F, 100); //noinspection ConstantConditions
List<MeteringRectangle> areas = Arrays.asList(area1, area2); MeteringRectangle area1 = createMeteringRectangle(referenceSize, referencePoint,
visibleSize, 0.05F, 1000);
MeteringRectangle area2 = createMeteringRectangle(referenceSize, referencePoint,
visibleSize, 0.1F, 100);
areas.add(area1);
areas.add(area2);
}
// 7. And finally dispatch everything // 7. And finally dispatch everything
mAutoFocus.startMetering(mCharacteristics, mBuilder, areas); boolean skipIfPossible = mPoint == null;
mAutoWhiteBalance.startMetering(mCharacteristics, mBuilder, areas); mAutoFocus.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible);
mAutoExposure.startMetering(mCharacteristics, mBuilder, areas); mAutoWhiteBalance.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible);
mAutoExposure.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible);
// Dispatch to callback // Dispatch to callback
mCallback.onMeteringStarted(mPoint, mGesture); mCallback.onMeteringStarted(mPoint, mGesture);
@ -310,7 +319,7 @@ public class Meter {
/** /**
* True if we're metering. False if we're not, for example if we're waiting for * True if we're metering. False if we're not, for example if we're waiting for
* a reset call, or if {@link #startMetering(PointF, Gesture)} was never called. * a reset call, or if {@link #startMetering(CaptureResult, PointF, Gesture)} was never called.
* @return true if metering * @return true if metering
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -361,12 +370,16 @@ public class Meter {
mEngine.mHandler.remove(mResetRunnable); mEngine.mHandler.remove(mResetRunnable);
if (mCallback.canResetMetering(mPoint, mGesture)) { if (mCallback.canResetMetering(mPoint, mGesture)) {
LOG.i("Resetting the meter parameters."); LOG.i("Resetting the meter parameters.");
Rect whole = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); MeteringRectangle whole = null;
if (whole == null) whole = new Rect(); if (mPoint != null) {
MeteringRectangle rectangle = new MeteringRectangle(whole, MeteringRectangle.METERING_WEIGHT_DONT_CARE); // If we have a point, we must reset the metering areas.
mAutoFocus.resetMetering(mCharacteristics, mBuilder, rectangle); Rect wholeRect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
mAutoWhiteBalance.resetMetering(mCharacteristics, mBuilder, rectangle); if (wholeRect == null) wholeRect = new Rect();
mAutoExposure.resetMetering(mCharacteristics, mBuilder, rectangle); whole = new MeteringRectangle(wholeRect, MeteringRectangle.METERING_WEIGHT_DONT_CARE);
}
mAutoFocus.resetMetering(mCharacteristics, mBuilder, whole);
mAutoWhiteBalance.resetMetering(mCharacteristics, mBuilder, whole);
mAutoExposure.resetMetering(mCharacteristics, mBuilder, whole);
mCallback.onMeteringReset(mPoint, mGesture); mCallback.onMeteringReset(mPoint, mGesture);
} }
} }

@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
@ -22,13 +23,9 @@ public class AutoExposure extends MeteringParameter {
private boolean isStarted; private boolean isStarted;
@Override @Override
public void startMetering(@NonNull CameraCharacteristics characteristics, protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder) {
@NonNull List<MeteringRectangle> areas) { // In our case, this means checking if we support the AE precapture trigger.
isSuccessful = false;
isMetered = false;
isStarted = false;
boolean isNotLegacy = readCharacteristic(characteristics, boolean isNotLegacy = readCharacteristic(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) !=
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
@ -39,9 +36,23 @@ public class AutoExposure extends MeteringParameter {
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH || aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE || aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
|| aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */); || aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */);
isSupported = isNotLegacy && isAEOn; return isNotLegacy && isAEOn;
}
@Override
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) {
Integer aeState = lastResult.get(CaptureResult.CONTROL_AE_STATE);
return aeState != null && aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED;
}
if (isSupported) { @Override
protected void onStartMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder,
@NonNull List<MeteringRectangle> areas,
boolean supportsProcessing) {
isStarted = false;
if (supportsProcessing) {
builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
} }
@ -49,16 +60,16 @@ public class AutoExposure extends MeteringParameter {
// Even if precapture is not supported, check the regions anyway. // Even if precapture is not supported, check the regions anyway.
int maxRegions = readCharacteristic(characteristics, int maxRegions = readCharacteristic(characteristics,
CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0);
if (maxRegions > 0) { if (!areas.isEmpty() && maxRegions > 0) {
int max = Math.min(maxRegions, areas.size()); int max = Math.min(maxRegions, areas.size());
builder.set(CaptureRequest.CONTROL_AE_REGIONS, builder.set(CaptureRequest.CONTROL_AE_REGIONS,
areas.subList(0, max).toArray(new MeteringRectangle[]{})); areas.subList(0, max).toArray(new MeteringRectangle[]{}));
} }
} }
@Override @Override
public void onCapture(@NonNull CaptureResult result) { public void processCapture(@NonNull CaptureResult result) {
if (isMetered || !isSupported) return;
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
LOG.i("onCapture:", "aeState:", aeState); LOG.i("onCapture:", "aeState:", aeState);
if (aeState == null) return; if (aeState == null) return;
@ -82,15 +93,16 @@ public class AutoExposure extends MeteringParameter {
} }
@Override @Override
public void resetMetering(@NonNull CameraCharacteristics characteristics, protected void onResetMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder,
@NonNull MeteringRectangle area) { @Nullable MeteringRectangle area,
boolean supportsProcessing) {
int maxRegions = readCharacteristic(characteristics, int maxRegions = readCharacteristic(characteristics,
CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0);
if (maxRegions > 0) { if (area != null && maxRegions > 0) {
builder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{area}); builder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{area});
} }
if (isSupported) { if (supportsProcessing) {
// Cleanup any precapture sequence. // Cleanup any precapture sequence.
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,

@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
@ -20,36 +21,44 @@ public class AutoFocus extends MeteringParameter {
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
@Override @Override
public void startMetering(@NonNull CameraCharacteristics characteristics, protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder) {
@NonNull List<MeteringRectangle> areas) {
isSuccessful = false;
isMetered = false;
Integer afMode = builder.get(CaptureRequest.CONTROL_AF_MODE);
// Exclude OFF and EDOF as per docs. // Exclude OFF and EDOF as per docs.
isSupported = afMode != null && Integer afMode = builder.get(CaptureRequest.CONTROL_AF_MODE);
return afMode != null &&
(afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_MACRO); || afMode == CameraCharacteristics.CONTROL_AF_MODE_MACRO);
if (isSupported) { }
@Override
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) {
Integer afState = lastResult.get(CaptureResult.CONTROL_AF_STATE);
return afState != null && afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED;
}
@Override
protected void onStartMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder,
@NonNull List<MeteringRectangle> areas,
boolean supportsProcessing) {
if (supportsProcessing) {
builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
} }
// Even if auto is not supported, change the regions anyway. // Even if auto is not supported, change the regions anyway.
int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); int maxRegions = readCharacteristic(characteristics,
if (maxRegions > 0) { CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0);
if (!areas.isEmpty() && maxRegions > 0) {
int max = Math.min(maxRegions, areas.size()); int max = Math.min(maxRegions, areas.size());
builder.set(CaptureRequest.CONTROL_AF_REGIONS, builder.set(CaptureRequest.CONTROL_AF_REGIONS,
areas.subList(0, max).toArray(new MeteringRectangle[]{})); areas.subList(0, max).toArray(new MeteringRectangle[]{}));
} }
} }
@Override @Override
public void onCapture(@NonNull CaptureResult result) { public void processCapture(@NonNull CaptureResult result) {
if (isMetered || !isSupported) return;
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
LOG.i("onCapture:", "afState:", afState); LOG.i("onCapture:", "afState:", afState);
if (afState == null) return; if (afState == null) return;
@ -71,16 +80,17 @@ public class AutoFocus extends MeteringParameter {
} }
@Override @Override
public void resetMetering(@NonNull CameraCharacteristics characteristics, protected void onResetMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder,
@NonNull MeteringRectangle area) { @Nullable MeteringRectangle area,
boolean supportsProcessing) {
int maxRegions = readCharacteristic(characteristics, int maxRegions = readCharacteristic(characteristics,
CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0);
if (maxRegions > 0) { if (area != null && maxRegions > 0) {
builder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{area}); builder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{area});
} }
if (isSupported) { // Cleanup any trigger. if (supportsProcessing) { // Cleanup any trigger.
builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
} }
} }

@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
@ -20,37 +21,44 @@ public class AutoWhiteBalance extends MeteringParameter {
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
@Override @Override
public void startMetering(@NonNull CameraCharacteristics characteristics, protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder) {
@NonNull List<MeteringRectangle> areas) {
isSuccessful = false;
isMetered = false;
boolean isNotLegacy = readCharacteristic(characteristics, boolean isNotLegacy = readCharacteristic(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) !=
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
Integer awbMode = builder.get(CaptureRequest.CONTROL_AWB_MODE); Integer awbMode = builder.get(CaptureRequest.CONTROL_AWB_MODE);
isSupported = isNotLegacy && awbMode != null && awbMode == CaptureRequest.CONTROL_AWB_MODE_AUTO; return isNotLegacy && awbMode != null && awbMode == CaptureRequest.CONTROL_AWB_MODE_AUTO;
}
if (isSupported) { @Override
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) {
Integer awbState = lastResult.get(CaptureResult.CONTROL_AWB_STATE);
return awbState != null && awbState == CaptureRequest.CONTROL_AWB_STATE_CONVERGED;
}
@Override
protected void onStartMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder,
@NonNull List<MeteringRectangle> areas,
boolean supportsProcessing) {
if (supportsProcessing) {
// Remove any lock. We're not setting any, but just in case. // Remove any lock. We're not setting any, but just in case.
// This would make processing be stuck into the process method.
builder.set(CaptureRequest.CONTROL_AWB_LOCK, false); builder.set(CaptureRequest.CONTROL_AWB_LOCK, false);
} }
// Even if auto is not supported, change the regions anyway. // Even if auto is not supported, change the regions anyway.
int maxRegions = readCharacteristic(characteristics, int maxRegions = readCharacteristic(characteristics,
CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0);
if (maxRegions > 0) { if (!areas.isEmpty() && maxRegions > 0) {
int max = Math.min(maxRegions, areas.size()); int max = Math.min(maxRegions, areas.size());
builder.set(CaptureRequest.CONTROL_AWB_REGIONS, builder.set(CaptureRequest.CONTROL_AWB_REGIONS,
areas.subList(0, max).toArray(new MeteringRectangle[]{})); areas.subList(0, max).toArray(new MeteringRectangle[]{}));
} }
} }
@Override @Override
public void onCapture(@NonNull CaptureResult result) { public void processCapture(@NonNull CaptureResult result) {
if (isMetered || !isSupported) return;
Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE); Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE);
LOG.i("onCapture:", "awbState:", awbState); LOG.i("onCapture:", "awbState:", awbState);
if (awbState == null) return; if (awbState == null) return;
@ -69,12 +77,13 @@ public class AutoWhiteBalance extends MeteringParameter {
} }
@Override @Override
public void resetMetering(@NonNull CameraCharacteristics characteristics, protected void onResetMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder,
@NonNull MeteringRectangle area) { @Nullable MeteringRectangle area,
boolean supportsProcessing) {
int maxRegions = readCharacteristic(characteristics, int maxRegions = readCharacteristic(characteristics,
CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0);
if (maxRegions > 0) { if (area != null && maxRegions > 0) {
builder.set(CaptureRequest.CONTROL_AWB_REGIONS, new MeteringRectangle[]{area}); builder.set(CaptureRequest.CONTROL_AWB_REGIONS, new MeteringRectangle[]{area});
} }
} }

@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import java.util.List; import java.util.List;
@ -14,15 +15,15 @@ import java.util.List;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public abstract class MeteringParameter { public abstract class MeteringParameter {
@SuppressWarnings("WeakerAccess")
protected boolean isSupported;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected boolean isSuccessful; protected boolean isSuccessful;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected boolean isMetered; protected boolean isMetered;
private boolean shouldSkip;
private boolean supportsProcessing;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@NonNull @NonNull
protected <T> T readCharacteristic(@NonNull CameraCharacteristics characteristics, protected <T> T readCharacteristic(@NonNull CameraCharacteristics characteristics,
@ -34,21 +35,53 @@ public abstract class MeteringParameter {
public final boolean isMetered() { public final boolean isMetered() {
// A non supported parameter should always appear as metered // A non supported parameter should always appear as metered
return isMetered || !isSupported; return isMetered || !supportsProcessing || shouldSkip;
} }
public final boolean isSuccessful() { public final boolean isSuccessful() {
// A non supported parameter should always appear as successful // A non supported parameter should always appear as successful
return isSuccessful || !isSupported; return isSuccessful || !supportsProcessing || shouldSkip;
} }
public abstract void startMetering(@NonNull CameraCharacteristics characteristics, public final void startMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder,
@NonNull List<MeteringRectangle> areas); @NonNull List<MeteringRectangle> areas,
@NonNull CaptureResult lastResult,
boolean skipIfPossible) {
isSuccessful = false;
isMetered = false;
shouldSkip = skipIfPossible && checkShouldSkip(lastResult);
supportsProcessing = checkSupportsProcessing(characteristics, builder);
if (!shouldSkip) {
onStartMetering(characteristics, builder, areas, supportsProcessing);
}
}
protected abstract boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder);
protected abstract boolean checkShouldSkip(@NonNull CaptureResult lastResult);
public abstract void resetMetering(@NonNull CameraCharacteristics characteristics, protected abstract void onStartMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder, @NonNull CaptureRequest.Builder builder,
@NonNull MeteringRectangle area); @NonNull List<MeteringRectangle> areas,
boolean supportsProcessing);
public final void onCapture(@NonNull CaptureResult result) {
if (isMetered()) return;
processCapture(result);
}
protected abstract void processCapture(@NonNull CaptureResult result);
public final void resetMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder,
@Nullable MeteringRectangle area) {
onResetMetering(characteristics, builder, area, supportsProcessing);
}
public abstract void onCapture(@NonNull CaptureResult result); protected abstract void onResetMetering(@NonNull CameraCharacteristics characteristics,
@NonNull CaptureRequest.Builder builder,
@Nullable MeteringRectangle area,
boolean supportsProcessing);
} }

Loading…
Cancel
Save