研究Android音视频-1-基础知识使用MediaExtractor和MediaMuxer

解决的主要问题

1. 分离MP4为raw数据,aac和h264

2. 分离MP4为音频和视频

3. 将音频和视频重新封装为MP4

MediaExtractor:音视频数据分离器,用于解复用(demuxer)

获取解复用后的信息

//查看视频信息
//解复用器 demuxer
val mediaExtractor = MediaExtractor();
mediaExtractor.setDataSource(videoPath);
val numTracks = mediaExtractor.trackCount;
println("extrarot streams $numTracks;")
for (i in 0 until numTracks) {
    val mediaFormat = mediaExtractor.getTrackFormat(i)
    val mine = mediaFormat.getString(MediaFormat.KEY_MIME)
    println("stream $i mime: $mine")
}
//打印信息
2021-04-16 18:30:21.099 /System.out: extrarot streams 2;
2021-04-16 18:30:21.099 /System.out: stream 0 mime: video/avc
2021-04-16 18:30:21.100 /System.out: stream 1 mime: audio/mp4a-latm
复制代码

获取音频视频

var audioFrameIndex = -1
var videoFrameIndex = -1
for (i in 0 until numTracks) {
    val mediaFormat = mediaExtractor.getTrackFormat(i)
    val mine = mediaFormat.getString(MediaFormat.KEY_MIME)//获取MIME格式内容
    println("stream $i [mime: $mine]")
    if (mine?.startsWith("video/")!!) {
        videoFrameIndex = i
    }
    if (mine.startsWith("audio/")) {
        audioFrameIndex = i
    }
}
复制代码

实例一:解析并保存音频信息,<原始数据> 存下来的数据无法处理啊,

if (audioFrameIndex != -1) {
 mediaExtractor.selectTrack(audioFrameIndex)
 val outAudioFile = File("$filesDir/a1.aac")
 outAudioFile.deleteOnExit()
 val fos = FileOutputStream(outAudioFile)
 val byteBuffer = ByteBuffer.allocate(1024 * 1024)
 val byteArray = ByteArray(byteBuffer.capacity());
 while (true) {
     val readCount = mediaExtractor.readSampleData(byteBuffer, 0)
     if (readCount < 0) {
         println("读取无音频数据,停止!")
         break
     }
     println("写入音频数据 [$readCount] 字节")
     byteBuffer.get(byteArray, 0, readCount)
     fos.write(byteArray, 0, readCount)
     byteBuffer.clear()
     mediaExtractor.advance()
     if (!mediaExtractor.advance()) {
         println("切换至下一读取点后,无数据,停止!")
         break
     }
 }
 fos.flush()
 fos.close()
 mediaExtractor.unselectTrack(audioFrameIndex)
}
复制代码

实例二:解析并保存视频信息,<原始数据> 存下来的数据无法处理啊,

if (videoFrameIndex != -1) {
 mediaExtractor.selectTrack(videoFrameIndex)
 val outVideoFile = File("$filesDir/a1.h264")
 outVideoFile.deleteOnExit()
 val fos = FileOutputStream(outVideoFile)
 val byteBuffer = ByteBuffer.allocate(1024 * 1024)
 val byteArray = ByteArray(byteBuffer.capacity());
 while (true) {
     val readCount = mediaExtractor.readSampleData(byteBuffer, 0)
     if (readCount < 0) {
         println("读取无视频数据,停止!")
         break
     }
     println("写入视频数据 [$readCount] 字节")
     byteBuffer.get(byteArray, 0, readCount)
     fos.write(byteArray, 0, readCount)
     byteBuffer.clear()
     mediaExtractor.advance()
     if (!mediaExtractor.advance()) {
         println("切换至下一读取点后,无视频数据,停止!")
         break
     }
 }
 fos.flush()
 fos.close()
 mediaExtractor.unselectTrack(videoFrameIndex)
}
复制代码

MediaMuxer解视频复用,分别解出音频mp3和视频mp4

//实例三:
val path2Save = "$filesDir/test.mp3"
mediaExtractor.extractorMedia(audioFrameIndex, path2Save)

val path2Save = "$filesDir/test.mp4"
mediaExtractor.extractorMedia(videoFrameIndex, path2Save)

/**
 * 分离对应轨道中的媒体,并保存到指定路径
 */
