Fix various bugs

pull/580/head
Mattia Iavarone 6 years ago
parent fd85afc71a
commit 104d54b7bd
  1. 31
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java
  3. 25
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/UnlockAction.java
  4. 11
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java
  5. 42
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureReset.java
  6. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusMeter.java
  7. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusReset.java
  8. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/MeterResetAction.java
  9. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceReset.java

@ -45,8 +45,8 @@ import com.otaliastudios.cameraview.controls.WhiteBalance;
import com.otaliastudios.cameraview.engine.action.Action; import com.otaliastudios.cameraview.engine.action.Action;
import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.Actions; import com.otaliastudios.cameraview.engine.action.Actions;
import com.otaliastudios.cameraview.engine.action.BaseAction;
import com.otaliastudios.cameraview.engine.action.CompletionCallback; import com.otaliastudios.cameraview.engine.action.CompletionCallback;
import com.otaliastudios.cameraview.engine.lock.UnlockAction;
import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper;
import com.otaliastudios.cameraview.engine.meter.MeterAction; import com.otaliastudios.cameraview.engine.meter.MeterAction;
import com.otaliastudios.cameraview.engine.meter.MeterResetAction; import com.otaliastudios.cameraview.engine.meter.MeterResetAction;
@ -243,8 +243,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
} }
private final CameraCaptureSession.CaptureCallback mRepeatingRequestCallback = private final CameraCaptureSession.CaptureCallback mRepeatingRequestCallback
new CameraCaptureSession.CaptureCallback() { = new CameraCaptureSession.CaptureCallback() {
@Override @Override
public void onCaptureStarted(@NonNull CameraCaptureSession session, public void onCaptureStarted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request, @NonNull CaptureRequest request,
@ -701,11 +701,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
boolean fullPicture = mPictureRecorder instanceof Full2PictureRecorder; boolean fullPicture = mPictureRecorder instanceof Full2PictureRecorder;
super.onPictureResult(result, error); super.onPictureResult(result, error);
if (fullPicture && mPictureCaptureStopsPreview) { if (fullPicture && mPictureCaptureStopsPreview) {
// See comments in Full2PictureRecorder.
applyRepeatingRequestBuilder(); applyRepeatingRequestBuilder();
} }
boolean unlock = (fullPicture && getPictureMetering()) ||
(!fullPicture && getPictureSnapshotMetering()); // Some picture recorders might lock metering, and we usually run a metering sequence
// before running the recorders. So, run an unlock/reset sequence if needed.
boolean unlock = (fullPicture && getPictureMetering())
|| (!fullPicture && getPictureSnapshotMetering());
if (unlock) { if (unlock) {
unlockAndResetMetering(); unlockAndResetMetering();
} }
@ -848,6 +850,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
builder.set(CaptureRequest.CONTROL_AE_REGIONS, oldBuilder.get(CaptureRequest.CONTROL_AE_REGIONS)); builder.set(CaptureRequest.CONTROL_AE_REGIONS, oldBuilder.get(CaptureRequest.CONTROL_AE_REGIONS));
builder.set(CaptureRequest.CONTROL_AWB_REGIONS, oldBuilder.get(CaptureRequest.CONTROL_AWB_REGIONS)); builder.set(CaptureRequest.CONTROL_AWB_REGIONS, oldBuilder.get(CaptureRequest.CONTROL_AWB_REGIONS));
builder.set(CaptureRequest.CONTROL_AF_MODE, oldBuilder.get(CaptureRequest.CONTROL_AF_MODE)); builder.set(CaptureRequest.CONTROL_AF_MODE, oldBuilder.get(CaptureRequest.CONTROL_AF_MODE));
// Do NOT copy exposure or focus triggers!
} }
} }
@ -1273,10 +1276,20 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private void unlockAndResetMetering() { private void unlockAndResetMetering() {
if (getEngineState() == STATE_STARTED) { if (getEngineState() == STATE_STARTED) {
applyDefaultFocus(mRepeatingRequestBuilder);
Actions.sequence( Actions.sequence(
new UnlockAction(), new BaseAction() {
new MeterResetAction(true) @Override
protected void onStart(@NonNull ActionHolder holder) {
super.onStart(holder);
applyDefaultFocus(holder.getBuilder(this));
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_LOCK, false);
holder.getBuilder(this).set(CaptureRequest.CONTROL_AWB_LOCK, false);
holder.applyBuilder(this);
setState(STATE_COMPLETED);
// TODO should wait results?
}
},
new MeterResetAction()
).start(Camera2Engine.this); ).start(Camera2Engine.this);
} }
} }

