1.自定义录制视频的控件(MovieRecordView)
public class MovieRecorderView extends LinearLayout implements OnErrorListener {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mProgressBar;
private MediaRecorder mMediaRecorder;
private Camera mCamera;
private Timer mTimer;// 计时器
private OnRecordFinishListener mOnRecordFinishListener;// 录制完成回调接口
private int mWidth;// 视频分辨率宽度
private int mHeight;// 视频分辨率高度
private boolean isOpenCamera;// 是否一开始就打开摄像头
private int mRecordMaxTime;// 一次拍摄最长时间
private int mTimeCount;// 时间计数
private File mRecordFile = null;// 文件
public MovieRecorderView(Context context) {
this(context, null);
}
public MovieRecorderView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@SuppressLint("NewApi")
public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 初始化各项组件
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0);
mWidth = a.getInteger(R.styleable.MovieRecorderView_video_width, 320);// 默认320
mHeight = a.getInteger(R.styleable.MovieRecorderView_video_height, 240);// 默认240
isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true);// 默认打开
mRecordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 10);// 默认为10
LayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mProgressBar.setMax(mRecordMaxTime);// 设置进度条最大量
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new CustomCallBack());
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
a.recycle();
}
private class CustomCallBack implements Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!isOpenCamera)
return;
try {
initCamera();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (!isOpenCamera)
return;
freeCameraResource();
}
}
/**
* 初始化摄像头
*/
@SuppressLint("NewApi")
public void initCamera() throws IOException {
if (mCamera != null) {
freeCameraResource();
}
try {
mCamera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
freeCameraResource();
}
if (mCamera == null)
return;
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
mCamera.unlock();
}
/**
* 释放摄像头资源
*/
private void freeCameraResource() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
private void createRecordDir() {
File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "im/video/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File vecordDir = sampleDir;
// 创建文件
try {
mRecordFile = File.createTempFile("recording", ".mp4", vecordDir); //mp4格式
Log.i("TAG",mRecordFile.getAbsolutePath());
} catch (IOException e) {
}
}
/**
* 初始化
*/
private void initRecord() throws IOException {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
if (mCamera != null){
mMediaRecorder.setCamera(mCamera);
}
mMediaRecorder.setOnErrorListener(this);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.setVideoSource(VideoSource.CAMERA);// 视频源
mMediaRecorder.setAudioSource(AudioSource.MIC);// 音频源
mMediaRecorder.setOutputFormat(OutputFormat.MPEG_4);// 视频输出格式
mMediaRecorder.setAudioEncoder(AudioEncoder.AAC);// 音频格式
mMediaRecorder.setVideoSize(mWidth, mHeight);// 设置分辨率:
// mMediaRecorder.setVideoFrameRate(16);// 这个我把它去掉了,感觉没什么用
mMediaRecorder.setVideoEncodingBitRate(1 * 1280 * 720);// 设置帧频率,然后就清晰了
mMediaRecorder.setOrientationHint(90);// 输出旋转90度,保持竖屏录制
if(android.os.Build.MODEL.contains("ALE-UL00")||android.os.Build.MODEL.contains("OPPO")|| Build.MODEL.contains("MT7-TL10")){
//华为p7型号手机,OPPO手机
mMediaRecorder.setVideoEncoder(VideoEncoder.DEFAULT);// 视频录制格式
}else{
mMediaRecorder.setVideoEncoder(VideoEncoder.MPEG_4_SP);// 视频录制格式
}
mMediaRecorder.setOutputFile(mRecordFile.getAbsolutePath());
mMediaRecorder.prepare();
try {
mMediaRecorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 开始录制视频 达到指定时间之后回调接口
*/
public void record(final OnRecordFinishListener onRecordFinishListener) {
this.mOnRecordFinishListener = onRecordFinishListener;
createRecordDir();
try {
if (!isOpenCamera)// 如果未打开摄像头,则打开
initCamera();
initRecord();
mTimeCount = 0;// 时间计数器重新赋值
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
mTimeCount++;
mProgressBar.setProgress(mTimeCount);// 设置进度条
if (mTimeCount == mRecordMaxTime) {// 达到指定时间,停止拍摄
stop();
if (mOnRecordFinishListener != null)
mOnRecordFinishListener.onRecordFinish();
}
}
}, 0, 1000);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止拍摄
*/
public void stop() {
stopRecord();
releaseRecord();
freeCameraResource();
}
/**
* 停止录制
*/
public void stopRecord() {
mProgressBar.setProgress(0);
if (mTimer != null)
mTimer.cancel();
if (mMediaRecorder != null) {
// 设置后不会崩
mMediaRecorder.setOnErrorListener(null);
try {
mMediaRecorder.stop();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
mMediaRecorder.setPreviewDisplay(null);
}
}
/**
* 释放资源
*/
private void releaseRecord() {
if (mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
try {
mMediaRecorder.release();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
mMediaRecorder = null;
}
public int getTimeCount() {
return mTimeCount;
}
/**
* @return the mVecordFile
*/
public File getmRecordFile() {
return mRecordFile;
}
/**
* 录制完成回调接口
*/
public interface OnRecordFinishListener {
public void onRecordFinish();
}
@Override
public void onError(MediaRecorder mr, int what, int extra) {
try {
if (mr != null)
mr.reset();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
关于录制时显示的进度条,如果想要实现微信的那种效果(从两边缩进),有两个方法
第一种,用还是用progressBar,设置对应的style即可。详情可以参见文章 仿微信小视屏拍摄的进度条/自定义的progressBar的样式。
第二种,可以自定一个View,根据具体的条件判断,重新绘制。详情参见文章 Android仿微信录制小视频的进度条
2.录制界面
在录制界面的布局中使用这个自定义view,写一个按钮点击开始录制调用方法
mRecorderView.record(new MovieRecorderView.OnRecordFinishListener() {
@Override
public void onRecordFinish() { //录制完成(结束)回调
handler.sendEmptyMessage(1);
}
});
写一个停止录制的按钮,这边的话是有删除文件的,如果不需要删除自行做判断即可
if (mRecorderView.getmRecordFile() != null)
mRecorderView.getmRecordFile().delete(); handler.sendEmptyMessage(1);
mRecorderView.stop();//停止录制
}
3.回调到展示界面
这边的就是根据录制界面setResult()回调本地视频的地址。拿到地址以后就可以,取视频的第一帧图片,以及点击播放视频。
如果想要上传视频,则可以将视频文件转化为二进制上传到服务器,服务器返回网络视频地址。
4.补充
如果想要实现预览界面的话(跟微信小视频拍摄一样),可以在录制完成的时候,handler的回调方法中跳转预览界面(预览界面跟播放界面差不多,关于视频播放界面的代码,百度一下)
遇到的问题:
(1)根据视频的网络地址播放的时候,会出现一种其实界面是跳转了,但是设置videoView的背景颜色不起作用,依然是透明的。这个时候可以调用videoview的
setZOrderOnTop()方法,将videoview移动到上层。还有一点需要注意的是,在加载网络视频的时候,如果视频还未加载出来,就点击返回的话,要在播放活动界面的
onPause(),onResume(),onDestory()生命周期调用videoView对应的pause(),resume(),stopPlayback()方法。
(2)设置视频全屏播放
可以自定义一个view继承VideoView,重写onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
}
(3)设置隐藏videoView的控制播放按钮,以及视频重复播放
MediaController controller = new MediaController(this);
controller.setVisibility(View.GONE);
mVideoView.setMediaController(controller);
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//设置重复播放
mVideoView.setVideoURI(Uri.parse(videoPath));
mVideoView.start();
}
});
(4)如果有设置预览界面的话,记得在预览界面返回(预览界面我们自己写的两个按钮,一个确认,一个返回)
在录制界面返回的回调里面要重新初始化camera,即调用movieRecordView的initCamera()方法。至于为什么的话,是因为在录制界面到预览界面的时候调用了stop()方法,导致movieRecordView中camera设置的值被清空了。所以如果不调用初始化,会导致从预览界面返回的时候,录制界面是暂停在上次录制的最后一刻画面,再次点击录制的话,屏幕会水平录制,不过输出的视频还是竖直的。如果你们有更好的方法,自己可以根据需要修改movieRecordView的代码。