|
|
@ -41,6 +41,7 @@ import com.otaliastudios.cameraview.size.SizeSelector; |
|
|
|
import com.otaliastudios.cameraview.size.SizeSelectors; |
|
|
|
import com.otaliastudios.cameraview.size.SizeSelectors; |
|
|
|
import com.otaliastudios.cameraview.video.VideoRecorder; |
|
|
|
import com.otaliastudios.cameraview.video.VideoRecorder; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.CallSuper; |
|
|
|
import androidx.annotation.NonNull; |
|
|
|
import androidx.annotation.NonNull; |
|
|
|
import androidx.annotation.Nullable; |
|
|
|
import androidx.annotation.Nullable; |
|
|
|
import androidx.annotation.VisibleForTesting; |
|
|
|
import androidx.annotation.VisibleForTesting; |
|
|
@ -53,6 +54,7 @@ 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.Executor; |
|
|
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -477,22 +479,25 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("restartBind", "executing."); |
|
|
|
LOG.w("restartBind", "executing stopPreview."); |
|
|
|
stopPreview(false).continueWithTask(mHandler.getExecutor(), new Continuation<Void, Task<Void>>() { |
|
|
|
stopPreview(false).continueWithTask(mHandler.getExecutor(), new Continuation<Void, Task<Void>>() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
public Task<Void> then(@NonNull Task<Void> task) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing stopBind."); |
|
|
|
return stopBind(false); |
|
|
|
return stopBind(false); |
|
|
|
} |
|
|
|
} |
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing startBind."); |
|
|
|
return startBind(); |
|
|
|
return startBind(); |
|
|
|
} |
|
|
|
} |
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
}).onSuccessTask(mHandler.getExecutor(), new SuccessContinuation<Void, Void>() { |
|
|
|
@NonNull |
|
|
|
@NonNull |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
public Task<Void> then(@Nullable Void aVoid) { |
|
|
|
|
|
|
|
LOG.w("restartBind", "executing startPreview."); |
|
|
|
return startPreview(); |
|
|
|
return startPreview(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
@ -667,7 +672,12 @@ public abstract class CameraEngine implements |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
try { |
|
|
|
try { |
|
|
|
latch.await(); |
|
|
|
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) {} |
|
|
|
} catch (InterruptedException ignore) {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -685,8 +695,11 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("Start:", "executing runnable. State:", getEngineStateName()); |
|
|
|
LOG.w("Start:", "executing runnable. AllState is", mAllStep.getState()); |
|
|
|
if (mAllStep.isStoppingOrStopped()) { |
|
|
|
// 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>>() { |
|
|
|
mAllStep.doStart(false, new Callable<Task<Void>>() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> call() { |
|
|
|
public Task<Void> call() { |
|
|
@ -711,10 +724,11 @@ public abstract class CameraEngine implements |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// } else {
|
|
|
|
// NOTE: this returns early if we were STARTING.
|
|
|
|
// // NOTE: this returns early if we were STARTING.
|
|
|
|
outTask.trySetResult(null); |
|
|
|
// LOG.i("Start:", "executing runnable. AllState is STARTING or STARTED, so we return early.");
|
|
|
|
} |
|
|
|
// outTask.trySetResult(null);
|
|
|
|
|
|
|
|
// }
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
return outTask.getTask(); |
|
|
|
return outTask.getTask(); |
|
|
@ -732,8 +746,11 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.i("Stop:", "executing runnable. State:", getEngineStateName()); |
|
|
|
LOG.w("Stop:", "executing runnable. AllState is", mAllStep.getState()); |
|
|
|
if (mAllStep.isStartedOrStarting()) { |
|
|
|
// 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>>() { |
|
|
|
mAllStep.doStop(swallowExceptions, new Callable<Task<Void>>() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Task<Void> call() { |
|
|
|
public Task<Void> call() { |
|
|
@ -761,10 +778,11 @@ public abstract class CameraEngine implements |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// } else {
|
|
|
|
// NOTE: this returns early if we were STOPPING.
|
|
|
|
// // NOTE: this returns early if we were STOPPING.
|
|
|
|
outTask.trySetResult(null); |
|
|
|
// LOG.i("Stop:", "executing runnable. AllState is STOPPING or STOPPED, so we return early.");
|
|
|
|
} |
|
|
|
// outTask.trySetResult(null);
|
|
|
|
|
|
|
|
// }
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
return outTask.getTask(); |
|
|
|
return outTask.getTask(); |
|
|
@ -933,17 +951,18 @@ public abstract class CameraEngine implements |
|
|
|
|
|
|
|
|
|
|
|
//region picture and video control
|
|
|
|
//region picture and video control
|
|
|
|
|
|
|
|
|
|
|
|
public final void takePicture(final @NonNull PictureResult.Stub stub) { |
|
|
|
/* not final for tests */ |
|
|
|
|
|
|
|
public void takePicture(final @NonNull PictureResult.Stub stub) { |
|
|
|
LOG.v("takePicture", "scheduling"); |
|
|
|
LOG.v("takePicture", "scheduling"); |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.v("takePicture", "performing. Engine:", getEngineStateName(), "isTakingPicture:", isTakingPicture()); |
|
|
|
LOG.v("takePicture", "performing. BindState:", getBindState(), "isTakingPicture:", isTakingPicture()); |
|
|
|
if (mMode == Mode.VIDEO) { |
|
|
|
if (mMode == Mode.VIDEO) { |
|
|
|
// Could redirect to takePictureSnapshot, but it's better if people know what they are doing.
|
|
|
|
// Could redirect to takePictureSnapshot, but it's better if people know what they are doing.
|
|
|
|
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 (getEngineState() < STATE_STARTED) return; |
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
if (isTakingPicture()) return; |
|
|
|
if (isTakingPicture()) return; |
|
|
|
stub.isSnapshot = false; |
|
|
|
stub.isSnapshot = false; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.location = mLocation; |
|
|
@ -964,8 +983,8 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.v("takePictureSnapshot", "performing. Engine:", getEngineStateName(), "isTakingPicture:", isTakingPicture()); |
|
|
|
LOG.v("takePictureSnapshot", "performing. BindState:", getBindState(), "isTakingPicture:", isTakingPicture()); |
|
|
|
if (getEngineState() < STATE_STARTED) return; |
|
|
|
if (getBindState() < STATE_STARTED) return; |
|
|
|
if (isTakingPicture()) return; |
|
|
|
if (isTakingPicture()) return; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.location = mLocation; |
|
|
|
stub.isSnapshot = true; |
|
|
|
stub.isSnapshot = true; |
|
|
@ -1000,8 +1019,8 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.v("takeVideo", "performing. Engine:", getEngineStateName(), "isTakingVideo:", isTakingVideo()); |
|
|
|
LOG.v("takeVideo", "performing. BindState:", getBindState(), "isTakingVideo:", isTakingVideo()); |
|
|
|
if (getEngineState() < STATE_STARTED) { mStartVideoOp.end(null); return; } |
|
|
|
if (getBindState() < STATE_STARTED) { mStartVideoOp.end(null); return; } |
|
|
|
if (isTakingVideo()) { mStartVideoOp.end(null); return; } |
|
|
|
if (isTakingVideo()) { mStartVideoOp.end(null); 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"); |
|
|
@ -1032,8 +1051,8 @@ public abstract class CameraEngine implements |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
mHandler.run(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.v("takeVideoSnapshot", "performing. Engine:", getEngineStateName(), "isTakingVideo:", isTakingVideo()); |
|
|
|
LOG.v("takeVideoSnapshot", "performing. BindState:", getBindState(), "isTakingVideo:", isTakingVideo()); |
|
|
|
if (getEngineState() < STATE_STARTED) { mStartVideoOp.end(null); return; } |
|
|
|
if (getBindState() < STATE_STARTED) { mStartVideoOp.end(null); return; } |
|
|
|
if (isTakingVideo()) { mStartVideoOp.end(null); return; } |
|
|
|
if (isTakingVideo()) { mStartVideoOp.end(null); return; } |
|
|
|
stub.file = file; |
|
|
|
stub.file = file; |
|
|
|
stub.isSnapshot = true; |
|
|
|
stub.isSnapshot = true; |
|
|
@ -1059,11 +1078,13 @@ public abstract class CameraEngine implements |
|
|
|
LOG.i("stopVideo", "executing.", "isTakingVideo?", isTakingVideo()); |
|
|
|
LOG.i("stopVideo", "executing.", "isTakingVideo?", isTakingVideo()); |
|
|
|
if (mVideoRecorder != null) { |
|
|
|
if (mVideoRecorder != null) { |
|
|
|
mVideoRecorder.stop(); |
|
|
|
mVideoRecorder.stop(); |
|
|
|
|
|
|
|
mVideoRecorder = null; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@CallSuper |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) { |
|
|
|
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) { |
|
|
|
mVideoRecorder = null; |
|
|
|
mVideoRecorder = null; |
|
|
|