Android 開發 MediaRecorder使用Camera1配合錄製視頻

前言

  MediaRecorder能夠不依靠Camera API 實現視頻的錄製,可是若是須要切換攝像頭/設置對焦/選擇分辨率等等就須要Camera來參與配合錄製視頻.這篇博客將介紹使用Camera1來實現視頻錄製.此篇博客不在重複一些細節和坑的介紹.若是你剛接觸建議你看我另外一篇博客http://www.javashuo.com/article/p-rrzbrcau-hh.html這篇博客用更簡單易懂的形式說明了MediaRecorder錄製視頻的步驟,而且有大量深坑的詳解介紹,防止你也掉坑裏.html

實現流程

  1.   設置TextureView的硬件加速
  2.   獲取權限
  3.   初始化TextureView的監聽
  4.   初始化MediaRecorder
  5.   初始化Camera
  6.   初始化配置Camera
  7.   開始相機預覽
  8.   配置MediaRecorder(每次錄製以前都須要配置一次)
  9.   開始錄製
  10.   中止錄製
  11.   恢復相機預覽

代碼部分

  關鍵點有大量註釋,就不作另外篇幅的介紹java

  若是你對下面代碼裏的計算分辨率不甚理解,能夠去我另外一篇博客有更詳細的講解分辨率的計算,這篇博客雖然是拿Camera2 API來計算分辨率,可是原理是同樣的. 傳送門:http://www.javashuo.com/article/p-vqopnfok-ko.htmlandroid

  若是你對Camera自動對焦不太理解,能夠去我另外一篇更詳細的博客有介紹Camera1的自動對焦. 傳送門:http://www.javashuo.com/article/p-mzlzotgk-hu.html安全

