当前位置:   article > 正文

MediaCodec同步异步使用_mediacodec异步编码

mediacodec异步编码

MediaCodec同步使用

为了简单,这里使用无预览的Camera视频采集,然后通过MediaCodec编码为H264并保存文件,界面只有两个按钮,如下:
在这里插入图片描述
MainActivity实现如下:

class MainActivity : AppCompatActivity() {

    private var camera: Camera? = null
    private var h264EncoderThread: H264EncoderThread? = null
    private val surfaceTexture: SurfaceTexture by lazy { SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES) }
    private val videoWidth = 640
    private val videoHeight = 480

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        PermissionUtil.registerForActivityResult(this)

        val openCameraButton: Button = findViewById(R.id.openCameraButton)
        val closeCameraButton: Button = findViewById(R.id.closeCameraButton)

        openCameraButton.setOnClickListener {
            PermissionUtil.requestPermission(this) {
                Timber.i("得到所有的权限了")
                openCamera()
            }
        }

        closeCameraButton.setOnClickListener { closeCamera() }

    }

    private fun openCamera() {
        if (camera != null) {
            return
        }

        h264EncoderThread = H264EncoderThread(videoWidth, videoHeight)
        h264EncoderThread?.start()
        camera = Camera.open()
        camera?.parameters = camera?.parameters?.apply {
            setPreviewSize(videoWidth, videoHeight)
            setPictureSize(videoWidth, videoHeight)
            previewFormat = ImageFormat.NV21
            previewFrameRate = 25
            focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO
        }
        camera?.setPreviewTexture(surfaceTexture)
        camera?.setPreviewCallback { data, _ ->
            h264EncoderThread?.addYUVBytes(data)
        }
        camera?.startPreview()
    }

    private fun closeCamera() {
        camera?.setPreviewCallback(null)
        camera?.stopPreview()
        camera?.release()
        camera = null
        h264EncoderThread?.close()
        h264EncoderThread = null
    }

    override fun onDestroy() {
        super.onDestroy()
        closeCamera()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

代码也很简单,就两个主要函数,openCamera()closeCamera(),需要注意的是,在打开摄像头之前,需要先申请权限。

H264编码是一个耗时操作,所以封装了一个线程:H264EncoderThread,实现如下:

class H264EncoderThread(videoWidth: Int, videoHeight: Int) : Thread(H264EncoderThread::class.java.simpleName) {

    private val mH264Encoder = H264Encoder(videoWidth, videoHeight)
    private val yuvBytesQueue: BlockingQueue<ByteArray> = LinkedBlockingQueue(5)
    private var frameLossCount: Int = 0
    private var needRun = true

    fun addYUVBytes(yuvBytes: ByteArray) {
        if (needRun) {
            val offer = yuvBytesQueue.offer(yuvBytes)
            if (!offer) {
                Timber.i("丢帧:${++frameLossCount}帧")
            }
        }
    }

    override fun run() {
        try {
            while (needRun) {
                yuvBytesQueue.poll(30L, TimeUnit.MILLISECONDS)?.let {
                    if (needRun) {
                        mH264Encoder.encodeYuvToH264(it)
                    }
                }
            }
        } catch (e: Exception) {
            Timber.e(e,"把YUV编码为H264时出现异常")
        }
    }

    fun close() {
        try {
            needRun = false
            mH264Encoder.close()
            Timber.i("close()已执行")
        } catch (e: Exception) {
            Timber.e(e, "关闭H264编码器时出现异常")
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

H264Encoder的实现如下:

class H264Encoder(videoWidth: Int, videoHeight: Int) {

    private val mMediaCodec: MediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
    private var mBufferInfo = MediaCodec.BufferInfo()
    private val _1K = 1000
    private val _1M = _1K * 1000
    private val ySize = videoWidth * videoHeight // Y分量大小
    private val oneFrameSize = (ySize * 3) shr 1 // 一帧画面大小
    private var index: Int = 0
    private var temp: Byte = 0
    private val h264Saver: H264Saver by lazy { H264Saver() }
    private var isPutEmptyArray = false

    init {
        val mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoWidth, videoHeight)
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible)
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR) // 设置码率为动态码率,默认也是动态码率
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, getBitrate(videoWidth, videoHeight)) // 码率(即比特率), 官方Demo这里的1000即1kbps,1000_000即1mpbs
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25)      // 帧速(25帧/秒)
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) // I帧间隔(1帧/2秒),因为帧 1秒出25帧,2秒就出50帧,所以I帧间隔为2的话就是每50帧出一个关键帧

        // 第二个参数用于显示解码器的视频内容,第三个参数为编解码器的解密参数,第四个参数为指定为编码器
        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        mMediaCodec.start()
        Timber.i("实际使用的H264编码器:${mMediaCodec.codecInfo.name}")
    }

    /** 把NV21格式的Yuv数据编码为H264数据 */
    fun encodeYuvToH264(yuvBytes: ByteArray) {
        if (isPutEmptyArray) return // 已经放入了空数组(用于结束编码),则不处理,因为多线程,所以有可能释放的时候还有数据扔进来编码的

        val flags = if (yuvBytes.isEmpty()) {
            isPutEmptyArray = true
            MediaCodec.BUFFER_FLAG_END_OF_STREAM
        } else {
            nv21ToNv12(yuvBytes)
            0
        }

        val inputBufferIndex = mMediaCodec.dequeueInputBuffer(10_000) // 如果10毫秒都等不到可用缓冲,则这一帧的yuv数据将丢掉。谷歌官方Demo也是用的这个值
        if (inputBufferIndex >= 0) {
            val inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex) ?: return
            inputBuffer.put(yuvBytes, 0, yuvBytes.size) // 官方Demo在调用put之前会先调用inputBuffer.clear(),实际上并不需要
            mMediaCodec.queueInputBuffer(inputBufferIndex,0, yuvBytes.size,System.nanoTime() / 1000, flags)
        }

        // 从MediaCodec中取出编好的H264数据并使用(如保存、发送)
        var outputBufferIndex: Int
        while (mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10_000).also { outputBufferIndex = it } >= 0) {
            val outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex) ?: return
            when (mBufferInfo.flags) {
                MediaCodec.BUFFER_FLAG_CODEC_CONFIG, MediaCodec.BUFFER_FLAG_KEY_FRAME, 0 -> { // 配置帧、关键帧、普通帧
                    h264Saver.write(outputBuffer)
                    mMediaCodec.releaseOutputBuffer(outputBufferIndex, false)
                }
                MediaCodec.BUFFER_FLAG_END_OF_STREAM -> {
                    Timber.i("已经到达流的终点了")
                    mMediaCodec.releaseOutputBuffer(outputBufferIndex, false)
                    releaseMediaCodec()
                    break // 退出循环,无需再去获取编码的数据。
                }
            }
        }
    }

    private fun getBitrate(videoWidth: Int, videoHeight: Int): Int = when {
        (videoWidth == 1920 && videoHeight == 1080) || (videoWidth == 1080 && videoHeight == 1920) -> _1M * 3 shr 1
        (videoWidth == 1280 && videoHeight == 720) || (videoWidth == 720 && videoHeight == 1280) -> _1M
        (videoWidth == 640 && videoHeight == 480) || (videoWidth == 480 && videoHeight == 640) -> _1K * 500
        (videoWidth == 352 && videoHeight == 288) || (videoWidth == 288 && videoHeight == 352) -> _1K * 300
        else -> _1M * 1
    }

    private fun nv21ToNv12(yuvBytes: ByteArray) {
        index = ySize
        while (index < oneFrameSize) {
            temp = yuvBytes[index]
            yuvBytes[index] = yuvBytes[index + 1]
            yuvBytes[index + 1] = temp
            index += 2
        }
    }

    /** 关闭编码器 */
    fun close() {
        Timber.i("close")
        encodeYuvToH264(ByteArray(0))
    }

    private fun releaseMediaCodec() {
        try {
            mMediaCodec.stop()
        } catch (e: Exception) {
            Timber.e(e,"别慌,正常停止${H264Encoder::class.java.simpleName}时出现的异常!")
        }

        try {
            mMediaCodec.release()
        } catch (e: Exception) {
            Timber.e(e,"别慌,正常释放${H264Encoder::class.java.simpleName}时出现的异常!")
        }

        h264Saver.close()
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

H264Saver的实现如下:

class H264Saver {

    private var fileChannel : FileChannel? = null

    init {
        @SuppressLint("SdCardPath")
        val fileDir = File("/sdcard/-0a/")
        var exists = fileDir.exists()

        if (!exists) {
            exists = fileDir.mkdir()
        }

        if (exists) {
            val fileName = "${DateFormat.format("yyyy_MM_dd_HHmmss", System.currentTimeMillis())}.h264"
            fileChannel = FileOutputStream(File(fileDir, fileName)).channel
        }
    }

    fun write(byteBuffer: ByteBuffer) {
        fileChannel?.write(byteBuffer)
    }

    fun close() {
        val channel = fileChannel
        if (channel != null) {
            try {
                channel.close()
            } catch (e: Exception) {
                Timber.e(e, "关闭fileChannel时出现异常")
            }
            fileChannel = null
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

完整示例代码:https://gitee.com/daizhufei/MediaCodecSynchronous

生成的h264文件是裸流,可以使用VLC播放器进行播放。

MediaCodec异步使用

异步方式和同步方式基本相同,大同小异,代码如下:

H264Encoder实现如下:

class H264Encoder(videoWidth: Int, videoHeight: Int) : MediaCodec.Callback() {

    private val yuvBytesQueue: BlockingQueue<ByteArray> = LinkedBlockingQueue(5)
    private val mMediaCodec: MediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
    private val _1K = 1000
    private val _1M = _1K * 1000
    private val ySize = videoWidth * videoHeight // Y分量大小
    private val oneFrameSize = (ySize * 3) shr 1 // 一帧画面大小
    private var index: Int = 0
    private var temp: Byte = 0
    /** 表示已经调用了close()方法 */
    @Volatile // 因为多线程访问这个变量,所以加上这个注解
    private var calledCloseMethod = false
    private val h264Saver: H264Saver by lazy { H264Saver() }
    private var frameLossCount: Int = 0
    private var isPutEmptyArray = false
    private var mHandlerThread: HandlerThread? = null

    init {
        val mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoWidth, videoHeight)
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible)
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR) // 设置码率为动态码率,默认也是动态码率
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, getBitrate(videoWidth, videoHeight)) // 码率(即比特率), 官方Demo这里的1000即1kbps,1000_000即1mpbs
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25)      // 帧速(25帧/秒)
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) // I帧间隔(1帧/2秒),因为帧 1秒出25帧,2秒就出50帧,所以I帧间隔为2的话就是每50帧出一个关键帧

        // 第二个参数用于显示解码器的视频内容,第三个参数为编解码器的解密参数,第四个参数为指定为编码器
        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        val handlerThread = HandlerThread("H264EncoderThread").apply { start() }
        mHandlerThread = handlerThread
        val handler = Handler(handlerThread.looper)
        mMediaCodec.setCallback(this, handler) // 传入一个子线程的Handler,以便回调函数可以运行在子线程,如果不传默认运行在主线程
        mMediaCodec.start()
        Timber.i("实际使用的H264编码器:${mMediaCodec.codecInfo.name}")
    }

    override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {
        if (isPutEmptyArray) return // 已经放入了空数组(用于结束编码),则不处理,因为多线程,所以有可能释放的时候还有数据扔进来编码的
        val inputBuffer: ByteBuffer = codec.getInputBuffer(index) ?: return
        try {
            while (true) {
                var yuvBytes = yuvBytesQueue.poll(30L, TimeUnit.MILLISECONDS)
                if (yuvBytes == null && !calledCloseMethod) {
                    continue // 如果已经超时了从队列中取不到数据,并且没有调用close函数,则继续再从队列中再取数据
                }

                val flags = if (calledCloseMethod) {
                    isPutEmptyArray = true
                    Timber.i("已放入空数组")
                    // 如果已经调用了关闭函数,则使用结束flag标志,并放入一个空数组
                    yuvBytes = ByteArray(0)
                    MediaCodec.BUFFER_FLAG_END_OF_STREAM
                } else {
                    nv21ToNv12(yuvBytes)
                    0
                }

                inputBuffer.put(yuvBytes, 0, yuvBytes.size)
                codec.queueInputBuffer(index, 0, yuvBytes.size, System.nanoTime() / 1000, flags)
                break
            }
        } catch (e: Exception) {
            Timber.e(e,"把YUV编码为H264时出现异常")
        }
    }

    override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) {
        val outputBuffer: ByteBuffer = codec.getOutputBuffer(index) ?: return
        when (info.flags) {
            MediaCodec.BUFFER_FLAG_CODEC_CONFIG, MediaCodec.BUFFER_FLAG_KEY_FRAME, 0 -> { // 配置帧、关键帧、普通帧
                h264Saver.write(outputBuffer)
                mMediaCodec.releaseOutputBuffer(index, false)
            }
            MediaCodec.BUFFER_FLAG_END_OF_STREAM -> {
                Timber.i("已经到达流的终点了")
                mMediaCodec.releaseOutputBuffer(index, false)
                releaseMediaCodec()
            }
        }
    }

    override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
        Timber.e(e, "H264编码器出现异常")
    }

    override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {

    }

    fun addYUVBytes(yuvBytes: ByteArray) {
        if (calledCloseMethod) {
            return
        }

        val offer = yuvBytesQueue.offer(yuvBytes)
        if (!offer) {
            Timber.i("丢帧:${++frameLossCount}帧")
        }
    }

    private fun getBitrate(videoWidth: Int, videoHeight: Int): Int = when {
        (videoWidth == 1920 && videoHeight == 1080) || (videoWidth == 1080 && videoHeight == 1920) -> _1M * 3 shr 1
        (videoWidth == 1280 && videoHeight == 720) || (videoWidth == 720 && videoHeight == 1280) -> _1M
        (videoWidth == 640 && videoHeight == 480) || (videoWidth == 480 && videoHeight == 640) -> _1K * 500
        (videoWidth == 352 && videoHeight == 288) || (videoWidth == 288 && videoHeight == 352) -> _1K * 300
        else -> _1M * 1
    }

    private fun nv21ToNv12(yuvBytes: ByteArray) {
        index = ySize
        while (index < oneFrameSize) {
            temp = yuvBytes[index]
            yuvBytes[index] = yuvBytes[index + 1]
            yuvBytes[index + 1] = temp
            index += 2
        }
    }

    /** 关闭编码器 */
    fun close() {
        Timber.i("close")
        if (calledCloseMethod) return // 预防关闭函数被调用两次
        calledCloseMethod = true
        //yuvBytesQueue.offer(ByteArray(0))不可取,万一队列满了,则无法存进去
    }

    private fun releaseMediaCodec() {
        Timber.i("releaseMediaCodec()")
        mHandlerThread?.quitSafely()
        mHandlerThread = null

        try {
            mMediaCodec.stop()
        } catch (e: Exception) {
            Timber.e(e,"别慌,正常停止${H264Encoder::class.java.simpleName}时出现的异常!")
        }

        try {
            mMediaCodec.release()
        } catch (e: Exception) {
            Timber.e(e,"别慌,正常释放${H264Encoder::class.java.simpleName}时出现的异常!")
        }

        h264Saver.close()
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

MainActivity实现如下:

class MainActivity : AppCompatActivity() {

    private var camera: Camera? = null
    private var h264Encoder: H264Encoder? = null
    private val surfaceTexture: SurfaceTexture by lazy { SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES) }
    private val videoWidth = 640
    private val videoHeight = 480

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        PermissionUtil.registerForActivityResult(this)

        val openCameraButton: Button = findViewById(R.id.openCameraButton)
        val closeCameraButton: Button = findViewById(R.id.closeCameraButton)

        openCameraButton.setOnClickListener {
            PermissionUtil.requestPermission(this) {
                Timber.i("得到所有的权限了")
                openCamera()
            }
        }

        closeCameraButton.setOnClickListener { closeCamera() }

    }

    private fun openCamera() {
        if (camera != null) {
            return
        }

        h264Encoder = H264Encoder(videoWidth, videoHeight)
        camera = Camera.open()
        camera?.parameters = camera?.parameters?.apply {
            setPreviewSize(videoWidth, videoHeight)
            setPictureSize(videoWidth, videoHeight)
            previewFormat = ImageFormat.NV21
            previewFrameRate = 25
            focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO
        }
        camera?.setPreviewTexture(surfaceTexture)
        camera?.setPreviewCallback { data, _ ->
            h264Encoder?.addYUVBytes(data)
        }
        camera?.startPreview()
    }

    private fun closeCamera() {
        camera?.setPreviewCallback(null)
        camera?.stopPreview()
        camera?.release()
        camera = null
        h264Encoder?.close()
        h264Encoder = null
    }

    override fun onDestroy() {
        super.onDestroy()
        closeCamera()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

完整示例代码:https://gitee.com/daizhufei/MediaCodecAsynchronous

相比之下,使用异步方式要比同步方式简单一些,异步方式需要注意的一些点:

  • 变量calledCloseMethod因为被多个线程访问,所以最好加上@Volatile注解
  • MediaCodec通过HandlerThread实现子线程的异步回调,跟主线程一样,HandlerThread.start()之后会是一个死循环,所以在我们结束编码的时候,记得结束这个死循环:mHandlerThread?.quitSafely()
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号