implement video recording

This commit is contained in:
tibbi 2016-06-12 19:58:39 +02:00
parent fbc3d9b162
commit 4a542be54d
11 changed files with 165 additions and 33 deletions

View File

@ -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"/>

View File

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

View File

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

View File

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

View File

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

View File

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