diff --git a/build.gradle b/build.gradle
index 469b3eb10..f0056dc6b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,7 +20,7 @@ allprojects {
ext {
compileSdkVersion = 29
- minSdkVersion = 15
+ minSdkVersion = 18
targetSdkVersion = 29
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
index 9f8a71de4..d9187fd80 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 void onVideoRecordingEnd() {
}
+ @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 5dbd656b5..189acab3a 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 void run() {
});
}
+ 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 boolean isTakingVideo() {
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 void run() {
}
});
}
+
+ @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 95549c648..8bc6f7682 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 void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception
}
}
+ @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 7e09f5884..5fbaa96a1 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.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 void onVideoRecordingEnd() {
}
}
+ @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 df95098f3..3c15c2c29 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 final boolean isTakingVideo() {
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 @@ protected void onStopVideo() {
}
}
+ @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 2817bbdb4..c32466f1f 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 @@ void dispatchOnExposureCorrectionChanged(float newValue, @NonNull float[] bounds
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 void startAutoFocus(@Nullable Gesture gesture,
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 c5a4a4e32..77e15319e 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 @@
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 @@ protected void onStop(boolean isCameraShutdown) {
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 b702dbf8b..11e4b7037 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 @@
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 @@ protected void onStop(boolean isCameraShutdown) {
}
}
+
@RendererThread
@Override
public void onRendererTextureCreated(int textureId) {
@@ -339,4 +341,16 @@ public void onEncodingEnd(int stopReason, @Nullable Exception e) {
}
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 d2a2bddb1..53b98cb91 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 interface VideoResultListener {
* 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 final void stop(boolean isCameraShutdown) {
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 @@ protected void dispatchVideoRecordingEnd() {
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/build.gradle b/demo/build.gradle
index fe41eebd0..90142b58d 100644
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -23,5 +23,8 @@ android {
dependencies {
implementation project(':cameraview')
implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'com.google.android.material:material:1.1.0-beta01'
+ implementation 'com.google.android.material:material:1.1.0-alpha09'
+ implementation 'com.otaliastudios:transcoder:0.8.0'
+ implementation 'com.otaliastudios.opengl:egloo:0.4.0'
+
}
diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml
index e7e1162ed..cf23792fe 100644
--- a/demo/src/main/AndroidManifest.xml
+++ b/demo/src/main/AndroidManifest.xml
@@ -4,6 +4,10 @@
package="com.otaliastudios.cameraview.demo">
+
+
+
+
+
+
diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/AlbumStorageDirFactory.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/AlbumStorageDirFactory.java
new file mode 100755
index 000000000..7cb44e919
--- /dev/null
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/AlbumStorageDirFactory.java
@@ -0,0 +1,7 @@
+package com.otaliastudios.cameraview.demo;
+
+import java.io.File;
+
+public abstract class AlbumStorageDirFactory {
+ public abstract File getAlbumStorageDir(String albumName);
+}
diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/BaseAlbumDirFactory.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/BaseAlbumDirFactory.java
new file mode 100755
index 000000000..58340b3a3
--- /dev/null
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/BaseAlbumDirFactory.java
@@ -0,0 +1,21 @@
+package com.otaliastudios.cameraview.demo;
+
+import android.os.Environment;
+
+import java.io.File;
+
+public final class BaseAlbumDirFactory extends AlbumStorageDirFactory {
+
+ // Standard storage location for digital camera files
+ private static final String CAMERA_DIR = "/Pictures/";
+
+
+ @Override
+ public File getAlbumStorageDir(String albumName) {
+ return new File(
+ Environment.getExternalStorageDirectory()
+ + CAMERA_DIR
+ + albumName
+ );
+ }
+}
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 bf5a4c521..1ef9460b5 100644
--- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
@@ -1,25 +1,33 @@
package com.otaliastudios.cameraview.demo;
+import android.Manifest;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.YuvImage;
+import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import android.os.Environment;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.ImageButton;
+import android.widget.TextView;
import android.widget.Toast;
import com.otaliastudios.cameraview.CameraException;
@@ -32,16 +40,21 @@
import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.filter.Filters;
-import com.otaliastudios.cameraview.filter.MultiFilter;
-import com.otaliastudios.cameraview.filters.BrightnessFilter;
-import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor;
+import com.otaliastudios.transcoder.Transcoder;
+import com.otaliastudios.transcoder.TranscoderListener;
+import com.otaliastudios.transcoder.TranscoderOptions;
+import com.otaliastudios.transcoder.sink.DataSink;
+import com.otaliastudios.transcoder.sink.DefaultDataSink;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
public class CameraActivity extends AppCompatActivity implements View.OnClickListener, OptionView.Callback {
@@ -53,10 +66,25 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
private CameraView camera;
private ViewGroup controlPanel;
private long mCaptureTime;
-
+ private long mVideoCaptureTime;
private int mCurrentFilter = 0;
private final Filters[] mAllFilters = Filters.values();
+
+ private TextView mTvTimer;
+
+ private ImageButton mImgPlayPauseVideo;
+ private ImageButton mImgStopVideo;
+
+ private String videoFilePath;
+
+ private static final int MAX_RECORDING_TIME = 2 * 60 * 1000; // 2 MINUTES!
+
+
+ private Handler mUpdateDurationHandler;
+
+ private static final String TAG = "CameraActivity_";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -67,6 +95,82 @@ protected void onCreate(Bundle savedInstanceState) {
camera.setLifecycleOwner(this);
camera.addCameraListener(new Listener());
+ mImgPlayPauseVideo = findViewById(R.id.imgPlayPauseVideo);
+ mImgStopVideo = findViewById(R.id.imgStopVideo);
+
+ mTvTimer = findViewById(R.id.tvTimer);
+ mTvTimer.setText("");
+
+ 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()) {
+
+ // PAUSE THE VIDEO
+
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
+ camera.pauseVideoRecording();
+
+ killVideoDurationElapsedTimeHandler();
+ mVideoCaptureTime = System.currentTimeMillis(); // saves the pause time!
+ long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
+
+ } else if (camera.isRecordingPaused()) {
+
+ // RESUME VIDEO RECORDING
+
+
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
+ camera.resumeVideoRecording();
+
+ long pauseTime = System.currentTimeMillis() - mVideoCaptureTime;
+ mVideoCaptureTime = mVideoCaptureTime+pauseTime;
+
+// mVideoCaptureTime = System.currentTimeMillis() + mVideoCaptureTime;
+// mVideoCaptureTime = System.currentTimeMillis() -(System.currentTimeMillis() - mVideoCaptureTime);
+// mVideoCaptureTime = System.currentTimeMillis() - mVideoCaptureTime;
+ long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
+ updateVideoDurationElapsedTime();
+
+
+ } else {
+
+ mVideoCaptureTime = System.currentTimeMillis();
+
+ updateVideoDurationElapsedTime();
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
+ File albumF = getAlbumDir();
+ String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
+ videoFileName = videoFileName+".mp4";
+
+ File videoFile = new File(albumF,videoFileName);
+ videoFilePath = videoFile.getAbsolutePath();
+ camera.takeVideo(videoFile, MAX_RECORDING_TIME);
+// camera.takeVideoSnapshot(videoFile, MAX_RECORDING_TIME);
+
+
+
+// camera.takeVideo(new File(getFilesDir(), "video.mp4"), MAX_RECORDING_TIME);
+ }
+
+ }
+ });
+
+
+ mImgStopVideo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ killVideoDurationElapsedTimeHandler(); // make sure not to init more then one instance
+ camera.stopVideo();
+ }
+ });
+
+
if (USE_FRAME_PROCESSOR) {
camera.addFrameProcessor(new FrameProcessor() {
private long lastTime = System.currentTimeMillis();
@@ -189,6 +293,42 @@ public void onAnimationUpdate(ValueAnimator animation) {
animator.start();
}
+
+
+ private final String TEMP_FILE_DIR = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +getPackageName() + "/temp";
+
+ public File getTempMediaFileDirectory() throws NullPointerException {
+ File result = new File(TEMP_FILE_DIR);
+ if (!result.exists()){
+ result.mkdirs();
+ }
+ if (result != null){
+ return result;
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+ private File getAlbumDir() {
+ File storageDir = null;
+ AlbumStorageDirFactory mAlbumStorageDirFactory = new BaseAlbumDirFactory();
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getString(R.string.album_name));
+ if (!storageDir.mkdirs()) {
+ if (!storageDir.exists()) {
+// Logger.Log("failed to create directory");
+// return storageDir;
+ return null;
+ }
+ }
+ } else {
+// Logger.Log("External storage is not mounted READ/WRITE.");
+ }
+ return storageDir;
+ }
+
+
+
private void message(@NonNull String content, boolean important) {
if (important) {
LOG.w(content);
@@ -244,6 +384,30 @@ public void onVideoTaken(@NonNull VideoResult result) {
Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class);
startActivity(intent);
LOG.w("onVideoTaken called! Launched activity.");
+
+
+
+
+ // notify yhe gallery after a new video added
+ if (videoFilePath != null) {
+ // check if android M has access to write external storage
+ if (ActivityCompat.checkSelfPermission(CameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ File f = new File(videoFilePath);
+ Uri contentUri;
+
+ contentUri = Uri.fromFile(f);
+
+ mediaScanIntent.setData(contentUri);
+ sendBroadcast(mediaScanIntent);
+
+
+// transcodeVideo();
+
+ }
+
}
@Override
@@ -257,8 +421,27 @@ public void onVideoRecordingEnd() {
super.onVideoRecordingEnd();
message("Video taken. Processing...", false);
LOG.w("onVideoRecordingEnd!");
+ mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
+ mImgStopVideo.setVisibility(View.GONE);
+ mTvTimer.setText("");
+ }
+
+
+ @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);
@@ -272,6 +455,64 @@ public void onZoomChanged(float newValue, @NonNull float[] bounds, @Nullable Poi
}
}
+ private void transcodeVideo() {
+
+// File f = new File(videoFilePath);
+// Uri contentUri;
+//
+// contentUri = Uri.fromFile(f);
+
+ File albumF = getAlbumDir();
+ String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
+ videoFileName = videoFileName+".mp4";
+
+ File videoFile = new File(albumF,videoFileName);
+ final String videoFilePathCoded = videoFile.getAbsolutePath();
+ Transcoder.into(videoFilePathCoded).addDataSource(videoFilePath).setListener(new TranscoderListener() {
+ @Override
+ public void onTranscodeProgress(double progress) {
+ Log.d(TAG, "onTranscodeProgress: "+progress);
+ }
+
+ @Override
+ public void onTranscodeCompleted(int successCode) {
+ if (successCode == Transcoder.SUCCESS_TRANSCODED) {
+
+
+ // notify yhe gallery after a new video added
+ // check if android M has access to write external storage
+ if (ActivityCompat.checkSelfPermission(CameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ File f = new File(videoFilePathCoded);
+ Uri contentUri;
+
+ contentUri = Uri.fromFile(f);
+
+ mediaScanIntent.setData(contentUri);
+ sendBroadcast(mediaScanIntent);
+
+
+ }else{
+ // error
+ }
+ }
+
+ @Override
+ public void onTranscodeCanceled() {
+ Log.d(TAG, "onTranscodeCanceled: ");
+ }
+
+ @Override
+ public void onTranscodeFailed(@NonNull Throwable exception) {
+ // error!
+ Log.d(TAG, "onTranscodeFailed: "+exception.getMessage());
+
+ }
+ }).transcode();
+ }
+
@Override
public void onClick(View view) {
switch (view.getId()) {
@@ -381,6 +622,37 @@ private void changeCurrentFilter() {
// camera.setFilter(new MultiFilter(duotone, filter.newInstance()));
}
+
+
+
+ private void updateVideoDurationElapsedTime(){
+
+ killVideoDurationElapsedTimeHandler(); // make sure not to init more then one instance
+
+ mUpdateDurationHandler = new Handler();
+ mUpdateDurationHandler.postDelayed(mVideoDurationRunnable,1000);
+
+ long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
+ mTvTimer.setText(DateUtils.formatElapsedTime(secondsElapsed));
+
+
+ }
+
+ private Runnable mVideoDurationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ updateVideoDurationElapsedTime();
+ }
+ };
+
+
+ private void killVideoDurationElapsedTimeHandler(){
+ if (mUpdateDurationHandler!=null){
+ mUpdateDurationHandler.removeCallbacks(mVideoDurationRunnable);
+ }
+ }
+
+
@Override
public boolean onValueChanged(@NonNull Option option, @NonNull T value, @NonNull String name) {
if ((option instanceof Option.Width || option instanceof Option.Height)) {
@@ -414,4 +686,18 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}
//endregion
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ killVideoDurationElapsedTimeHandler();
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ killVideoDurationElapsedTimeHandler();
+ }
}
diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
index a37192ccc..72f58161c 100644
--- a/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
@@ -1,15 +1,21 @@
package com.otaliastudios.cameraview.demo;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
+
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
+import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -23,8 +29,13 @@
import com.otaliastudios.cameraview.FileCallback;
import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.size.AspectRatio;
+import com.otaliastudios.transcoder.Transcoder;
+import com.otaliastudios.transcoder.TranscoderListener;
import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
public class VideoPreviewActivity extends AppCompatActivity {
@@ -33,6 +44,8 @@ public class VideoPreviewActivity extends AppCompatActivity {
private static VideoResult videoResult;
+ private static final String TAG = "CameraActivity_";
+
public static void setVideoResult(@Nullable VideoResult result) {
videoResult = result;
}
@@ -93,6 +106,10 @@ public void onPrepared(MediaPlayer mp) {
}
}
});
+
+
+
+ transcodeVideo();
}
void playVideo() {
@@ -101,6 +118,84 @@ void playVideo() {
}
}
+
+ private void transcodeVideo() {
+
+// File f = new File(videoFilePath);
+// Uri contentUri;
+//
+// contentUri = Uri.fromFile(f);
+
+ File albumF = getAlbumDir();
+ String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
+ videoFileName = videoFileName+".mp4";
+
+ File videoFile = new File(albumF,videoFileName);
+ final String videoFilePathCoded = videoFile.getAbsolutePath();
+ Transcoder.into(videoFilePathCoded).addDataSource(videoResult.getFile().getAbsolutePath()).setListener(new TranscoderListener() {
+ @Override
+ public void onTranscodeProgress(double progress) {
+ Log.d(TAG, "onTranscodeProgress: "+progress);
+ }
+
+ @Override
+ public void onTranscodeCompleted(int successCode) {
+ if (successCode == Transcoder.SUCCESS_TRANSCODED) {
+
+
+ // notify yhe gallery after a new video added
+ // check if android M has access to write external storage
+ if (ActivityCompat.checkSelfPermission(VideoPreviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ File f = new File(videoFilePathCoded);
+ Uri contentUri;
+
+ contentUri = Uri.fromFile(f);
+
+ mediaScanIntent.setData(contentUri);
+ sendBroadcast(mediaScanIntent);
+
+
+ }else{
+ // error
+ }
+ }
+
+ @Override
+ public void onTranscodeCanceled() {
+ Log.d(TAG, "onTranscodeCanceled: ");
+ }
+
+ @Override
+ public void onTranscodeFailed(@NonNull Throwable exception) {
+ // error!
+ Log.d(TAG, "onTranscodeFailed: "+exception.getMessage());
+
+ }
+ }).transcode();
+ }
+
+
+ private File getAlbumDir() {
+ File storageDir = null;
+ AlbumStorageDirFactory mAlbumStorageDirFactory = new BaseAlbumDirFactory();
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getString(R.string.album_name));
+ if (!storageDir.mkdirs()) {
+ if (!storageDir.exists()) {
+// Logger.Log("failed to create directory");
+// return storageDir;
+ return null;
+ }
+ }
+ } else {
+// Logger.Log("External storage is not mounted READ/WRITE.");
+ }
+ return storageDir;
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
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 000000000..83e5e1c2b
--- /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 000000000..41bec11bd
--- /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 000000000..081c346d5
--- /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 2cc1d8a5a..3ba7cabc5 100644
--- a/demo/src/main/res/layout/activity_camera.xml
+++ b/demo/src/main/res/layout/activity_camera.xml
@@ -7,13 +7,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+
+
+
+
-
+
CameraView
+ CameraView Album