首页 文章详情

Android 音视频学习:MediaExtractor 和 MediaMuxer 讲解

字节流动 | 381 2021-12-22 23:33 0 0 0
UniSMS (合一短信)

原文链接: https://juejin.cn/post/6978908459982913567

一个音视频文件是由音频和视频组成的,我们可以通过 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
}

源码地址:https://gitee.com/vvwwvv/video-practice.git


-- END --


进技术交流群,扫码添加我的微信:Byte-Flow



获取视频教程和源码



推荐:

Android FFmpeg 实现带滤镜的微信小视频录制功能

全网最全的 Android 音视频和 OpenGL ES 干货,都在这了

抖音传送带特效是怎么实现的?

所有你想要的图片转场效果,都在这了

我用 OpenGL ES 给小姐姐做了几个抖音滤镜

good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter