Android 音视频知识点(四)

135 阅读1分钟

本文着重介绍媒体信息解释器 MediaExtractor,组合器 MediaMuxer 的使用

1、MediaExtractor 可以获取视频、音频的文件内部信息,如帧率、声道、mime类型、码率 等

fun getMediaInfo() {
        var sb = StringBuilder()
        val file = File(ROOT_PATH + File.separator + ORIGIN_VIDEO_FILENAME)

        val mediaExtractor = MediaExtractor()
        sb.append("视频名称: ${file.name}").append("\n")
        mediaExtractor.setDataSource(ROOT_PATH + File.separator + ORIGIN_VIDEO_FILENAME)
        val count = mediaExtractor.trackCount
        sb.append("视频轨道数:${count}  ").append("\n")
        for (i in 0 until  count) {
            val format = mediaExtractor.getTrackFormat(i)
            // 获取 mime
            format.getString(MediaFormat.KEY_MIME)?.let {mime ->
                sb.append(mime).append("\n")
                // 视频轨
                if (mime.startsWith("video")) {
                    mVideoTrackId = i
                    mVideoFormat = format
                } else if (mime.startsWith("audio")) {
                    mAudiotrackId = i
                    mAudioFormat = format
                }
            }
        }

        mVideoFormat?.let {mVideoFormat ->
            val width = mVideoFormat.getInteger(MediaFormat.KEY_WIDTH)
            val height = mVideoFormat.getInteger(MediaFormat.KEY_HEIGHT)
            sb.append("视频宽高:${width} x ${height}").append("\n")

            val aLong = mVideoFormat.getLong(MediaFormat.KEY_DURATION)
            sb.append("视频播放总时长: ${(aLong / 1000 / 1000 / 60)}分钟").append("\n")

            val frameRate = mVideoFormat.getInteger(MediaFormat.KEY_FRAME_RATE)
            sb.append("视频帧率: ${frameRate}fps").append("\n")
        }

        mAudioFormat?.let { mAudioFormat ->
            val aLong = mAudioFormat.getLong(MediaFormat.KEY_DURATION)
            sb.append("音频时长:${(aLong / 1000 / 1000 / 60)}分钟").append("\n")

            val audioRate = mAudioFormat.getInteger(MediaFormat.KEY_BIT_RATE)
            sb.append("音频码率:${audioRate}").append("\n")
        }

        video_info.setText(sb.toString())
    }

2、MediaMuxer 可以通过获取到的视频、音频信息,再次组合成一个新的视频、音频文件 合成新文件的过程中,是IO耗时操作,需要在子线程中执行。要区分视频、音频,分别进行合成。

object MediaExtractAndMux {

    fun extractAndMuxVideo(inputPath: String, outputPath: String) {
        CoroutineScope(Dispatchers.IO).launch {
            Log.d("Mux", "开始处理,inputpath: $inputPath  outputPath: $outputPath")

            val extractor = MediaExtractor()
            val muxer: MediaMuxer

            try {
                extractor.setDataSource(inputPath)

                muxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

                var videoTrackIndex = -1
                var audioTrackIndex = -1
                var newVideoTezckIndex = -1
                var newAudioTrackIndex = -1

                for (i in 0 until extractor.trackCount) {
                    val format = extractor.getTrackFormat(i)
                    val mime = format.getString(MediaFormat.KEY_MIME)

                    if (mime?.startsWith("video/") == true) {
                        videoTrackIndex = i
                        newVideoTezckIndex = muxer.addTrack(format)
                    } else if (mime?.startsWith("audio/") == true) {
                        audioTrackIndex = i
                        newAudioTrackIndex = muxer.addTrack(format)
                    }
                }

                muxer.start()

                val bufferSize = 1 * 1014 * 1024
                val buffer = ByteBuffer.allocate(bufferSize)
                val bufferInfo = MediaCodec.BufferInfo()

                if (videoTrackIndex != -1) {
                    extractor.selectTrack(videoTrackIndex)
                    while (true) {
                        buffer.clear()
                        val sampleSize = extractor.readSampleData(buffer, 0)
                        if (sampleSize < 0) {
                            break
                        }

                        bufferInfo.offset = 0
                        bufferInfo.size = sampleSize
                        bufferInfo.presentationTimeUs = extractor.sampleTime
                        bufferInfo.flags = extractor.sampleFlags

                        muxer.writeSampleData(newVideoTezckIndex, buffer, bufferInfo)
                        extractor.advance()
                    }
                    extractor.unselectTrack(videoTrackIndex)
                }

                if (audioTrackIndex != -1) {
                    extractor.selectTrack(audioTrackIndex)
                    while (true) {
                        buffer.clear()
                        val sampleSize = extractor.readSampleData(buffer, 0)
                        if (sampleSize < 0) {
                            break
                        }

                        bufferInfo.offset = 0
                        bufferInfo.size = sampleSize
                        bufferInfo.presentationTimeUs = extractor.sampleTime
                        bufferInfo.flags = extractor.sampleFlags

                        muxer.writeSampleData(newAudioTrackIndex, buffer, bufferInfo)
                        extractor.advance()
                    }
                    extractor.unselectTrack(audioTrackIndex)
                }

                muxer.stop()
                muxer.release()
                extractor.release()

                Log.d("Mux", "处理完成,保存为: $outputPath")

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

}