Android 開發 MediaRecorder視頻錄製入門

前言

  MediaRecorder是Android SDK提供用於錄製音視頻,關於音頻的錄製在我另外一篇博客裏已經介紹.傳送門: http://www.javashuo.com/article/p-qswhcuuw-ez.html,而這篇博客將介紹MediaRecorder視頻錄製的入門和一些MediaRecorder視頻錄製的深坑.爲何只介紹簡單的錄製視頻的入門操做,由於MediaRecorder在實際開發的時候確定還須要配合Camera來使用.而Camera這個大坑又有Camera1和Camera2,因此咱們須要分篇幅來介紹Camera1和Camera2與MediaRecorder的使用方式.html

  雖然是入門可是仍是須要分2個錄製操做來講明android

  •   MediaRecorder簡單的錄製視頻(不設置Camera)
  •   MediaRecorder設置Camera的錄製視頻(這裏指的是Camera1)

  雖然這2個在簡單錄製視頻的時候操做沒啥區別,可是設置Camera錄製有一個坑,秉承着寫博客就是爲了記錄深坑避免下次掉坑的精神,因此我要抓出來單獨說明.算法

MediaRecorder簡單的錄製視頻

實現流程

  1. 開啓硬件加速(由於我的使用TextureView)
  2. 獲取權限
  3. 初始化MediaRecorder
  4. 配置MediaRecorder
  5. 開始錄製視頻
  6. 中止錄製視頻
  7. 暫停錄製與恢復錄製
  8. 銷燬釋放

開啓硬件加速

由於TextureView來預覽圖像使用效果會比SurfaceView與SurfaceTexture,在預覽的時候更不會那麼卡頓.可是使用它須要開啓硬件加速,可是如今的手機設備上基本上都支持硬件加速,因此主流是使用TextureView.下面就是開啓硬件加速的方式,在AndroidManifest.xml裏給須要硬件加速的activity添加android:hardwareAccelerated="true" 屬性ide

<activity android:name=".MediaRecorderVideoActivity"
            android:hardwareAccelerated="true"></activity>

獲取權限

    <!--音頻錄製權限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!--相機權限-->
    <uses-permission android:name="android.permission.CAMERA" />
    <!--讀取和寫入存儲權限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

若是是Android5.0版本這4個權限是須要動態受權的.ui

初始化MediaRecorder

private void initMediaRecorder(){
        mMediaRecorder = new MediaRecorder();
    }

很簡單就是new一個MediaRecorderthis

配置MediaRecorder

 

 private void configMediaRecorder(){
        File videoFile = new File(getExternalCacheDir(),"video.mp4");
        Log.e(TAG, "文件路徑="+videoFile.getAbsolutePath());
        if (videoFile.exists()){
            videoFile.delete();
        }
      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);//設置音頻輸入源  也可使用 MediaRecorder.AudioSource.MIC
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//設置視頻輸入源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//音頻輸出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//設置音頻的編碼格式  
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);//設置圖像編碼格式

//        mMediaRecorder.setVideoFrameRate(30);//要錄製的視頻幀率 幀率越高視頻越流暢 若是設置設備不支持的幀率會報錯  按照註釋說設備會支持自動幀率因此通常狀況下不須要設置
//        mMediaRecorder.setVideoSize(1280,1920);//設置錄製視頻的分辨率  若是設置設備不支持的分辨率會報錯
     mMediaRecorder.setVideoEncodingBitRate(8*1920*1080); //設置比特率,比特率是每一幀所含的字節流數量,比特率越大每幀字節越大,畫面就越清晰,算法通常是 5 * 選擇分辨率寬 * 選擇分辨率高,通常能夠調整5-10,比特率過大也會報錯
        mMediaRecorder.setOrientationHint(90);//設置視頻的攝像頭角度 只會改變錄製的視頻文件的角度(對預覽圖像角度沒有效果)

        Surface surface = new Surface(mTextureView.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);//設置拍攝預覽
        mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());//MP4文件保存路徑
        
    }

