天天看點

仿微信錄制小視訊

先上張圖檔看看效果

仿微信錄制小視訊

簡單叙述下

  1. 首先通過Camera類調用系統相機
  2. 通過surfaceview繪制出來
  3. 通過MediaRecorder來錄制視訊
  4. 閃光燈 和 切換攝像頭 需要重新配置Camera的參數
  5. Camera預覽界面畫面拉升的原因是因為Surfaceview的大小與設定的比例不一緻的

**本次版本更新了 切換前置攝像頭錄制視訊問題

Android部分手機錄制視訊适配IOS手機問題 (OPPO手機部分不适配 目前啟用的H264格式)**

完整的代碼

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.tongxun.atongmu.dialog.CommonDialog;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class CircleViedoActivity extends Activity implements View.OnClickListener,MediaRecorder.OnErrorListener {

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private ProgressBar mProgressBar;
    private Button shoot_button;
    private ImageView circle_light_btn;
    private ImageView circle_change_camera_btn;
    private TextView circle_camera_time;

    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 int mNumberOfCameras=;//手機攝像頭的數量
    Bitmap bitmap;//圖檔縮略圖
    private int screenWidth;
    public static File mVecordFile = null;// 檔案
    private boolean isOpenFlash=false;
    private boolean isBackCamera=true;
    private int mbackCamera;
    private int mfrontCamera;
    private CommonDialog commonDialog;

    private static final int MOVICE_SUCCESS=;//錄制完成
    private static final int MOVICE_FILE=;//錄制失敗


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_super_video);
        init_datas();
        init_view();
        set_datas();
        init_event();
    }

    private void init_datas() {
        isOpenCamera=true;//預設一開始就打開相機
        mRecordMaxTime=;//設定錄制的時間

        //擷取手機攝像頭的數量
        Camera.CameraInfo cameraInfo=new Camera.CameraInfo();
        mNumberOfCameras=Camera.getNumberOfCameras();
        for(int camidx=;camidx<mNumberOfCameras;camidx++){
            Camera.getCameraInfo(camidx, cameraInfo);
            if(cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_BACK){
                mbackCamera=camidx;
            }else if(cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_FRONT){
                mfrontCamera=camidx;
            }
        }



    }

    private void init_view() {
        mWidth=;
        mHeight=;

        //擷取螢幕的寬度
        screenWidth = getWindowManager().getDefaultDisplay().getWidth();
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);//預覽界面
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);//進度條
        shoot_button = (Button) findViewById(R.id.shoot_button);//拍攝按鈕
        circle_camera_time= (TextView) findViewById(R.id.circle_camera_time);
        circle_change_camera_btn= (ImageView) findViewById(R.id.circle_change_camera_btn);//切換攝像頭
        circle_light_btn= (ImageView) findViewById(R.id.circle_light_btn);//開啟閃光燈
        ViewGroup.LayoutParams params=mSurfaceView.getLayoutParams();
        params.height=(screenWidth*)/;
        params.width=screenWidth;
        mSurfaceView.setLayoutParams(params);


        top_model2_left_img= (ImageView)findViewById(R.id.top_model2_left_img);
        top_model2_left_img.setOnClickListener(this);
        top_model2_right_img= (ImageView)findViewById(R.id.top_model2_right_img);
        top_model2_right_img.setVisibility(View.INVISIBLE);
        top_model2_txt= (TextView)findViewById(R.id.top_model2_txt);
        top_model2_txt.setText("視訊錄制");
    }


    private void set_datas() {
        mSurfaceHolder = mSurfaceView.getHolder();
        mProgressBar.setMax(mRecordMaxTime);// 設定進度條最大量
        mSurfaceHolder.setKeepScreenOn(true);//設定螢幕常亮
        mSurfaceHolder.addCallback(new CustomCallBack());
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        circle_camera_time.setText("");
    }

    private void init_event() {
        circle_light_btn.setOnClickListener(this);
        circle_change_camera_btn.setOnClickListener(this);
        shoot_button.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    record(new OnRecordFinishListener() {
                        @Override
                        public void onRecordFinish() {
                            //錄制時間達到最大值
                            handler.sendEmptyMessage(MOVICE_SUCCESS);
                        }
                    });
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (mTimeCount >  && mTimeCount<) { //防止達到最大值up事件
                        //錄制時間大于一秒
                        handler.sendEmptyMessage(MOVICE_SUCCESS);
                    } else if(mTimeCount<=) {
                        //删除小于一秒的視訊
                        if (getmVecordFile() != null) {
                            getmVecordFile().delete();
                        }
                        handler.sendEmptyMessage(MOVICE_FILE);

                    }
                }
                return true;
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        if(mCamera!=null){
            freeCameraResource();
        }
        try {
            initCamera();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //   mRecorderView.stop();
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //停止拍攝
            stopRecord();
            switch (msg.what){
                case MOVICE_SUCCESS:
                    String msgs = "确認上傳嗎?";
                    commonDialog = new CommonDialog(CircleViedoActivity.this, new CommonDialog.GoCommonDialog() {
                        @Override
                        public void go() {
                            finishActivity();
                            commonDialog.dismiss();
                        }
                        @Override
                        public void cancel() {
                            //删除沒有上傳的視訊
                            if (getmVecordFile() != null) {
                                getmVecordFile().delete();
                            }
                            commonDialog.dismiss();
                        }
                    }, msgs, "确定", "取消");
                    commonDialog.show();


                    break;
                case MOVICE_FILE:
                    Toast.makeText(CircleViedoActivity.this, "視訊錄制時間太短", Toast.LENGTH_SHORT).show();
                    break;
            }

        }
    };


    private void finishActivity() {
        stop();
        Bitmap bitmap = null;
        if (getmVecordFile() != null) {
            //得到檔案 File類型
            File mfile = getmVecordFile();
            bitmap = getVideoThumbnail(mfile.toString());
        }



----------

部分Android手機縮略圖拉不到,找到一種解決辦法不是太滿意 有沒有大神提供思路
----------


        // VideoPlayerActivity.startActivity(this, mRecorderView.getVecordFile().toString());
        Intent intent = new Intent();
        intent.putExtra("bitmap",bitmap);
        setResult(RESULT_OK,intent);
        finish();
    }

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

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.circle_light_btn:
                //開啟關閉閃光燈 預設關閉
                if(isOpenFlash){
                    isOpenFlash=false;
                    circle_light_btn.setImageResource(R.drawable.camera_light);
                }else {
                    isOpenFlash=true;
                    circle_light_btn.setImageResource(R.drawable.camera_no_light);
                }
                try {
                    initCamera();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.circle_change_camera_btn:
                if(isBackCamera){
                    isBackCamera=false;
                }else {
                    isBackCamera=true;
                }
                try {
                    initCamera();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.top_model2_left_img:
                stop();
                finish();
                break;
        }
    }

    private class CustomCallBack implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            if (!isOpenCamera)
                return;
            try {
                initCamera();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }
    /**
     * 初始化攝像頭
     */
    private void initCamera() throws IOException {
        if (mCamera != null) {
            freeCameraResource();
        }
        try {
            if(isBackCamera){
                mCamera = Camera.open(mbackCamera);//打開後攝像頭
                setCameraParams(isOpenFlash);
            }else {
                mCamera=Camera.open(mfrontCamera);//打開前攝像頭
            }
        } catch (Exception e) {
            e.printStackTrace();
            freeCameraResource();
        }
        if (mCamera == null)
            return;

        mCamera.setDisplayOrientation();
        mCamera.setPreviewDisplay(mSurfaceHolder);
        mCamera.startPreview();
        mCamera.unlock();
    }


    /**
     * 設定攝像頭為豎屏
     */
    private void setCameraParams(Boolean isOpenFlash) {
        if (mCamera != null) {
            Camera.Parameters params = mCamera.getParameters();
            //擷取預覽的各種分辨率
            List<Camera.Size> supportedPreviewSizes = params.getSupportedPreviewSizes();
            params.set("orientation", "portrait");//豎屏錄制
            params.setPreviewSize(mWidth, mHeight);//預設640*480
            params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//持續對焦
            if(isBackCamera){
                if(isOpenFlash){
                    //開啟閃光燈
                    params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);

                }else {
                    //關閉閃光燈
                    params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                }

            }else {

            }
            mCamera.setParameters(params);
        }
    }


    /**
     * 釋放攝像頭資源
     */
    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 + "ATOMImg/video/");
        if (!sampleDir.exists()) {
            sampleDir.mkdirs();
        }
        File vecordDir = sampleDir;
        // 建立檔案
        try {
            mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式

        } catch (IOException e) {
        }
    }

    /**
     * 初始化
     */
    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(MediaRecorder.VideoSource.CAMERA);// 視訊源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 音頻源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 視訊輸出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);// 音頻格式
        mMediaRecorder.setVideoSize(mWidth, mHeight);// 設定分辨率:
        // mMediaRecorder.setVideoFrameRate(16);// 這個我把它去掉了,感覺沒什麼用
        mMediaRecorder.setVideoEncodingBitRate( *  * );// 設定幀頻率,然後就清晰了
        if(isBackCamera) {
            mMediaRecorder.setOrientationHint();// 輸出旋轉90度,保持豎屏錄制
        }else {
            mMediaRecorder.setOrientationHint();
        }mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 視訊錄制格式



