|
|
|
@ -1,26 +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; |
|
|
|
@ -33,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 { |
|
|
|
@ -54,16 +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); |
|
|
|
@ -77,6 +98,9 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis |
|
|
|
|
mImgPlayPauseVideo = findViewById(R.id.imgPlayPauseVideo); |
|
|
|
|
mImgStopVideo = findViewById(R.id.imgStopVideo); |
|
|
|
|
|
|
|
|
|
mTvTimer = findViewById(R.id.tvTimer); |
|
|
|
|
mTvTimer.setText(""); |
|
|
|
|
|
|
|
|
|
mImgStopVideo.setVisibility(View.GONE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -86,14 +110,52 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis |
|
|
|
|
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); |
|
|
|
|
camera.takeVideo(new File(getFilesDir(), "video.mp4"), MAX_RECORDING_TIME); |
|
|
|
|
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);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
@ -103,6 +165,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis |
|
|
|
|
mImgStopVideo.setOnClickListener(new View.OnClickListener() { |
|
|
|
|
@Override |
|
|
|
|
public void onClick(View view) { |
|
|
|
|
killVideoDurationElapsedTimeHandler(); // make sure not to init more then one instance
|
|
|
|
|
camera.stopVideo(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
@ -230,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); |
|
|
|
@ -285,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 |
|
|
|
@ -300,6 +423,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis |
|
|
|
|
LOG.w("onVideoRecordingEnd!"); |
|
|
|
|
mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp); |
|
|
|
|
mImgStopVideo.setVisibility(View.GONE); |
|
|
|
|
mTvTimer.setText(""); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -331,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()) { |
|
|
|
@ -440,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)) { |
|
|
|
@ -473,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(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|