Revisit Full2PictureRecorder using actions

pull/580/head
Mattia Iavarone 6 years ago
parent eb6193757c
commit f08221b46a
  1. 32
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java
  3. 113
      cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java

@ -137,6 +137,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
return value == null ? fallback : value; return value == null ? fallback : value;
} }
@SuppressWarnings("DuplicateBranchesInSwitch")
@NonNull @NonNull
private CameraException createCameraException(@NonNull CameraAccessException exception) { private CameraException createCameraException(@NonNull CameraAccessException exception) {
int reason; int reason;
@ -151,6 +152,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
return new CameraException(exception, reason); return new CameraException(exception, reason);
} }
@SuppressWarnings("DuplicateBranchesInSwitch")
@NonNull @NonNull
private CameraException createCameraException(int stateCallbackError) { private CameraException createCameraException(int stateCallbackError) {
int reason; int reason;
@ -246,10 +248,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private final CameraCaptureSession.CaptureCallback mRepeatingRequestCallback = new CameraCaptureSession.CaptureCallback() { private final CameraCaptureSession.CaptureCallback mRepeatingRequestCallback = new CameraCaptureSession.CaptureCallback() {
@Override @Override
public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) { 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) { for (Action action : mActions) {
action.onCaptureStarted(Camera2Engine.this, request); action.onCaptureStarted(Camera2Engine.this, request);
} }
@ -257,7 +255,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@Override @Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
super.onCaptureProgressed(session, request, partialResult);
if (mMeter != null && mMeter.isMetering()) { if (mMeter != null && mMeter.isMetering()) {
mMeter.onCapture(partialResult); mMeter.onCapture(partialResult);
} }
@ -271,11 +268,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@Override @Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
mLastRepeatingResult = result; mLastRepeatingResult = result;
if (mPictureRecorder instanceof Full2PictureRecorder) {
((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result);
}
if (mPictureRecorder instanceof Snapshot2PictureRecorder) { if (mPictureRecorder instanceof Snapshot2PictureRecorder) {
((Snapshot2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); ((Snapshot2PictureRecorder) mPictureRecorder).onCaptureCompleted(result);
} }
@ -683,14 +676,18 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} else { } else {
LOG.i("onTakePicture:", "doMetering is false. Performing."); LOG.i("onTakePicture:", "doMetering is false. Performing.");
try { 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); CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
applyAllParameters(builder, mRepeatingRequestBuilder); applyAllParameters(builder, mRepeatingRequestBuilder);
mPictureRecorder = new Full2PictureRecorder(stub, this, mPictureRecorder = new Full2PictureRecorder(stub, this, builder, mPictureReader);
mSession,
mRepeatingRequestCallback,
builder,
mPictureReader,
mPictureCaptureStopsPreview);
mPictureRecorder.take(); mPictureRecorder.take();
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
throw createCameraException(e); throw createCameraException(e);
@ -1421,5 +1418,10 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
applyRepeatingRequestBuilder(); applyRepeatingRequestBuilder();
} }
@Override
public void applyBuilder(@NonNull Action source, @NonNull CaptureRequest.Builder builder) throws CameraAccessException {
mSession.capture(builder.build(), mRepeatingRequestCallback, null);
}
//endregion //endregion
} }

@ -1,5 +1,6 @@
package com.otaliastudios.cameraview.engine.action; package com.otaliastudios.cameraview.engine.action;
import android.hardware.camera2.CameraAccessException;
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.CaptureResult;
@ -23,4 +24,6 @@ public interface ActionHolder {
CaptureRequest.Builder getBuilder(@NonNull Action action); CaptureRequest.Builder getBuilder(@NonNull Action action);
void applyBuilder(@NonNull Action source); void applyBuilder(@NonNull Action source);
void applyBuilder(@NonNull Action source, @NonNull CaptureRequest.Builder builder) throws CameraAccessException;
} }

