Implement cameraPictureMetering and cameraPictureSnapshotMetering in engine

pull/580/head
Mattia Iavarone 6 years ago
parent ee1c4f9cd5
commit 619e8b0d9e
  1. 61
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 35
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.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,18 +616,31 @@ 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 {
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();
}
}
@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);
if (doMetering) {
mDelayedPictureStub = stub;
startMetering(null, null);
} else {
try {
CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
applyAllParameters(builder);
@ -639,6 +655,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
throw createCameraException(e);
}
}
}
@Override
public void onPictureResult(@Nullable PictureResult.Stub result, @Nullable Exception error) {
@ -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
}

@ -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<MeteringRectangle> 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);
}
}

Loading…
Cancel
Save