视频录制

视频录制控件
布局文件就是一个surfaceView

/**
 * 视频录制控件
 */
public class MovieRecorderView extends LinearLayout implements OnErrorListener {

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;

    
     //屏幕方向
     //手机顶部朝上
    public static final int UP = 0;
    //手机底部朝上
    public static final int DOWN = 1;
    //手机左边朝上
    public static final int LEFT = 2;
    //手机右边朝上
    public static final int RIGHT = 3;
    
    private int orientation = UP;

    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;// 文件
    private final int FRONT_CAMERA = 0; //前置摄像头
    private final int BACK_CAMERA = 1;  //后置摄像头
    private int cameraType = BACK_CAMERA;


    public MovieRecorderView(Context context) {
        this(context, null);
    }

    public MovieRecorderView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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


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

    }

    /**
     * 初始化摄像头
     *
     * @date 2015-2-5
     * @throws IOException
     */
    @SuppressWarnings("deprecation")
    public void initCamera() throws IOException {
        if (mCamera != null) {
            freeCameraResource();
        }
        
        int cameraCount = 0;
        CameraInfo cameraInfo = new CameraInfo();
        cameraCount = Camera.getNumberOfCameras();// 得到摄像头的个数
        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraType == FRONT_CAMERA) {
                //切换成前置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    //释放资源
                    freeCameraResource();
                    try {
                        mCamera = Camera.open(camIdx);
                         if (mCamera == null)
                                return;
                        setCameraParams();
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        freeCameraResource();
                    }
                }
            }else{
                //切换成后置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    //释放资源
                    freeCameraResource();
                    try {
                        mCamera = Camera.open(camIdx);
                         if (mCamera == null)
                                return;
                        setCameraParams();
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        freeCameraResource();
                    }
                }
            }
        }
//        try {
//            mCamera = Camera.open();
//        } catch (Exception e) {
//            e.printStackTrace();
//            freeCameraResource();
//        }
//        if (mCamera == null)
//            return;
//
//        setCameraParams();
        
    }
    
    /**
     * 设置摄像头属性
     */
    @SuppressWarnings("deprecation")
    public void setCameraParams() {
        try {
            if (mCamera != null) {
                Parameters params = mCamera.getParameters();
//              params.set("orientation", "portrait");
                setPreviewSize(params);
                mCamera.setParameters(params);
            }
            //设置预览竖屏
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mSurfaceHolder);
            mCamera.startPreview();
            mCamera.unlock();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 切换前后摄像头
     */
    @SuppressWarnings("deprecation")
    public void toggleCamera(){
        
        int cameraCount = 0;
        CameraInfo cameraInfo = new CameraInfo();
        cameraCount = Camera.getNumberOfCameras();// 得到摄像头的个数

        
        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraType == BACK_CAMERA) {
                //切换成前置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    //释放资源
                    freeCameraResource();
                    try {
                        mCamera = Camera.open(camIdx);
                        cameraType = FRONT_CAMERA;
                         if (mCamera == null)
                                return;
                        setCameraParams();
                        return;
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        freeCameraResource();
                    }
                }
            }else{
                //切换成后置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    //释放资源
                    freeCameraResource();
                    try {
                        mCamera = Camera.open(camIdx);
                        cameraType = BACK_CAMERA;
                         if (mCamera == null)
                                return;
                        setCameraParams();
                        return;
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        freeCameraResource();
                    }
                }
            }
        }
    }


    /**
     * 释放摄像头资源
     *
     */
    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) {
        }
    }

    /**
     * 初始化
     *
     * @throws IOException
     */
    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.AMR_NB);// 音频格式