import androidx.appcompat.app.AppCompatActivity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class MedioRecorderCamera1Activity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = MedioRecorderCamera1Activity.class.getSimpleName();
    private TextureView mTextureview;
    private Button mBtnStart, mBtnFinish;
    private MediaRecorder mMediaRecorder;
    private Camera mCamera;
    private Camera.Size mSelectSize;//記錄當前選擇的分辨率
    private boolean isRecorder = false;//用於判斷當前是否在錄製視頻
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0x01:
                    mCamera.autoFocus(new Camera.AutoFocusCallback() { //自動對焦
                        @Override
                        public void onAutoFocus(boolean success, Camera camera) {

                        }
                    });
                    mHandler.sendEmptyMessageDelayed(0x01,2*1000);//2秒以後在對焦一次,一直重複自動對焦
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_medio_recorder_camera1);
        mTextureview = findViewById(R.id.textureview);
        mBtnStart = findViewById(R.id.btn_start);
        mBtnFinish = findViewById(R.id.btn_finish);
        mBtnStart.setOnClickListener(this);
        mBtnFinish.setOnClickListener(this);
        initTextureViewListener();
        initMediaRecorder();

    }

    /**
     * 初始化TextureView監聽
     */
    private void initTextureViewListener(){
        mTextureview.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { //Textureview初始化啓用回調
                initCamera();
                initCameraConfig();
                try {
                    mCamera.setPreviewTexture(surface);
                    mCamera.startPreview();
                    mHandler.sendEmptyMessage(0x01);//啓動對焦
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_start:
                startRecorder();

                break;
            case R.id.btn_finish:
                stopRecorder();

                break;
            default:
                break;
        }

    }

    /**
     * 初始化MediaRecorder
     */
    private void initMediaRecorder(){
        mMediaRecorder = new MediaRecorder();
    }

    /**
     * 選擇攝像頭
     * @param isFacing true=前攝像頭 false=後攝像頭
     * @return 攝像id
     */
    private Integer selectCamera(boolean isFacing){
        int cameraCount = Camera.getNumberOfCameras();
//        CameraInfo.CAMERA_FACING_BACK 後攝像頭
//        CameraInfo.CAMERA_FACING_FRONT  前攝像頭
        int facing = isFacing ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
        Log.e(TAG, "selectCamera: cameraCount="+cameraCount);
        if (cameraCount == 0){
            Log.e(TAG, "selectCamera: The device does not have a camera ");
            return null;
        }
        Camera.CameraInfo info = new Camera.CameraInfo();
        for (int i=0; i < cameraCount; i++){
            Camera.getCameraInfo(i,info);
            if (info.facing == facing){
                return i;
            }

        }
        return null;

    }

    /**
     * 初始化相機
     */
    private void initCamera(){
        mCamera = Camera.open(selectCamera(false));
        mSelectSize = selectPreviewSize(mCamera.getParameters());


    }

    /**
     * 初始化相機配置
     */
    private void initCameraConfig(){
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);//關閉閃光燈
        parameters.setFocusMode(Camera.Parameters.FLASH_MODE_AUTO); //對焦設置爲自動
        parameters.setPreviewSize(mSelectSize.width,mSelectSize.height);//設置預覽尺寸
        parameters.setPictureSize(mSelectSize.width,mSelectSize.height);//設置圖片尺寸  就拿預覽尺寸做爲圖片尺寸,其實他們基本上是同樣的
        parameters.set("orientation", "portrait");//相片方向
        parameters.set("rotation", 90); //相片鏡頭角度轉90度(默認攝像頭是橫拍)
        mCamera.setParameters(parameters);//添加參數
        mCamera.setDisplayOrientation(90);//設置顯示方向

    }

    /**
     * 計算獲取預覽尺寸
     * @param parameters
     * @return
     */
    private Camera.Size selectPreviewSize(Camera.Parameters parameters){
        List<Camera.Size> previewSizeList =  parameters.getSupportedPreviewSizes();
        if (previewSizeList.size() == 0){
            Log.e(TAG, "selectPreviewSize: previewSizeList size is 0" );
            return null;

        }

        Camera.Size currentSelectSize = null;
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int deviceWidth =displayMetrics.widthPixels;
        int deviceHeight = displayMetrics.heightPixels;
        for (int i = 1; i < 41 ; i++){
            for(Camera.Size itemSize : previewSizeList){
//                Log.e(TAG, "selectPreviewSize: itemSize 寬="+itemSize.width+"高"+itemSize.height);
                if (itemSize.height > (deviceWidth - i*5) && itemSize.height < (deviceWidth + i*5)){
                    if (currentSelectSize != null){ //若是以前已經找到一個匹配的寬度
                        if (Math.abs(deviceHeight-itemSize.width) < Math.abs(deviceHeight - currentSelectSize.width)){ //求絕對值算出最接近設備高度的尺寸
                            currentSelectSize = itemSize;
                            continue;
                        }
                    }else {
                        currentSelectSize = itemSize;
                    }

                }

            }
        }
        Log.e(TAG, "selectPreviewSize: 當前選擇的尺寸 寬="+currentSelectSize.width+"高"+currentSelectSize.height);
        return currentSelectSize;
    }

    /**
     * 配置MedioRecorder
     */
    private void configMedioRecorder(){
        File saveRecorderFile = new File(getExternalCacheDir(),"CameraRecorder.mp4");
        if (saveRecorderFile.exists()){
            saveRecorderFile.delete();
        }
        mCamera.unlock();
        mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//設置音頻源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);//設置視頻源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//設置音頻輸出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//設置音頻編碼格式
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);//設置視頻編碼格式
        mMediaRecorder.setVideoSize(mSelectSize.width,mSelectSize.height);//設置視頻分辨率
        mMediaRecorder.setVideoEncodingBitRate(8*1920*1080);//設置視頻的比特率
        mMediaRecorder.setVideoFrameRate(60);//設置視頻的幀率
        mMediaRecorder.setOrientationHint(90);//設置視頻的角度
        mMediaRecorder.setMaxDuration(60*1000);//設置最大錄製時間
        Surface surface = new Surface(mTextureview.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);//設置預覽
        mMediaRecorder.setOutputFile(saveRecorderFile.getAbsolutePath());//設置文件保存路徑
        mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() { //錄製異常監聽
            @Override
            public void onError(MediaRecorder mr, int what, int extra) {
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                try {
                    mCamera.setPreviewTexture(mTextureview.getSurfaceTexture());
                    mCamera.startPreview();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });


    }

    /**
     * 開啓錄製視頻
     */
    private void startRecorder(){
        if (!isRecorder) {//若是不在錄製視頻
            mCamera.stopPreview();//暫停相機預覽
            configMedioRecorder();//再次配置MedioRecorder
            try {
                mMediaRecorder.prepare();//準備錄製
            } catch (IOException e) {
                e.printStackTrace();
            }
            mMediaRecorder.start();//開始錄製
            isRecorder = true;
        }

    }

    /**
     * 中止錄製視頻
     */
    private void stopRecorder(){
        if (isRecorder){ //若是在錄製視頻
            mMediaRecorder.stop();//暫停錄製
            mMediaRecorder.reset();//重置,將MediaRecorder調整爲空閒狀態
            isRecorder = false;
            try {
                mCamera.setPreviewTexture(mTextureview.getSurfaceTexture());//從新設置預覽SurfaceTexture
                mCamera.startPreview(); //從新開啓相機預覽
                mCamera.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {

                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMediaRecorder != null){
            if (isRecorder) {
                mMediaRecorder.stop();
            }
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        if (mCamera != null){
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;

        }
    }
}

 這裏說明一下爲何在配置MediaRecorder的時候大量使用DEFAULT 默認模式,是由於若是選定一個模式,並不必定全部機型都是適配的,全部爲了安全適配全部機型這裏最好選擇默認.app

        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//設置音頻源  mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);//設置視頻源 mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//設置音頻輸出格式 mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//設置音頻編碼格式 mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);//設置視頻編碼格式
相關文章
相關標籤/搜索