配置MediaRecorder是坑比較多的地方,如今咱們來一一說下這些坑編碼

  • 坑1:  首先MediaRecorder的配置是有順序要求的,隨便配置參數將會報錯,通常順序是 設置音頻輸入源與視頻輸入源  >  設置音頻編碼格式/視頻編碼格式/音頻輸出格式 > 設置分辨率/幀率/攝像頭角度 > 添加預覽 > 添加保存文件路徑
  • 坑2:  在上面的代碼上我註釋了setVideoFrameRate(int rate) 與 setVideoSize(int width, int height) 這2個方法,設置錄製視頻幀率與設置視頻分辨率.由於這2個設置的參數都是須要手機設備支持所輸入的的值的,隨便設置將會拋出 start failed -19 的錯誤
  • 坑3:  setOrientationHint()方法設置角度不會改變預覽圖像的角度
  • 坑4:  setPreviewDisplay()設置預覽,其實只預覽錄製過程當中的圖像,中止錄製,相機圖像預覽也中止了.若是想要一直預覽就須要操做Camera來實現
  • 坑5:  關於各類參數設置的格式(設置視頻輸入源,音頻輸入源,音頻輸出格式,音頻編碼格式,圖像編碼),這裏推薦所有使用DEFAULT,上面的demo圖像編碼和音頻編碼使用MP4格式只是一個demo告訴你有不少其餘格式.不少狀況系統雖然提供了不少格式,可是下各個設備支持的格式是不一樣的.若是你是大量適配機型最好使用默認格式,若是是隻作單一設備開發,可使用指定格式.

注意!上面的坑5關於參數配置的問題,你發現出現MediaRecorder: start failed: -38 或者其餘數值的報錯,必定會去百度,這時候-38報錯 百度可能會告訴是MIC沒釋放或者Camera沒釋放(Camera沒釋放的問題我下面那個demo會說明),這的確有可能,可是還有另一種多是設置setAudioEncoder或者setVideoEncoder在或者setAudioSource 上出現了問題,好比一些設備是支持setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);可是換調另一個設備上就不支持了,因此這個時候請切換成setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);默認模式,使用仍是從新提醒你若是有須要適配大量機型的APP那麼儘可能選擇默認格式.若是你只是爲單一Android設備開發你能夠指定格式.spa

小提醒~若是你想一進入Activity就配置MediaRecorder好,那麼你就須要設置TextureView的監聽setSurfaceTextureListener(SurfaceTextureListener listener),必需它先啓動準備好以後在配置MediaRecorder,不然會報錯的由於TextureView在啓動activity後須要一段時間初始化啓動code

開始錄製視頻

private void startRecorder(){
        configMediaRecorder();//配置MediaRecorder 由於每一次中止錄製後調用重置方法後都會取消配置,因此每一次開始錄製都須要從新配置一次
        try {
            mMediaRecorder.prepare();//準備
            mMediaRecorder.start();//開啓
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意!每一次中止錄製後調用重置方法後都會取消配置,因此每一次開始錄製都須要從新配置一次orm

中止錄製視頻

private void stopRecorder(){
        mMediaRecorder.stop();//暫停
        mMediaRecorder.reset();//重置 重置後將進入空閒狀態,再次啓動錄製須要從新配置MediaRecorder
    }

暫停錄製與恢復錄製

暫停錄製,注意這裏是pause()方法,不是stop()

private void pauseRecorder(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.pause();//暫停
        }
    }

恢復錄製

private void resumeRecorder(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.resume();//恢復
        }
    }

銷燬釋放

private void destroy(){
        if (mMediaRecorder != null){
            mMediaRecorder.stop();
            mMediaRecorder.release();//釋放 釋放以前須要先調用stop()
            mMediaRecorder = null;
        }
    }

MediaRecorder設置Camera的錄製視頻

添加Camera來錄製視頻原本是另一個篇幅應該說的事情,可是有個坑不得不在這裏先說. 其餘初始化和開啓/中止/都與上面的介紹一致,下面咱們來講說萬惡的MediaRecorder配置:

