implement video recording
This commit is contained in:
parent
fbc3d9b162
commit
4a542be54d
|
@ -6,6 +6,7 @@
|
||||||
android:name="android.hardware.camera"
|
android:name="android.hardware.camera"
|
||||||
android:required="true"/>
|
android:required="true"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,15 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
|
||||||
if (isPhoto) {
|
if (isPhoto) {
|
||||||
preview.takePicture();
|
preview.takePicture();
|
||||||
} else {
|
} else {
|
||||||
|
final Resources res = getResources();
|
||||||
|
final boolean isRecording = preview.toggleRecording();
|
||||||
|
if (isRecording) {
|
||||||
|
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_stop));
|
||||||
|
toggleCameraBtn.setVisibility(View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
|
||||||
|
toggleCameraBtn.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,16 +107,25 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
|
||||||
|
|
||||||
@OnClick(R.id.toggle_videocam)
|
@OnClick(R.id.toggle_videocam)
|
||||||
public void toggleVideo() {
|
public void toggleVideo() {
|
||||||
final Resources res = getResources();
|
|
||||||
isPhoto = !isPhoto;
|
isPhoto = !isPhoto;
|
||||||
|
toggleCameraBtn.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
if (isPhoto) {
|
if (isPhoto) {
|
||||||
|
final Resources res = getResources();
|
||||||
togglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam));
|
togglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam));
|
||||||
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera));
|
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera));
|
||||||
|
preview.initPhotoMode();
|
||||||
} else {
|
} else {
|
||||||
|
initVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVideo() {
|
||||||
|
final Resources res = getResources();
|
||||||
togglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo));
|
togglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo));
|
||||||
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
|
shutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
|
||||||
}
|
preview.initRecorder();
|
||||||
|
toggleCameraBtn.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideNavigationBarIcons() {
|
private void hideNavigationBarIcons() {
|
||||||
|
@ -130,12 +147,19 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
|
||||||
final Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
final Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||||
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
|
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isPhoto) {
|
||||||
|
preview.initRecorder();
|
||||||
|
initVideo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
if (preview != null) {
|
||||||
preview.releaseCamera();
|
preview.releaseCamera();
|
||||||
|
}
|
||||||
|
|
||||||
if (sensorManager != null)
|
if (sensorManager != null)
|
||||||
sensorManager.unregisterListener(this);
|
sensorManager.unregisterListener(this);
|
||||||
|
|
|
@ -6,17 +6,13 @@ import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
import android.media.MediaScannerConnection;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
||||||
private static final String TAG = PhotoProcessor.class.getSimpleName();
|
private static final String TAG = PhotoProcessor.class.getSimpleName();
|
||||||
|
@ -30,12 +26,13 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(byte[]... params) {
|
protected Void doInBackground(byte[]... params) {
|
||||||
final File photoFile = getOutputMediaFile();
|
final String photoPath = Utils.getOutputMediaFile(mContext, true);
|
||||||
if (photoFile == null) {
|
if (photoPath.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final File photoFile = new File(photoPath);
|
||||||
final byte[] data = params[0];
|
final byte[] data = params[0];
|
||||||
final FileOutputStream fos = new FileOutputStream(photoFile);
|
final FileOutputStream fos = new FileOutputStream(photoFile);
|
||||||
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
|
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
|
||||||
|
@ -45,7 +42,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
|
||||||
|
|
||||||
fos.close();
|
fos.close();
|
||||||
scanPhoto(photoFile);
|
Utils.scanFile(photoPath, mContext);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.d(TAG, "onPictureTaken file not found: " + e.getMessage());
|
Log.d(TAG, "onPictureTaken file not found: " + e.getMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -55,20 +52,6 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getOutputMediaFile() {
|
|
||||||
final String appName = mContext.getResources().getString(R.string.app_name);
|
|
||||||
final File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
|
|
||||||
|
|
||||||
if (!mediaStorageDir.exists()) {
|
|
||||||
if (!mediaStorageDir.mkdirs()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
|
||||||
return new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bitmap setBitmapRotation(Bitmap bitmap, String path) throws IOException {
|
private Bitmap setBitmapRotation(Bitmap bitmap, String path) throws IOException {
|
||||||
final ExifInterface exif = new ExifInterface(path);
|
final ExifInterface exif = new ExifInterface(path);
|
||||||
final String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
|
final String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
|
||||||
|
@ -127,9 +110,4 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
|
||||||
}
|
}
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanPhoto(File photo) {
|
|
||||||
final String[] photoPath = {photo.getAbsolutePath()};
|
|
||||||
MediaScannerConnection.scanFile(mContext, photoPath, null, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
|
import android.media.CamcorderProfile;
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -34,6 +36,11 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
private static boolean isFlashEnabled;
|
private static boolean isFlashEnabled;
|
||||||
private static Camera.Parameters parameters;
|
private static Camera.Parameters parameters;
|
||||||
|
|
||||||
|
private static MediaRecorder recorder;
|
||||||
|
private static boolean isRecording;
|
||||||
|
private static boolean isVideoMode;
|
||||||
|
private static String curVideoPath;
|
||||||
|
|
||||||
public Preview(Context context) {
|
public Preview(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +75,6 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
|
|
||||||
releaseCamera();
|
releaseCamera();
|
||||||
camera = newCamera;
|
camera = newCamera;
|
||||||
|
|
||||||
if (camera != null) {
|
if (camera != null) {
|
||||||
parameters = camera.getParameters();
|
parameters = camera.getParameters();
|
||||||
supportedPreviewSizes = parameters.getSupportedPreviewSizes();
|
supportedPreviewSizes = parameters.getSupportedPreviewSizes();
|
||||||
|
@ -90,6 +96,9 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
setupPreview();
|
setupPreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isVideoMode)
|
||||||
|
initRecorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setCameraDisplayOrientation(int cameraId, android.hardware.Camera camera) {
|
public static void setCameraDisplayOrientation(int cameraId, android.hardware.Camera camera) {
|
||||||
|
@ -141,8 +150,9 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
new Handler().postDelayed(new Runnable() {
|
new Handler().postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (camera != null)
|
if (camera != null) {
|
||||||
camera.startPreview();
|
camera.startPreview();
|
||||||
|
}
|
||||||
|
|
||||||
canTakePicture = true;
|
canTakePicture = true;
|
||||||
}
|
}
|
||||||
|
@ -200,6 +210,8 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseCamera() {
|
public void releaseCamera() {
|
||||||
|
stopRecording();
|
||||||
|
|
||||||
if (camera != null) {
|
if (camera != null) {
|
||||||
camera.stopPreview();
|
camera.stopPreview();
|
||||||
camera.release();
|
camera.release();
|
||||||
|
@ -240,6 +252,19 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
if (camera != null) {
|
if (camera != null) {
|
||||||
camera.stopPreview();
|
camera.stopPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupRecorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanupRecorder() {
|
||||||
|
if (recorder != null) {
|
||||||
|
if (isRecording) {
|
||||||
|
recorder.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder.release();
|
||||||
|
recorder = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -307,4 +332,64 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
|
||||||
public void disableFlash() {
|
public void disableFlash() {
|
||||||
isFlashEnabled = false;
|
isFlashEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initPhotoMode() {
|
||||||
|
isRecording = false;
|
||||||
|
isVideoMode = false;
|
||||||
|
stopRecording();
|
||||||
|
cleanupRecorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIDEO RECORDING
|
||||||
|
public void initRecorder() {
|
||||||
|
isRecording = false;
|
||||||
|
isVideoMode = true;
|
||||||
|
recorder = new MediaRecorder();
|
||||||
|
recorder.setCamera(camera);
|
||||||
|
recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
|
||||||
|
recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
|
||||||
|
|
||||||
|
curVideoPath = Utils.getOutputMediaFile(getContext(), false);
|
||||||
|
if (curVideoPath.isEmpty()) {
|
||||||
|
Utils.showToast(getContext(), R.string.video_creating_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CamcorderProfile cpHigh = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||||
|
recorder.setProfile(cpHigh);
|
||||||
|
recorder.setOutputFile(curVideoPath);
|
||||||
|
recorder.setPreviewDisplay(surfaceHolder.getSurface());
|
||||||
|
recorder.setOrientationHint(90);
|
||||||
|
|
||||||
|
try {
|
||||||
|
recorder.prepare();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.e(TAG, "initRecorder " + e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "initRecorder " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean toggleRecording() {
|
||||||
|
if (isRecording) {
|
||||||
|
stopRecording();
|
||||||
|
initRecorder();
|
||||||
|
} else {
|
||||||
|
camera.lock();
|
||||||
|
camera.unlock();
|
||||||
|
recorder.start();
|
||||||
|
isRecording = true;
|
||||||
|
}
|
||||||
|
return isRecording;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopRecording() {
|
||||||
|
if (recorder != null && isRecording) {
|
||||||
|
recorder.stop();
|
||||||
|
recorder = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRecording = false;
|
||||||
|
Utils.scanFile(curVideoPath, getContext());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package com.simplemobiletools.camera;
|
package com.simplemobiletools.camera;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.os.Environment;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static Camera.CameraInfo getCameraInfo(int cameraId) {
|
public static Camera.CameraInfo getCameraInfo(int cameraId) {
|
||||||
|
@ -36,4 +43,38 @@ public class Utils {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getOutputMediaFile(Context context, boolean isPhoto) {
|
||||||
|
final File mediaStorageDir = getFolderName(context, isPhoto);
|
||||||
|
|
||||||
|
if (!mediaStorageDir.exists()) {
|
||||||
|
if (!mediaStorageDir.mkdirs()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||||
|
if (isPhoto) {
|
||||||
|
return mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg";
|
||||||
|
} else {
|
||||||
|
return mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getFolderName(Context context, boolean isPhoto) {
|
||||||
|
final Resources res = context.getResources();
|
||||||
|
final String appName = res.getString(R.string.app_name);
|
||||||
|
final String sharedPath = new File(Environment.getExternalStorageDirectory(), appName).getAbsolutePath();
|
||||||
|
String typeDirectory = res.getString(R.string.photo_directory);
|
||||||
|
if (!isPhoto) {
|
||||||
|
typeDirectory = res.getString(R.string.video_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File(sharedPath, typeDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void scanFile(String path, Context context) {
|
||||||
|
final String[] paths = {path};
|
||||||
|
MediaScannerConnection.scanFile(context, paths, null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 933 B |
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
|
@ -1,6 +1,9 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Simple Camera</string>
|
<string name="app_name">Simple Camera</string>
|
||||||
<string name="camera_open_error">An error occurred at obtaining the camera</string>
|
<string name="camera_open_error">An error occurred at obtaining the camera</string>
|
||||||
|
<string name="video_creating_error">An error occurred at creating the video file</string>
|
||||||
|
<string name="video_directory">Simple Videos</string>
|
||||||
|
<string name="photo_directory">Simple Photos</string>
|
||||||
|
|
||||||
<!-- About -->
|
<!-- About -->
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
|
|
Loading…
Reference in New Issue