解决的主要问题
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()
}
复制代码