Create OneShotAction

pull/580/head
Mattia Iavarone 6 years ago
parent f48c6bd5b7
commit 156bb37355
  1. 201
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 28
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/OneShotAction.java

@ -44,6 +44,7 @@ import com.otaliastudios.cameraview.controls.Mode;
import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.controls.WhiteBalance;
import com.otaliastudios.cameraview.engine.action.Action; import com.otaliastudios.cameraview.engine.action.Action;
import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.OneShotAction;
import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper;
import com.otaliastudios.cameraview.engine.offset.Axis; import com.otaliastudios.cameraview.engine.offset.Axis;
import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.engine.offset.Reference;
@ -66,7 +67,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAvailableListener, public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAvailableListener,
@ -86,7 +86,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private CameraCharacteristics mCameraCharacteristics; private CameraCharacteristics mCameraCharacteristics;
private CameraCaptureSession mSession; private CameraCaptureSession mSession;
private CaptureRequest.Builder mRepeatingRequestBuilder; private CaptureRequest.Builder mRepeatingRequestBuilder;
private CameraCaptureSession.CaptureCallback mRepeatingRequestCallback;
private TotalCaptureResult mLastRepeatingResult; private TotalCaptureResult mLastRepeatingResult;
private final Camera2Mapper mMapper = Camera2Mapper.get(); private final Camera2Mapper mMapper = Camera2Mapper.get();
@ -230,107 +229,104 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
* {@link #createRepeatingRequestBuilder(int)}. * {@link #createRepeatingRequestBuilder(int)}.
*/ */
private void applyRepeatingRequestBuilder() { private void applyRepeatingRequestBuilder() {
applyRepeatingRequestBuilder(true, CameraException.REASON_DISCONNECTED, null); applyRepeatingRequestBuilder(true, CameraException.REASON_DISCONNECTED);
} }
private void applyRepeatingRequestBuilder(boolean checkStarted, int errorReason, @Nullable final Runnable onFirstFrame) { private void applyRepeatingRequestBuilder(boolean checkStarted, int errorReason) {
if (!checkStarted || getPreviewState() == STATE_STARTED) { if (!checkStarted || getPreviewState() == STATE_STARTED) {
try { try {
CaptureRequest repeatingRequest = mRepeatingRequestBuilder.build(); mSession.setRepeatingRequest(mRepeatingRequestBuilder.build(),
final AtomicBoolean firstFrame = new AtomicBoolean(false); mRepeatingRequestCallback, null);
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 (firstFrame.compareAndSet(false, true) && onFirstFrame != null) {
onFirstFrame.run();
}
if (mPictureRecorder instanceof Full2PictureRecorder) {
((Full2PictureRecorder) mPictureRecorder).onCaptureStarted(request);
}
for (Action action : mActions) {
action.onCaptureStarted(Camera2Engine.this, request);
}
}
@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);
}
if (mLocker != null && mLocker.isLocking()) {
mLocker.onCapture(partialResult);
}
for (Action action : mActions) {
action.onCaptureProgressed(Camera2Engine.this, request, partialResult);
}
}
@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);
}
if (mMeter != null && mMeter.isMetering()) {
mMeter.onCapture(result);
}
if (mLocker != null && mLocker.isLocking()) {
mLocker.onCapture(result);
}
for (Action action : mActions) {
action.onCaptureProgressed(Camera2Engine.this, request, result);
}
Integer aeMode = result.get(CaptureResult.CONTROL_AE_MODE);
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
Boolean aeLock = result.get(CaptureResult.CONTROL_AE_LOCK);
Integer aeTriggerState = result.get(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
Integer afTriggerState = result.get(CaptureResult.CONTROL_AF_TRIGGER);
String log = "aeMode: " + aeMode + " aeLock: " + aeLock +
" aeState: " + aeState + " aeTriggerState: " + aeTriggerState +
" afState: " + afState + " afTriggerState: " + afTriggerState;
if (!log.equals(mLastLog)) {
mLastLog = log;
LOG.w(log);
}
// START
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 0 afState: 2 afTriggerState: 0
//
// DURING metering (focus skips)
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 0 afState: 0 afTriggerState: 0
// aeMode: 3 aeLock: false aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// DURING locking (focus skips)
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 1 afState: 0 afTriggerState: 0
// aeMode: 3 aeLock: true aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// AFTER locked
// aeMode: 3 aeLock: true aeState: 3 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// AFTER super.take() called
// aeMode: 1 aeLock: true aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
// aeMode: 1 aeLock: true aeState: 3 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// Reverting flash changes + reset lock + reset metering
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 2(1 now) afState: 2 afTriggerState: 0
// aeMode: 3 aeLock: false aeState: 1 aeTriggerState: 2(1 now) afState: 2 afTriggerState: 0
}
};
mSession.setRepeatingRequest(repeatingRequest, mRepeatingRequestCallback, null);
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
throw new CameraException(e, errorReason); throw new CameraException(e, errorReason);
} }
} }
} }
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);
}
}
@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);
}
if (mLocker != null && mLocker.isLocking()) {
mLocker.onCapture(partialResult);
}
for (Action action : mActions) {
action.onCaptureProgressed(Camera2Engine.this, request, partialResult);
}
}
@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);
}
if (mMeter != null && mMeter.isMetering()) {
mMeter.onCapture(result);
}
if (mLocker != null && mLocker.isLocking()) {
mLocker.onCapture(result);
}
for (Action action : mActions) {
action.onCaptureProgressed(Camera2Engine.this, request, result);
}
Integer aeMode = result.get(CaptureResult.CONTROL_AE_MODE);
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
Boolean aeLock = result.get(CaptureResult.CONTROL_AE_LOCK);
Integer aeTriggerState = result.get(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
Integer afTriggerState = result.get(CaptureResult.CONTROL_AF_TRIGGER);
String log = "aeMode: " + aeMode + " aeLock: " + aeLock +
" aeState: " + aeState + " aeTriggerState: " + aeTriggerState +
" afState: " + afState + " afTriggerState: " + afTriggerState;
if (!log.equals(mLastLog)) {
mLastLog = log;
LOG.w(log);
}
// START
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 0 afState: 2 afTriggerState: 0
//
// DURING metering (focus skips)
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 0 afState: 0 afTriggerState: 0
// aeMode: 3 aeLock: false aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// DURING locking (focus skips)
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 1 afState: 0 afTriggerState: 0
// aeMode: 3 aeLock: true aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// AFTER locked
// aeMode: 3 aeLock: true aeState: 3 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// AFTER super.take() called
// aeMode: 1 aeLock: true aeState: 5 aeTriggerState: 1 afState: 0 afTriggerState: 0
// aeMode: 1 aeLock: true aeState: 3 aeTriggerState: 1 afState: 0 afTriggerState: 0
//
// Reverting flash changes + reset lock + reset metering
// aeMode: 3 aeLock: false aeState: 4 aeTriggerState: 2(1 now) afState: 2 afTriggerState: 0
// aeMode: 3 aeLock: false aeState: 1 aeTriggerState: 2(1 now) afState: 2 afTriggerState: 0
}
};
private String mLastLog; private String mLastLog;
//endregion //endregion
@ -583,7 +579,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
LOG.i("onStartPreview", "Starting preview."); LOG.i("onStartPreview", "Starting preview.");
addRepeatingRequestBuilderSurfaces(); addRepeatingRequestBuilderSurfaces();
applyRepeatingRequestBuilder(false, CameraException.REASON_FAILED_TO_START_PREVIEW, null); applyRepeatingRequestBuilder(false, CameraException.REASON_FAILED_TO_START_PREVIEW);
LOG.i("onStartPreview", "Started preview."); LOG.i("onStartPreview", "Started preview.");
// Start delayed video if needed. // Start delayed video if needed.
@ -774,18 +770,14 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private void doTakeVideo(@NonNull final VideoResult.Stub stub) { private void doTakeVideo(@NonNull final VideoResult.Stub stub) {
if (!(mVideoRecorder instanceof Full2VideoRecorder)) { if (!(mVideoRecorder instanceof Full2VideoRecorder)) {
throw new IllegalStateException("doTakeVideo called, but video recorder is not a Full2VideoRecorder! " + mVideoRecorder); throw new IllegalStateException("doTakeVideo called, but video recorder " +
"is not a Full2VideoRecorder! " + mVideoRecorder);
} }
Full2VideoRecorder recorder = (Full2VideoRecorder) mVideoRecorder; Full2VideoRecorder recorder = (Full2VideoRecorder) mVideoRecorder;
try { try {
createRepeatingRequestBuilder(CameraDevice.TEMPLATE_RECORD); createRepeatingRequestBuilder(CameraDevice.TEMPLATE_RECORD);
addRepeatingRequestBuilderSurfaces(recorder.getInputSurface()); addRepeatingRequestBuilderSurfaces(recorder.getInputSurface());
applyRepeatingRequestBuilder(true, CameraException.REASON_DISCONNECTED, new Runnable() { applyRepeatingRequestBuilder(true, CameraException.REASON_DISCONNECTED);
@Override
public void run() {
mVideoRecorder.start(stub);
}
});
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
onVideoResult(null, e); onVideoResult(null, e);
throw createCameraException(e); throw createCameraException(e);
@ -793,6 +785,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
onVideoResult(null, e); onVideoResult(null, e);
throw e; throw e;
} }
// Start the video recorder on the first available frame.
new OneShotAction(new Runnable() {
@Override
public void run() {
mVideoRecorder.start(stub);
}
}).start(this);
} }
@WorkerThread @WorkerThread

@ -0,0 +1,28 @@
package com.otaliastudios.cameraview.engine.action;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class OneShotAction extends BaseAction {
private Runnable runnable;
public OneShotAction(@NonNull Runnable runnable) {
this.runnable = runnable;
}
@Override
public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) {
super.onCaptureStarted(holder, request);
Object tag = holder.getBuilder(this).build().getTag();
Object currentTag = request.getTag();
if (tag == null ? currentTag == null : tag.equals(currentTag)) {
runnable.run();
setState(STATE_COMPLETED);
}
}
}
Loading…
Cancel
Save