序言
一个音视频文件是由音频和视频组成的,Android 提供了 MediaExtractor 和 MediaMuxer 类,用来把音频或视频单独抽取出来,然后合成新的视频。
我们分别看一下 API 的使用,实现分离 MP4 视频文件,然后再合成分离的音视频功能。
MediaExtractor 的使用主要有这么几步:
- 设置数据源
- 获取通道数,找到想要的轨道和通道格式,并切换过去
- 循环读取每帧的样本数据
- 完成后释放资源
MediaMuxer 的使用和 MediaExtractor 类似:
- 设置目标文件路径和音视频格式
- 添加感兴趣的通道
- 开始合成,循环写入样本数据
- 完成后释放资源
下面是个提取并合成视频的示例,MediaExtractor 和 MediaMuxer 一起使用,相当于把原视频的声音通道去掉,生成一个无声的视频文件。
// 分离视频的纯视频 输入视频 input.mp4,分离保存的视频 output_video.mp4
private void extractVideo() {
MediaExtractor mediaExtractor = new MediaExtractor();
MediaMuxer mediaMuxer = null;
try {
File fileDir = FileUtils.getFileDir(this);
// 设置视频源
mediaExtractor.setDataSource(new File(fileDir, VIDEO_SOURCE_PATH).getAbsolutePath());
int videoIndex = -1;
int maxInputSize = -1;
int frameRate = -1;
// 获取数据源的轨道数
int trackCount = mediaExtractor.getTrackCount();
// 循环轨道数,找到我们想要的视频轨
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
// 主要描述mime类型的媒体格式
String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
// //找到视频轨
if (mimeType.startsWith("video/")) {
videoIndex = i;
// 获取视频最大的输入大小
maxInputSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
// 获取视频的帧率
frameRate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
}
}
logger.info("frameRate:{}, maxInputSize:{}", frameRate, maxInputSize);
//切换视频的信道
mediaExtractor.selectTrack(videoIndex);
MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoIndex);
mediaMuxer = new MediaMuxer(new File(fileDir, OUTPUT_VIDEO).getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
//将视频轨添加到 MediaMuxer,并返回新的轨道
int trackIndex = mediaMuxer.addTrack(trackFormat);
ByteBuffer byteBuffer = ByteBuffer.allocate(maxInputSize);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
// 开始合成
mediaMuxer.start();
while (true) {
// 检索当前编码的样本并将其存储在字节缓冲区中
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 如果没有可获取的样本则退出循环
if (readSampleSize < 0) {
mediaExtractor.unselectTrack(videoIndex);
break;
}
// 设置样本编码信息
bufferInfo.size = readSampleSize;
bufferInfo.offset = 0;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.presentationTimeUs += 1000 * 1000 / frameRate;
//写入样本数据
mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
//推进到下一个样本,类似快进
mediaExtractor.advance();
}
logger.info("finish extract video");
} catch (IOException e) {
logger.error(e);
} finally {
if (mediaMuxer != null) {
mediaMuxer.stop();
mediaMuxer.release();
}
mediaExtractor.release();
}
}
同样地,分离音频和合成音视频的过程和上面差不多,代码在 GitHub 上,欢迎各位参阅。
【附录】
需要资料的朋友可以加入Android架构交流QQ群聊:513088520
点击链接加入群聊【Android移动架构总群】:加入群聊
获取免费学习视频,学习大纲另外还有像高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等Android高阶开发资料免费分享。
网友评论