pull/789/merge
digitalmidges 5 years ago committed by GitHub
commit 26a5d7a165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build.gradle
  2. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
  3. 50
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  4. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  5. 17
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  6. 58
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
  7. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  8. 27
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  9. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  10. 62
      cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
  11. 5
      demo/build.gradle
  12. 7
      demo/src/main/AndroidManifest.xml
  13. 7
      demo/src/main/java/com/otaliastudios/cameraview/demo/AlbumStorageDirFactory.java
  14. 21
      demo/src/main/java/com/otaliastudios/cameraview/demo/BaseAlbumDirFactory.java
  15. 296
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  16. 95
      demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
  17. 5
      demo/src/main/res/drawable/ic_pause_circle_outline_black_24dp.xml
  18. 5
      demo/src/main/res/drawable/ic_play_circle_outline_black_24dp.xml
  19. 5
      demo/src/main/res/drawable/ic_stop_white_24dp.xml
  20. 50
      demo/src/main/res/layout/activity_camera.xml
  21. 1
      demo/src/main/res/values/strings.xml

@ -20,7 +20,7 @@ allprojects {
ext {
compileSdkVersion = 29
minSdkVersion = 15
minSdkVersion = 18
targetSdkVersion = 29
}

@ -161,4 +161,14 @@ public abstract class CameraListener {
}
@UiThread
public void onVideoRecordingPause() {
}
@UiThread
public void onVideoRecordingResume() {
}
}

@ -1856,6 +1856,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
@ -2104,6 +2125,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
@ -2402,6 +2428,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

@ -440,6 +440,16 @@ public class Camera1Engine extends CameraBaseEngine implements
}
}
@Override
public void onVideoRecordingPause() {
setVideoRecordingPauseCallback();
}
@Override
public void onVideoRecordingResume() {
setVideoRecordingResumeCallback();
}
//endregion
//region Parameters

@ -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;
@ -951,6 +952,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);

@ -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) {

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

@ -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();
}
}
}
}

@ -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;
@ -100,6 +101,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
}
}
@RendererThread
@Override
public void onRendererTextureCreated(int textureId) {
@ -336,4 +338,16 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
}
dispatchResult();
}
@Override
protected void onPauseVideoRecording() {
// not supported
}
@Override
protected void onResumeVideoRecording() {
// not supported
}
}

@ -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();
}
}
}

@ -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'
}

@ -4,6 +4,10 @@
package="com.otaliastudios.cameraview.demo">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk tools:overrideLibrary="com.otaliastudios.opengl" />
<application
android:allowBackup="false"
@ -11,6 +15,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
tools:ignore="GoogleAppIndexingWarning">
<activity
@ -41,6 +46,8 @@
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>

@ -0,0 +1,7 @@
package com.otaliastudios.cameraview.demo;
import java.io.File;
public abstract class AlbumStorageDirFactory {
public abstract File getAlbumStorageDir(String albumName);
}

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

@ -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.controls.Mode;
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 @@ 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);
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 class CameraActivity extends AppCompatActivity implements View.OnClickLis
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 class CameraActivity extends AppCompatActivity implements View.OnClickLis
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 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);
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 class CameraActivity extends AppCompatActivity implements View.OnClickLis
}
}
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 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
// 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 <T> boolean onValueChanged(@NonNull Option<T> option, @NonNull T value, @NonNull String name) {
if ((option instanceof Option.Width || option instanceof Option.Height)) {
@ -414,4 +686,18 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
}
//endregion
@Override
protected void onPause() {
super.onPause();
killVideoDurationElapsedTimeHandler();
}
@Override
protected void onDestroy() {
super.onDestroy();
killVideoDurationElapsedTimeHandler();
}
}

@ -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.CameraUtils;
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 class VideoPreviewActivity extends AppCompatActivity {
}
}
});
transcodeVideo();
}
void playVideo() {
@ -101,6 +118,84 @@ public class VideoPreviewActivity extends AppCompatActivity {
}
}
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();

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M9,16h2L11,8L9,8v8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM13,16h2L15,8h-2v8z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M6,6h12v12H6z"/>
</vector>

@ -7,13 +7,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Camera -->
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginBottom="88dp"
app:cameraVideoCodec="h264"
app:cameraVideoSizeMinWidth="100"
app:cameraVideoSizeMinHeight="100"
app:cameraVideoSizeMaxWidth="780"
app:cameraVideoSizeMaxHeight="1280"
app:cameraSnapshotMaxWidth="1080"
app:cameraSnapshotMaxHeight="1920"
app:cameraUseDeviceOrientation="true"
app:cameraPictureSnapshotMetering="true"
app:cameraPreviewFrameRate="30"
android:keepScreenOn="true"
app:cameraExperimental="true"
app:cameraEngine="camera2"
@ -26,6 +44,7 @@
app:cameraGestureTap="autoFocus"
app:cameraGestureLongTap="none"
app:cameraGesturePinch="zoom"
app:cameraRequestPermissions="true"
app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="exposureCorrection"
app:cameraMode="picture"
@ -51,6 +70,23 @@
android:layout_marginBottom="96dp"
android:layout_gravity="bottom|start">
<ImageButton
android:id="@+id/imgStopVideo"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_margin="16dp"
android:background="@drawable/background"
android:elevation="3dp"
app:srcCompat="@drawable/ic_stop_white_24dp" />
<ImageButton
android:id="@+id/imgPlayPauseVideo"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_margin="16dp"
android:background="@drawable/background"
android:elevation="3dp"
app:srcCompat="@drawable/ic_play_circle_outline_black_24dp" />
<ImageButton
android:id="@+id/changeFilter"
android:layout_width="56dp"
@ -162,7 +198,15 @@
android:text="SNAP"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tvTimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TIME"
android:layout_margin="10dp"
android:textSize="20sp"
android:textColor="#FFFFFF"
/>
<!-- Edit -->
<androidx.core.widget.NestedScrollView
android:id="@+id/controls"

@ -1,3 +1,4 @@
<resources>
<string name="app_name">CameraView</string>
<string name="album_name" translatable="false">CameraView Album</string>
</resources>

Loading…
Cancel
Save