@ -67,7 +67,7 @@ public abstract class BaseAction implements Action {
* holder stream anymore. It will soon be marked as completed. * holder stream anymore. It will soon be marked as completed.
* @param holder holder * @param holder holder
*/ */
@SuppressWarnings({"WeakerAccess", "unused"}) @SuppressWarnings("unused")
protected void onAbort(@NonNull ActionHolder holder) { protected void onAbort(@NonNull ActionHolder holder) {
// Overrideable // Overrideable
} }
@ -100,6 +100,7 @@ public abstract class BaseAction implements Action {
} }
if (state == STATE_COMPLETED) { if (state == STATE_COMPLETED) {
holder.removeAction(this); holder.removeAction(this);
onCompleted(holder);
} }
} }
} }
@ -108,11 +109,18 @@ public abstract class BaseAction implements Action {
* Whether this action has reached the completed state. * Whether this action has reached the completed state.
* @return true if completed * @return true if completed
*/ */
@SuppressWarnings("WeakerAccess")
public boolean isCompleted() { public boolean isCompleted() {
return state == STATE_COMPLETED; return state == STATE_COMPLETED;
} }
/**
* Called when this action has completed (possibly aborted).
* @param holder holder
*/
protected void onCompleted(@NonNull ActionHolder holder) {
// Overrideable
}
/** /**
* Returns the holder. * Returns the holder.
* @return the holder * @return the holder
@ -130,7 +138,6 @@ public abstract class BaseAction implements Action {
* @param <T> key type * @param <T> key type
* @return value or fallback * @return value or fallback
*/ */
@SuppressWarnings("WeakerAccess")
@NonNull @NonNull
protected <T> T readCharacteristic(@NonNull CameraCharacteristics.Key<T> key, protected <T> T readCharacteristic(@NonNull CameraCharacteristics.Key<T> key,
@NonNull T fallback) { @NonNull T fallback) {

@ -1,25 +0,0 @@
package com.otaliastudios.cameraview.engine.lock;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.BaseAction;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class UnlockAction extends BaseAction {
@Override
protected void onStart(@NonNull ActionHolder holder) {
super.onStart(holder);
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_LOCK, false);
holder.getBuilder(this).set(CaptureRequest.CONTROL_AWB_LOCK, false);
holder.applyBuilder(this);
setState(STATE_COMPLETED);
// TODO focus is managed by the engine
// TODO should wait results?
}
}

@ -24,12 +24,11 @@ public class ExposureMeter extends BaseMeter {
private static final int STATE_WAITING_PRECAPTURE = 0; private static final int STATE_WAITING_PRECAPTURE = 0;
private static final int STATE_WAITING_PRECAPTURE_END = 1; private static final int STATE_WAITING_PRECAPTURE_END = 1;
@SuppressWarnings("WeakerAccess")
public ExposureMeter(@NonNull List<MeteringRectangle> areas, boolean skipIfPossible) { public ExposureMeter(@NonNull List<MeteringRectangle> areas, boolean skipIfPossible) {
super(areas, skipIfPossible); super(areas, skipIfPossible);
} }
// TODO set trigger to null?
@Override @Override
protected boolean checkIsSupported(@NonNull ActionHolder holder) { protected boolean checkIsSupported(@NonNull ActionHolder holder) {
// In our case, this means checking if we support the AE precapture trigger. // In our case, this means checking if we support the AE precapture trigger.
@ -76,6 +75,14 @@ public class ExposureMeter extends BaseMeter {
setState(STATE_WAITING_PRECAPTURE); setState(STATE_WAITING_PRECAPTURE);
} }
@Override
protected void onCompleted(@NonNull ActionHolder holder) {
super.onCompleted(holder);
// Remove (but not apply) the risky parameter so it is not included in new requests.
// Documentation about this key says that this should be allowed.
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, null);
}
@Override @Override
public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) { @NonNull TotalCaptureResult result) {

@ -2,6 +2,8 @@ package com.otaliastudios.cameraview.engine.meter;
import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
@ -18,33 +20,53 @@ public class ExposureReset extends BaseReset {
private static final String TAG = ExposureReset.class.getSimpleName(); private static final String TAG = ExposureReset.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
public ExposureReset(boolean resetArea) { private static final int STATE_WAITING_LOCK = 0;
super(resetArea);
@SuppressWarnings("WeakerAccess")
public ExposureReset() {
super(true);
} }
@Override @Override
protected void onStarted(@NonNull ActionHolder holder, @Nullable MeteringRectangle area) { protected void onStarted(@NonNull ActionHolder holder, @Nullable MeteringRectangle area) {
boolean changed = false;
int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0);
if (area != null && maxRegions > 0) { if (area != null && maxRegions > 0) {
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_REGIONS, holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_REGIONS,
new MeteringRectangle[]{area}); new MeteringRectangle[]{area});
changed = true;
} }
// NOTE: precapture might not be supported, in which case I think it will be ignored. // NOTE: precapture might not be supported, in which case I think it will be ignored.
Integer trigger = holder.getBuilder(this).get(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER); Integer trigger = holder.getLastResult(this).get(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
LOG.w("onStarted:", "current precapture trigger is", trigger); LOG.i("onStarted:", "last precapture trigger is", trigger);
if (trigger == null || trigger == CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START) { if (trigger != null && trigger == CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START) {
LOG.w("onStarted:", "canceling precapture."); LOG.i("onStarted:", "canceling precapture.");
int newTrigger = Build.VERSION.SDK_INT >= 23 int newTrigger = Build.VERSION.SDK_INT >= 23
? CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL ? CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
: CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE; : CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, newTrigger); holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, newTrigger);
changed = true;
} }
if (changed) holder.applyBuilder(this); // Documentation about CONTROL_AE_PRECAPTURE_TRIGGER says that, if it was started but not
// followed by a CAPTURE_INTENT_STILL_PICTURE request, the internal AE routine might remain
// locked unless we unlock manually.
// This is often the case for us, since the snapshot picture recorder does not use the intent
// and anyway we use the precapture sequence for touch metering as well.
// To reset, docs suggest the use of CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL, which we do above,
// or the technique used below: locking then unlocking. This proved to be the ONLY method
// to unlock reliably, unlike the cancel trigger (which we'll run anyway).
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_LOCK, true);
holder.applyBuilder(this);
setState(STATE_WAITING_LOCK);
}
@Override
public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
super.onCaptureCompleted(holder, request, result);
if (getState() == STATE_WAITING_LOCK) {
holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_LOCK, false);
holder.applyBuilder(this);
setState(STATE_COMPLETED); setState(STATE_COMPLETED);
} }
}
} }

