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