diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 6ef11363..5dbb9ec8 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -137,6 +137,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv return value == null ? fallback : value; } + @SuppressWarnings("DuplicateBranchesInSwitch") @NonNull private CameraException createCameraException(@NonNull CameraAccessException exception) { int reason; @@ -151,6 +152,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv return new CameraException(exception, reason); } + @SuppressWarnings("DuplicateBranchesInSwitch") @NonNull private CameraException createCameraException(int stateCallbackError) { int reason; @@ -246,10 +248,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv private final CameraCaptureSession.CaptureCallback mRepeatingRequestCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) { - super.onCaptureStarted(session, request, timestamp, frameNumber); - if (mPictureRecorder instanceof Full2PictureRecorder) { - ((Full2PictureRecorder) mPictureRecorder).onCaptureStarted(request); - } for (Action action : mActions) { action.onCaptureStarted(Camera2Engine.this, request); } @@ -257,7 +255,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { - super.onCaptureProgressed(session, request, partialResult); if (mMeter != null && mMeter.isMetering()) { mMeter.onCapture(partialResult); } @@ -271,11 +268,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { - super.onCaptureCompleted(session, request, result); mLastRepeatingResult = result; - if (mPictureRecorder instanceof Full2PictureRecorder) { - ((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); - } if (mPictureRecorder instanceof Snapshot2PictureRecorder) { ((Snapshot2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); } @@ -683,14 +676,18 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } else { LOG.i("onTakePicture:", "doMetering is false. Performing."); try { + if (mPictureCaptureStopsPreview) { + // These two are present in official samples and are probably meant to speed things up? + // But from my tests, they actually make everything slower. So this is disabled by default + // with a boolean flag. Maybe in the future we can make this configurable as some + // people might want to stop the preview while picture is being taken even if it + // increases the latency. + mSession.stopRepeating(); + mSession.abortCaptures(); + } CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); applyAllParameters(builder, mRepeatingRequestBuilder); - mPictureRecorder = new Full2PictureRecorder(stub, this, - mSession, - mRepeatingRequestCallback, - builder, - mPictureReader, - mPictureCaptureStopsPreview); + mPictureRecorder = new Full2PictureRecorder(stub, this, builder, mPictureReader); mPictureRecorder.take(); } catch (CameraAccessException e) { throw createCameraException(e); @@ -1421,5 +1418,10 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv applyRepeatingRequestBuilder(); } + @Override + public void applyBuilder(@NonNull Action source, @NonNull CaptureRequest.Builder builder) throws CameraAccessException { + mSession.capture(builder.build(), mRepeatingRequestCallback, null); + } + //endregion } \ No newline at end of file diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java index ee2ca2c2..aa65d900 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java @@ -1,5 +1,6 @@ package com.otaliastudios.cameraview.engine.action; +import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; @@ -23,4 +24,6 @@ public interface ActionHolder { CaptureRequest.Builder getBuilder(@NonNull Action action); void applyBuilder(@NonNull Action source); + + void applyBuilder(@NonNull Action source, @NonNull CaptureRequest.Builder builder) throws CameraAccessException; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java index ce51b09d..6a53e49e 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java @@ -1,18 +1,18 @@ package com.otaliastudios.cameraview.picture; 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; -import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.media.ImageReader; import android.os.Build; import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.PictureResult; +import com.otaliastudios.cameraview.engine.Camera2Engine; +import com.otaliastudios.cameraview.engine.action.Action; +import com.otaliastudios.cameraview.engine.action.ActionHolder; +import com.otaliastudios.cameraview.engine.action.BaseAction; import com.otaliastudios.cameraview.internal.utils.ExifHelper; import com.otaliastudios.cameraview.internal.utils.WorkerHandler; @@ -21,7 +21,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.exifinterface.media.ExifInterface; @@ -34,81 +33,57 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader private static final String TAG = Full2PictureRecorder.class.getSimpleName(); private static final CameraLogger LOG = CameraLogger.create(TAG); - private static final int STATE_IDLE = 0; - private static final int STATE_WAITING_CAPTURE = 1; - private static final int STATE_WAITING_IMAGE = 2; - - private CameraCaptureSession mSession; - private CameraCaptureSession.CaptureCallback mCallback; - private ImageReader mPictureReader; - private CaptureRequest.Builder mPictureBuilder; - private boolean mStopPreviewBeforeCapture; - private int mState = STATE_IDLE; - private int mSequenceId; + private final ActionHolder mHolder; + private final Action mAction; + private final ImageReader mPictureReader; + private final CaptureRequest.Builder mPictureBuilder; public Full2PictureRecorder(@NonNull PictureResult.Stub stub, - @Nullable PictureResultListener listener, - @NonNull CameraCaptureSession session, - @NonNull CameraCaptureSession.CaptureCallback callback, + @NonNull Camera2Engine engine, @NonNull CaptureRequest.Builder pictureBuilder, - @NonNull ImageReader pictureReader, - boolean stopPreviewBeforeCapture) { - super(stub, listener); - mSession = session; - mCallback = callback; + @NonNull ImageReader pictureReader) { + super(stub, engine); + mHolder = engine; mPictureBuilder = pictureBuilder; - mStopPreviewBeforeCapture = stopPreviewBeforeCapture; mPictureReader = pictureReader; mPictureReader.setOnImageAvailableListener(this, WorkerHandler.get().getHandler()); - } - - @Override - public void take() { - try { - mState = STATE_WAITING_CAPTURE; - mPictureBuilder.addTarget(mPictureReader.getSurface()); - mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation); - mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE); - if (mStopPreviewBeforeCapture) { - // These two are present in official samples and are probably meant to speed things up? - // But from my tests, they actually make everything slower. So this is disabled by default - // with a boolean coming from the engine. Maybe in the future we can make this configurable - // as some people might want to stop the preview while picture is being taken even if it - // increases the latency. - mSession.stopRepeating(); - mSession.abortCaptures(); + mAction = new BaseAction() { + + @Override + protected void onStart(@NonNull ActionHolder holder) { + super.onStart(holder); + mPictureBuilder.addTarget(mPictureReader.getSurface()); + mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation); + mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE); + try { + holder.applyBuilder(this, mPictureBuilder); + } catch (CameraAccessException e) { + mResult = null; + mError = e; + dispatchResult(); + } } - mSequenceId = mSession.capture(mPictureBuilder.build(), mCallback, null); - } catch (CameraAccessException e) { - mResult = null; - mError = e; - dispatchResult(); - } - } - public void onCaptureStarted(@NonNull CaptureRequest request) { - if (mState == STATE_WAITING_CAPTURE) { - if (request.getTag() == (Integer) CameraDevice.TEMPLATE_STILL_CAPTURE) { - dispatchOnShutter(false); + @Override + public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { + super.onCaptureStarted(holder, request); + if (request.getTag() == (Integer) CameraDevice.TEMPLATE_STILL_CAPTURE) { + LOG.i("onCaptureStarted:", "Dispatching picture shutter."); + dispatchOnShutter(false); + setState(STATE_COMPLETED); + } } - } + }; } - public void onCaptureCompleted(@NonNull TotalCaptureResult result) { - if (mState == STATE_WAITING_CAPTURE) { - if (result.getSequenceId() == mSequenceId) { - // This has no real use for now other than logging. - LOG.i("onCaptureCompleted:", "Got result, moving to STATE_WAITING_IMAGE"); - mState = STATE_WAITING_IMAGE; - } - } + @Override + public void take() { + mAction.start(mHolder); } @Override public void onImageAvailable(ImageReader reader) { LOG.i("onImageAvailable started."); - mState = STATE_IDLE; - // Read the JPEG. Image image = null; //noinspection TryFinallyCanBeTryWithResources @@ -133,7 +108,8 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader mResult.rotation = 0; try { ExifInterface exif = new ExifInterface(new ByteArrayInputStream(mResult.data)); - int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL); mResult.rotation = ExifHelper.readExifOrientation(exifOrientation); } catch (IOException ignore) { } @@ -141,11 +117,4 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader LOG.i("onImageAvailable ended."); dispatchResult(); } - - - @Override - protected void dispatchResult() { - mState = STATE_IDLE; - super.dispatchResult(); - } }