pull/493/head
Mattia Iavarone 5 years ago
parent 14f03a43f6
commit 6721f3400d
  1. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegration1Test.java
  2. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegration2Test.java
  3. 17
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java
  4. 6
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java
  5. 2
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/picture/PictureRecorderTest.java
  6. 16
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/video/VideoRecorderTest.java
  7. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  8. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  9. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  10. 67
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  11. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/video/Full1VideoRecorder.java
  12. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java

@ -3,6 +3,7 @@ package com.otaliastudios.cameraview.engine;
import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Engine;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -17,8 +18,8 @@ import androidx.test.filters.LargeTest;
*/ */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@LargeTest @LargeTest
@Ignore // @Ignore
public class Camera2IntegrationTest extends CameraIntegrationTest { public class CameraIntegration1Test extends CameraIntegrationTest {
@NonNull @NonNull
@Override @Override

@ -17,8 +17,8 @@ import androidx.test.filters.LargeTest;
*/ */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@LargeTest @LargeTest
@Ignore // @Ignore
public class Camera1IntegrationTest extends CameraIntegrationTest { public class CameraIntegration2Test extends CameraIntegrationTest {
@NonNull @NonNull
@Override @Override

@ -92,6 +92,8 @@ public abstract class CameraIntegrationTest extends BaseTest {
} }
}; };
listener = mock(CameraListener.class); listener = mock(CameraListener.class);
camera.setExperimental(true);
camera.setEngine(getEngine());
camera.addCameraListener(listener); camera.addCameraListener(listener);
rule.getActivity().inflate(camera); rule.getActivity().inflate(camera);
@ -117,8 +119,10 @@ public abstract class CameraIntegrationTest extends BaseTest {
} }
private void waitForUiException() throws Throwable { private void waitForUiException() throws Throwable {
Throwable throwable = uiExceptionOp.await(2500); Throwable throwable = uiExceptionOp.await(5000);
if (throwable != null) throw throwable; if (throwable != null) {
throw throwable;
}
} }
private CameraOptions waitForOpen(boolean expectSuccess) { private CameraOptions waitForOpen(boolean expectSuccess) {
@ -128,6 +132,8 @@ public abstract class CameraIntegrationTest extends BaseTest {
CameraOptions result = open.await(4000); CameraOptions result = open.await(4000);
if (expectSuccess) { if (expectSuccess) {
assertNotNull("Can open", result); assertNotNull("Can open", result);
// Extra wait for the bind state.
while (controller.getBindState() != CameraEngine.STATE_STARTED) {}
} else { } else {
assertNull("Should not open", result); assertNull("Should not open", result);
} }
@ -149,7 +155,7 @@ public abstract class CameraIntegrationTest extends BaseTest {
private void waitForVideoEnd(boolean expectSuccess) { private void waitForVideoEnd(boolean expectSuccess) {
final Op<Boolean> video = new Op<>(true); final Op<Boolean> video = new Op<>(true);
doEndTask(video, true).when(listener).onVideoTaken(any(VideoResult.class)); doEndTask(video, true).when(listener).onVideoTaken(any(VideoResult.class));
Boolean result = video.await(8000); Boolean result = video.await(12000);
if (expectSuccess) { if (expectSuccess) {
assertNotNull("Should end video", result); assertNotNull("Should end video", result);
} else { } else {
@ -173,7 +179,7 @@ public abstract class CameraIntegrationTest extends BaseTest {
controller.mStartVideoOp.listen(); controller.mStartVideoOp.listen();
File file = new File(context().getFilesDir(), "video.mp4"); File file = new File(context().getFilesDir(), "video.mp4");
camera.takeVideo(file); camera.takeVideo(file);
controller.mStartVideoOp.await(400); controller.mStartVideoOp.await(1000);
} }
//region test open/close //region test open/close
@ -593,7 +599,7 @@ public abstract class CameraIntegrationTest extends BaseTest {
// Expect 30 frames // Expect 30 frames
CountDownLatch latch = new CountDownLatch(30); CountDownLatch latch = new CountDownLatch(30);
doCountDown(latch).when(mock).process(any(Frame.class)); doCountDown(latch).when(mock).process(any(Frame.class));
boolean did = latch.await(4, TimeUnit.SECONDS); boolean did = latch.await(60, TimeUnit.SECONDS);
assertTrue(did); assertTrue(did);
} }
@ -640,7 +646,6 @@ public abstract class CameraIntegrationTest extends BaseTest {
waitForOpen(true); waitForOpen(true);
camera.takeVideo(new File(context().getFilesDir(), "video.mp4"), 4000); camera.takeVideo(new File(context().getFilesDir(), "video.mp4"), 4000);
waitForVideoEnd(true); waitForVideoEnd(true);
assert30Frames(processor); assert30Frames(processor);
} }

@ -143,9 +143,15 @@ public class MockCameraEngine extends CameraEngine {
@Override @Override
public void takePicture(@NonNull PictureResult.Stub stub) { public void takePicture(@NonNull PictureResult.Stub stub) {
super.takePicture(stub);
mPictureCaptured = true; mPictureCaptured = true;
} }
@Override
protected void onTakePicture(@NonNull PictureResult.Stub stub) {
}
@Override @Override
protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio viewAspectRatio) { protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio viewAspectRatio) {

@ -31,7 +31,7 @@ public class PictureRecorderTest extends BaseTest {
} }
}; };
recorder.take(); recorder.take();
Mockito.verify(listener, Mockito.times(1)).onPictureResult(result); Mockito.verify(listener, Mockito.times(1)).onPictureResult(result, null);
assertNull(recorder.mListener); assertNull(recorder.mListener);
assertNull(recorder.mResult); assertNull(recorder.mResult);
} }

@ -27,17 +27,19 @@ public class VideoRecorderTest extends BaseTest {
public void testRecorder() throws Exception { public void testRecorder() throws Exception {
VideoResult.Stub result = createStub(); VideoResult.Stub result = createStub();
VideoRecorder.VideoResultListener listener = Mockito.mock(VideoRecorder.VideoResultListener.class); VideoRecorder.VideoResultListener listener = Mockito.mock(VideoRecorder.VideoResultListener.class);
VideoRecorder recorder = new VideoRecorder(result, listener) { VideoRecorder recorder = new VideoRecorder(listener) {
public void start() {} @Override
public void stop() { protected void onStart() { }
@Override
protected void onStop() {
dispatchResult(); dispatchResult();
} }
}; };
recorder.start(); recorder.start(result);
recorder.stop(); recorder.stop();
Mockito.verify(listener, Mockito.times(1)).onVideoResult(result, null); Mockito.verify(listener, Mockito.times(1))
assertNull(recorder.mListener); .onVideoResult(result, null);
assertNull(recorder.mResult);
} }
private VideoResult.Stub createStub() throws Exception { private VideoResult.Stub createStub() throws Exception {

@ -713,6 +713,14 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
//region Public APIs for controls //region Public APIs for controls
/**
* Sets the experimental flag which occasionally can enable
* new, unstable beta features.
* @param experimental true to enable new features
*/
public void setExperimental(boolean experimental) {
mExperimental = experimental;
}
/** /**
* Shorthand for the appropriate set* method. * Shorthand for the appropriate set* method.

@ -13,6 +13,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import android.util.Log;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Task;
@ -506,6 +508,7 @@ public class Camera1Engine extends CameraEngine implements
@Override @Override
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) { public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) {
super.onVideoResult(result, exception);
if (result == null) { if (result == null) {
// Something went wrong, lock the camera again. // Something went wrong, lock the camera again.
mCamera.lock(); mCamera.lock();

@ -503,11 +503,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
if (mFullVideoPendingStub != null) { if (mFullVideoPendingStub != null) {
// Do not call takeVideo/onTakeVideo. It will reset some stub parameters that the recorder sets. // Do not call takeVideo/onTakeVideo. It will reset some stub parameters that the recorder sets.
// Also we are posting this so that doTakeVideo sees a started preview. // Also we are posting this so that doTakeVideo sees a started preview.
LOG.i("onStartPreview", "Posting doTakeVideo call.");
final VideoResult.Stub stub = mFullVideoPendingStub; final VideoResult.Stub stub = mFullVideoPendingStub;
mFullVideoPendingStub = null; mFullVideoPendingStub = null;
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
LOG.i("onStartPreview", "Executing doTakeVideo call.");
doTakeVideo(stub); doTakeVideo(stub);
} }
}); });
@ -522,6 +524,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@NonNull @NonNull
@Override @Override
protected Task<Void> onStopPreview() { protected Task<Void> onStopPreview() {
LOG.i("onStopPreview:", "About to clean up.");
if (mVideoRecorder != null) { if (mVideoRecorder != null) {
// This should synchronously call onVideoResult that will reset the repeating builder // This should synchronously call onVideoResult that will reset the repeating builder
// to the PREVIEW template. This is very important. // to the PREVIEW template. This is very important.
@ -546,6 +549,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
mRepeatingRequest = null; mRepeatingRequest = null;
mAutoFocusPoint = null; mAutoFocusPoint = null;
mAutoFocusGesture = null; mAutoFocusGesture = null;
LOG.i("onStopPreview:", "Returning.");
return Tasks.forResult(null); return Tasks.forResult(null);
} }
@ -553,6 +557,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@NonNull @NonNull
@Override @Override
protected Task<Void> onStopBind() { protected Task<Void> onStopBind() {
LOG.i("onStopBind:", "About to clean up.");
if (mFullVideoPersistentSurface != null) { if (mFullVideoPersistentSurface != null) {
mFullVideoPersistentSurface.release(); mFullVideoPersistentSurface.release();
mFullVideoPersistentSurface = null; mFullVideoPersistentSurface = null;
@ -572,6 +577,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
mSession.close(); mSession.close();
mSession = null; mSession = null;
LOG.i("onStopBind:", "Returning.");
return Tasks.forResult(null); return Tasks.forResult(null);
} }
@ -650,11 +656,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
@WorkerThread @WorkerThread
@Override @Override
protected void onTakeVideo(@NonNull VideoResult.Stub stub) { protected void onTakeVideo(@NonNull VideoResult.Stub stub) {
LOG.i("onTakeVideo", "called.");
stub.rotation = offset(REF_SENSOR, REF_OUTPUT); stub.rotation = offset(REF_SENSOR, REF_OUTPUT);
stub.size = flip(REF_SENSOR, REF_OUTPUT) ? mCaptureSize.flip() : mCaptureSize; stub.size = flip(REF_SENSOR, REF_OUTPUT) ? mCaptureSize.flip() : mCaptureSize;
if (!Full2VideoRecorder.SUPPORTS_PERSISTENT_SURFACE) { if (!Full2VideoRecorder.SUPPORTS_PERSISTENT_SURFACE) {
// On API 21 and 22, we must restart the session at each time. // On API 21 and 22, we must restart the session at each time.
// Save the pending data and restart the session. // Save the pending data and restart the session.
LOG.w("onTakeVideo", "calling restartBind.");
mFullVideoPendingStub = stub; mFullVideoPendingStub = stub;
restartBind(); restartBind();
} else { } else {

@ -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;

@ -2,6 +2,7 @@ package com.otaliastudios.cameraview.video;
import android.hardware.Camera; import android.hardware.Camera;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.util.Log;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
@ -44,9 +45,9 @@ public class Full1VideoRecorder extends FullVideoRecorder {
} }
@Override @Override
protected void onStop() { protected void dispatchResult() {
// Restore frame processing. // Restore frame processing.
mCamera.setPreviewCallbackWithBuffer(mEngine); mCamera.setPreviewCallbackWithBuffer(mEngine);
super.onStop(); super.dispatchResult();
} }
} }

@ -76,7 +76,9 @@ public abstract class VideoRecorder {
@CallSuper @CallSuper
protected void dispatchResult() { protected void dispatchResult() {
mIsRecording = false; mIsRecording = false;
if (mListener != null) mListener.onVideoResult(mResult, mError); if (mListener != null) {
mListener.onVideoResult(mResult, mError);
}
mResult = null; mResult = null;
mError = null; mError = null;
} }

Loading…
Cancel
Save