|
|
|
@ -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,20 +77,53 @@ 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() { |
|
|
|
|
if (supportsFocusLock()) { |
|
|
|
|
try { |
|
|
|
|
mState = STATE_WAITING_FOCUS_LOCK; |
|
|
|
|
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, |
|
|
|
|
CaptureRequest.CONTROL_AF_TRIGGER_START); |
|
|
|
|
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() { |
|
|
|
|
@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, |
|
|
|
@ -100,10 +134,15 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader |
|
|
|
|
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.
|
|
|
|
|
if (supportsFocusLock()) { |
|
|
|
|
try { |
|
|
|
|
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, |
|
|
|
|
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); |
|
|
|
|
mSession.capture(mBuilder.build(), mCallback, null); |
|
|
|
|
} catch (CameraAccessException ignore) { } |
|
|
|
|
} catch (CameraAccessException ignore) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Leave.
|
|
|
|
|
LOG.i("onImageAvailable ended."); |
|
|
|
|
dispatchResult(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|