1.解决录制后文件较大的问题
2.解决清晰度问题
3.同一控件上实现录制和播放
4.解决VideoView播放时不能全屏问题
5.解决了预览图拉抻的问题
6.自定义播/录组件 连续录制/播放
所有解决方案在代码注释中:
package in.langhua.spray.view.customview;import android.content.Context;import android.hardware.Camera;import android.media.CamcorderProfile;import android.media.MediaPlayer;import android.media.MediaRecorder;import android.os.Environment;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.MediaController;import android.widget.VideoView;import java.io.File;import java.io.IOException;import java.util.List;import in.langhua.spray.common.AppConstant;import in.langhua.spray.common.tools.LogUtils;/**
* 视频播放控件
* <p>
* Created by wangxingsheng
*/public class MyMovieRecorderView extends LinearLayout implements SurfaceHolder.Callback { private FullScreenVideoView mVideoView; private String mVideoFileAbPath; private static String suffix = ".mp4"; private Camera mCamera; private String TAG = "MyMovieRecorderView"; private MediaRecorder record; private int cameraPosition = 0;//默认前摄
private SurfaceHolder mHolder; public MyMovieRecorderView(Context context) { this(context, null);
} public MyMovieRecorderView(Context context, AttributeSet attrs) { this(context, attrs, 0);
} public MyMovieRecorderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //String VIDEO_PATH = "/video";
mVideoFileAbPath = Environment.getExternalStorageDirectory() + AppConstant.RunningConfig.VIDEO_PATH; //保证路径存在
File dir = new File(mVideoFileAbPath); if (!dir.exists()) {
dir.mkdirs();
}
mVideoView = new FullScreenVideoView(getContext());
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addView(mVideoView, params);
mHolder = mVideoView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} public String getAllPath(String name) { return mVideoFileAbPath + name + suffix;
} public String getPath(){ return mVideoFileAbPath;
} /**
* 停止播放
*/
public void stopPlayVideo() { try {
stopPlayingRecording();
} catch (Exception e) {
LogUtils.SystemOut(TAG, e.toString());
e.printStackTrace();
}
} /**
* 播放指定视频
*
* @param tempVideoName
*/
public void startPlayVideo(String tempVideoName) { try {
playRecording(getAllPath(tempVideoName));
} catch (Exception e) {
LogUtils.SystemOut(TAG, e.toString());
e.printStackTrace();
}
} /**
* 停止录制
*/
public void stopRecorder() { try {
stopRecording();
} catch (Exception e) {
LogUtils.SystemOut(TAG, e.toString());
e.printStackTrace();
}
} /**
* 开始录制,指定视频名称 例: tempVideo
*
* @param tempVideoName
*/
public void startRecorder(String tempVideoName) { try {
beginRecording(mHolder, getAllPath(tempVideoName), -1);
} catch (IOException e) {
e.printStackTrace();
stopRecording();
}
} /**
* 开始录制,指定视频录制时间
*
* @param tempVideoName
* @param duration
*/
public void startRecorder(String tempVideoName, int duration) { try {
beginRecording(mHolder, getAllPath(tempVideoName), duration);
} catch (IOException e) {
e.printStackTrace();
stopRecording();
}
} private void beginRecording(SurfaceHolder holder, String path, int duration) throws IOException { // TODO Auto-generated method stub
LogUtils.SystemOut(TAG, "camera beginRecording record = " + record); if (record != null) {
record.stop();
record.release();
}
File outFile = new File(path); if (outFile.exists()) {
outFile.delete();
} if(mCamera == null){
initCamera(holder);
}
record = new MediaRecorder();
record.setCamera(mCamera);
record.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
record.setAudioSource(MediaRecorder.AudioSource.DEFAULT); if(cameraPosition == 1){
record.setOrientationHint(90);//后摄 时保证输出正向
record.setProfile(getBestCamcorderProfile(Camera.CameraInfo.CAMERA_FACING_BACK));
}else{
record.setOrientationHint(270);//前摄 时保证输出正向
record.setProfile(getBestCamcorderProfile(Camera.CameraInfo.CAMERA_FACING_FRONT));
}
record.setPreviewDisplay(holder.getSurface());
record.setOutputFile(path);
record.prepare(); if (duration != -1) {
record.setMaxDuration(duration);
}
record.start();
} /**
*
* 解决录像时清晰度问题
*
* 视频清晰度顺序 High 1080 720 480 cif qvga gcif 详情请查看 CamcorderProfile.java
* 在12秒mp4格式视频大小维持在1M左右时,以下四个选择效果最佳
*
* 不同的CamcorderProfile.QUALITY_ 代表每帧画面的清晰度,
* 变换 profile.videoBitRate 可减少每秒钟帧数
*
* @param cameraID 前摄 Camera.CameraInfo.CAMERA_FACING_FRONT /后摄 Camera.CameraInfo.CAMERA_FACING_BACK
* @return
*/
private CamcorderProfile getBestCamcorderProfile(int cameraID){
CamcorderProfile profile = CamcorderProfile.get(cameraID,CamcorderProfile.QUALITY_LOW); if(CamcorderProfile.hasProfile(cameraID,CamcorderProfile.QUALITY_480P)){ //对比下面720 这个选择 每帧不是很清晰
LogUtils.SystemOut("camera getBestCamcorderProfile 480P");
profile = CamcorderProfile.get(cameraID, CamcorderProfile.QUALITY_480P);
profile.videoBitRate = profile.videoBitRate/5; return profile;
} if(CamcorderProfile.hasProfile(cameraID,CamcorderProfile.QUALITY_720P)){ //对比上面480 这个选择 动作大时马赛克!!
LogUtils.SystemOut("camera getBestCamcorderProfile 720P");
profile = CamcorderProfile.get(cameraID,CamcorderProfile.QUALITY_720P);
profile.videoBitRate = profile.videoBitRate/35; return profile;
} if(CamcorderProfile.hasProfile(cameraID,CamcorderProfile.QUALITY_CIF)){
LogUtils.SystemOut("camera getBestCamcorderProfile CIF");
profile = CamcorderProfile.get(cameraID, CamcorderProfile.QUALITY_CIF); return profile;
} if(CamcorderProfile.hasProfile(cameraID,CamcorderProfile.QUALITY_QVGA)){
LogUtils.SystemOut("camera getBestCamcorderProfile QVGA");
profile = CamcorderProfile.get(cameraID, CamcorderProfile.QUALITY_QVGA); return profile;
}
LogUtils.SystemOut("camera getBestCamcorderProfile QUALITY_LOW"); return profile;
} private void stopRecording() { // TODO Auto-generated method stub
LogUtils.SystemOut(TAG, "camera stopRecording record = " + record); if (record != null) { try{
record.stop();
record.release();
record = null;
}catch (Exception e){
e.printStackTrace(); //如果stop时报错 release,保证资源释放
record.reset();
record.stop();
record.release();
record = null;
}
}
freeCameraResource();
} private void playRecording(String path) { // TODO Auto-generated method stub
LogUtils.SystemOut(TAG, " playRecording path = " + path); //添加控制器,前进后退 暂停
freeCameraResource();
MediaController mc = new MediaController(getContext());
mVideoView.setMediaController(mc);
mVideoView.setVideoPath(path);
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override
public void onPrepared(MediaPlayer mp) {
mVideoView.start();
}
});
} private void stopPlayingRecording() throws Exception {
LogUtils.SystemOut(TAG, " stopPlayingRecording "); // TODO Auto-generated method stub
//停止播放,手动调用这个方法,才能保证该组件连续录像 一次接一次录像
mVideoView.stopPlayback();
} private void initCamera(SurfaceHolder holder) {
LogUtils.SystemOut(TAG, "camera initCamera "); if (mCamera != null) {
freeCameraResource();
} try {
mCamera = getCamera(); if (mCamera == null) return; if (mCamera != null) {
Camera.Parameters params = mCamera.getParameters();
params.set("orientation", "portrait"); //从系统相机所支持的size列表中找到与屏幕长宽比最相近的size
Camera.Size size = getCloselyPreSize(mVideoView.getWidth(),mVideoView.getHeight(),params.getSupportedPreviewSizes());
params.setPreviewSize(size.width,size.height);
mCamera.setParameters(params);
} //调正相机预览
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
mCamera.unlock();
} catch (Exception e) {
e.printStackTrace();
freeCameraResource();
}
} private void freeCameraResource() { if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
} /**
* 切换前后摄像头
*/
public void switchCamera() {
cameraPosition = cameraPosition == 1 ? 0 : 1;
initCamera(mHolder);
} /**
* 激活相机预览
*/
public void refCamera(){
initCamera(mHolder);
} /**
* open 前/后摄像头
*
* @return
*/
private Camera getCamera() {
LogUtils.SystemOut("camera getCamera"); int numberOfCameras = Camera.getNumberOfCameras();
Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo); if (cameraPosition == 1) { if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { return Camera.open(i);
}
} else { if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { return Camera.open(i);
}
}
} return null;
} @Override
public void surfaceCreated(SurfaceHolder holder) {
LogUtils.SystemOut("camera sufraceCreate");
mHolder = holder;
initCamera(mHolder);
} @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
LogUtils.SystemOut("camera sufraceChanged");
mHolder = holder;
} @Override
public void surfaceDestroyed(SurfaceHolder holder) {
LogUtils.SystemOut("camera surfaceDestroyed");
mHolder = holder;
stopRecording();
} /**
* 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择)
*
* @param surfaceWidth
* 需要被进行对比的原宽
* @param surfaceHeight
* 需要被进行对比的原高
* @param preSizeList
* 需要对比的预览尺寸列表
* @return 得到与原宽高比例最接近的尺寸
*/
private Camera.Size getCloselyPreSize(int surfaceWidth, int surfaceHeight,
List<Camera.Size> preSizeList) { //因为预览相机图像需要旋转90度,所以在找相机预览size时切换长宽
int ReqTmpWidth = surfaceHeight; int ReqTmpHeight = surfaceWidth; // 得到与传入的宽高比最接近的size
float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight; float curRatio, deltaRatio; float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null; for (Camera.Size size : preSizeList) { if ((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)) { return size;
}
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio); if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
} return retSize;
} /**
* 保证播放时全屏
*/
class FullScreenVideoView extends VideoView{ public FullScreenVideoView(Context context) { super(context);
} public FullScreenVideoView (Context context, AttributeSet attrs)
{ super(context,attrs);
} public FullScreenVideoView(Context context, AttributeSet attrs,int defStyle)
{ super(context,attrs,defStyle);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{ int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width , height);
}
}
}