diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
index 9f8a71de..d9187fd8 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
@@ -161,4 +161,14 @@ public abstract class CameraListener {
}
+ @UiThread
+ public void onVideoRecordingPause() {
+
+ }
+
+ @UiThread
+ public void onVideoRecordingResume() {
+
+ }
+
}
\ No newline at end of file
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
index 5dbd656b..189acab3 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
@@ -1834,6 +1834,27 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
});
}
+ public void pauseVideoRecording() {
+ mCameraEngine.pauseVideoRecording();
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getKeepScreenOn() != mKeepScreenOn) setKeepScreenOn(mKeepScreenOn);
+ }
+ });
+ }
+
+
+ public void resumeVideoRecording() {
+ mCameraEngine.resumeVideoRecording();
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getKeepScreenOn() != mKeepScreenOn) setKeepScreenOn(mKeepScreenOn);
+ }
+ });
+ }
+
/**
* Sets the max width for snapshots taken with {@link #takePictureSnapshot()} or
* {@link #takeVideoSnapshot(File)}. If the snapshot width exceeds this value, the snapshot
@@ -2082,6 +2103,11 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraEngine.isTakingVideo();
}
+
+ public boolean isRecordingPaused() {
+ return mCameraEngine.isRecordingPaused();
+ }
+
/**
* Returns true if the camera is currently capturing a picture
* @return boolean indicating if the camera is capturing a picture
@@ -2380,6 +2406,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
}
});
}
+
+ @Override
+ public void dispatchOnVideoRecordingPause() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (CameraListener listener : mListeners) {
+ listener.onVideoRecordingPause();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void dispatchOnVideoRecordingResume() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (CameraListener listener : mListeners) {
+ listener.onVideoRecordingResume();
+ }
+ }
+ });
+ }
}
//endregion
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
index 95549c64..8bc6f768 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
@@ -439,6 +439,16 @@ public class Camera1Engine extends CameraBaseEngine implements
}
}
+ @Override
+ public void onVideoRecordingPause() {
+ setVideoRecordingPauseCallback();
+ }
+
+ @Override
+ public void onVideoRecordingResume() {
+ setVideoRecordingResumeCallback();
+ }
+
//endregion
//region Parameters
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
index 7e09f588..5fbaa96a 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
@@ -19,6 +19,7 @@ import android.location.Location;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
+import android.util.Log;
import android.util.Pair;
import android.util.Range;
import android.util.Rational;
@@ -966,6 +967,22 @@ public class Camera2Engine extends CameraBaseEngine implements
}
}
+ @Override
+ public void onVideoRecordingPause() {
+ Log.d(TAG, "onVideoRecordingPause: ");
+ setVideoRecordingPauseCallback();
+
+
+ }
+
+ @Override
+ public void onVideoRecordingResume() {
+ Log.d(TAG, "onVideoRecordingResume: ");
+ setVideoRecordingResumeCallback();
+ }
+
+
+
@Override
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) {
super.onVideoResult(result, exception);
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
index df95098f..3c15c2c2 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
@@ -560,6 +560,13 @@ public abstract class CameraBaseEngine extends CameraEngine {
return mVideoRecorder != null && mVideoRecorder.isRecording();
}
+
+ @Override
+ public final boolean isRecordingPaused() {
+ return mVideoRecorder != null && mVideoRecorder.isRecordingPaused();
+ }
+
+
@Override
public final void takeVideo(final @NonNull VideoResult.Stub stub,
final @Nullable File file,
@@ -644,6 +651,57 @@ public abstract class CameraBaseEngine extends CameraEngine {
}
}
+ @Override
+ public final void pauseVideoRecording() {
+ getOrchestrator().schedule("pause video", true, new Runnable() {
+ @Override
+ public void run() {
+// LOG.i("pauseVideoRecording", "running. isTakingVideo?", isTakingVideo());
+ onPauseVideoRecording();
+ }
+ });
+ }
+
+ @EngineThread
+ @SuppressWarnings("WeakerAccess")
+ protected void onPauseVideoRecording() {
+ if (mVideoRecorder != null) {
+ mVideoRecorder.pauseVideoRecording();
+ }
+ }
+
+
+ protected void setVideoRecordingPauseCallback() {
+ getCallback().dispatchOnVideoRecordingPause();
+ }
+
+
+ @Override
+ public final void resumeVideoRecording() {
+ getOrchestrator().schedule("resume video", true, new Runnable() {
+ @Override
+ public void run() {
+// LOG.i("pauseVideoRecording", "running. isTakingVideo?", isTakingVideo());
+ onResumeVideoRecording();
+ }
+ });
+ }
+
+
+ @EngineThread
+ @SuppressWarnings("WeakerAccess")
+ protected void onResumeVideoRecording() {
+ if (mVideoRecorder != null) {
+ mVideoRecorder.resumeVideoRecording();
+ }
+ }
+
+ protected void setVideoRecordingResumeCallback() {
+ getCallback().dispatchOnVideoRecordingResume();
+ }
+
+
+
@CallSuper
@Override
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) {
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
index 2817bbdb..c32466f1 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
@@ -127,6 +127,8 @@ public abstract class CameraEngine implements
void dispatchError(CameraException exception);
void dispatchOnVideoRecordingStart();
void dispatchOnVideoRecordingEnd();
+ void dispatchOnVideoRecordingPause();
+ void dispatchOnVideoRecordingResume();
}
protected static final String TAG = CameraEngine.class.getSimpleName();
@@ -714,11 +716,14 @@ public abstract class CameraEngine implements
public abstract void takePictureSnapshot(final @NonNull PictureResult.Stub stub);
public abstract boolean isTakingVideo();
+ public abstract boolean isRecordingPaused();
public abstract void takeVideo(@NonNull VideoResult.Stub stub,
@Nullable File file,
@Nullable FileDescriptor fileDescriptor);
public abstract void takeVideoSnapshot(@NonNull VideoResult.Stub stub, @NonNull File file);
public abstract void stopVideo();
+ public abstract void pauseVideoRecording();
+ public abstract void resumeVideoRecording();
//endregion
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
index c5a4a4e3..77e15319 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
@@ -2,6 +2,8 @@ package com.otaliastudios.cameraview.video;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
+import android.os.Build;
+import android.util.Log;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.VideoResult;
@@ -346,4 +348,29 @@ public abstract class FullVideoRecorder extends VideoRecorder {
dispatchResult();
}
+
+ @Override
+ protected void onPauseVideoRecording() {
+ Log.d(TAG, "onPauseVideoRecording: ");
+ if (mMediaRecorder != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
+ mMediaRecorder.pause();
+ dispatchVideoRecordingPause();
+ }
+
+ }
+ }
+
+
+ @Override
+ protected void onResumeVideoRecording() {
+ Log.d(TAG, "onPauseVideoRecording: ");
+ if (mMediaRecorder != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
+ mMediaRecorder.resume();
+ dispatchVideoRecordingResume();
+ }
+ }
+ }
+
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
index b702dbf8..11e4b703 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
@@ -3,6 +3,7 @@ package com.otaliastudios.cameraview.video;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.os.Build;
+import android.util.Log;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.DeviceEncoders;
@@ -103,6 +104,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
}
}
+
@RendererThread
@Override
public void onRendererTextureCreated(int textureId) {
@@ -339,4 +341,16 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
}
dispatchResult();
}
+
+
+ @Override
+ protected void onPauseVideoRecording() {
+ // not supported
+ }
+
+ @Override
+ protected void onResumeVideoRecording() {
+ // not supported
+ }
+
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
index d2a2bddb..53b98cb9 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
@@ -39,11 +39,17 @@ public abstract class VideoRecorder {
* and soon {@link #onVideoResult(VideoResult.Stub, Exception)} will be called.
*/
void onVideoRecordingEnd();
+
+ void onVideoRecordingPause();
+
+ void onVideoRecordingResume();
+
}
private final static int STATE_IDLE = 0;
private final static int STATE_RECORDING = 1;
private final static int STATE_STOPPING = 2;
+ private final static int STATE_PAUSING = 3;
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) VideoResult.Stub mResult;
private final VideoResultListener mListener;
@@ -105,7 +111,15 @@ public abstract class VideoRecorder {
public boolean isRecording() {
// true if not idle.
synchronized (mStateLock) {
- return mState != STATE_IDLE;
+ return mState != STATE_IDLE && mState!=STATE_PAUSING;
+ }
+ }
+
+
+ public boolean isRecordingPaused() {
+ // true if not idle.
+ synchronized (mStateLock) {
+ return mState ==STATE_PAUSING;
}
}
@@ -173,4 +187,50 @@ public abstract class VideoRecorder {
mListener.onVideoRecordingEnd();
}
}
+
+
+ public final void pauseVideoRecording() {
+ synchronized (mStateLock) {
+ if (mState == STATE_IDLE) {
+ return;
+ }
+ LOG.i("pause:", "Changed state to STATE_PAUSING");
+ mState = STATE_PAUSING;
+ }
+ onPauseVideoRecording();
+ }
+
+ protected abstract void onPauseVideoRecording();
+
+ public final void resumeVideoRecording() {
+ synchronized (mStateLock) {
+ if (mState == STATE_IDLE) {
+ return;
+ }
+ LOG.i("resume:", "Changed state to STATE_RECORDING");
+ mState = STATE_RECORDING;
+ }
+ onResumeVideoRecording();
+ }
+
+ protected abstract void onResumeVideoRecording();
+
+ @SuppressWarnings("WeakerAccess")
+ @CallSuper
+ protected void dispatchVideoRecordingPause() {
+ LOG.i("dispatchVideoRecordingPause:", "pause");
+ if (mListener != null) {
+ mListener.onVideoRecordingPause();
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ @CallSuper
+ protected void dispatchVideoRecordingResume() {
+ LOG.i("dispatchVideoRecordingResume:", "resume");
+ if (mListener != null) {
+ mListener.onVideoRecordingResume();
+ }
+ }
+
}
diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
index bf5a4c52..a60e8d43 100644
--- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
@@ -20,6 +20,7 @@ import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.ImageButton;
import android.widget.Toast;
import com.otaliastudios.cameraview.CameraException;
@@ -57,6 +58,12 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
private int mCurrentFilter = 0;
private final Filters[] mAllFilters = Filters.values();
+
+ private ImageButton mImgPlayPauseVideo;
+ private ImageButton mImgStopVideo;
+
+ private static final int MAX_RECORDING_TIME = 2 * 60 * 1000; // 2 MINUTES!
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -67,6 +74,40 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
camera.setLifecycleOwner(this);
camera.addCameraListener(new Listener());
+ mImgPlayPauseVideo = findViewById(R.id.imgPlayPauseVideo);
+ mImgStopVideo = findViewById(R.id.imgStopVideo);
+
+ mImgStopVideo.setVisibility(View.GONE);
+
+
+ mImgPlayPauseVideo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ camera.setMode(Mode.VIDEO);
+ mImgStopVideo.setVisibility(View.VISIBLE);
+ if (camera.isTakingVideo()) {
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
+ camera.pauseVideoRecording();
+ } else if (camera.isRecordingPaused()) {
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
+ camera.resumeVideoRecording();
+ } else {
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
+ camera.takeVideo(new File(getFilesDir(), "video.mp4"), MAX_RECORDING_TIME);
+ }
+
+ }
+ });
+
+
+ mImgStopVideo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ camera.stopVideo();
+ }
+ });
+
+
if (USE_FRAME_PROCESSOR) {
camera.addFrameProcessor(new FrameProcessor() {
private long lastTime = System.currentTimeMillis();
@@ -257,8 +298,26 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
super.onVideoRecordingEnd();
message("Video taken. Processing...", false);
LOG.w("onVideoRecordingEnd!");
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
+ mImgStopVideo.setVisibility(View.GONE);
}
+
+ @Override
+ public void onVideoRecordingPause() {
+ super.onVideoRecordingPause();
+ message("Video pause", true);
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
+ }
+
+ @Override
+ public void onVideoRecordingResume() {
+ super.onVideoRecordingResume();
+ message("Video resume",true);
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
+ }
+
+
@Override
public void onExposureCorrectionChanged(float newValue, @NonNull float[] bounds, @Nullable PointF[] fingers) {
super.onExposureCorrectionChanged(newValue, bounds, fingers);
diff --git a/demo/src/main/res/drawable/ic_pause_circle_outline_black_24dp.xml b/demo/src/main/res/drawable/ic_pause_circle_outline_black_24dp.xml
new file mode 100644
index 00000000..83e5e1c2
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_pause_circle_outline_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/demo/src/main/res/drawable/ic_play_circle_outline_black_24dp.xml b/demo/src/main/res/drawable/ic_play_circle_outline_black_24dp.xml
new file mode 100644
index 00000000..41bec11b
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_play_circle_outline_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/demo/src/main/res/drawable/ic_stop_white_24dp.xml b/demo/src/main/res/drawable/ic_stop_white_24dp.xml
new file mode 100644
index 00000000..081c346d
--- /dev/null
+++ b/demo/src/main/res/drawable/ic_stop_white_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml
index 2cc1d8a5..3d99aea1 100644
--- a/demo/src/main/res/layout/activity_camera.xml
+++ b/demo/src/main/res/layout/activity_camera.xml
@@ -51,6 +51,23 @@
android:layout_marginBottom="96dp"
android:layout_gravity="bottom|start">
+
+
+