private void configMediaRecorder(){
        File videoFile = new File(getExternalCacheDir(),"video.mp4");
        Log.e(TAG, "文件路徑="+videoFile.getAbsolutePath());
        if (videoFile.exists()){
            videoFile.delete();
        }

        Camera camera = Camera.open();
        camera.unlock();
        mMediaRecorder.setCamera(camera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//設置音頻輸入源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//設置視頻輸入源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//音頻輸出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//設置音頻的編碼格式
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);//設置圖像編碼格式
        mMediaRecorder.setOrientationHint(90);//設置視頻的攝像頭角度 只會改變錄製的視頻角度
        Surface surface = new Surface(mTextureView.getSurfaceTexture());
        mMediaRecorder.setPreviewDisplay(surface);
        mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());


    }

  也是一個簡單的配置,也就是多了有攝像頭的代碼,若是你須要選擇攝像頭能夠本身獲取攝像頭信息來判斷先後在獲取對應id,打開對應攝像頭,做爲簡單的demo就暫時不演示選擇攝像頭的代碼.

  而後重點來了這裏有一個關鍵,你不會想到Camera.open();執行後攝像頭竟然是鎖定狀態的,就算是明白打開後就是鎖定,可是你也不會發如今設置mMediaRecorder.setCamera(camera);以前是須要將攝像頭解除鎖定 camera.unlock();! 是的,就是這行代碼困擾我了一下午!!!!!! 因此重點!重點!重點! 就是camera.unlock(); 蛋疼的是若是你不作解除鎖定的操做報錯的提示是 start failed -19, 這個報錯提示成功的讓我反反覆覆研究是否是分辨率尺寸設置有問題........

  而這個Camera.unlock()在官方註釋裏也有說明在啓動在設置以前先須要解鎖攝像頭,以下:

     * <p>Use this function to switch quickly between preview and capture mode without a teardown of
     * the camera object. {@link android.hardware.Camera#unlock()} should be called before
     * this. Must call before {@link #prepare}.</p>

setProfile

這個功能實際上是相機預設的一些錄製質量,格式和分辨率,官方註釋說:

使用CamcorderProfile對象中的設置進行錄製。這種方法應該在設置視頻和音頻源以後和setOutputfile()以前調用。若是使用延時攝像機配置文件,音頻相關源或錄製參數被忽略。

咱們看到關鍵CamcorderProfile對象,那麼看看什麼CamcorderProfile的解釋:

攝像機應用程序的預約義攝像機配置文件設置,這些設置是隻讀的。其實就是預設的一些攝像頭配置設置

預設的屬性有這些:

  • QUALITY_LOW
  • QUALITY_HIGH
  • QUALITY_QCIF
  • QUALITY_CIF
  • QUALITY_480P
  • QUALITY_720P
  • QUALITY_1080P
  • QUALITY_2160P
  • QUALITY_TIME_LAPSE_LOW
  • QUALITY_TIME_LAPSE_HIGH
  • QUALITY_TIME_LAPSE_QCIF
  • QUALITY_TIME_LAPSE_CIF
  • QUALITY_TIME_LAPSE_480P
  • QUALITY_TIME_LAPSE_720P
  • QUALITY_TIME_LAPSE_1080P
  • QUALITY_TIME_LAPSE_2160P
  • QUALITY_HIGH_SPEED_LOW
  • QUALITY_HIGH_SPEED_HIGH
  • QUALITY_HIGH_SPEED_480P
  • QUALITY_HIGH_SPEED_720P
  • QUALITY_HIGH_SPEED_1080P
  • QUALITY_HIGH_SPEED_2160P

使用代碼:

if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
                CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
                profile.videoBitRate = mPreviewSize.getWidth() * mPreviewSize.getHeight();
                mMediaRecorder.setProfile(profile);
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
                CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
                profile.videoBitRate = mPreviewSize.getWidth() * mPreviewSize.getHeight();
                mMediaRecorder.setProfile(profile);
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
                mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA));
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_CIF)) {
                mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_CIF));
                mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
            } else {
                mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
                mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                mMediaRecorder.setVideoEncodingBitRate(10000000);
                mMediaRecorder.setVideoFrameRate(30);
                mMediaRecorder.setVideoSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
}

 

 

end

相關文章
相關標籤/搜索