New approach for metering

pull/574/head
Mattia Iavarone 6 years ago
parent 0731b64c2f
commit f26970a042
  1. 164
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 1
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Mapper.java
  3. 2
      demo/src/main/res/layout/activity_camera.xml

@ -101,9 +101,14 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private ImageReader mPictureReader; private ImageReader mPictureReader;
private final boolean mPictureCaptureStopsPreview = false; // can make configurable at some point private final boolean mPictureCaptureStopsPreview = false; // can make configurable at some point
// Autofocus // 3A Metering
private PointF mAutoFocusPoint; private PointF mMeteringPoint;
private Gesture mAutoFocusGesture; private Gesture mMeteringGesture;
private boolean mMeteringAEDone;
private boolean mMeteringAEStarted;
private boolean mMeteringAFDone;
private boolean mMeteringAFSuccess;
private boolean mMeteringAWBDone;
public Camera2Engine(Callback callback) { public Camera2Engine(Callback callback) {
super(callback); super(callback);
@ -234,7 +239,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
if (mPictureRecorder instanceof Full2PictureRecorder) { if (mPictureRecorder instanceof Full2PictureRecorder) {
((Full2PictureRecorder) mPictureRecorder).onCaptureProgressed(partialResult); ((Full2PictureRecorder) mPictureRecorder).onCaptureProgressed(partialResult);
} }
if (isInAutoFocus()) onAutoFocusCapture(partialResult); if (isMetering()) onMeteringCapture(partialResult);
} }
@Override @Override
@ -243,7 +248,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
if (mPictureRecorder instanceof Full2PictureRecorder) { if (mPictureRecorder instanceof Full2PictureRecorder) {
((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); ((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result);
} }
if (isInAutoFocus()) onAutoFocusCapture(result); if (isMetering()) onMeteringCapture(result);
} }
}; };
@ -554,8 +559,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
removeRepeatingRequestBuilderSurfaces(); removeRepeatingRequestBuilderSurfaces();
mRepeatingRequest = null; mRepeatingRequest = null;
mAutoFocusPoint = null; mMeteringPoint = null;
mAutoFocusGesture = null; mMeteringGesture = null;
LOG.i("onStopPreview:", "Returning."); LOG.i("onStopPreview:", "Returning.");
return Tasks.forResult(null); return Tasks.forResult(null);
} }
@ -853,12 +858,24 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
for (int mode : modes) { for (int mode : modes) {
for (int availableMode : availableModes) { for (int availableMode : availableModes) {
if (mode == availableMode) { if (mode == availableMode) {
LOG.i("applyFlash: setting CONTROL_AE_MODE to", mode);
builder.set(CaptureRequest.CONTROL_AE_MODE, mode); builder.set(CaptureRequest.CONTROL_AE_MODE, mode);
if (mFlash == Flash.TORCH) { if (mFlash == Flash.TORCH) {
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_TORCH);
} else if (mFlash == Flash.OFF) { } else if (mFlash == Flash.OFF) {
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_OFF);
} else { // Go to OFF anyway, the AE mode will deal with flash.
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_OFF);
} }
// On some devices, switching from TORCH/OFF to AUTO/ON is not immediately
// reflected (for example, torch stays active) unless we do as follows.
// It's just a way to wake up the AE routine.
builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
return true; return true;
} }
} }
@ -1105,21 +1122,26 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
//endregion //endregion
//region Auto Focus //region 3A Metering
@Override @Override
public void startAutoFocus(@Nullable final Gesture gesture, @NonNull final PointF point) { public void startAutoFocus(@Nullable final Gesture gesture, @NonNull final PointF point) {
LOG.i("startAutoFocus", "dispatching. Gesture:", gesture); // 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) {
LOG.i("startMetering", "dispatching. Gesture:", gesture);
mHandler.run(new Runnable() { mHandler.run(new Runnable() {
@Override @Override
public void run() { public void run() {
LOG.i("startAutoFocus", "executing. Preview state:", getPreviewState()); LOG.i("startMetering", "executing. Preview state:", getPreviewState());
// This will only work when we have a preview, since it launches the preview in the end. // This will only work when we have a preview, since it launches the preview in the end.
// Even without this it would need the bind state at least, since we need the preview size. // Even without this it would need the bind state at least, since we need the preview size.
if (!mCameraOptions.isAutoFocusSupported()) return; if (!mCameraOptions.isAutoFocusSupported()) return;
if (getPreviewState() < STATE_STARTED) return; if (getPreviewState() < STATE_STARTED) return;
mAutoFocusPoint = point; mMeteringPoint = point;
mAutoFocusGesture = gesture; mMeteringGesture = gesture;
// This is a good Q/A. https://stackoverflow.com/a/33181620/4288782 // This is a good Q/A. https://stackoverflow.com/a/33181620/4288782
// At first, the point is relative to the View system and does not account our own cropping. // At first, the point is relative to the View system and does not account our own cropping.
@ -1233,9 +1255,30 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
// When this ends, we will reset everything. We know CONTROL_AF_MODE_AUTO is available // When this ends, we will reset everything. We know CONTROL_AF_MODE_AUTO is available
// because we have called cameraOptions.isAutoFocusSupported(). // because we have called cameraOptions.isAutoFocusSupported().
mCallback.dispatchOnFocusStart(gesture, point); mCallback.dispatchOnFocusStart(gesture, point);
// AF
mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); mMeteringAFDone = false;
// AE
boolean isLegacy = readCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) !=
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
Integer aeMode = mRepeatingRequestBuilder.get(CaptureRequest.CONTROL_AE_MODE);
boolean isAEOn = aeMode != null &&
(aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
boolean supportsAE = !isLegacy && isAEOn;
if (supportsAE) {
mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
}
mMeteringAEStarted = false;
mMeteringAEDone = !supportsAE; // If supported, we're not done.
// AWB
mMeteringAWBDone = true; // TODO support this if possible
// 9. Apply everything.
applyRepeatingRequestBuilder(); applyRepeatingRequestBuilder();
} }
}); });
@ -1265,32 +1308,44 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
/** /**
* Whether we are in an auto focus operation, which means that * Whether we are in a metering operation, which means, among other things, that
* {@link CaptureResult#CONTROL_AF_MODE} is set to {@link CaptureResult#CONTROL_AF_MODE_AUTO}. * {@link CaptureResult#CONTROL_AF_MODE} is set to {@link CaptureResult#CONTROL_AF_MODE_AUTO}.
* @return true if we're in auto focus * @return true if we're in a metering operation
*/ */
private boolean isInAutoFocus() { private boolean isMetering() {
return mAutoFocusPoint != null; return mMeteringPoint != null;
} }
/** /**
* If this is called, we're in autofocus and {@link CaptureResult#CONTROL_AF_MODE} * If this is called, we're in 3A metering.
* is set to {@link CaptureResult#CONTROL_AF_MODE_AUTO}.
* @param result the result * @param result the result
*/ */
private void onAutoFocusCapture(@NonNull CaptureResult result) { private void onMeteringCapture(@NonNull CaptureResult result) {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); checkMeteringAutoFocus(result);
if (afState == null) { checkMeteringAutoExposure(result);
LOG.i("onAutoFocusCapture", "afState is null! This can happen for partial results. Waiting."); checkMeteringAutoWhiteBalance(result);
return; if (mMeteringAFDone && mMeteringAEDone && mMeteringAWBDone) {
// Use the AF success for dispatching the callback, since the public
// callback is currently related to AF.
onMeteringEnd(mMeteringAFSuccess);
} }
}
private void checkMeteringAutoFocus(@NonNull CaptureResult result) {
if (mMeteringAFDone || !(result instanceof TotalCaptureResult)) return;
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
LOG.i("checkMeteringAutoFocus:", "afState:", afState);
if (afState == null) return;
switch (afState) { switch (afState) {
case CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED: { case CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED: {
onAutoFocusEnd(true); mMeteringAFDone = true;
mMeteringAFSuccess = true;
break; break;
} }
case CaptureRequest.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: { case CaptureRequest.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: {
onAutoFocusEnd(false); mMeteringAFDone = true;
mMeteringAFSuccess = false;
break; break;
} }
case CaptureRequest.CONTROL_AF_STATE_INACTIVE: break; case CaptureRequest.CONTROL_AF_STATE_INACTIVE: break;
@ -1299,36 +1354,63 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
} }
private void checkMeteringAutoExposure(@NonNull CaptureResult result) {
if (mMeteringAEDone || !(result instanceof TotalCaptureResult)) return;
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
LOG.i("checkMeteringAutoExposure:", "aeState:", aeState);
if (aeState == null) return;
if (!mMeteringAEStarted) {
if (aeState == CaptureRequest.CONTROL_AE_STATE_PRECAPTURE) {
mMeteringAEStarted = true;
}
} else {
if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED
|| aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mMeteringAEDone = true;
}
}
}
private void checkMeteringAutoWhiteBalance(@NonNull CaptureResult result) {
if (mMeteringAWBDone) return;
// No op until being implemented.
}
/** /**
* Called by {@link #onAutoFocusCapture(CaptureResult)} when we detect that the * Called by {@link #onMeteringCapture(CaptureResult)} when we detect that the
* auto focus operataion has ended. * auto focus operataion has ended.
* @param success true if success * @param success true if success
*/ */
private void onAutoFocusEnd(boolean success) { private void onMeteringEnd(boolean success) {
Gesture gesture = mAutoFocusGesture; LOG.w("onMeteringEnd - success:", success);
PointF point = mAutoFocusPoint; Gesture gesture = mMeteringGesture;
mAutoFocusGesture = null; PointF point = mMeteringPoint;
mAutoFocusPoint = null; mMeteringGesture = null;
mMeteringPoint = null;
if (point == null) return; if (point == null) return;
mCallback.dispatchOnFocusEnd(gesture, success, point); mCallback.dispatchOnFocusEnd(gesture, success, point);
mHandler.remove(mAutoFocusResetRunnable); mHandler.remove(mMeteringResetRunnable);
if (shouldResetAutoFocus()) { if (shouldResetAutoFocus()) {
mHandler.post(getAutoFocusResetDelay(), mAutoFocusResetRunnable); mHandler.post(getAutoFocusResetDelay(), mMeteringResetRunnable);
} }
} }
private Runnable mAutoFocusResetRunnable = new Runnable() { private Runnable mMeteringResetRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (getEngineState() < STATE_STARTED) return; if (getEngineState() < STATE_STARTED) return;
LOG.i("Running the 3A metering reset runnable.");
Rect whole = readCharacteristic(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE, new Rect()); Rect whole = readCharacteristic(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE, new Rect());
MeteringRectangle[] rectangle = new MeteringRectangle[]{new MeteringRectangle(whole, MeteringRectangle.METERING_WEIGHT_DONT_CARE)}; MeteringRectangle[] rectangle = new MeteringRectangle[]{new MeteringRectangle(whole, MeteringRectangle.METERING_WEIGHT_DONT_CARE)};
int maxReagionsAf = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); int maxRegionsAf = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0);
int maxReagionsAe = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); int maxRegionsAe = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0);
int maxReagionsAwb = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); int maxRegionsAwb = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0);
if (maxReagionsAf > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, rectangle); if (maxRegionsAf > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, rectangle);
if (maxReagionsAe > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, rectangle); if (maxRegionsAe > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, rectangle);
if (maxReagionsAwb > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AWB_REGIONS, rectangle); if (maxRegionsAwb > 0) mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AWB_REGIONS, rectangle);
// mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
// mRepeatingRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
applyDefaultFocus(mRepeatingRequestBuilder); applyDefaultFocus(mRepeatingRequestBuilder);
applyRepeatingRequestBuilder(); // only if preview started already applyRepeatingRequestBuilder(); // only if preview started already
} }

@ -177,7 +177,6 @@ public abstract class Mapper {
HDR.put(Hdr.ON, 18 /* CameraCharacteristics.CONTROL_SCENE_MODE_HDR */); HDR.put(Hdr.ON, 18 /* CameraCharacteristics.CONTROL_SCENE_MODE_HDR */);
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public <T> T map(Flash flash) { public <T> T map(Flash flash) {
return (T) FLASH.get(flash); return (T) FLASH.get(flash);

@ -20,7 +20,7 @@
app:cameraPreview="glSurface" app:cameraPreview="glSurface"
app:cameraPlaySounds="true" app:cameraPlaySounds="true"
app:cameraGrid="off" app:cameraGrid="off"
app:cameraFlash="off" app:cameraFlash="torch"
app:cameraAudio="on" app:cameraAudio="on"
app:cameraFacing="back" app:cameraFacing="back"
app:cameraGestureTap="autoFocus" app:cameraGestureTap="autoFocus"

Loading…
Cancel
Save