Fix Camera2 picture recorder (#521)

* Fix Camera2 picture recorder

* Improve logic, check focusLock when releasing
debug-base
Mattia Iavarone 5 years ago committed by GitHub
parent 03b5df7a10
commit bc3c72cdc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 132
      cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java

@ -629,7 +629,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
try {
CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
applyAllParameters(builder);
mPictureRecorder = new Full2PictureRecorder(stub, this, mSession,
mPictureRecorder = new Full2PictureRecorder(stub, this,
mCameraCharacteristics,
mSession,
mRepeatingRequestBuilder,
mRepeatingRequestCallback,
builder,

@ -1,8 +1,8 @@
package com.otaliastudios.cameraview.picture;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@ -10,9 +10,7 @@ import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.view.Surface;
import com.otaliastudios.cameraview.CameraException;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.internal.utils.ExifHelper;
@ -46,6 +44,7 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
private static final int REQUEST_TAG = CameraDevice.TEMPLATE_STILL_CAPTURE;
private CameraCaptureSession mSession;
private CameraCharacteristics mCharacteristics;
private CaptureRequest.Builder mBuilder;
private CameraCaptureSession.CaptureCallback mCallback;
private ImageReader mPictureReader;
@ -55,6 +54,7 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
public Full2PictureRecorder(@NonNull PictureResult.Stub stub,
@Nullable PictureResultListener listener,
@NonNull CameraCharacteristics characteristics,
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest.Builder builder,
@NonNull CameraCaptureSession.CaptureCallback callback,
@ -62,6 +62,7 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
@NonNull ImageReader pictureReader,
boolean stopPreviewBeforeCapture) {
super(stub, listener);
mCharacteristics = characteristics;
mSession = session;
mBuilder = builder;
mCallback = callback;
@ -76,34 +77,72 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
runFocusLock();
}
private boolean supportsFocusLock() {
//noinspection ConstantConditions
int afMode = mBuilder.get(CaptureRequest.CONTROL_AF_MODE);
// Exclude OFF and EDOF as per their docs.
return afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO
|| afMode == CameraCharacteristics.CONTROL_AF_MODE_MACRO;
}
private void runFocusLock() {
try {
mState = STATE_WAITING_FOCUS_LOCK;
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException e) {
mResult = null;
mError = e;
dispatchResult();
if (supportsFocusLock()) {
try {
mState = STATE_WAITING_FOCUS_LOCK;
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException e) {
mResult = null;
mError = e;
dispatchResult();
}
} else {
LOG.w("Device does not support focus lock. Running precapture.");
runPrecapture(null);
}
}
private void runPrecapture() {
try {
mState = STATE_WAITING_PRECAPTURE_START;
mBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException e) {
mResult = null;
mError = e;
dispatchResult();
@SuppressWarnings("ConstantConditions")
private boolean supportsPrecapture() {
// Precapture is not supported on legacy devices.
int level = mCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) return false;
// We still have to check the current AE mode, see CaptureResult.CONTROL_AE_STATE.
int aeMode = mBuilder.get(CaptureRequest.CONTROL_AE_MODE);
return 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
|| aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */;
}
private void runPrecapture(@Nullable CaptureResult lastResult) {
//noinspection ConstantConditions
boolean shouldSkipPrecapture = lastResult != null
&& lastResult.get(CaptureResult.CONTROL_AE_STATE) != null
&& lastResult.get(CaptureResult.CONTROL_AE_STATE) == CaptureResult.CONTROL_AE_STATE_CONVERGED;
if (supportsPrecapture() && !shouldSkipPrecapture) {
try {
mState = STATE_WAITING_PRECAPTURE_START;
mBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException e) {
mResult = null;
mError = e;
dispatchResult();
}
} else {
LOG.w("Device does not support precapture. Running capture.");
runCapture();
}
}
private void runCapture() {
try {
mState = STATE_WAITING_CAPTURE;
mPictureBuilder.setTag(REQUEST_TAG);
mPictureBuilder.addTarget(mPictureReader.getSurface());
mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation);
@ -143,26 +182,21 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
case STATE_IDLE: break;
case STATE_WAITING_FOCUS_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
runCapture();
} else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
if (afState == null
|| afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
|| afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_WAITING_CAPTURE;
runCapture();
} else {
runPrecapture();
}
runPrecapture(result);
}
break;
}
case STATE_WAITING_PRECAPTURE_START: {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
if (aeState == null
|| aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
// The one above is a transient state, which means it might not be reported
// by the camera. So in addition let's also check for the precature end states.
|| aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED
|| aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mState = STATE_WAITING_PRECAPTURE_END;
}
break;
@ -170,8 +204,15 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
case STATE_WAITING_PRECAPTURE_END: {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null
|| aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_WAITING_CAPTURE;
|| aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED
|| aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED
// The two above are the correct states. However, just for safety, and
// since we got STATE_WAITING_PRECAPTURE_START already, let's accept anything
// other than the precapturing state. We don't want to get stuck here.
|| aeState == CaptureRequest.CONTROL_AE_STATE_SEARCHING // Camera is in normal AE routine. Should never happen.
|| aeState == CaptureRequest.CONTROL_AE_STATE_INACTIVE // AE is OFF. Should never happen.
|| aeState == CaptureResult.CONTROL_AE_STATE_LOCKED // AE has been locked. Should never happen.
) {
runCapture();
}
break;
@ -188,6 +229,7 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
@Override
public void onImageAvailable(ImageReader reader) {
LOG.i("onImageAvailable started.");
mState = STATE_IDLE;
// Read the JPEG.
@ -219,13 +261,17 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
} catch (IOException ignore) { }
// Before leaving, unlock focus.
try {
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException ignore) { }
if (supportsFocusLock()) {
try {
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
mSession.capture(mBuilder.build(), mCallback, null);
} catch (CameraAccessException ignore) {
}
}
// Leave.
LOG.i("onImageAvailable ended.");
dispatchResult();
}

Loading…
Cancel
Save