@ -62,7 +62,13 @@ public class FocusMeter extends BaseMeter {
holder.applyBuilder(this); holder.applyBuilder(this);
} }
// TODO Set trigger to null? @Override
protected void onCompleted(@NonNull ActionHolder holder) {
super.onCompleted(holder);
// Remove (but not apply) the risky parameter so it is not included in new requests.
// Documentation about this key says that this should be allowed.
holder.getBuilder(this).set(CaptureRequest.CONTROL_AF_TRIGGER, null);
}
@Override @Override
public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request,

@ -2,6 +2,7 @@ package com.otaliastudios.cameraview.engine.meter;
import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build; import android.os.Build;
@ -18,8 +19,9 @@ public class FocusReset extends BaseReset {
private static final String TAG = FocusReset.class.getSimpleName(); private static final String TAG = FocusReset.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
public FocusReset(boolean resetArea) { @SuppressWarnings("WeakerAccess")
super(resetArea); public FocusReset() {
super(true);
} }
@Override @Override
@ -33,9 +35,9 @@ public class FocusReset extends BaseReset {
} }
// NOTE: trigger might not be supported, in which case I think it will be ignored. // NOTE: trigger might not be supported, in which case I think it will be ignored.
Integer trigger = holder.getBuilder(this).get(CaptureRequest.CONTROL_AF_TRIGGER); Integer trigger = holder.getLastResult(this).get(CaptureResult.CONTROL_AF_TRIGGER);
LOG.w("onStarted:", "current focus trigger is", trigger); LOG.w("onStarted:", "last focus trigger is", trigger);
if (trigger == null || trigger == CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START) { if (trigger != null && trigger == CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START) {
holder.getBuilder(this).set(CaptureRequest.CONTROL_AF_TRIGGER, holder.getBuilder(this).set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
changed = true; changed = true;

@ -18,11 +18,11 @@ public class MeterResetAction extends ActionWrapper {
private final BaseAction action; private final BaseAction action;
public MeterResetAction(boolean resetAreas) { public MeterResetAction() {
this.action = Actions.together( this.action = Actions.together(
new ExposureReset(resetAreas), new ExposureReset(),
new FocusReset(resetAreas), new FocusReset(),
new WhiteBalanceReset(resetAreas) new WhiteBalanceReset()
); );
} }

@ -22,8 +22,9 @@ public class WhiteBalanceReset extends BaseReset {
private static final String TAG = WhiteBalanceReset.class.getSimpleName(); private static final String TAG = WhiteBalanceReset.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
public WhiteBalanceReset(boolean resetArea) { @SuppressWarnings("WeakerAccess")
super(resetArea); public WhiteBalanceReset() {
super(true);
} }
@Override @Override

Loading…
Cancel
Save