面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>
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;
/**
* 视频播放控件
*
* 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 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);
}
}
}
可直接在xml中引用此控件.
本博客原地址:http://my.oschina.net/reone/blog/710346