1、MediaExtractor初探:什么是MediaExtractor?它能做什么?核心API概览。
各位同学,咱们今天来聊聊Android音视频开发里一个特别基础、但又特别重要的工具——MediaExtractor。
说实话,我刚开始接触音视频开发那会儿,面对一堆媒体文件格式,心里是有点懵的。MP4、MKV、FLV……这些容器里到底装了啥?怎么把里面的视频轨道、音频轨道单独拎出来?后来发现,Android SDK早就给我们准备好了MediaExtractor这个神器。
什么是MediaExtractor?
简单来说,MediaExtractor是一个媒体文件解封装器。它的核心任务,就是从多媒体容器文件中提取出独立的音视频数据轨道。
你想想看,一个MP4文件就像一个大箱子。箱子里混装着视频流、音频流、字幕流,甚至还有元数据。MediaExtractor就是那个能帮你把箱子里的东西分类整理好的工具。它能告诉你:
- 这个文件里有多少条轨道?
- 每条轨道是什么类型?(视频、音频、还是字幕?)
- 每条轨道的编码格式是什么?(H.264、AAC、MP3?)
- 关键帧在什么位置?
我个人习惯把MediaExtractor比作“媒体文件的拆解工”。它不负责解码,也不负责渲染,只负责把原始数据从容器里“拆”出来。
核心要点:MediaExtractor ≠ 解码器。它只做解封装,不做解码。解码是MediaCodec的事,后面章节我们会详细讲。
它能做什么?
MediaExtractor的应用场景其实非常广泛。我在项目中遇到过不少需求,都离不开它:
- 音视频分离:从视频文件里单独提取音频轨道,比如做铃声制作工具。
- 视频剪辑:定位到指定时间点,读取指定时长的数据,实现精准裁剪。
- 缩略图生成:定位到关键帧位置,提取一帧数据交给解码器生成缩略图。
- 媒体信息读取:获取视频的时长、分辨率、帧率、码率等元数据。
- 音视频同步处理:在播放器开发中,通过时间戳信息实现音画同步。
说白了,只要你想从媒体文件里“拿”出点什么,MediaExtractor几乎都是第一步。
核心API概览
MediaExtractor的API设计得很简洁,核心就那么几个方法。我给大家梳理一下:
| 方法 | 作用 | 使用频率 |
|---|---|---|
setDataSource(String path) |
设置要解析的媒体文件路径 | ★★★★★ |
getTrackCount() |
获取轨道总数 | ★★★★★ |
getTrackFormat(int index) |
获取指定轨道的格式信息(MediaFormat) | ★★★★★ |
selectTrack(int index) |
选中要读取的轨道 | ★★★★☆ |
unselectTrack(int index) |
取消选中轨道 | ★★☆☆☆ |
readSampleData(ByteBuffer, int) |
读取当前样本数据到缓冲区 | ★★★★★ |
advance() |
移动到下一个样本 | ★★★★★ |
getSampleTime() |
获取当前样本的时间戳(微秒) | ★★★★★ |
getSampleFlags() |
获取当前样本的标志位(如是否为关键帧) | ★★★★☆ |
seekTo(long timeUs, int mode) |
跳转到指定时间点附近 | ★★★★☆ |
release() |
释放资源 | ★★★★★ |
小技巧:我建议你在使用完MediaExtractor后,一定要在finally块或者try-with-resources中调用release()。曾经有个同事忘记释放,导致文件句柄泄漏,排查了好久才发现问题。
核心工作流程
MediaExtractor的使用流程其实很固定,我总结了一个四步法:
- 创建并设置数据源:new MediaExtractor(),然后setDataSource()。
- 遍历轨道,选择目标轨道:getTrackCount()循环,getTrackFormat()判断类型,selectTrack()选中。
- 循环读取样本数据:readSampleData()读取,处理完后advance()跳到下一个。
- 释放资源:release()收尾。
嗯,这里要注意:一次只能选中一条轨道。如果你想同时读取视频和音频,需要创建两个MediaExtractor实例,或者交替选中轨道。我个人更推荐用两个实例,逻辑更清晰。
核心逻辑结构图
下面这张图,是我用SVG画的MediaExtractor核心工作流程。你可以把它当作本章的“知识地图”:
一个简单的实战片段
光说不练假把式。我写一个最基础的代码片段,让你感受一下MediaExtractor的用法:
MediaExtractor extractor = new MediaExtractor();
try {
// 1. 设置数据源
extractor.setDataSource("/sdcard/DCIM/example.mp4");
// 2. 遍历轨道,找到视频轨道
int trackCount = extractor.getTrackCount();
int videoTrackIndex = -1;
for (int i = 0; i < trackCount; i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime != null && mime.startsWith("video/")) {
videoTrackIndex = i;
break;
}
}
if (videoTrackIndex >= 0) {
// 3. 选中视频轨道
extractor.selectTrack(videoTrackIndex);
// 4. 循环读取样本
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1MB缓冲区
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
while (true) {
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) break; // 读取完毕
info.size = sampleSize;
info.presentationTimeUs = extractor.getSampleTime();
info.flags = extractor.getSampleFlags();
// 这里可以交给MediaCodec解码,或者保存到文件
// 处理你的业务逻辑...
extractor.advance(); // 移动到下一个样本
}
}
} finally {
// 5. 释放资源
extractor.release();
}
避坑指南:我曾经在项目里犯过一个低级错误——在循环读取时忘记调用advance(),结果程序一直卡在第一个样本上死循环。嗯,这个坑踩过一次就再也不会忘了。
关于MediaFormat
上面代码里出现了MediaFormat,它和MediaExtractor是“黄金搭档”。通过getTrackFormat()拿到的MediaFormat对象,包含了这条轨道的所有关键信息:
- 视频轨道:宽、高、帧率、编码格式、颜色格式等。
- 音频轨道:采样率、声道数、编码格式、比特率等。
- 通用信息:时长、关键帧间隔等。
我个人习惯在选中轨道之前,先把所有轨道的MediaFormat打印出来看看。这样能对媒体文件有个整体认知,避免后续操作时踩坑。
小结
MediaExtractor是Android音视频开发的入门第一课。它不复杂,但非常重要。你只要记住它的核心使命——从容器中提取原始音视频数据,以及那四个步骤的流程,就掌握了它的精髓。
后面的课程里,我们会把MediaExtractor和MediaCodec、MediaMuxer结合起来,做真正的音视频处理实战。到时候你会发现,今天学的这些API,几乎每个项目都会用到。
课后建议:找几个不同格式的媒体文件(MP4、MKV、3GP),用MediaExtractor读取它们的轨道信息,看看每种格式的轨道数量和编码类型有什么不同。动手试试,比看十遍文章都管用。
公众号:蓝海资料掘金营,微信deep3321