fun MediaExtractor.extractorMedia(frameIndex: Int, path2Save: String) {
    val mediaMuxer = MediaMuxer(path2Save, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
    mediaMuxer.demuxerMedia(this, frameIndex)
    mediaMuxer.release()
}

/**
 * 将分离器中指定index数据,写入指定混合器
 */
fun MediaMuxer.demuxerMedia(extractor: MediaExtractor, frameIndex: Int) {
    extractor.selectTrack(frameIndex)
    val format = extractor.getTrackFormat(frameIndex)

    val trackIndex = addTrack(format)
    start()
    writeMediaBuffer(extractor, format, trackIndex)

    extractor.unselectTrack(frameIndex)
}

/**
 * 真正写入的方法
 */
private fun MediaMuxer.writeMediaBuffer(
    extractor: MediaExtractor,
    format: MediaFormat,
    trackIndex: Int
) {
    val maxMediaBufferCount: Int = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
    val byteBuffer = ByteBuffer.allocate(maxMediaBufferCount)
    val bufferInfo = MediaCodec.BufferInfo()
    while (true) {
        val readSampleDataSize = extractor.readSampleData(byteBuffer, 0)
        if (readSampleDataSize < 0) {
//            println("index [$trackIndex] 读取无媒体数据,停止!")
            break
        }
        bufferInfo.size = readSampleDataSize
        bufferInfo.offset = 0
        bufferInfo.presentationTimeUs = extractor.sampleTime
        bufferInfo.flags = extractor.sampleFlags
//        println("index [$trackIndex] write data:[$readSampleDataSize]")
        writeSampleData(trackIndex, byteBuffer, bufferInfo)
        if (!extractor.advance()) {
//            println("index [$trackIndex] 切换至下一读取点后,无媒体数据,停止!")
            break
        }
    }
}
复制代码

MediaMuxer解视频复用,分别将上一步解出音频mp3和视频mp4合并为一个新的mp4

//实例四:
fun muxerAudioAndVideo(audioPath: String, videoPath: String, outPath: String) {
    //合并解析好的音频和视频为mp4

    val audioExtractor = MediaExtractor();
    audioExtractor.setDataSource(audioPath)
    val audioFrameIndex = audioExtractor.findTargetStreamIndex("audio/")

    val videoExtractor = MediaExtractor()
    videoExtractor.setDataSource(videoPath)
    val videoFrameIndex = videoExtractor.findTargetStreamIndex("video/")

    if (audioFrameIndex != -1 && videoFrameIndex != -1) {
        val mediaMuxer =
        MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        mediaMuxer.muxerAudioAndVideo(
            audioExtractor,
            videoExtractor,
            audioFrameIndex,
            videoFrameIndex
        )
        mediaMuxer.release()
    } else {
        println("输入音频[$audioPath: $audioFrameIndex]或视频[$videoPath: $videoFrameIndex]有异常,请检查")
    }

    audioExtractor.release()
    videoExtractor.release()
}

/**
 * 是否解复用出指定header,video/或audio/
 */
fun MediaExtractor.findTargetStreamIndex(header: String): Int {
    val trackCount = trackCount
    for (i in 0 until trackCount) {
        val format = getTrackFormat(i)
        val mine = format.getString(MediaFormat.KEY_MIME)
        if (mine?.startsWith(header)!!) {
            println("found target stream [$header] index[$i]")
            return i
        }
    }
    println("not found target stream [$header]")
    return -1
}

/**
 * 合并两路流
 */
fun MediaMuxer.muxerAudioAndVideo(
    audioExtractor: MediaExtractor,
    videoExtractor: MediaExtractor,
    audioFrameIndex: Int,
    videoFrameIndex: Int
) {

    var audioTrackIndex = -1
    audioExtractor.selectTrack(audioFrameIndex)
    var audioFormat = audioExtractor.getTrackFormat(audioFrameIndex)

    var videoTrackIndex = -1
    videoExtractor.selectTrack(videoFrameIndex)
    val videoFormat = videoExtractor.getTrackFormat(videoFrameIndex)

    audioTrackIndex = addTrack(audioFormat)//追踪音频信道
    videoTrackIndex = addTrack(videoFormat)//追踪视频信道
    start()//开始准备混合
    //写入音频流
    writeMediaBuffer(audioExtractor, audioFormat, audioTrackIndex)
    //写入视频流
    writeMediaBuffer(videoExtractor, videoFormat, videoTrackIndex)

    audioExtractor.unselectTrack(audioFrameIndex)
    videoExtractor.unselectTrack(videoFrameIndex)
    release()
}
复制代码

分类:
Android
标签: