視頻錄制

視頻錄制控件
布局文件就是一個(gè)surfaceView

/**
 * 視頻錄制控件
 */
public class MovieRecorderView extends LinearLayout implements OnErrorListener {

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;

    
     //屏幕方向
     //手機(jī)頂部朝上
    public static final int UP = 0;
    //手機(jī)底部朝上
    public static final int DOWN = 1;
    //手機(jī)左邊朝上
    public static final int LEFT = 2;
    //手機(jī)右邊朝上
    public static final int RIGHT = 3;
    
    private int orientation = UP;

    private MediaRecorder mMediaRecorder;
    private Camera mCamera;
    private Timer mTimer;// 計(jì)時(shí)器
    private OnRecordFinishListener mOnRecordFinishListener;// 錄制完成回調(diào)接口

    private int mWidth;// 視頻分辨率寬度
    private int mHeight;// 視頻分辨率高度
    private boolean isOpenCamera;// 是否一開始就打開攝像頭
    private int mRecordMaxTime;// 一次拍攝最長(zhǎng)時(shí)間
    private int mTimeCount;// 時(shí)間計(jì)數(shù)
    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);
        // 初始化各項(xiàng)組件
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0);
        mWidth = a.getInteger(R.styleable.MovieRecorderView_video_width, 320);// 默認(rèn)320
        mHeight = a.getInteger(R.styleable.MovieRecorderView_video_height, 240);// 默認(rèn)240

        isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true);// 默認(rèn)打開
        mRecordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 10);// 默認(rèn)為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();// 得到攝像頭的個(gè)數(shù)
        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();
        
    }
    
    /**
     * 設(shè)置攝像頭屬性
     */
    @SuppressWarnings("deprecation")
    public void setCameraParams() {
        try {
            if (mCamera != null) {
                Parameters params = mCamera.getParameters();
//              params.set("orientation", "portrait");
                setPreviewSize(params);
                mCamera.setParameters(params);
            }
            //設(shè)置預(yù)覽豎屏
            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();// 得到攝像頭的個(gè)數(shù)

        
        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;
        // 創(chuàng)建文件
        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);// 去掉這一行,有些手機(jī)會(huì)mMediaRecorder.start()失敗
        mMediaRecorder.setVideoEncodingBitRate(1 * 1280 * 720);// 設(shè)置幀頻率,然后就清晰了
        
        //輸出角度
        switch (orientation) {
        case UP:
            mMediaRecorder.setOrientationHint(90);// 輸出旋轉(zhuǎn)90度,頂部朝上錄制
            break;
        case DOWN:
            mMediaRecorder.setOrientationHint(270);
            break;
        case LEFT:
            mMediaRecorder.setOrientationHint(0);
            break;
        case RIGHT:
            mMediaRecorder.setOrientationHint(180);
            break;
        }
        
        mMediaRecorder.setVideoSize(mWidth, mHeight);// 設(shè)置分辨率:
        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
     *            達(dá)到指定時(shí)間之后回調(diào)接口
     */
    public void record(final OnRecordFinishListener onRecordFinishListener) {
        this.mOnRecordFinishListener = onRecordFinishListener;
        createRecordDir();
        try {
            if (!isOpenCamera)// 如果未打開攝像頭,則打開
                initCamera();
            initRecord();
            mTimeCount = 0;// 時(shí)間計(jì)數(shù)器重新賦值
            mTimer = new Timer();
            mTimer.schedule(new TimerTask() {

                @Override
                public void run() {
                    mTimeCount++;
//                    mProgressBar.setProgress(mTimeCount);
                    // 設(shè)置進(jìn)度
                    if (mOnRecordFinishListener != null)
                        mOnRecordFinishListener.onRecord(mTimeCount);
                    if (mTimeCount == mRecordMaxTime) {// 達(dá)到指定時(shí)間,停止拍攝
                        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) {
            // 設(shè)置后不會(huì)崩
            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;
    }

    /**
     * 錄制完成回調(diào)接口
     *
     * @author liuyinjun
     *
     * @date 2015-2-5
     */
    public interface OnRecordFinishListener {
        public void onRecordFinish();
        void onRecord(int progress);
    }
    
    /**
     * 設(shè)置最大錄制時(shí)間
     * @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();
        }
    }
    
    
    /**
     * 根據(jù)手機(jī)支持的視頻分辨率,設(shè)置預(yù)覽尺寸
     *
     * @param params
     */
    @SuppressWarnings("deprecation")
    private void setPreviewSize(Parameters params) {
        if (mCamera == null) {
            return;
        }
        //獲取手機(jī)支持的分辨率集合,并以寬度為基準(zhǔn)降序排序
        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);//預(yù)覽比率

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

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

        // 大部分手機(jī)支持的預(yù)覽尺寸和錄制尺寸是一樣的,也有特例,有些手機(jī)獲取不到,那就把設(shè)置錄制尺寸放到設(shè)置預(yù)覽的方法里面
        if (params.getSupportedVideoSizes() == null || params.getSupportedVideoSizes().size() == 0) {
            mWidth = best.width;
            mHeight = best.height;
        } else {
            setVideoSize(params);
        }
    }

    /**
     * 根據(jù)手機(jī)支持的視頻分辨率,設(shè)置錄制尺寸
     *
     * @param params
     */
    @SuppressWarnings("deprecation")
    private void setVideoSize(Parameters params) {
        if (mCamera == null) {
            return;
        }
        //獲取手機(jī)支持的分辨率集合,并以寬度為基準(zhǔn)降序排序
        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);
        //設(shè)置錄制尺寸
        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" />
        <!-- 一次拍攝最長(zhǎng)時(shí)間 -->
        <attr name="record_max_time" format="integer"/>
        <!-- 視頻分辨率寬度 -->
        <attr name="video_width" format="integer"/>
        <!-- 視頻分辨率高度 -->
        <attr name="video_height" format="integer"/>
    </declare-styleable>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容