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 e54ec203..11eccf4d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -104,6 +104,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv // 3A metering private Meter mMeter; + private Gesture mMeteringGesture; + private PictureResult.Stub mDelayedPictureStub; + private AspectRatio mDelayedPictureRatio; public Camera2Engine(Callback callback) { super(callback); @@ -613,30 +616,44 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio outputRatio, boolean doMetering) { stub.size = getUncroppedSnapshotSize(Reference.OUTPUT); // Not the real size: it will be cropped to match the view ratio stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); // Actually it will be rotated and set to 0. - if (mPreview instanceof GlCameraPreview) { - mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio, getOverlay()); + if (doMetering) { + mDelayedPictureStub = stub; + mDelayedPictureRatio = outputRatio; + startMetering(null, null); } else { - throw new RuntimeException("takePictureSnapshot with Camera2 is only supported with Preview.GL_SURFACE"); + if (!(mPreview instanceof GlCameraPreview)) { + throw new RuntimeException("takePictureSnapshot with Camera2 is only supported with Preview.GL_SURFACE"); + } + mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, + (GlCameraPreview) mPreview, + outputRatio, + getOverlay()); + mPictureRecorder.take(); } - mPictureRecorder.take(); } @Override protected void onTakePicture(@NonNull PictureResult.Stub stub, boolean doMetering) { stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); stub.size = getPictureSize(Reference.OUTPUT); - try { - CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); - applyAllParameters(builder); - mPictureRecorder = new Full2PictureRecorder(stub, this, - mSession, - mRepeatingRequestCallback, - builder, - mPictureReader, - mPictureCaptureStopsPreview); - mPictureRecorder.take(); - } catch (CameraAccessException e) { - throw createCameraException(e); + + if (doMetering) { + mDelayedPictureStub = stub; + startMetering(null, null); + } else { + try { + CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + applyAllParameters(builder); + mPictureRecorder = new Full2PictureRecorder(stub, this, + mSession, + mRepeatingRequestCallback, + builder, + mPictureReader, + mPictureCaptureStopsPreview); + mPictureRecorder.take(); + } catch (CameraAccessException e) { + throw createCameraException(e); + } } } @@ -1136,11 +1153,10 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void startAutoFocus(@Nullable final Gesture gesture, @NonNull final PointF point) { - // TODO Should change this name at some point, and deprecate AF methods startMetering(gesture, point); } - private void startMetering(@Nullable final Gesture gesture, @NonNull final PointF point) { + private void startMetering(@Nullable final Gesture gesture, @Nullable final PointF point) { LOG.i("startMetering", "dispatching. Gesture:", gesture); mHandler.run(new Runnable() { @Override @@ -1151,8 +1167,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv 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; + // refers to "3A metering to a specific point". So if we have one, let's check. + if (point != null && !mCameraOptions.isAutoFocusSupported()) return; // Reset the old meter if present. if (mMeter != null) { @@ -1168,11 +1184,12 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv applyFocusForMetering(mRepeatingRequestBuilder); // Create the meter and start. + mMeteringGesture = gesture; mMeter = new Meter(Camera2Engine.this, mRepeatingRequestBuilder, mCameraCharacteristics, Camera2Engine.this); - mMeter.startMetering(mLastRepeatingResult, point, gesture); + mMeter.startMetering(mLastRepeatingResult, point); } }); } @@ -1181,13 +1198,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * Called by {@link Meter} when the metering process has started. * We are currently exposing an auto focus API so that's what we dispatch. * @param point point - * @param gesture gesture + * */ @Override - public void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture) { - LOG.w("onMeteringStarted - point:", point, "gesture:", gesture); + public void onMeteringStarted(@Nullable PointF point) { + LOG.w("onMeteringStarted - point:", point, "gesture:", mMeteringGesture); if (point != null) { - mCallback.dispatchOnFocusStart(gesture, point); + mCallback.dispatchOnFocusStart(mMeteringGesture, point); } applyRepeatingRequestBuilder(); } @@ -1196,14 +1213,21 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * Called by {@link Meter} when the metering process has ended. * We are currently exposing an auto focus API so that's what we dispatch. * @param point point - * @param gesture gesture * @param success success */ @Override - public void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success) { - LOG.w("onMeteringEnd - point:", point, "gesture:", gesture, "success:", success); + public void onMeteringEnd(@Nullable PointF point, boolean success) { + LOG.w("onMeteringEnd - point:", point, "gesture:", mMeteringGesture, "success:", success); if (point != null) { - mCallback.dispatchOnFocusEnd(gesture, success, point); + mCallback.dispatchOnFocusEnd(mMeteringGesture, success, point); + } else { + if (mDelayedPictureStub.isSnapshot) { + onTakePictureSnapshot(mDelayedPictureStub, mDelayedPictureRatio, false); + } else { + onTakePicture(mDelayedPictureStub, false); + } + mDelayedPictureStub = null; + mDelayedPictureRatio = null; } } @@ -1211,11 +1235,10 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * When metering is reset, we're not sure that the engine is still alive. * We should check this here. * @param point point - * @param gesture gesture * @return true if metering can be reset */ @Override - public boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture) { + public boolean canResetMetering(@Nullable PointF point) { return getEngineState() == STATE_STARTED; } @@ -1223,10 +1246,10 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * Called by {@link Meter} after resetting the metering parameters. * We should apply them, and also go back to default focus. * @param point point - * @param gesture gesture + * */ @Override - public void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture) { + public void onMeteringReset(@Nullable PointF point) { applyDefaultFocus(mRepeatingRequestBuilder); applyRepeatingRequestBuilder(); // only if preview started already } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java index 1381cf58..8211c70f 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java @@ -20,19 +20,17 @@ import com.otaliastudios.cameraview.engine.metering.AutoWhiteBalance; import com.otaliastudios.cameraview.engine.metering.MeteringParameter; import com.otaliastudios.cameraview.engine.offset.Axis; import com.otaliastudios.cameraview.engine.offset.Reference; -import com.otaliastudios.cameraview.gesture.Gesture; import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.size.Size; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** * Helps Camera2-based engines to perform 3A (auto focus, auto exposure and auto white balance) * metering. Users are required to: * - * - Call {@link #startMetering(PointF, Gesture)} to start + * - Call {@link #startMetering(CaptureResult, PointF)} to start * - Call {@link #onCapture(CaptureResult)} when they have partial or total results, as long as the * meter is still in a metering operation, which can be checked through {@link #isMetering()} * - Call {@link #resetMetering()} to reset the metering parameters if needed. This is done automatically @@ -51,38 +49,36 @@ public class Meter { * Notifies that metering has started. At this point implementors should apply * the builder onto the preview. * @param point point - * @param gesture gesture + * */ - void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture); + void onMeteringStarted(@Nullable PointF point); /** * Notifies that metering has ended. No action is required for implementors. * From now on, {@link #isMetering()} will return false so the meter should not * be passed capture results anymore. * @param point point - * @param gesture gesture * @param success success */ - void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success); + void onMeteringEnd(@Nullable PointF point, boolean success); /** * Notifies that metering has been reset. From now on, this meter instance * is done, although in theory it could be reused by calling - * {@link #startMetering(CaptureResult, PointF, Gesture)} again. + * {@link #startMetering(CaptureResult, PointF)} again. * @param point point - * @param gesture gesture + * */ - void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture); + void onMeteringReset(@Nullable PointF point); /** * Whether metering can be reset. Since it happens at a future time, this should * return true if the engine is still in a legit state for this operation. * @param point point - * @param gesture gesture * @return true if can reset */ // TODO is this useful? engine could do its checks onMeteringReset() - boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture); + boolean canResetMetering(@Nullable PointF point); } private static final String TAG = Meter.class.getSimpleName(); @@ -94,7 +90,6 @@ public class Meter { private final CameraCharacteristics mCharacteristics; private final Callback mCallback; private PointF mPoint; - private Gesture mGesture; private boolean mIsMetering; private long mMeteringStartTime; @@ -124,12 +119,10 @@ public class Meter { * Starts a metering sequence. * @param lastResult the last result * @param point point - * @param gesture gesture */ @SuppressWarnings("WeakerAccess") - public void startMetering(@NonNull CaptureResult lastResult, @Nullable PointF point, @Nullable Gesture gesture) { + public void startMetering(@NonNull CaptureResult lastResult, @Nullable PointF point) { mPoint = point; - mGesture = gesture; mIsMetering = true; List areas = new ArrayList<>(); @@ -179,7 +172,7 @@ public class Meter { mAutoExposure.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible); // Dispatch to callback - mCallback.onMeteringStarted(mPoint, mGesture); + mCallback.onMeteringStarted(mPoint); mMeteringStartTime = System.currentTimeMillis(); } @@ -319,7 +312,7 @@ public class Meter { /** * True if we're metering. False if we're not, for example if we're waiting for - * a reset call, or if {@link #startMetering(CaptureResult, PointF, Gesture)} was never called. + * a reset call, or if {@link #startMetering(CaptureResult, PointF)} was never called. * @return true if metering */ @SuppressWarnings("WeakerAccess") @@ -353,7 +346,7 @@ public class Meter { } private void onMeteringEnd(boolean success) { - mCallback.onMeteringEnd(mPoint, mGesture, success); + mCallback.onMeteringEnd(mPoint, success); mIsMetering = false; mEngine.mHandler.remove(mResetRunnable); if (mEngine.shouldResetAutoFocus()) { @@ -368,7 +361,7 @@ public class Meter { @SuppressWarnings("WeakerAccess") public void resetMetering() { mEngine.mHandler.remove(mResetRunnable); - if (mCallback.canResetMetering(mPoint, mGesture)) { + if (mCallback.canResetMetering(mPoint)) { LOG.i("Resetting the meter parameters."); MeteringRectangle whole = null; if (mPoint != null) { @@ -380,7 +373,7 @@ public class Meter { mAutoFocus.resetMetering(mCharacteristics, mBuilder, whole); mAutoWhiteBalance.resetMetering(mCharacteristics, mBuilder, whole); mAutoExposure.resetMetering(mCharacteristics, mBuilder, whole); - mCallback.onMeteringReset(mPoint, mGesture); + mCallback.onMeteringReset(mPoint); } }