【Android音视频学习之路(五)】MediaExtractor和MediaMuxer讲解

【Android音视频学习之路(一)】如何在 Android 平台绘制一张图片

【Android音视频学习之路(二)】AudioRecord API详解及应用

【Android音视频学习之路(三)】AudioTrack 使用与详解

【Android音视频学习之路(四)】Camera 的使用

一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor、MediaMuxer把音频或视频给
单独抽取出来,抽取出来的音频和视频能单独播放;

一、MediaExtractor API介绍

MediaExtractor的作用是把音频和视频的数据进行分离。

主要API介绍:

  • setDataSource(String path):即可以设置本地文件又可以设置网络文件
  • getTrackCount():得到源文件通道数
  • getTrackFormat(int index):获取指定(index)的通道格式
  • getSampleTime():返回当前的时间戳
  • readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的数据按偏移量读取到ByteBuffer中;
  • advance():读取下一帧数据
  • release(): 读取结束后释放资源

使用示例:

private fun onMediaExtractor() {
        val path =
            Environment.getExternalStorageDirectory().absolutePath + File.separator + "test.mp4"
        val extractor = MediaExtractor()
        //1.设置数据源
        extractor.setDataSource(path)
        val numTracks = extractor.trackCount
        ALog.e("xiao", "源文件通道数: $numTracks")
        for (i in 0 until numTracks) {
            ALog.e("xiao", "*******")
            //2.分离轨道
            val format = extractor.getTrackFormat(i)
            val mine = format.getString(MediaFormat.KEY_MIME)
            ALog.e("xiao", "mime: $mine")
            //3.选择轨道
            extractor.selectTrack(i)
            val inputBuffer = ByteBuffer.allocate(format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)) //获取视频缓存输出的最大大小
            //4.读取数据
            while (true) {
                val readSampleDataSize = extractor.readSampleData(inputBuffer, 0)
                if (readSampleDataSize < 0) break
                ALog.e("xiao", "trackIndex: ${extractor.sampleTrackIndex}")
                ALog.e("xiao", "presentationTimeUs: ${extractor.sampleTime}")
                // 其他操作省略
                //5.下一帧
                extractor.advance()
            }
        }
        //6.释放
        extractor.release()
    }

复制代码

二、MediaMuxer API介绍

MediaMuxer的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件。

相关API介绍:

  • MediaMuxer(String path, int format):path:输出文件的名称 format:输出文件的格式;当前只支持MP4格式;
  • addTrack(MediaFormat format):添加通道;我们更多的是使用MediaCodec.getOutpurForma()或Extractor.getTrackFormat(int index)来获取MediaFormat;也可以自己创建;
  • start():开始合成文件
  • writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把
  • ByteBuffer中的数据写入到在构造器设置的文件中;
  • stop():停止合成文件
  • release():释放资源

使用示例:

  val muxer = MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        val audioFormat = MediaFormat(...) 
        val videoFormat = MediaFormat(...) 
        val audioTrackIndex = muxer.addTrack(audioFormat)
        val videoTrackIndex = muxer.addTrack(videoFormat)
        val inputBuffer = ByteBuffer.allocate(1024 * 8 *100) //随便填的一个,需要获取
        val finished = false
        val bufferInfo = BufferInfo()
        muxer.start()
        while(!finished) {
            finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo);
            if (!finished) {
                val currentTrackIndex = if (isAudioSample) audioTrackIndex else videoTrackIndex
                muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo);
            }
        }
        muxer.stop();
        muxer.release();
复制代码

三、使用情境

3.1 从MP4文件中提取视频并生成新的视频文件

  //从MP4文件中提取视频并生成新的视频文件
    private fun ex(): Boolean {
        requestPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
        val sourcePath =
            Environment.getExternalStorageDirectory().absolutePath + File.separator + "test.mp4"
        val outputPath =
            Environment.getExternalStorageDirectory().absolutePath + File.separator + "output.mp4"
        val mediaExtractor = MediaExtractor()
        var mediaMuxer: MediaMuxer? = null
        mediaExtractor.setDataSource(sourcePath)
        var videoTrackIndex = -1
        var frameRate = 0
        val numTracks = mediaExtractor.trackCount
        for (i in 0 until numTracks) {
            val format = mediaExtractor.getTrackFormat(i)
            val mime = format.getString(MediaFormat.KEY_MIME) ?: continue
            if (!mime.startsWith("video/")) {
                continue
            }
            frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE)
            mediaExtractor.selectTrack(i)
            mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
            videoTrackIndex = mediaMuxer.addTrack(format)
            mediaMuxer.start()
        }

        val _mediaMuxer = mediaMuxer ?: return false
        val info = MediaCodec.BufferInfo()
        info.presentationTimeUs = 0
        val buffer = ByteBuffer.allocate(500 * 1024)
        var sampleSize = 0
        while (true) {
            sampleSize = mediaExtractor.readSampleData(buffer, 0)
            if (sampleSize <= 0) break

            info.offset = 0
            info.size = sampleSize
            info.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME
            info.presentationTimeUs += 1000 * 1000 / frameRate
            _mediaMuxer.writeSampleData(videoTrackIndex, buffer, info)
            mediaExtractor.advance()
        }

        mediaExtractor.release()

        _mediaMuxer.stop()
        _mediaMuxer.release()

        return true
    }
复制代码

源码地址

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享