@ -1,18 +1,18 @@
package com.otaliastudios.cameraview.picture; package com.otaliastudios.cameraview.picture;
import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image; import android.media.Image;
import android.media.ImageReader; import android.media.ImageReader;
import android.os.Build; import android.os.Build;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.PictureResult; 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.ExifHelper;
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; import com.otaliastudios.cameraview.internal.utils.WorkerHandler;
@ -21,7 +21,6 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.exifinterface.media.ExifInterface; 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 String TAG = Full2PictureRecorder.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
private static final int STATE_IDLE = 0; private final ActionHolder mHolder;
private static final int STATE_WAITING_CAPTURE = 1; private final Action mAction;
private static final int STATE_WAITING_IMAGE = 2; private final ImageReader mPictureReader;
private final CaptureRequest.Builder mPictureBuilder;
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;
public Full2PictureRecorder(@NonNull PictureResult.Stub stub, public Full2PictureRecorder(@NonNull PictureResult.Stub stub,
@Nullable PictureResultListener listener, @NonNull Camera2Engine engine,
@NonNull CameraCaptureSession session,
@NonNull CameraCaptureSession.CaptureCallback callback,
@NonNull CaptureRequest.Builder pictureBuilder, @NonNull CaptureRequest.Builder pictureBuilder,
@NonNull ImageReader pictureReader, @NonNull ImageReader pictureReader) {
boolean stopPreviewBeforeCapture) { super(stub, engine);
super(stub, listener); mHolder = engine;
mSession = session;
mCallback = callback;
mPictureBuilder = pictureBuilder; mPictureBuilder = pictureBuilder;
mStopPreviewBeforeCapture = stopPreviewBeforeCapture;
mPictureReader = pictureReader; mPictureReader = pictureReader;
mPictureReader.setOnImageAvailableListener(this, WorkerHandler.get().getHandler()); mPictureReader.setOnImageAvailableListener(this, WorkerHandler.get().getHandler());
} mAction = new BaseAction() {
@Override @Override
public void take() { protected void onStart(@NonNull ActionHolder holder) {
try { super.onStart(holder);
mState = STATE_WAITING_CAPTURE; mPictureBuilder.addTarget(mPictureReader.getSurface());
mPictureBuilder.addTarget(mPictureReader.getSurface()); mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation);
mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation); mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE);
mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE); try {
if (mStopPreviewBeforeCapture) { holder.applyBuilder(this, mPictureBuilder);
// These two are present in official samples and are probably meant to speed things up? } catch (CameraAccessException e) {
// But from my tests, they actually make everything slower. So this is disabled by default mResult = null;
// with a boolean coming from the engine. Maybe in the future we can make this configurable mError = e;
// as some people might want to stop the preview while picture is being taken even if it dispatchResult();
// increases the latency. }
mSession.stopRepeating();
mSession.abortCaptures();
} }
mSequenceId = mSession.capture(mPictureBuilder.build(), mCallback, null);
} catch (CameraAccessException e) {
mResult = null;
mError = e;
dispatchResult();
}
}
public void onCaptureStarted(@NonNull CaptureRequest request) { @Override
if (mState == STATE_WAITING_CAPTURE) { public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) {
if (request.getTag() == (Integer) CameraDevice.TEMPLATE_STILL_CAPTURE) { super.onCaptureStarted(holder, request);
dispatchOnShutter(false); 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) { @Override
if (mState == STATE_WAITING_CAPTURE) { public void take() {
if (result.getSequenceId() == mSequenceId) { mAction.start(mHolder);
// 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 @Override
public void onImageAvailable(ImageReader reader) { public void onImageAvailable(ImageReader reader) {
LOG.i("onImageAvailable started."); LOG.i("onImageAvailable started.");
mState = STATE_IDLE;
// Read the JPEG. // Read the JPEG.
Image image = null; Image image = null;
//noinspection TryFinallyCanBeTryWithResources //noinspection TryFinallyCanBeTryWithResources
@ -133,7 +108,8 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
mResult.rotation = 0; mResult.rotation = 0;
try { try {
ExifInterface exif = new ExifInterface(new ByteArrayInputStream(mResult.data)); 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); mResult.rotation = ExifHelper.readExifOrientation(exifOrientation);
} catch (IOException ignore) { } } catch (IOException ignore) { }
@ -141,11 +117,4 @@ public class Full2PictureRecorder extends PictureRecorder implements ImageReader
LOG.i("onImageAvailable ended."); LOG.i("onImageAvailable ended.");
dispatchResult(); dispatchResult();
} }
@Override
protected void dispatchResult() {
mState = STATE_IDLE;
super.dispatchResult();
}
} }

Loading…
Cancel
Save