----------


/**
        這裡是配置攝像頭的重點 因為我們錄制視訊需要适配IOS開發和Android不同版本的手機 而IOS和Android通配的視訊格式不多 我們這裡采用H264格式 它的相容範圍更廣闊 主要解決的就是部分OPPO手機不适配問題
*/


----------


        // mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
        mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
        mMediaRecorder.prepare();
        try {
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 錄制完成回調接口
     */
    public interface OnRecordFinishListener {
        public void onRecordFinish();
    }

    /**
     * 錄制
     * @param onRecordFinishListener
     */
    public void record(final OnRecordFinishListener onRecordFinishListener) {
        this.mOnRecordFinishListener = onRecordFinishListener;
        createRecordDir();//建立目錄

        try {
            if (!isOpenCamera)// 如果未打開攝像頭,則打開
                initCamera();//初始化攝像頭
            initRecord();//初始化錄制參數
            mTimeCount = ;// 時間計數器重新指派

            mTimer = new Timer();//建立一個定時器

            mTimer.schedule(new TimerTask() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    mTimeCount++;
                    mProgressBar.setProgress(mTimeCount);// 設定進度條
                    //  handler.sendEmptyMessage(TIME_CHANGW);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            circle_camera_time.setText(mTimeCount + "″");
                        }
                    });
                    if (mTimeCount == mRecordMaxTime) {// 達到指定時間,停止拍攝
                        //錄制完成調用錄制回調接口
                        if (mOnRecordFinishListener != null)
                            mOnRecordFinishListener.onRecordFinish();
                    }
                }
            }, , );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 停止拍攝
     */
    public void stop() {
        stopRecord();
        releaseRecord();
        freeCameraResource();
    }

    /**
     * 停止錄制
     */
    public void stopRecord() {
        mProgressBar.setProgress();
        circle_camera_time.setText("");
        if (mTimer != null)
            mTimer.cancel();
        if (mMediaRecorder != null) {
            // 設定後不會崩
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setPreviewDisplay(null);
            try {
                mMediaRecorder.stop();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (RuntimeException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 擷取的視訊的位置
     * @return
     */
    public File getmVecordFile() {
        return mVecordFile;
    }

    /**
     * 釋放資源
     */
    private void releaseRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null);
            try {
                mMediaRecorder.release();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        mMediaRecorder = null;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK)) {
            stop();
            finish();
            return false;
        }else {
            return super.onKeyDown(keyCode, event);
        }

    }

    /**
     * 擷取視訊縮略圖
     * @param filePath
     * @return
     */
    public Bitmap getVideoThumbnail(String filePath) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setDataSource(filePath);
            bitmap = retriever.getFrameAtTime();
        }
        catch(IllegalArgumentException e) {
            e.printStackTrace();
        }
        catch (RuntimeException e) {
            e.printStackTrace();
        }
        finally {
            try {
                retriever.release();
            }
            catch (RuntimeException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(bitmap!=null){
            try {
                bitmap.recycle();
            } catch (Exception e) {
                e.printStackTrace();
            }
            bitmap=null;
        }

    }
}