• 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()
  • 相关阅读:
    【Element-ui】el-slider滑块自定义样式,实时显示选中两点的数值在上方,左右显示0到100
    读书笔记---蛤蟆先生去看心理医生
    Springboot毕设项目个性化学习推荐网站ua750(java+VUE+Mybatis+Maven+Mysql)
    linux网络编程之TCP协议编程
    AI初识--LLM、ollama、llama都是些个啥?
    jvm虚拟机之类加载器子系统
    SpringMVC ---- @RequestMapping注解
    【JavaEE】Servlet API 详解(HttpServletResponse类方法演示、实现自动刷新、实现自动重定向)
    Window10运行onnxruntime报错的处理
    EventEmitter3在vue中的使用
  • 原文地址:https://blog.csdn.net/android_cai_niao/article/details/126369098