//        mMediaRecorder.setVideoFrameRate(16);// 去掉这一行,有些手机会mMediaRecorder.start()失败
        mMediaRecorder.setVideoEncodingBitRate(1 * 1280 * 720);// 设置帧频率,然后就清晰了
        
        //输出角度
        switch (orientation) {
        case UP:
            mMediaRecorder.setOrientationHint(90);// 输出旋转90度,顶部朝上录制
            break;
        case DOWN:
            mMediaRecorder.setOrientationHint(270);
            break;
        case LEFT:
            mMediaRecorder.setOrientationHint(0);
            break;
        case RIGHT:
            mMediaRecorder.setOrientationHint(180);
            break;
        }
        
        mMediaRecorder.setVideoSize(mWidth, mHeight);// 设置分辨率:
        mMediaRecorder.setVideoEncoder(VideoEncoder.MPEG_4_SP);// 视频录制格式
        
        // mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
        
        mMediaRecorder.setOutputFile(mRecordFile.getAbsolutePath());
        mMediaRecorder.prepare();
        try {
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始录制视频
     *
     * @param onRecordFinishListener
     *            达到指定时间之后回调接口
     */
    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() {
                    mTimeCount++;
//                    mProgressBar.setProgress(mTimeCount);
                    // 设置进度
                    if (mOnRecordFinishListener != null)
                        mOnRecordFinishListener.onRecord(mTimeCount);
                    if (mTimeCount == mRecordMaxTime) {// 达到指定时间,停止拍摄
                        stop();
                        if (mOnRecordFinishListener != null)
                            mOnRecordFinishListener.onRecordFinish();
                    }
                }
            }, 0, 1000);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 停止拍摄
     *
     * @author liuyinjun
     * @date 2015-2-5
     */
    public void stop() {
        stopRecord();
        releaseRecord();
        freeCameraResource();
    }

    /**
     * 停止录制
     *
     * @author liuyinjun
     * @date 2015-2-5
     */
    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);
        }
    }

    /**
     * 释放资源
     *
     * @author liuyinjun
     * @date 2015-2-5
     */
    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;
    }

    /**
     * 录制完成回调接口
     *
     * @author liuyinjun
     *
     * @date 2015-2-5
     */
    public interface OnRecordFinishListener {
        public void onRecordFinish();
        void onRecord(int progress);
    }
    
    /**
     * 设置最大录制时间
     * @param max
     */
    public void setMaxTime(int max){
        mRecordMaxTime = max; 
    }
    

    @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();
        }
    }
    
    
    /**
     * 根据手机支持的视频分辨率,设置预览尺寸
     *
     * @param params
     */
    @SuppressWarnings("deprecation")
    private void setPreviewSize(Parameters params) {
        if (mCamera == null) {
            return;
        }
        //获取手机支持的分辨率集合,并以宽度为基准降序排序
        List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
        Collections.sort(previewSizes, new Comparator<Camera.Size>() {
            @Override
            public int compare(Camera.Size lhs, Camera.Size rhs) {
                if (lhs.width > rhs.width) {
                    return -1;
                } else if (lhs.width == rhs.width) {
                    return 0;
                } else {
                    return 1;
                }
            }
        });

        float tmp = 0f;
        float minDiff = 100f;
        float ratio = 3.0f / 4.0f;// 高宽比率3:4,且最接近屏幕宽度的分辨率,可以自己选择合适的想要的分辨率
        Camera.Size best = null;
        for (Camera.Size s : previewSizes) {
            tmp = Math.abs(((float) s.height / (float) s.width) - ratio);
//            LogUtil.e(LOG_TAG,"tmp:" + tmp);
//            if (tmp == 0 && getWindowWidth(getContext()) <= s.width) {
//              break;
//          }
            if (tmp < minDiff) {
                minDiff = tmp;
                best = s;
                Log.e("屏幕", "setPreviewSize: width:" + s.width + "...height:" + s.height);
            }
        }
//        List<int[]> range = params.getSupportedPreviewFpsRange();
//        int[] fps = range.get(0);
//        LogUtil.e(LOG_TAG,"min="+fps[0]+",max="+fps[1]);
//        params.setPreviewFpsRange(3,7);

        params.setPreviewSize(best.width, best.height);//预览比率

//        params.setPictureSize(480, 720);//拍照保存比率

//        Log.e(LOG_TAG, "setPreviewSize BestSize: width:" + best.width + "...height:" + best.height);

        // 大部分手机支持的预览尺寸和录制尺寸是一样的,也有特例,有些手机获取不到,那就把设置录制尺寸放到设置预览的方法里面
        if (params.getSupportedVideoSizes() == null || params.getSupportedVideoSizes().size() == 0) {
            mWidth = best.width;
            mHeight = best.height;
        } else {
            setVideoSize(params);
        }
    }

    /**
     * 根据手机支持的视频分辨率,设置录制尺寸
     *
     * @param params
     */
    @SuppressWarnings("deprecation")
    private void setVideoSize(Parameters params) {
        if (mCamera == null) {
            return;
        }
        //获取手机支持的分辨率集合,并以宽度为基准降序排序
        List<Camera.Size> previewSizes = params.getSupportedVideoSizes();
        Collections.sort(previewSizes, new Comparator<Camera.Size>() {
            @Override
            public int compare(Camera.Size lhs, Camera.Size rhs) {
                if (lhs.width > rhs.width) {
                    return -1;
                } else if (lhs.width == rhs.width) {
                    return 0;
                } else {
                    return 1;
                }
            }
        });

        float tmp = 0f;
        float minDiff = 100f;
        float ratio = 720.0f / 1080.0f;//高宽比率3:4,且最接近屏幕宽度的分辨率
        Camera.Size best = null;
        for (Camera.Size s : previewSizes) {
            tmp = Math.abs(((float) s.height / (float) s.width) - ratio);
//            if (tmp == 0 && getWindowWidth(getContext()) <= s.width) {
//              break;
//          }
            if (tmp < minDiff) {
                minDiff = tmp;
                best = s;
                Log.e("摄像头", "setVideoSize: width:" + s.width + "...height:" + s.height);
            }
        }
//        Log.e(LOG_TAG, "setVideoSize BestSize: width:" + best.width + "...height:" + best.height);
        //设置录制尺寸
        mWidth = best.width;
        mHeight = best.height;
    }
     
    public void setOrientation(int orientation){
        this.orientation = orientation;
    }

attrs文件

<declare-styleable name="MovieRecorderView">
        <!-- 开始是否打开摄像头 -->
        <attr name="is_open_camera" format="boolean" />
        <!-- 一次拍摄最长时间 -->
        <attr name="record_max_time" format="integer"/>
        <!-- 视频分辨率宽度 -->
        <attr name="video_width" format="integer"/>
        <!-- 视频分辨率高度 -->
        <attr name="video_height" format="integer"/>
    </declare-styleable>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容