|
|
@ -8,24 +8,24 @@ import android.location.Location; |
|
|
|
import android.os.Handler; |
|
|
|
import android.os.Handler; |
|
|
|
import android.os.Looper; |
|
|
|
import android.os.Looper; |
|
|
|
|
|
|
|
|
|
|
|
import com.google.android.gms.tasks.Continuation; |
|
|
|
|
|
|
|
import com.google.android.gms.tasks.OnCompleteListener; |
|
|
|
import com.google.android.gms.tasks.OnCompleteListener; |
|
|
|
import com.google.android.gms.tasks.OnFailureListener; |
|
|
|
import com.google.android.gms.tasks.OnSuccessListener; |
|
|
|
import com.google.android.gms.tasks.SuccessContinuation; |
|
|
|
|
|
|
|
import com.google.android.gms.tasks.TaskCompletionSource; |
|
|
|
|
|
|
|
import com.google.android.gms.tasks.Task; |
|
|
|
import com.google.android.gms.tasks.Task; |
|
|
|
|
|
|
|
import com.google.android.gms.tasks.Tasks; |
|
|
|
import com.otaliastudios.cameraview.CameraException; |
|
|
|
import com.otaliastudios.cameraview.CameraException; |
|
|
|
import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
import com.otaliastudios.cameraview.CameraOptions; |
|
|
|
import com.otaliastudios.cameraview.CameraOptions; |
|
|
|
import com.otaliastudios.cameraview.PictureResult; |
|
|
|
import com.otaliastudios.cameraview.PictureResult; |
|
|
|
import com.otaliastudios.cameraview.controls.PictureFormat; |
|
|
|
import com.otaliastudios.cameraview.controls.PictureFormat; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.engine.orchestrator.CameraOrchestrator; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.engine.orchestrator.CameraState; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.engine.orchestrator.CameraStateOrchestrator; |
|
|
|
import com.otaliastudios.cameraview.overlay.Overlay; |
|
|
|
import com.otaliastudios.cameraview.overlay.Overlay; |
|
|
|
import com.otaliastudios.cameraview.VideoResult; |
|
|
|
import com.otaliastudios.cameraview.VideoResult; |
|
|
|
import com.otaliastudios.cameraview.engine.offset.Angles; |
|
|
|
import com.otaliastudios.cameraview.engine.offset.Angles; |
|
|
|
import com.otaliastudios.cameraview.engine.offset.Reference; |
|
|
|
import com.otaliastudios.cameraview.engine.offset.Reference; |
|
|
|
import com.otaliastudios.cameraview.frame.Frame; |
|
|
|
import com.otaliastudios.cameraview.frame.Frame; |
|
|
|
import com.otaliastudios.cameraview.frame.FrameManager; |
|
|
|
import com.otaliastudios.cameraview.frame.FrameManager; |
|
|
|
import com.otaliastudios.cameraview.internal.utils.Op; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; |
|
|
|
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; |
|
|
|
import com.otaliastudios.cameraview.picture.PictureRecorder; |
|
|
|
import com.otaliastudios.cameraview.picture.PictureRecorder; |
|
|
|
import com.otaliastudios.cameraview.preview.CameraPreview; |
|
|
|
import com.otaliastudios.cameraview.preview.CameraPreview; |
|
|
@ -54,7 +54,6 @@ import java.util.Collection; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.concurrent.Callable; |
|
|
|
import java.util.concurrent.Callable; |
|
|
|
import java.util.concurrent.CountDownLatch; |
|
|
|
import java.util.concurrent.CountDownLatch; |
|
|
|
import java.util.concurrent.Executor; |
|
|
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -73,13 +72,8 @@ import java.util.concurrent.TimeUnit; |
|
|
|
* So at the end of both step 1 and 2, the engine should check if both have |
|
|
|
* So at the end of both step 1 and 2, the engine should check if both have |
|
|
|
* been performed and trigger the steps 3 and 4. |
|
|
|
* been performed and trigger the steps 3 and 4. |
|
|
|
* |
|
|
|
* |
|
|
|
* We use an abstraction for each step called {@link Step} that manages the state of |
|
|
|
|
|
|
|
* each step and ensures that start and stop operations, for each step, are never called if the |
|
|
|
|
|
|
|
* previous one has not ended. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* STATE |
|
|
|
* STATE |
|
|
|
* We only expose generic {@link #start()} and {@link #stop()} calls to the outside. |
|
|
|
* We only expose generic {@link #start()} and {@link #stop(boolean)} calls to the outside. |
|
|
|
* The external users of this class are most likely interested in whether we have completed step 2 |
|
|
|
* The external users of this class are most likely interested in whether we have completed step 2 |
|
|
|
* or not, since that tells us if we can act on the camera or not, rather than knowing about |
|
|
|
* or not, since that tells us if we can act on the camera or not, rather than knowing about |
|
|
|
* steps 3 and 4. |
|
|
|
* steps 3 and 4. |
|
|
@ -87,18 +81,11 @@ import java.util.concurrent.TimeUnit; |
|
|
|
* So in the {@link CameraEngine} notation, |
|
|
|
* So in the {@link CameraEngine} notation, |
|
|
|
* - {@link #start()}: ASYNC - starts the engine (S2). When possible, at a later time, |
|
|
|
* - {@link #start()}: ASYNC - starts the engine (S2). When possible, at a later time, |
|
|
|
* S3 and S4 are also performed. |
|
|
|
* S3 and S4 are also performed. |
|
|
|
* - {@link #stop()}: ASYNC - stops everything: undoes S4, then S3, then S2. |
|
|
|
* - {@link #stop(boolean)}: ASYNC - stops everything: undoes S4, then S3, then S2. |
|
|
|
* - {@link #restart()}: ASYNC - completes a stop then a start. |
|
|
|
* - {@link #restart()}: ASYNC - completes a stop then a start. |
|
|
|
* - {@link #destroy()}: SYNC - performs a {@link #stop()} that will go on no matter the exceptions, |
|
|
|
* - {@link #destroy()}: SYNC - performs a {@link #stop(boolean)} that will go on no matter what, |
|
|
|
* without throwing. Makes the engine unusable and clears resources. |
|
|
|
* without throwing. Makes the engine unusable and clears resources. |
|
|
|
* |
|
|
|
* |
|
|
|
* For example, we expose the engine (S2) state through {@link #getEngineState()}. It will be: |
|
|
|
|
|
|
|
* - {@link #STATE_STARTING} if we're into step 2 |
|
|
|
|
|
|
|
* - {@link #STATE_STARTED} if we've completed step 2. No clue about 3 or 4. |
|
|
|
|
|
|
|
* - {@link #STATE_STOPPING} if we're undoing steps 4, 3 and 2. |
|
|
|
|
|
|
|
* - {@link #STATE_STOPPED} if we have undone steps 4, 3 and 2 (or they never started at all). |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* THREADING |
|
|
|
* THREADING |
|
|
|
* Subclasses should always execute code on the thread given by {@link #mHandler}. |
|
|
|
* Subclasses should always execute code on the thread given by {@link #mHandler}. |
|
|
|
* For convenience, all the setup and tear down methods are called on this engine thread: |
|
|
|
* For convenience, all the setup and tear down methods are called on this engine thread: |
|
|
@ -127,17 +114,16 @@ public abstract class CameraEngine implements |
|
|
|
|
|
|
|
|
|
|
|
public interface Callback { |
|
|
|
public interface Callback { |
|
|
|
@NonNull Context getContext(); |
|
|
|
@NonNull Context getContext(); |
|
|
|
void dispatchOnCameraOpened(CameraOptions options); |
|
|
|
void dispatchOnCameraOpened(@NonNull CameraOptions options); |
|
|
|
void dispatchOnCameraClosed(); |
|
|
|
void dispatchOnCameraClosed(); |
|
|
|
void onCameraPreviewStreamSizeChanged(); |
|
|
|
void onCameraPreviewStreamSizeChanged(); |
|
|
|
void onShutter(boolean shouldPlaySound); |
|
|
|
void onShutter(boolean shouldPlaySound); |
|
|
|
void dispatchOnVideoTaken(VideoResult.Stub stub); |
|
|
|
void dispatchOnVideoTaken(@NonNull VideoResult.Stub stub); |
|
|
|
void dispatchOnPictureTaken(PictureResult.Stub stub); |
|
|
|
void dispatchOnPictureTaken(@NonNull PictureResult.Stub stub); |
|
|
|
void dispatchOnFocusStart(@Nullable Gesture trigger, @NonNull PointF where); |
|
|
|
void dispatchOnFocusStart(@Nullable Gesture trigger, @NonNull PointF where); |
|
|
|
void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, @NonNull PointF where); |
|
|
|
void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, @NonNull PointF where); |
|
|
|
void dispatchOnZoomChanged(final float newValue, @Nullable final PointF[] fingers); |
|
|
|
void dispatchOnZoomChanged(final float newValue, @Nullable final PointF[] fingers); |
|
|
|
void dispatchOnExposureCorrectionChanged(float newValue, |
|
|
|
void dispatchOnExposureCorrectionChanged(float newValue, @NonNull float[] bounds, |
|
|
|
@NonNull float[] bounds, |
|
|
|
|
|
|
|
@Nullable PointF[] fingers); |
|
|
|
@Nullable PointF[] fingers); |
|
|
|
void dispatchFrame(@NonNull Frame frame); |
|
|
|
void dispatchFrame(@NonNull Frame frame); |
|
|
|
void dispatchError(CameraException exception); |
|
|
|
void dispatchError(CameraException exception); |
|
|
@ -148,15 +134,7 @@ public abstract class CameraEngine implements |
|
|
|
private static final String TAG = CameraEngine.class.getSimpleName(); |
|
|
|
private static final String TAG = CameraEngine.class.getSimpleName(); |
|
|
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings({"WeakerAccess", "unused"}) |
|
|
|
|
|
|
|
public static final int STATE_STOPPING = Step.STATE_STOPPING; |
|
|
|
|
|
|
|
public static final int STATE_STOPPED = Step.STATE_STOPPED; |
|
|
|
|
|
|
|
@SuppressWarnings({"WeakerAccess", "unused"}) |
|
|
|
|
|
|
|
public static final int STATE_STARTING = Step.STATE_STARTING; |
|
|
|
|
|
|
|
public static final int STATE_STARTED = Step.STATE_STARTED; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Need to be protected
|
|
|
|
// Need to be protected
|
|
|
|
@SuppressWarnings("WeakerAccess") protected WorkerHandler mHandler; |
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") protected final Callback mCallback; |
|
|
|
@SuppressWarnings("WeakerAccess") protected final Callback mCallback; |
|
|
|
@SuppressWarnings("WeakerAccess") protected CameraPreview mPreview; |
|
|
|
@SuppressWarnings("WeakerAccess") protected CameraPreview mPreview; |
|
|
|
@SuppressWarnings("WeakerAccess") protected CameraOptions mCameraOptions; |
|
|
|
@SuppressWarnings("WeakerAccess") protected CameraOptions mCameraOptions; |
|
|
@ -177,7 +155,7 @@ public abstract class CameraEngine implements |
|
|
|
@SuppressWarnings("WeakerAccess") protected boolean mPictureSnapshotMetering; |
|
|
|
@SuppressWarnings("WeakerAccess") protected boolean mPictureSnapshotMetering; |
|
|
|
@SuppressWarnings("WeakerAccess") protected float mPreviewFrameRate; |
|
|
|
@SuppressWarnings("WeakerAccess") protected float mPreviewFrameRate; |
|
|
|
|
|
|
|
|
|
|
|
// Can be private
|
|
|
|
private WorkerHandler mHandler; |
|
|
|
@VisibleForTesting Handler mCrashHandler; |
|
|
|
@VisibleForTesting Handler mCrashHandler; |
|
|
|
private final FrameManager mFrameManager; |
|
|
|
private final FrameManager mFrameManager; |
|
|
|
private final Angles mAngles; |
|
|
|
private final Angles mAngles; |
|
|
@ -193,36 +171,42 @@ public abstract class CameraEngine implements |
|
|
|
private int mAudioBitRate; |
|
|
|
private int mAudioBitRate; |
|
|
|
private boolean mHasFrameProcessors; |
|
|
|
private boolean mHasFrameProcessors; |
|
|
|
private long mAutoFocusResetDelayMillis; |
|
|
|
private long mAutoFocusResetDelayMillis; |
|
|
|
// in REF_VIEW, for consistency with SizeSelectors
|
|
|
|
private int mSnapshotMaxWidth = Integer.MAX_VALUE; // in REF_VIEW like SizeSelectors
|
|
|
|
private int mSnapshotMaxWidth = Integer.MAX_VALUE; |
|
|
|
private int mSnapshotMaxHeight = Integer.MAX_VALUE; // in REF_VIEW like SizeSelectors
|
|
|
|
// in REF_VIEW, for consistency with SizeSelectors
|
|
|
|
|
|
|
|
private int mSnapshotMaxHeight = Integer.MAX_VALUE; |
|
|
|
|
|
|
|
private Overlay overlay; |
|
|
|
private Overlay overlay; |
|
|
|
|
|
|
|
|
|
|
|
// Steps
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
private final Step.Callback mStepCallback = new Step.Callback() { |
|
|
|
protected final CameraStateOrchestrator mOrchestrator |
|
|
|
@Override @NonNull public Executor getExecutor() { return mHandler.getExecutor(); } |
|
|
|
= new CameraStateOrchestrator(new CameraOrchestrator.Callback() { |
|
|
|
@Override public void handleException(@NonNull Exception exception) { |
|
|
|
@Override |
|
|
|
CameraEngine.this.handleException(Thread.currentThread(), exception, false); |
|
|
|
@NonNull |
|
|
|
|
|
|
|
public WorkerHandler getJobWorker(@NonNull String job) { |
|
|
|
|
|
|
|
return mHandler; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void handleJobException(@NonNull String job, @NonNull Exception exception) { |
|
|
|
|
|
|
|
handleException(Thread.currentThread(), exception, false); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}); |
|
|
|
@VisibleForTesting |
|
|
|
|
|
|
|
Step mEngineStep = new Step("engine", mStepCallback); |
|
|
|
|
|
|
|
private Step mBindStep = new Step("bind", mStepCallback); |
|
|
|
|
|
|
|
private Step mPreviewStep = new Step("preview", mStepCallback); |
|
|
|
|
|
|
|
private Step mAllStep = new Step("all", mStepCallback); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ops used for testing.
|
|
|
|
// Ops used for testing.
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mZoomOp = new Op<>(); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mZoomTask |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mExposureCorrectionOp |
|
|
|
= Tasks.forResult(null); |
|
|
|
= new Op<>(); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mExposureCorrectionTask |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mFlashOp = new Op<>(); |
|
|
|
= Tasks.forResult(null); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mWhiteBalanceOp |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mFlashTask |
|
|
|
= new Op<>(); |
|
|
|
= Tasks.forResult(null); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mHdrOp = new Op<>(); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mWhiteBalanceTask |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mLocationOp = new Op<>(); |
|
|
|
= Tasks.forResult(null); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mPlaySoundsOp = new Op<>(); |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mHdrTask |
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mPreviewFrameRateOp = new Op<>(); |
|
|
|
= Tasks.forResult(null); |
|
|
|
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mLocationTask |
|
|
|
|
|
|
|
= Tasks.forResult(null); |
|
|
|
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mPlaySoundsTask |
|
|
|
|
|
|
|
= Tasks.forResult(null); |
|
|
|
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Task<Void> mPreviewFrameRateTask |
|
|
|
|
|
|
|
= Tasks.forResult(null); |
|
|
|
|
|
|
|
|
|
|
|
protected CameraEngine(@NonNull Callback callback) { |
|
|
|
protected CameraEngine(@NonNull Callback callback) { |
|
|
|
mCallback = callback; |
|
|
|
mCallback = callback; |
|
|
@ -252,7 +236,7 @@ public abstract class CameraEngine implements |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private class CrashExceptionHandler implements Thread.UncaughtExceptionHandler { |
|
|
|
private class CrashExceptionHandler implements Thread.UncaughtExceptionHandler { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void uncaughtException(final Thread thread, final Throwable throwable) { |
|
|
|
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { |
|
|
|
handleException(thread, throwable, true); |
|
|
|
handleException(thread, throwable, true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -263,7 +247,7 @@ public abstract class CameraEngine implements |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static class NoOpExceptionHandler implements Thread.UncaughtExceptionHandler { |
|
|
|
private static class NoOpExceptionHandler implements Thread.UncaughtExceptionHandler { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void uncaughtException(Thread t, Throwable e) { |
|
|
|
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { |
|
|
|
// No-op.
|
|
|
|
// No-op.
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -305,7 +289,7 @@ public abstract class CameraEngine implements |
|
|
|
|
|
|
|
|
|
|
|
final CameraException cameraException = (CameraException) throwable; |
|
|
|
final CameraException cameraException = (CameraException) throwable; |
|
|
|
LOG.e("uncaughtException:", "Got CameraException:", cameraException, |
|
|
|
LOG.e("uncaughtException:", "Got CameraException:", cameraException, |
|
|
|
"on engine state:", getEngineState()); |
|
|
|
"on state:", getState()); |
|
|
|
if (fromExceptionHandler) { |
|
|
|
if (fromExceptionHandler) { |
|
|
|
// Got to restart the handler.
|
|
|
|
// Got to restart the handler.
|
|
|
|
thread.interrupt(); |
|
|
|
thread.interrupt(); |
|
|
@ -322,49 +306,95 @@ public abstract class CameraEngine implements |
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
|
|
|
|
|
|
|
|
|
//region states and steps
|
|
|
|
//region State management
|
|
|
|
|
|
|
|
|
|
|
|
public final int getEngineState() { |
|
|
|
@NonNull |
|
|
|
return mEngineStep.getState(); |
|
|
|
public final CameraState getState() { |
|
|
|
|
|
|
|
return mOrchestrator.getCurrentState(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
@NonNull |
|
|
|
public final int getBindState() { |
|
|
|
public final CameraState getTargetState() { |
|
|
|
return mBindStep.getState(); |
|
|
|
return mOrchestrator.getTargetState(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings({"unused", "WeakerAccess"}) |
|
|
|
public boolean isChangingState() { |
|
|
|
public final int getPreviewState() { |
|
|
|
return mOrchestrator.hasPendingStateChange(); |
|
|
|
return mPreviewStep.getState(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean canStartEngine() { |
|
|
|
/** |
|
|
|
return mEngineStep.isStoppingOrStopped(); |
|
|
|
* Calls {@link #stop(boolean)} and waits for it. |
|
|
|
|
|
|
|
* Not final due to mockito requirements. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* NOTE: Should not be called on the orchestrator thread! This would cause deadlocks due to us |
|
|
|
|
|
|
|
* awaiting for {@link #stop(boolean)} to return. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void destroy() { |
|
|
|
|
|
|
|
LOG.i("DESTROY:", "state:", getState(), "thread:", Thread.currentThread()); |
|
|
|
|
|
|
|
// Prevent CameraEngine leaks. Don't set to null, or exceptions
|
|
|
|
|
|
|
|
// inside the standard stop() method might crash the main thread.
|
|
|
|
|
|
|
|
mHandler.getThread().setUncaughtExceptionHandler(new NoOpExceptionHandler()); |
|
|
|
|
|
|
|
// Stop if needed, synchronously and silently.
|
|
|
|
|
|
|
|
// Cannot use Tasks.await() because we might be on the UI thread.
|
|
|
|
|
|
|
|
final CountDownLatch latch = new CountDownLatch(1); |
|
|
|
|
|
|
|
stop(true).addOnCompleteListener( |
|
|
|
|
|
|
|
WorkerHandler.get().getExecutor(), |
|
|
|
|
|
|
|
new OnCompleteListener<Void>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onComplete(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
latch.countDown(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
boolean success = latch.await(3, TimeUnit.SECONDS); |
|
|
|
|
|
|
|
if (!success) { |
|
|
|
|
|
|
|
LOG.e("Probably some deadlock in destroy.", |
|
|
|
|
|
|
|
"Current thread:", Thread.currentThread(), |
|
|
|
|
|
|
|
"Handler thread: ", mHandler.getThread()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (InterruptedException ignore) {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean needsStopEngine() { |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
return mEngineStep.isStartedOrStarting(); |
|
|
|
public void restart() { |
|
|
|
|
|
|
|
LOG.i("RESTART:", "scheduled. State:", getState()); |
|
|
|
|
|
|
|
stop(false); |
|
|
|
|
|
|
|
start(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean canStartBind() { |
|
|
|
@NonNull |
|
|
|
return mEngineStep.isStarted() |
|
|
|
public Task<Void> start() { |
|
|
|
&& mPreview != null |
|
|
|
LOG.i("START:", "scheduled. State:", getState()); |
|
|
|
&& mPreview.hasSurface() |
|
|
|
Task<Void> engine = startEngine(); |
|
|
|
&& mBindStep.isStoppingOrStopped(); |
|
|
|
startBind(); |
|
|
|
|
|
|
|
startPreview(); |
|
|
|
|
|
|
|
return engine; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean needsStopBind() { |
|
|
|
@NonNull |
|
|
|
return mBindStep.isStartedOrStarting(); |
|
|
|
public Task<Void> stop(final boolean swallowExceptions) { |
|
|
|
|
|
|
|
LOG.i("STOP:", "scheduled. State:", getState()); |
|
|
|
|
|
|
|
stopPreview(swallowExceptions); |
|
|
|
|
|
|
|
stopBind(swallowExceptions); |
|
|
|
|
|
|
|
return stopEngine(swallowExceptions); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean canStartPreview() { |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
return mEngineStep.isStarted() |
|
|
|
@NonNull |
|
|
|
&& mBindStep.isStarted() |
|
|
|
protected Task<Void> restartBind() { |
|
|
|
&& mPreviewStep.isStoppingOrStopped(); |
|
|
|
LOG.i("RESTART BIND:", "scheduled. State:", getState()); |
|
|
|
|
|
|
|
stopPreview(false); |
|
|
|
|
|
|
|
stopBind(false); |
|
|
|
|
|
|
|
startBind(); |
|
|
|
|
|
|
|
return startPreview(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean needsStopPreview() { |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
return mPreviewStep.isStartedOrStarting(); |
|
|
|
@NonNull |
|
|
|
|
|
|
|
protected Task<Void> restartPreview() { |
|
|
|
|
|
|
|
LOG.i("RESTART PREVIEW:", "scheduled. State:", getState()); |
|
|
|
|
|
|
|
stopPreview(false); |
|
|
|
|
|
|
|
return startPreview(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
@ -374,43 +404,41 @@ public abstract class CameraEngine implements |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> startEngine() { |
|
|
|
private Task<Void> startEngine() { |
|
|
|
if (canStartEngine()) { |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.OFF, CameraState.ENGINE, |
|
|
|
mEngineStep.doStart(false, new Callable<Task<Void>>() { |
|
|
|
true, |
|
|
|
@Override |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
public Task<Void> call() { |
|
|
|
@Override |
|
|
|
if (!collectCameraInfo(mFacing)) { |
|
|
|
public Task<Void> call() { |
|
|
|
LOG.e("onStartEngine:", "No camera available for facing", mFacing); |
|
|
|
if (!collectCameraInfo(mFacing)) { |
|
|
|
throw new CameraException(CameraException.REASON_NO_CAMERA); |
|
|
|
LOG.e("onStartEngine:", "No camera available for facing", mFacing); |
|
|
|
} |
|
|
|
throw new CameraException(CameraException.REASON_NO_CAMERA); |
|
|
|
return onStartEngine(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
mCallback.dispatchOnCameraOpened(mCameraOptions); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return onStartEngine(); |
|
|
|
} |
|
|
|
} |
|
|
|
return mEngineStep.getTask(); |
|
|
|
}).addOnSuccessListener(new OnSuccessListener<Void>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onSuccess(Void aVoid) { |
|
|
|
|
|
|
|
mCallback.dispatchOnCameraOpened(mCameraOptions); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> stopEngine(boolean swallowExceptions) { |
|
|
|
private Task<Void> stopEngine(boolean swallowExceptions) { |
|
|
|
if (needsStopEngine()) { |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.ENGINE, CameraState.OFF, |
|
|
|
mEngineStep.doStop(swallowExceptions, new Callable<Task<Void>>() { |
|
|
|
!swallowExceptions, |
|
|
|
@Override |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
public Task<Void> call() { |
|
|
|
@Override |
|
|
|
return onStopEngine(); |
|
|
|
public Task<Void> call() { |
|
|
|
} |
|
|
|
return onStopEngine(); |
|
|
|
}, new Runnable() { |
|
|
|
} |
|
|
|
@Override |
|
|
|
}).addOnSuccessListener(new OnSuccessListener<Void>() { |
|
|
|
public void run() { |
|
|
|
@Override |
|
|
|
mCallback.dispatchOnCameraClosed(); |
|
|
|
public void onSuccess(Void aVoid) { |
|
|
|
} |
|
|
|
mCallback.dispatchOnCameraClosed(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return mEngineStep.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -438,29 +466,32 @@ public abstract class CameraEngine implements |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> startBind() { |
|
|
|
private Task<Void> startBind() { |
|
|
|
if (canStartBind()) { |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.ENGINE, CameraState.BIND, |
|
|
|
mBindStep.doStart(false, new Callable<Task<Void>>() { |
|
|
|
true, |
|
|
|
@Override |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
public Task<Void> call() { |
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> call() { |
|
|
|
|
|
|
|
if (mPreview != null && mPreview.hasSurface()) { |
|
|
|
return onStartBind(); |
|
|
|
return onStartBind(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return Tasks.forCanceled(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return mBindStep.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("UnusedReturnValue") |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> stopBind(boolean swallowExceptions) { |
|
|
|
private Task<Void> stopBind(boolean swallowExceptions) { |
|
|
|
if (needsStopBind()) { |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.BIND, CameraState.ENGINE, |
|
|
|
mBindStep.doStop(swallowExceptions, new Callable<Task<Void>>() { |
|
|
|
!swallowExceptions, |
|
|
|
@Override |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
public Task<Void> call() { |
|
|
|
@Override |
|
|
|
return onStopBind(); |
|
|
|
public Task<Void> call() { |
|
|
|
} |
|
|
|
return onStopBind(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return mBindStep.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -481,39 +512,6 @@ public abstract class CameraEngine implements |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
protected abstract Task<Void> onStopBind(); |
|
|
|
protected abstract Task<Void> onStopBind(); |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
|
|
|
protected void restartBind() { |
|
|
|
|
|
|
|
LOG.i("restartBind", "posting."); |
|
|
|
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing stopPreview."); |
|
|
|
|
|
|
|
stopPreview(false).continueWithTask(mHandler.getExecutor(), |
|
|
|
|
|
|
|
new Continuation<Void, Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing stopBind."); |
|
|
|
|
|
|
|
return stopBind(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing startBind."); |
|
|
|
|
|
|
|
return startBind(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing startPreview."); |
|
|
|
|
|
|
|
return startPreview(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
|
|
|
|
|
|
|
|
|
//region Start & Stop preview
|
|
|
|
//region Start & Stop preview
|
|
|
@ -521,44 +519,26 @@ public abstract class CameraEngine implements |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> startPreview() { |
|
|
|
private Task<Void> startPreview() { |
|
|
|
LOG.i("startPreview", "canStartPreview:", canStartPreview()); |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.BIND, CameraState.PREVIEW, |
|
|
|
if (canStartPreview()) { |
|
|
|
true, |
|
|
|
mPreviewStep.doStart(false, new Callable<Task<Void>>() { |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> call() { |
|
|
|
public Task<Void> call() { |
|
|
|
return onStartPreview(); |
|
|
|
return onStartPreview(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
return mPreviewStep.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("UnusedReturnValue") |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
private Task<Void> stopPreview(boolean swallowExceptions) { |
|
|
|
private Task<Void> stopPreview(boolean swallowExceptions) { |
|
|
|
LOG.i("stopPreview", |
|
|
|
return mOrchestrator.scheduleStateChange(CameraState.PREVIEW, CameraState.BIND, |
|
|
|
"needsStopPreview:", needsStopPreview(), |
|
|
|
!swallowExceptions, |
|
|
|
"swallowExceptions:", swallowExceptions); |
|
|
|
new Callable<Task<Void>>() { |
|
|
|
if (needsStopPreview()) { |
|
|
|
|
|
|
|
mPreviewStep.doStop(swallowExceptions, new Callable<Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> call() { |
|
|
|
|
|
|
|
return onStopPreview(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return mPreviewStep.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
|
|
|
protected void restartPreview() { |
|
|
|
|
|
|
|
LOG.i("restartPreview", "posting."); |
|
|
|
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public Task<Void> call() { |
|
|
|
LOG.i("restartPreview", "executing."); |
|
|
|
return onStopPreview(); |
|
|
|
stopPreview(false); |
|
|
|
|
|
|
|
startPreview(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
@ -592,33 +572,24 @@ public abstract class CameraEngine implements |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public final void onSurfaceAvailable() { |
|
|
|
public final void onSurfaceAvailable() { |
|
|
|
LOG.i("onSurfaceAvailable:", "Size is", getPreviewSurfaceSize(Reference.VIEW)); |
|
|
|
LOG.i("onSurfaceAvailable:", "Size is", getPreviewSurfaceSize(Reference.VIEW)); |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
startBind(); |
|
|
|
@Override |
|
|
|
startPreview(); |
|
|
|
public void run() { |
|
|
|
} |
|
|
|
startBind().onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public final void onSurfaceDestroyed() { |
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
LOG.i("onSurfaceDestroyed"); |
|
|
|
return startPreview(); |
|
|
|
stopPreview(false); |
|
|
|
} |
|
|
|
stopBind(false); |
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public final void onSurfaceChanged() { |
|
|
|
public final void onSurfaceChanged() { |
|
|
|
LOG.i("onSurfaceChanged:", "Size is", getPreviewSurfaceSize(Reference.VIEW), |
|
|
|
LOG.i("onSurfaceChanged:", "Size is", getPreviewSurfaceSize(Reference.VIEW)); |
|
|
|
"Posting."); |
|
|
|
mOrchestrator.scheduleStateful("surface changed", CameraState.BIND, |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("onSurfaceChanged:", |
|
|
|
|
|
|
|
"Engine started?", mEngineStep.isStarted(), |
|
|
|
|
|
|
|
"Bind started?", mBindStep.isStarted()); |
|
|
|
|
|
|
|
if (!mEngineStep.isStarted()) return; // Too early
|
|
|
|
|
|
|
|
if (!mBindStep.isStarted()) return; // Too early
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Compute a new camera preview size and apply.
|
|
|
|
// Compute a new camera preview size and apply.
|
|
|
|
Size newSize = computePreviewStreamSize(); |
|
|
|
Size newSize = computePreviewStreamSize(); |
|
|
|
if (newSize.equals(mPreviewStreamSize)) { |
|
|
|
if (newSize.equals(mPreviewStreamSize)) { |
|
|
@ -643,176 +614,6 @@ public abstract class CameraEngine implements |
|
|
|
@EngineThread |
|
|
|
@EngineThread |
|
|
|
protected abstract void onPreviewStreamSizeChanged(); |
|
|
|
protected abstract void onPreviewStreamSizeChanged(); |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public final void onSurfaceDestroyed() { |
|
|
|
|
|
|
|
LOG.i("onSurfaceDestroyed"); |
|
|
|
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
stopPreview(false).onSuccessTask(mHandler.getExecutor(), |
|
|
|
|
|
|
|
new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
return stopBind(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//region Start & Stop all
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Not final due to mockito requirements, but this is basically |
|
|
|
|
|
|
|
* it, nothing more to do. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* NOTE: Should not be called on the {@link #mHandler} thread! I think |
|
|
|
|
|
|
|
* that would cause deadlocks due to us awaiting for {@link #stop()} to return. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void destroy() { |
|
|
|
|
|
|
|
LOG.i("destroy:", "state:", getEngineState(), "thread:", Thread.currentThread()); |
|
|
|
|
|
|
|
// Prevent CameraEngine leaks. Don't set to null, or exceptions
|
|
|
|
|
|
|
|
// inside the standard stop() method might crash the main thread.
|
|
|
|
|
|
|
|
mHandler.getThread().setUncaughtExceptionHandler(new NoOpExceptionHandler()); |
|
|
|
|
|
|
|
// Stop if needed, synchronously and silently.
|
|
|
|
|
|
|
|
// Cannot use Tasks.await() because we might be on the UI thread.
|
|
|
|
|
|
|
|
final CountDownLatch latch = new CountDownLatch(1); |
|
|
|
|
|
|
|
stop(true).addOnCompleteListener(mHandler.getExecutor(), |
|
|
|
|
|
|
|
new OnCompleteListener<Void>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onComplete(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
latch.countDown(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
boolean success = latch.await(3, TimeUnit.SECONDS); |
|
|
|
|
|
|
|
if (!success) { |
|
|
|
|
|
|
|
// TODO seems like this is always the case?
|
|
|
|
|
|
|
|
LOG.e("Probably some deadlock in destroy.", |
|
|
|
|
|
|
|
"Current thread:", Thread.currentThread(), |
|
|
|
|
|
|
|
"Handler thread: ", mHandler.getThread()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (InterruptedException ignore) {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
|
|
|
protected final void restart() { |
|
|
|
|
|
|
|
LOG.i("Restart:", "calling stop and start"); |
|
|
|
|
|
|
|
stop(); |
|
|
|
|
|
|
|
start(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
public Task<Void> start() { |
|
|
|
|
|
|
|
LOG.i("Start:", "posting runnable. State:", getEngineState()); |
|
|
|
|
|
|
|
final TaskCompletionSource<Void> outTask = new TaskCompletionSource<>(); |
|
|
|
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
LOG.w("Start:", "executing runnable. AllState is", mAllStep.getState()); |
|
|
|
|
|
|
|
// It's better to schedule anyway. allStep might be STARTING and we might be
|
|
|
|
|
|
|
|
// tempted to early return here, but the truth is that there might be a stop
|
|
|
|
|
|
|
|
// already scheduled when the STARTING op ends.
|
|
|
|
|
|
|
|
// if (mAllStep.isStoppingOrStopped()) {
|
|
|
|
|
|
|
|
// LOG.i("Start:", "executing runnable. AllState is STOPPING or STOPPED,
|
|
|
|
|
|
|
|
// so we schedule a start.");
|
|
|
|
|
|
|
|
mAllStep.doStart(false, new Callable<Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> call() { |
|
|
|
|
|
|
|
return startEngine().addOnFailureListener(mHandler.getExecutor(), |
|
|
|
|
|
|
|
new OnFailureListener() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onFailure(@NonNull Exception e) { |
|
|
|
|
|
|
|
outTask.trySetException(e); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
outTask.trySetResult(null); |
|
|
|
|
|
|
|
return startBind(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
return startPreview(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// // NOTE: this returns early if we were STARTING.
|
|
|
|
|
|
|
|
// LOG.i("Start:",
|
|
|
|
|
|
|
|
// "executing runnable. AllState is STARTING or STARTED, so we return early.");
|
|
|
|
|
|
|
|
// outTask.trySetResult(null);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
return outTask.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
public Task<Void> stop() { |
|
|
|
|
|
|
|
return stop(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
|
|
|
private Task<Void> stop(final boolean swallowExceptions) { |
|
|
|
|
|
|
|
LOG.i("Stop:", "posting runnable. State:", getEngineState()); |
|
|
|
|
|
|
|
final TaskCompletionSource<Void> outTask = new TaskCompletionSource<>(); |
|
|
|
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
LOG.w("Stop:", "executing runnable. AllState is", mAllStep.getState()); |
|
|
|
|
|
|
|
// It's better to schedule anyway. allStep might be STOPPING and we might be
|
|
|
|
|
|
|
|
// tempted to early return here, but the truth is that there might be a start
|
|
|
|
|
|
|
|
// already scheduled when the STOPPING op ends.
|
|
|
|
|
|
|
|
// if (mAllStep.isStartedOrStarting()) {
|
|
|
|
|
|
|
|
// LOG.i("Stop:", "executing runnable. AllState is STARTING or STARTED,
|
|
|
|
|
|
|
|
// so we schedule a stop.");
|
|
|
|
|
|
|
|
mAllStep.doStop(swallowExceptions, new Callable<Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> call() { |
|
|
|
|
|
|
|
return stopPreview(swallowExceptions).continueWithTask( |
|
|
|
|
|
|
|
mHandler.getExecutor(), new Continuation<Void, Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
return stopBind(swallowExceptions); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).continueWithTask(mHandler.getExecutor(), new Continuation<Void, Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
return stopEngine(swallowExceptions); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}).continueWithTask(mHandler.getExecutor(), new Continuation<Void, Task<Void>>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
if (task.isSuccessful()) { |
|
|
|
|
|
|
|
outTask.trySetResult(null); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
//noinspection ConstantConditions
|
|
|
|
|
|
|
|
outTask.trySetException(task.getException()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return task; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// // NOTE: this returns early if we were STOPPING.
|
|
|
|
|
|
|
|
// LOG.i("Stop:", "executing runnable.
|
|
|
|
|
|
|
|
// AllState is STOPPING or STOPPED, so we return early.");
|
|
|
|
|
|
|
|
// outTask.trySetResult(null);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
return outTask.getTask(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
|
|
|
|
|
|
|
|
|
//region Final setters and getters
|
|
|
|
//region Final setters and getters
|
|
|
@ -922,7 +723,7 @@ public abstract class CameraEngine implements |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets a new facing value. This will restart the session (if there's any) |
|
|
|
* Sets a new facing value. This will restart the engine session (if there's any) |
|
|
|
* so that we can open the new facing camera. |
|
|
|
* so that we can open the new facing camera. |
|
|
|
* @param facing facing |
|
|
|
* @param facing facing |
|
|
|
*/ |
|
|
|
*/ |
|
|
@ -930,10 +731,10 @@ public abstract class CameraEngine implements |
|
|
|
final Facing old = mFacing; |
|
|
|
final Facing old = mFacing; |
|
|
|
if (facing != old) { |
|
|
|
if (facing != old) { |
|
|
|
mFacing = facing; |
|
|
|
mFacing = facing; |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mOrchestrator.scheduleStateful("facing", CameraState.ENGINE, |
|
|
|
|
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
if (getEngineState() < STATE_STARTED) return; |
|
|
|
|
|
|
|
if (collectCameraInfo(facing)) { |
|
|
|
if (collectCameraInfo(facing)) { |
|
|
|
restart(); |
|
|
|
restart(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -975,12 +776,11 @@ public abstract class CameraEngine implements |
|
|
|
public final void setMode(@NonNull Mode mode) { |
|
|
|
public final void setMode(@NonNull Mode mode) { |
|
|
|
if (mode != mMode) { |
|
|
|
if (mode != mMode) { |
|
|
|
mMode = mode; |
|
|
|
mMode = mode; |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mOrchestrator.scheduleStateful("mode", CameraState.ENGINE, |
|
|
|
|
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
if (getEngineState() == STATE_STARTED) { |
|
|
|
restart(); |
|
|
|
restart(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
@ -1134,22 +934,22 @@ public abstract class CameraEngine implements |
|
|
|
|
|
|
|
|
|
|
|
/* not final for tests */ |
|
|
|
/* not final for tests */ |
|
|
|
public void takePicture(final @NonNull PictureResult.Stub stub) { |
|
|
|
public void takePicture(final @NonNull PictureResult.Stub stub) { |
|
|
|
LOG.i("takePicture", "scheduling"); |
|
|
|
// Save boolean before scheduling! See how Camera2Engine calls this with a temp value.
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
final boolean metering = mPictureMetering; |
|
|
|
|
|
|
|
mOrchestrator.scheduleStateful("take picture", CameraState.BIND, |
|
|
|
|
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("takePicture", "performing. BindState:", getBindState(), |
|
|
|
LOG.i("takePicture:", "running. isTakingPicture:", isTakingPicture()); |
|
|
|
"isTakingPicture:", isTakingPicture()); |
|
|
|
if (isTakingPicture()) return; |
|
|
|
if (mMode == Mode.VIDEO) { |
|
|
|
if (mMode == Mode.VIDEO) { |
|
|
|
throw new IllegalStateException("Can't take hq pictures while in VIDEO mode"); |
|
|
|
throw new IllegalStateException("Can't take hq pictures while in VIDEO mode"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
|
|
|
|
if (isTakingPicture()) return; |
|
|
|
|
|
|
|
stub.isSnapshot = false; |
|
|
|
stub.isSnapshot = false; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.facing = mFacing; |
|
|
|
stub.facing = mFacing; |
|
|
|
stub.format = mPictureFormat; |
|
|
|
stub.format = mPictureFormat; |
|
|
|
onTakePicture(stub, mPictureMetering); |
|
|
|
onTakePicture(stub, metering); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
@ -1160,13 +960,13 @@ public abstract class CameraEngine implements |
|
|
|
* @param stub a picture stub |
|
|
|
* @param stub a picture stub |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public final void takePictureSnapshot(final @NonNull PictureResult.Stub stub) { |
|
|
|
public final void takePictureSnapshot(final @NonNull PictureResult.Stub stub) { |
|
|
|
LOG.i("takePictureSnapshot", "scheduling"); |
|
|
|
// Save boolean before scheduling! See how Camera2Engine calls this with a temp value.
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
final boolean metering = mPictureSnapshotMetering; |
|
|
|
|
|
|
|
mOrchestrator.scheduleStateful("take picture snapshot", CameraState.BIND, |
|
|
|
|
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("takePictureSnapshot", "performing. BindState:", |
|
|
|
LOG.i("takePictureSnapshot:", "running. isTakingPicture:", isTakingPicture()); |
|
|
|
getBindState(), "isTakingPicture:", isTakingPicture()); |
|
|
|
|
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
|
|
|
|
if (isTakingPicture()) return; |
|
|
|
if (isTakingPicture()) return; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.isSnapshot = true; |
|
|
|
stub.isSnapshot = true; |
|
|
@ -1175,7 +975,7 @@ public abstract class CameraEngine implements |
|
|
|
// Leave the other parameters to subclasses.
|
|
|
|
// Leave the other parameters to subclasses.
|
|
|
|
//noinspection ConstantConditions
|
|
|
|
//noinspection ConstantConditions
|
|
|
|
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT)); |
|
|
|
AspectRatio ratio = AspectRatio.of(getPreviewSurfaceSize(Reference.OUTPUT)); |
|
|
|
onTakePictureSnapshot(stub, ratio, mPictureSnapshotMetering); |
|
|
|
onTakePictureSnapshot(stub, ratio, metering); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
@ -1202,13 +1002,10 @@ public abstract class CameraEngine implements |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public final void takeVideo(final @NonNull VideoResult.Stub stub, final @NonNull File file) { |
|
|
|
public final void takeVideo(final @NonNull VideoResult.Stub stub, final @NonNull File file) { |
|
|
|
LOG.i("takeVideo", "scheduling"); |
|
|
|
mOrchestrator.scheduleStateful("take video", CameraState.BIND, new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("takeVideo", "performing. BindState:", getBindState(), |
|
|
|
LOG.i("takeVideo:", "running. isTakingVideo:", isTakingVideo()); |
|
|
|
"isTakingVideo:", isTakingVideo()); |
|
|
|
|
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
|
|
|
|
if (isTakingVideo()) return; |
|
|
|
if (isTakingVideo()) return; |
|
|
|
if (mMode == Mode.PICTURE) { |
|
|
|
if (mMode == Mode.PICTURE) { |
|
|
|
throw new IllegalStateException("Can't record video while in PICTURE mode"); |
|
|
|
throw new IllegalStateException("Can't record video while in PICTURE mode"); |
|
|
@ -1234,14 +1031,11 @@ public abstract class CameraEngine implements |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public final void takeVideoSnapshot(@NonNull final VideoResult.Stub stub, |
|
|
|
public final void takeVideoSnapshot(@NonNull final VideoResult.Stub stub, |
|
|
|
@NonNull final File file) { |
|
|
|
@NonNull final File file) { |
|
|
|
LOG.i("takeVideoSnapshot", "scheduling"); |
|
|
|
mOrchestrator.scheduleStateful("take video snapshot", CameraState.BIND, |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("takeVideoSnapshot", "performing. BindState:", getBindState(), |
|
|
|
LOG.i("takeVideoSnapshot:", "running. isTakingVideo:", isTakingVideo()); |
|
|
|
"isTakingVideo:", isTakingVideo()); |
|
|
|
|
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
|
|
|
|
if (isTakingVideo()) return; |
|
|
|
|
|
|
|
stub.file = file; |
|
|
|
stub.file = file; |
|
|
|
stub.isSnapshot = true; |
|
|
|
stub.isSnapshot = true; |
|
|
|
stub.videoCodec = mVideoCodec; |
|
|
|
stub.videoCodec = mVideoCodec; |
|
|
@ -1260,11 +1054,10 @@ public abstract class CameraEngine implements |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public final void stopVideo() { |
|
|
|
public final void stopVideo() { |
|
|
|
LOG.i("stopVideo", "posting"); |
|
|
|
mOrchestrator.schedule("stop video", true, new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("stopVideo", "executing.", "isTakingVideo?", isTakingVideo()); |
|
|
|
LOG.i("stopVideo", "running. isTakingVideo?", isTakingVideo()); |
|
|
|
onStopVideo(); |
|
|
|
onStopVideo(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|