视频录制:
首先视频的录制和音频的录制都是耗时的,需要在单个线程中去操作,在开启录制时,视频这块的录制,配置参数的前后,有严格的要求,颠倒会报错,报关于Camera,和MediaRecorder的错误,直接上代码,代码里写的很清楚了,对了这块用到了SurfaceView这个控件,不清楚的可以去查查资料。
SurfaceView的初始化工作:
private void initSurfaceView() {
mSurfaceHolder = mSurfaceView.getHolder();
//设置屏幕分辨率
mSurfaceHolder.setFixedSize(videoWidth, videoHeight);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);
/**
* 单独处理mSurfaceView的双击事件
*/
mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mDetector.onTouchEvent(event);
return true;
}
});
}
实现SurfaceHolder.Callback这个回调,3个方法:
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceHolder = holder;
startPreView(holder);
}
/**
* 开启预览
*
* @param holder
*/
private void startPreView(SurfaceHolder holder) {
if (mCamera == null) {
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
}
if (mMediaRecorder == null) {
mMediaRecorder = new MediaRecorder();
}
if (mCamera != null) {
mCamera.setDisplayOrientation(90);
try {
mCamera.setPreviewDisplay(holder);
Camera.Parameters parameters = mCamera.getParameters();
//实现Camera自动对焦
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes != null) {
for (String mode : focusModes) {
mode.contains("continuous-video");
parameters.setFocusMode("continuous-video");
}
}
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
//停止预览并释放摄像头资源
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if (mMediaRecorder != null) {
mMediaRecorder.release();
mMediaRecorder = null;
}
}
onTouch实现按下录制,松开保存功能,上移取消录制:
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean ret = false;
int action = event.getAction();
float ey = event.getY();
float ex = event.getX();
//只监听中间的按钮处
int vW = v.getWidth();
int left = LISTENER_START;
int right = vW - LISTENER_START;
float downY = 0;
switch (v.getId()) {
case R.id.main_press_control: {
switch (action) {
case MotionEvent.ACTION_DOWN:
if (ex > left && ex < right) {
mProgressBar.setCancel(false);
//显示上滑取消
mTvTip.setVisibility(View.VISIBLE);
mTvTip.setText("↑ 上滑取消");
//记录按下的Y坐标
downY = ey;
// TODO: 2016/10/20 开始录制视频, 进度条开始走
mProgressBar.setVisibility(View.VISIBLE);
//开始录制
Toast.makeText(this, "开始录制", Toast.LENGTH_SHORT).show();
mRecorderService.submit(new Runnable() {
@Override
public void run() {
startRecord();
}
});
//更新进度条
updateProgress();
ret = true;
}
break;
case MotionEvent.ACTION_UP:
if (ex > left && ex < right) {
mTvTip.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
stopRecord();
ret = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (ex > left && ex < right) {
float currentY = event.getY();
if (downY - currentY > 10) {
isCancel = true;
mProgressBar.setCancel(true);
}
}
break;
}
break;
}
}
return ret;
}
开启录制和Recorder的配置:
(配置顺序很重要,顺序不对,有的手机可以,有的手机会报错)
/**
* 开始录制
*/
private void startRecord() {
if (mMediaRecorder != null) {
//没有外置存储, 直接停止录制
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
try {
if (mMediaRecorder == null) {
mMediaRecorder = new MediaRecorder();
/// mMediaRecorder.setOnErrorListener(this);
} else {
mMediaRecorder.reset();
}
// Step 1: Unlock and set camera to MediaRecorder
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
// Step 2: Set sources
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//before setOutputFormat()
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//before setOutputFormat()
//设置视频输出的格式和编码
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_CIF);
//after setVideoSource(),after setOutFormat()
//mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
mMediaRecorder.setVideoSize(videoWidth, videoHeight);
mMediaRecorder.setAudioEncodingBitRate(44100);
if (mProfile.videoBitRate > 2 * 1024 * 1024)
mMediaRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);
else
mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
//after setVideoSource(),after setOutFormat();
mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
//after setOutputFormat()
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//after setOutputFormat()
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
// Step 3: Set output file 新建文件保存录制视频
recordFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/RecordDemo/" + System.currentTimeMillis() + ".mp4");
if (!recordFile.getParentFile().exists()) recordFile.getParentFile().mkdirs();
recordFile.createNewFile();
//设置输出路径
mMediaRecorder.setOutputFile(recordFile.getAbsolutePath());
//解决录制视频, 播放器横向问题
mMediaRecorder.setOrientationHint(90);
// Step 4: start and return
mMediaRecorder.prepare();
mMediaRecorder.start();
// mMediaObject.setStartTime(System.currentTimeMillis());
isRecording = true;
} catch (Exception e) {
e.printStackTrace();
}
}
}
实现进度条的更新操作:
/**
* 跟新进度条,启用一个线程
*/
private void updateProgress() {
mUpdateProgressService.submit(new Runnable() {
@Override
public void run() {
try {
mProgress = 0;
isRunning = true;
while (isRunning) {
mProgress++;
Thread.sleep(20);
//主线程更新进度条
mHandler.post(new Runnable() {
@Override
public void run() {
mProgressBar.setProgress(mProgress);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
松开结束录制:
private void stopRecord() {
//判断是否为录制结束, 或者为成功录制(时间过短)
if (!isCancel) {
if (mProgress < 50) {
//时间太短不保存
stopRecordUnSave();
Toast.makeText(this, "时间太短", Toast.LENGTH_SHORT).show();
} else {
//停止录制
stopRecordSave();
}
} else {
//现在是取消状态,不保存
stopRecordUnSave();
isCancel = false;
Toast.makeText(this, "取消录制", Toast.LENGTH_SHORT).show();
mProgressBar.setCancel(false);
}
}
保存视频和不保存视频:
/**
* 停止录制 并且保存
*/
private void stopRecordSave() {
if (isRecording) {
isRunning = false;
mMediaRecorder.stop();
isRecording = false;
Toast.makeText(MainActivity.this, "视频已经放至" + recordFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
}
}
/**
* 停止录制, 不保存
*/
private void stopRecordUnSave() {
if (isRecording) {
isRunning = false;
mMediaRecorder.stop();
isRecording = false;
if (recordFile.exists()) {
//不保存直接删掉
recordFile.delete();
}
}
}
释放资源:
private void releaseRecorder() {
if (mMediaRecorder != null) {
mMediaRecorder.release();
mMediaRecorder.reset();
;
mMediaRecorder = null;
}
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
手势的监听,双击实现焦距变化,放大:
class ZoomGestureListener extends GestureDetector.SimpleOnGestureListener {
//双击手势事件
@Override
public boolean onDoubleTap(MotionEvent e) {
super.onDoubleTap(e);
if (mMediaRecorder != null) {
if (!isZoomIn) {
setZoom(20);
isZoomIn = true;
} else {
setZoom(0);
isZoomIn = false;
}
}
return true;
}
}
题外:还有一点点代码,和布局这里就不放上了,代码会上传,可以下载看看,试了几个手机,都没有出现问题,这里没有对录制的视频进行压缩,后面会试试看,就先写到这里了,有问题下面提出来就可以了 。
问题:下面这个参考代码有点问题,在小米手机上开启视频录制那里会报错,走catch这块报异常,录制的会发现是0kb,可以把我的这块startRecord这里代码复制过去,覆盖掉那一部分就可以解决了,我这块代码更简洁点,逻辑更清楚点,也可以下我的代码,效果会好一点……
参考:http://www.jianshu.com/p/6f84739ab85f