Android使用MediaRecorder進行錄影,暫停和繼續錄影的VideoUtils

乌拉小考發表於2024-03-20

使用MediaRecorder進行錄影,要注意再設定MediaRecorder的引數的時候設定,這裡也是查了網上很多程式碼都沒有一個完整能實現的,或多或少都有點問題。
還有再暫停/繼續錄製的時候要注意將Camera的預覽關閉camera.stopPreview()不然預覽的介面還是會繼續動給人暫停了還在錄製的錯覺。
還有camera再使用之前記得要unlock一下。至於設定MediaRecorder的引數,也是嘗試了好多次,最後選擇了使用profile選擇影片質量來進行配置,這樣最簡單,可以考慮到影片大小的要求選擇預設的 720P等,但是注意要硬體適配的大小。
所以如果出現media start failed,那麼可以嘗試降低影片錄製引數來重新嘗試。
完整的VideoUtils程式碼如下:


import android.hardware.Camera
import android.media.CamcorderProfile
import android.media.MediaRecorder
import android.os.Build
import android.view.Surface
import android.view.TextureView
import com.blankj.utilcode.util.PathUtils
import com.blankj.utilcode.util.PermissionUtils
import com.blankj.utilcode.util.ToastUtils
import java.io.File
import java.io.IOException
import java.lang.Exception

// 簽署時候,錄製影片的工具,包含錄製和播放
class VideoUtils {

    companion object {

        private var instance: VideoUtils ?= null

        fun getInstance(): VideoUtils {
            if (instance == null) {
                instance = VideoUtils()
            }
            return instance!!
        }
    }

    private var camera: Camera? = null

    // 是否正在錄製
    var isRecording: Boolean = false
    // 是否開啟了錄製,初始化並開啟錄製,之後是暫停
    var started: Boolean = false
    private var mMediaRecorder: MediaRecorder ?= null

    init {
        mMediaRecorder = MediaRecorder()
    }

    private fun configMediaRecorder(textureView: TextureView){
        val videoFile = File(PathUtils.getExternalDownloadsPath(),"record.mp4")
        if (videoFile.exists()){
            videoFile.delete()
        }
        camera = Camera.open(0)
        camera?.stopPreview()
        camera?.unlock()

        val profile = CamcorderProfile.get( 0, CamcorderProfile.QUALITY_480P )

//        val sizeList = camera.parameters.supportedVideoSizes
//        val videoSize = sizeList.first()
        mMediaRecorder?.setCamera(camera)
        mMediaRecorder?.setAudioSource(MediaRecorder.AudioSource.CAMCORDER)//設定音訊輸入源  也可以使用 MediaRecorder.AudioSource.MIC
        mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.CAMERA)//設定影片輸入源


        mMediaRecorder?.setOutputFormat(profile.fileFormat)//音訊輸出格式
        mMediaRecorder?.setAudioEncoder(profile.audioCodec)//設定音訊的編碼格式
        mMediaRecorder?.setVideoEncoder(profile.videoCodec)//設定影像編碼格式

        mMediaRecorder?.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight)
        mMediaRecorder?.setVideoFrameRate(profile.videoFrameRate)
        mMediaRecorder?.setVideoEncodingBitRate(profile.videoBitRate)
        mMediaRecorder?.setAudioEncodingBitRate(profile.audioBitRate)

        mMediaRecorder?.setAudioSamplingRate(profile.audioSampleRate)
        mMediaRecorder?.setAudioChannels(profile.audioChannels)

//        mMediaRecorder?.setVideoFrameRate(20)//要錄製的影片幀率 幀率越高影片越流暢 如果設定裝置不支援的幀率會報錯  按照註釋說裝置會支援自動幀率所以一般情況下不需要設定
//        mMediaRecorder?.setVideoSize(1080,780)//設定錄製影片的解析度  如果設定裝置不支援的解析度會報錯
//        mMediaRecorder?.setVideoEncodingBitRate(5 * 100 * 100) //設定位元率,位元率是每一幀所含的位元組流數量,位元率越大每幀位元組越大,畫面就越清晰,演算法一般是 5 * 選擇解析度寬 * 選擇解析度高,一般可以調整5-10,位元率過大也會報錯
//        mMediaRecorder?.setOrientationHint(360)//設定影片的攝像頭角度 只會改變錄製的影片檔案的角度(對預覽影像角度沒有效果)

        val surface = Surface(textureView.getSurfaceTexture())
        mMediaRecorder?.setOutputFile(videoFile.getAbsolutePath())//MP4檔案儲存路徑
        mMediaRecorder?.setPreviewDisplay(surface)//設定拍攝預覽

    }

    fun startRecorder(textureView: TextureView) {
        try {
            MyPermissionUtils.requestMicrophonePermissions(object: PermissionUtils.SimpleCallback{
                override fun onGranted() {
                    MyPermissionUtils.requestCameraPermissions(object: PermissionUtils.SimpleCallback{
                        override fun onGranted() {
                            try {
                                configMediaRecorder(textureView) //配置MediaRecorder  因為每一次停止錄製後呼叫重置方法後都會取消配置,所以每一次開始錄製都需要重新配置一次
                                mMediaRecorder!!.prepare() //準備
                                mMediaRecorder!!.start() //開啟
                                isRecording = true
                                started = true
                            } catch (e: Exception) {
                                ToastUtils.showShort(e.message)
                                e.printStackTrace()
                            }
                        }

                        override fun onDenied() {
                            ToastUtils.showShort("請開啟錄影許可權")
                        }

                    })
                }

                override fun onDenied() {
                    ToastUtils.showShort("請開啟錄音許可權")
                }

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

    fun stopRecorder() {
        mMediaRecorder!!.stop() //暫停
        mMediaRecorder!!.reset() //重置 重置後將進入空閒狀態,再次啟動錄製需要重新配置MediaRecorder
        camera?.stopPreview()
        camera?.release()
        camera?.lock()
        isRecording = false
        started = false
    }

    fun pauseRecorder() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder!!.pause() //暫停
            camera?.stopPreview()
            isRecording = false
        }
    }

    fun resumeRecorder() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder!!.resume() //恢復
            camera?.startPreview()
            isRecording = true
        }
    }

    fun destroy() {
        if (mMediaRecorder != null) {
            mMediaRecorder!!.stop()
            mMediaRecorder!!.release() //釋放 釋放之前需要先呼叫stop()
            // 攝像頭關閉預覽和釋放
            camera?.stopPreview()
            camera?.release()
            mMediaRecorder = null
        }
        instance = null
    }

}

相關文章