本次业务需求是录不超过1分钟的视频,并且要求能暂停,继续录,最后录制完成时需要有一个完整的视频文件用于上传。
方法:暂停时先将前一小段的视频存下,最终结束录制时将几小段视频合并成一整段保存。
首先,我们的工具类MediaUtils,此处参照https://github.com/Werb/MediaUtils稍作修改,主要需要其输出的方法有
setTargetDir() 设置输出视频文件的目录
setTargetName() 设置视频文件名
getTargetFilePath() 获取最终视频文件的完整路径
stopRecordSave() 结束录制,录制过程中没有暂停,录制的文件保留
combine() 合并多段视频,合并成功后将原有视频删除,将视频路径列表清空,将合并后的视频路径加入,将合并后的视频作为结果视频
pauseRecordSave() 暂停录制,并将此段视频加入待合并的视频路径列表
stopRecordUnSave() 停止录制,删除录制过的所有视频
import android.view.SurfaceHolder;
import android.app.Activity;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static cn.reschool.parent.utils.DateUtil.getCurrentDetailTime;
/**
* Created by kLin 11509 on 8/15/2017.
* email 1150954859@qq.com
*/
public class MediaUtils implements SurfaceHolder.Callback {
private static final String TAG = "MediaUtils";
public static final int MEDIA_AUDIO = 0;
public static final int MEDIA_VIDEO = 1;
private Activity activity;
private MediaRecorder mMediaRecorder;
private CamcorderProfile profile;
private Camera mCamera;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private File targetDir;
private String targetName;
List<String> paths = new ArrayList<>();
private File targetFile;
private int previewWidth, previewHeight;
private int recorderType;
private boolean isRecording;
private GestureDetector mDetector;
private boolean isZoomIn = false;
private int or = 90;
private int cameraPosition = 1;//0代表前置摄像头,1代表后置摄像头
public MediaUtils(Activity activity) {
this.activity = activity;
}
public void setRecorderType(int type) {
this.recorderType = type;
}
public void setTargetDir(File file) {
if (!file.exists()) {
file.mkdir();
}
this.targetDir = file;
}
public void setTargetName(String name) {
this.targetName = name;
}
public String getTargetFilePath() {
return targetFile.getPath();
}
public boolean deleteTargetFile() {
if (targetFile.exists()) {
return targetFile.delete();
} else {
return false;
}
}
public void setSurfaceView(SurfaceView view) {
this.mSurfaceView = view;
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setFixedSize(previewWidth, previewHeight);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);
mDetector = new GestureDetector(activity, new ZoomGestureListener());
mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mDetector.onTouchEvent(event);
return true;
}
});
}
// public void setTextureView(AutoFitTextureView view) {
// this.textureView = view;
// initCamera();
// mDetector = new GestureDetector(activity, new ZoomGestureListener());
// this.textureView.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View v, MotionEvent event) {
// mDetector.onTouchEvent(event);
// return true;
// }
// });
// }
public int getPreviewWidth() {
return previewWidth;
}
public int getPreviewHeight() {
return previewHeight;
}
public boolean isRecording() {
return isRecording;
}
public void record() {
if (isRecording) {
try {
mMediaRecorder.stop(); // stop the recording
} catch (RuntimeException e) {
// RuntimeException is thrown when stop() is called immediately after start().
// In this case the output file is not properly constructed ans should be deleted.
Log.d(TAG, "RuntimeException: stop() is called immediately after start()");
//noinspection ResultOfMethodCallIgnored
targetFile.delete();
}
releaseMediaRecorder(); // release the MediaRecorder object
mCamera.lock(); // take camera access back from MediaRecorder
isRecording = false;
} else {
startRecordThread();
}
}
private boolean prepareRecord() {
try {
mMediaRecorder = new MediaRecorder();
if (recorderType == MEDIA_VIDEO) {
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(profile);
// 实际视屏录制后的方向
if(cameraPosition == 0){
mMediaRecorder.setOrientationHint(270);
}else {
mMediaRecorder.setOrientationHint(or);
}
} else if (recorderType == MEDIA_AUDIO) {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
}
targetFile = new File(targetDir, targetName);
mMediaRecorder.setOutputFile(targetFile.getPath());
} catch (Exception e) {
e.printStackTrace();
Log.d("MediaRecorder", "Exception prepareRecord: ");
releaseMediaRecorder();
return false;
}
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
Log.d("MediaRecorder", "IllegalStateException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d("MediaRecorder", "IOException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
return true;
}
public void stopRecordSave() {
Log.d("Recorder", "stopRecordSave");
if (isRecording) {
isRecording = false;
try {
mMediaRecorder.stop();
Log.d("Recorder", targetFile.getPath());
} catch (RuntimeException r) {
Log.d("Recorder", "RuntimeException: stop() is called immediately after start()");
} finally {
releaseMediaRecorder();
}
}
}
public boolean combine(){
if (paths == null || 1>= paths.size()) {
return false;
}else {
String combineMp3Path = targetDir +"/"+ getCurrentDetailTime() + "-combine.mp4";
String[] strings = new String[paths.size()];
for (int i = 0; i < paths.size(); i++) {
strings[i] = paths.get(i);
}
VideoSplicing videoSplicing = new VideoSplicing(activity,strings,combineMp3Path);
videoSplicing.videoSpl