本文着重介绍媒体信息解释器 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()
}
}
}
}